diff --git a/.gitignore b/.gitignore index 18401528..7a466181 100644 --- a/.gitignore +++ b/.gitignore @@ -182,4 +182,5 @@ __pycache__/ sentinel.toml sentinel_state.json gravity_e2e/cluster_test_cases/bridge/mock_anvil_metadata.json +gravity_e2e/cluster_test_cases/oracle_live_soak/artifacts/ bench_reports/ diff --git a/Cargo.lock b/Cargo.lock index 223474e6..086981ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4981,7 +4981,7 @@ dependencies = [ "bitflags 2.13.1", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.12.1", "proc-macro2", "quote", "regex", @@ -8519,7 +8519,7 @@ dependencies = [ [[package]] name = "gravity-precompiles" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "blst", @@ -8531,7 +8531,7 @@ dependencies = [ [[package]] name = "gravity-primitives" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" [[package]] name = "gravity-sdk" @@ -8545,7 +8545,7 @@ dependencies = [ [[package]] name = "gravity-storage" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "async-trait", @@ -8668,7 +8668,7 @@ dependencies = [ [[package]] name = "greth" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "async-trait", "gravity-primitives", @@ -8709,7 +8709,7 @@ dependencies = [ [[package]] name = "grevm" version = "2.2.2" -source = "git+https://github.com/Galxe/grevm?rev=dcd1460b20d9f3d793c8309c28b0b7401be91523#dcd1460b20d9f3d793c8309c28b0b7401be91523" +source = "git+https://github.com/Galxe/grevm?rev=25b71edcac1b94be7fc5ee0e4f20e5fcce6908ad#25b71edcac1b94be7fc5ee0e4f20e5fcce6908ad" dependencies = [ "ahash 0.8.12", "alloy-evm", @@ -12274,7 +12274,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.114", @@ -13671,7 +13671,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.114", @@ -13684,7 +13684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.114", @@ -14539,7 +14539,7 @@ checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "reth" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "alloy-rpc-types 2.0.5", @@ -14586,7 +14586,7 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -14613,7 +14613,7 @@ dependencies = [ [[package]] name = "reth-chain-state" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -14642,7 +14642,7 @@ dependencies = [ [[package]] name = "reth-chainspec" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-chains", "alloy-consensus 2.0.5", @@ -14662,7 +14662,7 @@ dependencies = [ [[package]] name = "reth-cli" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-genesis", "clap 4.5.57", @@ -14675,7 +14675,7 @@ dependencies = [ [[package]] name = "reth-cli-commands" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-chains", "alloy-consensus 2.0.5", @@ -14759,7 +14759,7 @@ dependencies = [ [[package]] name = "reth-cli-runner" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "reth-tasks", "tokio", @@ -14769,7 +14769,7 @@ dependencies = [ [[package]] name = "reth-cli-util" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eips 2.0.5", "alloy-primitives", @@ -14818,7 +14818,7 @@ dependencies = [ [[package]] name = "reth-config" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "eyre", "humantime-serde", @@ -14834,7 +14834,7 @@ dependencies = [ [[package]] name = "reth-consensus" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eip7928 0.4.5", @@ -14848,7 +14848,7 @@ dependencies = [ [[package]] name = "reth-consensus-common" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -14861,7 +14861,7 @@ dependencies = [ [[package]] name = "reth-consensus-debug-client" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -14886,7 +14886,7 @@ dependencies = [ [[package]] name = "reth-db" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "derive_more 2.1.1", @@ -14910,7 +14910,7 @@ dependencies = [ [[package]] name = "reth-db-api" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-genesis", @@ -14936,7 +14936,7 @@ dependencies = [ [[package]] name = "reth-db-common" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-genesis", @@ -14967,7 +14967,7 @@ dependencies = [ [[package]] name = "reth-db-models" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eips 2.0.5", "alloy-primitives", @@ -14981,7 +14981,7 @@ dependencies = [ [[package]] name = "reth-discv4" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -15006,7 +15006,7 @@ dependencies = [ [[package]] name = "reth-discv5" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -15030,7 +15030,7 @@ dependencies = [ [[package]] name = "reth-dns-discovery" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "dashmap 6.2.1", @@ -15054,7 +15054,7 @@ dependencies = [ [[package]] name = "reth-downloaders" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15085,7 +15085,7 @@ dependencies = [ [[package]] name = "reth-ecies" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "aes", "alloy-primitives", @@ -15113,7 +15113,7 @@ dependencies = [ [[package]] name = "reth-engine-local" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -15136,7 +15136,7 @@ dependencies = [ [[package]] name = "reth-engine-primitives" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15161,7 +15161,7 @@ dependencies = [ [[package]] name = "reth-engine-tree" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15216,7 +15216,7 @@ dependencies = [ [[package]] name = "reth-engine-util" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -15246,7 +15246,7 @@ dependencies = [ [[package]] name = "reth-era" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15262,7 +15262,7 @@ dependencies = [ [[package]] name = "reth-era-downloader" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "bytes", @@ -15278,7 +15278,7 @@ dependencies = [ [[package]] name = "reth-era-utils" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -15300,7 +15300,7 @@ dependencies = [ [[package]] name = "reth-errors" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "reth-consensus", "reth-execution-errors", @@ -15311,7 +15311,7 @@ dependencies = [ [[package]] name = "reth-eth-wire" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-chains", "alloy-primitives", @@ -15339,7 +15339,7 @@ dependencies = [ [[package]] name = "reth-eth-wire-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-chains", "alloy-consensus 2.0.5", @@ -15361,7 +15361,7 @@ dependencies = [ [[package]] name = "reth-ethereum-cli" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "clap 4.5.57", "eyre", @@ -15384,7 +15384,7 @@ dependencies = [ [[package]] name = "reth-ethereum-consensus" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15400,7 +15400,7 @@ dependencies = [ [[package]] name = "reth-ethereum-engine-primitives" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eips 2.0.5", "alloy-primitives", @@ -15416,7 +15416,7 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eip2124", "alloy-hardforks", @@ -15429,7 +15429,7 @@ dependencies = [ [[package]] name = "reth-ethereum-payload-builder" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15459,7 +15459,7 @@ dependencies = [ [[package]] name = "reth-ethereum-primitives" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15473,7 +15473,7 @@ dependencies = [ [[package]] name = "reth-etl" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "rayon", "reth-db-api", @@ -15483,7 +15483,7 @@ dependencies = [ [[package]] name = "reth-evm" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eip7928 0.4.5", @@ -15493,6 +15493,7 @@ dependencies = [ "auto_impl", "derive_more 2.1.1", "futures-util", + "grevm", "metrics", "rayon", "reth-execution-errors", @@ -15508,7 +15509,7 @@ dependencies = [ [[package]] name = "reth-evm-ethereum" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15533,7 +15534,7 @@ dependencies = [ [[package]] name = "reth-execution-cache" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "fixed-cache", @@ -15551,7 +15552,7 @@ dependencies = [ [[package]] name = "reth-execution-errors" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-evm", "alloy-primitives", @@ -15564,7 +15565,7 @@ dependencies = [ [[package]] name = "reth-execution-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15583,7 +15584,7 @@ dependencies = [ [[package]] name = "reth-exex" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15621,7 +15622,7 @@ dependencies = [ [[package]] name = "reth-exex-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eips 2.0.5", "alloy-primitives", @@ -15635,7 +15636,7 @@ dependencies = [ [[package]] name = "reth-fs-util" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "serde", "serde_json", @@ -15645,7 +15646,7 @@ dependencies = [ [[package]] name = "reth-invalid-block-hooks" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -15673,7 +15674,7 @@ dependencies = [ [[package]] name = "reth-ipc" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "bytes", "futures", @@ -15693,7 +15694,7 @@ dependencies = [ [[package]] name = "reth-libmdbx" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "bitflags 2.13.1", "byteorder", @@ -15710,7 +15711,7 @@ dependencies = [ [[package]] name = "reth-mdbx-sys" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "bindgen", "cc", @@ -15719,7 +15720,7 @@ dependencies = [ [[package]] name = "reth-metrics" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "futures", "metrics", @@ -15732,7 +15733,7 @@ dependencies = [ [[package]] name = "reth-net-banlist" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "ipnet", @@ -15741,7 +15742,7 @@ dependencies = [ [[package]] name = "reth-net-nat" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "futures-util", "if-addrs", @@ -15755,7 +15756,7 @@ dependencies = [ [[package]] name = "reth-network" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15812,7 +15813,7 @@ dependencies = [ [[package]] name = "reth-network-api" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -15837,7 +15838,7 @@ dependencies = [ [[package]] name = "reth-network-p2p" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15859,7 +15860,7 @@ dependencies = [ [[package]] name = "reth-network-peers" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -15874,7 +15875,7 @@ dependencies = [ [[package]] name = "reth-network-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eip2124", "humantime-serde", @@ -15888,7 +15889,7 @@ dependencies = [ [[package]] name = "reth-nippy-jar" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "anyhow", "bincode", @@ -15905,7 +15906,7 @@ dependencies = [ [[package]] name = "reth-node-api" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-rpc-types-engine", "eyre", @@ -15929,7 +15930,7 @@ dependencies = [ [[package]] name = "reth-node-builder" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -15998,7 +15999,7 @@ dependencies = [ [[package]] name = "reth-node-core" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -16054,7 +16055,7 @@ dependencies = [ [[package]] name = "reth-node-ethereum" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -16101,7 +16102,7 @@ dependencies = [ [[package]] name = "reth-node-ethstats" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -16125,7 +16126,7 @@ dependencies = [ [[package]] name = "reth-node-events" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -16149,7 +16150,7 @@ dependencies = [ [[package]] name = "reth-node-metrics" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "bytes", "eyre", @@ -16173,7 +16174,7 @@ dependencies = [ [[package]] name = "reth-node-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "reth-chainspec", "reth-db-api", @@ -16185,7 +16186,7 @@ dependencies = [ [[package]] name = "reth-payload-builder" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -16209,7 +16210,7 @@ dependencies = [ [[package]] name = "reth-payload-builder-primitives" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "pin-project", "reth-payload-primitives", @@ -16221,7 +16222,7 @@ dependencies = [ [[package]] name = "reth-payload-primitives" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -16244,7 +16245,7 @@ dependencies = [ [[package]] name = "reth-payload-validator" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-rpc-types-engine", @@ -16254,7 +16255,7 @@ dependencies = [ [[package]] name = "reth-pipe-exec-layer-event-bus" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "reth-chain-state", @@ -16267,7 +16268,7 @@ dependencies = [ [[package]] name = "reth-pipe-exec-layer-ext-v2" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -16314,7 +16315,7 @@ dependencies = [ [[package]] name = "reth-pipe-exec-layer-relayer" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-network 2.0.5", "alloy-primitives", @@ -16338,7 +16339,7 @@ dependencies = [ [[package]] name = "reth-primitives" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "once_cell", @@ -16351,7 +16352,7 @@ dependencies = [ [[package]] name = "reth-primitives-traits" version = "0.4.1" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -16381,7 +16382,7 @@ dependencies = [ [[package]] name = "reth-provider" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -16424,7 +16425,7 @@ dependencies = [ [[package]] name = "reth-prune" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -16452,7 +16453,7 @@ dependencies = [ [[package]] name = "reth-prune-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "derive_more 2.1.1", @@ -16467,7 +16468,7 @@ dependencies = [ [[package]] name = "reth-ress-protocol" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -16486,7 +16487,7 @@ dependencies = [ [[package]] name = "reth-ress-provider" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -16513,7 +16514,7 @@ dependencies = [ [[package]] name = "reth-revm" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -16528,7 +16529,7 @@ dependencies = [ [[package]] name = "reth-rpc" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-dyn-abi", @@ -16605,7 +16606,7 @@ dependencies = [ [[package]] name = "reth-rpc-api" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eips 2.0.5", "alloy-genesis", @@ -16635,7 +16636,7 @@ dependencies = [ [[package]] name = "reth-rpc-builder" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-network 2.0.5", "alloy-provider 2.0.5", @@ -16678,7 +16679,7 @@ dependencies = [ [[package]] name = "reth-rpc-convert" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-evm", @@ -16699,7 +16700,7 @@ dependencies = [ [[package]] name = "reth-rpc-engine-api" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eips 2.0.5", "alloy-primitives", @@ -16730,7 +16731,7 @@ dependencies = [ [[package]] name = "reth-rpc-eth-api" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-dyn-abi", @@ -16776,7 +16777,7 @@ dependencies = [ [[package]] name = "reth-rpc-eth-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-chains", "alloy-consensus 2.0.5", @@ -16826,7 +16827,7 @@ dependencies = [ [[package]] name = "reth-rpc-layer" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-rpc-types-engine", "http 1.4.0", @@ -16840,7 +16841,7 @@ dependencies = [ [[package]] name = "reth-rpc-server-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eips 2.0.5", "alloy-primitives", @@ -16871,7 +16872,7 @@ dependencies = [ [[package]] name = "reth-stages" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -16915,7 +16916,7 @@ dependencies = [ [[package]] name = "reth-stages-api" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eips 2.0.5", "alloy-primitives", @@ -16943,7 +16944,7 @@ dependencies = [ [[package]] name = "reth-stages-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "bytes", @@ -16956,7 +16957,7 @@ dependencies = [ [[package]] name = "reth-static-file" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "parking_lot 0.12.5", @@ -16976,7 +16977,7 @@ dependencies = [ [[package]] name = "reth-static-file-types" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "clap 4.5.57", @@ -16990,7 +16991,7 @@ dependencies = [ [[package]] name = "reth-storage-api" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -17020,7 +17021,7 @@ dependencies = [ [[package]] name = "reth-storage-errors" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eips 2.0.5", "alloy-primitives", @@ -17038,7 +17039,7 @@ dependencies = [ [[package]] name = "reth-tasks" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "crossbeam-utils", "dashmap 6.2.1", @@ -17059,7 +17060,7 @@ dependencies = [ [[package]] name = "reth-tokio-util" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "tokio", "tokio-stream", @@ -17069,7 +17070,7 @@ dependencies = [ [[package]] name = "reth-tracing" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "clap 4.5.57", "eyre", @@ -17087,7 +17088,7 @@ dependencies = [ [[package]] name = "reth-tracing-otlp" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "base64 0.22.1", "clap 4.5.57", @@ -17105,7 +17106,7 @@ dependencies = [ [[package]] name = "reth-transaction-pool" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -17151,7 +17152,7 @@ dependencies = [ [[package]] name = "reth-trie" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -17176,7 +17177,7 @@ dependencies = [ [[package]] name = "reth-trie-common" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-consensus 2.0.5", "alloy-primitives", @@ -17186,7 +17187,6 @@ dependencies = [ "alloy-trie", "bytes", "derive_more 2.1.1", - "gravity-primitives", "itertools 0.14.0", "nybbles", "once_cell", @@ -17203,7 +17203,7 @@ dependencies = [ [[package]] name = "reth-trie-db" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -17222,7 +17222,7 @@ dependencies = [ [[package]] name = "reth-trie-parallel" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-eip7928 0.4.5", "alloy-evm", @@ -17251,7 +17251,7 @@ dependencies = [ [[package]] name = "reth-trie-sparse" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -17272,7 +17272,7 @@ dependencies = [ [[package]] name = "reth-trie-sparse-parallel" version = "2.3.0" -source = "git+https://github.com/Galxe/gravity-reth?rev=0c038e180cc26ae400f3fb2e6199e6f079f68fbc#0c038e180cc26ae400f3fb2e6199e6f079f68fbc" +source = "git+https://github.com/Galxe/gravity-reth?rev=e63817884bd015b73a9d3568828e4f50a07cd83a#e63817884bd015b73a9d3568828e4f50a07cd83a" dependencies = [ "alloy-primitives", "alloy-rlp", diff --git a/Cargo.toml b/Cargo.toml index 15383c26..81fe4e09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -496,7 +496,7 @@ debug-assertions = true debug = true [patch.crates-io] -reth-primitives-traits = { git = "https://github.com/Galxe/gravity-reth", rev = "0c038e180cc26ae400f3fb2e6199e6f079f68fbc" } +reth-primitives-traits = { git = "https://github.com/Galxe/gravity-reth", rev = "e63817884bd015b73a9d3568828e4f50a07cd83a" } serde-reflection = { git = "https://github.com/aptos-labs/serde-reflection", rev = "73b6bbf748334b71ff6d7d09d06a29e3062ca075" } merlin = { git = "https://github.com/aptos-labs/merlin" } tonic = { git = "https://github.com/aptos-labs/tonic.git", rev = "0da1ba8b1751d6e19eb55be24cccf9ae933c666e" } diff --git a/bin/gravity_node/Cargo.toml b/bin/gravity_node/Cargo.toml index 63c6b092..a9d8f957 100644 --- a/bin/gravity_node/Cargo.toml +++ b/bin/gravity_node/Cargo.toml @@ -33,7 +33,7 @@ sha2.workspace = true bincode = "1.3" time = "0.3.36" anyhow = "1.0.87" -greth = { git = "https://github.com/Galxe/gravity-reth", rev = "0c038e180cc26ae400f3fb2e6199e6f079f68fbc" } +greth = { git = "https://github.com/Galxe/gravity-reth", rev = "e63817884bd015b73a9d3568828e4f50a07cd83a" } reqwest = "0.12.9" alloy-primitives = { version = "=1.6.0", default-features = false, features = ["map-foldhash"] } alloy-eips = { version = "=2.0.5", default-features = false } diff --git a/gravity_e2e/cluster_test_cases/binance_price_feed_multivalidator/genesis.toml b/gravity_e2e/cluster_test_cases/binance_price_feed_multivalidator/genesis.toml index 9acb8ab1..914c0faf 100644 --- a/gravity_e2e/cluster_test_cases/binance_price_feed_multivalidator/genesis.toml +++ b/gravity_e2e/cluster_test_cases/binance_price_feed_multivalidator/genesis.toml @@ -1,6 +1,6 @@ [dependencies.genesis_contracts] repo = "https://github.com/Galxe/gravity_chain_core_contracts.git" -ref = "ccd88ee62d90d38f824def26b32063fd094807c7" +ref = "d90d81583b95adac73e86c8aafd27530414df03f" # node1 is the faucet so the test can execute the governance setup with one # development key. All four validators retain equal JWK voting power. diff --git a/gravity_e2e/cluster_test_cases/binance_price_feed_multivalidator/test_binance_price_feed_multivalidator.py b/gravity_e2e/cluster_test_cases/binance_price_feed_multivalidator/test_binance_price_feed_multivalidator.py index 8f0fbb7d..806c7343 100644 --- a/gravity_e2e/cluster_test_cases/binance_price_feed_multivalidator/test_binance_price_feed_multivalidator.py +++ b/gravity_e2e/cluster_test_cases/binance_price_feed_multivalidator/test_binance_price_feed_multivalidator.py @@ -7,6 +7,7 @@ import time import pytest +from eth_abi import decode from gravity_e2e.cluster.manager import Cluster from gravity_e2e.utils import oracle_test_support as support @@ -24,6 +25,36 @@ FEEDS = {1001: "NVDAUSDT", 1002: "TSLAUSDT"} TARGET_NONCE = 1 QUORUM_VALIDATORS = 3 +PRICE_V1_GOLDEN_INNER = bytes.fromhex( + "0100000000000007d101b2e020018e23f2365f0000000000000009543637a800" +) +PRICE_V1_GOLDEN_WRAPPER = bytes.fromhex( + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000018e23f2365f" + "0000000000000000000000000000000000000000000000000000000000000060" + "0000000000000000000000000000000000000000000000000000000000000020" + "0100000000000007d101b2e020018e23f2365f0000000000000009543637a800" +) + + +def test_price_v1_golden_vectors_match_contracts_and_reth(): + payload = PRICE_V1_GOLDEN_INNER + assert len(payload) == 32 + assert payload[0] == 1 + assert int.from_bytes(payload[1:9], "big") == 2001 + assert int.from_bytes(payload[9:13], "big") == 28_500_000 + assert int.from_bytes(payload[13:19], "big") == 1_710_000_059_999 + assert int.from_bytes(payload[19:31], "big") == 40_067_545_000 + assert payload[31] == 0 + + assert len(PRICE_V1_GOLDEN_WRAPPER) == 192 + nonce, source_position, callback_body = decode( + ["(uint128,uint256,bytes)"], PRICE_V1_GOLDEN_WRAPPER + )[0] + assert nonce == 1 + assert source_position == 1_710_000_059_999 + assert callback_body == payload def _price_feed_uri(feed_id: int, pair: str) -> str: @@ -235,13 +266,11 @@ async def test_four_validators_resolve_deterministic_binance_prices(cluster: Clu delivery_nonce = int(progress[0]) bucket_start = BUCKET_START_MS + (delivery_nonce - 1) * INTERVAL_MS assert latest == ( - True, bucket_start // INTERVAL_MS, bucket_start + INTERVAL_MS - 1, - DECIMALS, mock_scaled_price(pair, delivery_nonce - 1), ) - assert progress[1] == latest[2] + assert progress[1] == latest[1] expected_progress[feed_id] = progress expected_rounds[feed_id] = latest @@ -272,5 +301,5 @@ async def test_four_validators_resolve_deterministic_binance_prices(cluster: Clu LOG.info( "Four-validator Binance QC stored rounds: %s", - {feed_id: round_data[1] for feed_id, round_data in expected_rounds.items()}, + {feed_id: round_data[0] for feed_id, round_data in expected_rounds.items()}, ) diff --git a/gravity_e2e/cluster_test_cases/oracle_live_soak/README.md b/gravity_e2e/cluster_test_cases/oracle_live_soak/README.md new file mode 100644 index 00000000..090a6b42 --- /dev/null +++ b/gravity_e2e/cluster_test_cases/oracle_live_soak/README.md @@ -0,0 +1,189 @@ +# Wave 7 Binance Price Feed Live Soak + +This manual, non-gating suite runs four equal-power Gravity validators against +three continuous Binance Futures testnet closed index-price kline feeds: + +- `NVDAUSDT` (`sourceType=3`, feed ID `1001`); +- `BTCUSDT` (`sourceType=3`, feed ID `1002`); +- `ETHUSDT` (`sourceType=3`, feed ID `1003`). + +Each callback uses packed Price Feed V1: a canonical 32-byte big-endian body +containing `version`, `feedId`, `roundId`, `resolvedAtMs`, fixed-8 `price`, and +zero `flags`. The resolver exposes the latest round as +`(uint32 roundId, uint48 resolvedAtMs, uint96 price)`. + +The suite submits one Gravity governance proposal that registers the three +price tasks and the `PriceFeedResolver` callback. The same proposal changes the +epoch interval from the 60-second bootstrap value to two hours for `E+1`. It +proves that observers remain absent in proposal epoch `E`, start in `E+1`, and +reach a three-of-four JWK quorum before the soak timer begins. + +This is the initial Binance-only launch gate tracked by gravity-audit #1093. +The suite does not enable source type 6, deploy or register a Polymarket +resolver, create a Polymarket task or callback, require Polygon credentials, or +write a `gravity://6/...` validator mapping. The merged deterministic +Polymarket transport suite remains separate regression coverage for a possible +future activation. + +The short bootstrap epoch keeps dynamic task activation fast. The two-hour soak +epoch intentionally follows the repository's regular four-validator topology, +so a long Oracle soak does not also become a minute-by-minute reconfiguration +stress test. + +## What Every Heartbeat Checks + +- all four Gravity RPC replicas expose the same block hash at the common + confirmed height `min(latest node heights) - 16`; +- each replica returns the same NativeOracle progress and PriceFeedResolver + state at that exact EIP-1898 canonical block hash; +- bounded whole-snapshot retries tolerate a transient execution-view lag, while + a state difference that persists across the retry window fails the soak; +- every price snapshot uses a progress/resolver/progress seqlock read; +- each feed's delivery nonce, source position, and resolver round never regress; +- each latest resolver value maps to the pair's exact closed one-minute bucket; +- all three feeds and every Gravity node stay within the stall budget; +- at least three relayers have checkpointed each committed Binance nonce; +- the governance-pending two-hour epoch interval is active before timing starts. + +At completion, the suite fetches each exact final Binance bucket again and +compares its close with the corresponding onchain value. Each feed's +NativeOracle callback count must equal its independent final delivery nonce. +Observed price changes remain informational because timely identical index +closes are valid Oracle updates. + +For runs of at least one hour, `node4` becomes eligible to restart halfway +through by default. A focused run can configure multiple restart times. The +runner defers each restart while the chain is within five minutes of an epoch +boundary, then requires RPC, block height, and all three relayer checkpoints to +catch up without nonce regression. + +## Prerequisites + +- Current quick-release `gravity_node` and `gravity_cli` binaries. +- Foundry `forge` for the PriceFeedResolver artifact. +- Approved outbound access to Binance Futures testnet. + +The Binance Futures testnet `indexPriceKlines` endpoint is public and does not +require an API key. Each validator independently fetches all three pairs. The +values are testnet index data and must be labeled that way in any demo. The +generated relayer mapping and source metadata live under the ignored suite +`artifacts/` directory and are removed during normal teardown. + +The runner performs a global local `gravity_node` cleanup before and after the +suite. Do not run it beside another local Gravity cluster that must stay alive. + +## Build + +From the SDK repository root: + +```bash +make MODE=quick-release gravity_node gravity_cli +export PATH="$HOME/.foundry/bin:$PWD/target/quick-release:$PATH" +``` + +## Quick Burn-In + +Use a short no-restart run after changing task activation, payload handling, or +the price resolver: + +```bash +ORACLE_SOAK_DURATION_SECONDS=1800 \ +ORACLE_SOAK_POLL_SECONDS=15 \ +ORACLE_SOAK_STALL_TIMEOUT_SECONDS=900 \ +ORACLE_SOAK_MIN_ADVANCES=24 \ +ORACLE_SOAK_RESTART_AFTER_SECONDS=0 \ + ./gravity_e2e/run_test.sh \ + oracle_live_soak \ + --force-init \ + --log-cli-level=INFO +``` + +## Four-Hour Two-Restart Gate + +This is the focused release-profile stability run. It schedules node4 restarts +one and three hours into the monitored four-hour period, away from the expected +two-hour epoch boundaries: + +```bash +ORACLE_SOAK_DURATION_SECONDS=14400 \ +ORACLE_SOAK_POLL_SECONDS=15 \ +ORACLE_SOAK_STALL_TIMEOUT_SECONDS=900 \ +ORACLE_SOAK_MIN_ADVANCES=192 \ +ORACLE_SOAK_RESTART_SCHEDULE_SECONDS=3600,10800 \ + ./gravity_e2e/run_test.sh \ + oracle_live_soak \ + --force-init \ + --log-cli-level=INFO +``` + +The summary must contain two entries in `restartRecoveries`; the final snapshot +must still show all four replicas, every relayer checkpoint caught up, and every +price callback count equal to its source nonce. + +## Two-Hour Packed V1 Gate + +This focused compatibility run validates the packed contracts/reth/SDK stack +and restarts `node4` at the midpoint: + +```bash +ORACLE_SOAK_DURATION_SECONDS=7200 \ +ORACLE_SOAK_POLL_SECONDS=15 \ +ORACLE_SOAK_STALL_TIMEOUT_SECONDS=900 \ +ORACLE_SOAK_MIN_ADVANCES=96 \ +ORACLE_SOAK_RESTART_AFTER_SECONDS=3600 \ + ./gravity_e2e/run_test.sh \ + oracle_live_soak \ + --force-init \ + --log-cli-level=INFO +``` + +The final summary must report `status: passed`, one restart recovery, at least +96 advances for every feed, four converged replicas, and callback counts equal +to final source nonces. + +## Optional 24-Hour Soak + +Run from a terminal multiplexer or another session that will stay alive: + +```bash +ORACLE_SOAK_DURATION_SECONDS=86400 \ +ORACLE_SOAK_POLL_SECONDS=15 \ +ORACLE_SOAK_STALL_TIMEOUT_SECONDS=900 \ + ./gravity_e2e/run_test.sh \ + oracle_live_soak \ + --force-init \ + --log-cli-level=INFO +``` + +With the defaults, `node4` becomes eligible to restart after 12 hours and runs +as soon as the five-minute epoch-transition guard is open. The pytest process +is the monitor, performs final assertions, writes the report, and tears down the +cluster. A long-lived node can spend more than the harness's usual 30 seconds +reopening its persisted databases, so this suite gives restart RPC recovery a +three-minute window while still failing if the process exits. + +## Controls + +| Variable | Default | Meaning | +| --- | ---: | --- | +| `ORACLE_SOAK_DURATION_SECONDS` | `86400` | Monitored soak duration | +| `ORACLE_SOAK_POLL_SECONDS` | `15` | Onchain heartbeat interval | +| `ORACLE_SOAK_STALL_TIMEOUT_SECONDS` | `900` | Maximum feed or chain stall | +| `ORACLE_SOAK_MIN_ADVANCES` | 80% of expected minutes | Required nonce advances for each Binance feed | +| `ORACLE_SOAK_RESTART_AFTER_SECONDS` | Halfway for runs >= 1 hour | Earliest restart time; `0` disables | +| `ORACLE_SOAK_RESTART_SCHEDULE_SECONDS` | unset | Strictly increasing comma-separated restart times; mutually exclusive with `ORACLE_SOAK_RESTART_AFTER_SECONDS` | +| `ORACLE_SOAK_RESTART_NODE` | `node4` | Validator selected for restart | +| `ORACLE_SOAK_RESTART_RPC_TIMEOUT_SECONDS` | `180` | RPC recovery window for the restarted validator | +| `BINANCE_PRICE_FEED_BASE_URL` | `https://testnet.binancefuture.com` | Binance Futures testnet base URL | +| `BINANCE_PRICE_FEED_GRACE_MS` | `120000` | Closed-bucket safety delay | + +## Evidence + +Runtime evidence remains local and ignored by Git: + +- `artifacts/oracle_live_soak_heartbeat.jsonl`: one bounded checkpoint per poll; +- `artifacts/oracle_live_soak_summary.json`: final PASS/FAIL report and restart + recovery times. + +The summary is the acceptance artifact. A process that is still running has not +yet passed the soak. diff --git a/gravity_e2e/cluster_test_cases/oracle_live_soak/cluster.toml b/gravity_e2e/cluster_test_cases/oracle_live_soak/cluster.toml new file mode 100644 index 00000000..4dc9d61e --- /dev/null +++ b/gravity_e2e/cluster_test_cases/oracle_live_soak/cluster.toml @@ -0,0 +1,73 @@ +# Four-validator live Binance price-feed soak test. +# Keep fixed listeners below Linux's default ephemeral source-port range. + +[cluster] +name = "gravity-devnet-oracle-live-soak" +base_dir = "/tmp/gravity-cluster-oracle-live-soak" + +[genesis_source] +genesis_path = "./artifacts/genesis.json" +waypoint_path = "./artifacts/waypoint.txt" + +[[nodes]] +id = "node1" +role = "genesis" +source = { project_path = "../" } +host = "127.0.0.1" +validator_port = 26680 +vfn_port = 26690 +rpc_port = 26700 +api_port = 26710 +metrics_port = 26720 +inspection_port = 26730 +https_port = 26740 +authrpc_port = 26750 +reth_p2p_port = 26760 + +[[nodes]] +id = "node2" +role = "genesis" +source = { project_path = "../" } +host = "127.0.0.1" +validator_port = 26681 +vfn_port = 26691 +rpc_port = 26701 +api_port = 26711 +metrics_port = 26721 +inspection_port = 26731 +https_port = 26741 +authrpc_port = 26751 +reth_p2p_port = 26761 + +[[nodes]] +id = "node3" +role = "genesis" +source = { project_path = "../" } +host = "127.0.0.1" +validator_port = 26682 +vfn_port = 26692 +rpc_port = 26702 +api_port = 26712 +metrics_port = 26722 +inspection_port = 26732 +https_port = 26742 +authrpc_port = 26752 +reth_p2p_port = 26762 + +[[nodes]] +id = "node4" +role = "genesis" +source = { project_path = "../" } +host = "127.0.0.1" +validator_port = 26683 +vfn_port = 26693 +rpc_port = 26703 +api_port = 26713 +metrics_port = 26723 +inspection_port = 26733 +https_port = 26743 +authrpc_port = 26753 +reth_p2p_port = 26763 + +[faucet_init] +num_accounts = 0 diff --git a/gravity_e2e/cluster_test_cases/oracle_live_soak/genesis.toml b/gravity_e2e/cluster_test_cases/oracle_live_soak/genesis.toml new file mode 100644 index 00000000..27ccf7d2 --- /dev/null +++ b/gravity_e2e/cluster_test_cases/oracle_live_soak/genesis.toml @@ -0,0 +1,105 @@ +# Source type 3 is enabled at genesis, while its concrete price-feed tasks are +# registered by governance during the test. Source type 6 stays disabled for +# the initial Binance-only launch profile. + +[dependencies.genesis_contracts] +repo = "https://github.com/Galxe/gravity_chain_core_contracts.git" +ref = "d90d81583b95adac73e86c8aafd27530414df03f" + +# Equal voting power means a JWK QC requires matching signatures from at least +# three of the four validators. +[[genesis_validators]] +id = "node1" +address = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +host = "127.0.0.1" +validator_port = 26680 +vfn_port = 26690 +stake_amount = "2000000000000000000" +voting_power = "2000000000000000000" +consensus_pop = "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + +[[genesis_validators]] +id = "node2" +address = "0x7b254Bd44F6CE45e00a912b2460D47F3Be56fAD7" +host = "127.0.0.1" +validator_port = 26681 +vfn_port = 26691 +stake_amount = "2000000000000000000" +voting_power = "2000000000000000000" +consensus_pop = "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + +[[genesis_validators]] +id = "node3" +address = "0x9B2C25E77a97d3e84DC0Cb7F83fb676ddC4F24b9" +host = "127.0.0.1" +validator_port = 26682 +vfn_port = 26692 +stake_amount = "2000000000000000000" +voting_power = "2000000000000000000" +consensus_pop = "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + +[[genesis_validators]] +id = "node4" +address = "0x18c23753385ce7A60B15d171302E48b6AFf0BDC5" +host = "127.0.0.1" +validator_port = 26683 +vfn_port = 26693 +stake_amount = "2000000000000000000" +voting_power = "2000000000000000000" +consensus_pop = "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + +[genesis] +chain_id = 1337 +epoch_interval_micros = 60000000 +major_version = 1 +consensus_config = "0x0301010a00000000000000280000000000000001010000000a000000000000000100010200000000000000000020000000000000" +execution_config = "0x00" +initial_locked_until_micros = 1798848000000000 +governance_owner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + +[genesis.faucet] +address = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +balance = "0x2000000000000000000000000000000000000000000000000000000000000000" + +[genesis.validator_config] +minimum_bond = "1000000000000000000" +maximum_bond = "1000000000000000000000000" +unbonding_delay_micros = 604800000000 +allow_validator_set_change = true +voting_power_increase_limit_pct = 20 +max_validator_set_size = "100" +auto_evict_enabled = false +auto_evict_threshold_pct = 0 + +[genesis.staking_config] +minimum_stake = "1000000000000000000" +lockup_duration_micros = 86400000000 +unbonding_delay_micros = 86400000000 + +[genesis.governance_config] +min_voting_threshold = "1000000000000000000" +required_proposer_stake = "1000000000000000000" +voting_duration_micros = 5000000 + +[genesis.randomness_config] +variant = 1 +secrecy_threshold = 9223372036854775808 +reconstruction_threshold = 12297829382473033728 +fast_path_secrecy_threshold = 12297829382473033728 + +[genesis.oracle_config] +source_types = [1, 3] +callbacks = [ + "0x00000000000000000000000000000001625F4001", + "0x0000000000000000000000000000000000000000", +] + +[genesis.jwk_config] +issuers = ["0x68747470733a2f2f6163636f756e74732e676f6f676c652e636f6d"] + +[[genesis.jwk_config.jwks]] +kid = "f5f4c0ae6e6090a65ab0a694d6ba6f19d5d0b4e6" +kty = "RSA" +alg = "RS256" +e = "AQAB" +n = "2K7epoJWl_aBoYGpXmDBBiEnwQ0QdVRU1gsbGXNrEbrZEQdY5KjH5P5gZMq3d3KvT1j5KsD2tF_9jFMDLqV4VWDNJRLgSNJxhJuO_oLO2BXUSL9a7fLHxnZCUfJvT2K-O8AXjT3_ZM8UuL8d4jBn_fZLzdEI4MHrZLVSaHDvvKqL_mExQo6cFD-qyLZ-T6aHv2x8R7L_3X7E1nGMjKVVZMveQ_HMeXvnGxKf5yfEP0hIQlC_kFm4L_1kV1S0UPmMptZL2qI4VnXqmqI6TZJyE-3VXHgNn1Z1O_9QZlPC0fF0spLHf2S3nNqI0v3k2E7q3DkqxVf5xvn7q_X-gPqzVE9Jw" diff --git a/gravity_e2e/cluster_test_cases/oracle_live_soak/hooks.py b/gravity_e2e/cluster_test_cases/oracle_live_soak/hooks.py new file mode 100644 index 00000000..9045f864 --- /dev/null +++ b/gravity_e2e/cluster_test_cases/oracle_live_soak/hooks.py @@ -0,0 +1,153 @@ +"""Prepare live Binance inputs before deploying the soak cluster.""" + +import ipaddress +import json +import logging +import os +from pathlib import Path +import time +from urllib.parse import urlparse + +try: + import tomllib +except ImportError: + import tomli as tomllib + + +LOG = logging.getLogger(__name__) + +INTERVAL_MS = 60_000 +DEFAULT_GRACE_MS = 120_000 +DEFAULT_BINANCE_BASE_URL = "https://testnet.binancefuture.com" +BINANCE_FEEDS = ( + {"feedId": 1001, "pair": "NVDAUSDT"}, + {"feedId": 1002, "pair": "BTCUSDT"}, + {"feedId": 1003, "pair": "ETHUSDT"}, +) + +_METADATA_FILE = "oracle_live_soak_metadata.json" +_RELAYER_FILE = "relayer_config.live.json" +_HEARTBEAT_FILE = "oracle_live_soak_heartbeat.jsonl" +_SUMMARY_FILE = "oracle_live_soak_summary.json" + + +def _require_public_https_url(value: str, name: str) -> str: + parsed = urlparse(value) + if parsed.scheme != "https" or not parsed.hostname: + raise RuntimeError(f"{name} must be an https URL with a hostname") + hostname = parsed.hostname.lower() + if hostname == "localhost" or hostname.endswith(".localhost"): + raise RuntimeError(f"{name} must not use a loopback host") + try: + address = ipaddress.ip_address(hostname) + except ValueError: + address = None + if address is not None and not address.is_global: + raise RuntimeError(f"{name} must not use a private or loopback address") + return value.rstrip("/") + + +def _bucket_start_ms(env: dict) -> int: + configured = env.get("BINANCE_PRICE_FEED_BUCKET_START_MS") + if configured: + bucket_start_ms = int(configured) + else: + grace_ms = int(env.get("BINANCE_PRICE_FEED_GRACE_MS", DEFAULT_GRACE_MS)) + minimum_lag = (grace_ms + INTERVAL_MS - 1) // INTERVAL_MS + 1 + lag_minutes = int( + env.get("BINANCE_PRICE_FEED_LAG_MINUTES", minimum_lag) + ) + bucket_start_ms = ( + int(time.time() * 1000) // INTERVAL_MS - lag_minutes + ) * INTERVAL_MS + if bucket_start_ms <= 0 or bucket_start_ms % INTERVAL_MS != 0: + raise RuntimeError( + "BINANCE_PRICE_FEED_BUCKET_START_MS must be a positive aligned minute" + ) + return bucket_start_ms + + +def _price_uri( + feed_id: int, pair: str, bucket_start_ms: int, grace_ms: int +) -> str: + return ( + f"gravity://3/{feed_id}/price_feed?" + f"provider=binance_index_kline_v1&pair={pair}&interval=1m&" + f"bucketStartMs={bucket_start_ms}&decimals=8&graceMs={grace_ms}" + ) + + +def pre_deploy(test_dir: Path, env: dict, pytest_args: list[str]): + binance_base_url = _require_public_https_url( + env.get("BINANCE_PRICE_FEED_BASE_URL", DEFAULT_BINANCE_BASE_URL), + "BINANCE_PRICE_FEED_BASE_URL", + ) + grace_ms = int(env.get("BINANCE_PRICE_FEED_GRACE_MS", DEFAULT_GRACE_MS)) + if grace_ms < 0: + raise RuntimeError("BINANCE_PRICE_FEED_GRACE_MS must be non-negative") + bucket_start_ms = _bucket_start_ms(env) + binance_feeds = [ + { + "baseUrl": binance_base_url, + **feed, + "bucketStartMs": bucket_start_ms, + "graceMs": grace_ms, + "taskUri": _price_uri( + feed["feedId"], feed["pair"], bucket_start_ms, grace_ms + ), + } + for feed in BINANCE_FEEDS + ] + artifacts = test_dir / "artifacts" + artifacts.mkdir(parents=True, exist_ok=True) + for name in (_HEARTBEAT_FILE, _SUMMARY_FILE): + (artifacts / name).unlink(missing_ok=True) + + relayer_path = artifacts / _RELAYER_FILE + relayer_path.write_text( + json.dumps( + { + "uri_mappings": { + feed["taskUri"]: binance_base_url + for feed in binance_feeds + } + }, + indent=2, + ) + + "\n" + ) + metadata_path = artifacts / _METADATA_FILE + metadata_path.write_text( + json.dumps( + {"binanceFeeds": binance_feeds}, + indent=2, + ensure_ascii=False, + ) + + "\n" + ) + env["RELAYER_CONFIG_TPL"] = str(relayer_path) + LOG.info( + "Prepared live Oracle inputs: Binance pairs=%s anchor=%s", + [feed["pair"] for feed in binance_feeds], + bucket_start_ms, + ) + + +def post_stop(test_dir: Path, env: dict): + artifacts = test_dir / "artifacts" + for name in (_METADATA_FILE, _RELAYER_FILE): + (artifacts / name).unlink(missing_ok=True) + + cluster_config = Path( + env.get("GRAVITY_CLUSTER_CONFIG", test_dir / "cluster.toml") + ) + try: + with cluster_config.open("rb") as config_file: + config = tomllib.load(config_file) + base_dir = Path(config["cluster"]["base_dir"]) + for node in config.get("nodes", []): + (base_dir / node["id"] / "config" / "relayer_config.json").unlink( + missing_ok=True + ) + except (KeyError, OSError, tomllib.TOMLDecodeError) as error: + LOG.warning("Could not remove deployed live relayer configs: %s", error) diff --git a/gravity_e2e/cluster_test_cases/oracle_live_soak/test_oracle_live_soak.py b/gravity_e2e/cluster_test_cases/oracle_live_soak/test_oracle_live_soak.py new file mode 100644 index 00000000..90b271e6 --- /dev/null +++ b/gravity_e2e/cluster_test_cases/oracle_live_soak/test_oracle_live_soak.py @@ -0,0 +1,1309 @@ +"""Manual four-validator soak against live Binance index-price data.""" + +import asyncio +from dataclasses import dataclass +from decimal import Decimal +from eth_utils.abi import get_abi_output_types +from hexbytes import HexBytes +import json +import logging +import os +from pathlib import Path +import time +from urllib.parse import urlencode +from urllib.request import urlopen + +try: + import tomllib +except ImportError: + import tomli as tomllib + +import pytest +from web3 import Web3 +from web3._utils.abi import map_abi_data +from web3._utils.normalizers import BASE_RETURN_NORMALIZERS + +from gravity_e2e.cluster.manager import Cluster +from gravity_e2e.utils import oracle_test_support as support + + +LOG = logging.getLogger(__name__) +SUITE_DIR = Path(__file__).resolve().parent + +INTERVAL_MS = 60_000 +DECIMALS = 8 +RECONFIGURATION_ADDRESS = Web3.to_checksum_address( + "0x00000000000000000000000000000001625F2003" +) +EPOCH_CONFIG_ADDRESS = Web3.to_checksum_address( + "0x00000000000000000000000000000001625F1005" +) +SEL_CURRENT_EPOCH = Web3.keccak(text="currentEpoch()")[:4] +SEL_REMAINING_TIME = Web3.keccak(text="getRemainingTimeSeconds()")[:4] +CALLBACK_SUCCESS_TOPIC0 = Web3.keccak( + text="CallbackSuccess(uint32,uint256,uint128,address)" +).hex() + +DEFAULT_SOAK_SECONDS = 24 * 60 * 60 +DEFAULT_POLL_SECONDS = 15 +DEFAULT_STALL_SECONDS = 15 * 60 +DEFAULT_RESTART_RPC_TIMEOUT_SECONDS = 3 * 60 +EPOCH_TIMEOUT_SECONDS = 180 +ORACLE_TIMEOUT_SECONDS = 360 +MIN_PROPOSAL_WINDOW_SECONDS = 40 +QUORUM_VALIDATORS = 3 +BOOTSTRAP_EPOCH_INTERVAL_MICROS = 60 * 1_000_000 +SOAK_EPOCH_INTERVAL_MICROS = 2 * 60 * 60 * 1_000_000 +RESTART_EPOCH_GUARD_SECONDS = 5 * 60 +SNAPSHOT_CONFIRMATION_BLOCKS = 16 +SNAPSHOT_READ_RETRIES = 20 +SNAPSHOT_CONVERGENCE_RETRIES = 20 +SNAPSHOT_CONVERGENCE_DELAY_SECONDS = 0.25 +MAX_GET_LOGS_BLOCKS = 100_000 + + +class SnapshotNotConverged(AssertionError): + """A canonical snapshot is not yet readable consistently by every RPC.""" + +_HEARTBEAT_FILE = "oracle_live_soak_heartbeat.jsonl" +_SUMMARY_FILE = "oracle_live_soak_summary.json" + + +@dataclass(frozen=True) +class SoakSettings: + duration_seconds: int + poll_seconds: int + stall_seconds: int + minimum_advances: int + restart_schedule_seconds: tuple[int, ...] + restart_node: str + restart_rpc_timeout_seconds: int + + +def _env_int(name: str, default: int, minimum: int = 0) -> int: + value = int(os.environ.get(name, str(default))) + if value < minimum: + raise ValueError(f"{name} must be at least {minimum}") + return value + + +def _soak_settings() -> SoakSettings: + duration = _env_int( + "ORACLE_SOAK_DURATION_SECONDS", DEFAULT_SOAK_SECONDS, minimum=1 + ) + poll_seconds = _env_int( + "ORACLE_SOAK_POLL_SECONDS", DEFAULT_POLL_SECONDS, minimum=1 + ) + stall_seconds = _env_int( + "ORACLE_SOAK_STALL_TIMEOUT_SECONDS", + DEFAULT_STALL_SECONDS, + minimum=poll_seconds * 2, + ) + configured_minimum = os.environ.get("ORACLE_SOAK_MIN_ADVANCES") + minimum_advances = ( + max(1, (duration // 60) * 8 // 10) + if configured_minimum is None and duration >= 120 + else int(configured_minimum or 0) + ) + if minimum_advances < 0: + raise ValueError("ORACLE_SOAK_MIN_ADVANCES must be non-negative") + + configured_schedule = os.environ.get( + "ORACLE_SOAK_RESTART_SCHEDULE_SECONDS" + ) + configured_restart = os.environ.get("ORACLE_SOAK_RESTART_AFTER_SECONDS") + if configured_schedule is not None and configured_restart is not None: + raise ValueError( + "configure only one of ORACLE_SOAK_RESTART_SCHEDULE_SECONDS " + "and ORACLE_SOAK_RESTART_AFTER_SECONDS" + ) + if configured_schedule is not None: + restart_schedule = tuple( + int(value.strip()) + for value in configured_schedule.split(",") + if value.strip() + ) + elif configured_restart is not None: + restart_after = int(configured_restart or 0) + restart_schedule = (restart_after,) if restart_after else () + elif duration >= 60 * 60: + restart_schedule = (duration // 2,) + else: + restart_schedule = () + if any( + restart_after <= 0 or restart_after >= duration + for restart_after in restart_schedule + ): + raise ValueError( + "restart schedule entries must be positive and less than duration" + ) + if tuple(sorted(set(restart_schedule))) != restart_schedule: + raise ValueError( + "restart schedule entries must be unique and strictly increasing" + ) + return SoakSettings( + duration_seconds=duration, + poll_seconds=poll_seconds, + stall_seconds=stall_seconds, + minimum_advances=minimum_advances, + restart_schedule_seconds=restart_schedule, + restart_node=os.environ.get("ORACLE_SOAK_RESTART_NODE", "node4"), + restart_rpc_timeout_seconds=_env_int( + "ORACLE_SOAK_RESTART_RPC_TIMEOUT_SECONDS", + DEFAULT_RESTART_RPC_TIMEOUT_SECONDS, + minimum=30, + ), + ) + + +def test_soak_settings_defaults_to_one_midpoint_restart(monkeypatch): + monkeypatch.setenv("ORACLE_SOAK_DURATION_SECONDS", "7200") + monkeypatch.delenv( + "ORACLE_SOAK_RESTART_SCHEDULE_SECONDS", raising=False + ) + monkeypatch.delenv("ORACLE_SOAK_RESTART_AFTER_SECONDS", raising=False) + + assert _soak_settings().restart_schedule_seconds == (3600,) + + +def test_soak_settings_accepts_multiple_restarts(monkeypatch): + monkeypatch.setenv("ORACLE_SOAK_DURATION_SECONDS", "1200") + monkeypatch.setenv( + "ORACLE_SOAK_RESTART_SCHEDULE_SECONDS", "360, 720,960" + ) + monkeypatch.delenv("ORACLE_SOAK_RESTART_AFTER_SECONDS", raising=False) + + assert _soak_settings().restart_schedule_seconds == (360, 720, 960) + + +def test_soak_settings_legacy_zero_disables_restart(monkeypatch): + monkeypatch.setenv("ORACLE_SOAK_DURATION_SECONDS", "7200") + monkeypatch.delenv( + "ORACLE_SOAK_RESTART_SCHEDULE_SECONDS", raising=False + ) + monkeypatch.setenv("ORACLE_SOAK_RESTART_AFTER_SECONDS", "0") + + assert _soak_settings().restart_schedule_seconds == () + + +@pytest.mark.parametrize("schedule", ["720,360", "360,360", "0", "1200"]) +def test_soak_settings_rejects_invalid_restart_schedule( + monkeypatch, schedule +): + monkeypatch.setenv("ORACLE_SOAK_DURATION_SECONDS", "1200") + monkeypatch.setenv("ORACLE_SOAK_RESTART_SCHEDULE_SECONDS", schedule) + monkeypatch.delenv("ORACLE_SOAK_RESTART_AFTER_SECONDS", raising=False) + + with pytest.raises(ValueError): + _soak_settings() + + +def test_soak_settings_rejects_two_restart_controls(monkeypatch): + monkeypatch.setenv("ORACLE_SOAK_DURATION_SECONDS", "1200") + monkeypatch.setenv("ORACLE_SOAK_RESTART_SCHEDULE_SECONDS", "360") + monkeypatch.setenv("ORACLE_SOAK_RESTART_AFTER_SECONDS", "720") + + with pytest.raises(ValueError, match="configure only one"): + _soak_settings() + + +def _metadata() -> dict: + path = SUITE_DIR / "artifacts" / "oracle_live_soak_metadata.json" + return json.loads(path.read_text()) + + +def _current_epoch(w3: Web3) -> int: + return int.from_bytes( + w3.eth.call({"to": RECONFIGURATION_ADDRESS, "data": SEL_CURRENT_EPOCH}), + "big", + ) + + +def _remaining_epoch_seconds(w3: Web3) -> int: + return int.from_bytes( + w3.eth.call( + {"to": RECONFIGURATION_ADDRESS, "data": SEL_REMAINING_TIME} + ), + "big", + ) + + +def _restart_window_is_safe(w3: Web3) -> bool: + remaining = _remaining_epoch_seconds(w3) + interval = SOAK_EPOCH_INTERVAL_MICROS // 1_000_000 + return ( + RESTART_EPOCH_GUARD_SECONDS < remaining + < interval - RESTART_EPOCH_GUARD_SECONDS + ) + + +async def _wait_for_epoch_advance(cluster: Cluster, start_epoch: int) -> int: + deadline = time.monotonic() + EPOCH_TIMEOUT_SECONDS + node1 = cluster.get_node("node1") + while time.monotonic() < deadline: + epoch = _current_epoch(node1.w3) + if epoch > start_epoch: + for node_id, node in cluster.nodes.items(): + while _current_epoch(node.w3) < epoch: + if time.monotonic() >= deadline: + raise TimeoutError(f"{node_id} did not reach epoch {epoch}") + await asyncio.sleep(1) + return epoch + await asyncio.sleep(1) + raise TimeoutError(f"chain did not advance past epoch {start_epoch}") + + +async def _wait_for_proposal_window(cluster: Cluster) -> int: + node1 = cluster.get_node("node1") + deadline = time.monotonic() + EPOCH_TIMEOUT_SECONDS + while time.monotonic() < deadline: + remaining = _remaining_epoch_seconds(node1.w3) + if remaining >= MIN_PROPOSAL_WINDOW_SECONDS: + return _current_epoch(node1.w3) + current = _current_epoch(node1.w3) + LOG.info( + "Only %ss remain in epoch %s; waiting for a proposal window", + remaining, + current, + ) + await _wait_for_epoch_advance(cluster, current) + raise TimeoutError("no epoch had enough time for governance activation") + + +def _validator_log(cluster: Cluster, node_id: str) -> Path: + return cluster.base_dir / node_id / "consensus_log" / "validator.log" + + +def _log_contains(cluster: Cluster, node_id: str, marker: str, issuer: str) -> bool: + path = _validator_log(cluster, node_id) + content = path.read_text(errors="replace") if path.exists() else "" + return any(marker in line and issuer in line for line in content.splitlines()) + + +async def _wait_for_consensus_evidence( + cluster: Cluster, issuers: list[str] +) -> None: + missing_observers = { + (node_id, issuer) + for node_id in cluster.nodes + for issuer in issuers + } + certifiers = {issuer: set() for issuer in issuers} + missing_quorums = set(issuers) + deadline = time.monotonic() + ORACLE_TIMEOUT_SECONDS + while time.monotonic() < deadline: + logs = { + node_id: ( + _validator_log(cluster, node_id).read_text(errors="replace") + if _validator_log(cluster, node_id).exists() + else "" + ) + for node_id in cluster.nodes + } + for node_id, issuer in list(missing_observers): + if any( + "JWKObserver spawned." in line and issuer in line + for line in logs[node_id].splitlines() + ): + missing_observers.remove((node_id, issuer)) + for issuer in issuers: + for node_id, content in logs.items(): + lines = content.splitlines() + if any( + "Start certifying update." in line and issuer in line + for line in lines + ): + certifiers[issuer].add(node_id) + if any( + "Peer vote aggregated." in line + and issuer in line + and ( + "threshold_exceeded=true" in line + or '"threshold_exceeded":true' in line + ) + for line in lines + ): + missing_quorums.discard(issuer) + if ( + not missing_observers + and not missing_quorums + and all( + len(certifiers[issuer]) >= QUORUM_VALIDATORS + for issuer in issuers + ) + ): + return + await asyncio.sleep(2) + raise AssertionError( + "missing consensus evidence: " + f"observers={sorted(missing_observers)} " + f"certifiers={ {key: sorted(value) for key, value in certifiers.items()} } " + f"quorums={sorted(missing_quorums)}" + ) + + +async def _wait_for_block( + node_id: str, w3: Web3, block_number: int, timeout: int = 90 +): + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + try: + if w3.eth.block_number >= block_number: + return + except Exception: + pass + await asyncio.sleep(1) + last_height = None + try: + last_height = w3.eth.block_number + except Exception: + pass + raise TimeoutError( + f"{node_id} RPC did not reach Gravity block {block_number}; " + f"last height was {last_height}" + ) + + +def _call_at_block_hash(function, block_hash: str): + response = function.w3.provider.make_request( + "eth_call", + [ + { + "to": function.address, + "data": function._encode_transaction_data(), + }, + {"blockHash": block_hash, "requireCanonical": True}, + ], + ) + if "error" in response: + raise SnapshotNotConverged( + f"EIP-1898 eth_call failed at {block_hash}: {response['error']}" + ) + output_types = get_abi_output_types(function.abi) + decoded = function.w3.codec.decode( + output_types, HexBytes(response["result"]) + ) + normalized = map_abi_data( + BASE_RETURN_NORMALIZERS, output_types, decoded + ) + return normalized[0] if len(normalized) == 1 else normalized + + +def _price_state_at_block_hash( + native_oracle, + price_resolver, + feed_id: int, + pair: str, + block_hash: str, +) -> tuple[tuple, tuple]: + for _ in range(SNAPSHOT_READ_RETRIES): + progress_before = tuple( + _call_at_block_hash( + native_oracle.functions.getSourceProgress( + support.SOURCE_TYPE_PRICE_FEED, feed_id + ), + block_hash, + ) + ) + latest = tuple( + _call_at_block_hash( + price_resolver.functions.latestPrice(feed_id), block_hash + ) + ) + progress_after = tuple( + _call_at_block_hash( + native_oracle.functions.getSourceProgress( + support.SOURCE_TYPE_PRICE_FEED, feed_id + ), + block_hash, + ) + ) + if ( + progress_before == progress_after + and latest[0] > 0 + and progress_after[1] == latest[1] + ): + return progress_after, latest + time.sleep(0.05) + raise SnapshotNotConverged( + f"could not obtain an atomic {pair} Oracle snapshot at {block_hash}: " + f"progress_before={progress_before}, latest={latest}, " + f"progress_after={progress_after}" + ) + + +def _topic(value: int) -> str: + return "0x" + value.to_bytes(32, "big").hex() + + +def _callback_success_logs( + w3: Web3, + source_type: int, + source_id: int, + from_block: int, + to_block: int | str = "latest", +) -> list: + final_block = ( + int(w3.eth.block_number) if to_block == "latest" else int(to_block) + ) + if final_block < from_block: + return [] + + logs = [] + for chunk_start in range( + from_block, final_block + 1, MAX_GET_LOGS_BLOCKS + ): + chunk_end = min( + chunk_start + MAX_GET_LOGS_BLOCKS - 1, final_block + ) + logs.extend( + w3.eth.get_logs( + { + "fromBlock": chunk_start, + "toBlock": chunk_end, + "address": support.NATIVE_ORACLE_ADDRESS, + "topics": [ + CALLBACK_SUCCESS_TOPIC0, + _topic(source_type), + _topic(source_id), + ], + } + ) + ) + return logs + + +def test_callback_success_logs_chunks_large_ranges(): + class FakeEth: + block_number = 400_000 + + def __init__(self): + self.requests = [] + + def get_logs(self, request): + self.requests.append(request) + return [(request["fromBlock"], request["toBlock"])] + + class FakeWeb3: + def __init__(self): + self.eth = FakeEth() + + w3 = FakeWeb3() + logs = _callback_success_logs(w3, 3, 1001, 68, 343_799) + + assert logs == [ + (68, 100_067), + (100_068, 200_067), + (200_068, 300_067), + (300_068, 343_799), + ] + assert all( + request["address"] == support.NATIVE_ORACLE_ADDRESS + and request["topics"] + == [CALLBACK_SUCCESS_TOPIC0, _topic(3), _topic(1001)] + for request in w3.eth.requests + ) + + +def test_callback_success_logs_snapshots_latest_once(): + class FakeEth: + block_number = 200_001 + + def __init__(self): + self.requests = [] + + def get_logs(self, request): + self.requests.append(request) + return [] + + class FakeWeb3: + def __init__(self): + self.eth = FakeEth() + + w3 = FakeWeb3() + assert _callback_success_logs(w3, 3, 42, 0) == [] + assert [ + (request["fromBlock"], request["toBlock"]) + for request in w3.eth.requests + ] == [(0, 99_999), (100_000, 199_999), (200_000, 200_001)] + + +def _round_start_ms(first_bucket_start_ms: int, delivery_nonce: int) -> int: + return first_bucket_start_ms + (delivery_nonce - 1) * INTERVAL_MS + + +def _fetch_binance_price( + base_url: str, pair: str, bucket_start_ms: int +) -> int: + bucket_end_ms = bucket_start_ms + INTERVAL_MS - 1 + query = urlencode( + { + "pair": pair, + "interval": "1m", + "startTime": bucket_start_ms, + "endTime": bucket_end_ms, + "limit": 1, + } + ) + with urlopen( + f"{base_url}/fapi/v1/indexPriceKlines?{query}", timeout=20 + ) as response: + rows = json.loads(response.read()) + assert len(rows) == 1, f"Binance returned {len(rows)} rows for {pair}" + assert int(rows[0][0]) == bucket_start_ms + assert int(rows[0][6]) == bucket_end_ms + scaled = Decimal(rows[0][4]) * (10**DECIMALS) + assert scaled == scaled.to_integral_value() + return int(scaled) + + +async def _fetch_binance_price_with_retry( + base_url: str, + pair: str, + bucket_start_ms: int, + timeout: int = 120, +) -> int: + deadline = time.monotonic() + timeout + last_error = None + while time.monotonic() < deadline: + try: + return await asyncio.to_thread( + _fetch_binance_price, base_url, pair, bucket_start_ms + ) + except (AssertionError, OSError, ValueError) as error: + last_error = error + await asyncio.sleep(5) + raise TimeoutError( + f"Binance did not return the exact closed {pair} bucket" + ) from last_error + + +def _relayer_sources(cluster: Cluster, node_id: str) -> dict: + path = cluster.base_dir / node_id / "data" / "reth" / "relayer_state.json" + if not path.exists(): + return {} + try: + return json.loads(path.read_text()).get("sources", {}) + except (OSError, json.JSONDecodeError): + return {} + + +def _relayer_quorum(cluster: Cluster, uri: str, minimum_nonce: int) -> list[str]: + reached = [] + for node_id in cluster.nodes: + raw_nonce = _relayer_sources(cluster, node_id).get(uri, {}).get("last_nonce") + try: + nonce = int(raw_nonce or 0) + except (TypeError, ValueError): + nonce = 0 + if nonce >= minimum_nonce: + reached.append(node_id) + return sorted(reached) + + +async def _wait_for_relayer_node( + cluster: Cluster, + node_id: str, + uri: str, + minimum_nonce: int, + timeout: int = 180, +) -> None: + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + state = _relayer_sources(cluster, node_id).get(uri, {}) + try: + nonce = int(state.get("last_nonce") or 0) + except (TypeError, ValueError): + nonce = 0 + if nonce >= minimum_nonce: + return + await asyncio.sleep(2) + raise TimeoutError( + f"{node_id} relayer did not catch up to nonce {minimum_nonce}" + ) + + +async def _replicated_snapshot_once( + cluster: Cluster, + native_artifact: dict, + price_artifact: dict, + price_resolver_address: str, + binance_feeds: list[dict], +) -> dict: + heights = {} + for node_id, node in cluster.nodes.items(): + assert node.w3.is_connected(), f"{node_id} RPC disconnected" + heights[node_id] = node.w3.eth.block_number + + minimum_height = min(heights.values()) + assert minimum_height >= SNAPSHOT_CONFIRMATION_BLOCKS, ( + "Gravity chain is too young for a confirmed Oracle snapshot" + ) + snapshot_block = minimum_height - SNAPSHOT_CONFIRMATION_BLOCKS + snapshot_hash = None + for node_id, node in cluster.nodes.items(): + block = node.w3.eth.get_block(snapshot_block) + assert int(block["number"]) == snapshot_block + block_hash = Web3.to_hex(block["hash"]).lower() + if snapshot_hash is None: + snapshot_hash = block_hash + if block_hash != snapshot_hash: + raise SnapshotNotConverged( + f"{node_id} block hash diverged at Gravity block " + f"{snapshot_block}: {block_hash} != {snapshot_hash}" + ) + + node1 = cluster.get_node("node1") + + native_oracle = node1.w3.eth.contract( + address=support.NATIVE_ORACLE_ADDRESS, abi=native_artifact["abi"] + ) + price_resolver = node1.w3.eth.contract( + address=price_resolver_address, abi=price_artifact["abi"] + ) + price_feeds = {} + for feed in binance_feeds: + feed_id = int(feed["feedId"]) + pair = feed["pair"] + progress, latest = _price_state_at_block_hash( + native_oracle, + price_resolver, + feed_id, + pair, + snapshot_hash, + ) + assert progress[0] >= 1 + round_start = _round_start_ms( + int(feed["bucketStartMs"]), progress[0] + ) + assert latest[0] == round_start // INTERVAL_MS + assert latest[1] == round_start + INTERVAL_MS - 1 + assert progress[1] == latest[1] + assert latest[2] > 0 + price_feeds[pair] = { + "feedId": feed_id, + "progress": progress, + "latestPrice": latest, + } + + for node_id, node in cluster.nodes.items(): + replica_native = node.w3.eth.contract( + address=support.NATIVE_ORACLE_ADDRESS, abi=native_artifact["abi"] + ) + replica_price = node.w3.eth.contract( + address=price_resolver_address, abi=price_artifact["abi"] + ) + for feed in binance_feeds: + feed_id = int(feed["feedId"]) + pair = feed["pair"] + replica_progress, replica_latest = _price_state_at_block_hash( + replica_native, + replica_price, + feed_id, + pair, + snapshot_hash, + ) + if replica_progress != price_feeds[pair]["progress"]: + raise SnapshotNotConverged( + f"{node_id} {pair} progress diverged: " + f"{replica_progress} != " + f"{price_feeds[pair]['progress']}" + ) + if replica_latest != price_feeds[pair]["latestPrice"]: + raise SnapshotNotConverged( + f"{node_id} {pair} latest price diverged: " + f"{replica_latest} != " + f"{price_feeds[pair]['latestPrice']}" + ) + + return { + "block": snapshot_block, + "blockHash": snapshot_hash, + "heights": heights, + "priceFeeds": price_feeds, + } + + +async def _replicated_snapshot( + cluster: Cluster, + native_artifact: dict, + price_artifact: dict, + price_resolver_address: str, + binance_feeds: list[dict], +) -> dict: + first_error = None + for attempt in range(1, SNAPSHOT_CONVERGENCE_RETRIES + 1): + try: + snapshot = await _replicated_snapshot_once( + cluster, + native_artifact, + price_artifact, + price_resolver_address, + binance_feeds, + ) + if attempt > 1: + LOG.info( + "canonical replica snapshot converged after %d attempts", + attempt, + ) + return snapshot + except SnapshotNotConverged as exc: + if first_error is None: + first_error = str(exc) + LOG.warning( + "canonical replica snapshot not yet converged: %s", exc + ) + if attempt == SNAPSHOT_CONVERGENCE_RETRIES: + raise AssertionError( + "canonical replica snapshot did not converge after " + f"{attempt} attempts; first={first_error}; last={exc}" + ) from exc + await asyncio.sleep(SNAPSHOT_CONVERGENCE_DELAY_SECONDS) + + raise AssertionError("unreachable replica snapshot retry state") + + +def _append_json_line(path: Path, payload: dict) -> None: + with path.open("a") as output: + output.write(json.dumps(payload, sort_keys=True) + "\n") + + +async def _restart_validator( + cluster: Cluster, + node_id: str, + target_block: int, + price_targets: list[tuple[str, int]], + rpc_timeout: int, +) -> float: + node = cluster.get_node(node_id) + if node is None: + raise AssertionError(f"unknown restart node {node_id}") + started = time.monotonic() + assert await node.restart(rpc_timeout=rpc_timeout), ( + f"failed to restart {node_id} within {rpc_timeout}s RPC timeout" + ) + await _wait_for_block(node_id, node.w3, target_block, timeout=180) + assert await cluster.check_block_increasing(timeout=60) + for uri, target_nonce in price_targets: + await _wait_for_relayer_node( + cluster, node_id, uri, target_nonce + ) + return time.monotonic() - started + + +async def _run_soak( + cluster: Cluster, + settings: SoakSettings, + native_artifact: dict, + price_artifact: dict, + price_resolver_address: str, + binance_feeds: list[dict], +) -> dict: + heartbeat_path = SUITE_DIR / "artifacts" / _HEARTBEAT_FILE + heartbeat_path.unlink(missing_ok=True) + + initial_tip = max( + node.w3.eth.block_number for node in cluster.nodes.values() + ) + confirmation_target = ( + initial_tip + SNAPSHOT_CONFIRMATION_BLOCKS + 1 + ) + for node_id, node in cluster.nodes.items(): + await _wait_for_block( + node_id, node.w3, confirmation_target, timeout=90 + ) + + initial = await _replicated_snapshot( + cluster, + native_artifact, + price_artifact, + price_resolver_address, + binance_feeds, + ) + initial_nonces = { + pair: int(state["progress"][0]) + for pair, state in initial["priceFeeds"].items() + } + last_nonces = dict(initial_nonces) + last_prices = { + pair: int(state["latestPrice"][2]) + for pair, state in initial["priceFeeds"].items() + } + observed_price_changes = {pair: 0 for pair in initial_nonces} + started = time.monotonic() + deadline = started + settings.duration_seconds + last_price_advances = {pair: started for pair in initial_nonces} + max_price_gaps = {pair: 0.0 for pair in initial_nonces} + last_height_change = {node_id: started for node_id in cluster.nodes} + previous_heights = dict(initial["heights"]) + restart_index = 0 + restart_recoveries = [] + restart_epoch_guard_deferrals = 0 + samples = 0 + final = initial + + while time.monotonic() < deadline: + now = time.monotonic() + if ( + restart_index < len(settings.restart_schedule_seconds) + and now - started + >= settings.restart_schedule_seconds[restart_index] + ): + epoch_rpc = next(iter(cluster.nodes.values())).w3 + if _restart_window_is_safe(epoch_rpc): + scheduled_after = settings.restart_schedule_seconds[ + restart_index + ] + recovery_seconds = await _restart_validator( + cluster, + settings.restart_node, + max(int(height) for height in final["heights"].values()), + [ + ( + feed["taskUri"], + int( + final["priceFeeds"][feed["pair"]][ + "progress" + ][0] + ), + ) + for feed in binance_feeds + ], + settings.restart_rpc_timeout_seconds, + ) + restart_recoveries.append( + { + "sequence": restart_index + 1, + "scheduledAfterSeconds": scheduled_after, + "actualAfterSeconds": round( + time.monotonic() - started, 3 + ), + "recoverySeconds": round(recovery_seconds, 3), + } + ) + restart_index += 1 + else: + restart_epoch_guard_deferrals += 1 + if restart_epoch_guard_deferrals == 1 or ( + restart_epoch_guard_deferrals - 1 + ) % 20 == 0: + LOG.info( + "deferring validator restart %d/%d outside the " + "epoch transition guard", + restart_index + 1, + len(settings.restart_schedule_seconds), + ) + + final = await _replicated_snapshot( + cluster, + native_artifact, + price_artifact, + price_resolver_address, + binance_feeds, + ) + now = time.monotonic() + price_heartbeats = {} + for feed in binance_feeds: + pair = feed["pair"] + state = final["priceFeeds"][pair] + nonce = int(state["progress"][0]) + price = int(state["latestPrice"][2]) + assert nonce >= last_nonces[pair], ( + f"{pair} source nonce regressed" + ) + if nonce > last_nonces[pair]: + max_price_gaps[pair] = max( + max_price_gaps[pair], now - last_price_advances[pair] + ) + last_price_advances[pair] = now + last_nonces[pair] = nonce + if price != last_prices[pair]: + observed_price_changes[pair] += 1 + last_prices[pair] = price + assert now - last_price_advances[pair] <= settings.stall_seconds, ( + f"{pair} price feed stalled at nonce {nonce} for " + f"{now - last_price_advances[pair]:.1f}s" + ) + relayer_nodes = _relayer_quorum( + cluster, feed["taskUri"], nonce + ) + assert len(relayer_nodes) >= QUORUM_VALIDATORS, ( + f"only {relayer_nodes} persisted {pair} nonce {nonce}" + ) + price_heartbeats[pair] = { + "feedId": int(feed["feedId"]), + "nonce": nonce, + "position": int(state["progress"][1]), + "round": int(state["latestPrice"][0]), + "price": str(price), + "observedPriceChanges": observed_price_changes[pair], + "relayerQuorumNodes": relayer_nodes, + } + + for node_id, height in final["heights"].items(): + assert height >= previous_heights[node_id], ( + f"{node_id} block height regressed" + ) + if height > previous_heights[node_id]: + last_height_change[node_id] = now + previous_heights[node_id] = height + assert now - last_height_change[node_id] <= settings.stall_seconds, ( + f"{node_id} chain height stalled for " + f"{now - last_height_change[node_id]:.1f}s" + ) + + samples += 1 + heartbeat = { + "elapsedSeconds": round(now - started, 3), + "gravityBlock": int(final["block"]), + "gravityBlockHash": final["blockHash"], + "nodeHeights": final["heights"], + "priceFeeds": price_heartbeats, + "restartCompleted": bool(settings.restart_schedule_seconds) + and restart_index == len(settings.restart_schedule_seconds), + "restartCount": restart_index, + "restartEpochGuardDeferrals": restart_epoch_guard_deferrals, + } + _append_json_line(heartbeat_path, heartbeat) + LOG.info("Oracle soak heartbeat: %s", heartbeat) + await asyncio.sleep( + min(settings.poll_seconds, max(0.0, deadline - time.monotonic())) + ) + + final = await _replicated_snapshot( + cluster, + native_artifact, + price_artifact, + price_resolver_address, + binance_feeds, + ) + if settings.restart_schedule_seconds: + assert restart_index == len(settings.restart_schedule_seconds), ( + f"only {restart_index}/{len(settings.restart_schedule_seconds)} " + "configured validator restarts ran" + ) + + price_summaries = {} + for feed in binance_feeds: + pair = feed["pair"] + state = final["priceFeeds"][pair] + final_nonce = int(state["progress"][0]) + advances = final_nonce - initial_nonces[pair] + assert advances >= settings.minimum_advances, ( + f"{pair} advanced {advances} rounds; expected at least " + f"{settings.minimum_advances}" + ) + if settings.restart_schedule_seconds: + await _wait_for_relayer_node( + cluster, + settings.restart_node, + feed["taskUri"], + final_nonce, + ) + final_bucket = _round_start_ms( + int(feed["bucketStartMs"]), final_nonce + ) + max_price_gaps[pair] = max( + max_price_gaps[pair], + time.monotonic() - last_price_advances[pair], + ) + expected_price = await _fetch_binance_price_with_retry( + feed["baseUrl"], pair, final_bucket + ) + assert int(state["latestPrice"][2]) == expected_price + price_summaries[pair] = { + "feedId": int(feed["feedId"]), + "initialNonce": initial_nonces[pair], + "finalNonce": final_nonce, + "advances": advances, + "minimumAdvances": settings.minimum_advances, + "finalPrice": str(state["latestPrice"][2]), + "observedPriceChanges": observed_price_changes[pair], + "maxObservedGapSeconds": round(max_price_gaps[pair], 3), + } + return { + "status": "passed", + "configuredDurationSeconds": settings.duration_seconds, + "actualDurationSeconds": round(time.monotonic() - started, 3), + "samples": samples, + "priceFeeds": price_summaries, + "finalGravityBlock": int(final["block"]), + "finalGravityBlockHash": final["blockHash"], + "restartNode": ( + settings.restart_node + if settings.restart_schedule_seconds + else None + ), + "restartScheduleSeconds": list(settings.restart_schedule_seconds), + "restartRpcTimeoutSeconds": settings.restart_rpc_timeout_seconds, + "restartRecoverySeconds": ( + restart_recoveries[0]["recoverySeconds"] + if len(restart_recoveries) == 1 + else None + ), + "restartRecoveries": restart_recoveries, + "restartEpochGuardDeferrals": restart_epoch_guard_deferrals, + } + + +def _write_summary(payload: dict) -> None: + path = SUITE_DIR / "artifacts" / _SUMMARY_FILE + path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n") + + +@pytest.mark.asyncio +async def test_governance_activated_price_feeds_soak_for_configured_duration( + cluster: Cluster, +): + settings = _soak_settings() + metadata = _metadata() + assert set(metadata) == {"binanceFeeds"} + binance_feeds = metadata["binanceFeeds"] + assert [feed["pair"] for feed in binance_feeds] == [ + "NVDAUSDT", + "BTCUSDT", + "ETHUSDT", + ] + assert len({int(feed["feedId"]) for feed in binance_feeds}) == len( + binance_feeds + ) + relayer_config = json.loads( + (SUITE_DIR / "artifacts" / "relayer_config.live.json").read_text() + ) + expected_uris = {feed["taskUri"] for feed in binance_feeds} + assert set(relayer_config["uri_mappings"]) == expected_uris + assert all(uri.startswith("gravity://3/") for uri in expected_uris) + with (SUITE_DIR / "genesis.toml").open("rb") as genesis_file: + oracle_config = tomllib.load(genesis_file)["genesis"]["oracle_config"] + assert oracle_config["source_types"] == [1, 3] + assert len(oracle_config["callbacks"]) == 2 + + assert len(cluster.nodes) == 4 + assert await cluster.set_full_live(timeout=180) + assert await cluster.check_block_increasing(timeout=60) + active = await cluster.validator_list() + assert {node.id for node in active.active} == set(cluster.nodes) + + node1 = cluster.get_node("node1") + assert node1 is not None and node1.w3.is_connected() + w3 = node1.w3 + required = [ + ("PriceFeedResolver.sol", "PriceFeedResolver"), + ("NativeOracle.sol", "NativeOracle"), + ("OracleTaskConfig.sol", "OracleTaskConfig"), + ("EpochConfig.sol", "EpochConfig"), + ] + contracts_out = support.ensure_contract_artifacts(SUITE_DIR, required) + price_artifact = support.load_artifact( + contracts_out, "PriceFeedResolver.sol", "PriceFeedResolver" + ) + native_artifact = support.load_artifact( + contracts_out, "NativeOracle.sol", "NativeOracle" + ) + task_artifact = support.load_artifact( + contracts_out, "OracleTaskConfig.sol", "OracleTaskConfig" + ) + epoch_artifact = support.load_artifact( + contracts_out, "EpochConfig.sol", "EpochConfig" + ) + + price_resolver = support.deploy_contract(w3, price_artifact) + native_oracle = w3.eth.contract( + address=support.NATIVE_ORACLE_ADDRESS, abi=native_artifact["abi"] + ) + task_config = w3.eth.contract( + address=support.ORACLE_TASK_CONFIG_ADDRESS, abi=task_artifact["abi"] + ) + epoch_config = w3.eth.contract( + address=EPOCH_CONFIG_ADDRESS, abi=epoch_artifact["abi"] + ) + + assert ( + epoch_config.functions.epochIntervalMicros().call() + == BOOTSTRAP_EPOCH_INTERVAL_MICROS + ) + assert tuple(epoch_config.functions.getPendingConfig().call()) == (False, 0) + + for feed in binance_feeds: + assert not task_config.functions.hasTask( + support.SOURCE_TYPE_PRICE_FEED, + int(feed["feedId"]), + support.TASK_PRICE_FEED, + ).call() + + setup_epoch = await _wait_for_proposal_window(cluster) + targets = [EPOCH_CONFIG_ADDRESS, support.NATIVE_ORACLE_ADDRESS] + datas = [ + support.function_calldata( + epoch_config.functions.setForNextEpoch( + SOAK_EPOCH_INTERVAL_MICROS + ) + ), + support.function_calldata( + native_oracle.functions.setDefaultCallback( + support.SOURCE_TYPE_PRICE_FEED, price_resolver.address + ) + ), + ] + for feed in binance_feeds: + targets.append(support.ORACLE_TASK_CONFIG_ADDRESS) + datas.append( + support.function_calldata( + task_config.functions.setTask( + support.SOURCE_TYPE_PRICE_FEED, + int(feed["feedId"]), + support.TASK_PRICE_FEED, + feed["taskUri"].encode(), + ) + ) + ) + receipt = await support.execute_governance_proposal( + w3, + support.faucet_voting_pool(w3), + targets, + datas, + "activate-live-binance-price-soak", + ) + assert _current_epoch(w3) == setup_epoch + assert ( + epoch_config.functions.epochIntervalMicros().call() + == BOOTSTRAP_EPOCH_INTERVAL_MICROS + ) + assert tuple(epoch_config.functions.getPendingConfig().call()) == ( + True, + SOAK_EPOCH_INTERVAL_MICROS, + ) + configured_tasks = [ + ( + support.SOURCE_TYPE_PRICE_FEED, + int(feed["feedId"]), + support.TASK_PRICE_FEED, + feed["taskUri"], + ) + for feed in binance_feeds + ] + for source_type, source_id, task_name, expected_uri in configured_tasks: + assert task_config.functions.hasTask( + source_type, source_id, task_name + ).call() + task = task_config.functions.getTask( + source_type, source_id, task_name + ).call() + assert bytes(task[0]).decode() == expected_uri + assert native_oracle.functions.getLatestNonce( + source_type, source_id + ).call() == 0 + + issuers = [ + f"gravity://3/{int(feed['feedId'])}" for feed in binance_feeds + ] + for node_id in cluster.nodes: + for issuer in issuers: + assert not _log_contains( + cluster, node_id, "JWKObserver spawned.", issuer + ), f"{node_id} started {issuer} before the epoch boundary" + + activation_epoch = await _wait_for_epoch_advance(cluster, setup_epoch) + assert activation_epoch == setup_epoch + 1 + assert ( + epoch_config.functions.epochIntervalMicros().call() + == SOAK_EPOCH_INTERVAL_MICROS + ) + assert tuple(epoch_config.functions.getPendingConfig().call()) == (False, 0) + assert _remaining_epoch_seconds(w3) > ( + SOAK_EPOCH_INTERVAL_MICROS // 1_000_000 - 300 + ) + initial_prices = {} + for feed in binance_feeds: + initial_prices[feed["pair"]] = await support.wait_for_latest_price( + native_oracle, + price_resolver, + int(feed["feedId"]), + 1, + timeout=ORACLE_TIMEOUT_SECONDS, + ) + + for feed in binance_feeds: + price_progress, initial_price = initial_prices[feed["pair"]] + first_bucket = _round_start_ms( + int(feed["bucketStartMs"]), int(price_progress[0]) + ) + expected_initial_price = await _fetch_binance_price_with_retry( + feed["baseUrl"], feed["pair"], first_bucket + ) + assert int(initial_price[2]) == expected_initial_price + await _wait_for_consensus_evidence(cluster, issuers) + for feed in binance_feeds: + price_progress, _ = initial_prices[feed["pair"]] + assert len( + _relayer_quorum( + cluster, feed["taskUri"], int(price_progress[0]) + ) + ) >= QUORUM_VALIDATORS + + try: + summary = await _run_soak( + cluster, + settings, + native_artifact, + price_artifact, + price_resolver.address, + binance_feeds, + ) + except BaseException as error: + last_heartbeat = None + heartbeat_path = SUITE_DIR / "artifacts" / _HEARTBEAT_FILE + if heartbeat_path.exists(): + lines = heartbeat_path.read_text().splitlines() + if lines: + last_heartbeat = json.loads(lines[-1]) + _write_summary( + { + "status": "failed", + "errorType": type(error).__name__, + "error": str(error), + "configuredDurationSeconds": settings.duration_seconds, + "restartNode": ( + settings.restart_node + if settings.restart_schedule_seconds + else None + ), + "restartScheduleSeconds": list( + settings.restart_schedule_seconds + ), + "binancePairs": [ + feed["pair"] for feed in binance_feeds + ], + "lastHeartbeat": last_heartbeat, + } + ) + raise + + price_delivery_counts = {} + for feed in binance_feeds: + pair = feed["pair"] + delivery_count = len( + _callback_success_logs( + w3, + support.SOURCE_TYPE_PRICE_FEED, + int(feed["feedId"]), + receipt["blockNumber"], + summary["finalGravityBlock"], + ) + ) + assert delivery_count == summary["priceFeeds"][pair]["finalNonce"], ( + f"{pair} callback count does not match final source nonce" + ) + price_delivery_counts[pair] = delivery_count + summary.update( + { + "activationEpoch": activation_epoch, + "governanceBlock": receipt["blockNumber"], + "priceCallbackEvents": price_delivery_counts, + "soakEpochIntervalSeconds": ( + SOAK_EPOCH_INTERVAL_MICROS // 1_000_000 + ), + } + ) + _write_summary(summary) + LOG.info("Oracle live soak passed: %s", summary) diff --git a/gravity_e2e/gravity_e2e/cluster/node.py b/gravity_e2e/gravity_e2e/cluster/node.py index 9371863b..e4d61cf3 100644 --- a/gravity_e2e/gravity_e2e/cluster/node.py +++ b/gravity_e2e/gravity_e2e/cluster/node.py @@ -184,10 +184,13 @@ async def get_state(self) -> Tuple[NodeState, int]: else: return NodeState.STOPPED, -1 - async def start(self) -> bool: + async def start(self, rpc_timeout: int = 30) -> bool: """ Start this individual node. Returns True if node is now RUNNING. + + ``rpc_timeout`` may be increased by long-running recovery tests whose + persisted databases need more than the default startup window. """ if not self.start_script.exists(): LOG.warning( @@ -259,7 +262,7 @@ async def log_stream(stream, level): return False # Wait for RPC to come up - if await self.wait_for_rpc(timeout=30): + if await self.wait_for_rpc(timeout=rpc_timeout): LOG.info(f"Node {self.id} started and RPC verified.") return True else: @@ -361,13 +364,13 @@ async def _wait_for_pid_exit(self, pid: int, timeout: float = 50.0) -> bool: await asyncio.sleep(0.25) return False - async def restart(self) -> bool: + async def restart(self, rpc_timeout: int = 30) -> bool: """Bounce the node.""" if not await self.stop(): return False # stop() already waits for process exit / RocksDB unlock; short settle only. await asyncio.sleep(0.5) - return await self.start() + return await self.start(rpc_timeout=rpc_timeout) def is_running(self) -> bool: """Check if node process is running based on PID file.""" diff --git a/gravity_e2e/gravity_e2e/utils/oracle_test_support.py b/gravity_e2e/gravity_e2e/utils/oracle_test_support.py index ddd0ff47..4ed44bf6 100644 --- a/gravity_e2e/gravity_e2e/utils/oracle_test_support.py +++ b/gravity_e2e/gravity_e2e/utils/oracle_test_support.py @@ -293,7 +293,7 @@ async def wait_for_latest_price( ).call() ) latest = tuple(resolver.functions.latestPrice(feed_id).call()) - if progress[0] >= target_nonce and latest[0] and latest[2] == progress[1]: + if progress[0] >= target_nonce and latest[0] > 0 and latest[1] == progress[1]: return progress, latest await asyncio.sleep(2) raise TimeoutError(f"latestPrice({feed_id}) did not reach delivery nonce {target_nonce}") diff --git a/gravity_e2e/runner.py b/gravity_e2e/runner.py index a20dbeb5..737d16d2 100644 --- a/gravity_e2e/runner.py +++ b/gravity_e2e/runner.py @@ -328,6 +328,10 @@ def run_test_suite( env[env_var] = str(tpl_path) logger.info(f"Using custom reth config template: {env_var}={tpl_path}") + if hooks and hasattr(hooks, "pre_deploy"): + logger.info(f"Running pre_deploy hook from {hooks_path}") + hooks.pre_deploy(test_dir, env, pytest_args or []) + run_command( ["bash", str(deploy_script), str(cluster_config)], cwd=CLUSTER_SCRIPTS_DIR, @@ -439,7 +443,7 @@ def main(): ) # Long-running suites excluded from CI by default. # Run them explicitly: runner.py long_test - DEFAULT_EXCLUDES = ["long_test", "rolling_upgrade"] + DEFAULT_EXCLUDES = ["long_test", "rolling_upgrade", "oracle_live_soak"] parser.add_argument( "--exclude",