diff --git a/Cargo.lock b/Cargo.lock index 8b570cf2cd3..81b0f0d7ec6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -189,7 +189,7 @@ dependencies = [ "alloy-transport", "futures", "futures-util", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -235,7 +235,7 @@ dependencies = [ "crc", "rand 0.8.5", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -323,7 +323,7 @@ dependencies = [ "alloy-sol-types", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", ] @@ -349,7 +349,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -377,7 +377,7 @@ dependencies = [ "rand 0.8.5", "serde_json", "tempfile", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", "url", ] @@ -447,7 +447,7 @@ dependencies = [ "schnellru", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", "url", @@ -581,7 +581,7 @@ dependencies = [ "ethereum_ssz_derive", "serde", "serde_with", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -634,7 +634,7 @@ dependencies = [ "jsonrpsee-types", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -662,7 +662,7 @@ dependencies = [ "alloy-serde", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -700,7 +700,7 @@ dependencies = [ "auto_impl", "elliptic-curve", "k256", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -718,7 +718,7 @@ dependencies = [ "coins-bip39", "k256", "rand 0.8.5", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -803,7 +803,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tower 0.5.2", "tracing", @@ -1455,6 +1455,42 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitfinity-block-validator" +version = "1.1.5" +dependencies = [ + "alloy-consensus", + "alloy-genesis", + "alloy-primitives", + "alloy-signer", + "alloy-signer-local", + "async-trait", + "candid", + "did", + "ethereum-json-rpc-client", + "evm-canister-client", + "ic-agent", + "ic-canister-client", + "itertools 0.13.0", + "reth-chain-state", + "reth-chainspec", + "reth-db", + "reth-db-common", + "reth-engine-tree", + "reth-evm", + "reth-evm-ethereum", + "reth-node-types", + "reth-primitives", + "reth-provider", + "reth-revm", + "reth-trie", + "reth-trie-db", + "serde", + "tempfile", + "tokio", + "tracing", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -1579,7 +1615,7 @@ dependencies = [ "static_assertions", "tap", "thin-vec", - "thiserror 2.0.10", + "thiserror 2.0.11", "time", ] @@ -1911,9 +1947,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.7" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -2743,7 +2779,7 @@ dependencies = [ "serde_with", "sha2 0.10.8", "sha3", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -2952,7 +2988,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "walkdir", ] @@ -3190,7 +3226,7 @@ dependencies = [ "reth-node-ethereum", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -3278,7 +3314,7 @@ dependencies = [ "reth-tracing", "reth-trie-db", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", ] @@ -4067,7 +4103,7 @@ dependencies = [ "once_cell", "rand 0.8.5", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tinyvec", "tokio", "tracing", @@ -4091,7 +4127,7 @@ dependencies = [ "resolv-conf", "serde", "smallvec", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -4380,7 +4416,7 @@ dependencies = [ "sha2 0.10.8", "simple_asn1", "stop-token", - "thiserror 2.0.10", + "thiserror 2.0.11", "time", "tokio", "tower-service", @@ -4397,7 +4433,7 @@ dependencies = [ "ic-agent", "ic-exports", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -4554,7 +4590,7 @@ dependencies = [ "ic-stable-structures 0.6.7", "parking_lot", "schnellru", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -4572,7 +4608,7 @@ dependencies = [ "serde_cbor", "serde_repr", "sha2 0.10.8", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -4907,9 +4943,9 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894813a444908c0c8c0e221b041771d107c4a21de1d317dc49bcc66e3c9e5b3f" +checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" dependencies = [ "darling", "indoc", @@ -5060,9 +5096,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -5549,9 +5585,9 @@ dependencies = [ [[package]] name = "maili-protocol" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcd6cd2bab854872c24d551308cb84df4c20db38593b4392d68cacd75d4c60c" +checksum = "428caa534dd054a449e64d8007d0fd0a15519d1033b272d37d02b74a29cf69f7" dependencies = [ "alloc-no-stdlib", "alloy-consensus", @@ -5566,7 +5602,7 @@ dependencies = [ "op-alloy-consensus", "op-alloy-genesis", "rand 0.8.5", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", "unsigned-varint", ] @@ -6120,7 +6156,7 @@ dependencies = [ "derive_more", "serde", "serde_with", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -6133,7 +6169,7 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-sol-types", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -6176,7 +6212,7 @@ dependencies = [ "derive_more", "op-alloy-consensus", "op-alloy-protocol", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -6395,7 +6431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.10", + "thiserror 2.0.11", "ucd-trie", ] @@ -6631,9 +6667,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483f8c21f64f3ea09fe0f30f5d48c3e8eefe5dac9129f0075f76593b4c1da705" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", "syn 2.0.96", @@ -6692,9 +6728,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -6849,7 +6885,7 @@ dependencies = [ "rustc-hash 2.1.0", "rustls", "socket2", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -6868,7 +6904,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.10", + "thiserror 2.0.11", "tinyvec", "tracing", "web-time", @@ -7121,11 +7157,11 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "regress" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1541daf4e4ed43a0922b7969bdc2170178bcacc5dabf7e39bc508a9fa3953a7a" +checksum = "4f56e622c2378013c6c61e2bd776604c46dc1087b2dc5293275a0c20a44f0771" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", "memchr", ] @@ -7209,6 +7245,7 @@ dependencies = [ "async-channel 2.3.1", "async-trait", "backon", + "bitfinity-block-validator", "candid", "clap", "did", @@ -7217,6 +7254,7 @@ dependencies = [ "evm-canister-client", "eyre", "futures", + "hex", "jsonrpsee", "lightspeed_scheduler", "parking_lot", @@ -7235,6 +7273,7 @@ dependencies = [ "reth-db", "reth-db-api", "reth-db-common", + "reth-discv4", "reth-downloaders", "reth-engine-util", "reth-errors", @@ -7359,7 +7398,7 @@ dependencies = [ "reth-tokio-util", "reth-tracing", "schnellru", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -7395,7 +7434,7 @@ dependencies = [ "reth-rpc-types-compat", "reth-tracing", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tower 0.4.13", "tracing", @@ -7451,7 +7490,7 @@ dependencies = [ "reth-primitives", "reth-primitives-traits", "reth-storage-errors", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -7606,7 +7645,7 @@ dependencies = [ "reth-fs-util", "secp256k1", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tikv-jemallocator", "tracy-client", ] @@ -7748,7 +7787,7 @@ dependencies = [ "sysinfo", "tempfile", "test-fuzz", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -7806,7 +7845,7 @@ dependencies = [ "reth-trie-db", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", ] @@ -7848,7 +7887,7 @@ dependencies = [ "schnellru", "secp256k1", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -7873,7 +7912,7 @@ dependencies = [ "reth-network-peers", "reth-tracing", "secp256k1", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -7900,7 +7939,7 @@ dependencies = [ "secp256k1", "serde", "serde_with", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -7917,6 +7956,7 @@ dependencies = [ "alloy-rlp", "assert_matches", "backon", + "bitfinity-block-validator", "candid", "did", "ethereum-json-rpc-client", @@ -7924,6 +7964,7 @@ dependencies = [ "futures", "futures-util", "hex", + "ic-canister-client", "ic-cbor", "ic-certificate-verification", "ic-certification", @@ -7941,6 +7982,7 @@ dependencies = [ "reth-metrics", "reth-network-p2p", "reth-network-peers", + "reth-node-api", "reth-primitives", "reth-primitives-traits", "reth-provider", @@ -7948,9 +7990,10 @@ dependencies = [ "reth-tasks", "reth-testing-utils", "reth-tracing", + "reth-trie-db", "serde_json", "tempfile", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -8027,7 +8070,7 @@ dependencies = [ "secp256k1", "sha2 0.10.8", "sha3", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -8082,7 +8125,7 @@ dependencies = [ "reth-primitives-traits", "reth-trie", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", ] @@ -8109,7 +8152,7 @@ dependencies = [ "reth-prune", "reth-stages-api", "reth-tasks", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", ] @@ -8167,7 +8210,7 @@ dependencies = [ "reth-trie-parallel", "reth-trie-sparse", "revm-primitives", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -8213,7 +8256,7 @@ dependencies = [ "reth-execution-errors", "reth-fs-util", "reth-storage-errors", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -8246,7 +8289,7 @@ dependencies = [ "serde", "snap", "test-fuzz", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -8275,7 +8318,7 @@ dependencies = [ "reth-primitives", "reth-primitives-traits", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -8462,7 +8505,7 @@ dependencies = [ "reth-prune-types", "reth-storage-errors", "revm-primitives", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -8558,7 +8601,7 @@ dependencies = [ "reth-transaction-pool", "reth-trie-db", "tempfile", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", ] @@ -8585,7 +8628,7 @@ version = "1.1.5" dependencies = [ "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -8628,7 +8671,7 @@ dependencies = [ "rand 0.8.5", "reth-tracing", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -8653,7 +8696,7 @@ dependencies = [ "reth-mdbx-sys", "smallvec", "tempfile", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", ] @@ -8692,7 +8735,7 @@ dependencies = [ "reqwest", "reth-tracing", "serde_with", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -8752,7 +8795,7 @@ dependencies = [ "serial_test", "smallvec", "tempfile", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -8777,7 +8820,7 @@ dependencies = [ "reth-network-types", "reth-tokio-util", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", ] @@ -8815,7 +8858,7 @@ dependencies = [ "secp256k1", "serde_json", "serde_with", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "url", ] @@ -8846,7 +8889,7 @@ dependencies = [ "reth-fs-util", "serde", "tempfile", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", "zstd", ] @@ -8980,7 +9023,7 @@ dependencies = [ "serde", "shellexpand", "strum", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "toml", "tracing", @@ -9171,7 +9214,7 @@ dependencies = [ "reth-primitives", "revm-primitives", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", ] @@ -9270,7 +9313,7 @@ dependencies = [ "serde_json", "serde_with", "test-fuzz", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -9350,7 +9393,7 @@ dependencies = [ "reth-tokio-util", "reth-tracing", "rustc-hash 2.1.0", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -9370,7 +9413,7 @@ dependencies = [ "serde", "serde_json", "test-fuzz", - "thiserror 2.0.10", + "thiserror 2.0.11", "toml", ] @@ -9455,7 +9498,7 @@ dependencies = [ "revm-primitives", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tower 0.4.13", @@ -9548,7 +9591,7 @@ dependencies = [ "reth-transaction-pool", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-util", "tower 0.4.13", @@ -9588,7 +9631,7 @@ dependencies = [ "reth-tokio-util", "reth-transaction-pool", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -9674,7 +9717,7 @@ dependencies = [ "schnellru", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -9774,7 +9817,7 @@ dependencies = [ "reth-trie", "reth-trie-db", "tempfile", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -9802,7 +9845,7 @@ dependencies = [ "reth-static-file-types", "reth-testing-utils", "reth-tokio-util", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -9896,7 +9939,7 @@ dependencies = [ "reth-fs-util", "reth-primitives-traits", "reth-static-file-types", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -9910,7 +9953,7 @@ dependencies = [ "pin-project", "rayon", "reth-metrics", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", "tracing-futures", @@ -9995,7 +10038,7 @@ dependencies = [ "serde_json", "smallvec", "tempfile", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -10110,7 +10153,7 @@ dependencies = [ "reth-trie", "reth-trie-common", "reth-trie-db", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", ] @@ -10135,7 +10178,7 @@ dependencies = [ "reth-trie", "reth-trie-common", "smallvec", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -10177,7 +10220,7 @@ dependencies = [ "colorchoice", "revm", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -10468,9 +10511,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "log", "once_cell", @@ -11071,13 +11114,13 @@ dependencies = [ [[package]] name = "simple_asn1" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 1.0.69", + "thiserror 2.0.11", "time", ] @@ -11456,11 +11499,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.10", + "thiserror-impl 2.0.11", ] [[package]] @@ -11476,9 +11519,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", @@ -12249,20 +12292,21 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -12274,9 +12318,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -12287,9 +12331,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12297,9 +12341,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -12310,9 +12354,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" @@ -12343,9 +12390,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -12694,9 +12741,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.22" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index d12157c0cfd..dfc4d727ca2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,9 @@ exclude = [".github/"] members = [ "bin/reth-bench/", "bin/reth/", - "crates/blockchain-tree-api/", + "crates/bitfinity-block-validator", "crates/blockchain-tree/", + "crates/blockchain-tree-api/", "crates/chain-state/", "crates/chainspec/", "crates/cli/cli/", @@ -477,10 +478,10 @@ alloy-transport-ws = { version = "0.9.2", default-features = false } # op op-alloy-rpc-types = { version = "0.9.0", default-features = false } -op-alloy-rpc-types-engine = { version = "0.9.0", default-features = false } -op-alloy-rpc-jsonrpsee = { version = "0.9.0", default-features = false } -op-alloy-network = { version = "0.9.0", default-features = false } -op-alloy-consensus = { version = "0.9.0", default-features = false } +op-alloy-rpc-types-engine = { version = "0.9.0", default-features = false } +op-alloy-rpc-jsonrpsee = { version = "0.9.0", default-features = false } +op-alloy-network = { version = "0.9.0", default-features = false } +op-alloy-consensus = { version = "0.9.0", default-features = false } # misc aquamarine = "0.6" @@ -520,7 +521,9 @@ rayon = "1.7" rustc-hash = { version = "2.0", default-features = false } schnellru = "0.2" serde = { version = "1.0", default-features = false } -serde_json = { version = "1.0.94", default-features = false, features = ["alloc"] } +serde_json = { version = "1.0.94", default-features = false, features = [ + "alloc", +] } serde_with = { version = "3", default-features = false, features = ["macros"] } sha2 = { version = "0.10", default-features = false } shellexpand = "3.0.0" @@ -647,6 +650,9 @@ tracy-client = "0.17.3" # op-alloy-rpc-types = { git = "https://github.com/alloy-rs/op-alloy", rev = "debfc29" } # op-alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/op-alloy", rev = "debfc29" } +# Bitfinity crates +bitfinity-block-validator = { path = "crates/bitfinity-block-validator" } + # Bitfinity Deps async-channel = "2" candid = "0.10" @@ -658,6 +664,10 @@ ethereum-json-rpc-client = { git = "https://github.com/bitfinity-network/bitfini evm-canister-client = { git = "https://github.com/bitfinity-network/bitfinity-evm-sdk", package = "evm-canister-client", features = [ "ic-agent-client", ], tag = "v0.40.x" } +ic-agent = "0.39" +ic-canister-client = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-canister-client", features = [ + "ic-agent-client", +], tag = "v0.23.x" } ic-cbor = "3" ic-certificate-verification = "3" ic-certification = "3" diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index ca31910c019..e62f176a1de 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -98,13 +98,16 @@ similar-asserts.workspace = true # bitfinity dependencies async-channel.workspace = true +bitfinity-block-validator.workspace = true candid.workspace = true did.workspace = true evm-canister-client = { workspace = true, features = ["ic-agent-client"] } +hex.workspace = true lightspeed_scheduler = { workspace = true, features = ["tracing"] } # rlp = { workspace = true } [dev-dependencies] +reth-discv4.workspace = true tempfile.workspace = true # bitfinity dev dependencies @@ -127,9 +130,9 @@ default = ["jemalloc"] dev = ["reth-cli-commands/arbitrary"] asm-keccak = [ - "reth-node-core/asm-keccak", - "reth-primitives/asm-keccak", - "alloy-primitives/asm-keccak" + "reth-node-core/asm-keccak", + "reth-primitives/asm-keccak", + "alloy-primitives/asm-keccak", ] jemalloc = [ @@ -137,10 +140,7 @@ jemalloc = [ "reth-node-core/jemalloc", "reth-node-metrics/jemalloc", ] -jemalloc-prof = [ - "reth-cli-util/jemalloc", - "reth-cli-util/jemalloc-prof" -] +jemalloc-prof = ["reth-cli-util/jemalloc", "reth-cli-util/jemalloc-prof"] tracy-allocator = ["reth-cli-util/tracy-allocator"] min-error-logs = ["tracing/release_max_level_error"] diff --git a/bin/reth/src/commands/bitfinity_import.rs b/bin/reth/src/commands/bitfinity_import.rs index f6a6a714843..6a4c797fbeb 100644 --- a/bin/reth/src/commands/bitfinity_import.rs +++ b/bin/reth/src/commands/bitfinity_import.rs @@ -1,6 +1,9 @@ //! Command that initializes the node by importing a chain from a remote EVM node. -use crate::{dirs::DataDirPath, version::SHORT_VERSION}; +use crate::dirs::DataDirPath; +use crate::version::SHORT_VERSION; +use bitfinity_block_validator::BitfinityBlockValidator; +use evm_canister_client::{EvmCanisterClient, IcAgentClient}; use futures::{Stream, StreamExt}; use lightspeed_scheduler::{job::Job, scheduler::Scheduler, JobExecutor}; use reth_beacon_consensus::EthBeaconConsensus; @@ -128,6 +131,28 @@ impl BitfinityImportCommand { /// Execute the import job. async fn single_execution(&self) -> eyre::Result<()> { + let evmc_principal = candid::Principal::from_text(&self.bitfinity.evmc_principal) + .expect("Failed to parse principal"); + + // make block validation client if identity path is set + let evm_block_validator = + if let Some(identity) = self.bitfinity.validate_block_ic_identity_file_path.as_ref() { + let evm_client = EvmCanisterClient::new( + IcAgentClient::with_identity( + evmc_principal, + identity, + &self.bitfinity.evm_network, + Some(Duration::from_secs(30)), + ) + .await + .expect("Failed to create agent client"), + ); + + Some(BitfinityBlockValidator::new(evm_client, self.provider_factory.clone())) + } else { + None + }; + let consensus = Arc::new(EthBeaconConsensus::new(self.chain.clone())); debug!(target: "reth::cli - BitfinityImportCommand", "Consensus engine initialized"); let provider_factory = self.provider_factory.clone(); @@ -144,6 +169,26 @@ impl BitfinityImportCommand { retry_delay: Duration::from_secs(self.bitfinity.retry_delay_secs), }; + let ic_root_key = if self.bitfinity.fetch_ic_root_key { + let ic_identity_path = self + .bitfinity + .validate_block_ic_identity_file_path + .as_ref() + .expect("identity path not set"); + let agent = evm_canister_client::agent::identity::init_agent( + &ic_identity_path, + &self.bitfinity.evm_network, + None, + ) + .await?; + + agent.fetch_root_key().await.expect("failed to fetch IC root key"); + let root_key = agent.read_root_key(); + hex::encode(root_key) + } else { + self.bitfinity.ic_root_key.clone() + }; + let remote_client = Arc::new( BitfinityEvmClient::from_rpc_url( rpc_config, @@ -153,8 +198,9 @@ impl BitfinityImportCommand { self.bitfinity.max_fetch_blocks, Some(CertificateCheckSettings { evmc_principal: self.bitfinity.evmc_principal.clone(), - ic_root_key: self.bitfinity.ic_root_key.clone(), + ic_root_key, }), + evm_block_validator, ) .await?, ); diff --git a/bin/reth/tests/commands/utils.rs b/bin/reth/tests/commands/utils.rs index 3f2b102f22a..a37623e2d18 100644 --- a/bin/reth/tests/commands/utils.rs +++ b/bin/reth/tests/commands/utils.rs @@ -172,6 +172,9 @@ pub async fn bitfinity_import_config_data( backup_rpc_url: backup_evm_datasource_url, max_retries: 3, retry_delay_secs: 3, + validate_block_ic_identity_file_path: None, + evm_network: "ic".to_string(), + fetch_ic_root_key: false, }; Ok(( diff --git a/crates/bitfinity-block-validator/Cargo.toml b/crates/bitfinity-block-validator/Cargo.toml new file mode 100644 index 00000000000..73d981ff49c --- /dev/null +++ b/crates/bitfinity-block-validator/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "bitfinity-block-validator" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +alloy-consensus.workspace = true +alloy-primitives.workspace = true +async-trait.workspace = true +did.workspace = true +evm-canister-client.workspace = true +ic-agent.workspace = true +ic-canister-client.workspace = true +itertools.workspace = true +reth-chain-state.workspace = true +reth-chainspec.workspace = true +reth-db.workspace = true +reth-engine-tree.workspace = true +reth-evm.workspace = true +reth-evm-ethereum.workspace = true +reth-node-types.workspace = true +reth-provider.workspace = true +reth-primitives.workspace = true +reth-revm.workspace = true +reth-trie.workspace = true +reth-trie-db.workspace = true +tracing.workspace = true + +[dev-dependencies] +alloy-consensus.workspace = true +alloy-genesis.workspace = true +alloy-signer.workspace = true +alloy-signer-local.workspace = true +async-trait.workspace = true +candid.workspace = true +ethereum-json-rpc-client = { workspace = true, features = ["reqwest"] } +reth-db-common.workspace = true +tokio.workspace = true +serde.workspace = true +tempfile.workspace = true diff --git a/crates/bitfinity-block-validator/src/lib.rs b/crates/bitfinity-block-validator/src/lib.rs new file mode 100644 index 00000000000..5e20cee358b --- /dev/null +++ b/crates/bitfinity-block-validator/src/lib.rs @@ -0,0 +1,428 @@ +//! Bitfinity block validator. + +use alloy_primitives::Address; +use did::unsafe_blocks::ValidateUnsafeBlockArgs; +use did::{Transaction, H256}; +use evm_canister_client::{CanisterClient, EvmCanisterClient}; +use itertools::Itertools; +use reth_chain_state::MemoryOverlayStateProvider; +use reth_evm::execute::BasicBlockExecutor; +use reth_evm::execute::BasicBlockExecutorProvider; +use reth_evm::execute::Executor as _; +use reth_evm::execute::{BlockExecutionOutput, BlockExecutorProvider as _}; +use reth_evm_ethereum::execute::EthExecutionStrategy; +use reth_evm_ethereum::execute::EthExecutionStrategyFactory; +use reth_evm_ethereum::EthEvmConfig; +use reth_node_types::NodeTypesWithDB; +use reth_primitives::Receipt; +use reth_primitives::{Block, BlockWithSenders}; +use reth_provider::providers::ProviderNodeTypes; +use reth_provider::HashedPostStateProvider as _; +use reth_provider::LatestStateProviderRef; +use reth_provider::{ChainSpecProvider as _, ExecutionOutcome, ProviderFactory}; +use reth_revm::database::StateProviderDatabase; +use reth_trie::StateRoot; +use reth_trie_db::DatabaseStateRoot; + +/// Block validator for Bitfinity. +/// +/// The validator validates the block by executing it and then +/// confirming it on the EVM. +#[derive(Clone, Debug)] +pub struct BitfinityBlockValidator +where + C: CanisterClient, + DB: NodeTypesWithDB + Clone, +{ + evm_client: EvmCanisterClient, + provider_factory: ProviderFactory, +} + +impl BitfinityBlockValidator +where + C: CanisterClient, + DB: NodeTypesWithDB + ProviderNodeTypes + Clone, +{ + /// Create a new [`BitfinityBlockValidator`]. + pub fn new(evm_client: EvmCanisterClient, provider_factory: ProviderFactory) -> Self { + Self { evm_client, provider_factory } + } + + /// Validate a block. + pub async fn validate_block( + &self, + block: Block, + transactions: &[Transaction], + ) -> Result<(), Box> { + let validate_args = self.execute_block(block, transactions)?; + self.validate_unsafe_block(validate_args).await?; + + Ok(()) + } + + /// Execute block and return validation arguments. + fn execute_block( + &self, + block: Block, + transactions: &[Transaction], + ) -> Result> { + // execute the block + let block_number = block.number; + tracing::debug!("Executing block: {block_number}",); + let executor = self.executor(); + let block_with_senders = Self::convert_block(block, transactions); + + let output = match executor.execute(&block_with_senders) { + Ok(output) => output, + Err(err) => { + tracing::error!("Failed to execute block: {err:?}"); + return Ok(ValidateUnsafeBlockArgs { + block_number, + block_hash: H256::zero(), + transactions_root: H256::zero(), + state_root: H256::zero(), + receipts_root: H256::zero(), + }); + } + }; + + // calculate the receipts root + let receipts_root = self.calculate_receipts_root(&output.receipts); + tracing::debug!("Block {block_number} receipts root: {receipts_root}",); + + // calculate trasnsactions_root + let transactions_root = self.calculate_transactions_root(&block_with_senders); + tracing::debug!("Block {block_number} transactions root: {transactions_root}",); + + // calculate block hash + let block_hash = self.calculate_block_hash(&block_with_senders); + tracing::debug!("Block {block_number} hash: {block_hash}",); + + // calculate state root + let state_root = self.calculate_state_root(output, block_number)?; + tracing::debug!("Block {block_number} state root: {state_root}",); + + Ok(ValidateUnsafeBlockArgs { + block_number, + block_hash, + transactions_root, + state_root, + receipts_root, + }) + } + + /// Calculate the receipts root. + fn calculate_receipts_root(&self, receipts: &[reth_primitives::Receipt]) -> H256 { + let receipts_with_bloom = + receipts.iter().map(|receipt| receipt.clone().with_bloom()).collect::>(); + let calculated_root = reth_primitives::proofs::calculate_receipt_root(&receipts_with_bloom); + H256::from_slice(calculated_root.as_ref()) + } + + /// Calculate the transactions root. + fn calculate_transactions_root(&self, block: &BlockWithSenders) -> H256 { + let calculated_root = + reth_primitives::proofs::calculate_transaction_root(&block.block.body.transactions); + H256::from_slice(calculated_root.as_ref()) + } + + /// Calculate the block hash. + fn calculate_block_hash(&self, block: &BlockWithSenders) -> H256 { + let calculated_hash = block.clone().seal_slow().hash(); + H256::from_slice(calculated_hash.as_ref()) + } + + /// Calculate the state root. + fn calculate_state_root( + &self, + execution_output: BlockExecutionOutput, + block_number: u64, + ) -> Result> { + // get state root + let execution_outcome = ExecutionOutcome::new( + execution_output.state, + execution_output.receipts.into(), + block_number, + vec![execution_output.requests.into()], + ); + let provider = match self.provider_factory.provider() { + Ok(provider) => provider, + Err(err) => { + tracing::error!("Failed to get provider: {err:?}"); + return Err(Box::new(err)); + } + }; + + let state_provider = LatestStateProviderRef::new(&provider); + let calculated_state_root = StateRoot::overlay_root_with_updates( + provider.tx_ref(), + state_provider.hashed_post_state(execution_outcome.state()), + )? + .0; + + Ok(H256::from_slice(calculated_state_root.as_ref())) + } + + /// Convert [`Block`] to [`BlockWithSenders`]. + fn convert_block(block: Block, transactions: &[Transaction]) -> BlockWithSenders { + let senders = transactions + .iter() + .map(|tx| Address::from_slice(tx.from.0.as_ref())) + .unique() + .collect::>(); + tracing::debug!("Found {} unique senders in block", senders.len()); + + BlockWithSenders { block, senders } + } + + /// Try to validate an unsafe block on the EVM. + async fn validate_unsafe_block( + &self, + validate_args: ValidateUnsafeBlockArgs, + ) -> Result<(), Box> { + let block_number = validate_args.block_number; + tracing::info!("Confirming block on EVM: {block_number}: {validate_args:?}",); + + let res = self.evm_client.validate_unsafe_block(validate_args).await?; + + tracing::info!("Validate unsafe block response for block {block_number}: {res:?}",); + + if let Err(err) = res { + tracing::error!("Failed to validate unsafe block: {err:?}"); + return Err(Box::new(err)); + } + + Ok(()) + } + + /// Get the block executor for the latest block. + fn executor( + &self, + ) -> BasicBlockExecutor< + EthExecutionStrategy< + StateProviderDatabase>, + EthEvmConfig, + >, + > { + let historical = self.provider_factory.latest().expect("no latest provider"); + + let db = MemoryOverlayStateProvider::new(historical, Vec::new()); + let executor = BasicBlockExecutorProvider::new(EthExecutionStrategyFactory::ethereum( + self.provider_factory.chain_spec(), + )); + executor.executor(StateProviderDatabase::new(db)) + } +} + +#[cfg(test)] +mod test { + + use std::{ + collections::{BTreeMap, HashMap}, + str::FromStr, + sync::Arc, + }; + + use alloy_consensus::{proofs::calculate_transaction_root, TxEip1559}; + use alloy_genesis::{Genesis, GenesisAccount}; + use alloy_primitives::{FixedBytes, B256, U256}; + use alloy_signer::SignerSync; + use alloy_signer_local::PrivateKeySigner; + use candid::utils::ArgumentEncoder; + use candid::CandidType; + use ic_canister_client::CanisterClientResult; + use reth_chainspec::{Chain, ChainSpec, EthereumHardfork, ForkCondition, MIN_TRANSACTION_GAS}; + use reth_db::init_db; + use reth_node_types::NodeTypes; + use reth_primitives::{Account, EthPrimitives, RecoveredTx, TransactionSigned}; + use reth_provider::{providers::StaticFileProvider, EthStorage}; + use reth_trie::EMPTY_ROOT_HASH; + use reth_trie_db::MerklePatriciaTrie; + use serde::de::DeserializeOwned; + + use super::*; + + const CHAIN_ID: u64 = 1; + const INITIAL_BASE_FEE: u64 = 1_000_000_000; + const ETHEREUM_BLOCK_GAS_LIMIT: u64 = 30_000_000; + + #[derive(Clone)] + struct DummyDb; + + impl NodeTypesWithDB for DummyDb { + type DB = std::sync::Arc; + } + + impl NodeTypes for DummyDb { + type Primitives = EthPrimitives; + type ChainSpec = ChainSpec; + type StateCommitment = MerklePatriciaTrie; + type Storage = EthStorage; + } + + #[derive(Clone)] + struct DummyCanisterClient; + + #[async_trait::async_trait] + impl CanisterClient for DummyCanisterClient { + async fn update(&self, _method: &str, _args: T) -> CanisterClientResult + where + T: ArgumentEncoder + Send + Sync, + R: DeserializeOwned + CandidType, + { + unimplemented!() + } + + async fn query(&self, _method: &str, _args: T) -> CanisterClientResult + where + T: ArgumentEncoder + Send + Sync, + R: DeserializeOwned + CandidType, + { + unimplemented!() + } + } + + #[tokio::test] + async fn test_should_validate_block() { + let signer_pk = PrivateKeySigner::from_str( + "cc3c029ee81d3a4122270c09ea1f4f35fed7f60c952ef0765f960de3a67f3439", + ) + .expect("failed to parse private key"); + let signer = signer_pk.address(); + + let block = generate_block(&signer_pk); + let txs = block + .body + .transactions + .iter() + .map(|_| { + let mut did_tx = did::Transaction::default(); + did_tx.from = signer.into(); + + did_tx + }) + .collect::>(); + + let client = EvmCanisterClient::new(DummyCanisterClient); + + let db_dir = tempfile::tempdir().expect("failed to create temp dir"); + let database = + Arc::new(init_db(db_dir.path(), Default::default()).expect("failed to init db")); + + let data_dir = tempfile::tempdir().expect("failed to create temp dir"); + + let chainspec = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(Genesis { + alloc: BTreeMap::from([( + signer, + GenesisAccount { + balance: U256::from(10).pow(U256::from(18)), + ..Default::default() + }, + )]), + ..Default::default() + }) + .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0)) + .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(0)) + .with_fork(EthereumHardfork::Shanghai, ForkCondition::Block(0)) + .with_fork(EthereumHardfork::Cancun, ForkCondition::Block(0)) + .build(); + let provider_factory: ProviderFactory = ProviderFactory::new( + database, + Arc::new(chainspec), + StaticFileProvider::read_write(data_dir.path()).expect("failed to create provider"), + ); + reth_db_common::init::init_genesis(&provider_factory).expect("failed to init genesis"); + + let validator = BitfinityBlockValidator::new(client, provider_factory); + + let execute_result = + validator.execute_block(block.clone(), &txs).expect("failed to execute block"); + + println!("{execute_result:?}",); + + assert_eq!(execute_result.block_number, block.header.number,); + assert_eq!(execute_result.transactions_root, block.header.transactions_root.into()); + } + + fn generate_block(signer_pk: &PrivateKeySigner) -> reth_primitives::Block { + let signer = signer_pk.address(); + let tx_cost = U256::from(INITIAL_BASE_FEE * MIN_TRANSACTION_GAS); + let num_tx = 5; + + let mock_tx = |nonce: u64| -> RecoveredTx<_> { + let tx = reth_primitives::Transaction::Eip1559(TxEip1559 { + chain_id: CHAIN_ID, + nonce, + gas_limit: MIN_TRANSACTION_GAS, + to: signer.into(), + max_fee_per_gas: INITIAL_BASE_FEE as u128, + max_priority_fee_per_gas: 1, + ..Default::default() + }); + let signature_hash = tx.signature_hash(); + let signature = signer_pk.sign_hash_sync(&signature_hash).unwrap(); + + TransactionSigned::new_unhashed(tx, signature).with_signer(signer) + }; + let signer_balance_decrease = tx_cost * U256::from(num_tx); + let initial_signer_balance = U256::from(10).pow(U256::from(18)); + let transactions: Vec> = (0..num_tx).map(mock_tx).collect(); + + let receipts = transactions + .iter() + .enumerate() + .map(|(idx, tx)| { + Receipt { + tx_type: tx.tx_type(), + success: true, + cumulative_gas_used: (idx as u64 + 1) * MIN_TRANSACTION_GAS, + ..Default::default() + } + .with_bloom() + }) + .collect::>(); + + let signed_body = + transactions.clone().into_iter().map(|tx| tx.into_tx()).collect::>(); + let transactions_root = calculate_transaction_root(&signed_body); + + let header = reth_primitives::Header { + number: 1, + parent_hash: FixedBytes::new([0; 32]), + gas_used: transactions.len() as u64 * MIN_TRANSACTION_GAS, + gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, + mix_hash: B256::ZERO, + base_fee_per_gas: Some(INITIAL_BASE_FEE), + transactions_root, + receipts_root: alloy_consensus::proofs::calculate_receipt_root(&receipts), + beneficiary: signer, + state_root: reth_trie::root::state_root_unhashed(HashMap::from([( + signer, + Account { + balance: initial_signer_balance - signer_balance_decrease, + nonce: num_tx, + ..Default::default() + } + .into_trie_account(EMPTY_ROOT_HASH), + )])), + // use the number as the timestamp so it is monotonically increasing + timestamp: 0, + withdrawals_root: Some(alloy_consensus::proofs::calculate_withdrawals_root(&[])), + blob_gas_used: Some(0), + excess_blob_gas: Some(0), + parent_beacon_block_root: None, + ..Default::default() + }; + + reth_primitives::Block { + header, + body: reth_primitives::BlockBody { + transactions: signed_body, + ommers: Vec::new(), + withdrawals: Some(vec![].into()), + }, + } + } +} diff --git a/crates/ethereum/consensus/src/lib.rs b/crates/ethereum/consensus/src/lib.rs index 2ebf3cfe827..de11fbab10b 100644 --- a/crates/ethereum/consensus/src/lib.rs +++ b/crates/ethereum/consensus/src/lib.rs @@ -59,8 +59,9 @@ impl EthBeaconConsensus let parent_gas_limit = if self.chain_spec.fork(EthereumHardfork::London).transitions_at_block(header.number()) { - parent.gas_limit() * - self.chain_spec + parent.gas_limit() + * self + .chain_spec .base_fee_params_at_timestamp(header.timestamp()) .elasticity_multiplier as u64 } else { @@ -73,23 +74,23 @@ impl EthBeaconConsensus return Err(ConsensusError::GasLimitInvalidIncrease { parent_gas_limit, child_gas_limit: header.gas_limit(), - }) + }); } } // Check for a decrease in gas limit beyond the allowed threshold. - else if parent_gas_limit - header.gas_limit() >= - parent_gas_limit / GAS_LIMIT_BOUND_DIVISOR + else if parent_gas_limit - header.gas_limit() + >= parent_gas_limit / GAS_LIMIT_BOUND_DIVISOR { return Err(ConsensusError::GasLimitInvalidDecrease { parent_gas_limit, child_gas_limit: header.gas_limit(), - }) + }); } // Check if the self gas limit is below the minimum required limit. else if header.gas_limit() < MINIMUM_GAS_LIMIT { return Err(ConsensusError::GasLimitInvalidMinimum { child_gas_limit: header.gas_limit(), - }) + }); } Ok(()) @@ -142,33 +143,33 @@ where validate_header_base_fee(header.header(), &self.chain_spec)?; // EIP-4895: Beacon chain push withdrawals as operations - if self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) && - header.withdrawals_root().is_none() + if self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) + && header.withdrawals_root().is_none() { - return Err(ConsensusError::WithdrawalsRootMissing) - } else if !self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) && - header.withdrawals_root().is_some() + return Err(ConsensusError::WithdrawalsRootMissing); + } else if !self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) + && header.withdrawals_root().is_some() { - return Err(ConsensusError::WithdrawalsRootUnexpected) + return Err(ConsensusError::WithdrawalsRootUnexpected); } // Ensures that EIP-4844 fields are valid once cancun is active. if self.chain_spec.is_cancun_active_at_timestamp(header.timestamp()) { validate_4844_header_standalone(header.header())?; } else if header.blob_gas_used().is_some() { - return Err(ConsensusError::BlobGasUsedUnexpected) + return Err(ConsensusError::BlobGasUsedUnexpected); } else if header.excess_blob_gas().is_some() { - return Err(ConsensusError::ExcessBlobGasUnexpected) + return Err(ConsensusError::ExcessBlobGasUnexpected); } else if header.parent_beacon_block_root().is_some() { - return Err(ConsensusError::ParentBeaconBlockRootUnexpected) + return Err(ConsensusError::ParentBeaconBlockRootUnexpected); } if self.chain_spec.is_prague_active_at_timestamp(header.timestamp()) { if header.requests_hash().is_none() { - return Err(ConsensusError::RequestsHashMissing) + return Err(ConsensusError::RequestsHashMissing); } } else if header.requests_hash().is_some() { - return Err(ConsensusError::RequestsHashUnexpected) + return Err(ConsensusError::RequestsHashUnexpected); } Ok(()) @@ -220,16 +221,16 @@ where if is_post_merge { // TODO: add `is_zero_difficulty` to `alloy_consensus::BlockHeader` trait if !header.difficulty().is_zero() { - return Err(ConsensusError::TheMergeDifficultyIsNotZero) + return Err(ConsensusError::TheMergeDifficultyIsNotZero); } // TODO: helper fn in `alloy_consensus::BlockHeader` trait if !header.nonce().is_some_and(|nonce| nonce.is_zero()) { - return Err(ConsensusError::TheMergeNonceIsNotZero) + return Err(ConsensusError::TheMergeNonceIsNotZero); } if header.ommers_hash() != EMPTY_OMMER_ROOT_HASH { - return Err(ConsensusError::TheMergeOmmerRootIsNotEmpty) + return Err(ConsensusError::TheMergeOmmerRootIsNotEmpty); } // Post-merge, the consensus layer is expected to perform checks such that the block @@ -259,7 +260,7 @@ where return Err(ConsensusError::TimestampIsInFuture { timestamp: header.timestamp(), present_timestamp, - }) + }); } validate_header_extra_data(header)?; diff --git a/crates/net/downloaders/Cargo.toml b/crates/net/downloaders/Cargo.toml index 4a0523f0eee..b49b04c1510 100644 --- a/crates/net/downloaders/Cargo.toml +++ b/crates/net/downloaders/Cargo.toml @@ -21,9 +21,7 @@ reth-primitives.workspace = true reth-primitives-traits.workspace = true reth-storage-api.workspace = true reth-tasks.workspace = true - -# optional deps for the test-utils feature -reth-db = { workspace = true, optional = true } +reth-db = { workspace = true } reth-db-api = { workspace = true, optional = true } reth-testing-utils = { workspace = true, optional = true } @@ -54,17 +52,21 @@ tempfile = { workspace = true, optional = true } itertools.workspace = true # Bitfinity deps +bitfinity-block-validator.workspace = true alloy-genesis.workspace = true candid.workspace = true did.workspace = true eyre.workspace = true ethereum-json-rpc-client.workspace = true hex.workspace = true +ic-canister-client.workspace = true ic-cbor.workspace = true ic-certificate-verification.workspace = true ic-certification.workspace = true reth-chainspec.workspace = true reth-cli.workspace = true +reth-node-api.workspace = true +reth-provider.workspace = true serde_json.workspace = true backon.workspace = true @@ -76,6 +78,7 @@ reth-consensus = { workspace = true, features = ["test-utils"] } reth-network-p2p = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } reth-testing-utils.workspace = true +reth-trie-db.workspace = true reth-tracing.workspace = true assert_matches.workspace = true @@ -89,9 +92,9 @@ tempfile.workspace = true [features] optimism = [ "reth-primitives/optimism", - "reth-db?/optimism", + "reth-db/optimism", "reth-db-api?/optimism", - "reth-provider/optimism" + "reth-provider/optimism", ] test-utils = [ @@ -105,5 +108,5 @@ test-utils = [ "reth-primitives/test-utils", "reth-db-api?/test-utils", "reth-provider/test-utils", - "reth-primitives-traits/test-utils" + "reth-primitives-traits/test-utils", ] diff --git a/crates/net/downloaders/src/bitfinity_evm_client.rs b/crates/net/downloaders/src/bitfinity_evm_client.rs index 4c529c71920..323d07e7443 100644 --- a/crates/net/downloaders/src/bitfinity_evm_client.rs +++ b/crates/net/downloaders/src/bitfinity_evm_client.rs @@ -1,10 +1,12 @@ use alloy_eips::BlockHashOrNumber; use alloy_genesis::{ChainConfig, Genesis, GenesisAccount}; use alloy_primitives::{BlockHash, BlockNumber, B256, U256}; +use bitfinity_block_validator::BitfinityBlockValidator; use candid::Principal; -use did::certified::CertifiedResult; +use did::{certified::CertifiedResult, error::EvmError}; use ethereum_json_rpc_client::{reqwest::ReqwestClient, EthJsonRpcClient}; use eyre::Result; +use ic_canister_client::IcAgentClient; use ic_cbor::{CertificateToCbor, HashTreeToCbor}; use ic_certificate_verification::VerifyCertificate; use ic_certification::{Certificate, HashTree, LookupResult}; @@ -24,7 +26,9 @@ use reth_network_p2p::{ priority::Priority, }; use reth_network_peers::PeerId; +use reth_node_api::NodeTypesWithDB; use reth_primitives::{BlockBody, ForkCondition, Header, TransactionSigned}; +use reth_provider::providers::ProviderNodeTypes; use serde_json::json; use std::{self, cmp::min, collections::HashMap}; @@ -35,8 +39,10 @@ use backon::{ExponentialBuilder, Retryable}; use tracing::{debug, error, info, trace, warn}; +type EvmBlockValidator = BitfinityBlockValidator; + /// RPC client configuration -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct RpcClientConfig { /// Primary RPC URL pub primary_url: String, @@ -78,6 +84,14 @@ pub enum RemoteClientError { /// Certificate check error #[error("certification check error: {0}")] CertificateError(String), + + /// Error occurred when validating blocks on the evm + #[error("EVM block validation error: {0}")] + BlockValidationError(String), + + /// EVM error + #[error(transparent)] + Evm(#[from] EvmError), } /// Setting for checking last certified block @@ -91,14 +105,18 @@ pub struct CertificateCheckSettings { impl BitfinityEvmClient { /// `BitfinityEvmClient` from rpc url - pub async fn from_rpc_url( + pub async fn from_rpc_url( rpc_config: RpcClientConfig, start_block: u64, end_block: Option, batch_size: usize, max_blocks: u64, certificate_settings: Option, - ) -> Result { + evm_block_validator: Option>, + ) -> Result + where + DB: NodeTypesWithDB + ProviderNodeTypes + Clone, + { let mut headers = HashMap::new(); let mut hash_to_number = HashMap::new(); let mut bodies = HashMap::new(); @@ -154,12 +172,19 @@ impl BitfinityEvmClient { info!(target: "downloaders::bitfinity_evm_client", blocks = full_blocks.len(), "Fetched blocks"); for block in full_blocks { - if let Some(block_checker) = &block_checker { - block_checker.check_block(&block)?; - } let header = reth_primitives::Header::decode(&mut block.header_rlp_encoded().as_slice())?; + let reth_block = Self::did_block_to_reth_block(&block)?; + + if let Some(block_checker) = &block_checker { + debug!("Checking block {}", block.number); + block_checker + .check_block(&block, &reth_block, evm_block_validator.as_ref()) + .await?; + debug!("block {} OK", block.number); + } + let mut body = reth_primitives::BlockBody::default(); for tx in block.transactions { let decoded_tx = @@ -317,7 +342,11 @@ impl BitfinityEvmClient { (EthereumHardfork::London.boxed(), ForkCondition::Block(0)), ( EthereumHardfork::Paris.boxed(), - ForkCondition::TTD { activation_block_number: 0, fork_block: Some(0), total_difficulty: U256::from(0) }, + ForkCondition::TTD { + activation_block_number: 0, + fork_block: Some(0), + total_difficulty: U256::from(0), + }, ), ]), deposit_contract: None, @@ -395,6 +424,30 @@ impl BitfinityEvmClient { Err(eyre::eyre!("Failed to connect to any RPC endpoint")) } + + /// Convert [`did::Block`] to [`reth_primitives::Block`] + fn did_block_to_reth_block( + block: &did::Block, + ) -> Result { + let header = reth_primitives::Header::decode(&mut block.header_rlp_encoded().as_slice())?; + + let encoded_txs = + block.transactions.iter().map(|tx| tx.rlp_encoded_2718()).flatten().collect::>(); + + let reth_txs = encoded_txs + .into_iter() + .map(|tx| TransactionSigned::decode(&mut tx.to_vec().as_slice())) + .collect::, _>>()?; + + Ok(reth_primitives::Block { + header, + body: reth_primitives::BlockBody { + transactions: reth_txs, + ommers: vec![], + withdrawals: None, + }, + }) + } } struct BlockCertificateChecker { @@ -426,7 +479,15 @@ impl BlockCertificateChecker { self.certified_data.data.number.as_u64() } - fn check_block(&self, block: &did::Block) -> Result<(), RemoteClientError> { + async fn check_block( + &self, + block: &did::Block, + reth_block: &reth_primitives::Block, + evm_block_validator: Option<&EvmBlockValidator>, + ) -> Result<(), RemoteClientError> + where + DB: NodeTypesWithDB + ProviderNodeTypes + Clone, + { if block.number < self.certified_data.data.number { return Ok(()); } @@ -451,20 +512,32 @@ impl BlockCertificateChecker { })?; let current_time_ns = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .expect("Should be able to get the time") - .as_nanos(); + .duration_since(std::time::UNIX_EPOCH) + .expect("Should be able to get the time") + .as_nanos(); let allowed_certificate_time_offset = Duration::from_secs(120).as_nanos(); - certificate.verify(self.evmc_principal.as_ref(), &self.ic_root_key, ¤t_time_ns, &allowed_certificate_time_offset).map_err(|e| { - RemoteClientError::CertificateError(format!("certificate validation error: {e}")) - })?; + certificate + .verify( + self.evmc_principal.as_ref(), + &self.ic_root_key, + ¤t_time_ns, + &allowed_certificate_time_offset, + ) + .map_err(|e| { + RemoteClientError::CertificateError(format!("certificate validation error: {e}")) + })?; let tree = HashTree::from_cbor(&self.certified_data.witness).map_err(|e| { RemoteClientError::CertificateError(format!("failed to parse witness: {e}")) })?; Self::validate_tree(self.evmc_principal.as_ref(), &certificate, &tree); + if let Some(evm_block_validator) = evm_block_validator { + Self::validate_block(evm_block_validator, reth_block.clone(), &block.transactions) + .await?; + } + Ok(()) } @@ -485,6 +558,22 @@ impl BlockCertificateChecker { true } + + /// Validate block on the EVM + async fn validate_block( + validator: &EvmBlockValidator, + block: reth_primitives::Block, + transactions: &[did::Transaction], + ) -> Result<(), RemoteClientError> + where + DB: NodeTypesWithDB + ProviderNodeTypes + Clone, + { + tracing::debug!("Validating block {}", block.number); + + validator.validate_block(block, transactions).await.map_err(|e| { + RemoteClientError::BlockValidationError(format!("failed to validate block: {e}")) + }) + } } impl HeadersClient for BitfinityEvmClient { @@ -581,11 +670,30 @@ impl DownloadClient for BitfinityEvmClient { #[cfg(test)] mod tests { + use reth_node_api::NodeTypes; + use reth_primitives::EthPrimitives; + use reth_storage_api::EthStorage; + use reth_trie_db::MerklePatriciaTrie; + use super::*; + #[derive(Clone)] + struct DummyDb; + + impl NodeTypesWithDB for DummyDb { + type DB = std::sync::Arc; + } + + impl NodeTypes for DummyDb { + type Primitives = EthPrimitives; + type ChainSpec = ChainSpec; + type StateCommitment = MerklePatriciaTrie; + type Storage = EthStorage; + } + #[tokio::test] async fn bitfinity_remote_client_from_rpc_url() { - let client = BitfinityEvmClient::from_rpc_url( + let client = BitfinityEvmClient::from_rpc_url::( RpcClientConfig { primary_url: "https://cloudflare-eth.com".to_string(), backup_url: None, @@ -597,6 +705,7 @@ mod tests { 5, 1000, None, + None, ) .await .unwrap(); @@ -605,7 +714,7 @@ mod tests { #[tokio::test] async fn bitfinity_remote_client_from_backup_rpc_url() { - let client = BitfinityEvmClient::from_rpc_url( + let client = BitfinityEvmClient::from_rpc_url::( RpcClientConfig { primary_url: "https://cloudflare.com".to_string(), backup_url: Some("https://cloudflare-eth.com".to_string()), @@ -617,6 +726,7 @@ mod tests { 5, 1000, None, + None, ) .await .unwrap(); @@ -625,7 +735,7 @@ mod tests { #[tokio::test] async fn bitfinity_test_headers_client() { - let client = BitfinityEvmClient::from_rpc_url( + let client = BitfinityEvmClient::from_rpc_url::( RpcClientConfig { primary_url: "https://cloudflare-eth.com".to_string(), backup_url: None, @@ -637,6 +747,7 @@ mod tests { 5, 1000, None, + None, ) .await .unwrap(); @@ -656,7 +767,7 @@ mod tests { #[tokio::test] async fn bitfinity_test_bodies_client() { - let client = BitfinityEvmClient::from_rpc_url( + let client = BitfinityEvmClient::from_rpc_url::( RpcClientConfig { primary_url: "https://cloudflare-eth.com".to_string(), backup_url: None, @@ -668,6 +779,7 @@ mod tests { 5, 1000, None, + None, ) .await .unwrap(); diff --git a/crates/node/core/src/args/bitfinity_args.rs b/crates/node/core/src/args/bitfinity_args.rs index 029d2079aab..b3ab2e37939 100644 --- a/crates/node/core/src/args/bitfinity_args.rs +++ b/crates/node/core/src/args/bitfinity_args.rs @@ -56,6 +56,24 @@ pub struct BitfinityImportArgs { /// Root key for the IC network #[arg(long, value_name = "IC_ROOT_KEY", default_value = IC_MAINNET_KEY)] pub ic_root_key: String, + + /// Path to an identity PEM file to use to validate blocks when running the node. + /// + /// If not provided blocks will not be validated. + #[arg(long, value_name = "IDENTITY_FILE_PATH")] + pub validate_block_ic_identity_file_path: Option, + + /// Network url + /// This is the URL of the IC network. + /// E.g. + /// - https://ic0.app + /// - http://127.0.0.1:3333 + #[arg(long, default_value = "ic")] + pub evm_network: String, + + /// Fetch the IC root key (to be used on local networks) + #[arg(long, default_value = "false")] + pub fetch_ic_root_key: bool, } /// Bitfinity Related Args