diff --git a/Cargo.lock b/Cargo.lock index e20ad5bbcb..0212ed5cb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,9 +12,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ "gimli", ] @@ -45,9 +45,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4" +checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" [[package]] name = "arrayref" @@ -103,8 +103,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -114,8 +114,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -245,7 +245,7 @@ dependencies = [ "log 0.4.11", "peeking_take_while", "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "regex", "rustc-hash", "shlex", @@ -406,9 +406,9 @@ checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] name = "bytes" @@ -433,6 +433,12 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0dcbc35f504eb6fc275a6d20e4ebcda18cf50d40ba6fabff8c711fa16cb3b16" +[[package]] +name = "bytes" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72" + [[package]] name = "bytesize" version = "1.0.1" @@ -589,9 +595,9 @@ dependencies = [ [[package]] name = "console" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50aab2529019abfabfa93f1e6c41ef392f91fbf179b347a7e96abb524884a08" +checksum = "7cc80946b3480f421c2f17ed1cb841753a371c7c5104f51d507e13f532c856aa" dependencies = [ "encode_unicode", "lazy_static", @@ -600,14 +606,13 @@ dependencies = [ "terminal_size", "unicode-width", "winapi 0.3.9", - "winapi-util", ] [[package]] name = "const_fn" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" +checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" [[package]] name = "constant_time_eq" @@ -865,9 +870,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "2.1.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" +checksum = "434e1720189a637d44fe464f4df1e6eb900b4835255b14354497c78af37d9bb8" dependencies = [ "byteorder", "digest 0.8.1", @@ -878,13 +883,24 @@ dependencies = [ [[package]] name = "derivative" -version = "2.1.1" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaed5874effa6cde088c644ddcdcb4ffd1511391c5be4fdd7a5ccd02c7e4a183" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn 1.0.58", +] + +[[package]] +name = "derive_more" +version = "0.99.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" +checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -989,9 +1005,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dtoa" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" +checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" [[package]] name = "ed25519" @@ -1054,8 +1070,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -1107,7 +1123,7 @@ dependencies = [ "hash-db", "hash256-std-hasher", "parity-scale-codec", - "rlp", + "rlp 0.4.6", "rlp-derive", "serde", "sha3 0.9.1", @@ -1141,7 +1157,7 @@ dependencies = [ "log 0.4.11", "parity-scale-codec", "primitive-types", - "rlp", + "rlp 0.4.6", "serde", "sha3 0.8.2", ] @@ -1164,7 +1180,7 @@ dependencies = [ "num_cpus", "paw", "primitive-types", - "rlp", + "rlp 0.4.6", "secp256k1", "serde_json", "sha3 0.9.1", @@ -1213,7 +1229,7 @@ dependencies = [ "jsonrpc-http-server", "jsonrpc-pubsub", "primitive-types", - "rlp", + "rlp 0.4.6", "secp256k1", "serde", "serde_json", @@ -1235,19 +1251,22 @@ name = "evm-state" version = "0.1.0" dependencies = [ "anyhow", - "assert_matches", "bincode", "bytes 0.6.0", "criterion", + "derive_more", "evm", "hex", "keccak-hash", "lazy_static", "log 0.4.11", + "paste 1.0.4", "primitive-types", + "quickcheck", + "quickcheck_macros", "rand 0.6.1", "rand 0.7.3", - "rlp", + "rlp 0.4.6", "rocksdb 0.15.0", "secp256k1", "serde", @@ -1293,8 +1312,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", "synstructure", ] @@ -1416,9 +1435,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "funty" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba62103ce691c2fd80fbae2213dfdda9ce60804973ac6b6e97de818ea7f52c8" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" [[package]] name = "futures" @@ -1428,9 +1447,9 @@ checksum = "4c7e4c2612746b0df8fed4ce0c69156021b704c9aefa360311c04e6e9e002eed" [[package]] name = "futures" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" +checksum = "c70be434c505aee38639abccb918163b63158a4b4bb791b45b7023044bdc3c9c" dependencies = [ "futures-channel", "futures-core", @@ -1443,9 +1462,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" +checksum = "f01c61843314e95f96cc9245702248733a3a3d744e43e2e755e3c7af8348a0a9" dependencies = [ "futures-core", "futures-sink", @@ -1453,9 +1472,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" +checksum = "db8d3b0917ff63a2a96173133c02818fac4a746b0a57569d3baca9ec0e945e08" [[package]] name = "futures-cpupool" @@ -1469,9 +1488,9 @@ dependencies = [ [[package]] name = "futures-executor" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" +checksum = "9ee9ca2f7eb4475772cf39dd1cd06208dce2670ad38f4d9c7262b3e15f127068" dependencies = [ "futures-core", "futures-task", @@ -1480,42 +1499,42 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" +checksum = "e37c1a51b037b80922864b8eed90692c5cd8abd4c71ce49b77146caa47f3253b" [[package]] name = "futures-macro" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" +checksum = "0f8719ca0e1f3c5e34f3efe4570ef2c0610ca6da85ae7990d472e9cbfba13664" dependencies = [ "proc-macro-hack", "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] name = "futures-sink" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" +checksum = "f6adabac1290109cfa089f79192fb6244ad2c3f1cc2281f3e1dd987592b71feb" [[package]] name = "futures-task" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" +checksum = "a92a0843a2ff66823a8f7c77bffe9a09be2b64e533562c412d63075643ec0038" dependencies = [ "once_cell", ] [[package]] name = "futures-util" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" +checksum = "036a2107cdeb57f6d7322f1b6c363dad67cd63ca3b7d1b925bdf75bd5d96cda9" dependencies = [ "futures-channel", "futures-core", @@ -1524,7 +1543,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project 1.0.2", + "pin-project-lite 0.2.3", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -1573,11 +1592,11 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -1613,7 +1632,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5945153d9c566dd5544eca9da4aa430a16343129221a2064cb600d1e6c307212" dependencies = [ - "futures 0.3.8", + "futures 0.3.9", "log 0.4.11", "reqwest", "serde", @@ -1621,7 +1640,7 @@ dependencies = [ "serde_json", "simpl", "smpl_jwt", - "time 0.2.23", + "time 0.2.24", "tokio 0.2.24", ] @@ -1665,7 +1684,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.2", + "http 0.2.3", "indexmap", "slab", "tokio 0.2.24", @@ -1712,9 +1731,9 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "heck" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" dependencies = [ "unicode-segmentation", ] @@ -1785,11 +1804,11 @@ dependencies = [ [[package]] name = "http" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26" +checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" dependencies = [ - "bytes 0.5.6", + "bytes 1.0.0", "fnv", "itoa", ] @@ -1813,7 +1832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ "bytes 0.5.6", - "http 0.2.2", + "http 0.2.3", ] [[package]] @@ -1903,12 +1922,12 @@ dependencies = [ "futures-core", "futures-util", "h2 0.2.7", - "http 0.2.2", + "http 0.2.3", "http-body 0.3.1", "httparse", "httpdate", "itoa", - "pin-project 1.0.2", + "pin-project 1.0.4", "socket2", "tokio 0.2.24", "tower-service", @@ -1988,7 +2007,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f7a72f11830b52333f36e3b09a288333888bf54380fd0ac0790a3c31ab0f3c5" dependencies = [ - "rlp", + "rlp 0.4.6", ] [[package]] @@ -2022,7 +2041,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" dependencies = [ - "console 0.13.0", + "console 0.14.0", "lazy_static", "number_prefix", "regex", @@ -2081,9 +2100,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "jemalloc-ctl" @@ -2093,7 +2112,7 @@ checksum = "c502a5ff9dd2924f1ed32ba96e3b65735d837b4bfd978d3161b1702e66aca4b7" dependencies = [ "jemalloc-sys", "libc", - "paste", + "paste 0.1.18", ] [[package]] @@ -2183,8 +2202,8 @@ checksum = "d0e77e8812f02155b85a677a96e1d16b60181950c0636199bc4528524fba98dc" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -2307,9 +2326,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" [[package]] name = "libloading" @@ -2361,9 +2380,9 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "lock_api" @@ -2560,9 +2579,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f" +checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" dependencies = [ "lazy_static", "libc", @@ -2652,8 +2671,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -2703,8 +2722,8 @@ checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -2748,9 +2767,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.31" +version = "0.10.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d008f51b1acffa0d3450a68606e6a51c123012edaacb0f4e1426bd978869187" +checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -2768,9 +2787,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.59" +version = "0.9.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de52d8eabd217311538a39bba130d7dea1f1e118010fee7a033d966845e7d5fe" +checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" dependencies = [ "autocfg 1.0.1", "cc", @@ -2800,8 +2819,8 @@ checksum = "198db82bb1c18fc00176004462dd809b2a6d851669550aa17af6dacd21ae0c14" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -2833,7 +2852,7 @@ checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", "lock_api 0.4.2", - "parking_lot_core 0.8.1", + "parking_lot_core 0.8.2", ] [[package]] @@ -2847,7 +2866,7 @@ dependencies = [ "libc", "redox_syscall", "rustc_version", - "smallvec 0.6.13", + "smallvec 0.6.14", "winapi 0.3.9", ] @@ -2861,21 +2880,21 @@ dependencies = [ "cloudabi", "libc", "redox_syscall", - "smallvec 1.5.1", + "smallvec 1.6.1", "winapi 0.3.9", ] [[package]] name = "parking_lot_core" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c6d9b8427445284a09c55be860a15855ab580a417ccad9da88f5a06787ced0" +checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" dependencies = [ "cfg-if 1.0.0", "instant", "libc", "redox_syscall", - "smallvec 1.5.1", + "smallvec 1.6.1", "winapi 0.3.9", ] @@ -2889,6 +2908,12 @@ dependencies = [ "proc-macro-hack", ] +[[package]] +name = "paste" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" + [[package]] name = "paste-impl" version = "0.1.18" @@ -2915,8 +2940,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f35583365be5d148e959284f42526841917b7bfa09e2d1a7ad5dde2cf0eaa39" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -2977,11 +3002,11 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" +checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2" dependencies = [ - "pin-project-internal 1.0.2", + "pin-project-internal 1.0.4", ] [[package]] @@ -2991,19 +3016,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] name = "pin-project-internal" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" +checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -3014,9 +3039,9 @@ checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" [[package]] name = "pin-project-lite" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" +checksum = "ba36e0a6cc5a4c645073f4984f1ed55d09f5857d4de7c14550baa81a39ef5a17" [[package]] name = "pin-utils" @@ -3056,9 +3081,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "predicates" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bfead12e90dccead362d62bb2c90a5f6fc4584963645bc7f71a735e0b0735a" +checksum = "73dd9b7b200044694dfede9edf907c1ca19630908443e9447e624993700c6932" dependencies = [ "difference", "predicates-core", @@ -3066,15 +3091,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" +checksum = "fb3dbeaaf793584e29c58c7e3a82bbb3c7c06b63cea68d13b0e3cddc124104dc" [[package]] name = "predicates-tree" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" +checksum = "aee95d988ee893cb35c06b148c80ed2cd52c8eea927f50ba7a0be1a786aeab73" dependencies = [ "predicates-core", "treeline", @@ -3116,8 +3141,8 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", "version_check 0.9.2", ] @@ -3128,7 +3153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "version_check 0.9.2", ] @@ -3181,8 +3206,8 @@ dependencies = [ "anyhow", "itertools 0.8.2", "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -3201,6 +3226,29 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger 0.7.1", + "log 0.4.11", + "rand 0.7.3", + "rand_core 0.5.1", +] + +[[package]] +name = "quickcheck_macros" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn 1.0.58", +] + [[package]] name = "quote" version = "0.6.13" @@ -3212,9 +3260,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" dependencies = [ "proc-macro2 1.0.24", ] @@ -3441,14 +3489,14 @@ checksum = "a415a013dd7c5d4221382329a5a3482566da675737494935cbbbcdec04662f9d" dependencies = [ "cc", "libc", - "smallvec 1.5.1", + "smallvec 1.6.1", ] [[package]] name = "regex" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ "aho-corasick", "memchr", @@ -3467,9 +3515,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "remove_dir_all" @@ -3491,7 +3539,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http 0.2.2", + "http 0.2.3", "http-body 0.3.1", "hyper 0.13.9", "hyper-rustls", @@ -3504,7 +3552,7 @@ dependencies = [ "mime_guess", "native-tls", "percent-encoding 2.1.0", - "pin-project-lite 0.2.0", + "pin-project-lite 0.2.3", "rustls", "serde", "serde_json", @@ -3544,6 +3592,16 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rlp" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54369147e3e7796c9b885c7304db87ca3d09a0a98f72843d532868675bbfba8" +dependencies = [ + "bytes 1.0.0", + "rustc-hex", +] + [[package]] name = "rlp-derive" version = "0.1.0" @@ -3551,8 +3609,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -3696,8 +3754,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b12bd20b94c7cdfda8c7ba9b92ad0d9a56e3fa018c25fca83b51aa664c9b4c0d" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -3802,15 +3860,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] name = "serde_json" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" +checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" dependencies = [ "itoa", "ryu", @@ -3831,9 +3889,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7" +checksum = "971be8f6e4d4a47163b405a3df70d14359186f9ab0f3a3ec37df144ca1ce089f" dependencies = [ "dtoa", "linked-hash-map", @@ -3859,8 +3917,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d08338d8024b227c62bd68a12c7c9883f5c66780abaef15c550dc56f46ee6515" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -3926,9 +3984,9 @@ checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "signal-hook" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed" +checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" dependencies = [ "libc", "signal-hook-registry", @@ -3936,18 +3994,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" dependencies = [ "libc", ] [[package]] name = "signature" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210" +checksum = "0f0242b8e50dd9accdd56170e94ca1ebd223b098eb9c83539a6e367d0f36ae68" [[package]] name = "simpl" @@ -3976,18 +4034,18 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" dependencies = [ "maybe-uninit", ] [[package]] name = "smallvec" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "smpl_jwt" @@ -4002,7 +4060,7 @@ dependencies = [ "serde_derive", "serde_json", "simpl", - "time 0.2.23", + "time 0.2.24", ] [[package]] @@ -4022,15 +4080,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] name = "socket2" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97e0e9fd577458a4f61fb91fcb559ea2afecc54c934119421f9f5d3d5b1a1057" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" dependencies = [ "cfg-if 1.0.0", "libc", @@ -4100,7 +4158,7 @@ version = "1.4.0" dependencies = [ "async-trait", "bincode", - "futures 0.3.8", + "futures 0.3.9", "solana-banks-interface", "solana-banks-server", "solana-runtime", @@ -4124,7 +4182,7 @@ name = "solana-banks-server" version = "1.4.0" dependencies = [ "bincode", - "futures 0.3.8", + "futures 0.3.9", "log 0.4.11", "solana-banks-interface", "solana-metrics", @@ -4412,7 +4470,7 @@ dependencies = [ "rayon", "regex", "reqwest", - "rlp", + "rlp 0.4.6", "rustc_version", "secp256k1", "serde", @@ -4476,16 +4534,16 @@ dependencies = [ "reqwest", "serde", "syn 0.15.44", - "syn 1.0.54", + "syn 1.0.58", "tokio 0.1.22", "winapi 0.3.9", ] [[package]] name = "solana-crate-features" -version = "1.4.17" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250a18e04d94d3b115eb72600eed2124f190f2e63411866cb02b3f2faa4c4a4e" +checksum = "1b04080bbd3fac60b60de703dfa5bcd144be6f3a20f06ef7a954bb0213ad69cb" dependencies = [ "backtrace", "bytes 0.4.12", @@ -4500,7 +4558,7 @@ dependencies = [ "reqwest", "serde", "syn 0.15.44", - "syn 1.0.54", + "syn 1.0.58", "tokio 0.1.22", "winapi 0.3.9", ] @@ -4713,7 +4771,7 @@ dependencies = [ "dlopen_derive", "ed25519-dalek", "fs_extra", - "futures 0.3.8", + "futures 0.3.9", "futures-util", "itertools 0.9.0", "lazy_static", @@ -4757,7 +4815,7 @@ dependencies = [ "bs58", "bytecount", "clap", - "futures 0.3.8", + "futures 0.3.9", "futures-util", "histogram", "log 0.4.11", @@ -4834,11 +4892,11 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.4.17" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b97055ab14e7c1f67a3141b066ada44e6dfd1b2424d61ba2411dfd7cab08b69" +checksum = "8f35069c0ec1ce82b9f9351a3e16de23ea98b76cd0d16bc1b64f16de07326b63" dependencies = [ - "env_logger 0.7.1", + "env_logger 0.8.2", "lazy_static", "log 0.4.11", ] @@ -5119,9 +5177,9 @@ dependencies = [ "serde_json", "sha2", "sha3 0.9.1", - "solana-crate-features 1.4.17", - "solana-logger 1.4.17", - "solana-sdk-macro 1.4.17", + "solana-crate-features 1.5.2", + "solana-logger 1.5.2", + "solana-sdk-macro 1.5.2", "solana-sdk-macro-frozen-abi 1.4.1", "thiserror", ] @@ -5171,22 +5229,22 @@ version = "1.4.0" dependencies = [ "bs58", "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "rustversion", - "syn 1.0.54", + "syn 1.0.58", ] [[package]] name = "solana-sdk-macro" -version = "1.4.17" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090e095a5ac39010fa83488dfae422132798e15183d887cc9ab33ed6bb9dab8f" +checksum = "d9874e9b88fb0608cf3ed5d2c293f512cf68cf21c5c111b9222c53bfa3be92e6" dependencies = [ "bs58", "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "rustversion", - "syn 1.0.54", + "syn 1.0.58", ] [[package]] @@ -5195,9 +5253,9 @@ version = "1.4.0" dependencies = [ "lazy_static", "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "rustc_version", - "syn 1.0.54", + "syn 1.0.58", ] [[package]] @@ -5208,9 +5266,9 @@ checksum = "8e47f618ad2d7af7b9c701e9cc9951681f6d6a9c754863f2ab63e1b98507e515" dependencies = [ "lazy_static", "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "rustc_version", - "syn 1.0.54", + "syn 1.0.58", ] [[package]] @@ -5300,7 +5358,7 @@ dependencies = [ "bzip2", "enum-iterator", "flate2", - "futures 0.3.8", + "futures 0.3.9", "goauth", "log 0.4.11", "prost", @@ -5567,9 +5625,9 @@ dependencies = [ [[package]] name = "standback" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf906c8b8fc3f6ecd1046e01da1d8ddec83e48c8b08b84dcc02b585a6bedf5a8" +checksum = "c66a8cff4fa24853fdf6b51f75c6d7f8206d7c75cab4e467bcd7f25c2b1febe0" dependencies = [ "version_check 0.9.2", ] @@ -5601,10 +5659,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "serde", "serde_derive", - "syn 1.0.54", + "syn 1.0.58", ] [[package]] @@ -5615,12 +5673,12 @@ checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" dependencies = [ "base-x", "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "serde", "serde_derive", "serde_json", "sha1", - "syn 1.0.54", + "syn 1.0.58", ] [[package]] @@ -5665,8 +5723,8 @@ dependencies = [ "heck", "proc-macro-error", "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -5700,12 +5758,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.54" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "unicode-xid 0.2.1", ] @@ -5716,8 +5774,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", "unicode-xid 0.2.1", ] @@ -5775,7 +5833,7 @@ checksum = "1503e47bfae912674d6f4226c09cb8d2f0271a57eef7e799b6f98a545f89c7a3" dependencies = [ "anyhow", "fnv", - "futures 0.3.8", + "futures 0.3.9", "humantime 1.3.0", "log 0.4.11", "pin-project 0.4.27", @@ -5795,8 +5853,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edbaf92ceea0a2ab555bea18a47a891e46ba2d6f930ec9506771662f4ab82bb7" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -5862,22 +5920,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" +checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" +checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -5888,9 +5946,9 @@ checksum = "bcbb6aa301e5d3b0b5ef639c9a9c7e2f1c944f177b460c04dc24c69b1fa2bd99" [[package]] name = "thread_local" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" dependencies = [ "lazy_static", ] @@ -5908,9 +5966,9 @@ dependencies = [ [[package]] name = "time" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b" +checksum = "273d3ed44dca264b0d6b3665e8d48fb515042d42466fad93d2a45b90ec4058f7" dependencies = [ "const_fn", "libc", @@ -5939,9 +5997,9 @@ checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" dependencies = [ "proc-macro-hack", "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "standback", - "syn 1.0.54", + "syn 1.0.58", ] [[package]] @@ -5971,9 +6029,9 @@ dependencies = [ [[package]] name = "tinytemplate" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f" +checksum = "a2ada8616fad06a2d0c455adc530de4ef57605a8120cc65da9653e0e9623ca74" dependencies = [ "serde", "serde_json", @@ -6113,8 +6171,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -6157,7 +6215,7 @@ dependencies = [ "bincode", "bytes 0.5.6", "derivative", - "futures 0.3.8", + "futures 0.3.9", "pin-project 0.4.27", "serde", ] @@ -6285,9 +6343,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ "serde", ] @@ -6304,7 +6362,7 @@ dependencies = [ "bytes 0.5.6", "futures-core", "futures-util", - "http 0.2.2", + "http 0.2.3", "http-body 0.3.1", "hyper 0.13.9", "percent-encoding 2.1.0", @@ -6390,9 +6448,9 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35d656f2638b288b33495d1053ea74c40dc05ec0b92084dd71ca5566c4ed1dc" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" [[package]] name = "tower-limit" @@ -6509,7 +6567,7 @@ checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" dependencies = [ "cfg-if 1.0.0", "log 0.4.11", - "pin-project-lite 0.2.0", + "pin-project-lite 0.2.3", "tracing-attributes", "tracing-core", ] @@ -6521,8 +6579,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", ] [[package]] @@ -6567,12 +6625,12 @@ dependencies = [ [[package]] name = "triehash" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f490aa7aa4e4d07edeba442c007e42e3e7f43aafb5112c5b047fff0b1aa5449c" +checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" dependencies = [ "hash-db", - "rlp", + "rlp 0.5.0", ] [[package]] @@ -6590,7 +6648,7 @@ dependencies = [ "base64 0.11.0", "byteorder", "bytes 0.5.6", - "http 0.2.2", + "http 0.2.3", "httparse", "input_buffer", "log 0.4.11", @@ -6850,8 +6908,8 @@ dependencies = [ "lazy_static", "log 0.4.11", "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", "wasm-bindgen-shared", ] @@ -6873,7 +6931,7 @@ version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" dependencies = [ - "quote 1.0.7", + "quote 1.0.8", "wasm-bindgen-macro-support", ] @@ -6884,8 +6942,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7072,9 +7130,9 @@ dependencies = [ [[package]] name = "yaml-rust" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] @@ -7095,25 +7153,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f369ddb18862aba61aa49bf31e74d29f0f162dec753063200e1dc084345d16" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.54", + "quote 1.0.8", + "syn 1.0.58", "synstructure", ] [[package]] name = "zstd" -version = "0.5.3+zstd.1.4.5" +version = "0.5.4+zstd.1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b32eaf771efa709e8308605bbf9319bf485dc1503179ec0469b611937c0cd8" +checksum = "69996ebdb1ba8b1517f61387a883857818a66c8a295f487b1ffd8fd9d2c82910" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "2.0.5+zstd.1.4.5" +version = "2.0.6+zstd.1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfb642e0d27f64729a639c52db457e0ae906e7bc6f5fe8f5c453230400f1055" +checksum = "98aa931fb69ecee256d44589d19754e61851ae4769bf963b385119b1cc37a49e" dependencies = [ "libc", "zstd-sys", @@ -7121,9 +7179,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "1.4.17+zstd.1.4.5" +version = "1.4.18+zstd.1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b" +checksum = "a1e6e8778706838f43f771d80d37787cb2fe06dafe89dd3aebaf6721b9eaec81" dependencies = [ "cc", "glob", diff --git a/Dockerfile b/Dockerfile index cc8208a6fb..0d9ced161a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,26 @@ FROM ubuntu:20.04 as builder - +RUN apt-get -y update ENV TZ=Europe/Stockholm RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get -y update && apt-get install && \ - apt-get -y install curl git libssl-dev libudev-dev make pkg-config zlib1g-dev llvm clang +RUN apt-get -y install curl git libssl-dev libudev-dev make pkg-config zlib1g-dev llvm clang RUN curl https://sh.rustup.rs -sSf | bash -s -- -y ENV PATH="/root/.cargo/bin:${PATH}" RUN rustup component add rustfmt && rustup update -#Use own solana, clone for repo or add to existent repo -COPY ./ /solana -#COPY ./solana /solana -#RUN git clone https://github.com/solana-labs/solana +COPY . /solana WORKDIR /solana RUN cargo build --release RUN rm /solana/target/release/deps -rf RUN rm /solana/target/release/build -rf + FROM ubuntu:20.04 as dest -RUN apt-get update && \ - apt-get -y install libssl-dev libudev-dev curl +RUN apt-get -y update +RUN apt-get -y install libssl-dev libudev-dev curl + COPY --from=builder /solana/target/release/ /usr/local/solana COPY ./entrypoint.sh /entrypoint.sh ENV PATH="/usr/local/solana:$PATH" -#CMD /bin/bash +# CMD /bin/bash diff --git a/README.md b/README.md index afb01c2a8d..5bf6da0965 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- Solana + Velas chain

# Building @@ -29,8 +29,8 @@ $ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang ## **2. Download the source code.** ```bash -$ git clone https://github.com/velas/velas.git -$ cd velas +$ git clone https://github.com/velas/velas-chain.git +$ cd velas-chain ``` ## **3. Build.** @@ -53,7 +53,7 @@ $ cargo test ``` ### EVM integration -Info about evm integration is at our [docs](https://docs.next.velas.com/evm). +Info about EVM integration is at our [docs](https://docs.next.velas.com/evm). ### Starting a local devnet Start your own devnet locally, instructions are in the [online docs](https://docs.next.velas.com/cluster/bench-tps). diff --git a/bench-tps-evm/src/bench_evm.rs b/bench-tps-evm/src/bench_evm.rs index 7b44f3cd2e..260caa43c1 100644 --- a/bench-tps-evm/src/bench_evm.rs +++ b/bench-tps-evm/src/bench_evm.rs @@ -33,7 +33,7 @@ use solana_evm_loader_program::scope::*; pub const BENCH_SEED: &str = "authority"; -pub fn generate_evm_keypair(seed_keypair: &Keypair) -> evm::SecretKey { +pub fn generate_evm_key(seed_keypair: &Keypair) -> evm::SecretKey { use solana_evm_loader_program::scope::evm::rand::SeedableRng; let mut seed = [0u8; 32]; @@ -54,8 +54,8 @@ pub fn generate_and_fund_evm_keypairs( let mut keypairs: Vec<_> = sources .into_iter() .map(|key| { - let evm_keys = generate_evm_keypair(&key); - (key, evm_keys) + let evm_key = generate_evm_key(&key); + (key, evm_key) }) .collect(); info!("Get lamports..."); @@ -163,7 +163,7 @@ impl<'a> FundingTransactions<'a> for Vec<(&'a Keypair, &'a evm::SecretKey, Trans .par_iter() .map(|(k, evm)| { let instructions = solana_evm_loader_program::transfer_native_to_eth_ixs( - &k.pubkey(), + k.pubkey(), to_lamports, evm.to_address(), ); @@ -323,7 +323,7 @@ fn generate_system_txs( let tx_call = tx_call.sign(&from.1, None); - let ix = solana_evm_loader_program::send_raw_tx(&from.0.pubkey(), tx_call); + let ix = solana_evm_loader_program::send_raw_tx(from.0.pubkey(), tx_call); let message = Message::new(&[ix], Some(&from.0.pubkey())); diff --git a/core/src/evm_rpc_impl/mod.rs b/core/src/evm_rpc_impl/mod.rs index b8453250b8..971b153e3b 100644 --- a/core/src/evm_rpc_impl/mod.rs +++ b/core/src/evm_rpc_impl/mod.rs @@ -151,9 +151,9 @@ impl ChainMockERPC for ChainMockERPCImpl { .iter() .flatten() .map(|tx_hash| { - evm_lock - .get_tx_receipt_by_hash(*tx_hash) - .expect("Transaction exist") + evm_lock + .get_tx_receipt_by_hash(*tx_hash) + .expect("Transaction exist") }) .filter_map(|receipt| { RPCTransaction::new_from_receipt(receipt, block_hash.clone()).ok() @@ -278,7 +278,8 @@ impl BasicERPC for BasicERPCImpl { ) -> Result, Error> { let bank = meta.bank(block_to_commitment(block)); let evm_state = bank.evm_state.read().unwrap(); - Ok(Hex(evm_state.basic(address.0).balance)) + let account = evm_state.get_account(address.0).unwrap_or_default(); + Ok(Hex(account.balance)) } fn storage_at( @@ -303,7 +304,8 @@ impl BasicERPC for BasicERPCImpl { ) -> Result, Error> { let bank = meta.bank(block_to_commitment(block)); let evm_state = bank.evm_state.read().unwrap(); - Ok(Hex(evm_state.basic(address.0).nonce)) + let account = evm_state.get_account(address.0).unwrap_or_default(); + Ok(Hex(account.nonce)) } fn code( @@ -314,7 +316,8 @@ impl BasicERPC for BasicERPCImpl { ) -> Result { let bank = meta.bank(block_to_commitment(block)); let evm_state = bank.evm_state.read().unwrap(); - Ok(Bytes(evm_state.basic(address.0).code)) + let account = evm_state.get_account(address.0).unwrap_or_default(); + Ok(Bytes(account.code)) } fn transaction_by_hash( diff --git a/core/src/validator.rs b/core/src/validator.rs index 5ffc1e1276..66bba74614 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -672,9 +672,13 @@ fn new_banks_from_ledger( TransactionHistoryServices::default() }; + // TODO: Add evm-state to config. + let evm_state_path = ledger_path.join("evm-state"); + let (mut bank_forks, mut leader_schedule_cache, snapshot_hash) = bank_forks_utils::load( &genesis_config, &blockstore, + evm_state_path, config.account_paths.clone(), config.snapshot_config.as_ref(), process_options, diff --git a/core/tests/bank_forks.rs b/core/tests/bank_forks.rs index a528376669..9639304c9e 100644 --- a/core/tests/bank_forks.rs +++ b/core/tests/bank_forks.rs @@ -65,6 +65,7 @@ mod tests { DEFINE_SNAPSHOT_VERSION_PARAMETERIZED_TEST_FUNCTIONS!(V1_2_0, MainnetBeta, V1_2_0_MainnetBeta); struct SnapshotTestConfig { + evm_state_dir: TempDir, accounts_dir: TempDir, snapshot_dir: TempDir, _snapshot_output_path: TempDir, @@ -79,6 +80,7 @@ mod tests { cluster_type: ClusterType, snapshot_interval_slots: u64, ) -> SnapshotTestConfig { + let evm_state_dir = TempDir::new().unwrap(); let accounts_dir = TempDir::new().unwrap(); let snapshot_dir = TempDir::new().unwrap(); let snapshot_output_path = TempDir::new().unwrap(); @@ -102,6 +104,7 @@ mod tests { }; bank_forks.set_snapshot_config(Some(snapshot_config.clone())); SnapshotTestConfig { + evm_state_dir, accounts_dir, snapshot_dir, _snapshot_output_path: snapshot_output_path, @@ -116,6 +119,7 @@ mod tests { old_bank_forks: &BankForks, old_last_slot: Slot, old_genesis_config: &GenesisConfig, + evm_state_path: &Path, account_paths: &[PathBuf], ) { let (snapshot_path, snapshot_package_output_path) = old_bank_forks @@ -127,6 +131,7 @@ mod tests { let old_last_bank = old_bank_forks.get(old_last_slot).unwrap(); let deserialized_bank = snapshot_utils::bank_from_archive( + evm_state_path, &account_paths, &[], &old_bank_forks @@ -213,9 +218,16 @@ mod tests { snapshot_utils::archive_snapshot_package(&snapshot_package).unwrap(); // Restore bank from snapshot + let evm_state_path = snapshot_test_config.evm_state_dir.path(); let account_paths = &[snapshot_test_config.accounts_dir.path().to_path_buf()]; let genesis_config = &snapshot_test_config.genesis_config_info.genesis_config; - restore_from_snapshot(bank_forks, last_slot, genesis_config, account_paths); + restore_from_snapshot( + evm_state_path, + bank_forks, + last_slot, + genesis_config, + account_paths, + ); } fn run_test_bank_forks_snapshot_n( diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index ad7f098763..0df4b32672 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -17,7 +17,7 @@ module.exports = { links: [ { to: "evm", - label: "Evm integration", + label: "EVM integration", position: "left", }, { @@ -78,12 +78,12 @@ module.exports = { items: [ { label: "GitHub", - href: "https://github.com/velas", + href: "https://github.com/velas/velas-chain", }, ], }, ], - copyright: `Copyright © ${new Date().getFullYear()} Velas Foundation`, + copyright: `Copyright © ${new Date().getFullYear()} Velas`, }, }, presets: [ diff --git a/docs/src/evm.md b/docs/src/evm.md index de11988060..15f6138773 100644 --- a/docs/src/evm.md +++ b/docs/src/evm.md @@ -1,11 +1,11 @@ --- -title: EVM in solana +title: EVM in Solana --- -[Solana application model](apps/rent.md) is aiming to high performance by spliting its modifiable state on accounts. -While this allows to process transactions in parallel on single shard, it also introduce complication for ordinary DApps developer. +[Solana application model](apps/rent.md) is aiming to high performance by spliting its modifiable state across multiple accounts. +While this allows to process transactions in parallel on single shard, thats also introduce complication for ordinary DApps developer. Also, most of DApps infrastructure is already relies on Solidity, and targeting Ethereum blockchain. -This two reasons can significantly slow down the spread of solana ecosystem. +These two reasons can significantly slow down the spread of solana ecosystem. To make life of DApps developers, and integrators more easier, we at Velas introduce full hybrid of solana and EVM. @@ -20,7 +20,8 @@ Note: EVM store tokens in nano plancks, so when you transfer for example, 5 plan Usage: ``` -/target/debug/evm-utils transfer-to-eth --help +> evm-utils transfer-to-eth --help + evm-utils-transfer-to-eth 0.1.0 Transfer solana token to EVM world @@ -39,11 +40,11 @@ ARGS: Example: ``` -evm-utils transfer-to-eth 5 9Edb9E0B88Dbf2a29aE121a657e1860aEceaA53D +> evm-utils transfer-to-eth 5 9Edb9E0B88Dbf2a29aE121a657e1860aEceaA53D ``` Result after transaction processing: ``` -[2020-12-26T15:03:01Z INFO evm_utils] Loading keypair from: /home/vladimir/.config/solana/id.json +[2020-12-26T15:03:01Z INFO evm_utils] Loading keypair from: /home/user/.config/solana/id.json Transaction signature = 5d3eP741NYgemyM4CLmXuTEcP8f8w7QxfZ5vBxorqenEtNeSHWMFpkwtyi1meFKHVNXzDD3NbvFCExjZH79gEMKk ``` diff --git a/docs/src/evm/bridge.md b/docs/src/evm/bridge.md index bb0301219c..195e46f778 100644 --- a/docs/src/evm/bridge.md +++ b/docs/src/evm/bridge.md @@ -2,7 +2,7 @@ title: Bridge to EVM --- -All Ethereum transaction is wrapped into native format. In order to execute native transaction, +Any Ethereum transaction is wrapped into native format. In order to execute native transaction, someone should pay a fee in native coin. EVM bridge is managing this routine. Its a regular web-server that wrap EVM transaction into native, and take gas price as fee. @@ -16,4 +16,4 @@ For devnet we provide a public evm-bridge, which is located at http://bridge.nex ## Gas price, and gas limit collecting: Every evm-bridge is responsible to set it's own commision, every evm-bridge users is paying this commission by increasing gas price in transaction. -This mechanism provide incentivise to host your own evm bridge, and increase decentralisation. \ No newline at end of file +This mechanism provide incentivise to host your own evm bridge publicly, and increase decentralisation. \ No newline at end of file diff --git a/evm-utils/evm-bridge/src/main.rs b/evm-utils/evm-bridge/src/main.rs index 879aafc128..f86bed195a 100644 --- a/evm-utils/evm-bridge/src/main.rs +++ b/evm-utils/evm-bridge/src/main.rs @@ -99,7 +99,7 @@ impl EvmBridge { tx.signature.chain_id() ); - let ix = solana_evm_loader_program::send_raw_tx(&self.key.pubkey(), tx); + let ix = solana_evm_loader_program::send_raw_tx(self.key.pubkey(), tx); let message = Message::new(&[ix], Some(&self.key.pubkey())); let mut send_raw_tx: solana::Transaction = solana::Transaction::new_unsigned(message); @@ -197,7 +197,6 @@ impl BridgeERPC for BridgeERPCImpl { let tx: evm::Transaction = rlp::decode(&bytes.0).unwrap(); let unsigned_tx: evm::UnsignedTransaction = tx.clone().into(); let hash = unsigned_tx.signing_hash(CHAIN_ID.into()); - debug!("loaded tx_hash = {}", hash); meta.send_tx(tx) } diff --git a/evm-utils/evm-state/.gitignore b/evm-utils/evm-state/.gitignore new file mode 100644 index 0000000000..9f970225ad --- /dev/null +++ b/evm-utils/evm-state/.gitignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/evm-utils/evm-state/Cargo.toml b/evm-utils/evm-state/Cargo.toml index f4c5eb029a..6704fbf4ab 100644 --- a/evm-utils/evm-state/Cargo.toml +++ b/evm-utils/evm-state/Cargo.toml @@ -15,12 +15,11 @@ rocksdb = { version = "0.15.0", default-features = false } primitive-types = "0.7.2" keccak-hash = "0.5" -log = "0.4" +log = "0.4.11" simple_logger = "1.11" hex = "0.4.2" serde = "1.0" sha3 = "0.9.1" -assert_matches = "1.4" rand = "0.7.3" rlp = "0.4.5" thiserror = "1.0.22" @@ -29,10 +28,15 @@ bincode = "1.3.1" lazy_static = "1.4.0" bytes = "0.6.0" snafu = "0.6.8" +derive_more = "0.99.11" [dev-dependencies] criterion = "0.3" +quickcheck = "0.9.2" +quickcheck_macros = "0.9.1" +rand = "0.7.3" +paste = "1.0.3" [[bench]] name = "bench_evm" -harness = false \ No newline at end of file +harness = false diff --git a/evm-utils/evm-state/benches/bench_evm.rs b/evm-utils/evm-state/benches/bench_evm.rs index cc3a4d3dfc..b44aacbe2a 100644 --- a/evm-utils/evm-state/benches/bench_evm.rs +++ b/evm-utils/evm-state/benches/bench_evm.rs @@ -1,60 +1,130 @@ +use std::fs; +use std::path::{Path, PathBuf}; + use criterion::{criterion_group, criterion_main, Criterion, Throughput}; -use assert_matches::assert_matches; -use evm::{Capture, CreateScheme, ExitReason, ExitSucceed}; +use anyhow::{bail, Context, Result}; +use evm::{ExitReason, ExitSucceed}; use evm_state::*; - use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; -use std::sync::RwLock; fn name_to_key(name: &str) -> H160 { let hash = H256::from_slice(Keccak256::digest(name.as_bytes()).as_slice()); hash.into() } +fn prepare_dir>(path: P) -> Result { + let path = path.as_ref(); + if path.exists() { + bail!("Path {} is already exists", path.display()); + } + fs::create_dir_all(path).context("Unable to create for bench data")?; + Ok(path.to_owned()) +} + +fn cleanup_dir>(path: P) -> Result<()> { + let path = path.as_ref(); + if !path.exists() { + bail!("Path {} is not exists", path.display()); + } + fs::remove_dir_all(path).context("Clean-up")?; + Ok(()) +} + fn criterion_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("Evm"); - - group.throughput(Throughput::Elements(1 as u64)); - // simple_logger::SimpleLogger::new().init().unwrap(); + group.throughput(Throughput::Elements(1)); group.bench_function("call_hello", |b| { let code = hex::decode(HELLO_WORLD_CODE).unwrap(); let data = hex::decode(HELLO_WORLD_ABI).unwrap(); + let accounts = ["contract", "caller"]; + + let dir = prepare_dir("call_hello").unwrap(); + let mut state = EvmState::load_from(&dir, 0).unwrap(); + + for acc in &accounts { + let account = name_to_key(acc); + let memory = AccountState { + ..Default::default() + }; + state.set_account(account, memory); + } let config = evm::Config::istanbul(); - let backend = EvmState::new(); + let mut executor = Executor::with_config(state.clone(), config, usize::max_value(), 0); - let backend = RwLock::new(backend); + let exit_reason = executor.with_executor(|executor| { + executor.transact_create( + name_to_key("caller"), + U256::zero(), + code, + usize::max_value(), + ) + }); + let contract_address = TransactionAction::Create.address(name_to_key("caller"), 0.into()); + assert!(matches!( + exit_reason, + ExitReason::Succeed(ExitSucceed::Returned) + )); - let mut executor = Executor::with_config( - backend.read().unwrap().clone(), - config, - usize::max_value(), - 0, - ); + b.iter(|| { + let exit_reason = executor.with_executor(|executor| { + executor.transact_call( + name_to_key("caller"), + contract_address, + U256::zero(), + data.to_vec(), + usize::max_value(), + ) + }); - let exit_reason = match executor.with_executor(|e| { - e.create( + let result = hex::decode(HELLO_WORLD_RESULT).unwrap(); + match exit_reason { + (ExitReason::Succeed(ExitSucceed::Returned), res) if res == result => {} + any_other => panic!("Not expected result={:?}", any_other), + } + }); + + cleanup_dir(dir).unwrap(); + }); + + group.bench_function("call_hello_with_executor_recreate", |b| { + let code = hex::decode(HELLO_WORLD_CODE).unwrap(); + let data = hex::decode(HELLO_WORLD_ABI).unwrap(); + + let dir = prepare_dir("call_hello_with_executor_recreate").unwrap(); + let mut state = EvmState::load_from(&dir, 0).unwrap(); + let config = evm::Config::istanbul(); + + let mut executor = + Executor::with_config(state.clone(), config.clone(), usize::max_value(), 0); + + let exit_reason = executor.with_executor(|executor| { + executor.transact_create( name_to_key("caller"), - CreateScheme::Fixed(name_to_key("contract")), U256::zero(), - code.clone(), - None, + code, + usize::max_value(), ) - }) { - Capture::Exit((s, _, v)) => (s, v), - Capture::Trap(_) => unreachable!(), - }; + }); + assert!(matches!( + exit_reason, + ExitReason::Succeed(ExitSucceed::Returned) + )); - assert_matches!(exit_reason, (ExitReason::Succeed(ExitSucceed::Returned), _)); + let contract_address = TransactionAction::Create.address(name_to_key("caller"), 0.into()); + let patch = executor.deconstruct(); + state.swap_commit(patch); b.iter(|| { - let exit_reason = executor.with_executor(|e| { - e.transact_call( - name_to_key("contract"), - name_to_key("contract"), + let mut executor = + Executor::with_config(state.clone(), config.clone(), usize::max_value(), 0); + let exit_reason = executor.with_executor(|executor| { + executor.transact_call( + name_to_key("caller"), + contract_address, U256::zero(), data.to_vec(), usize::max_value(), @@ -67,53 +137,58 @@ fn criterion_benchmark(c: &mut Criterion) { any_other => panic!("Not expected result={:?}", any_other), } }); + + cleanup_dir(dir).unwrap(); }); - group.bench_function("call_hello_with_executor_recreate", |b| { + group.bench_function("call_hello_on_dumped_state", |b| { let accounts = ["contract", "caller"]; let code = hex::decode(HELLO_WORLD_CODE).unwrap(); let data = hex::decode(HELLO_WORLD_ABI).unwrap(); + let dir = prepare_dir("call_hello_on_dumped_state").unwrap(); + let mut state = EvmState::load_from(&dir, 0).unwrap(); + + for acc in &accounts { + let account = name_to_key(acc); + let memory = AccountState { + ..Default::default() + }; + state.set_account(account, memory); + } + state.dump_all().unwrap(); + let config = evm::Config::istanbul(); - let backend = EvmState::new(); - - let backend = RwLock::new(backend); - let mut executor = Executor::with_config( - backend.read().unwrap().clone(), - config.clone(), - usize::max_value(), - 0, - ); - - let exit_reason = match executor.with_executor(|e| { - e.create( + let mut executor = + Executor::with_config(state.clone(), config.clone(), usize::max_value(), 0); + + let exit_reason = executor.with_executor(|executor| { + executor.transact_create( name_to_key("caller"), - CreateScheme::Fixed(name_to_key("contract")), U256::zero(), - code.clone(), - None, + code, + usize::max_value(), ) - }) { - Capture::Exit((s, _, v)) => (s, v), - Capture::Trap(_) => unreachable!(), - }; + }); + assert!(matches!( + exit_reason, + ExitReason::Succeed(ExitSucceed::Returned), + )); - assert_matches!(exit_reason, (ExitReason::Succeed(ExitSucceed::Returned), _)); let patch = executor.deconstruct(); - backend.write().unwrap().swap_commit(patch); + state.swap_commit(patch); + + state.dump_all().unwrap(); + let contract_address = TransactionAction::Create.address(name_to_key("caller"), 0.into()); b.iter(|| { - let mut executor = Executor::with_config( - backend.read().unwrap().clone(), - config.clone(), - usize::max_value(), - 1, - ); - let exit_reason = executor.with_executor(|e| { - e.transact_call( - name_to_key("contract"), - name_to_key("contract"), + let mut executor = + Executor::with_config(state.clone(), config.clone(), usize::max_value(), 0); + let exit_reason = executor.with_executor(|executor| { + executor.transact_call( + name_to_key("caller"), + contract_address, U256::zero(), data.to_vec(), usize::max_value(), @@ -126,7 +201,10 @@ fn criterion_benchmark(c: &mut Criterion) { any_other => panic!("Not expected result={:?}", any_other), } }); + + cleanup_dir(dir).unwrap(); }); + group.finish(); } diff --git a/evm-utils/evm-state/src/evm_backend.rs b/evm-utils/evm-state/src/evm_backend.rs index 45b0243cda..290957175c 100644 --- a/evm-utils/evm-state/src/evm_backend.rs +++ b/evm-utils/evm-state/src/evm_backend.rs @@ -3,20 +3,19 @@ use evm::backend::{Apply, Backend, Basic}; use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; +use crate::types::MemoryVicinity; + pub struct EvmBackend { pub(crate) evm_state: EvmState, - pub(crate) tx_info: crate::layered_backend::MemoryVicinity, + pub(crate) tx_info: MemoryVicinity, } impl EvmBackend { - pub fn new_from_state( - evm_state: EvmState, - tx_info: crate::layered_backend::MemoryVicinity, - ) -> Self { + pub fn new_from_state(evm_state: EvmState, tx_info: MemoryVicinity) -> Self { Self { evm_state, tx_info } } - fn tx_info(&self) -> &crate::layered_backend::MemoryVicinity { + fn tx_info(&self) -> &MemoryVicinity { &self.tx_info } @@ -59,9 +58,11 @@ impl EvmBackend { for (index, value) in storage { if value == H256::default() { - self.evm_state.storage.remove((address, index)); + self.evm_state.accounts_storage.remove((address, index)); } else { - self.evm_state.storage.insert((address, index), value); + self.evm_state + .accounts_storage + .insert((address, index), value); } } diff --git a/evm-utils/evm-state/src/layered_backend.rs b/evm-utils/evm-state/src/layered_backend.rs index a80d19a41c..ce1647571b 100644 --- a/evm-utils/evm-state/src/layered_backend.rs +++ b/evm-utils/evm-state/src/layered_backend.rs @@ -1,154 +1,332 @@ -use primitive_types::{H160, H256, U256}; +use std::{ + any::type_name, borrow::Cow, collections::BTreeMap, fmt::Debug, marker::PhantomData, + ops::Deref, path::Path, +}; + +use log::*; + +use crate::{ + mb_value::MaybeValue, + persistent_types, + storage::{PersistentAssoc, Result as StorageResult, VersionedStorage}, + transactions::TransactionReceipt, + types::*, +}; use serde::{Deserialize, Serialize}; -use super::transactions::TransactionReceipt; -use super::version_map::Map; +pub type Storage = VersionedStorage; -/// Vivinity value of a memory backend. -#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct MemoryVicinity { - /// Gas price. - pub gas_price: U256, - /// Origin. - pub origin: H160, - /// Chain ID. - pub chain_id: U256, - /// Environmental block hashes. - pub block_hashes: Vec, - /// Environmental block number. - pub block_number: U256, - /// Environmental coinbase. - pub block_coinbase: H160, - /// Environmental block timestamp. - pub block_timestamp: U256, - /// Environmental block difficulty. - pub block_difficulty: U256, - /// Environmental block gas limit. - pub block_gas_limit: U256, +// Store every account storage at single place, to use power of versioned map. +// This allows us to save only changed data. +persistent_types! { + Accounts in "accounts" => H160 : AccountState, + AccountsStorage in "accounts_storage" => (H160, H256) : H256, + TransactionReceipts in "txs_receipts" => H256 : TransactionReceipt, + TransactionsInBlock in "txs_in_block" => Slot : Vec, // TODO: Key is Slot or U256? + BigTransactions in "big_tx_storage" => H256 : BigTransactionStorage, } -pub struct LogWithLocation { - pub transaction_hash: H256, - pub transaction_id: u64, - pub block_num: u64, - pub address: H160, - pub data: Vec, - pub topics: Vec, +pub(crate) struct Layer +where + M::Key: Ord, +{ + map: BTreeMap>, + is_frozen: bool, + _type: PhantomData, } -pub struct LogFilter { - pub from_block: u64, - pub to_block: u64, - pub address: Option, - pub topics: Vec, +impl Layer +where + M: PersistentAssoc, + M::Key: Ord, +{ + pub fn empty() -> Self { + Self { + map: BTreeMap::new(), + is_frozen: false, + _type: PhantomData, + } + } + + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + pub fn insert(&mut self, key: M::Key, value: M::Value) + where + M::Key: Debug, + M::Value: Debug, + { + assert!( + !self.is_frozen, + "Modification of frozen layer is prohibited" + ); + trace!( + "layer :: {} inserts {:?} => {:?}", + type_name::(), + key, + value + ); + self.map.insert(key, MaybeValue::Value(value)); + } + + pub fn remove(&mut self, key: M::Key) + where + M::Key: Debug, + { + assert!( + !self.is_frozen, + "Modification of frozen layer is prohibited" + ); + + trace!("layer :: {} removes {:?}", type_name::(), key); + self.map.insert(key, MaybeValue::Removed); + } + + fn freeze(&mut self) { + self.is_frozen = true; + } + + fn dump_into( + &mut self, + storage: impl Deref, + version: Slot, + ) -> StorageResult<()> + where + M::Key: Ord + Copy + Debug, + M::Value: Clone + Debug, + { + let storage = storage.deref().typed::(); + + for (key, value) in std::mem::replace(self, Self::empty()).map { + debug!( + "{}: {:?} {:?} migrates from memory into storage", + version, key, &value + ); + + storage.insert_with(version, key, value)?; + } + + Ok(()) + } } -#[derive(Default, Clone, Debug, Eq, PartialEq)] -pub struct AccountState { - /// Account nonce. - pub nonce: U256, - /// Account balance. - pub balance: U256, - /// Account code. - pub code: Vec, +impl Clone for Layer +where + M::Key: Clone + Ord, + M::Value: Clone, +{ + fn clone(&self) -> Self { + Self { + map: self.map.clone(), + is_frozen: false, + _type: PhantomData, + } + } } -#[derive(Default, Clone, Debug, Eq, PartialEq)] +#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct BigTransactionStorage { pub tx_chunks: Vec, } -#[derive(Debug, Clone)] +#[derive(Clone)] // TODO: Debug pub struct EvmState { - pub(crate) accounts: Map, - // Store every account storage at single place, to use power of versioned map. - // This allows us to save only changed data. - pub(crate) storage: Map<(H160, H256), H256>, - pub(crate) txs_receipts: Map, - pub(crate) txs_in_block: Map>, - //TODO: Deadline for storing data. - pub(crate) big_transactions: Map, + pub(crate) current_slot: Slot, + pub(crate) previous_slot: Option, + + pub(crate) accounts: Layer, + pub(crate) accounts_storage: Layer, + pub(crate) txs_receipts: Layer, + pub(crate) txs_in_block: Layer, + + pub(crate) big_transactions: Layer, + + pub storage: Storage, } +// TODO: move this logic outside impl Default for EvmState { fn default() -> Self { - Self::new_not_forget_to_deserialize_later() + let path = std::env::temp_dir().join("evm-state"); + Self::new(path).expect("Unable to instantiate default EVM state") } } impl EvmState { - pub fn new() -> Self { - Self { - accounts: Map::new(), - storage: Map::new(), - txs_receipts: Map::new(), - txs_in_block: Map::new(), - big_transactions: Map::new(), - } - } - pub fn freeze(&mut self) { + debug!("freezing evm state (slot {})", self.current_slot); + self.dump_all() + .expect("Unable to dump EVM state layers into storage"); + self.accounts.freeze(); - self.storage.freeze(); + self.accounts_storage.freeze(); self.txs_receipts.freeze(); self.txs_in_block.freeze(); self.big_transactions.freeze(); + + debug!( + "new slot {} with previous {:?}", + self.current_slot, self.previous_slot + ); + self.storage + .new_version(self.current_slot, self.previous_slot) + .expect("Unable to create new version in storage"); } - pub fn try_fork(&self) -> Option { - let accounts = self.accounts.try_fork()?; - let storage = self.storage.try_fork()?; - let txs_receipts = self.txs_receipts.try_fork()?; - let txs_in_block = self.txs_in_block.try_fork()?; - let big_transactions = self.big_transactions.try_fork()?; + // TODO: dump all + pub fn try_fork(&self, new_slot: Slot) -> Option { + info!( + "forking evm state from slot {} to slot {}", + self.current_slot, new_slot + ); + + // TODO: assert that all these maps are empty + let accounts = self.accounts.clone(); + let accounts_storage = self.accounts_storage.clone(); + let txs_receipts = self.txs_receipts.clone(); + let txs_in_block = self.txs_in_block.clone(); + let big_transactions = self.big_transactions.clone(); Some(Self { + current_slot: new_slot, + previous_slot: Some(self.current_slot), + accounts, - storage, + accounts_storage, txs_receipts, txs_in_block, big_transactions, + storage: self.storage.clone(), }) } -} -impl EvmState { - // TODO: Replace it by persistent storage - pub fn new_not_forget_to_deserialize_later() -> Self { - EvmState { - accounts: Map::new(), - storage: Map::new(), - txs_receipts: Map::new(), - txs_in_block: Map::new(), - big_transactions: Map::new(), + #[rustfmt::skip] + fn dump_all(&mut self) -> anyhow::Result<()> { + self.accounts.dump_into(&self.storage, self.current_slot)?; + self.accounts_storage.dump_into(&self.storage, self.current_slot)?; + self.txs_receipts.dump_into(&self.storage, self.current_slot)?; + self.txs_in_block.dump_into(&self.storage, self.current_slot)?; + self.big_transactions.dump_into(&self.storage, self.current_slot)?; + Ok(()) + } + + // TODO: elide map arg, TBD: maybe typemap + fn lookup<'a, M: PersistentAssoc>( + &'a self, + layer: &'a Layer, + key: M::Key, + ) -> Option> + where + M::Key: Copy + Ord + Debug, + M::Value: Clone + Debug, + { + debug!("lookup {} for key {:?}", type_name::(), &key); + if let Some(mb_value) = layer.map.get(&key) { + Option::from(mb_value.by_ref()).map(Cow::Borrowed) + } else { + let lookup_slot = if self.storage.is_exists(self.current_slot).unwrap() { + Some(self.current_slot) + } else { + self.previous_slot + }; + + if let Some(slot) = lookup_slot { + if let Some(mb_value) = + self.storage + .typed::() + .get_for(slot, key) + .unwrap_or_else(|err| { + panic!( + "Storage ({} :: Key {} => Value {}) lookup error: {:?}", + type_name::(), + type_name::(), + type_name::(), + err + ); + }) + { + debug!( + "{}: key {:?} was found in storage, value: {:?}", + type_name::(), + key, + &mb_value + ); + Option::from(mb_value).map(Cow::Owned) + } else { + debug!( + "{}: key {:?} was not found in storage", + type_name::(), + key + ); + None + } + } else { + None + } } } +} - pub fn get_tx_receipt_by_hash(&self, tx_hash: H256) -> Option { - self.txs_receipts.get(&tx_hash).cloned() +impl EvmState { + pub fn new>(path: P) -> Result { + //TODO: add flags, to asserts for empty storage. + Self::load_from(path, 0) } - pub fn get_txs_in_block(&self, block_num: u64) -> Option> { - self.txs_in_block.get(&block_num).cloned() + pub fn load_from>(path: P, slot: Slot) -> Result { + info!( + "open evm state storage {} for slot {}", + path.as_ref().display(), + slot + ); + let storage = Storage::open(path, COLUMN_NAMES)?; + let previous_slot = storage.previous_of(slot)?; + debug!( + "storage reports: previous of {} is {:?}", + slot, previous_slot + ); + + Ok(Self { + current_slot: slot, + previous_slot, + + accounts: Layer::empty(), + accounts_storage: Layer::empty(), + txs_receipts: Layer::empty(), + txs_in_block: Layer::empty(), + big_transactions: Layer::empty(), + storage, + }) } pub fn get_account(&self, address: H160) -> Option { - self.accounts.get(&address).cloned() + self.lookup(&self.accounts, address).map(Cow::into_owned) } - pub fn basic(&self, account: H160) -> AccountState { - self.get_account(account).unwrap_or_default() + pub fn get_storage(&self, address: H160, index: H256) -> Option { + self.lookup(&self.accounts_storage, (address, index)) + .map(Cow::into_owned) } - pub fn get_storage(&self, address: H160, index: H256) -> Option { - self.storage.get(&(address, index)).cloned() + pub fn get_tx_receipt_by_hash(&self, tx_hash: H256) -> Option { + self.lookup(&self.txs_receipts, tx_hash) + .map(Cow::into_owned) + } + + pub fn get_txs_in_block(&self, block_num: Slot) -> Option> { + self.lookup(&self.txs_in_block, block_num) + .map(Cow::into_owned) + } + + pub fn get_big_tx(&self, address: H256) -> Option { + self.lookup(&self.big_transactions, address) + .map(Cow::into_owned) } - // transaction_hash: H256, - // transaction_id: u64, - // block_num: u64, - // address: H160, - // data: H256, - // topics: Vec + // NOTE: currently used in benches only + pub fn set_account(&mut self, address: H160, state: AccountState) { + self.accounts.insert(address, state); + } // TODO: Optimize, using bloom filters. // TODO: Check topics query limits <= 4. @@ -157,7 +335,7 @@ impl EvmState { let mut result = Vec::new(); for (block_id, txs) in (logs_filter.from_block..=logs_filter.to_block) - .filter_map(|b| self.txs_in_block.get(&b).cloned().map(|k| (b, k))) + .filter_map(|b| self.get_txs_in_block(b).map(|k| (b, k))) { let txs_in_block = txs .into_iter() @@ -188,34 +366,61 @@ impl EvmState { } pub fn swap_commit(&mut self, mut updated: Self) { - // TODO: Assert that updated is newer than current state. + // Assert that updated is newer than current state. + // Slot can not change, because we allow multiple commits per block. + // assert!( + // updated.current_slot > self.current_slot + // || (updated.current_slot == self.current_slot && self.is_empty()), + // "Not expected commit: current = slot {}, is_empty {}, updated = slot {}, is_empty {}", + // self.current_slot, + // self.is_empty(), + // updated.current_slot, + // updated.is_empty() + // ); + std::mem::swap(self, &mut updated); } + + /// True if current layer has no any update, false otherwise. + fn is_empty(&self) -> bool { + self.accounts.is_empty() + && self.accounts_storage.is_empty() + && self.txs_receipts.is_empty() + && self.txs_in_block.is_empty() + } } #[cfg(test)] -mod test { - use super::*; +mod tests { + use std::collections::{BTreeMap, BTreeSet}; + use primitive_types::{H160, H256, U256}; use rand::rngs::mock::StepRng; use rand::Rng; - use std::collections::{BTreeMap, BTreeSet}; - const RANDOM_INCR: u64 = 734512; + + use crate::test_utils::TmpDir; + use anyhow::anyhow; + + use super::*; + + const RANDOM_INCR: u64 = 1; // TODO: replace by rand::SeedableRng implementor const MAX_SIZE: usize = 32; // Max size of test collections. - const SEED: u64 = 123; - - impl EvmState { - pub(crate) fn testing_default() -> EvmState { - EvmState { - accounts: Default::default(), - storage: Default::default(), - txs_receipts: Default::default(), - txs_in_block: Default::default(), - big_transactions: Default::default(), - logs: Default::default(), - } - } + const SEED: u64 = 1; + + #[test] + fn it_handles_my_own_expectations() { + let tmp_dir = TmpDir::new("it_handles_my_own_expectations"); + let evm_state = EvmState::load_from(&tmp_dir, 0).unwrap(); + assert_eq!(evm_state.current_slot, 0); + assert_eq!(evm_state.previous_slot, None); + assert_eq!( + evm_state + .storage + .previous_of(evm_state.current_slot) + .unwrap(), + None + ); } fn generate_account_by_seed(seed: u64) -> AccountState { @@ -310,8 +515,8 @@ mod test { for s in storage { match &s.1 { - Some(v) => state.storage.insert(*s.0, *v), - None => state.storage.remove(*s.0), + Some(v) => state.accounts_storage.insert(*s.0, *v), + None => state.accounts_storage.remove(*s.0), } } } @@ -321,12 +526,15 @@ mod test { accounts: &BTreeMap>, storage: &BTreeMap<(H160, H256), Option>, ) { - for account in accounts { - assert_eq!(state.accounts.get(account.0), account.1.as_ref()) + for (address, expected) in accounts { + assert_eq!(state.get_account(*address).as_ref(), expected.as_ref()) } - for s in storage { - assert_eq!(state.storage.get(s.0), s.1.as_ref()) + for ((address, index), expected) in storage { + assert_eq!( + state.get_storage(*address, *index).as_ref(), + expected.as_ref() + ) } } @@ -339,8 +547,16 @@ mod test { let storage_diff = to_state_diff(storage, BTreeSet::new()); let accounts_state_diff = to_state_diff(accounts_state, BTreeSet::new()); - let mut evm_state = EvmState::testing_default(); - assert_eq!(evm_state.basic(H160::random()).balance, U256::from(0)); + let tmp_dir = TmpDir::new("add_two_accounts_check_helpers"); + let mut evm_state = EvmState::load_from(tmp_dir, Default::default()).unwrap(); + + assert_eq!( + evm_state + .get_account(H160::random()) + .unwrap_or_default() + .balance, + U256::from(0) + ); save_state(&mut evm_state, &accounts_state_diff, &storage_diff); assert_state(&evm_state, &accounts_state_diff, &storage_diff); @@ -355,13 +571,15 @@ mod test { let storage_diff = to_state_diff(storage, BTreeSet::new()); let accounts_state_diff = to_state_diff(accounts_state, BTreeSet::new()); - let mut evm_state = EvmState::testing_default(); + let tmp_dir = TmpDir::new("fork_add_remove_accounts"); + let mut evm_state = EvmState::load_from(tmp_dir, Default::default()).unwrap(); + save_state(&mut evm_state, &accounts_state_diff, &storage_diff); evm_state.freeze(); assert_state(&evm_state, &accounts_state_diff, &storage_diff); - let mut new_evm_state = evm_state.try_fork().unwrap(); + let mut new_evm_state = evm_state.try_fork(1).unwrap(); assert_state(&new_evm_state, &accounts_state_diff, &storage_diff); let new_accounts = generate_accounts_addresses(SEED + 1, 2); @@ -377,4 +595,77 @@ mod test { assert_state(&new_evm_state, &new_accounts_state_diff, &BTreeMap::new()); } + + #[test] + fn reads_the_same_after_consequent_dumps() -> anyhow::Result<()> { + use std::ops::Bound::Included; + let _ = simple_logger::SimpleLogger::from_env().init(); + + const N_VERSIONS: usize = 10; + const ACCOUNTS_PER_VERSION: usize = 10; + + let accounts = generate_accounts_addresses(SEED, ACCOUNTS_PER_VERSION * N_VERSIONS); + let accounts_state = generate_accounts_state(SEED, &accounts); + let accounts_storage = generate_storage(SEED, &accounts); + + let tmp_dir = TmpDir::new("reads_the_same_after_dump"); + let mut evm_state = EvmState::load_from(tmp_dir, 0)?; + + for accounts_per_version in accounts.chunks(N_VERSIONS) { + for account in accounts_per_version { + log::debug!("working with account: {:?}", account); + evm_state + .accounts + .insert(*account, accounts_state[account].clone()); + + for (account_with_index, data) in accounts_storage.range(( + Included((*account, H256::zero())), + Included((*account, H256::repeat_byte(u8::MAX))), + )) { + evm_state + .accounts_storage + .insert(*account_with_index, *data); + } + } + + evm_state.freeze(); + + let next_slot = evm_state.current_slot + 1; + evm_state = evm_state + .try_fork(next_slot) + .expect("unable to fork evm state after freeze"); + } + + let accounts_state_diff = to_state_diff(accounts_state, BTreeSet::new()); + let accounts_storage_diff = to_state_diff(accounts_storage, BTreeSet::new()); + + assert_state(&evm_state, &accounts_state_diff, &accounts_storage_diff); + + Ok(()) + } + + #[test] + fn lookups_thru_forks() { + let _ = simple_logger::SimpleLogger::new().init(); + + let tmp_dir = TmpDir::new("lookups_thru_forks"); + let mut state = EvmState::load_from(tmp_dir, 0).unwrap(); + + let accounts = generate_accounts_addresses(SEED, 1); + let account_states = generate_accounts_state(SEED, &accounts); + + let account = accounts.first().copied().unwrap(); + let account_state = account_states[&account].clone(); + + state.accounts.insert(account, account_state.clone()); + + for _ in 0..42 { + state.freeze(); + + let next_slot = state.current_slot + 1; + state = state.try_fork(next_slot).unwrap(); + } + + assert_eq!(state.get_account(account), Some(account_state)); + } } diff --git a/evm-utils/evm-state/src/lib.rs b/evm-utils/evm-state/src/lib.rs index c868377276..aea476428b 100644 --- a/evm-utils/evm-state/src/lib.rs +++ b/evm-utils/evm-state/src/lib.rs @@ -1,5 +1,3 @@ -use std::fmt; - pub use evm::{ backend::{Apply, ApplyBackend, Backend, Log}, executor::StackExecutor, @@ -11,15 +9,23 @@ pub use primitive_types::{H256, U256}; pub use secp256k1::rand; mod error; -mod evm_backend; mod layered_backend; + pub mod transactions; -mod version_map; use error::*; pub use evm_backend::*; +pub use layered_backend::Storage; pub use layered_backend::*; pub use transactions::*; +pub use types::*; + +mod evm_backend; +mod mb_value; +mod storage; +mod types; + +use std::fmt; pub const MAX_TX_LEN: u64 = 3 * 1024 * 1024; // Limit size to 3 MB pub const TX_MTU: u64 = 920; @@ -62,24 +68,6 @@ impl Executor { gas_limit: usize, block_number: u64, ) -> Self { - //TODO: Request info from solana blockchain for vicinity - - // /// Gas price. - // pub gas_price: U256, - // /// Chain ID. - // pub chain_id: U256, - // /// Environmental block hashes. - // pub block_hashes: Vec, - // /// Environmental block number. - // pub block_number: U256, - // /// Environmental coinbase. - // pub block_coinbase: H160, - // /// Environmental block timestamp. - // pub block_timestamp: U256, - // /// Environmental block difficulty. - // pub block_difficulty: U256, - // /// Environmental block gas limit. - // pub block_gas_limit: U256, let vicinity = MemoryVicinity { block_gas_limit: gas_limit.into(), block_number: block_number.into(), @@ -169,20 +157,19 @@ impl Executor { } pub fn take_big_tx(&mut self, key: H256) -> Result, Error> { - let big_tx_storage = - if let Some(big_tx_storage) = self.evm.evm_state.big_transactions.get(&key) { - debug!("data at get = {:?}", big_tx_storage.tx_chunks); - big_tx_storage.clone() - } else { - return DataNotFound { key }.fail(); - }; + let big_tx_storage = if let Some(big_tx_storage) = self.evm.evm_state.get_big_tx(key) { + debug!("data at get = {:?}", big_tx_storage.tx_chunks); + big_tx_storage.clone() + } else { + return DataNotFound { key }.fail(); + }; self.evm.evm_state.big_transactions.remove(key); Ok(big_tx_storage.tx_chunks) } pub fn allocate_store(&mut self, key: H256, size: u64) -> Result<(), Error> { - if self.evm.evm_state.big_transactions.get(&key).is_some() || size > MAX_TX_LEN { + if self.evm.evm_state.get_big_tx(key).is_some() || size > MAX_TX_LEN { error!("Double allocation for key = {:?}", key); return AllocationError { key, size }.fail(); }; @@ -200,24 +187,23 @@ impl Executor { } pub fn publish_data(&mut self, key: H256, offset: u64, data: &[u8]) -> Result<(), Error> { - let mut big_tx_storage = - if let Some(big_tx_storage) = self.evm.evm_state.big_transactions.get(&key) { - let max_len = big_tx_storage.tx_chunks.len() as u64; - let data_end = offset.saturating_add(data.len() as u64); - // check offset to avoid integer overflow - if data_end > max_len { - return OutOfBound { - key, - offset, - size: max_len, - } - .fail(); + let mut big_tx_storage = if let Some(big_tx_storage) = self.evm.evm_state.get_big_tx(key) { + let max_len = big_tx_storage.tx_chunks.len() as u64; + let data_end = offset.saturating_add(data.len() as u64); + // check offset to avoid integer overflow + if data_end > max_len { + return OutOfBound { + key, + offset, + size: max_len, } - big_tx_storage.clone() - } else { - error!("Failed to write without allocation = {:?}", key); - return FailedToWrite { key, offset }.fail(); - }; + .fail(); + } + big_tx_storage.clone() + } else { + error!("Failed to write without allocation = {:?}", key); + return FailedToWrite { key, offset }.fail(); + }; let offset = offset as usize; big_tx_storage.tx_chunks[offset..offset + data.len()].copy_from_slice(data); @@ -243,22 +229,20 @@ impl Executor { let block_num = self.evm.tx_info.block_number.as_u64(); let tx_hash = tx.signing_hash(); - log::debug!("Register tx in evm block={}, tx= {}", block_num, tx_hash); - //TODO: replace by entry api - let updated_vec = match self.evm.evm_state.txs_in_block.get(&block_num) { - None => vec![tx_hash], - Some(v) => { - let mut v = v.clone(); - v.push(tx_hash); - v - } - }; + debug!("Register tx in evm block={}, tx= {}", block_num, tx_hash); + // TODO: replace by Entry-like api + let mut hashes = self + .evm + .evm_state + .get_txs_in_block(block_num) + .unwrap_or_default(); + hashes.push(tx_hash); - let index = updated_vec.len() as u64; + let index = hashes.len() as u64; self.evm .evm_state .txs_in_block - .insert(block_num, updated_vec); + .insert(block_num, hashes.into()); let tx_receipt = TransactionReceipt::new( tx, @@ -280,17 +264,22 @@ pub const HELLO_WORLD_CODE:&str = "608060405234801561001057600080fd5b5061011e806 pub const HELLO_WORLD_ABI: &str = "942ae0a7"; pub const HELLO_WORLD_RESULT:&str = "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a68656c6c6f576f726c6400000000000000000000000000000000000000000000"; pub const HELLO_WORLD_CODE_SAVED:&str = "6080604052348015600f57600080fd5b506004361060285760003560e01c8063942ae0a714602d575b600080fd5b603360ab565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101560715780820151818401526020810190506058565b50505050905090810190601f168015609d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60606040518060400160405280600a81526020017f68656c6c6f576f726c640000000000000000000000000000000000000000000081525090509056fea2646970667358221220fa787b95ca91ffe90fdb780b8ee8cb11c474bc63cb8217112c88bc465f7ea7d364736f6c63430007020033"; + #[cfg(test)] -pub mod tests { +mod test_utils; - use super::Executor; - use super::*; - use assert_matches::assert_matches; - use evm::{Capture, CreateScheme, ExitReason, ExitSucceed, Handler}; +#[cfg(test)] +mod tests { + use anyhow::anyhow; + use evm::{Capture, CreateScheme, ExitReason, ExitSucceed, Handler}; use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; - use std::sync::RwLock; + + use crate::test_utils::TmpDir; + + use super::Executor; + use super::*; fn name_to_key(name: &str) -> H160 { let hash = H256::from_slice(Keccak256::digest(name.as_bytes()).as_slice()); @@ -299,36 +288,27 @@ pub mod tests { #[test] fn test_evm_bytecode() { - let _ = simple_logger::SimpleLogger::new().init(); + let _logger_error = simple_logger::SimpleLogger::new().init(); let accounts = ["contract", "caller"]; - let code = hex::decode(HELLO_WORLD_CODE).unwrap(); - let data = hex::decode(HELLO_WORLD_ABI).unwrap(); - - let backend = EvmState::new(); - let backend = RwLock::new(backend); + let code = hex::decode(HELLO_WORLD_CODE)?; + let data = hex::decode(HELLO_WORLD_ABI)?; - { - let mut state = backend.write().unwrap(); + let tmp_dir = TmpDir::new("test_evm_bytecode"); + let mut backend = EvmState::load_from(tmp_dir, Slot::default())?; - for acc in &accounts { - let account = name_to_key(acc); - let memory = AccountState { - ..Default::default() - }; - state.accounts.insert(account, memory); - } + for acc in &accounts { + let account = name_to_key(acc); + let memory = AccountState { + ..Default::default() + }; + backend.accounts.insert(account, memory); } - backend.write().unwrap().freeze(); + backend.freeze(); let config = evm::Config::istanbul(); - let mut executor = Executor::with_config( - backend.read().unwrap().try_fork().unwrap(), - config, - usize::max_value(), - 0, - ); + let mut executor = Executor::with_config(backend.clone(), config, usize::max_value(), 0); let exit_reason = match executor.with_executor(|e| { e.create( @@ -343,7 +323,10 @@ pub mod tests { Capture::Trap(_) => unreachable!(), }; - assert_matches!(exit_reason, (ExitReason::Succeed(ExitSucceed::Returned), _)); + assert!(matches!( + exit_reason, + (ExitReason::Succeed(ExitSucceed::Returned), _) + )); let exit_reason = executor.with_executor(|e| { e.transact_call( name_to_key("contract"), @@ -354,21 +337,21 @@ pub mod tests { ) }); - let result = hex::decode(HELLO_WORLD_RESULT).unwrap(); + let result = hex::decode(HELLO_WORLD_RESULT)?; match exit_reason { (ExitReason::Succeed(ExitSucceed::Returned), res) if res == result => {} any_other => panic!("Not expected result={:?}", any_other), } let patch = executor.deconstruct(); - backend.write().unwrap().swap_commit(patch); + backend.swap_commit(patch); - let mutex_lock = backend.read().unwrap(); - let contract = mutex_lock.accounts.get(&name_to_key("contract")); + let contract = backend.get_account(name_to_key("contract")); assert_eq!( &contract.unwrap().code, &hex::decode(HELLO_WORLD_CODE_SAVED).unwrap() ); + Ok(()) } #[test] diff --git a/evm-utils/evm-state/src/mb_value.rs b/evm-utils/evm-state/src/mb_value.rs new file mode 100644 index 0000000000..72786ba9cc --- /dev/null +++ b/evm-utils/evm-state/src/mb_value.rs @@ -0,0 +1,44 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum MaybeValue { + /// Value exist, and was changed. + Value(V), + /// Value was removed. + Removed, +} + +use MaybeValue::*; + +impl MaybeValue { + pub fn by_ref(&self) -> MaybeValue<&V> { + match self { + Value(ref v) => Value(v), + Removed => Removed, + } + } +} + +impl From for MaybeValue { + fn from(value: T) -> Self { + Value(value) + } +} + +impl From> for MaybeValue { + fn from(value: Option) -> Self { + match value { + Some(value) => Value(value), + None => Removed, + } + } +} + +impl From> for Option { + fn from(mb_value: MaybeValue) -> Option { + match mb_value { + Value(value) => Some(value), + Removed => None, + } + } +} diff --git a/evm-utils/evm-state/src/storage.rs b/evm-utils/evm-state/src/storage.rs new file mode 100644 index 0000000000..5b3d9a0d1f --- /dev/null +++ b/evm-utils/evm-state/src/storage.rs @@ -0,0 +1,1080 @@ +use std::{ + array::TryFromSliceError, + convert::{TryFrom, TryInto}, + fmt::{self, Debug, Display}, + fs, + io::Cursor, + marker::PhantomData, + mem::size_of, + ops::{Deref, Sub}, + path::Path, + sync::{Arc, RwLock}, +}; + +use bincode::config::{BigEndian, DefaultOptions, Options as _, WithOtherEndian}; +use lazy_static::lazy_static; +use log::*; +use rocksdb::{ + self, + backup::{BackupEngine, BackupEngineOptions, RestoreOptions}, + ColumnFamily, ColumnFamilyDescriptor, IteratorMode, Options, DB, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +use crate::mb_value::MaybeValue; + +pub type Result = std::result::Result; +pub type StdResult = std::result::Result; + +type BincodeOpts = WithOtherEndian; +lazy_static! { + static ref CODER: BincodeOpts = DefaultOptions::new().with_big_endian(); +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + DatabaseErr(#[from] rocksdb::Error), + #[error("Type {1} :: {0}")] + BincodeErr(bincode::Error, &'static str), + #[error("Unable to construct key from bytes")] + KeyErr(#[from] TryFromSliceError), +} + +pub struct VersionedStorage { + db: Arc, + squash_guard: Arc>, + _version: PhantomData, +} + +impl Clone for VersionedStorage { + fn clone(&self) -> Self { + Self { + db: Arc::clone(&self.db), + squash_guard: Arc::clone(&self.squash_guard), + _version: PhantomData, + } + } +} + +type Previous = Option; // TODO: Vec + +trait BincodeResultExt { + fn typed_ctx(self) -> Result; +} + +impl BincodeResultExt for StdResult { + fn typed_ctx(self) -> Result { + self.map_err(|err| Error::BincodeErr(err, std::any::type_name::())) + } +} + +impl VersionedStorage +where + V: Copy + Serialize + DeserializeOwned, + Previous: Serialize + DeserializeOwned, +{ + pub fn is_exists(&self, version: V) -> Result { + let key = CODER.serialize(&version).typed_ctx()?; + Ok(self.db.get_pinned(key)?.is_some()) + } + + pub fn previous_of(&self, version: V) -> Result> { + let version = CODER.serialize(&version).typed_ctx()?; + + if let Some(bytes) = self.db.get_pinned(version)? { + let previous = CODER.deserialize::>(&bytes).typed_ctx()?; + Ok(previous) + } else { + Ok(None) + } + } + + pub fn versions(&self) -> impl Iterator)> + '_ { + self.db + .iterator(IteratorMode::End) + .map(move |(key, value)| { + let version = CODER.deserialize(&key).unwrap_or_else(|err| { + panic!("Unable to deserialize version from {:?}: {:?}", key, err) + }); + let previous = CODER.deserialize(&value).unwrap_or_else(|err| { + panic!("Unable to deserialize previous from {:?}: {:?}", value, err) + }); + (version, previous) + }) + } + + pub fn new_version(&self, version: V, previous: Previous) -> Result<()> + where + V: PartialEq + Debug, + { + assert_ne!(Some(version), previous); + let key = CODER.serialize(&version).typed_ctx()?; + + match self.db.get_pinned(&key)? { + None => { + let value = CODER.serialize(&previous).typed_ctx()?; + self.db.put(key, value)?; + } + Some(data) => { + warn!("Found confict previous: previous = {:?}, version = {:?}, previous_in_db = {:?}", previous, + version, + CODER.deserialize::>(&data).typed_ctx() ); + // TODO: assert or do some check + // assert_eq!( + // previous, + // CODER.deserialize(&data).typed_ctx()?, + // "attempt to insert for {:?}", + // version + // ); + } + } + + Ok(()) + } +} + +impl VersionedStorage { + pub fn save_into>(&self, path: P) -> Result<()> { + let path = path.as_ref(); + assert!( + path.is_dir() && path.exists(), + "storage can be saved only into some existing directory" + ); + info!( + "saving storage data into {} (as new backup)", + path.display() + ); + let mut engine = BackupEngine::open(&BackupEngineOptions::default(), path)?; + engine.create_new_backup_flush(self.db.as_ref(), true)?; + Ok(()) + } + + pub fn restore_from, P2: AsRef>(path: P1, target: P2) -> Result<()> { + let path = path.as_ref(); + let target = target.as_ref(); + + // TODO: check target dir is empty or doesn't exists at all + fs::create_dir_all(target).expect("Unable to create target dir"); + + assert!( + path.is_dir() && path.exists(), + "storage can be loaded only from existing directory" + ); + info!( + "loading storage data from {} into {} (restore from backup)", + path.display(), + target.display() + ); + let mut engine = BackupEngine::open(&BackupEngineOptions::default(), path)?; + assert!( + target.is_dir(), + "loaded storage data must lays in target dir" + ); + engine.restore_from_latest_backup(&target, &target, &RestoreOptions::default())?; + + Ok(()) + } +} + +pub trait PersistentAssoc { + const COLUMN_NAME: &'static str; + type Key: Serialize + DeserializeOwned; + type Value: Serialize + DeserializeOwned; +} + +#[macro_export] +macro_rules! persistent_types { + ($($Marker:ident in $Column:expr => $Key:ty : $Value:ty,)+) => { + const COLUMN_NAMES: &[&'static str] = &[$($Column),+]; + + $( + pub(crate) enum $Marker {} + impl PersistentAssoc for $Marker { + const COLUMN_NAME: &'static str = $Column; + type Key = $Key; + type Value = $Value; + } + )+ + }; + ($($Marker:ident in $Column:expr => $Key:ty : $Value:ty),+) => { + persistent_types! { $($Marker in $Column => $Key : $Value,)+ } + } +} + +impl VersionedStorage { + pub fn open, S: AsRef>( + path: P, + column_names: impl IntoIterator, + ) -> Result + where + V: AsBytePrefix, + { + let db_opts = default_db_opts(); + + let descriptors = column_names + .into_iter() + .map(|type_name| { + let mut cf_opts = Options::default(); + cf_opts.set_prefix_extractor(rocksdb::SliceTransform::create_fixed_prefix(V::SIZE)); + ColumnFamilyDescriptor::new(type_name.as_ref(), cf_opts) + }) + .collect::>(); + + let db = Arc::new(DB::open_cf_descriptors(&db_opts, &path, descriptors)?); + let squash_guard = Arc::new(RwLock::default()); + + Ok(Self { + db, + squash_guard, + _version: PhantomData, + }) + } + + pub fn typed(&self) -> PersistentMap<'_, V, M> { + assert!(self.db.cf_handle(M::COLUMN_NAME).is_some()); + + PersistentMap { + storage: &self, + _marker: PhantomData, + } + } +} + +pub struct PersistentMap<'a, V, M: PersistentAssoc> { + // TODO: revise this ref type + storage: &'a VersionedStorage, + _marker: PhantomData, +} + +impl<'a, V, M: PersistentAssoc> Deref for PersistentMap<'a, V, M> { + type Target = VersionedStorage; + fn deref(&self) -> &Self::Target { + &self.storage + } +} + +pub trait AsBytePrefix { + const SIZE: usize; + + type Bytes: AsRef<[u8]>; + fn to_bytes(&self) -> Self::Bytes; + + type FromBytesError; + fn from_bytes(_: &[u8]) -> StdResult + where + Self: Sized; +} + +#[derive(Serialize, Deserialize)] +struct VersionedKey { + version: V, + key: Key, +} + +impl TryInto> for VersionedKey +where + V: AsBytePrefix, + Key: Serialize, +{ + type Error = Error; + + fn try_into(self) -> StdResult, Self::Error> { + let mut bytes = Vec::from(self.version.to_bytes().as_ref()); + let mut cursor = Cursor::new(&mut bytes); + cursor.set_position(::SIZE as u64); + bincode::serialize_into(&mut cursor, &self.key).typed_ctx()?; + Ok(bytes) + } +} + +impl VersionedKey +where + V: AsBytePrefix, +{ + #[allow(dead_code)] + fn version_of(bytes: &[u8]) -> Result + where + Error: From, + { + V::from_bytes(bytes).map_err(Error::from) + } + + fn key_from(bytes: &[u8]) -> Result + where + Key: DeserializeOwned, + { + bincode::deserialize_from(&bytes[::SIZE..]).typed_ctx() + } +} + +impl<'a, V, M: PersistentAssoc> PersistentMap<'a, V, M> { + fn db(&self) -> &DB { + self.storage.db.as_ref() + } + + fn cf(&self) -> &ColumnFamily { + self.db() + .cf_handle(M::COLUMN_NAME) + .unwrap_or_else(|| panic!("Missed Column Family '{}'", M::COLUMN_NAME)) + } +} + +mod track { + use std::fmt::{self, Display}; + use std::ops::{Range, Sub}; + + #[derive(Debug, Clone)] + enum RevTrack { + Single(V), + Sequence(std::ops::Range), + } + + use RevTrack::*; + + impl RevTrack { + fn single(v: V) -> Self { + Self::Single(v) + } + + fn is_prev(&self, other: V) -> bool + where + V: Copy + Sub + PartialEq + Stepped, + { + (match self { + Single(v) => *v, + Sequence(range) => range.start, + }) - other + == V::ONE + } + + fn prepend(&mut self, prev: V) + where + V: Copy, + { + match self { + Single(v) => { + *self = Sequence(Range { + start: prev, + end: *v, + }) + } + Sequence(range) => range.start = prev, + } + } + } + + pub(super) struct Checked(Vec>); + + impl Default for Checked { + fn default() -> Self { + Self(vec![]) + } + } + + impl Checked { + pub fn prepend(&mut self, prev: V) + where + V: Copy + Sub + PartialEq + Stepped, + { + match self.0.last_mut() { + Some(ref mut last) if last.is_prev(prev) => last.prepend(prev), + Some(_) | None => self.0.push(Single(prev)), + } + } + } + + impl Display for RevTrack { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Single(v) => write!(f, "{}", v), + Sequence(range) => write!(f, "{}..{}", range.end, range.start), + } + } + } + + impl Display for Checked { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[")?; + let mut iter = self.0.iter().peekable(); + while let Some(track) = iter.next() { + write!(f, "{}", track)?; + if iter.peek().is_some() { + write!(f, ", ")?; + } + } + write!(f, "]")?; + Ok(()) + } + } + + #[test] + fn it_prints_tracks_as_expected() { + impl Checked { + fn assert_display(&self, s: &str) { + assert_eq!(format!("{}", self), s); + } + } + let mut c = Checked::::default(); + c.assert_display("[]"); + c.prepend(42); + c.assert_display("[42]"); + c.prepend(19); + c.assert_display("[42, 19]"); + c.prepend(18); + c.assert_display("[42, 19..18]"); + c.prepend(17); + c.assert_display("[42, 19..17]"); + c.prepend(15); + c.prepend(14); + c.prepend(13); + c.assert_display("[42, 19..17, 15..13]"); + } + + pub trait Stepped { + const ONE: Self; + } + + macro_rules! nums_as_stepped { + ($($ty:ty),+) => { + $( + impl Stepped for $ty { + const ONE: $ty = 1; + } + )+ + } + } + + nums_as_stepped! { + u8, u16, u32, u64, u128 + } +} + +impl<'a, V, M: PersistentAssoc> PersistentMap<'a, V, M> +where + V: Copy + AsBytePrefix + Serialize + DeserializeOwned, + M::Key: Copy, +{ + pub fn insert_with(&self, version: V, key: M::Key, value: MaybeValue) -> Result<()> { + let versioned_key: Vec = VersionedKey { version, key }.try_into()?; + let value = CODER.serialize(&value).typed_ctx()?; + self.db().put_cf(self.cf(), versioned_key, value)?; + Ok(()) + } + + pub fn get_for(&self, version: V, key: M::Key) -> Result>> + where + V: Display + Debug + PartialEq + Sub + track::Stepped, + M::Key: Debug, + M::Value: Debug, + { + let _guard = self + .storage + .squash_guard + .read() + .expect("squash guard was poisoned"); + + let mut next_version = Some(version); + + let mut track = track::Checked::::default(); + + while let Some(version) = next_version.take() { + track.prepend(version); + let value = self.get_exact_for(version, key)?; + if value.is_some() { + debug!( + "get_for: key {:?} found with track {}: value {:?}", + key, track, &value + ); + return Ok(value); + } else { + let previous = self.storage.previous_of(version)?; + assert_ne!(Some(version), previous); + next_version = previous; + continue; + } + } + + debug!("get_for: key {:?} not found {}", key, track); + + Ok(None) + } + + fn get_exact_for(&self, version: V, key: M::Key) -> Result>> { + let versioned_key: Vec = VersionedKey { version, key }.try_into()?; + let bytes = self.db().get_pinned_cf(self.cf(), versioned_key)?; + let mb_value = bytes + .map(|bytes| { + CODER + .deserialize::>(&bytes) + .typed_ctx() + }) + .transpose()?; + Ok(mb_value) + } + + pub fn prefix_iter_for( + &self, + version: V, + ) -> Result)> + '_> { + Ok(self + .db() + .prefix_iterator_cf(self.cf(), version.to_bytes()) + .map(move |(key, value)| { + let key = VersionedKey::::key_from(&key).unwrap_or_else(|err| { + panic!("Unable to deserialize key from {:?}: {:?}", key, err) + }); + let value = CODER.deserialize(&value).unwrap_or_else(|err| { + panic!("Unable to deserialize value from {:?}: {:?}", value, err) + }); + (key, value) + })) + } + + fn has_value_for(&self, version: V, key: M::Key) -> Result { + let versioned_key: Vec = VersionedKey { version, key }.try_into()?; + let data_ref = self.db().get_pinned_cf(self.cf(), versioned_key)?; + Ok(data_ref.is_some()) + } + + pub fn squash_into_rev_pass(&self, target: V) -> Result<()> + where + Previous: DeserializeOwned, + M::Key: HasMax, + { + let _guard = self + .storage + .squash_guard + .write() + .expect("squash guard was poisoned"); + + let mut track = vec![target]; + while let Some(prev) = self.storage.previous_of(track[track.len() - 1])? { + track.push(prev); + } + + let mut rev_track = track.into_iter().rev().peekable(); + while let (Some(current), Some(parent)) = (rev_track.next(), rev_track.peek().copied()) { + for (key, value) in self.prefix_iter_for(parent)? { + if !self.has_value_for(current, key)? { + self.insert_with(current, key, value)?; + } + } + + self.delete_all_for(parent)?; + + // TODO: cleanup all None's + } + + Ok(()) + } + + pub fn squash_into_tracing(&self, target: V) -> Result<()> + where + Previous: DeserializeOwned, + M::Key: HasMax, + { + let _guard = self + .storage + .squash_guard + .write() + .expect("squash guard was poisoned"); + + let mut next_parent_for = target; + + while let Some(parent) = self.storage.previous_of(next_parent_for)? { + for (key, value) in self.prefix_iter_for(parent)? { + if !self.has_value_for(target, key)? { + self.insert_with(target, key, value)?; + } + } + + self.delete_all_for(parent)?; + + next_parent_for = parent; + } + + Ok(()) + } + + fn delete_all_for(&self, version: V) -> Result<()> + where + M::Key: HasMax, + { + let version_prefix: Vec = version.to_bytes().as_ref().iter().copied().collect(); + let key_max_bytes = bincode::serialized_size(&M::Key::MAX) + .expect("Unable to calculate serialized len") as usize + + 1; + let mut lexicographic_max: Vec = version_prefix.clone(); + lexicographic_max.extend(std::iter::repeat(0u8).take(key_max_bytes)); + self.db() + .delete_range_cf(self.cf(), version_prefix, lexicographic_max)?; + Ok(()) + } +} + +pub trait HasMax { + const MAX: Self; +} + +impl<'a, V, Value, M: PersistentAssoc> PersistentMap<'a, V, M> +where + V: Copy + AsBytePrefix + Serialize + DeserializeOwned, + Value: Serialize + DeserializeOwned, +{ + // TODO: previous versions as argument + pub fn insert(&self, version: V, value: Value) -> Result<()> + where + V: std::fmt::Debug + PartialEq, + { + self.insert_with(version, (), value.into())?; + self.storage.new_version(version, None)?; + Ok(()) + } + + pub fn get(&self, version: V) -> Result>> { + self.get_exact_for(version, ()) + } + + pub fn keys(&self) -> impl Iterator + '_ { + self.storage.versions().map(|(version, _)| version) + } +} + +pub fn default_db_opts() -> Options { + let mut opts = Options::default(); + opts.create_if_missing(true); + opts.create_missing_column_families(true); + opts +} + +macro_rules! nums_as_byte_prefixes { + ($($ty:ty),+) => { + $( + impl AsBytePrefix for $ty { + const SIZE: usize = size_of::<$ty>(); + + type Bytes = [u8; size_of::<$ty>()]; + + fn to_bytes(&self) -> Self::Bytes { + self.to_be_bytes() + } + + type FromBytesError = TryFromSliceError; + + fn from_bytes(bytes: &[u8]) -> StdResult<$ty, Self::FromBytesError> { + Self::Bytes::try_from(bytes) + .map(<$ty>::from_be_bytes) + } + } + )+ + } +} + +nums_as_byte_prefixes! { + u8, u16, u32, u64, u128 +} + +macro_rules! nums_has_max { + ($($ty:ty),+) => { + $( + impl HasMax for $ty { + const MAX: $ty = <$ty>::MAX; + } + )+ + } +} + +nums_has_max! { + u8, u16, u32, u64, u128 +} + +macro_rules! primitive_type_has_max { + ($($ty:ty),+) => { + $( + impl HasMax for $ty { + const MAX: $ty = <$ty>::repeat_byte(u8::MAX); + } + )+ + } + +} + +use primitive_types::{H160, H256, H512}; + +primitive_type_has_max! { + H160, H256, H512 +} + +impl Debug for VersionedStorage +where + V: 'static, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("VersionedStorage") + .field("Version", &std::any::type_name::()) + .field("database", &self.db.path().display()) + .finish() + } +} + +impl<'a, V, M: PersistentAssoc> fmt::Debug for PersistentMap<'a, V, M> +where + V: 'static, + M::Key: 'static, + M::Value: 'static, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use std::any::TypeId; + + f.debug_struct("Storage") + .field("Version", &TypeId::of::()) + .field("Key", &TypeId::of::()) + .field("Value", &TypeId::of::()) + .field("database", &self.db().path().display()) + .field("column", &M::COLUMN_NAME) + .finish() + } +} + +#[cfg(test)] +#[allow(clippy::blacklisted_name)] +mod tests { + use std::collections::{BTreeMap, HashMap}; + use std::thread::{self, JoinHandle}; + + use quickcheck_macros::quickcheck; + + use crate::test_utils::TmpDir; + + use super::*; + + impl AsBytePrefix for () { + const SIZE: usize = 0; + type Bytes = [u8; 0]; + fn to_bytes(&self) -> Self::Bytes { + [] + } + + type FromBytesError = TryFromSliceError; + fn from_bytes(bytes: &[u8]) -> StdResult { + Self::Bytes::try_from(bytes).map(|_| ()) + } + } + + impl AsBytePrefix for (A, B) + where + A: AsBytePrefix, + B: AsBytePrefix, + TryFromSliceError: From, + TryFromSliceError: From, + { + const SIZE: usize = A::SIZE + B::SIZE; + type Bytes = Vec; + fn to_bytes(&self) -> Self::Bytes { + let mut bytes = Vec::with_capacity(Self::SIZE); + bytes.extend(self.0.to_bytes().as_ref()); + bytes.extend(self.1.to_bytes().as_ref()); + bytes + } + + type FromBytesError = TryFromSliceError; + fn from_bytes(bytes: &[u8]) -> StdResult<(A, B), Self::FromBytesError> { + let a = A::from_bytes(&bytes[..A::SIZE])?; + let b = B::from_bytes(&bytes[A::SIZE..(A::SIZE + B::SIZE)])?; + Ok((a, b)) + } + } + + #[test] + fn it_handles_versions_as_expected() -> Result<()> { + persistent_types! { KV in "kv" => u64 : usize } // TODO: rm, can be any + let dir = TmpDir::new("it_handles_versions_as_expected"); + let s = VersionedStorage::::open(&dir, COLUMN_NAMES)?; + assert_eq!(s.previous_of(0)?, None); + Ok(()) + } + + #[test] + fn it_handles_full_range_mapping() -> Result<()> { + use rand::Rng; + + type Version = u8; + type Key = u8; + type Value = u64; + type Assoc = HashMap>; + type Storage = VersionedStorage; + persistent_types! { KV in "kv" => Key : Value } + + let mut rng = rand::thread_rng(); + + let mut assoc: Assoc = (0..=Version::MAX) + .map(|version| { + ( + version, + (0..=Key::MAX).map(|key| (key, rng.gen())).collect(), + ) + }) + .collect(); + println!("assoc is ready"); + + let dir = TmpDir::new("it_handles_full_range_mapping"); + { + let s = Storage::open(&dir, COLUMN_NAMES)?; + + for (&version, map) in &assoc { + for (&key, &value) in map { + s.typed::().insert_with(version, key, value.into())?; + } + s.new_version(version, None)?; + } + s.db.flush()?; + println!("assoc is stored"); + } + { + let s = Storage::open(&dir, COLUMN_NAMES)?; + for (version, _) in s.versions() { + let new_map = s + .typed::() + .prefix_iter_for(version)? + .map(|(key, mb_value)| (key, Option::from(mb_value).unwrap())) + .collect::>(); + assert_eq!(assoc.remove(&version), Some(new_map)); + } + assert!(assoc.is_empty()); + } + Ok(()) + } + + #[quickcheck] + fn qc_version_precedes_key_in_serialized_data(version: u64, key: u64) -> Result<()> { + let version_data = CODER.serialize(&version).typed_ctx()?; + + let versioned_key = VersionedKey { version, key }; + let versioned_key_data = CODER.serialize(&versioned_key).typed_ctx()?; + assert!(versioned_key_data.starts_with(&version_data)); + Ok(()) + } + + macro_rules! pair_works_as_byte_prefix { + ($foo:ty, $bar:ty) => { + paste::item! { + mod [< $foo _ $bar _ as_version >] { + use super::*; + + type Pair = ($foo, $bar); + type Key = Vec; + type ComplexKey = VersionedKey; + + #[quickcheck] + fn [< qc_pair_of _ $foo _ $bar _works_as_byte_prefix >](version: Pair, key: Key) -> Result<()> { + assert_eq!(version.to_bytes().len(), Pair::SIZE); + + let data: Vec = ComplexKey { version, key: key.clone() }.try_into()?; + + assert_eq!(ComplexKey::version_of(&data)?, version); + assert_eq!(ComplexKey::key_from(&data)?, key); + Ok(()) + } + } + } + }; + } + + // TODO: recursive + // pairs_works_as_byte_prefix! { + // @foo: u8, u16, u32, u64, u128, + // @bar: u8, u16, u32, u64, u128, + // } + pair_works_as_byte_prefix!(u8, u16); + pair_works_as_byte_prefix!(u8, u64); + pair_works_as_byte_prefix!(u64, u16); + + type K = u16; + type V = u64; + type ThreadId = u64; + + #[quickcheck] + fn qc_keyless_storage_behaves_like_a_map_with_version_as_key( + map: BTreeMap, + ) -> Result<()> { + type Storage = VersionedStorage; + persistent_types! { KV in "kv" => () : V } + let dir = TmpDir::new("qc_keyless_storage_behaves_like_a_map_with_version_as_key"); + { + let s = Storage::open(&dir, COLUMN_NAMES)?; + for (&k, &v) in &map { + s.typed::().insert(k, v)?; + } + s.db.flush()?; + } + { + let s = Storage::open(&dir, COLUMN_NAMES)?; + let mut new_map = BTreeMap::new(); + for key in s.typed::().keys() { + new_map.insert( + key, + s.typed::().get(key)?.and_then(Option::from).unwrap(), + ); + } + assert_eq!(map, new_map); + } + Ok(()) + } + + #[quickcheck] + fn qc_two_concurrent_threads_works_on_shared_db_via_version_assoc( + (foo, bar): (BTreeMap, BTreeMap), + ) -> Result<()> { + type Storage = VersionedStorage<(ThreadId, K)>; + persistent_types! { TKV in "tkv" => () : V } + let dir = TmpDir::new("qc_two_concurrent_threads_works_on_shared_db_via_version_assoc"); + let s: Arc = Arc::new(Storage::open(&dir, COLUMN_NAMES)?); + + fn spawn_insert(s: &Arc, map: BTreeMap) -> JoinHandle> { + let s = Arc::clone(s); + thread::spawn(move || -> Result { + let id = thread_id(); + for (k, v) in map { + s.typed::().insert((id, k), v)?; + } + s.db.flush()?; + Ok(id) + }) + } + + let foo_wh = spawn_insert(&s, foo.clone()); + let bar_wh = spawn_insert(&s, bar.clone()); + + let foo_id = foo_wh.join().unwrap()?; + let bar_id = bar_wh.join().unwrap()?; + + fn spawn_read( + s: &Arc, + id: ThreadId, + keys: impl IntoIterator + Send + 'static, + ) -> JoinHandle>> { + let s = Arc::clone(s); + thread::spawn(move || -> Result> { + keys.into_iter() + .map(|key| { + s.typed::() + .get((id, key)) + .map(|value| (key, value.and_then(Option::from).unwrap())) + }) + .collect::>() + }) + } + + let foo_rh = spawn_read(&s, foo_id, foo.keys().copied().collect::>()); + let bar_rh = spawn_read(&s, bar_id, bar.keys().copied().collect::>()); + + let new_foo = foo_rh.join().unwrap()?; + let new_bar = bar_rh.join().unwrap()?; + + assert_eq!(foo, new_foo); + assert_eq!(bar, new_bar); + + Ok(()) + } + + #[quickcheck] + fn qc_two_concurrent_threads_works_on_shared_db_via_own_version_slot( + (foo, bar): (BTreeMap, BTreeMap), + ) -> Result<()> { + type Storage = VersionedStorage; + persistent_types! { KV in "kv" => K : V } + let dir = TmpDir::new("qc_two_concurrent_threads_works_on_shared_db_via_own_version_slot"); + let s = Arc::new(Storage::open(&dir, COLUMN_NAMES)?); + + fn spawn_insert(s: &Arc, map: BTreeMap) -> JoinHandle> { + let s = Arc::clone(s); + thread::spawn(move || -> Result { + let id = thread_id(); + for (k, v) in map { + s.typed::().insert_with(id, k, v.into())?; + } + s.new_version(id, None)?; + s.db.flush()?; + Ok(id) + }) + } + + let foo_wh = spawn_insert(&s, foo.clone()); + let bar_wh = spawn_insert(&s, bar.clone()); + + let foo_id = foo_wh.join().unwrap()?; + let bar_id = bar_wh.join().unwrap()?; + + fn spawn_read( + s: &Arc, + id: ThreadId, + keys: impl IntoIterator + Send + 'static, + ) -> JoinHandle>> { + let s = Arc::clone(s); + thread::spawn(move || -> Result> { + keys.into_iter() + .map(|key| { + s.typed::() + .get_exact_for(id, key) + .map(|value| (key, value.and_then(Option::from).unwrap())) + }) + .collect::>() + }) + } + + let foo_rh = spawn_read(&s, foo_id, foo.keys().copied().collect::>()); + let bar_rh = spawn_read(&s, bar_id, bar.keys().copied().collect::>()); + + let new_foo = foo_rh.join().unwrap()?; + let new_bar = bar_rh.join().unwrap()?; + + assert_eq!(foo, new_foo); + assert_eq!(bar, new_bar); + + Ok(()) + } + + #[quickcheck] + fn qc_reads_the_same_as_inserts(assoc: HashMap>>) -> Result<()> { + type Storage = VersionedStorage; + persistent_types! { KV in "kv" => K : V } + let dir = TmpDir::new("qc_reads_the_same_as_inserts"); + + { + let s = Storage::open(&dir, COLUMN_NAMES)?; + for (&version, map) in &assoc { + for (&k, &v) in map { + s.typed::().insert_with(version, k, v.into())?; + } + s.new_version(version, None)?; + } + s.db.flush()?; + } + { + let s = Storage::open(&dir, COLUMN_NAMES)?; + + let mut new_assoc = HashMap::>>::new(); + for (version, _) in s.versions() { + new_assoc.insert( + version, + s.typed::() + .prefix_iter_for(version) + .unwrap() + .map(|(key, mb_value)| (key, Option::from(mb_value))) + .collect(), + ); + } + + assert_eq!(assoc, new_assoc); + } + Ok(()) + } + + fn thread_id() -> u64 { + use std::hash::{Hash, Hasher}; + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + std::thread::current().id().hash(&mut hasher); + hasher.finish() + } +} diff --git a/evm-utils/evm-state/src/test_utils.rs b/evm-utils/evm-state/src/test_utils.rs new file mode 100644 index 0000000000..5948eb98f2 --- /dev/null +++ b/evm-utils/evm-state/src/test_utils.rs @@ -0,0 +1,40 @@ +use std::{ + env, fs, + path::{Path, PathBuf}, +}; + +#[derive(Clone)] +pub struct TmpDir(PathBuf); + +impl TmpDir { + pub fn new>(sub_dir: P) -> Self { + let path = env::temp_dir().join(sub_dir); + let pprint = path.as_path().display(); + if path.exists() { + // panic!("Path is {} already exists", pprint); + fs::remove_dir_all(&path).expect("Unable to cleanup existing dir"); + } + fs::create_dir(&path) + .unwrap_or_else(|err| panic!("Unable to create tmp dir {}: {:?}", pprint, err)); + println!("{}", pprint); + Self(path) + } +} + +impl Drop for TmpDir { + fn drop(&mut self) { + fs::remove_dir_all(self.0.as_path()).unwrap_or_else(|err| { + panic!( + "Unable to remove tmp dir {}: {:?}", + self.0.as_path().display(), + err + ) + }); + } +} + +impl AsRef for TmpDir { + fn as_ref(&self) -> &Path { + self.0.as_path() + } +} diff --git a/evm-utils/evm-state/src/transactions.rs b/evm-utils/evm-state/src/transactions.rs index 057b16e09a..48c2a21e55 100644 --- a/evm-utils/evm-state/src/transactions.rs +++ b/evm-utils/evm-state/src/transactions.rs @@ -290,8 +290,9 @@ impl From for UnsignedTransaction { } } } + // TODO: Work on logs and state_root. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct TransactionReceipt { pub transaction: Transaction, pub status: evm::ExitReason, @@ -302,6 +303,7 @@ pub struct TransactionReceipt { // pub logs_bloom: LogsBloom, pub logs: Vec, } + impl TransactionReceipt { pub fn new( transaction: Transaction, diff --git a/evm-utils/evm-state/src/types.rs b/evm-utils/evm-state/src/types.rs new file mode 100644 index 0000000000..280e4e221c --- /dev/null +++ b/evm-utils/evm-state/src/types.rs @@ -0,0 +1,69 @@ +pub use primitive_types::{H160, H256, U256}; +use serde::{Deserialize, Serialize}; + +pub(crate) type Slot = u64; // TODO: re-use existing one from sdk package + +#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AccountState { + /// Account nonce. + pub nonce: U256, + /// Account balance. + pub balance: U256, + /// Account code. + pub code: Vec, +} + +/// Vivinity value of a memory backend. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct MemoryVicinity { + /// Gas price. + pub gas_price: U256, + /// Origin. + pub origin: H160, + /// Chain ID. + pub chain_id: U256, + /// Environmental block hashes. + pub block_hashes: Vec, + /// Environmental block number. + pub block_number: U256, + /// Environmental coinbase. + pub block_coinbase: H160, + /// Environmental block timestamp. + pub block_timestamp: U256, + /// Environmental block difficulty. + pub block_difficulty: U256, + /// Environmental block gas limit. + pub block_gas_limit: U256, +} + +impl Default for MemoryVicinity { + fn default() -> Self { + Self { + gas_price: U256::zero(), + origin: H160::default(), + chain_id: U256::zero(), + block_hashes: Vec::new(), + block_number: U256::zero(), + block_coinbase: H160::default(), + block_timestamp: U256::zero(), + block_difficulty: U256::zero(), + block_gas_limit: U256::max_value(), + } + } +} + +pub struct LogWithLocation { + pub transaction_hash: H256, + pub transaction_id: u64, + pub block_num: u64, + pub address: H160, + pub data: Vec, + pub topics: Vec, +} + +pub struct LogFilter { + pub from_block: u64, + pub to_block: u64, + pub address: Option, + pub topics: Vec, +} diff --git a/evm-utils/evm-state/src/version_map.rs b/evm-utils/evm-state/src/version_map.rs deleted file mode 100644 index 99b728a4d9..0000000000 --- a/evm-utils/evm-state/src/version_map.rs +++ /dev/null @@ -1,217 +0,0 @@ -use std::borrow::Borrow; -use std::collections::BTreeMap; -use std::fmt; -use std::ops::Deref; -use std::sync::Arc; - -/// Represent state of value at current version. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] -pub enum State { - // Value exist, and was changed. - Changed(V), - // Value was removed. - Removed, -} - -impl State { - fn by_ref(&self) -> State<&V> { - match self { - State::Changed(ref v) => State::Changed(v), - State::Removed => State::Removed, - } - } -} - -impl From> for Option { - fn from(state: State) -> Option { - match state { - State::Changed(value) => Some(value), - State::Removed => None, - } - } -} - -#[derive(Clone)] -pub struct Map>> { - state: BTreeMap>, - parent: Option, -} - -impl Default for Map { - fn default() -> Self { - Map::new() - } -} - -impl fmt::Debug for Map -where - K: fmt::Debug, - V: fmt::Debug, - Store: MapLike, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Map") - .field("state", &self.state) - .field("parent", &"omited") - .finish() - } -} - -impl Map -where - K: Ord, - Store: MapLike, -{ - // Create new versioned map. - pub fn new() -> Map - where - Store: Sized, - { - Map { - state: BTreeMap::new(), - parent: None, - } - } - - // Borrow value by key - pub fn get(&self, key: &K) -> Option<&V> { - if let Some(s) = self.state.get(key) { - s.by_ref().into() - } else { - self.parent.as_ref().and_then(|parent| parent.get(key)) - } - } - - // Exclusively borrow value by key - pub fn get_mut(&mut self, _key: &Q) -> Option<&mut V> - where - K: Borrow, - Q: Ord, - { - unimplemented!() // TODO: Implement a guard that will save value at drop. - } - - // Override state of key. - pub(crate) fn push_change(&mut self, key: K, value: State) { - self.state.insert(key, value); - } - - // Insert new key, didn't query key before inserting. - pub fn insert(&mut self, key: K, value: V) { - self.push_change(key, State::Changed(value)); - } - - // Remove key, didn't query key before inserting. - pub fn remove(&mut self, key: K) { - self.push_change(key, State::Removed); - } -} - -impl Map -where - K: Ord + Send + Sync + 'static, - V: Send + Sync + 'static, -{ - pub fn freeze(&mut self) { - let this = Arc::new(std::mem::take(self)) as Arc>; - self.parent = Some(this); - } - - // Create new version from freezed one - pub fn try_fork(&self) -> Option { - if !self.state.is_empty() { - return None; - } - - Some(Self { - state: BTreeMap::new(), - parent: self.parent.clone(), - }) - } -} - -/// Map can store it's old version in database or in some other immutable structure. -/// This trait allows you to define your own storage -pub trait MapLike: Sync + Send { - type Key; - type Value; - fn get(&self, key: &Self::Key) -> Option<&Self::Value>; -} - -impl MapLike for Arc { - type Key = Store::Key; - type Value = Store::Value; - fn get(&self, key: &Self::Key) -> Option<&Self::Value> { - ::get(self.deref(), key) - } -} - -impl MapLike for Map -where - K: Ord + Sync + Send, - V: Sync + Send, - Store: MapLike + Send + Sync, -{ - type Key = Store::Key; - type Value = Store::Value; - fn get(&self, key: &Self::Key) -> Option<&Self::Value> { - Map::get(self, key) - } -} - -#[cfg(test)] -mod test { - use super::*; - #[test] - fn store_and_get_simple() { - let mut map: Map<_, _> = Map::new(); - map.insert("first", 1); - map.insert("second", 2); - assert_eq!(map.get(&"first"), Some(&1)); - assert_eq!(map.get(&"second"), Some(&2)); - } - - // Test that map can save version, and type of map is always remain the same. - #[test] - fn new_dynamic_version_insert_remove_test() { - let mut map: Map<_, _> = Map::new(); - map.insert("first", 1); - map.insert("second", 2); - map.insert("third", 3); - assert_eq!(map.get(&"first"), Some(&1)); - assert_eq!(map.get(&"second"), Some(&2)); - assert_eq!(map.get(&"third"), Some(&3)); - - map.freeze(); - let mut map: Map<_, _> = map.try_fork().unwrap(); - - map.remove("first"); - map.insert("third", 1); - - assert_eq!(map.get(&"first"), None); - assert_eq!(map.get(&"second"), Some(&2)); - assert_eq!(map.get(&"third"), Some(&1)); - } - - // Same as new_dynamic_version_insert_remove_test but dont hide type of store. - #[test] - fn new_static_version_insert_remove_test() { - let mut map: Map<_, _> = Map::new(); - map.insert("first", 1); - map.insert("second", 2); - map.insert("third", 3); - assert_eq!(map.get(&"first"), Some(&1)); - assert_eq!(map.get(&"second"), Some(&2)); - assert_eq!(map.get(&"third"), Some(&3)); - - map.freeze(); - let mut map = map.try_fork().unwrap(); - - map.remove("first"); - map.insert("third", 1); - - assert_eq!(map.get(&"first"), None); - assert_eq!(map.get(&"second"), Some(&2)); - assert_eq!(map.get(&"third"), Some(&1)); - } -} diff --git a/evm-utils/programs/evm_loader/src/lib.rs b/evm-utils/programs/evm_loader/src/lib.rs index 6f1a631b5b..d00bb4330c 100644 --- a/evm-utils/programs/evm_loader/src/lib.rs +++ b/evm-utils/programs/evm_loader/src/lib.rs @@ -29,10 +29,10 @@ use instructions::{EvmBigTransaction, EvmInstruction}; use scope::*; use solana_sdk::instruction::{AccountMeta, Instruction}; -pub fn send_raw_tx(signer: &solana::Address, evm_tx: evm::Transaction) -> solana::Instruction { +pub fn send_raw_tx(signer: solana::Address, evm_tx: evm::Transaction) -> solana::Instruction { let account_metas = vec![ AccountMeta::new(solana::evm_state::ID, false), - AccountMeta::new(*signer, true), + AccountMeta::new(signer, true), ]; Instruction::new( @@ -43,13 +43,13 @@ pub fn send_raw_tx(signer: &solana::Address, evm_tx: evm::Transaction) -> solana } pub(crate) fn transfer_native_to_eth( - owner: &solana::Address, + owner: solana::Address, lamports: u64, ether_address: evm::Address, ) -> solana::Instruction { let account_metas = vec![ AccountMeta::new(solana::evm_state::ID, false), - AccountMeta::new(*owner, true), + AccountMeta::new(owner, true), ]; Instruction::new( @@ -62,10 +62,10 @@ pub(crate) fn transfer_native_to_eth( ) } -pub(crate) fn free_ownership(owner: &solana::Address) -> solana::Instruction { +pub(crate) fn free_ownership(owner: solana::Address) -> solana::Instruction { let account_metas = vec![ AccountMeta::new(solana::evm_state::ID, false), - AccountMeta::new(*owner, true), + AccountMeta::new(owner, true), ]; Instruction::new(crate::ID, &EvmInstruction::FreeOwnership {}, account_metas) @@ -124,12 +124,12 @@ pub fn big_tx_execute(owner: &solana::Address, seed: evm::H256) -> solana::Instr } pub fn transfer_native_to_eth_ixs( - owner: &solana::Address, + owner: solana::Address, lamports: u64, ether_address: evm::Address, ) -> Vec { vec![ - solana_sdk::system_instruction::assign(owner, &crate::ID), + solana_sdk::system_instruction::assign(&owner, &crate::ID), transfer_native_to_eth(owner, lamports, ether_address), free_ownership(owner), ] diff --git a/evm-utils/programs/evm_loader/src/processor.rs b/evm-utils/programs/evm_loader/src/processor.rs index bb5b3b5295..0d9cbf04c5 100644 --- a/evm-utils/programs/evm_loader/src/processor.rs +++ b/evm-utils/programs/evm_loader/src/processor.rs @@ -318,8 +318,22 @@ mod test { let caller_address = tx_create.caller().unwrap(); let tx_address = tx_create.address().unwrap(); - assert_eq!(state.read().unwrap().basic(caller_address).nonce, 0.into()); - assert_eq!(state.read().unwrap().basic(tx_address).nonce, 0.into()); + assert_eq!( + state + .read() + .unwrap() + .get_account(caller_address) + .map(|account| account.nonce), + Some(0.into()) + ); + assert_eq!( + state + .read() + .unwrap() + .get_account(tx_address) + .map(|account| account.nonce), + Some(0.into()) + ); { let mut locked = state.write().unwrap(); let mut executor_orig = evm_state::Executor::with_config( @@ -347,8 +361,22 @@ mod test { locked.swap_commit(patch); } - assert_eq!(state.read().unwrap().basic(caller_address).nonce, 1.into()); - assert_eq!(state.read().unwrap().basic(tx_address).nonce, 1.into()); + assert_eq!( + state + .read() + .unwrap() + .get_account(caller_address) + .map(|account| account.nonce), + Some(1.into()) + ); + assert_eq!( + state + .read() + .unwrap() + .get_account(tx_address) + .map(|account| account.nonce), + Some(1.into()) + ); let tx_call = evm::UnsignedTransaction { nonce: 1.into(), @@ -466,9 +494,9 @@ mod test { let signer = solana::Address::new_rand(); vec![ - crate::transfer_native_to_eth(&signer, 1, tx_call.address().unwrap()), - crate::free_ownership(&signer), - crate::send_raw_tx(&signer, tx_call), + crate::transfer_native_to_eth(signer, 1, tx_call.address().unwrap()), + crate::free_ownership(signer), + crate::send_raw_tx(signer, tx_call), ] } diff --git a/evm-utils/src/main.rs b/evm-utils/src/main.rs index b3b7d65337..aa94299f37 100644 --- a/evm-utils/src/main.rs +++ b/evm-utils/src/main.rs @@ -44,6 +44,8 @@ enum SubCommands { #[derive(Debug, structopt::StructOpt)] struct Args { + #[structopt(short = "r", long = "rpc")] + rpc_address: Option, #[structopt(subcommand)] subcommand: SubCommands, } @@ -60,7 +62,10 @@ fn main(args: Args) -> Result<(), Box> { info!("Loading keypair from: {}", keypath); let signer = Box::new(read_keypair_file(&keypath).unwrap()) as Box; - let rpc_client = RpcClient::new("http://127.0.0.1:8899".to_string()); + let address = args + .rpc_address + .unwrap_or_else(|| "https://api.next.velas.com:8899".to_string()); + let rpc_client = RpcClient::new(address); match args.subcommand { SubCommands::SendRawTx { raw_tx } => { @@ -71,7 +76,7 @@ fn main(args: Args) -> Result<(), Box> { solana_sdk::program_utils::limited_deserialize(&buf).unwrap(); debug!("loaded tx = {:?}", tx); - let ix = solana_evm_loader_program::send_raw_tx(&signer.pubkey(), tx); + let ix = solana_evm_loader_program::send_raw_tx(signer.pubkey(), tx); let message = Message::new(&[ix], Some(&signer.pubkey())); let mut create_account_tx = solana::Transaction::new_unsigned(message); @@ -96,7 +101,7 @@ fn main(args: Args) -> Result<(), Box> { ether_address, } => { let ixs = solana_evm_loader_program::transfer_native_to_eth_ixs( - &signer.pubkey(), + signer.pubkey(), amount, ether_address, ); diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 3ad267ec04..332493cb6c 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -691,9 +691,12 @@ fn load_bank_forks( vec![non_primary_accounts_path] }; + let evm_state_path = ledger_path.join("evm-state"); + bank_forks_utils::load( &genesis_config, &blockstore, + evm_state_path, account_paths, snapshot_config.as_ref(), process_options, diff --git a/ledger/src/bank_forks_utils.rs b/ledger/src/bank_forks_utils.rs index 4002b7898d..9b211793c7 100644 --- a/ledger/src/bank_forks_utils.rs +++ b/ledger/src/bank_forks_utils.rs @@ -32,6 +32,7 @@ fn to_loadresult( pub fn load( genesis_config: &GenesisConfig, blockstore: &Blockstore, + evm_state_path: PathBuf, account_paths: Vec, snapshot_config: Option<&SnapshotConfig>, process_options: ProcessOptions, @@ -59,6 +60,7 @@ pub fn load( } let deserialized_bank = snapshot_utils::bank_from_archive( + &evm_state_path, &account_paths, &process_options.frozen_accounts, &snapshot_config.snapshot_path, @@ -104,6 +106,7 @@ pub fn load( blockstore_processor::process_blockstore( &genesis_config, &blockstore, + &evm_state_path, account_paths, process_options, ), diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index e72721d280..9b0afff3ba 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -36,7 +36,7 @@ use solana_vote_program::vote_state::VoteState; use std::{ cell::RefCell, collections::{BTreeMap, HashMap}, - path::PathBuf, + path::{Path, PathBuf}, result, sync::Arc, time::{Duration, Instant}, @@ -320,6 +320,7 @@ fn initiate_callback(mut bank: &mut Arc, genesis_config: &GenesisConfig) { pub fn process_blockstore( genesis_config: &GenesisConfig, blockstore: &Blockstore, + evm_state_path: &Path, account_paths: Vec, opts: ProcessOptions, ) -> BlockstoreProcessorResult { @@ -335,6 +336,7 @@ pub fn process_blockstore( // Setup bank for slot 0 let mut bank0 = Arc::new(Bank::new_with_paths( &genesis_config, + Some(evm_state_path), account_paths, &opts.frozen_accounts, )); diff --git a/run.sh b/run.sh index 8da6be2dff..7722fcdc33 100755 --- a/run.sh +++ b/run.sh @@ -104,7 +104,7 @@ args=( --enable-rpc-exit --enable-rpc-transaction-history --init-complete-file "$dataDir"/init-completed - --snapshot-interval-slots 0 + --snapshot-interval-slots 100 ) solana-validator "${args[@]}" & validator=$! diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 5b52b904e6..97eead34b7 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -66,7 +66,7 @@ use std::{ convert::TryFrom, mem, ops::RangeInclusive, - path::PathBuf, + path::{Path, PathBuf}, ptr, rc::Rc, sync::atomic::{AtomicBool, AtomicU64, Ordering}, @@ -470,11 +470,12 @@ impl Default for BlockhashQueue { impl Bank { pub fn new(genesis_config: &GenesisConfig) -> Self { - Self::new_with_paths(&genesis_config, Vec::new(), &[]) + Self::new_with_paths(&genesis_config, None, Vec::new(), &[]) } pub fn new_with_paths( genesis_config: &GenesisConfig, + evm_state_path: Option<&Path>, // TODO: Remove option, currently need for Bank::new, that is used for tests paths: Vec, frozen_account_pubkeys: &[Pubkey], ) -> Self { @@ -483,6 +484,9 @@ impl Bank { bank.ancestors.insert(bank.slot(), 0); bank.rc.accounts = Arc::new(Accounts::new(paths, &genesis_config.cluster_type)); + if let Some(evm_state_path) = evm_state_path { + bank.evm_state = RwLock::new(evm_state::EvmState::new(evm_state_path).unwrap()); + } bank.process_genesis_config(genesis_config); bank.finish_init(genesis_config); @@ -531,21 +535,19 @@ impl Bank { let fee_rate_governor = FeeRateGovernor::new_derived(&parent.fee_rate_governor, parent.signature_count()); + let evm_state = parent + .evm_state + .read() + .expect("parent evm state was poisoned") + .try_fork(slot) + .expect("unable to fork evm state from parent bank right after freezing it"); + let mut new = Bank { rc, src, slot, epoch, - evm_state: RwLock::new({ - let mut evm_wl = parent - .evm_state - .write() - .expect("parent evm state was poisoned"); - evm_wl.freeze(); - evm_wl - .try_fork() - .expect("Unable to fork EVM state right after freezing it") - }), + evm_state: RwLock::new(evm_state), blockhash_queue: RwLock::new(parent.blockhash_queue.read().unwrap().clone()), // TODO: clean this up, soo much special-case copying... @@ -639,6 +641,7 @@ impl Bank { /// Create a bank from explicit arguments and deserialized fields from snapshot #[allow(clippy::float_cmp)] pub(crate) fn new_from_fields( + evm_state: evm_state::EvmState, bank_rc: BankRc, genesis_config: &GenesisConfig, fields: BankFieldsToDeserialize, @@ -646,10 +649,11 @@ impl Bank { fn new() -> T { T::default() } + let mut bank = Self { rc: bank_rc, src: new(), - evm_state: RwLock::new(evm_state::EvmState::new_not_forget_to_deserialize_later()), + evm_state: RwLock::new(evm_state), blockhash_queue: RwLock::new(fields.blockhash_queue), ancestors: fields.ancestors, hash: RwLock::new(fields.hash), @@ -1122,6 +1126,12 @@ impl Bank { pub fn freeze(&self) { let mut hash = self.hash.write().unwrap(); + + self.evm_state + .write() + .expect("evm state was poisoned") + .freeze(); + if *hash == Hash::default() { // finish up any deferred changes to account state self.collect_rent_eagerly(); @@ -5947,7 +5957,7 @@ mod tests { let create_tx = |from_keypair: &Keypair, hash: Hash| { let from_pubkey = from_keypair.pubkey(); let instruction = solana_evm_loader_program::send_raw_tx( - &from_pubkey, + from_pubkey, solana_evm_loader_program::processor::dummy_call(), ); let message = Message::new(&[instruction], Some(&from_pubkey)); diff --git a/runtime/src/bank_client.rs b/runtime/src/bank_client.rs index 4cd44fb5d5..398d313021 100644 --- a/runtime/src/bank_client.rs +++ b/runtime/src/bank_client.rs @@ -269,7 +269,15 @@ impl SyncClient for BankClient { /// Get account balance or 0 if not found. fn get_evm_balance(&self, pubkey: &evm_state::Address) -> Result { - Ok(self.bank.evm_state.read().unwrap().basic(*pubkey).balance) + let account = self + .bank + .evm_state + .read() + .unwrap() + .get_account(*pubkey) + .unwrap_or_default(); + + Ok(account.balance) } } diff --git a/runtime/src/bank_forks.rs b/runtime/src/bank_forks.rs index ca30bbee19..fa20506141 100644 --- a/runtime/src/bank_forks.rs +++ b/runtime/src/bank_forks.rs @@ -221,6 +221,12 @@ impl BankForks { let parents = root_bank.parents(); banks.extend(parents.iter()); for bank in banks.iter() { + // bank.evm_state + // .write() + // .expect("evm state was poisoned") + // .dump_all() + // .expect("internal evm state error"); + let bank_slot = bank.slot(); if bank.block_height() % self.accounts_hash_interval_slots == 0 && bank_slot > self.last_accounts_hash_slot diff --git a/runtime/src/hardened_unpack.rs b/runtime/src/hardened_unpack.rs index a3976554f3..7616b240b1 100644 --- a/runtime/src/hardened_unpack.rs +++ b/runtime/src/hardened_unpack.rs @@ -156,6 +156,8 @@ fn is_valid_snapshot_archive_entry(parts: &[&str], kind: tar::EntryType) -> bool true } (["snapshots", dir], Directory) if like_slot.is_match(dir) => true, + (["snapshots", dir, "evm-state"], Directory) if like_slot.is_match(dir) => true, + (["snapshots", dir, "evm-state", ..], _) if like_slot.is_match(dir) => true, _ => false, } } @@ -267,6 +269,28 @@ mod tests { tar::EntryType::Directory )); + assert!(is_valid_snapshot_archive_entry( + &["snapshots", "3", "evm-state"], + tar::EntryType::Directory + )); + + assert!(is_valid_snapshot_archive_entry( + &["snapshots", "3", "evm-state", "meta"], + tar::EntryType::Directory + )); + assert!(is_valid_snapshot_archive_entry( + &["snapshots", "3", "evm-state", "meta", "1"], + tar::EntryType::Regular + )); + assert!(is_valid_snapshot_archive_entry( + &["snapshots", "3", "evm-state", "shared"], + tar::EntryType::Directory + )); + assert!(is_valid_snapshot_archive_entry( + &["snapshots", "3", "evm-state", "shared", "01.zst"], + tar::EntryType::Regular + )); + assert!(!is_valid_snapshot_archive_entry( &["accounts", "0x0"], tar::EntryType::Regular diff --git a/runtime/src/rent_collector.rs b/runtime/src/rent_collector.rs index 8d2ad6a6ce..87d2f17eed 100644 --- a/runtime/src/rent_collector.rs +++ b/runtime/src/rent_collector.rs @@ -79,6 +79,7 @@ impl RentCollector { || account.rent_epoch > self.epoch || sysvar::check_id(&account.owner) || *address == incinerator::id() + || *address == solana_sdk::evm_state::id() { 0 } else { diff --git a/runtime/src/serde_snapshot.rs b/runtime/src/serde_snapshot.rs index 39054414ea..848889c910 100644 --- a/runtime/src/serde_snapshot.rs +++ b/runtime/src/serde_snapshot.rs @@ -121,6 +121,7 @@ pub(crate) fn bank_from_stream( serde_style: SerdeStyle, stream: &mut BufReader, append_vecs_path: P, + evm_state_path: &Path, account_paths: &[PathBuf], genesis_config: &GenesisConfig, frozen_account_pubkeys: &[Pubkey], @@ -138,6 +139,7 @@ where accounts_db_fields, genesis_config, frozen_account_pubkeys, + evm_state_path, account_paths, append_vecs_path, )?; @@ -222,6 +224,7 @@ fn reconstruct_bank_from_fields( accounts_db_fields: AccountsDbFields, genesis_config: &GenesisConfig, frozen_account_pubkeys: &[Pubkey], + evm_state_path: &Path, account_paths: &[PathBuf], append_vecs_path: P, ) -> Result @@ -238,7 +241,9 @@ where accounts_db.freeze_accounts(&bank_fields.ancestors, frozen_account_pubkeys); let bank_rc = BankRc::new(Accounts::new_empty(accounts_db), bank_fields.slot); - let bank = Bank::new_from_fields(bank_rc, genesis_config, bank_fields); + let evm_state = evm_state::EvmState::load_from(evm_state_path, bank_fields.slot) + .expect("Unable to open EVM state storage"); + let bank = Bank::new_from_fields(evm_state, bank_rc, genesis_config, bank_fields); Ok(bank) } diff --git a/runtime/src/serde_snapshot/tests.rs b/runtime/src/serde_snapshot/tests.rs index ee21da53ac..dc1bf77781 100644 --- a/runtime/src/serde_snapshot/tests.rs +++ b/runtime/src/serde_snapshot/tests.rs @@ -197,6 +197,7 @@ fn test_bank_serialize_style(serde_style: SerdeStyle) { let rdr = Cursor::new(&buf[..]); let mut reader = std::io::BufReader::new(&buf[rdr.position() as usize..]); + let evm_state_dir = TempDir::new().unwrap(); // Create a new set of directories for this bank's accounts let (_accounts_dir, dbank_paths) = get_temp_accounts_paths(4).unwrap(); let ref_sc = StatusCacheRc::default(); @@ -208,6 +209,7 @@ fn test_bank_serialize_style(serde_style: SerdeStyle) { serde_style, &mut reader, copied_accounts.path(), + &evm_state_dir.path(), &dbank_paths, &genesis_config, &[], diff --git a/runtime/src/snapshot_utils.rs b/runtime/src/snapshot_utils.rs index 6b609dae73..88231b8977 100644 --- a/runtime/src/snapshot_utils.rs +++ b/runtime/src/snapshot_utils.rs @@ -38,6 +38,8 @@ pub const TAR_SNAPSHOTS_DIR: &str = "snapshots"; pub const TAR_ACCOUNTS_DIR: &str = "accounts"; pub const TAR_VERSION_FILE: &str = "version"; +const EVM_STATE_DIR: &str = "evm-state"; + const MAX_SNAPSHOT_DATA_FILE_SIZE: u64 = 32 * 1024 * 1024 * 1024; // 32 GiB const VERSION_STRING_V1_2_0: &str = "1.2.0"; const DEFAULT_SNAPSHOT_VERSION: SnapshotVersion = SnapshotVersion::V1_2_0; @@ -101,6 +103,7 @@ impl SnapshotVersion { pub struct SlotSnapshotPaths { pub slot: Slot, pub snapshot_file_path: PathBuf, + pub evm_state_backup_path: PathBuf, } #[derive(Error, Debug)] @@ -149,6 +152,14 @@ impl SlotSnapshotPaths { &self.snapshot_file_path, &new_slot_hardlink_dir.join(self.slot.to_string()), )?; + + // Hard link EVM backup folder + let evm_source = fs::canonicalize(&self.evm_state_backup_path)?; + let evm_target = new_slot_hardlink_dir.join(EVM_STATE_DIR); + info!("EVM backup linked {:?} => {:?}", evm_source, evm_target); + symlink::symlink_dir(evm_source, evm_target)?; + // std::os::unix::fs::symlink(evm_source, evm_target)?; + Ok(()) } } @@ -378,6 +389,7 @@ where SlotSnapshotPaths { slot, snapshot_file_path: snapshot_path.join(get_snapshot_file_name(slot)), + evm_state_backup_path: snapshot_path.join(EVM_STATE_DIR), } }) .collect::>(); @@ -521,9 +533,31 @@ pub fn add_snapshot>( bank_serialize, slot, snapshot_bank_file_path, ); + let evm_state_dir = slot_snapshot_dir.join(EVM_STATE_DIR); + fs::create_dir_all(&evm_state_dir)?; + info!( + "Saving EVM state for slot {}, path: {:?}", + slot, evm_state_dir + ); + + let mut evm_state_saving = Measure::start("evm-state-saving-ms"); + bank.evm_state + .write() + .unwrap() + .storage + .save_into(&evm_state_dir) + .expect("Unable to save EVM storage data in new place"); + evm_state_saving.stop(); + inc_new_counter_info!("evm-state-saving-ms", evm_state_saving.as_ms() as usize); + info!( + "{} for slot {} at {:?}", + evm_state_saving, slot, evm_state_dir + ); + Ok(SlotSnapshotPaths { slot, snapshot_file_path: snapshot_bank_file_path, + evm_state_backup_path: slot_snapshot_dir.join(EVM_STATE_DIR), }) } @@ -565,6 +599,7 @@ pub fn remove_snapshot>(slot: Slot, snapshot_path: P) -> Result<( } pub fn bank_from_archive>( + evm_state_path: &Path, account_paths: &[PathBuf], frozen_account_pubkeys: &[Pubkey], snapshot_path: &PathBuf, @@ -586,6 +621,7 @@ pub fn bank_from_archive>( let bank = rebuild_bank_from_snapshots( snapshot_version.trim(), + evm_state_path, account_paths, frozen_account_pubkeys, &unpacked_snapshots_dir, @@ -743,6 +779,7 @@ pub fn untar_snapshot_in, Q: AsRef>( fn rebuild_bank_from_snapshots

( snapshot_version: &str, + evm_state_path: &Path, account_paths: &[PathBuf], frozen_account_pubkeys: &[Pubkey], unpacked_snapshots_dir: &PathBuf, @@ -769,6 +806,16 @@ where .pop() .ok_or_else(|| get_io_error("No snapshots found in snapshots directory"))?; + info!( + "restoring database from storage backup: {:?}", + root_paths.evm_state_backup_path + ); + let mut measure = Measure::start("evm state database restore"); + evm_state::Storage::restore_from(root_paths.evm_state_backup_path, evm_state_path) + .expect("Unable to restore EVM state underlying database from storage backup"); + measure.stop(); + info!("{}", measure); + info!("Loading bank from {:?}", &root_paths.snapshot_file_path); let bank = deserialize_snapshot_data_file(&root_paths.snapshot_file_path, |mut stream| { Ok(match snapshot_version_enum { @@ -776,6 +823,7 @@ where SerdeStyle::NEWER, &mut stream, &append_vecs_path, + evm_state_path, account_paths, genesis_config, frozen_account_pubkeys,