diff --git a/Cargo.lock b/Cargo.lock index 6d51f86a5d074..820aec7cb3fbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90d103d3e440ad6f703dd71a5b58a6abd24834563bde8a5fabe706e00242f810" +checksum = "ad704069c12f68d0c742d0cad7e0a03882b42767350584627fbf8a47b1bf1846" dependencies = [ "alloy-eips", "alloy-primitives", @@ -90,6 +90,7 @@ dependencies = [ "alloy-trie", "alloy-tx-macros", "auto_impl", + "borsh", "c-kzg", "derive_more", "either", @@ -105,9 +106,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ead76c8c84ab3a50c31c56bc2c748c2d64357ad2131c32f9b10ab790a25e1a" +checksum = "bc374f640a5062224d7708402728e3d6879a514ba10f377da62e7dfb14c673e6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -119,9 +120,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5903097e4c131ad2dd80d87065f23c715ccb9cdb905fa169dffab8e1e798bae" +checksum = "15c493b2812943f7b58191063a8d13ea97c76099900869c08231e8eba3bf2f92" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -186,9 +187,9 @@ dependencies = [ [[package]] name = "alloy-eip5792" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420a02e98712e851daac5fa2c68bafb6f9a85602772f32b91a3ce6ffcfa04c9f" +checksum = "37aa145e9d61cda33702d8fbe349f25ba89604c98c4e9c04a3f69a790d92c0fa" dependencies = [ "alloy-primitives", "alloy-serde", @@ -212,9 +213,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bdbec74583d0067798d77afa43d58f00d93035335d7ceaa5d3f93857d461bb9" +checksum = "7e867b5fd52ed0372a95016f3a37cbff95a9d5409230fbaef2d8ea00e8618098" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -223,6 +224,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "auto_impl", + "borsh", "c-kzg", "derive_more", "either", @@ -236,9 +238,9 @@ dependencies = [ [[package]] name = "alloy-ens" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04b7b1959e53092cb57ee822e1129a32e90bab7a1ce4e3e43ba909b3a43d07ee" +checksum = "03947c30835388cd0e18d52e5bd1563ac3a2d784bf0801809bf693542ab68479" dependencies = [ "alloy-contract", "alloy-primitives", @@ -272,9 +274,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c25d5acb35706e683df1ea333c862bdb6b7c5548836607cd5bb56e501cca0b4f" +checksum = "b90be17e9760a6ba6d13cebdb049cea405ebc8bf57d90664ed708cc5bc348342" dependencies = [ "alloy-eips", "alloy-primitives", @@ -310,9 +312,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b67c5a702121e618217f7a86f314918acb2622276d0273490e2d4534490bc0" +checksum = "dcab4c51fb1273e3b0f59078e0cdf8aa99f697925b09f0d2055c18be46b4d48c" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -325,9 +327,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612296e6b723470bb1101420a73c63dfd535aa9bf738ce09951aedbd4ab7292e" +checksum = "196d7fd3f5d414f7bbd5886a628b7c42bd98d1b126f9a7cff69dbfd72007b39c" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -351,9 +353,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0e7918396eecd69d9c907046ec8a93fb09b89e2f325d5e7ea9c4e3929aa0dd2" +checksum = "0d3ae2777e900a7a47ad9e3b8ab58eff3d93628265e73bbdee09acf90bf68f75" dependencies = [ "alloy-consensus", "alloy-eips", @@ -424,9 +426,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c1313a527a2e464d067c031f3c2ec073754ef615cc0eabca702fd0fe35729c" +checksum = "9f9bf40c9b2a90c7677f9c39bccd9f06af457f35362439c0497a706f16557703" dependencies = [ "alloy-chains", "alloy-consensus", @@ -469,9 +471,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810766eeed6b10ffa11815682b3f37afc5019809e3b470b23555297d5770ce63" +checksum = "acfdbe41e2ef1a7e79b5ea115baa750f9381ac9088fb600f4cedc731cf04a151" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -513,9 +515,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f802228273056528dfd6cc8845cc91a7c7e0c6fc1a66d19e8673743dacdc7e" +checksum = "e7c2630fde9ff6033a780635e1af6ef40e92d74a9cacb8af3defc1b15cfebca5" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -539,9 +541,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ff3df608dcabd6bdd197827ff2b8faaa6cefe0c462f7dc5e74108666a01f56" +checksum = "ad098153a12382c22a597e865530033f5e644473742d6c733562d448125e02a2" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -555,9 +557,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2bc988d7455e02dfb53460e1caa61f932b3f8452e12424e68ba8dcf60bba90" +checksum = "214d9d1033c173ab8fa32edd8a4655cd784447c820b0b66cd0d5167e049567d6" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -567,9 +569,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdbf6d1766ca41e90ac21c4bc5cbc5e9e965978a25873c3f90b3992d905db4cb" +checksum = "50b8429b5b62d21bf3691eb1ae12aaae9bb496894d5a114e3cc73e27e6800ec8" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -578,9 +580,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab94e446a003dcef86843eea60d05a6cec360eb8e1829e4cf388ef94d799b5cf" +checksum = "f67f8269e8b5193a5328dd3ef4d60f93524071e53a993776e290581a59aa15fa" dependencies = [ "alloy-eips", "alloy-primitives", @@ -594,9 +596,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977698b458738369ba5ca645d2cdb4d51ba07a81db37306ff85322853161ea3a" +checksum = "01731601ea631bd825c652a225701ab466c09457f446b8d8129368a095389c5d" dependencies = [ "alloy-primitives", "derive_more", @@ -606,9 +608,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da696cc7fbfead4b1dda8afe408685cae80975cbb024f843ba74d9639cd0d3" +checksum = "9981491bb98e76099983f516ec7de550db0597031f5828c994961eb4bb993cce" dependencies = [ "alloy-consensus", "alloy-eips", @@ -626,9 +628,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15e4831b71eea9d20126a411c1c09facf1d01d5cac84fd51d532d3c429cfc26" +checksum = "29031a6bf46177d65efce661f7ab37829ca09dd341bc40afb5194e97600655cc" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -647,9 +649,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0c800e2ce80829fca1491b3f9063c29092850dc6cf19249d5f678f0ce71bb0" +checksum = "01b842f5aac6676ff4b2e328262d03bdf49807eaec3fe3a4735c45c97388518b" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -661,9 +663,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f82e3068673a3cf93fbbc2f60a59059395cd54bbe39af895827faa5e641cc8f" +checksum = "7fa12c608873beeb7afa392944dce8829fa8a50c487f266863bb2dd6b743c4a2" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -673,9 +675,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "751d1887f7d202514a82c5b3caf28ee8bd4a2ad9549e4f498b6f0bff99b52add" +checksum = "01e856112bfa0d9adc85bd7c13db03fad0e71d1d6fb4c2010e475b6718108236" dependencies = [ "alloy-primitives", "serde", @@ -684,9 +686,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf0b42ffbf558badfecf1dde0c3c5ed91f29bb7e97876d0bed008c3d5d67171" +checksum = "66a4f629da632d5279bbc5731634f0f5c9484ad9c4cad0cd974d9669dc1f46d6" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -701,9 +703,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6b73b812ab342d09de85eb302598a3a0c4d744cbe982ed76e309dcec9ddfa" +checksum = "66be762e60db50f81be7744c3a5b3efd4c69203576340df0519dacea09f5f4dd" dependencies = [ "alloy-consensus", "alloy-network", @@ -720,9 +722,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2bd8fcc42b831aa219deac7a0b642eb418446a402a4de3b302c6e9ad09702c" +checksum = "f7898c079cb61868a478ac718172a3098754db75b0013db36516b1fb34b1e318" dependencies = [ "alloy-consensus", "alloy-network", @@ -738,9 +740,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc99b13a9f90efcc9a4dbc817909a78b0cd52fe734f81ed48a7032a658fecd8" +checksum = "92490935797fe5625e123fc99ed67e8ba24f27c26d88e337e3fcabec0f350f7d" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -758,9 +760,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e7d555ee5f27be29af4ae312be014b57c6cff9acb23fe2cf008500be6ca7e33" +checksum = "76c8950810dc43660c0f22883659c4218e090a5c75dce33fa4ca787715997b7b" dependencies = [ "alloy-consensus", "alloy-network", @@ -778,9 +780,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f35ab4ebc1cdfa766332bcdf9b4f6d559a001edd9bea72c39fd88d62c976f1d7" +checksum = "0eab6b865f665871ebb8e65f881d50d75d1a5c367d4e376578ff35ccd926b43e" dependencies = [ "alloy-consensus", "alloy-network", @@ -795,9 +797,9 @@ dependencies = [ [[package]] name = "alloy-signer-turnkey" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5271719b6594785b7cd317a60ccfd8822469627dc9d3167836d7441cb3b9b087" +checksum = "a0e2185d95364495878d73b82a18ddc0279d8bbc791cf117ccf1f61b282e5bac" dependencies = [ "alloy-consensus", "alloy-network", @@ -884,9 +886,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b3deee699d6f271eab587624a9fa84d02d0755db7a95a043d52a6488d16ebe" +checksum = "fe215a2f9b51d5f1aa5c8cf22c8be8cdb354934de09c9a4e37aefb79b77552fd" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -907,9 +909,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1720bd2ba8fe7e65138aca43bb0f680e4e0bcbd3ca39bf9d3035c9d7d2757f24" +checksum = "dc1b37b1a30d23deb3a8746e882c70b384c574d355bc2bbea9ea918b0c31366e" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -922,9 +924,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea89c214c7ddd2bcad100da929d6b642bbfed85788caf3b1be473abacd3111f9" +checksum = "52c81a4deeaa0d4b022095db17b286188d731e29ea141d4ec765e166732972e4" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -942,9 +944,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571aadf0afce0d515a28b2c6352662a39cb9f48b4eeff9a5c34557d6ea126730" +checksum = "4e9d6f5f304e8943afede2680e5fc7008780d4fc49387eafd53192ad95e20091" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -976,9 +978,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7ce8ed34106acd6e21942022b6a15be6454c2c3ead4d76811d3bdcd63cf771" +checksum = "7ccf423f6de62e8ce1d6c7a11fb7508ae3536d02e0d68aaeb05c8669337d0937" dependencies = [ "darling 0.21.3", "proc-macro2", @@ -1161,7 +1163,6 @@ dependencies = [ "revm-inspectors", "serde", "serde_json", - "serde_with", "tempfile", "thiserror 2.0.17", "tokio", @@ -9599,7 +9600,7 @@ dependencies = [ "derive_more", "dunce", "inturn", - "itertools 0.10.5", + "itertools 0.14.0", "itoa", "normalize-path", "once_map", @@ -9611,7 +9612,7 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 1.0.69", + "thiserror 2.0.17", "tracing", "unicode-width 0.2.0", ] @@ -9634,7 +9635,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.10.0", "bumpalo", - "itertools 0.10.5", + "itertools 0.14.0", "memchr", "num-bigint", "num-rational", diff --git a/Cargo.toml b/Cargo.toml index eb4ba49bb229b..73c0e677a8e4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -235,31 +235,31 @@ svm = { package = "svm-rs", version = "0.5", default-features = false, features ] } ## alloy -alloy-consensus = { version = "1.1.0", default-features = false } -alloy-contract = { version = "1.1.0", default-features = false } -alloy-eips = { version = "1.1.0", default-features = false } -alloy-eip5792 = { version = "1.1.0", default-features = false } -alloy-ens = { version = "1.1.0", default-features = false } -alloy-genesis = { version = "1.1.0", default-features = false } -alloy-json-rpc = { version = "1.1.0", default-features = false } -alloy-network = { version = "1.1.0", default-features = false } -alloy-provider = { version = "1.1.0", default-features = false } -alloy-pubsub = { version = "1.1.0", default-features = false } -alloy-rpc-client = { version = "1.1.0", default-features = false } -alloy-rpc-types = { version = "1.1.0", default-features = true } -alloy-rpc-types-beacon = { version = "1.1.0", default-features = true } -alloy-serde = { version = "1.1.0", default-features = false } -alloy-signer = { version = "1.1.0", default-features = false } -alloy-signer-aws = { version = "1.1.0", default-features = false } -alloy-signer-gcp = { version = "1.1.0", default-features = false } -alloy-signer-ledger = { version = "1.1.0", default-features = false } -alloy-signer-local = { version = "1.1.0", default-features = false } -alloy-signer-trezor = { version = "1.1.0", default-features = false } -alloy-signer-turnkey = { version = "1.1.0", default-features = false } -alloy-transport = { version = "1.1.0", default-features = false } -alloy-transport-http = { version = "1.1.0", default-features = false } -alloy-transport-ipc = { version = "1.1.0", default-features = false } -alloy-transport-ws = { version = "1.1.0", default-features = false } +alloy-consensus = { version = "1.1.1", default-features = false } +alloy-contract = { version = "1.1.1", default-features = false } +alloy-eips = { version = "1.1.1", default-features = false } +alloy-eip5792 = { version = "1.1.1", default-features = false } +alloy-ens = { version = "1.1.1", default-features = false } +alloy-genesis = { version = "1.1.1", default-features = false } +alloy-json-rpc = { version = "1.1.1", default-features = false } +alloy-network = { version = "1.1.1", default-features = false } +alloy-provider = { version = "1.1.1", default-features = false } +alloy-pubsub = { version = "1.1.1", default-features = false } +alloy-rpc-client = { version = "1.1.1", default-features = false } +alloy-rpc-types = { version = "1.1.1", default-features = true } +alloy-rpc-types-beacon = { version = "1.1.1", default-features = true } +alloy-serde = { version = "1.1.1", default-features = false } +alloy-signer = { version = "1.1.1", default-features = false } +alloy-signer-aws = { version = "1.1.1", default-features = false } +alloy-signer-gcp = { version = "1.1.1", default-features = false } +alloy-signer-ledger = { version = "1.1.1", default-features = false } +alloy-signer-local = { version = "1.1.1", default-features = false } +alloy-signer-trezor = { version = "1.1.1", default-features = false } +alloy-signer-turnkey = { version = "1.1.1", default-features = false } +alloy-transport = { version = "1.1.1", default-features = false } +alloy-transport-http = { version = "1.1.1", default-features = false } +alloy-transport-ipc = { version = "1.1.1", default-features = false } +alloy-transport-ws = { version = "1.1.1", default-features = false } alloy-hardforks = { version = "0.4.0", default-features = false } alloy-op-hardforks = { version = "0.4.0", default-features = false } @@ -358,7 +358,6 @@ reqwest = { version = "0.12", default-features = false, features = [ rustls = "0.23" semver = "1" serde = { version = "1.0", features = ["derive"] } -serde_with = "3.15.1" serde_json = { version = "1.0", features = ["arbitrary_precision"] } similar-asserts = "1.7" soldeer-commands = "=0.9.0" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 8b64e242c0182..134f836c81f6e 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -89,7 +89,6 @@ async-trait.workspace = true flate2.workspace = true serde_json.workspace = true serde.workspace = true -serde_with.workspace = true thiserror.workspace = true yansi.workspace = true tempfile.workspace = true diff --git a/crates/anvil/src/eth/beacon/data.rs b/crates/anvil/src/eth/beacon/data.rs deleted file mode 100644 index 604a6407e9dba..0000000000000 --- a/crates/anvil/src/eth/beacon/data.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Beacon data structures for the Beacon API responses - -use alloy_primitives::{B256, aliases::B32}; -use serde::{Deserialize, Serialize}; -use serde_with::{DisplayFromStr, serde_as}; - -/// Ethereum Beacon chain genesis details -#[serde_as] -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct GenesisDetails { - /// The genesis_time configured for the beacon chain - #[serde_as(as = "DisplayFromStr")] - pub genesis_time: u64, - /// The genesis validators root - pub genesis_validators_root: B256, - /// The genesis fork version, as used in the beacon chain - pub genesis_fork_version: B32, -} diff --git a/crates/anvil/src/eth/beacon/mod.rs b/crates/anvil/src/eth/beacon/mod.rs index ec794e903c06c..7585a14d1df0b 100644 --- a/crates/anvil/src/eth/beacon/mod.rs +++ b/crates/anvil/src/eth/beacon/mod.rs @@ -3,10 +3,8 @@ //! This module provides types and utilities for implementing Beacon API endpoints //! in Anvil, allowing testing of blob-based transactions with standard beacon chain APIs. -pub mod data; pub mod error; pub mod response; -pub use data::GenesisDetails; pub use error::BeaconError; pub use response::BeaconResponse; diff --git a/crates/anvil/src/server/beacon_handler.rs b/crates/anvil/src/server/beacon_handler.rs index 17319bda55380..753e3ebd50dd5 100644 --- a/crates/anvil/src/server/beacon_handler.rs +++ b/crates/anvil/src/server/beacon_handler.rs @@ -1,10 +1,11 @@ use crate::eth::{ EthApi, - beacon::{BeaconError, BeaconResponse, GenesisDetails}, + beacon::{BeaconError, BeaconResponse}, }; use alloy_eips::BlockId; use alloy_primitives::{B256, aliases::B32}; use alloy_rpc_types_beacon::{ + genesis::{GenesisData, GenesisResponse}, header::Header, sidecar::{BlobData, GetBlobsResponse}, }; @@ -99,12 +100,17 @@ pub async fn handle_get_blobs( /// GET /eth/v1/beacon/genesis pub async fn handle_get_genesis(State(api): State) -> Response { match api.anvil_get_genesis_time() { - Ok(genesis_time) => BeaconResponse::new(GenesisDetails { - genesis_time, - genesis_validators_root: B256::ZERO, - genesis_fork_version: B32::ZERO, - }) - .into_response(), + Ok(genesis_time) => ( + StatusCode::OK, + Json(GenesisResponse { + data: GenesisData { + genesis_time, + genesis_validators_root: B256::ZERO, + genesis_fork_version: B32::ZERO, + }, + }), + ) + .into_response(), Err(_) => BeaconError::internal_error().into_response(), } } diff --git a/crates/anvil/tests/it/beacon_api.rs b/crates/anvil/tests/it/beacon_api.rs new file mode 100644 index 0000000000000..bce15f8017b15 --- /dev/null +++ b/crates/anvil/tests/it/beacon_api.rs @@ -0,0 +1,358 @@ +use crate::utils::http_provider; +use alloy_consensus::{SidecarBuilder, SimpleCoder, Transaction}; +use alloy_hardforks::EthereumHardfork; +use alloy_network::{TransactionBuilder, TransactionBuilder4844}; +use alloy_primitives::{B256, FixedBytes, U256, b256}; +use alloy_provider::Provider; +use alloy_rpc_types::TransactionRequest; +use alloy_rpc_types_beacon::{genesis::GenesisResponse, sidecar::GetBlobsResponse}; +use alloy_serde::WithOtherFields; +use anvil::{NodeConfig, spawn}; + +#[tokio::test(flavor = "multi_thread")] +async fn test_beacon_api_get_blob_sidecars() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); + let (api, handle) = spawn(node_config).await; + + // Disable auto-mining so we can include multiple transactions in the same block + api.anvil_set_auto_mine(false).await.unwrap(); + + let wallets = handle.dev_wallets().collect::>(); + let from = wallets[0].address(); + let to = wallets[1].address(); + + let provider = http_provider(&handle.http_endpoint()); + + let eip1559_est = provider.estimate_eip1559_fees().await.unwrap(); + let gas_price = provider.get_gas_price().await.unwrap(); + + // Create multiple blob transactions to be included in the same block + let blob_data = + [b"Hello Beacon API - Blob 1", b"Hello Beacon API - Blob 2", b"Hello Beacon API - Blob 3"]; + + let mut pending_txs = Vec::new(); + + // Send all transactions without waiting for receipts + for (i, data) in blob_data.iter().enumerate() { + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(data.as_slice()); + let sidecar = sidecar.build().unwrap(); + + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_nonce(i as u64) + .with_max_fee_per_blob_gas(gas_price + 1) + .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) + .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) + .with_blob_sidecar(sidecar) + .value(U256::from(100)); + + let mut tx = WithOtherFields::new(tx); + tx.populate_blob_hashes(); + + let pending = provider.send_transaction(tx).await.unwrap(); + pending_txs.push(pending); + } + + // Mine a block to include all transactions + api.evm_mine(None).await.unwrap(); + + // Get receipts for all transactions + let mut receipts = Vec::new(); + for pending in pending_txs { + let receipt = pending.get_receipt().await.unwrap(); + receipts.push(receipt); + } + + // Verify all transactions were included in the same block + let block_number = receipts[0].block_number.unwrap(); + for (i, receipt) in receipts.iter().enumerate() { + assert_eq!( + receipt.block_number.unwrap(), + block_number, + "Transaction {i} was not included in block {block_number}" + ); + } + + // Test Beacon API endpoint using HTTP client + let client = reqwest::Client::new(); + let url = format!("{}/eth/v1/beacon/blob_sidecars/{}", handle.http_endpoint(), block_number); + + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + + let body: serde_json::Value = response.json().await.unwrap(); + + // Verify response structure + assert!(body["data"].is_array()); + assert!(body["execution_optimistic"].is_boolean()); + assert!(body["finalized"].is_boolean()); + + // Verify we have blob data from all transactions + let blobs = body["data"].as_array().unwrap(); + assert_eq!(blobs.len(), 3, "Expected 3 blob sidecars from 3 transactions"); + + // Verify blob structure for each blob + for (i, blob) in blobs.iter().enumerate() { + assert!(blob["index"].is_string(), "Blob {i} missing index"); + assert!(blob["blob"].is_string(), "Blob {i} missing blob data"); + assert!(blob["kzg_commitment"].is_string(), "Blob {i} missing kzg_commitment"); + assert!(blob["kzg_proof"].is_string(), "Blob {i} missing kzg_proof"); + } + + // Test filtering with indices query parameter - single index + let url = format!( + "{}/eth/v1/beacon/blob_sidecars/{}?indices=1", + handle.http_endpoint(), + block_number + ); + let response = client.get(&url).send().await.unwrap(); + let status = response.status(); + if status != reqwest::StatusCode::OK { + let error_body = response.text().await.unwrap(); + panic!("Expected OK status, got {status}: {error_body}"); + } + let body: serde_json::Value = response.json().await.unwrap(); + let filtered_blobs = body["data"].as_array().unwrap(); + assert_eq!(filtered_blobs.len(), 1, "Expected 1 blob sidecar when filtering by indices=1"); + assert_eq!(filtered_blobs[0]["index"].as_str().unwrap(), "1"); + + // Test filtering with indices query parameter - multiple indices (comma-separated) + let url = format!( + "{}/eth/v1/beacon/blob_sidecars/{}?indices=0,2", + handle.http_endpoint(), + block_number + ); + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + let body: serde_json::Value = response.json().await.unwrap(); + let filtered_blobs = body["data"].as_array().unwrap(); + assert_eq!(filtered_blobs.len(), 2, "Expected 2 blob sidecars when filtering by indices=0,2"); + let indices: Vec = + filtered_blobs.iter().map(|b| b["index"].as_str().unwrap().to_string()).collect(); + assert!(indices.contains(&"0".to_string()), "Expected index 0 in results"); + assert!(indices.contains(&"2".to_string()), "Expected index 2 in results"); + + // Test filtering with non-existent index + let url = format!( + "{}/eth/v1/beacon/blob_sidecars/{}?indices=99", + handle.http_endpoint(), + block_number + ); + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + let body: serde_json::Value = response.json().await.unwrap(); + let filtered_blobs = body["data"].as_array().unwrap(); + assert_eq!( + filtered_blobs.len(), + 0, + "Expected 0 blob sidecars when filtering by non-existent index" + ); + + // Test with special block identifiers + let test_ids = vec!["latest", "finalized", "safe", "earliest"]; + for block_id in test_ids { + let url = format!("{}/eth/v1/beacon/blob_sidecars/{}", handle.http_endpoint(), block_id); + assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::OK); + } + let url = format!("{}/eth/v1/beacon/blob_sidecars/pending", handle.http_endpoint()); + assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::NOT_FOUND); + + // Test with hex block number + let url = format!("{}/eth/v1/beacon/blob_sidecars/0x{block_number:x}", handle.http_endpoint()); + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + + // Test with non-existent block + let url = format!("{}/eth/v1/beacon/blob_sidecars/999999", handle.http_endpoint()); + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::NOT_FOUND); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_beacon_api_get_blobs() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); + let (api, handle) = spawn(node_config).await; + + // Disable auto-mining so we can include multiple transactions in the same block + api.anvil_set_auto_mine(false).await.unwrap(); + + let wallets = handle.dev_wallets().collect::>(); + let from = wallets[0].address(); + let to = wallets[1].address(); + + let provider = http_provider(&handle.http_endpoint()); + + let eip1559_est = provider.estimate_eip1559_fees().await.unwrap(); + let gas_price = provider.get_gas_price().await.unwrap(); + + // Create multiple blob transactions to be included in the same block + let blob_data = + [b"Hello Beacon API - Blob 1", b"Hello Beacon API - Blob 2", b"Hello Beacon API - Blob 3"]; + + let mut pending_txs = Vec::new(); + + // Send all transactions without waiting for receipts + for (i, data) in blob_data.iter().enumerate() { + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(data.as_slice()); + let sidecar = sidecar.build().unwrap(); + + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_nonce(i as u64) + .with_max_fee_per_blob_gas(gas_price + 1) + .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) + .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) + .with_blob_sidecar(sidecar) + .value(U256::from(100)); + + let mut tx = WithOtherFields::new(tx); + tx.populate_blob_hashes(); + + let pending = provider.send_transaction(tx).await.unwrap(); + pending_txs.push(pending); + } + + // Mine a block to include all transactions + api.evm_mine(None).await.unwrap(); + + // Get receipts for all transactions + let mut receipts = Vec::new(); + for pending in pending_txs { + let receipt = pending.get_receipt().await.unwrap(); + receipts.push(receipt); + } + + // Verify all transactions were included in the same block + let block_number = receipts[0].block_number.unwrap(); + for (i, receipt) in receipts.iter().enumerate() { + assert_eq!( + receipt.block_number.unwrap(), + block_number, + "Transaction {i} was not included in block {block_number}" + ); + } + + // Extract the actual versioned hashes from the mined transactions + let mut actual_versioned_hashes = Vec::new(); + for receipt in &receipts { + let tx = provider.get_transaction_by_hash(receipt.transaction_hash).await.unwrap().unwrap(); + if let Some(blob_versioned_hashes) = tx.blob_versioned_hashes() { + actual_versioned_hashes.extend(blob_versioned_hashes.iter().copied()); + } + } + + // Test Beacon API endpoint using HTTP client + let client = reqwest::Client::new(); + let url = format!("{}/eth/v1/beacon/blobs/{}", handle.http_endpoint(), block_number); + + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + + let blobs_response: GetBlobsResponse = response.json().await.unwrap(); + + // Verify response structure + assert!(!blobs_response.execution_optimistic); + assert!(!blobs_response.finalized); + + // Verify we have blob data from all transactions + assert_eq!(blobs_response.data.len(), 3, "Expected 3 blobs from 3 transactions"); + + // Test filtering with versioned_hashes query parameter - single hash + let url = format!( + "{}/eth/v1/beacon/blobs/{}?versioned_hashes={}", + handle.http_endpoint(), + block_number, + actual_versioned_hashes[1] + ); + let response = client.get(&url).send().await.unwrap(); + let status = response.status(); + if status != reqwest::StatusCode::OK { + let error_body = response.text().await.unwrap(); + panic!("Expected OK status, got {status}: {error_body}"); + } + let blobs_response: GetBlobsResponse = response.json().await.unwrap(); + assert_eq!( + blobs_response.data.len(), + 1, + "Expected 1 blob when filtering by single versioned_hash" + ); + + // Test filtering with versioned_hashes query parameter - multiple versioned_hashes + // (comma-separated) + let url = format!( + "{}/eth/v1/beacon/blobs/{}?versioned_hashes={},{}", + handle.http_endpoint(), + block_number, + actual_versioned_hashes[0], + actual_versioned_hashes[2] + ); + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + let blobs_response: GetBlobsResponse = response.json().await.unwrap(); + assert_eq!( + blobs_response.data.len(), + 2, + "Expected 2 blobs when filtering by two versioned_hashes" + ); + + // Test filtering with non-existent versioned_hash + let non_existent_hash = + b256!("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); + let url = format!( + "{}/eth/v1/beacon/blobs/{}?versioned_hashes={}", + handle.http_endpoint(), + block_number, + non_existent_hash + ); + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + let blobs_response: GetBlobsResponse = response.json().await.unwrap(); + assert_eq!( + blobs_response.data.len(), + 0, + "Expected 0 blobs when filtering by non-existent versioned_hash" + ); + + // Test with special block identifiers + let test_ids = vec!["latest", "finalized", "safe", "earliest"]; + for block_id in test_ids { + let url = format!("{}/eth/v1/beacon/blobs/{}", handle.http_endpoint(), block_id); + assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::OK); + } + let url = format!("{}/eth/v1/beacon/blobs/pending", handle.http_endpoint()); + assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::NOT_FOUND); + + // Test with hex block number + let url = format!("{}/eth/v1/beacon/blobs/0x{block_number:x}", handle.http_endpoint()); + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + + // Test with non-existent block + let url = format!("{}/eth/v1/beacon/blobs/999999", handle.http_endpoint()); + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::NOT_FOUND); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_beacon_api_get_genesis() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); + let (_api, handle) = spawn(node_config).await; + + // Test Beacon API genesis endpoint using HTTP client + let client = reqwest::Client::new(); + let url = format!("{}/eth/v1/beacon/genesis", handle.http_endpoint()); + + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + + let genesis_response: GenesisResponse = response.json().await.unwrap(); + + assert!(genesis_response.data.genesis_time > 0); + assert_eq!(genesis_response.data.genesis_validators_root, B256::ZERO); + assert_eq!( + genesis_response.data.genesis_fork_version, + FixedBytes::from([0x00, 0x00, 0x00, 0x00]) + ); +} diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 7d0d38afb9e1b..88301d863437f 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -476,326 +476,3 @@ async fn can_get_blobs_by_tx_hash() { let blobs = api.anvil_get_blob_by_tx_hash(hash).unwrap().unwrap(); assert_eq!(blobs, sidecar.blobs); } - -#[tokio::test(flavor = "multi_thread")] -async fn test_beacon_api_get_blob_sidecars() { - let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); - let (api, handle) = spawn(node_config).await; - - // Disable auto-mining so we can include multiple transactions in the same block - api.anvil_set_auto_mine(false).await.unwrap(); - - let wallets = handle.dev_wallets().collect::>(); - let from = wallets[0].address(); - let to = wallets[1].address(); - - let provider = http_provider(&handle.http_endpoint()); - - let eip1559_est = provider.estimate_eip1559_fees().await.unwrap(); - let gas_price = provider.get_gas_price().await.unwrap(); - - // Create multiple blob transactions to be included in the same block - let blob_data = - [b"Hello Beacon API - Blob 1", b"Hello Beacon API - Blob 2", b"Hello Beacon API - Blob 3"]; - - let mut pending_txs = Vec::new(); - - // Send all transactions without waiting for receipts - for (i, data) in blob_data.iter().enumerate() { - let sidecar: SidecarBuilder = SidecarBuilder::from_slice(data.as_slice()); - let sidecar = sidecar.build().unwrap(); - - let tx = TransactionRequest::default() - .with_from(from) - .with_to(to) - .with_nonce(i as u64) - .with_max_fee_per_blob_gas(gas_price + 1) - .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) - .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) - .with_blob_sidecar(sidecar) - .value(U256::from(100)); - - let mut tx = WithOtherFields::new(tx); - tx.populate_blob_hashes(); - - let pending = provider.send_transaction(tx).await.unwrap(); - pending_txs.push(pending); - } - - // Mine a block to include all transactions - api.evm_mine(None).await.unwrap(); - - // Get receipts for all transactions - let mut receipts = Vec::new(); - for pending in pending_txs { - let receipt = pending.get_receipt().await.unwrap(); - receipts.push(receipt); - } - - // Verify all transactions were included in the same block - let block_number = receipts[0].block_number.unwrap(); - for (i, receipt) in receipts.iter().enumerate() { - assert_eq!( - receipt.block_number.unwrap(), - block_number, - "Transaction {i} was not included in block {block_number}" - ); - } - - // Test Beacon API endpoint using HTTP client - let client = reqwest::Client::new(); - let url = format!("{}/eth/v1/beacon/blob_sidecars/{}", handle.http_endpoint(), block_number); - - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::OK); - - let body: serde_json::Value = response.json().await.unwrap(); - - // Verify response structure - assert!(body["data"].is_array()); - assert!(body["execution_optimistic"].is_boolean()); - assert!(body["finalized"].is_boolean()); - - // Verify we have blob data from all transactions - let blobs = body["data"].as_array().unwrap(); - assert_eq!(blobs.len(), 3, "Expected 3 blob sidecars from 3 transactions"); - - // Verify blob structure for each blob - for (i, blob) in blobs.iter().enumerate() { - assert!(blob["index"].is_string(), "Blob {i} missing index"); - assert!(blob["blob"].is_string(), "Blob {i} missing blob data"); - assert!(blob["kzg_commitment"].is_string(), "Blob {i} missing kzg_commitment"); - assert!(blob["kzg_proof"].is_string(), "Blob {i} missing kzg_proof"); - } - - // Test filtering with indices query parameter - single index - let url = format!( - "{}/eth/v1/beacon/blob_sidecars/{}?indices=1", - handle.http_endpoint(), - block_number - ); - let response = client.get(&url).send().await.unwrap(); - let status = response.status(); - if status != reqwest::StatusCode::OK { - let error_body = response.text().await.unwrap(); - panic!("Expected OK status, got {status}: {error_body}"); - } - let body: serde_json::Value = response.json().await.unwrap(); - let filtered_blobs = body["data"].as_array().unwrap(); - assert_eq!(filtered_blobs.len(), 1, "Expected 1 blob sidecar when filtering by indices=1"); - assert_eq!(filtered_blobs[0]["index"].as_str().unwrap(), "1"); - - // Test filtering with indices query parameter - multiple indices (comma-separated) - let url = format!( - "{}/eth/v1/beacon/blob_sidecars/{}?indices=0,2", - handle.http_endpoint(), - block_number - ); - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::OK); - let body: serde_json::Value = response.json().await.unwrap(); - let filtered_blobs = body["data"].as_array().unwrap(); - assert_eq!(filtered_blobs.len(), 2, "Expected 2 blob sidecars when filtering by indices=0,2"); - let indices: Vec = - filtered_blobs.iter().map(|b| b["index"].as_str().unwrap().to_string()).collect(); - assert!(indices.contains(&"0".to_string()), "Expected index 0 in results"); - assert!(indices.contains(&"2".to_string()), "Expected index 2 in results"); - - // Test filtering with non-existent index - let url = format!( - "{}/eth/v1/beacon/blob_sidecars/{}?indices=99", - handle.http_endpoint(), - block_number - ); - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::OK); - let body: serde_json::Value = response.json().await.unwrap(); - let filtered_blobs = body["data"].as_array().unwrap(); - assert_eq!( - filtered_blobs.len(), - 0, - "Expected 0 blob sidecars when filtering by non-existent index" - ); - - // Test with special block identifiers - let test_ids = vec!["latest", "finalized", "safe", "earliest"]; - for block_id in test_ids { - let url = format!("{}/eth/v1/beacon/blob_sidecars/{}", handle.http_endpoint(), block_id); - assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::OK); - } - let url = format!("{}/eth/v1/beacon/blob_sidecars/pending", handle.http_endpoint()); - assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::NOT_FOUND); - - // Test with hex block number - let url = format!("{}/eth/v1/beacon/blob_sidecars/0x{block_number:x}", handle.http_endpoint()); - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::OK); - - // Test with non-existent block - let url = format!("{}/eth/v1/beacon/blob_sidecars/999999", handle.http_endpoint()); - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::NOT_FOUND); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_beacon_api_get_blobs() { - let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); - let (api, handle) = spawn(node_config).await; - - // Disable auto-mining so we can include multiple transactions in the same block - api.anvil_set_auto_mine(false).await.unwrap(); - - let wallets = handle.dev_wallets().collect::>(); - let from = wallets[0].address(); - let to = wallets[1].address(); - - let provider = http_provider(&handle.http_endpoint()); - - let eip1559_est = provider.estimate_eip1559_fees().await.unwrap(); - let gas_price = provider.get_gas_price().await.unwrap(); - - // Create multiple blob transactions to be included in the same block - let blob_data = - [b"Hello Beacon API - Blob 1", b"Hello Beacon API - Blob 2", b"Hello Beacon API - Blob 3"]; - - let mut pending_txs = Vec::new(); - - // Send all transactions without waiting for receipts - for (i, data) in blob_data.iter().enumerate() { - let sidecar: SidecarBuilder = SidecarBuilder::from_slice(data.as_slice()); - let sidecar = sidecar.build().unwrap(); - - let tx = TransactionRequest::default() - .with_from(from) - .with_to(to) - .with_nonce(i as u64) - .with_max_fee_per_blob_gas(gas_price + 1) - .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) - .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) - .with_blob_sidecar(sidecar) - .value(U256::from(100)); - - let mut tx = WithOtherFields::new(tx); - tx.populate_blob_hashes(); - - let pending = provider.send_transaction(tx).await.unwrap(); - pending_txs.push(pending); - } - - // Mine a block to include all transactions - api.evm_mine(None).await.unwrap(); - - // Get receipts for all transactions - let mut receipts = Vec::new(); - for pending in pending_txs { - let receipt = pending.get_receipt().await.unwrap(); - receipts.push(receipt); - } - - // Verify all transactions were included in the same block - let block_number = receipts[0].block_number.unwrap(); - for (i, receipt) in receipts.iter().enumerate() { - assert_eq!( - receipt.block_number.unwrap(), - block_number, - "Transaction {i} was not included in block {block_number}" - ); - } - - // Extract the actual versioned hashes from the mined transactions - let mut actual_versioned_hashes = Vec::new(); - for receipt in &receipts { - let tx = provider.get_transaction_by_hash(receipt.transaction_hash).await.unwrap().unwrap(); - if let Some(blob_versioned_hashes) = tx.blob_versioned_hashes() { - actual_versioned_hashes.extend(blob_versioned_hashes.iter().copied()); - } - } - - // Test Beacon API endpoint using HTTP client - let client = reqwest::Client::new(); - let url = format!("{}/eth/v1/beacon/blobs/{}", handle.http_endpoint(), block_number); - - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::OK); - - let body: serde_json::Value = response.json().await.unwrap(); - - // Verify response structure - assert!(body["data"].is_array()); - assert!(body["execution_optimistic"].is_boolean()); - assert!(body["finalized"].is_boolean()); - - // Verify we have blob data from all transactions - let blobs = body["data"].as_array().unwrap(); - assert_eq!(blobs.len(), 3, "Expected 3 blobs from 3 transactions"); - - // Test filtering with versioned_hashes query parameter - single hash - let url = format!( - "{}/eth/v1/beacon/blobs/{}?versioned_hashes={}", - handle.http_endpoint(), - block_number, - actual_versioned_hashes[1] - ); - let response = client.get(&url).send().await.unwrap(); - let status = response.status(); - if status != reqwest::StatusCode::OK { - let error_body = response.text().await.unwrap(); - panic!("Expected OK status, got {status}: {error_body}"); - } - let body: serde_json::Value = response.json().await.unwrap(); - let filtered_blobs = body["data"].as_array().unwrap(); - assert_eq!(filtered_blobs.len(), 1, "Expected 1 blob when filtering by single versioned_hash"); - - // Test filtering with versioned_hashes query parameter - multiple versioned_hashes - // (comma-separated) - let url = format!( - "{}/eth/v1/beacon/blobs/{}?versioned_hashes={},{}", - handle.http_endpoint(), - block_number, - actual_versioned_hashes[0], - actual_versioned_hashes[2] - ); - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::OK); - let body: serde_json::Value = response.json().await.unwrap(); - let filtered_blobs = body["data"].as_array().unwrap(); - assert_eq!(filtered_blobs.len(), 2, "Expected 2 blobs when filtering by two versioned_hashes"); - - // Test filtering with non-existent versioned_hash - let non_existent_hash = - b256!("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); - let url = format!( - "{}/eth/v1/beacon/blobs/{}?versioned_hashes={}", - handle.http_endpoint(), - block_number, - non_existent_hash - ); - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::OK); - let body: serde_json::Value = response.json().await.unwrap(); - let filtered_blobs = body["data"].as_array().unwrap(); - assert_eq!( - filtered_blobs.len(), - 0, - "Expected 0 blobs when filtering by non-existent versioned_hash" - ); - - // Test with special block identifiers - let test_ids = vec!["latest", "finalized", "safe", "earliest"]; - for block_id in test_ids { - let url = format!("{}/eth/v1/beacon/blobs/{}", handle.http_endpoint(), block_id); - assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::OK); - } - let url = format!("{}/eth/v1/beacon/blobs/pending", handle.http_endpoint()); - assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::NOT_FOUND); - - // Test with hex block number - let url = format!("{}/eth/v1/beacon/blobs/0x{block_number:x}", handle.http_endpoint()); - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::OK); - - // Test with non-existent block - let url = format!("{}/eth/v1/beacon/blobs/999999", handle.http_endpoint()); - let response = client.get(&url).send().await.unwrap(); - assert_eq!(response.status(), reqwest::StatusCode::NOT_FOUND); -} diff --git a/crates/anvil/tests/it/main.rs b/crates/anvil/tests/it/main.rs index 796c01c566a1f..4a52391f776c2 100644 --- a/crates/anvil/tests/it/main.rs +++ b/crates/anvil/tests/it/main.rs @@ -2,6 +2,7 @@ mod abi; mod anvil; mod anvil_api; mod api; +mod beacon_api; mod eip4844; mod eip7702; mod fork;