From 8e85f3a93a2cf02fc0dabd7bcd30bd61f8a5ea18 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Tue, 9 Dec 2025 21:09:29 +0530 Subject: [PATCH 1/3] Migrate to alloy from rust-web3 --- Cargo.lock | 4375 ++++++++++++----- Cargo.toml | 3 + chain/ethereum/Cargo.toml | 1 + chain/ethereum/build.rs | 1 + chain/ethereum/proto/ethereum.proto | 701 ++- chain/ethereum/src/adapter.rs | 267 +- chain/ethereum/src/call_helper.rs | 138 + chain/ethereum/src/chain.rs | 16 +- chain/ethereum/src/codec.rs | 931 +++- chain/ethereum/src/data_source.rs | 234 +- chain/ethereum/src/ethereum_adapter.rs | 1299 +++-- chain/ethereum/src/ingestor.rs | 12 +- chain/ethereum/src/lib.rs | 1 + .../src/protobuf/sf.ethereum.r#type.v2.rs | 780 ++- chain/ethereum/src/runtime/abi.rs | 233 +- chain/ethereum/src/runtime/runtime_adapter.rs | 76 +- chain/ethereum/src/tests.rs | 109 +- chain/ethereum/src/transport.rs | 177 +- chain/ethereum/src/trigger.rs | 244 +- chain/near/src/codec.rs | 13 +- chain/near/src/trigger.rs | 8 +- graph/Cargo.toml | 7 +- graph/src/abi/event_ext.rs | 169 + graph/src/abi/function_ext.rs | 303 ++ graph/src/abi/mod.rs | 20 + graph/src/abi/param.rs | 7 + graph/src/abi/value_ext.rs | 277 ++ graph/src/blockchain/mock.rs | 8 +- graph/src/blockchain/mod.rs | 16 +- graph/src/blockchain/types.rs | 156 +- graph/src/cheap_clone.rs | 2 +- graph/src/components/ethereum/mod.rs | 7 +- graph/src/components/ethereum/types.rs | 197 +- graph/src/components/store/mod.rs | 3 +- graph/src/components/store/traits.rs | 10 +- .../subgraph/proof_of_indexing/mod.rs | 22 +- .../subgraph/proof_of_indexing/online.rs | 4 +- graph/src/components/transaction_receipt.rs | 41 +- graph/src/data/graphql/values.rs | 13 +- graph/src/data/store/ethereum.rs | 22 +- graph/src/data/store/scalar/bigint.rs | 28 +- graph/src/data/store/scalar/bytes.rs | 14 +- graph/src/data/subgraph/mod.rs | 2 +- graph/src/data_source/common.rs | 305 +- graph/src/lib.rs | 6 +- graph/src/runtime/mod.rs | 2 +- graph/src/util/mod.rs | 3 + graph/src/util/test_utils.rs | 57 + graphql/src/store/resolver.rs | 7 +- node/src/manager/commands/chain.rs | 16 +- node/src/manager/commands/check_blocks.rs | 33 +- runtime/test/src/common.rs | 8 +- runtime/test/src/test.rs | 9 +- runtime/test/src/test/abi.rs | 42 +- runtime/wasm/Cargo.toml | 1 - runtime/wasm/src/asc_abi/class.rs | 31 +- runtime/wasm/src/host_exports.rs | 41 +- runtime/wasm/src/module/context.rs | 2 +- runtime/wasm/src/module/instance.rs | 2 +- runtime/wasm/src/to_from/external.rs | 167 +- server/index-node/src/resolver.rs | 4 +- store/postgres/src/chain_store.rs | 64 +- store/postgres/src/deployment.rs | 16 +- store/postgres/src/deployment_store.rs | 4 +- store/postgres/src/detail.rs | 5 +- store/postgres/src/store.rs | 4 +- store/postgres/src/subgraph_store.rs | 12 +- store/postgres/src/transaction_receipt.rs | 27 +- store/test-store/Cargo.toml | 1 + store/test-store/src/block_store.rs | 39 +- store/test-store/src/store.rs | 11 +- .../tests/chain/ethereum/manifest.rs | 12 +- store/test-store/tests/graph/entity_cache.rs | 4 +- store/test-store/tests/postgres/chain_head.rs | 20 +- store/test-store/tests/postgres/relational.rs | 11 +- .../tests/postgres/relational_bytes.rs | 13 +- store/test-store/tests/postgres/store.rs | 42 +- store/test-store/tests/postgres/writable.rs | 4 +- tests/Cargo.toml | 4 + tests/src/contract.rs | 21 +- tests/src/fixture/ethereum.rs | 116 +- tests/src/fixture/mod.rs | 5 +- tests/tests/integration_tests.rs | 2 +- tests/tests/runner_tests.rs | 9 +- 84 files changed, 8387 insertions(+), 3742 deletions(-) create mode 100644 chain/ethereum/src/call_helper.rs create mode 100644 graph/src/abi/event_ext.rs create mode 100644 graph/src/abi/function_ext.rs create mode 100644 graph/src/abi/mod.rs create mode 100644 graph/src/abi/param.rs create mode 100644 graph/src/abi/value_ext.rs create mode 100644 graph/src/util/test_utils.rs diff --git a/Cargo.lock b/Cargo.lock index c2d77c000a8..3ffeb3a7ecd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,42 +14,805 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "alloy" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f609fb6392508278b276906d6247ea44f5777e448db95444fa39e89b7aee896a" +dependencies = [ + "alloy-consensus", + "alloy-contract", + "alloy-core", + "alloy-eips", + "alloy-genesis", + "alloy-json-rpc", + "alloy-network", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "alloy-trie", +] + +[[package]] +name = "alloy-chains" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b163ff4acf0eac29af05a911397cc418a76e153467b859398adc26cb9335a611" +dependencies = [ + "alloy-primitives", + "num_enum", + "strum 0.27.2", +] + +[[package]] +name = "alloy-consensus" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3dcd2b4e208ce5477de90ccdcbd4bde2c8fb06af49a443974e92bb8f2c5e93f" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-trie", + "alloy-tx-macros", + "arbitrary", + "auto_impl", + "borsh", + "c-kzg", + "derive_more 2.1.1", + "either", + "k256", + "once_cell", + "rand 0.8.5", + "secp256k1 0.30.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.17", +] + +[[package]] +name = "alloy-consensus-any" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee5655f234985f5ab1e31bef7e02ed11f0a899468cf3300e061e1b96e9e11de0" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "arbitrary", + "serde", +] + +[[package]] +name = "alloy-contract" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f01b6d8e5b4f3222aaf7f18613a7292e2fbc9163fe120649cd1b078ca534349" +dependencies = [ + "alloy-consensus", + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "futures 0.3.31", + "futures-util", + "serde_json", + "thiserror 2.0.17", +] + +[[package]] +name = "alloy-core" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d4087016b0896051dd3d03e0bedda2f4d4d1689af8addc8450288c63a9e5f68" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "369f5707b958927176265e8a58627fc6195e5dfa5c55689396e68b241b3a72e6" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "arbitrary", + "itoa", + "proptest", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-eip2124" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741bdd7499908b3aa0b159bba11e71c8cddd009a2c2eb7a06e825f1ec87900a5" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "crc", + "rand 0.8.5", + "serde", + "thiserror 2.0.17", +] + +[[package]] +name = "alloy-eip2930" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9441120fa82df73e8959ae0e4ab8ade03de2aaae61be313fbf5746277847ce25" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "borsh", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2919c5a56a1007492da313e7a3b6d45ef5edc5d33416fdec63c0d7a2702a0d20" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "borsh", + "k256", + "rand 0.8.5", + "serde", + "thiserror 2.0.17", +] + +[[package]] +name = "alloy-eips" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6847d641141b92a1557094aa6c236cbe49c06fb24144d4a21fe6acb970c15888" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "arbitrary", + "auto_impl", + "borsh", + "c-kzg", + "derive_more 2.1.1", + "either", + "serde", + "serde_with", + "sha2", + "thiserror 2.0.17", +] + +[[package]] +name = "alloy-genesis" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe3192fca2eb0b0c4b122b3c2d8254496b88a4e810558dddd3ea2f30ad9469df" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "alloy-trie", + "borsh", + "serde", + "serde_with", +] + +[[package]] +name = "alloy-json-abi" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e3cf01219c966f95a460c95f1d4c30e12f6c18150c21a30b768af2a2a29142" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ab3330e491053e9608b2a315f147357bb8acb9377a988c1203f2e8e2b296c9" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "http 1.4.0", + "serde", + "serde_json", + "thiserror 2.0.17", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e22ff194b1e34b4defd1e257e3fe4dce0eee37451c7757a1510d6b23e7379a" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-any", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "derive_more 2.1.1", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.17", +] + +[[package]] +name = "alloy-network-primitives" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a6cbb9f431bdad294eebb5af9b293d6979e633bfe5468d1e87c1421a858265" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6a0fb18dd5fb43ec5f0f6a20be1ce0287c79825827de5744afaa6c957737c33" +dependencies = [ + "alloy-rlp", + "arbitrary", + "bytes", + "cfg-if 1.0.4", + "const-hex", + "derive_more 2.1.1", + "foldhash 0.2.0", + "hashbrown 0.16.1", + "indexmap 2.12.1", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "proptest-derive 0.6.0", + "rand 0.9.2", + "rapidhash", + "ruint", + "rustc-hash 2.1.1", + "serde", + "sha3", + "tiny-keccak 2.0.2", +] + +[[package]] +name = "alloy-provider" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5dde1abc3d582e53d139904fcdd8b2103f0bd03e8f2acb4292edbbaeaa7e6e" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types-anvil", + "alloy-rpc-types-debug", + "alloy-rpc-types-eth", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-signer", + "alloy-sol-types", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "either", + "futures 0.3.31", + "futures-utils-wasm", + "lru", + "parking_lot", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-pubsub" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbfe0a3c553a027f722185fb574124d205147fffb309cae52d0a2094f076887" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "auto_impl", + "bimap", + "futures 0.3.31", + "parking_lot", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing", + "wasmtimer", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +dependencies = [ + "alloy-rlp-derive", + "arrayvec 0.7.6", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "alloy-rpc-client" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a94bdef2710322c6770be08689fee0878c2ad75615b8fc40e05d7f3c9618c0b" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-pubsub", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "futures 0.3.31", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-rpc-types" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "811a573c8080e1b492d488e6a240ec5dd7677d7167e91ce9cb4d0ec1fcac8027" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-anvil", + "alloy-rpc-types-debug", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-anvil" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "838ca94be532a929f27961851000ec8bbbaeb06e2a2bcca44fac7855a2fe0f6f" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df0b34551ca2eab8ec83b56cb709ee5da991737282180d354a659b907f00dc" +dependencies = [ + "alloy-consensus-any", + "alloy-rpc-types-eth", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-debug" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49a3a168a5bf18f1cf7ed5723a650aebe714edf7665b53dacf5707716733d0" +dependencies = [ + "alloy-primitives", + "derive_more 2.1.1", + "serde", + "serde_with", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe16cd1dea6089902ec609e04261a9ae6d11ec66005ba24c1f97f0eefbc0fa9" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "arbitrary", + "derive_more 2.1.1", + "rand 0.8.5", + "serde", + "strum 0.27.2", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f9f130511b8632686dfe6f9909b38d7ae4c68de3ce17d28991400646a39b25" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "arbitrary", + "itertools 0.14.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.17", +] + +[[package]] +name = "alloy-rpc-types-trace" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cafe859944638c5d57d1a3a0034cdb5d07c98c37de8adce5508f28834acf958f" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", + "serde_json", + "thiserror 2.0.17", +] + +[[package]] +name = "alloy-rpc-types-txpool" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afaa06544e36f223b99b1415a12911230fd527994f020736c3c7950d5080208e" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-serde" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "067b718d2e6ac1bb889341fcc7a250cfa49bcd3ba4f23923f1c1eb1f2b10cb7c" +dependencies = [ + "alloy-primitives", + "arbitrary", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acff6b251740ef473932386d3b71657d3825daebf2217fb41a7ef676229225d4" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "either", + "elliptic-curve", + "k256", + "thiserror 2.0.17", +] + +[[package]] +name = "alloy-signer-local" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "c9129ef31975d987114c27c9930ee817cf3952355834d47f2fdf4596404507e8" dependencies = [ - "gimli 0.29.0", + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256", + "rand 0.8.5", + "thiserror 2.0.17", ] [[package]] -name = "addr2line" -version = "0.24.2" +name = "alloy-sol-macro" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "09eb18ce0df92b4277291bbaa0ed70545d78b02948df756bbd3d6214bf39a218" dependencies = [ - "gimli 0.31.1", + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] -name = "adler" -version = "1.0.2" +name = "alloy-sol-macro-expander" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "95d9fa2daf21f59aa546d549943f10b5cce1ae59986774019fbedae834ffe01b" +dependencies = [ + "alloy-json-abi", + "alloy-sol-macro-input", + "const-hex", + "heck 0.5.0", + "indexmap 2.12.1", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.111", + "syn-solidity", + "tiny-keccak 2.0.2", +] [[package]] -name = "aho-corasick" -version = "1.1.3" +name = "alloy-sol-macro-input" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "9396007fe69c26ee118a19f4dee1f5d1d6be186ea75b3881adf16d87f8444686" dependencies = [ - "memchr", + "alloy-json-abi", + "const-hex", + "dunce", + "heck 0.5.0", + "macro-string", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.111", + "syn-solidity", ] [[package]] -name = "allocator-api2" -version = "0.2.21" +name = "alloy-sol-type-parser" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +checksum = "af67a0b0dcebe14244fc92002cd8d96ecbf65db4639d479f5fcd5805755a4c27" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09aeea64f09a7483bdcd4193634c7e5cf9fd7775ee767585270cd8ce2d69dc95" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec1fb08ee484e615f24867c0b154fff5722bb00176102a16868c6532b7c3623" +dependencies = [ + "alloy-json-rpc", + "auto_impl", + "base64 0.22.1", + "derive_more 2.1.1", + "futures 0.3.31", + "futures-utils-wasm", + "parking_lot", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-transport-http" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b722073c76f2de7e118d546ee1921c50710f97feb32aed50db94cfa5b663e1" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest", + "serde_json", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdedcf401aab4b96d8b5e6638b79d04a6afb96c0bfcb50a2324fbadfe65c47b3" +dependencies = [ + "alloy-json-rpc", + "alloy-pubsub", + "alloy-transport", + "bytes", + "futures 0.3.31", + "interprocess", + "pin-project", + "serde", + "serde_json", + "tokio", + "tokio-util 0.7.17", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942210908f0c56941097f5653a5f334546940e6fd9073495b257e52216469feb" +dependencies = [ + "alloy-pubsub", + "alloy-transport", + "futures 0.3.31", + "http 1.4.0", + "serde_json", + "tokio", + "tokio-tungstenite 0.26.2", + "tracing", + "ws_stream_wasm", +] + +[[package]] +name = "alloy-trie" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b77b56af09ead281337d06b1d036c88e2dc8a2e45da512a532476dbee94912b" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "arrayvec 0.7.6", + "derive_arbitrary", + "derive_more 2.1.1", + "nybbles", + "proptest", + "proptest-derive 0.5.1", + "serde", + "smallvec", + "tracing", +] + +[[package]] +name = "alloy-tx-macros" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04950a13cc4209d8e9b78f306e87782466bad8538c94324702d061ff03e211c9" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.111", +] [[package]] name = "android_system_properties" @@ -62,9 +825,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -77,36 +840,37 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell_polyfill", + "windows-sys 0.61.2", ] [[package]] @@ -115,23 +879,227 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "ar_archive_writer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" +dependencies = [ + "object 0.32.2", +] + [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "arc-swap" -version = "1.7.1" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e" +dependencies = [ + "rustversion", +] + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec 0.7.6", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint 0.4.6", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.111", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-std 0.5.0", + "arrayvec 0.7.6", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", +] [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -141,9 +1109,12 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] [[package]] name = "ascii_utils" @@ -180,8 +1151,8 @@ dependencies = [ "futures-timer", "futures-util", "handlebars", - "http 1.3.1", - "indexmap 2.11.4", + "http 1.4.0", + "indexmap 2.12.1", "mime", "multer", "num-traits", @@ -192,7 +1163,7 @@ dependencies = [ "serde_urlencoded", "static_assertions_next", "tempfile", - "thiserror 1.0.61", + "thiserror 1.0.69", ] [[package]] @@ -202,13 +1173,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8725874ecfbf399e071150b8619c4071d7b2b7a2f117e173dddef53c6bdb6bb1" dependencies = [ "async-graphql", - "axum 0.8.6", + "axum 0.8.8", "bytes", "futures-util", "serde_json", "tokio", "tokio-stream", - "tokio-util 0.7.11", + "tokio-util 0.7.17", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -220,13 +1191,13 @@ checksum = "fd45deb3dbe5da5cdb8d6a670a7736d735ba65b455328440f236dfb113727a3d" dependencies = [ "Inflector", "async-graphql-parser", - "darling 0.20.10", + "darling 0.20.11", "proc-macro-crate", "proc-macro2", "quote", - "strum", - "syn 2.0.106", - "thiserror 1.0.61", + "strum 0.26.3", + "syn 2.0.111", + "thiserror 1.0.69", ] [[package]] @@ -248,7 +1219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ecdaff7c9cffa3614a9f9999bf9ee4c3078fe3ce4d6a6e161736b56febf2de" dependencies = [ "bytes", - "indexmap 2.11.4", + "indexmap 2.12.1", "serde", "serde_json", ] @@ -261,7 +1232,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -283,18 +1254,29 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures 0.3.31", + "pharos", + "rustc_version 0.4.1", ] [[package]] @@ -321,23 +1303,34 @@ dependencies = [ ] [[package]] -name = "autocfg" +name = "auto_impl" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", - "axum-core 0.4.3", + "axum-core 0.4.5", "bytes", "futures-util", - "http 1.3.1", - "http-body 1.0.0", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", "itoa", "matchit 0.7.3", @@ -347,27 +1340,27 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper 1.0.1", - "tower 0.4.13", + "sync_wrapper", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "axum" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" dependencies = [ "axum-core 0.5.5", "base64 0.22.1", "bytes", "form_urlencoded", "futures-util", - "http 1.3.1", - "http-body 1.0.0", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "itoa", "matchit 0.8.4", @@ -380,9 +1373,9 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper 1.0.1", + "sync_wrapper", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.28.0", "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -391,20 +1384,20 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.3.1", - "http-body 1.0.0", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -417,12 +1410,12 @@ checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ "bytes", "futures-core", - "http 1.3.1", - "http-body 1.0.0", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", - "sync_wrapper 1.0.1", + "sync_wrapper", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", @@ -430,34 +1423,35 @@ dependencies = [ [[package]] name = "backon" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0b50b1b78dbadd44ab18b3c794e496f3a139abb9fbc27d9c94c4eebbb96496" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" dependencies = [ "fastrand", ] -[[package]] -name = "backtrace" -version = "0.3.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" -dependencies = [ - "addr2line 0.22.0", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base-x" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str", + "match-lookup", +] + [[package]] name = "base64" version = "0.13.1" @@ -476,6 +1470,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" + [[package]] name = "beef" version = "0.5.2" @@ -508,6 +1508,56 @@ dependencies = [ "num-traits", ] +[[package]] +name = "bigdecimal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560f42649de9fa436b73517378a147ec21f6c997a546581df4b4b31677828934" +dependencies = [ + "autocfg", + "libm", + "num-bigint 0.4.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitcoin-io" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -516,9 +1566,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitvec" @@ -554,9 +1604,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "constant_time_eq 0.3.1", ] @@ -578,6 +1628,41 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "bs58" version = "0.4.0" @@ -595,9 +1680,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "serde", @@ -605,18 +1690,18 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" dependencies = [ "allocator-api2", ] [[package]] name = "byte-slice-cast" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "byteorder" @@ -626,18 +1711,34 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +dependencies = [ + "serde", +] + +[[package]] +name = "c-kzg" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "e00bf4b112b07b505472dbefd19e37e53307e2bfed5a79e0cc161d58ccd0e687" dependencies = [ + "arbitrary", + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", "serde", ] [[package]] name = "cc" -version = "1.2.43" +version = "1.2.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" +checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" dependencies = [ "find-msvc-tools", "jobserver", @@ -653,9 +1754,15 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" @@ -668,7 +1775,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -680,14 +1787,14 @@ dependencies = [ "core2", "multibase", "multihash", - "unsigned-varint 0.8.0", + "unsigned-varint", ] [[package]] name = "clap" -version = "4.5.8" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -695,9 +1802,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -708,33 +1815,36 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cobs" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.17", +] [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "combine" @@ -747,7 +1857,7 @@ dependencies = [ "memchr", "pin-project-lite", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.17", ] [[package]] @@ -759,10 +1869,54 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.0", + "unicode-width 0.2.2", "windows-sys 0.59.0", ] +[[package]] +name = "const-hex" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "proptest", + "serde_core", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + +[[package]] +name = "const_format" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -783,9 +1937,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "convert_case" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -802,9 +1956,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -827,18 +1981,18 @@ dependencies = [ [[package]] name = "cpp_demangle" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" +checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", ] [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -895,12 +2049,12 @@ dependencies = [ "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli 0.31.1", - "hashbrown 0.15.2", + "gimli", + "hashbrown 0.15.5", "log", "pulley-interpreter", "regalloc2", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "serde", "smallvec", "target-lexicon", @@ -979,13 +2133,28 @@ version = "0.120.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02e3f4d783a55c64266d17dc67d2708852235732a100fc40dd9f1051adc64d7b" +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", ] [[package]] @@ -1012,9 +2181,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1031,30 +2200,42 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1072,33 +2253,33 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ "csv-core", "itoa", "ryu", - "serde", + "serde_core", ] [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ "memchr", ] [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", + "darling_core 0.20.11", + "darling_macro 0.20.11", ] [[package]] @@ -1113,16 +2294,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1135,19 +2316,20 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "serde", "strsim", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core 0.20.10", + "darling_core 0.20.11", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1158,20 +2340,34 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.106", + "syn 2.0.111", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if 1.0.4", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "data-encoding-macro" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" +checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1179,21 +2375,22 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" +checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.111", ] [[package]] name = "deadpool" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f" +checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" dependencies = [ "deadpool-runtime", + "lazy_static", "num_cpus", "tokio", ] @@ -1222,14 +2419,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "930c7171c8df9fb1782bdf9b918ed9ed2d33d1d22300abb754f9085bc48bf8e8" +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "deranged" -version = "0.3.11" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -1243,49 +2450,61 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version", - "syn 2.0.106", + "rustc_version 0.4.1", + "syn 2.0.111", ] [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case 0.7.1", + "convert_case 0.10.0", "proc-macro2", "quote", - "syn 2.0.106", + "rustc_version 0.4.1", + "syn 2.0.111", "unicode-xid", ] [[package]] name = "diesel" -version = "2.3.2" +version = "2.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8496eeb328dce26ee9d9b73275d396d9bddb433fa30106cf6056dd8c3c2764c" +checksum = "e130c806dccc85428c564f2dc5a96e05b6615a27c9a28776bd7761a9af4bb552" dependencies = [ - "bigdecimal 0.3.1", - "bitflags 2.9.0", + "bigdecimal 0.4.9", + "bitflags 2.10.0", "byteorder", "chrono", "diesel_derives", @@ -1301,9 +2520,9 @@ dependencies = [ [[package]] name = "diesel-async" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c69eded9cb72c7e112505caec23da00149d4dd49f4c96b3c83b2b63f0aa3da5f" +checksum = "13096fb8dae53f2d411c4b523bec85f45552ed3044a2ab4d85fb2092d9cb4f34" dependencies = [ "deadpool", "diesel", @@ -1323,7 +2542,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1337,22 +2556,22 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.3.3" +version = "2.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09af0e983035368439f1383011cd87c46f41da81d0f21dc3727e2857d5a43c8e" +checksum = "c30b2969f923fa1f73744b92bb7df60b858df8832742d9a3aceb79236c0be1d2" dependencies = [ "diesel_table_macro_syntax", "dsl_auto_type", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "diesel_migrations" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee060f709c3e3b1cadd83fcd0f61711f7a8cf493348f758d3a1c1147d70b3c97" +checksum = "745fd255645f0f1135f9ec55c7b00e0882192af9683ab4731e4bba3da82b8f9c" dependencies = [ "diesel", "migrations_internals", @@ -1365,7 +2584,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" dependencies = [ - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1396,6 +2615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] @@ -1406,39 +2626,29 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "dirs-sys-next", ] [[package]] name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", + "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", - "windows-sys 0.48.0", + "redox_users 0.5.2", + "windows-sys 0.61.2", ] [[package]] @@ -1448,7 +2658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -1460,9 +2670,15 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + [[package]] name = "downcast-rs" version = "2.0.2" @@ -1480,14 +2696,76 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "serdect", + "signature", + "spki", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "serdect", + "subtle", + "zeroize", +] [[package]] name = "embedded-io" @@ -1509,18 +2787,38 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if 1.0.4", +] + +[[package]] +name = "enum-ordinalize" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" dependencies = [ - "cfg-if 1.0.0", + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] name = "env_filter" -version = "0.1.0" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", @@ -1541,29 +2839,29 @@ dependencies = [ [[package]] name = "envconfig" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1d02ec9fdd0a585580bdc8fb7ad01675eee5e3b7336cedbabe3aab4a026dbc" +checksum = "2934d78aeb06e54961db5b96cfca2b01f5cb01cf11c34f7b088932fd48233ac6" dependencies = [ "envconfig_derive", ] [[package]] name = "envconfig_derive" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4291f0c7220b67ad15e9d5300ba2f215cee504f0924d60e77c9d1c77e7a69b1" +checksum = "452c458dfb890a2fea64e4c894f83daad61689fb9bbe84c382e1ce6d7536710b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" @@ -1576,12 +2874,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.12" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1597,7 +2895,7 @@ dependencies = [ "serde", "serde_json", "sha3", - "thiserror 1.0.61", + "thiserror 1.0.69", "uint 0.9.5", ] @@ -1608,7 +2906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef" dependencies = [ "crunchy", - "fixed-hash", + "fixed-hash 0.7.0", "impl-rlp", "impl-serde", "tiny-keccak 2.0.2", @@ -1621,10 +2919,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6" dependencies = [ "ethbloom", - "fixed-hash", + "fixed-hash 0.7.0", "impl-rlp", "impl-serde", - "primitive-types", + "primitive-types 0.11.1", "uint 0.9.5", ] @@ -1651,15 +2949,47 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec 0.7.6", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec 0.7.6", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "firestorm" @@ -1685,6 +3015,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixedbitset" version = "0.5.7" @@ -1693,9 +3035,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.0.30" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -1713,6 +3055,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1814,7 +3162,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1854,6 +3202,12 @@ dependencies = [ "slab", ] +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + [[package]] name = "fxhash" version = "0.2.1" @@ -1869,7 +3223,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "debugid", "fxhash", "serde", @@ -1884,37 +3238,36 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", + "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", + "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", + "js-sys", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasip2", + "wasm-bindgen", ] -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" - [[package]] name = "gimli" version = "0.31.1" @@ -1922,7 +3275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator 0.3.0", - "indexmap 2.11.4", + "indexmap 2.12.1", "stable_deref_trait", ] @@ -1944,15 +3297,21 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "time", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "globset" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ "aho-corasick", "bstr", @@ -1987,6 +3346,7 @@ name = "graph" version = "0.36.0" dependencies = [ "Inflector", + "alloy", "anyhow", "async-stream", "async-trait", @@ -2011,14 +3371,14 @@ dependencies = [ "graph_derive", "graphql-parser", "hex", - "hex-literal 1.0.0", + "hex-literal 1.1.0", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "http-body-util", "humantime", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", - "itertools", + "itertools 0.14.0", "lazy_static", "lru_time_cache", "maplit", @@ -2036,7 +3396,7 @@ dependencies = [ "redis", "regex", "reqwest", - "semver", + "semver 1.0.27", "serde", "serde_derive", "serde_json", @@ -2057,12 +3417,11 @@ dependencies = [ "tokio", "tokio-retry", "tokio-stream", - "toml 0.9.7", + "toml 0.9.10+spec-1.1.0", "tonic", "tonic-build", "url", "wasmparser 0.118.2", - "web3", "wiremock", ] @@ -2088,17 +3447,18 @@ dependencies = [ "graph-runtime-derive", "graph-runtime-wasm", "hex", - "itertools", + "itertools 0.14.0", "jsonrpc-core", "prost", "prost-types", - "semver", + "semver 1.0.27", "serde", "thiserror 2.0.17", "tiny-keccak 1.5.0", "tokio", "tokio-stream", "tonic-build", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2132,7 +3492,7 @@ dependencies = [ "lazy_static", "prost", "prost-types", - "semver", + "semver 1.0.27", "serde", "tokio", "tokio-stream", @@ -2202,7 +3562,7 @@ dependencies = [ "graph-store-postgres", "graphman", "graphman-server", - "itertools", + "itertools 0.14.0", "json-structural-diff", "lazy_static", "notify", @@ -2221,7 +3581,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2234,7 +3594,7 @@ dependencies = [ "graph-runtime-derive", "graph-runtime-wasm", "rand 0.9.2", - "semver", + "semver 1.0.27", "test-store", "wasmtime", ] @@ -2246,13 +3606,12 @@ dependencies = [ "anyhow", "async-trait", "bs58 0.4.0", - "ethabi", "graph", "graph-runtime-derive", "hex", "never", "parity-wasm", - "semver", + "semver 1.0.27", "serde_yaml", "wasm-instrument", "wasmtime", @@ -2310,7 +3669,7 @@ dependencies = [ "chrono", "clap", "deadpool", - "derive_more 2.0.1", + "derive_more 2.1.1", "diesel", "diesel-async", "diesel-derive-enum", @@ -2323,7 +3682,7 @@ dependencies = [ "graphman-store", "graphql-parser", "hex", - "itertools", + "itertools 0.14.0", "lazy_static", "lru_time_cache", "maybe-owned", @@ -2358,12 +3717,13 @@ dependencies = [ "graph-runtime-wasm", "graph-server-index-node", "graph-store-postgres", - "secp256k1", + "secp256k1 0.21.3", "serde", "serde_yaml", "slog", "tokio", "tokio-stream", + "web3", ] [[package]] @@ -2374,7 +3734,7 @@ dependencies = [ "proc-macro-utils", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2387,7 +3747,7 @@ dependencies = [ "graph", "graph-store-postgres", "graphman-store", - "itertools", + "itertools 0.14.0", "thiserror 2.0.17", "tokio", ] @@ -2399,7 +3759,7 @@ dependencies = [ "anyhow", "async-graphql", "async-graphql-axum", - "axum 0.8.6", + "axum 0.8.8", "chrono", "diesel", "diesel-async", @@ -2426,7 +3786,7 @@ dependencies = [ "async-trait", "chrono", "diesel", - "strum", + "strum 0.26.3", ] [[package]] @@ -2436,7 +3796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a818c0d883d7c0801df27be910917750932be279c7bc82dc541b8769425f409" dependencies = [ "combine", - "thiserror 1.0.61", + "thiserror 1.0.69", ] [[package]] @@ -2452,11 +3812,22 @@ dependencies = [ "serde_with", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ "bytes", "fnv", @@ -2464,29 +3835,29 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.11.4", + "indexmap 2.12.1", "slab", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.17", "tracing", ] [[package]] name = "h2" -version = "0.4.5" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.11.4", + "http 1.4.0", + "indexmap 2.12.1", "slab", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.17", "tracing", ] @@ -2501,7 +3872,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 1.0.61", + "thiserror 1.0.69", ] [[package]] @@ -2512,12 +3883,31 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.1.5", + "serde", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "foldhash", + "foldhash 0.2.0", "serde", + "serde_core", ] [[package]] @@ -2532,14 +3922,14 @@ dependencies = [ [[package]] name = "headers" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "headers-core", - "http 1.3.1", + "http 1.4.0", "httpdate", "mime", "sha1", @@ -2551,7 +3941,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -2577,9 +3967,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2587,6 +3977,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec 0.7.6", +] + [[package]] name = "hex-literal" version = "0.3.4" @@ -2595,9 +3994,9 @@ checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" [[package]] name = "hex-literal" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcaaec4551594c969335c98c903c1397853d4198408ea609190f420500f6be71" +checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" [[package]] name = "hmac" @@ -2610,11 +4009,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2630,12 +4029,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -2652,12 +4050,12 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -2668,16 +4066,16 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", - "http-body 1.0.0", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.9.4" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -2693,22 +4091,22 @@ checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hyper" -version = "0.14.29" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.5.10", "tokio", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", @@ -2717,17 +4115,17 @@ dependencies = [ [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2 0.4.5", - "http 1.3.1", - "http-body 1.0.0", + "h2 0.4.12", + "http 1.4.0", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -2740,20 +4138,20 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", - "http 1.3.1", - "hyper 1.7.0", + "http 1.4.0", + "hyper 1.8.1", "hyper-util", "rustls", - "rustls-native-certs 0.7.1", + "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki-roots 1.0.4", ] [[package]] @@ -2762,7 +4160,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "pin-project-lite", "tokio", @@ -2777,7 +4175,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "native-tls", "tokio", @@ -2787,25 +4185,26 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", - "http-body 1.0.0", - "hyper 1.7.0", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.8.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.6.1", "system-configuration", "tokio", + "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", "windows-registry", @@ -2813,14 +4212,15 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -2840,7 +4240,7 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1fcc7f316b2c079dde77564a1360639c1a956a23fa96122732e416cb10717bb" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "num-traits", "rand 0.8.5", "static_assertions", @@ -2848,21 +4248,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -2871,99 +4272,61 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", + "icu_locale_core", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "id-arena" version = "2.2.1" @@ -3000,9 +4363,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -3037,13 +4400,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.111", ] [[package]] @@ -3059,12 +4422,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ + "arbitrary", "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -3075,7 +4439,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "inotify-sys", "libc", ] @@ -3090,27 +4454,31 @@ dependencies = [ ] [[package]] -name = "io-uring" -version = "0.7.10" +name = "interprocess" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" dependencies = [ - "bitflags 2.9.0", - "cfg-if 1.0.0", + "doctest-file", + "futures-core", "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", ] [[package]] name = "ipnet" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -3118,20 +4486,38 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.5.2", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] [[package]] name = "itertools" @@ -3144,9 +4530,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" [[package]] name = "ittapi" @@ -3170,42 +4556,43 @@ dependencies = [ [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", - "serde", + "serde_core", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -3256,14 +4643,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3dc3e9cf2ba50b7b1d7d76a667619f82846caa39e8e8daa8a4962d74acaddca" dependencies = [ "anyhow", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "async-trait", "beef", "futures-channel", "futures-util", "globset", "http 0.2.12", - "hyper 0.14.29", + "hyper 0.14.32", "jsonrpsee-types", "lazy_static", "parking_lot", @@ -3271,7 +4658,7 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "serde_json", - "thiserror 1.0.61", + "thiserror 1.0.69", "tokio", "tracing", "unicase", @@ -3285,7 +4672,7 @@ checksum = "03802f0373a38c2420c70b5144742d800b509e2937edc4afb116434f07120117" dependencies = [ "futures-channel", "futures-util", - "hyper 0.14.29", + "hyper 0.14.32", "jsonrpsee-core", "jsonrpsee-types", "serde", @@ -3305,10 +4692,24 @@ dependencies = [ "beef", "serde", "serde_json", - "thiserror 1.0.61", + "thiserror 1.0.69", "tracing", ] +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if 1.0.4", + "ecdsa", + "elliptic-curve", + "once_cell", + "serdect", + "sha2", +] + [[package]] name = "keccak" version = "0.1.5" @@ -3318,6 +4719,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "kqueue" version = "1.1.1" @@ -3358,9 +4769,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.175" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libm" @@ -3370,31 +4781,32 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "libc", + "redox_syscall 0.6.0", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" @@ -3407,9 +4819,24 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lru_time_cache" @@ -3419,19 +4846,41 @@ checksum = "9106e1d747ffd48e6be5bb2d97fa706ed25b144fbee4d5c02eae110cd8d6badd" [[package]] name = "mach2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" dependencies = [ "libc", ] +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "maplit" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "match-lookup" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "matches" version = "0.1.10" @@ -3462,23 +4911,23 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "digest 0.10.7", ] [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memfd" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" dependencies = [ - "rustix 0.38.34", + "rustix 1.1.3", ] [[package]] @@ -3488,7 +4937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36c791ecdf977c99f45f23280405d7723727470f6689a5e6dbf513ac547ae10d" dependencies = [ "serde", - "toml 0.9.7", + "toml 0.9.10+spec-1.1.0", ] [[package]] @@ -3520,23 +4969,24 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "adler", + "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -3548,7 +4998,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.3.1", + "http 1.4.0", "httparse", "memchr", "mime", @@ -3558,36 +5008,37 @@ dependencies = [ [[package]] name = "multibase" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" dependencies = [ "base-x", + "base256emoji", "data-encoding", "data-encoding-macro", ] [[package]] name = "multihash" -version = "0.19.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" dependencies = [ "core2", - "unsigned-varint 0.7.2", + "unsigned-varint", ] [[package]] name = "multimap" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -3595,7 +5046,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework 2.11.0", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -3612,7 +5063,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "fsevent-sys", "inotify", "kqueue", @@ -3674,18 +5125,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.5.2", "libc", ] +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "nybbles" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4b5ecbd0beec843101bffe848217f770e8b8da81d8355b7d6e226f2199b3dc" +dependencies = [ + "alloy-rlp", + "arbitrary", + "cfg-if 1.0.4", + "proptest", + "ruint", + "serde", + "smallvec", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "object" version = "0.36.7" @@ -3693,8 +5190,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", - "hashbrown 0.15.2", - "indexmap 2.11.4", + "hashbrown 0.15.5", + "indexmap 2.12.1", "memchr", ] @@ -3710,11 +5207,11 @@ dependencies = [ "chrono", "form_urlencoded", "futures 0.3.31", - "http 1.3.1", + "http 1.4.0", "http-body-util", "humantime", - "hyper 1.7.0", - "itertools", + "hyper 1.8.1", + "itertools 0.14.0", "parking_lot", "percent-encoding", "quick-xml", @@ -3736,9 +5233,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "opaque-debug" @@ -3748,12 +5251,12 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.0", - "cfg-if 1.0.0", + "bitflags 2.10.0", + "cfg-if 1.0.4", "foreign-types", "libc", "once_cell", @@ -3769,29 +5272,29 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.0+3.5.0" +version = "300.5.4+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -3812,33 +5315,35 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" dependencies = [ - "unicode-width 0.1.13", + "unicode-width 0.1.14", ] [[package]] name = "parity-scale-codec" -version = "3.6.12" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "bitvec", "byte-slice-cast", + "const_format", "impl-trait-for-tuples", "parity-scale-codec-derive", + "rustversion", "serde", ] [[package]] name = "parity-scale-codec-derive" -version = "3.6.12" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.111", ] [[package]] @@ -3863,13 +5368,19 @@ version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "libc", - "redox_syscall 0.5.2", + "redox_syscall 0.5.18", "smallvec", - "windows-link 0.2.0", + "windows-link", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -3878,20 +5389,19 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.7.11" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" dependencies = [ "memchr", - "thiserror 1.0.61", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" dependencies = [ "pest", "pest_generator", @@ -3899,24 +5409,23 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -3928,7 +5437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.11.4", + "indexmap 2.12.1", ] [[package]] @@ -3938,8 +5447,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", - "hashbrown 0.15.2", - "indexmap 2.11.4", + "hashbrown 0.15.5", + "indexmap 2.12.1", "serde", ] @@ -3954,6 +5463,16 @@ dependencies = [ "url", ] +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures 0.3.31", + "rustc_version 0.4.1", +] + [[package]] name = "phf" version = "0.13.1" @@ -3975,29 +5494,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -4005,17 +5524,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd" [[package]] name = "portable-atomic-util" @@ -4028,9 +5557,9 @@ dependencies = [ [[package]] name = "postcard" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" dependencies = [ "cobs", "embedded-io 0.4.0", @@ -4040,9 +5569,9 @@ dependencies = [ [[package]] name = "postgres" -version = "0.19.7" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7915b33ed60abc46040cbcaa25ffa1c7ec240668e0477c4f3070786f5916d451" +checksum = "e7c48ece1c6cda0db61b058c1721378da76855140e9214339fa1317decacb176" dependencies = [ "bytes", "fallible-iterator 0.2.0", @@ -4093,6 +5622,15 @@ dependencies = [ "postgres-protocol", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -4101,15 +5639,18 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] name = "pq-src" -version = "0.3.9+libpq-17.5" +version = "0.3.10+libpq-18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ee82a51d19317d15e43b82e496db215ad5bf09a245786e7ac75cb859e5ba46" +checksum = "56ef39ce621f4993d6084fdcd4cbf1e01c84bdba53109cfad095d2cf441b85b9" dependencies = [ "cc", "openssl-sys", @@ -4139,12 +5680,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -4153,13 +5694,24 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" dependencies = [ - "fixed-hash", + "fixed-hash 0.7.0", "impl-codec", "impl-rlp", "impl-serde", "uint 0.9.5", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash 0.8.0", + "impl-codec", + "uint 0.9.5", +] + [[package]] name = "priority-queue" version = "2.7.0" @@ -4167,17 +5719,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" dependencies = [ "equivalent", - "indexmap 2.11.4", + "indexmap 2.12.1", "serde", ] [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.10+spec-1.0.0", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ - "toml_edit 0.21.1", + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] @@ -4193,9 +5767,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -4206,7 +5780,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ca5326d8d0b950a9acd87e6a3f94745394f62e4dae1b1ee22b2bc0c394af43a" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "fnv", "lazy_static", "libc", @@ -4217,6 +5791,47 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "proptest" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.10.0", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "proptest-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "proptest-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "prost" version = "0.13.5" @@ -4234,7 +5849,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck 0.5.0", - "itertools", + "itertools 0.14.0", "log", "multimap", "once_cell", @@ -4243,7 +5858,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.106", + "syn 2.0.111", "tempfile", ] @@ -4254,10 +5869,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -4277,7 +5892,7 @@ checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" dependencies = [ "once_cell", "protobuf-support", - "thiserror 1.0.61", + "thiserror 1.0.69", ] [[package]] @@ -4287,12 +5902,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" dependencies = [ "anyhow", - "indexmap 2.11.4", + "indexmap 2.12.1", "log", "protobuf", "protobuf-support", "tempfile", - "thiserror 1.0.61", + "thiserror 1.0.69", "which", ] @@ -4302,15 +5917,16 @@ version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" dependencies = [ - "thiserror 1.0.61", + "thiserror 1.0.69", ] [[package]] name = "psm" -version = "0.1.21" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" dependencies = [ + "ar_archive_writer", "cc", ] @@ -4325,11 +5941,17 @@ dependencies = [ "wasmtime-math", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quick-xml" -version = "0.38.2" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d200a41a7797e6461bd04e4e95c3347053a731c32c87f066f2f0dda22dbdbba8" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", "serde", @@ -4337,60 +5959,74 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.2" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", + "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 1.1.0", + "rustc-hash 2.1.1", "rustls", - "thiserror 1.0.61", + "socket2 0.6.1", + "thiserror 2.0.17", "tokio", "tracing", + "web-time", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "rand 0.8.5", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", "ring", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "rustls", + "rustls-pki-types", "slab", - "thiserror 1.0.61", + "thiserror 2.0.17", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.2" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ + "cfg_aliases", "libc", "once_cell", - "socket2 0.5.7", + "socket2 0.6.1", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r2d2" version = "0.8.10" @@ -4417,6 +6053,7 @@ dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", + "serde", ] [[package]] @@ -4427,6 +6064,7 @@ checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", + "serde", ] [[package]] @@ -4455,7 +6093,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -4464,14 +6102,33 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.4", + "serde", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + +[[package]] +name = "rapidhash" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2988730ee014541157f48ce4dcc603940e00915edc3c7f9a8d78092256bb2493" +dependencies = [ + "rustversion", ] [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -4479,9 +6136,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -4504,9 +6161,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.111", ] +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redis" version = "0.31.0" @@ -4516,7 +6179,7 @@ dependencies = [ "arc-swap", "backon", "bytes", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "combine", "futures-channel", "futures-util", @@ -4526,39 +6189,70 @@ dependencies = [ "pin-project-lite", "ryu", "sha1_smol", - "socket2 0.5.7", + "socket2 0.5.10", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.17", "url", ] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.10.0", ] [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", - "thiserror 1.0.61", + "thiserror 2.0.17", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] @@ -4569,17 +6263,17 @@ checksum = "5216b1837de2149f8bc8e6d5f88a9326b63b8c836ed58ce4a0a29ec736a59734" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown 0.15.2", + "hashbrown 0.15.5", "log", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "smallvec", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -4589,9 +6283,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -4600,15 +6294,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", @@ -4616,11 +6310,11 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.5", - "http 1.3.1", - "http-body 1.0.0", + "h2 0.4.12", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-rustls", "hyper-tls", "hyper-util", @@ -4633,16 +6327,16 @@ dependencies = [ "pin-project-lite", "quinn", "rustls", - "rustls-native-certs 0.8.1", + "rustls-native-certs", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper", "tokio", "tokio-native-tls", "tokio-rustls", - "tokio-util 0.7.11", + "tokio-util 0.7.17", "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tower-http", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4651,17 +6345,28 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", + "webpki-roots 1.0.4", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", ] [[package]] name = "ring" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if 1.0.0", - "getrandom 0.2.15", + "cfg-if 1.0.4", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -4677,11 +6382,46 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "ruint" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" +dependencies = [ + "alloy-rlp", + "arbitrary", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types 0.12.2", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "rlp", + "ruint-macro", + "serde_core", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -4691,9 +6431,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc-hex" @@ -4703,44 +6443,53 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.27", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "errno", "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", ] [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.10" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "log", "once_cell", @@ -4753,50 +6502,40 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "rustls-pki-types", - "schannel", - "security-framework 2.11.0", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.2.0", + "security-framework 3.5.1", ] [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +dependencies = [ + "web-time", + "zeroize", +] [[package]] name = "rustls-webpki" -version = "0.102.5" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -4805,15 +6544,27 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rusty-fork" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" [[package]] name = "same-file" @@ -4826,11 +6577,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4842,6 +6593,30 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scoped-futures" version = "0.1.4" @@ -4857,13 +6632,40 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "serdect", + "subtle", + "zeroize", +] + [[package]] name = "secp256k1" version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c42e6f1735c5f00f51e43e28d6634141f2bcad10931b2609ddd74a86d751260" dependencies = [ - "secp256k1-sys", + "secp256k1-sys 0.4.2", +] + +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "secp256k1-sys 0.10.1", + "serde", ] [[package]] @@ -4875,13 +6677,22 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -4890,12 +6701,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.2.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.10.0", + "bitflags 2.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -4903,14 +6714,23 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", ] +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.27" @@ -4921,11 +6741,26 @@ dependencies = [ "serde_core", ] +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "serde" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -4933,43 +6768,46 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4" dependencies = [ "itoa", - "ryu", + "memchr", "serde", + "serde_core", + "zmij", ] [[package]] name = "serde_path_to_error" -version = "0.1.16" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" dependencies = [ "itoa", "serde", + "serde_core", ] [[package]] @@ -4993,18 +6831,18 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] [[package]] name = "serde_spanned" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] @@ -5023,17 +6861,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.4", - "serde", - "serde_derive", + "indexmap 2.12.1", + "schemars 0.9.0", + "schemars 1.1.0", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -5041,14 +6880,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ - "darling 0.20.10", + "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5057,13 +6896,23 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.1", "itoa", "ryu", "serde", "unsafe-libyaml", ] +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -5071,7 +6920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -5083,7 +6932,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "cpufeatures", "digest 0.10.7", ] @@ -5100,7 +6949,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "cpufeatures", "digest 0.10.7", ] @@ -5115,6 +6964,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if 1.0.4", +] + [[package]] name = "shellexpand" version = "3.1.1" @@ -5132,13 +6991,29 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "siphasher" version = "1.0.1" @@ -5147,12 +7022,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slog" @@ -5217,10 +7089,11 @@ dependencies = [ [[package]] name = "slog-term" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8" +checksum = "5cb1fc680b38eed6fad4c02b3871c09d2c81db8c96aa4e9c0a34904c830f09b5" dependencies = [ + "chrono", "is-terminal", "slog", "term", @@ -5230,18 +7103,19 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ + "arbitrary", "serde", ] [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -5249,12 +7123,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5278,6 +7152,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "sptr" version = "0.3.2" @@ -5303,7 +7187,7 @@ checksum = "da5fc6819faabb412da764b99d3b713bb55083c11e7e0c00144d386cd6a1939c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5322,23 +7206,23 @@ dependencies = [ [[package]] name = "stable-hash" version = "0.4.4" -source = "git+https://github.com/graphprotocol/stable-hash?branch=main#e50aabef55b8c4de581ca5c4ffa7ed8beed7e998" +source = "git+https://github.com/graphprotocol/stable-hash?branch=main#083b67b949eff48e210c5753eb9c777a73958d9b" dependencies = [ - "blake3 0.3.8", + "blake3 1.8.2", "firestorm 0.5.1", "ibig", "lazy_static", "leb128", "num-traits", - "uint 0.8.5", + "uint 0.10.0", "xxhash-rust", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" @@ -5347,7 +7231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" dependencies = [ "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "libc", "psm", "windows-sys 0.59.0", @@ -5391,6 +7275,15 @@ dependencies = [ "strum_macros 0.26.4", ] +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", +] + [[package]] name = "strum_macros" version = "0.26.4" @@ -5401,7 +7294,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5413,7 +7306,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5436,7 +7329,7 @@ dependencies = [ "prost-build", "prost-types", "substreams-macro", - "thiserror 1.0.61", + "thiserror 1.0.69", ] [[package]] @@ -5457,14 +7350,14 @@ version = "0.36.0" [[package]] name = "substreams-macro" -version = "0.6.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36f36e9da94db29f49daf3ab6b47b529b57c43fc5d58bc35b160aaad1a7233f" +checksum = "21b4fdca34da8c0250425bd798bd843f95b12dea527eeb3ba571e0b0af93d1b7" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "thiserror 1.0.61", + "thiserror 1.0.69", ] [[package]] @@ -5474,7 +7367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01ef8a763c5a5604b16f4898ab75d39494ef785c457aaca1fd7761b299f40fbf" dependencies = [ "bs58 0.4.0", - "getrandom 0.2.15", + "getrandom 0.2.16", "hex", "prost", "prost-build", @@ -5513,9 +7406,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -5523,29 +7416,35 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "0.1.2" +name = "syn-solidity" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "5f92d01b5de07eaf324f7fca61cc6bd3d82bbc1de5b6c963e6fe79e86f36580d" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.111", +] [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5554,7 +7453,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5583,31 +7482,30 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +checksum = "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ - "cfg-if 1.0.0", "fastrand", - "rustix 0.38.34", - "windows-sys 0.52.0", + "getrandom 0.3.4", + "once_cell", + "rustix 1.1.3", + "windows-sys 0.61.2", ] [[package]] name = "term" -version = "0.7.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "dirs-next", - "rustversion", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -5621,12 +7519,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix 0.38.34", - "windows-sys 0.48.0", + "rustix 1.1.3", + "windows-sys 0.60.2", ] [[package]] @@ -5642,20 +7540,21 @@ dependencies = [ "graph-node", "graph-store-postgres", "hex", - "hex-literal 1.0.0", + "hex-literal 1.1.0", "lazy_static", "pretty_assertions", "prost-types", + "serde_json", "tokio", ] [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.61", + "thiserror-impl 1.0.69", ] [[package]] @@ -5669,13 +7568,13 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5686,24 +7585,32 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "cfg-if 1.0.0", - "once_cell", + "cfg-if 1.0.4", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -5716,15 +7623,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -5750,9 +7657,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -5760,9 +7667,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6b6a2fb3a985e99cebfaefa9faa3024743da73304ca1c683a36429613d3d22" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -5775,33 +7682,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5816,11 +7720,10 @@ dependencies = [ [[package]] name = "tokio-openssl" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffab79df67727f6acf57f1ff743091873c24c579b1e2ce4d8f53e47ded4d63d" +checksum = "59df6849caa43bb7567f9a36f863c447d95a11d5903c9cc334ba32576a27eadd" dependencies = [ - "futures-util", "openssl", "openssl-sys", "tokio", @@ -5846,9 +7749,9 @@ dependencies = [ "postgres-protocol", "postgres-types", "rand 0.9.2", - "socket2 0.6.0", + "socket2 0.6.1", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.17", "whoami", ] @@ -5865,12 +7768,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] @@ -5883,7 +7785,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.17", ] [[package]] @@ -5899,6 +7801,22 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite 0.26.2", + "webpki-roots 0.26.11", +] + [[package]] name = "tokio-tungstenite" version = "0.28.0" @@ -5908,7 +7826,7 @@ dependencies = [ "futures-util", "log", "tokio", - "tungstenite", + "tungstenite 0.28.0", ] [[package]] @@ -5928,9 +7846,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -5942,87 +7860,95 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.15" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned 0.6.6", - "toml_datetime 0.6.6", - "toml_edit 0.22.16", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", ] [[package]] name = "toml" -version = "0.9.7" +version = "0.9.10+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.1", "serde_core", - "serde_spanned 1.0.2", - "toml_datetime 0.7.2", + "serde_spanned 1.0.4", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 0.7.13", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.11.4", - "toml_datetime 0.6.6", - "winnow 0.5.40", + "indexmap 2.12.1", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow", ] [[package]] name = "toml_edit" -version = "0.22.16" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.11.4", - "serde", - "serde_spanned 0.6.6", - "toml_datetime 0.6.6", - "winnow 0.6.13", + "indexmap 2.12.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow", ] [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ - "winnow 0.7.13", + "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "toml_writer" -version = "1.0.3" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tonic" @@ -6032,23 +7958,23 @@ checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", - "axum 0.7.5", + "axum 0.7.9", "base64 0.22.1", "bytes", "flate2", - "h2 0.4.5", - "http 1.3.1", - "http-body 1.0.0", + "h2 0.4.12", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", "prost", - "rustls-native-certs 0.8.1", + "rustls-native-certs", "rustls-pemfile", - "socket2 0.5.7", + "socket2 0.5.10", "tokio", "tokio-rustls", "tokio-stream", @@ -6069,7 +7995,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6086,7 +8012,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.17", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", @@ -6100,9 +8026,13 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", + "hdrhistogram", + "indexmap 2.12.1", "pin-project-lite", - "sync_wrapper 1.0.1", + "slab", + "sync_wrapper", "tokio", + "tokio-util 0.7.17", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", @@ -6111,17 +8041,17 @@ dependencies = [ [[package]] name = "tower" version = "0.5.2" -source = "git+https://github.com/tower-rs/tower.git#a1c277bc90839820bd8b4c0d8b47d14217977a79" +source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.11.4", + "indexmap 2.12.1", "pin-project-lite", "slab", - "sync_wrapper 1.0.1", + "sync_wrapper", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.17", "tower-layer 0.3.3 (git+https://github.com/tower-rs/tower.git)", "tower-service 0.3.3 (git+https://github.com/tower-rs/tower.git)", "tracing", @@ -6129,15 +8059,15 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "bytes", "futures-util", - "http 1.3.1", - "http-body 1.0.0", + "http 1.4.0", + "http-body 1.0.1", "iri-string", "pin-project-lite", "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6154,7 +8084,7 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-layer" version = "0.3.3" -source = "git+https://github.com/tower-rs/tower.git#a1c277bc90839820bd8b4c0d8b47d14217977a79" +source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" [[package]] name = "tower-service" @@ -6165,12 +8095,12 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tower-service" version = "0.3.3" -source = "git+https://github.com/tower-rs/tower.git#a1c277bc90839820bd8b4c0d8b47d14217977a79" +source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" [[package]] name = "tower-test" version = "0.4.1" -source = "git+https://github.com/tower-rs/tower.git#a1c277bc90839820bd8b4c0d8b47d14217977a79" +source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" dependencies = [ "pin-project-lite", "tokio", @@ -6181,9 +8111,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -6193,20 +8123,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] @@ -6229,7 +8159,7 @@ checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6245,6 +8175,25 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http 1.4.0", + "httparse", + "log", + "rand 0.9.2", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 2.0.17", + "utf-8", +] + [[package]] name = "tungstenite" version = "0.28.0" @@ -6253,7 +8202,7 @@ checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", - "http 1.3.1", + "http 1.4.0", "httparse", "log", "rand 0.9.2", @@ -6264,33 +8213,33 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" -version = "0.8.5" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db035e67dfaf7edd9aebfe8676afcd63eed53c8a4044fed514c8cccf1835177" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", "crunchy", - "rustc-hex", + "hex", "static_assertions", ] [[package]] name = "uint" -version = "0.9.5" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" dependencies = [ "byteorder", "crunchy", @@ -6298,41 +8247,44 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" [[package]] name = "unicode-segmentation" @@ -6342,21 +8294,21 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unsafe-libyaml" @@ -6364,12 +8316,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "unsigned-varint" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" - [[package]] name = "unsigned-varint" version = "0.8.0" @@ -6400,12 +8346,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -6420,9 +8360,19 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.15.1" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -6432,9 +8382,18 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] [[package]] name = "walkdir" @@ -6457,17 +8416,17 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.13.3+wasi-0.2.2" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -6478,47 +8437,35 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6526,22 +8473,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -6558,12 +8505,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.233.0" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9679ae3cf7cfa2ca3a327f7fab97f27f3294d402fd1a76ca8ab514e17973e4d3" +checksum = "c55db9c896d70bd9fa535ce83cd4e1f2ec3726b0edd2142079f594fc3be1cb35" dependencies = [ "leb128fmt", - "wasmparser 0.233.0", + "wasmparser 0.243.0", ] [[package]] @@ -6577,9 +8524,9 @@ dependencies = [ [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -6594,8 +8541,8 @@ version = "0.118.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77f1154f1ab868e2a01d9834a805faca7bf8b50d041b4ca714d005d0dab1c50c" dependencies = [ - "indexmap 2.11.4", - "semver", + "indexmap 2.12.1", + "semver 1.0.27", ] [[package]] @@ -6604,22 +8551,22 @@ version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc3b1f053f5d41aa55640a1fa9b6d1b8a9e4418d118ce308d20e24ff3575a8c" dependencies = [ - "bitflags 2.9.0", - "hashbrown 0.15.2", - "indexmap 2.11.4", - "semver", + "bitflags 2.10.0", + "hashbrown 0.15.5", + "indexmap 2.12.1", + "semver 1.0.27", "serde", ] [[package]] name = "wasmparser" -version = "0.233.0" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b51cb03afce7964bbfce46602d6cb358726f36430b6ba084ac6020d8ce5bc102" +checksum = "f6d8db401b0528ec316dfbe579e6ab4152d61739cfe076706d2009127970159d" dependencies = [ - "bitflags 2.9.0", - "indexmap 2.11.4", - "semver", + "bitflags 2.10.0", + "indexmap 2.12.1", + "semver 1.0.27", ] [[package]] @@ -6639,31 +8586,31 @@ version = "33.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57373e1d8699662fb791270ac5dfac9da5c14f618ecf940cdb29dc3ad9472a3c" dependencies = [ - "addr2line 0.24.2", + "addr2line", "anyhow", "async-trait", - "bitflags 2.9.0", + "bitflags 2.10.0", "bumpalo", "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "encoding_rs", "fxprof-processed-profile", - "gimli 0.31.1", - "hashbrown 0.15.2", - "indexmap 2.11.4", + "gimli", + "hashbrown 0.15.5", + "indexmap 2.12.1", "ittapi", "libc", "log", "mach2", "memfd", - "object", + "object 0.36.7", "once_cell", "postcard", "psm", "pulley-interpreter", "rayon", - "rustix 1.0.7", - "semver", + "rustix 1.1.3", + "semver 1.0.27", "serde", "serde_derive", "serde_json", @@ -6696,7 +8643,7 @@ version = "33.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0fc91372865167a695dc98d0d6771799a388a7541d3f34e939d0539d6583de" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", ] [[package]] @@ -6710,11 +8657,11 @@ dependencies = [ "directories-next", "log", "postcard", - "rustix 1.0.7", + "rustix 1.1.3", "serde", "serde_derive", "sha2", - "toml 0.8.15", + "toml 0.8.23", "windows-sys 0.59.0", "zstd", ] @@ -6728,7 +8675,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser", @@ -6747,16 +8694,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2bd72f0a6a0ffcc6a184ec86ac35c174e48ea0e97bbae277c8f15f8bf77a566" dependencies = [ "anyhow", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "cranelift-codegen", "cranelift-control", "cranelift-entity", "cranelift-frontend", "cranelift-native", - "gimli 0.31.1", - "itertools", + "gimli", + "itertools 0.14.0", "log", - "object", + "object 0.36.7", "pulley-interpreter", "smallvec", "target-lexicon", @@ -6776,13 +8723,13 @@ dependencies = [ "cpp_demangle", "cranelift-bitset", "cranelift-entity", - "gimli 0.31.1", - "indexmap 2.11.4", + "gimli", + "indexmap 2.12.1", "log", - "object", + "object 0.36.7", "postcard", "rustc-demangle", - "semver", + "semver 1.0.27", "serde", "serde_derive", "smallvec", @@ -6801,8 +8748,8 @@ checksum = "dc8965d2128c012329f390e24b8b2758dd93d01bf67e1a1a0dd3d8fd72f56873" dependencies = [ "anyhow", "cc", - "cfg-if 1.0.0", - "rustix 1.0.7", + "cfg-if 1.0.4", + "rustix 1.1.3", "wasmtime-asm-macros", "wasmtime-versioned-export-macros", "windows-sys 0.59.0", @@ -6815,8 +8762,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5882706a348c266b96dd81f560c1f993c790cf3a019857a9cde5f634191cfbb" dependencies = [ "cc", - "object", - "rustix 1.0.7", + "object 0.36.7", + "rustix 1.1.3", "wasmtime-versioned-export-macros", ] @@ -6827,7 +8774,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7af0e940cb062a45c0b3f01a926f77da5947149e99beb4e3dd9846d5b8f11619" dependencies = [ "anyhow", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "libc", "windows-sys 0.59.0", ] @@ -6855,7 +8802,7 @@ checksum = "d0963c1438357a3d8c0efe152b4ef5259846c1cf8b864340270744fe5b3bae5e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6866,8 +8813,8 @@ checksum = "cbc3b117d03d6eeabfa005a880c5c22c06503bb8820f3aa2e30f0e8d87b6752f" dependencies = [ "anyhow", "cranelift-codegen", - "gimli 0.31.1", - "object", + "gimli", + "object 0.36.7", "target-lexicon", "wasmparser 0.229.0", "wasmtime-cranelift", @@ -6883,37 +8830,51 @@ checksum = "1382f4f09390eab0d75d4994d0c3b0f6279f86a571807ec67a8253c87cf6a145" dependencies = [ "anyhow", "heck 0.5.0", - "indexmap 2.11.4", + "indexmap 2.12.1", "wit-parser", ] +[[package]] +name = "wasmtimer" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" +dependencies = [ + "futures 0.3.31", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "wast" -version = "233.0.0" +version = "243.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eaf4099d8d0c922b83bf3c90663f5666f0769db9e525184284ebbbdb1dd2180" +checksum = "df21d01c2d91e46cb7a221d79e58a2d210ea02020d57c092e79255cc2999ca7f" dependencies = [ "bumpalo", "leb128fmt", "memchr", - "unicode-width 0.2.0", - "wasm-encoder 0.233.0", + "unicode-width 0.2.2", + "wasm-encoder 0.243.0", ] [[package]] name = "wat" -version = "1.233.0" +version = "1.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d9bc80f5e4b25ea086ef41b91ccd244adde45d931c384d94a8ff64ab8bd7d87" +checksum = "226a9a91cd80a50449312fef0c75c23478fcecfcc4092bdebe1dc8e760ef521b" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -6934,10 +8895,10 @@ name = "web3" version = "0.19.0-graph" source = "git+https://github.com/graphprotocol/rust-web3?branch=graph-patches-onto-0.18#f9f27f45ce23bf489d8bd010b50b2b207eb316cb" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "base64 0.13.1", "bytes", - "derive_more 0.99.19", + "derive_more 0.99.20", "ethabi", "ethereum-types", "futures 0.3.31", @@ -6952,7 +8913,7 @@ dependencies = [ "pin-project", "reqwest", "rlp", - "secp256k1", + "secp256k1 0.21.3", "serde", "serde_json", "soketto", @@ -6971,11 +8932,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f6d8d1636b2627fe63518d5a9b38a569405d9c9bc665c43c9c341de57227ebb" dependencies = [ "native-tls", - "thiserror 1.0.61", + "thiserror 1.0.69", "tokio", "url", ] +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.4", +] + +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "4.4.2" @@ -6985,20 +8964,26 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.34", + "rustix 0.38.44", ] [[package]] name = "whoami" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" dependencies = [ - "redox_syscall 0.4.1", + "libredox", "wasite", "web-sys", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + [[package]] name = "winapi" version = "0.3.9" @@ -7017,11 +9002,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -7039,7 +9024,7 @@ dependencies = [ "anyhow", "cranelift-assembler-x64", "cranelift-codegen", - "gimli 0.31.1", + "gimli", "regalloc2", "smallvec", "target-lexicon", @@ -7051,61 +9036,72 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.52.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-link" -version = "0.1.3" +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.1.3", + "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-result" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-targets 0.48.5", + "windows-link", ] [[package]] @@ -7132,22 +9128,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-link", ] [[package]] @@ -7168,27 +9158,21 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.1.3", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -7197,15 +9181,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -7215,15 +9193,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -7233,9 +9205,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -7245,15 +9217,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -7263,15 +9229,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -7281,15 +9241,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -7299,15 +9253,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -7317,34 +9265,19 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.5.40" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" - [[package]] name = "wiremock" version = "0.6.5" @@ -7355,9 +9288,9 @@ dependencies = [ "base64 0.22.1", "deadpool", "futures 0.3.31", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "log", "once_cell", @@ -7369,13 +9302,10 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.33.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" -dependencies = [ - "bitflags 2.9.0", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "wit-parser" @@ -7385,9 +9315,9 @@ checksum = "459c6ba62bf511d6b5f2a845a2a736822e38059c1cfa0b644b467bbbfae4efa6" dependencies = [ "anyhow", "id-arena", - "indexmap 2.11.4", + "indexmap 2.12.1", "log", - "semver", + "semver 1.0.27", "serde", "serde_derive", "serde_json", @@ -7396,16 +9326,29 @@ dependencies = [ ] [[package]] -name = "write16" -version = "1.0.0" +name = "writeable" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] -name = "writeable" -version = "0.5.5" +name = "ws_stream_wasm" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" +dependencies = [ + "async_io_stream", + "futures 0.3.31", + "js-sys", + "log", + "pharos", + "rustc_version 0.4.1", + "send_wrapper", + "thiserror 2.0.17", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] [[package]] name = "wyz" @@ -7418,9 +9361,9 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.11" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63658493314859b4dfdf3fb8c1defd61587839def09582db50b8a4e93afca6bb" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" [[package]] name = "yansi" @@ -7430,11 +9373,10 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -7442,16 +9384,36 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "zerofrom" version = "0.1.6" @@ -7469,21 +9431,46 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -7492,15 +9479,21 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] +[[package]] +name = "zmij" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e404bcd8afdaf006e529269d3e85a743f9480c3cef60034d77860d02964f3ba" + [[package]] name = "zstd" version = "0.13.3" @@ -7521,9 +9514,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index f60d8e140a5..5b73941eef9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ repository = "https://github.com/graphprotocol/graph-node" license = "MIT OR Apache-2.0" [workspace.dependencies] +alloy = { version = "1.0.33", features = ["dyn-abi", "json-abi", "full", "arbitrary", "json-rpc"] } +alloy-rpc-types = "1.0.33" anyhow = "1.0" async-graphql = { version = "7.0.17", features = ["chrono"] } async-graphql-axum = "7.0.17" @@ -96,6 +98,7 @@ tokio-retry = "0.3.0" tonic = { version = "0.12.3", features = ["tls-roots", "gzip"] } tonic-build = { version = "0.12.3", features = ["prost"] } +tower = { version = "0.5.1", features = ["full"] } tower-http = { version = "0.6.6", features = ["cors"] } wasmparser = "0.118.1" wasmtime = { version = "33.0.2", features = ["async"] } diff --git a/chain/ethereum/Cargo.toml b/chain/ethereum/Cargo.toml index 1a395fa536e..86f6bcb7844 100644 --- a/chain/ethereum/Cargo.toml +++ b/chain/ethereum/Cargo.toml @@ -18,6 +18,7 @@ semver = "1.0.27" thiserror = { workspace = true } tokio = { workspace = true } tokio-stream = { workspace = true } +tower = { workspace = true } itertools = "0.14.0" diff --git a/chain/ethereum/build.rs b/chain/ethereum/build.rs index 227a50914a6..cb2257bc845 100644 --- a/chain/ethereum/build.rs +++ b/chain/ethereum/build.rs @@ -3,6 +3,7 @@ fn main() { tonic_build::configure() .out_dir("src/protobuf") + .protoc_arg("--experimental_allow_proto3_optional") .compile_protos(&["proto/ethereum.proto"], &["proto"]) .expect("Failed to compile Firehose Ethereum proto(s)"); } diff --git a/chain/ethereum/proto/ethereum.proto b/chain/ethereum/proto/ethereum.proto index 42adbd0ffa6..50c10f921f0 100644 --- a/chain/ethereum/proto/ethereum.proto +++ b/chain/ethereum/proto/ethereum.proto @@ -2,15 +2,62 @@ syntax = "proto3"; package sf.ethereum.type.v2; -option go_package = "github.com/streamingfast/sf-ethereum/types/pb/sf/ethereum/type/v2;pbeth"; +option go_package = "github.com/streamingfast/firehose-ethereum/types/pb/sf/ethereum/type/v2;pbeth"; import "google/protobuf/timestamp.proto"; +// Block is the representation of the tracing of a block in the Ethereum +// blockchain. A block is a collection of [TransactionTrace] that are grouped +// together and processed as an atomic unit. Each [TransactionTrace] is composed +// of a series of [Call] (a.k.a internal transactions) and there is also at +// least one call per transaction a.k.a the root call which essentially has the +// same parameters as the transaction itself (e.g. `from`, `to`, `gas`, `value`, +// etc.). +// +// The exact tracing method used to build the block must be checked against +// [DetailLevel] field. There is two levels of details available, `BASE` and +// `EXTENDED`. The `BASE` level has been extracted using archive node RPC calls +// and will contain only the block header, transaction receipts and event logs. +// Refers to the Firehose service provider to know which blocks are offered on +// each network. +// +// The `EXTENDED` level has been extracted using the Firehose tracer and all +// fields are available in this Protobuf. +// +// The Ethereum block model is used across many chains which means that it +// happen that certain fields are not available in one chain but are available +// in another. Each field should be documented when necesssary if it's available +// on a subset of chains. +// +// One major concept to get about the Block is the concept of 'ordinal'. The +// ordinal is a number that is used to globally order every element of execution +// that happened throughout the processing of the block like +// [TransactionTracer], [Call], [Log], [BalanceChange], [StateChange], etc. +// Element that have a start and end interval, [Transaction] and [Call], will +// have two ordinals: `begin_ordinal` and `end_ordinal`. Element that are +// executed as "point in time" [Log], [BalanceChange], [StateChange], etc. will +// have only one ordinal named `ordinal`. If you take all of the message in the +// Block that have an 'ordinal' field in an array and you sort each element +// against the `ordinal` field, you will get the exact order of execution of +// each element in the block. +// +// All the 'ordinal' fields in a block are globally unique for the given block, +// it is **not** a chain-wide global ordering. Furthermore, caution must be take +// with reverted elements due to execution failure. For anything attached to a +// [Call] that has a `state_reverted` field set to `true`, the `ordinal` field +// is not reliable and should not be used to order the element against other +// elements in the block as those element might have 0 as the ordinal. Only +// successful calls have a reliable `ordinal` field. message Block { - int32 ver = 1; + // Hash is the block's hash. bytes hash = 2; + // Number is the block's height at which this block was mined. uint64 number = 3; + // Size is the size in bytes of the RLP encoding of the block according to Ethereum + // rules. uint64 size = 4; + // Header contain's the block's header information like its parent hash, the merkel root hash + // and all other information the form a block. BlockHeader header = 5; // Uncles represents block produced with a valid solution but were not actually chosen @@ -20,46 +67,79 @@ message Block { // field will actually be always empty. repeated BlockHeader uncles = 6; + // TransactionTraces hold the execute trace of all the transactions that were executed + // in this block. In in there that you will find most of the Ethereum data model. + // + // They are ordered by the order of execution of the transaction in the block. repeated TransactionTrace transaction_traces = 10; + + // BalanceChanges here is the array of ETH transfer that happened at the block level + // outside of the normal transaction flow of a block. The best example of this is mining + // reward for the block mined, the transfer of ETH to the miner happens outside the normal + // transaction flow of the chain and is recorded as a `BalanceChange` here since we cannot + // attached it to any transaction. + // + // Only available in DetailLevel: EXTENDED repeated BalanceChange balance_changes = 11; - repeated CodeChange code_changes = 20; - reserved 40; // bool filtering_applied = 40 [deprecated = true]; - reserved 41; // string filtering_include_filter_expr = 41 [deprecated = true]; - reserved 42; // string filtering_exclude_filter_expr = 42 [deprecated = true]; -} + enum DetailLevel{ + DETAILLEVEL_EXTENDED = 0; + // DETAILLEVEL_TRACE = 1; // TBD + DETAILLEVEL_BASE = 2; + } -// HeaderOnlyBlock is used to optimally unpack the [Block] structure (note the -// corresponding message number for the `header` field) while consuming less -// memory, when only the `header` is desired. -// -// WARN: this is a client-side optimization pattern and should be moved in the -// consuming code. -message HeaderOnlyBlock { - BlockHeader header = 5; -} + // DetailLevel affects the data available in this block. + // + // ## DetailLevel_EXTENDED + // + // Describes the most complete block, with traces, balance changes, storage + // changes. It is extracted during the execution of the block. + // + // ## DetailLevel_BASE + // + // Describes a block that contains only the block header, transaction receipts + // and event logs: everything that can be extracted using the base JSON-RPC + // interface + // (https://ethereum.org/en/developers/docs/apis/json-rpc/#json-rpc-methods) + // Furthermore, the eth_getTransactionReceipt call has been avoided because it + // brings only minimal improvements at the cost of requiring an archive node + // or a full node with complete transaction index. + DetailLevel detail_level = 12; + + // CodeChanges here is the array of smart code change that happened that happened at the block level + // outside of the normal transaction flow of a block. Some Ethereum's fork like BSC and Polygon + // has some capabilities to upgrade internal smart contracts used usually to track the validator + // list. + // + // On hard fork, some procedure runs to upgrade the smart contract code to a new version. In those + // network, a `CodeChange` for each modified smart contract on upgrade would be present here. Note + // that this happen rarely, so the vast majority of block will have an empty list here. + // + // Only available in DetailLevel: EXTENDED + repeated CodeChange code_changes = 20; -// BlockWithRefs is a lightweight block, with traces and transactions -// purged from the `block` within, and only. It is used in transports -// to pass block data around. -message BlockWithRefs { - string id = 1; - Block block = 2; - TransactionRefs transaction_trace_refs = 3; - bool irreversible = 4; -} + // System calls are introduced in Cancun, along with blobs. They are executed outside of transactions but affect the state. + // + // Only available in DetailLevel: EXTENDED + repeated Call system_calls = 21; -message TransactionRefs { - repeated bytes hashes = 1; -} + // Withdrawals represents the list of validator balance withdrawals processed in this block. + // Introduced in the Shanghai hard fork (EIP-4895). + // + // This field has been added because Geth blocks include withdrawals after Shanghai fork, + // but our previous Firehose model didn't capture this data. Currently experimental - + // NOT ready for production use yet as we validate the tracing implementation. + // + // Only available when Shanghai fork is active on the chain. + repeated Withdrawal withdrawals = 22; -message UnclesHeaders { - repeated BlockHeader uncles = 1; -} + reserved 40; // bool filtering_applied = 40 [deprecated = true]; + reserved 41; // string filtering_include_filter_expr = 41 [deprecated = true]; + reserved 42; // string filtering_exclude_filter_expr = 42 [deprecated = true]; -message BlockRef { - bytes hash = 1; - uint64 number = 2; + // Ver represents that data model version of the block, it is used internally by Firehose on Ethereum + // as a validation that we are reading the correct version. + int32 ver = 1; } message BlockHeader { @@ -84,13 +164,10 @@ message BlockHeader { // consensus algorithm, this field will actually be constant and set to `0x00`. BigInt difficulty = 8; - // TotalDifficulty is the sum of all previous blocks difficulty including this block difficulty. + // TotalDifficulty used to be the sum of all previous blocks difficulty including this block difficulty. // - // If the Block containing this `BlockHeader` has been produced using the Proof of Stake - // consensus algorithm, this field will actually be constant and set to the terminal total difficulty - // that was required to transition to Proof of Stake algorithm, which varies per network. It is set to - // 58 750 000 000 000 000 000 000 on Ethereum Mainnet and to 10 790 000 on Ethereum Testnet Goerli. - BigInt total_difficulty = 17; + // It has been deprecated in geth v1.15.0 but was already removed from the JSON-RPC interface for a while + BigInt total_difficulty = 17 [deprecated = true]; uint64 number = 9; uint64 gas_limit = 10; @@ -134,19 +211,84 @@ message BlockHeader { // extra_data, // mix_hash, // nonce, - // base_fee_per_gas + // base_fee_per_gas (to be included only if London fork is active) + // withdrawals_root (to be included only if Shangai fork is active) + // blob_gas_used (to be included only if Cancun fork is active) + // excess_blob_gas (to be included only if Cancun fork is active) + // parent_beacon_root (to be included only if Cancun fork is active) + // requests_hash (to be included only if Prague fork is active) // ])) // bytes hash = 16; // Base fee per gas according to EIP-1559 (e.g. London Fork) rules, only set if London is present/active on the chain. BigInt base_fee_per_gas = 18; + + // Withdrawals root hash according to EIP-4895 (e.g. Shangai Fork) rules, only set if Shangai is present/active on the chain. + // + // Only available in DetailLevel: EXTENDED + bytes withdrawals_root = 19; + + // TxDependency is list of transaction indexes that are dependent on each other in the block + // header. This is metadata only that was used by the internal Polygon parallel execution engine. + // + // This field was available in a few versions on Polygon Mainnet and Polygon Mumbai chains. It was actually + // removed and is not populated anymore. It's now embedded in the `extraData` field, refer to Polygon source + // code to determine how to extract it if you need it. + // + // Only available in DetailLevel: EXTENDED + Uint64NestedArray tx_dependency = 20; + + // BlobGasUsed was added by EIP-4844 and is ignored in legacy headers. + optional uint64 blob_gas_used = 22; + + // ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers. + optional uint64 excess_blob_gas = 23; + + // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. + bytes parent_beacon_root = 24; + + // RequestsHash was added by EIP-7685 and is ignored in legacy headers. + bytes requests_hash = 25; +} + +message Uint64NestedArray { + repeated Uint64Array val = 1; +} + +message Uint64Array { + repeated uint64 val = 1; } message BigInt { bytes bytes = 1; } +// TransactionTrace is full trace of execution of the transaction when the +// it actually executed on chain. +// +// It contains all the transaction details like `from`, `to`, `gas`, etc. +// as well as all the internal calls that were made during the transaction. +// +// The `calls` vector contains Call objects which have balance changes, events +// storage changes, etc. +// +// If ordering is important between elements, almost each message like `Log`, +// `Call`, `StorageChange`, etc. have an ordinal field that is represents "execution" +// order of the said element against all other elements in this block. +// +// Due to how the call tree works doing "naively", looping through all calls then +// through a Call's element like `logs` while not yielding the elements in the order +// they were executed on chain. A log in call could have been done before or after +// another in another call depending on the actual call tree. +// +// The `calls` are ordered by creation order and the call tree can be re-computing +// using fields found in `Call` object (parent/child relationship). +// +// Another important thing to note is that even if a transaction succeed, some calls +// within it could have been reverted internally, if this is important to you, you must +// check the field `state_reverted` on the `Call` to determine if it was fully committed +// to the chain or not. message TransactionTrace { // consensus bytes to = 1; @@ -192,19 +334,47 @@ message TransactionTrace { // All transactions that ever existed prior Berlin fork before EIP-2718 was implemented. TRX_TYPE_LEGACY = 0; - // Field that specifies an access list of contract/storage_keys that is going to be used + // Transaction that specicy an access list of contract/storage_keys that is going to be used // in this transaction. // // Added in Berlin fork (EIP-2930). TRX_TYPE_ACCESS_LIST = 1; - // Transaction that specifies an access list just like TRX_TYPE_ACCESS_LIST but in addition defines the + // Transaction that specifis an access list just like TRX_TYPE_ACCESS_LIST but in addition defines the // max base gas gee and max priority gas fee to pay for this transaction. Transaction's of those type are // executed against EIP-1559 rules which dictates a dynamic gas cost based on the congestion of the network. TRX_TYPE_DYNAMIC_FEE = 2; + + // Transaction which contain a large amount of data that cannot be accessed by EVM execution, but whose commitment + // can be accessed. The format is intended to be fully compatible with the format that will be used in full sharding. + // + // Transaction that defines an access list just like TRX_TYPE_ACCESS_LIST and enables dynamic fee just like + // TRX_TYPE_DYNAMIC_FEE but in addition defines the fields 'max_fee_per_data_gas' of type 'uint256' and the fields + // 'blob_versioned_hashes' which represents a list of hash outputs from 'kzg_to_versioned_hash'. + // + // Activated in Cancun fork (EIP-4844) + TRX_TYPE_BLOB = 3; + + // Transaction that sets code to an EOA (Externally Owned Accounts) + // + // Activated in Prague (EIP-7702) + TRX_TYPE_SET_CODE = 4; + + // Arbitrum-specific transactions + TRX_TYPE_ARBITRUM_DEPOSIT = 100; + TRX_TYPE_ARBITRUM_UNSIGNED = 101; + TRX_TYPE_ARBITRUM_CONTRACT = 102; + TRX_TYPE_ARBITRUM_RETRY = 104; + TRX_TYPE_ARBITRUM_SUBMIT_RETRYABLE = 105; + TRX_TYPE_ARBITRUM_INTERNAL = 106; + TRX_TYPE_ARBITRUM_LEGACY = 120; + + // OPTIMISM-specific transactions + TRX_TYPE_OPTIMISM_DEPOSIT = 126; + } - // AcccessList represents the storage access this transaction has agreed to do in which case those storage + // AccessList represents the storage access this transaction has agreed to do in which case those storage // access cost less gas unit per access. // // This will is populated only if `TransactionTrace.Type == TRX_TYPE_ACCESS_LIST || TRX_TYPE_DYNAMIC_FEE` which @@ -215,6 +385,8 @@ message TransactionTrace { // // This will is populated only if `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE` which is possible only // if London fork is active on the chain. + // + // Only available in DetailLevel: EXTENDED BigInt max_fee_per_gas = 11; // MaxPriorityFeePerGas is priority fee per gas the user to pay in extra to the miner on top of the block's @@ -222,22 +394,106 @@ message TransactionTrace { // // This will is populated only if `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE` which is possible only // if London fork is active on the chain. + // + // Only available in DetailLevel: EXTENDED BigInt max_priority_fee_per_gas = 13; // meta uint32 index = 20; bytes hash = 21; bytes from = 22; + + // Only available in DetailLevel: EXTENDED + // Known Issues + // - Version 3: + // Field not populated. It will be empty. + // + // Fixed in `Version 4`, see https://docs.substreams.dev/reference-material/chains-and-endpoints/ethereum-data-model for information about block versions. bytes return_data = 23; + + // Only available in DetailLevel: EXTENDED bytes public_key = 24; + + // The block's global ordinal when the transaction started executing, refer to + // [Block] documentation for further information about ordinals and total ordering. uint64 begin_ordinal = 25; + + // The block's global ordinal when the transaction finished executing, refer to + // [Block] documentation for further information about ordinals and total ordering. uint64 end_ordinal = 26; + // TransactionTraceStatus is the status of the transaction execution and will let you know if the transaction + // was successful or not. + // + // ## Explanation relevant only for blocks with `DetailLevel: EXTENDED` + // + // A successful transaction has been recorded to the blockchain's state for calls in it that were successful. + // This means it's possible only a subset of the calls were properly recorded, refer to [calls[].state_reverted] field + // to determine which calls were reverted. + // + // A quirks of the Ethereum protocol is that a transaction `FAILED` or `REVERTED` still affects the blockchain's + // state for **some** of the state changes. Indeed, in those cases, the transactions fees are still paid to the miner + // which means there is a balance change for the transaction's emitter (e.g. `from`) to pay the gas fees, an optional + // balance change for gas refunded to the transaction's emitter (e.g. `from`) and a balance change for the miner who + // received the transaction fees. There is also a nonce change for the transaction's emitter (e.g. `from`). + // + // This means that to properly record the state changes for a transaction, you need to conditionally procees the + // transaction's status. + // + // For a `SUCCEEDED` transaction, you iterate over the `calls` array and record the state changes for each call for + // which `state_reverted == false` (if a transaction succeeded, the call at #0 will always `state_reverted == false` + // because it aligns with the transaction). + // + // For a `FAILED` or `REVERTED` transaction, you iterate over the root call (e.g. at #0, will always exist) for + // balance changes you process those where `reason` is either `REASON_GAS_BUY`, `REASON_GAS_REFUND` or + // `REASON_REWARD_TRANSACTION_FEE` and for nonce change, still on the root call, you pick the nonce change which the + // smallest ordinal (if more than one). TransactionTraceStatus status = 30; + TransactionReceipt receipt = 31; + + // Only available in DetailLevel: EXTENDED repeated Call calls = 32; -} + // BlobGas is the amount of gas the transaction is going to pay for the blobs, this is a computed value + // equivalent to `self.blob_gas_fee_cap * len(self.blob_hashes)` and provided in the model for convenience. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + optional uint64 blob_gas = 33; + + // BlobGasFeeCap is the maximum fee per data gas the user is willing to pay for the data gas used. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + optional BigInt blob_gas_fee_cap = 34; + + // BlobHashes field represents a list of hash outputs from 'kzg_to_versioned_hash' which + // essentially is a version byte + the sha256 hash of the blob commitment (e.g. + // `BLOB_COMMITMENT_VERSION_KZG + sha256(commitment)[1:]`. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + repeated bytes blob_hashes = 35; + + // SetCodeAuthorizations represents the authorizations of a transaction to set code to an EOA (Externally Owned Accounts) + // as defined in EIP-7702. The list will contain all the authorizations as they were specified in the + // transaction itself regardless of their validity. If you need to determined if a given authorization was + // correctly applied on chain's state, refer to [SetCodeAuthorization.discarded] field that records + // if the authorization was discarded or not by the chain due to invalidity. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-7702 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_SET_CODE` which is possible only + // if Prague fork is active on the chain. + repeated SetCodeAuthorization set_code_authorizations = 36; +} // AccessTuple represents a list of storage keys for a given contract's address and is used // for AccessList construction. @@ -246,10 +502,64 @@ message AccessTuple { repeated bytes storage_keys = 2; } -// TransactionTraceWithBlockRef -message TransactionTraceWithBlockRef { - TransactionTrace trace = 1; - BlockRef block_ref = 2; +// SetCodeAuthorization represents the authorization of a transaction to set code of an EOA (Externally Owned Account) +// as defined in EIP-7702. +// +// The 'authority' field is the address that is authorizing the delegation mechanism. The 'authority' value is computed +// from the signature contained in the message using the computation +// `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s)` +// where `MAGIC` is `0x5`, `||` is the bytes concatenation operator, `ecrecover` is the Ethereum signature recovery +// and `y_parity` is the recovery ID value denoted `v` in the message below. Checking the go-ethereum implementation +// at https://github.com/ethereum/go-ethereum/blob/v1.15.0/core/types/tx_setcode.go#L117 might prove easier to "read". +// +// We do extract the 'authority' value from the signature in the message and store it in the 'authority' field for +// convenience so you don't need to perform the computation yourself. +message SetCodeAuthorization { + // Discarded determines if this authorization was skipped due to being invalid. As EIP-7702 states, + // if the authorization is invalid (invalid signature, nonce mismatch, etc.) it must be simply + // discarded and the transaction is processed as if the authorization was not present in the + // authorization list. + // + // This boolean records if the authorization was discarded or not by the chain due to invalidity. + bool discarded = 1; + + // ChainID is the chain ID of the chain where the transaction was executed, used + // to recover the authority from the signature. + bytes chain_id = 2; + + // Address contains the address this account is delegating to. This address usually + // contain code that this account essentially "delegates" to. + // + // Note: This was missing when EIP-7702 was first activated on Holesky, Sepolia, BSC Chapel, + // BSC Mainnet and Arbitrum Sepolia but was ready for Ethereum Mainnet hard fork. We will backfill + // those missing values in the near future at which point we will remove this note. + bytes address = 8; + + // Nonce is the nonce of the account that is authorizing delegation mechanism, EIP-7702 rules + // states that nonce should be verified using this rule: + // + // - Verify the nonce of authority is equal to nonce. In case authority does not exist in the trie, + // verify that nonce is equal to 0. + // + // Read SetCodeAuthorization to know how to recover the `authority` value. + uint64 nonce = 3; + + // V is the recovery ID value for the signature Y point. While it's defined as a + // `uint32`, it's actually bounded by a `uint8` data type withing the Ethereum protocol. + uint32 v = 4; + + // R is the signature's X point on the elliptic curve (32 bytes). + bytes r = 5; + + // S is the signature's Y point on the elliptic curve (32 bytes). + bytes s = 6; + + // Authority is the address of the account that is authorizing delegation mechanism, it + // is computed from the signature contained in the message and stored for convenience. + // + // If the authority cannot be recovered from the signature, this field will be empty and + // the `discarded` field will be set to `true`. + optional bytes authority = 7; } enum TransactionTraceStatus { @@ -263,9 +573,7 @@ message TransactionReceipt { // State root is an intermediate state_root hash, computed in-between transactions to make // **sure** you could build a proof and point to state in the middle of a block. Geth client // uses `PostState + root + PostStateOrStatus`` while Parity used `status_code, root...`` this piles - // hardforks, see (read the EIPs first): - // - https://github.com/eoscanada/go-ethereum-private/blob/deep-mind/core/types/receipt.go#L147 - // - https://github.com/eoscanada/go-ethereum-private/blob/deep-mind/core/types/receipt.go#L50-L86 + // hard forks, see (read the EIPs first): // - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-658.md // // Moreover, the notion of `Outcome`` in parity, which segregates the two concepts, which are @@ -277,6 +585,23 @@ message TransactionReceipt { uint64 cumulative_gas_used = 2; bytes logs_bloom = 3; repeated Log logs = 4; + + // BlobGasUsed is the amount of blob gas that has been used within this transaction. At time + // of writing, this is equal to `self.blob_gas_fee_cap * len(self.blob_hashes)`. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + optional uint64 blob_gas_used = 5; + + // BlobGasPrice is the amount to pay per blob item in the transaction. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + optional BigInt blob_gas_price = 6; } message Log { @@ -285,8 +610,10 @@ message Log { bytes data = 3; // Index is the index of the log relative to the transaction. This index - // is always populated regardless of the state reversion of the call + // is always populated regardless of the state reversion of the the call // that emitted this log. + // + // Only available in DetailLevel: EXTENDED uint32 index = 4; // BlockIndex represents the index of the log relative to the Block. @@ -294,7 +621,7 @@ message Log { // An **important** notice is that this field will be 0 when the call // that emitted the log has been reverted by the chain. // - // Currently, there are two locations where a Log can be obtained: + // Currently, there is two locations where a Log can be obtained: // - block.transaction_traces[].receipt.logs[] // - block.transaction_traces[].calls[].logs[] // @@ -306,6 +633,8 @@ message Log { // the `blockIndex` value will always be 0. uint32 blockIndex = 6; + // The block's global ordinal when the log was recorded, refer to [Block] + // documentation for further information about ordinals and total ordering. uint64 ordinal = 7; } @@ -316,25 +645,76 @@ message Call { CallType call_type = 4; bytes caller = 5; bytes address = 6; + + // AddressDelegatesTo contains the address from which the actual code to execute will be loaded + // as defined per EIP-7702 rules. If the Call's address value resolves to a code + // that delegates to another address, this field will be populated with the address + // that the call is delegated to. It will be empty in all other situations. + // + // Assumes that a 'SetCode' transaction set address `0xA` to delegates to address `0xB`, + // then when a call is made to `0xA`, the Call object would have: + // + // - caller = + // - address = 0xA + // - address_delegates_to = 0xB + // + // Again, it's important to emphasize that this field relates to EIP-7702, if the call is + // a DELEGATE or CALLCODE type, this field will not be populated and will remain empty. + // + // It will be populated only if EIP-7702 is active on the chain (Prague fork) and if the + // 'address' of the call was pointing to another address at time of execution. + optional bytes address_delegates_to = 34; + BigInt value = 7; uint64 gas_limit = 8; uint64 gas_consumed = 9; bytes return_data = 13; + + // Known Issues + // - Version 3: + // When call is `CREATE` or `CREATE2`, this field is not populated. A couple of suggestions: + // 1. You can get the contract's code in the `code_changes` field. + // 2. In the root `CREATE` call, you can directly use the `TransactionTrace`'s input field. + // + // Fixed in `Version 4`, see https://docs.substreams.dev/reference-material/chains-and-endpoints/ethereum-data-model for information about block versions. bytes input = 14; + + // Indicates whether the call executed code. + // + // Known Issues + // - Version 3: + // This may be incorrectly set to `false` for accounts with code handling native value transfers, + // as well as for certain precompiles with no input. + // The value is initially set based on `call.type != CREATE && len(call.input) > 0` + // and later adjusted if the tracer detects an account without code. + // + // Fixed in `Version 4`, see https://docs.substreams.dev/reference-material/chains-and-endpoints/ethereum-data-model for information about block versions. bool executed_code = 15; bool suicide = 16; /* hex representation of the hash -> preimage */ map keccak_preimages = 20; + + // Known Issues + // - Version 3: + // The data might be not be in order. + // + // Fixed in `Version 4`, see https://docs.substreams.dev/reference-material/chains-and-endpoints/ethereum-data-model for information about block versions. repeated StorageChange storage_changes = 21; repeated BalanceChange balance_changes = 22; repeated NonceChange nonce_changes = 24; repeated Log logs = 25; repeated CodeChange code_changes = 26; - // Deprecated: repeated bytes created_accounts reserved 27; + // Known Issues + // - Version 3: + // Some gas changes are not correctly tracked: + // 1. Gas refunded due to data returned to the chain (occurs at the end of a transaction, before buyback). + // 2. Initial gas allocation (0 -> GasLimit) at the start of a call. + // 3. Final gas deduction (LeftOver -> 0) at the end of a call (if applicable). + // Fixed in `Version 4`, see https://docs.substreams.dev/reference-material/chains-and-endpoints/ethereum-data-model for information about block versions. repeated GasChange gas_changes = 28; // Deprecated: repeated GasEvent gas_events @@ -375,13 +755,44 @@ message Call { // ``` // // In the transaction above, while Call #2 and Call #3 would have the - // status `EXECUTED` + // status `EXECUTED`. + // + // If you check all calls and check only `state_reverted` flag, you might be missing + // some balance changes and nonce changes. This is because when a full transaction fails + // in ethereum (e.g. `calls.all(x.state_reverted == true)`), there is still the transaction + // fee that are recorded to the chain. + // + // Refer to [TransactionTrace#status] field for more details about the handling you must + // perform. bool state_reverted = 30; + // Known Issues + // - Version 3: + // 1. The block's global ordinal when the call started executing, refer to + // [Block] documentation for further information about ordinals and total ordering. + // 2. The transaction root call `begin_ordial` is always `0` (also in the GENESIS block), which can cause issues + // when sorting by this field. To ensure proper execution order, set it as follows: + // `trx.Calls[0].BeginOrdinal = trx.BeginOrdinal`. + // + // Fixed in `Version 4`, see https://docs.substreams.dev/reference-material/chains-and-endpoints/ethereum-data-model for information about block versions. uint64 begin_ordinal = 31; + + // Known Issues + // - Version 3: + // 1. The block's global ordinal when the call finished executing, refer to + // [Block] documentation for further information about ordinals and total ordering. + // 2. The root call of the GENESIS block is always `0`. To fix it, you can set it as follows: + // `rx.Calls[0].EndOrdinal = max.Uint64`. + // + // Fixed in `Version 4`, see https://docs.substreams.dev/reference-material/chains-and-endpoints/ethereum-data-model for information about block versions. uint64 end_ordinal = 32; - repeated AccountCreation account_creations = 33; + // Known Issues + // - Version 4: + // AccountCreations are NOT SUPPORTED anymore. DO NOT rely on them. + repeated AccountCreation account_creations = 33 [deprecated = true]; + + // The identifier 34 is taken by 'address_delegates_to' field above. reserved 50; // repeated ERC20BalanceChange erc20_balance_changes = 50 [deprecated = true]; reserved 51; // repeated ERC20TransferEvent erc20_transfer_events = 51 [deprecated = true]; @@ -403,20 +814,42 @@ message StorageChange { bytes old_value = 3; bytes new_value = 4; + // The block's global ordinal when the storage change was recorded, refer to [Block] + // documentation for further information about ordinals and total ordering. uint64 ordinal = 5; } message BalanceChange { + // Address is the address of the account that has changed balance. bytes address = 1; + + // OldValue is the balance of the address before the change. This value + // can be **nil/null/None** if there was no previous balance for the address. + // It is safe in those case(s) to consider the balance as being 0. + // + // If you consume this from a Substreams, you can safely use: + // + // ```ignore + // let old_value = old_value.unwrap_or_default(); + // ``` BigInt old_value = 2; - BigInt new_value = 3; - Reason reason = 4; - // Obtain all balance change reasons under deep mind repository: + // NewValue is the balance of the address after the change. This value + // can be **nil/null/None** if there was no previous balance for the address + // after the change. It is safe in those case(s) to consider the balance as being + // 0. + // + // If you consume this from a Substreams, you can safely use: // - // ```shell - // ack -ho 'BalanceChangeReason\(".*"\)' | grep -Eo '".*"' | sort | uniq + // ```ignore + // let new_value = new_value.unwrap_or_default(); // ``` + BigInt new_value = 3; + + // Reason is the reason why the balance has changed. This is useful to determine + // why the balance has changed and what is the context of the change. + Reason reason = 4; + enum Reason { REASON_UNKNOWN = 0; REASON_REWARD_MINE_UNCLE = 1; @@ -435,8 +868,20 @@ message BalanceChange { REASON_CALL_BALANCE_OVERRIDE = 12; // Used on chain(s) where some Ether burning happens REASON_BURN = 15; + REASON_WITHDRAWAL = 16; + + // Rewards for Blob processing on BNB chain added in Tycho hard-fork, refers + // to BNB documentation to check the timestamp at which it was activated. + REASON_REWARD_BLOB_FEE = 17; + + // This reason is used only on Optimism chain. + REASON_INCREASE_MINT = 18; + // This reason is used only on Optimism chain. + REASON_REVERT = 19; } + // The block's global ordinal when the balance change was recorded, refer to [Block] + // documentation for further information about ordinals and total ordering. uint64 ordinal = 5; } @@ -444,11 +889,17 @@ message NonceChange { bytes address = 1; uint64 old_value = 2; uint64 new_value = 3; + + // The block's global ordinal when the nonce change was recorded, refer to [Block] + // documentation for further information about ordinals and total ordering. uint64 ordinal = 4; } message AccountCreation { bytes account = 1; + + // The block's global ordinal when the account creation was recorded, refer to [Block] + // documentation for further information about ordinals and total ordering. uint64 ordinal = 2; } @@ -459,6 +910,8 @@ message CodeChange { bytes new_hash = 4; bytes new_code = 5; + // The block's global ordinal when the code change was recorded, refer to [Block] + // documentation for further information about ordinals and total ordering. uint64 ordinal = 6; } @@ -466,43 +919,155 @@ message CodeChange { // The gas is computed per actual op codes. Doing them completely might prove // overwhelming in most cases. // -// Hence, we only index some of them, those that are costly like all the calls +// Hence, we only index some of them, those that are costy like all the calls // one, log events, return data, etc. message GasChange { uint64 old_value = 1; uint64 new_value = 2; Reason reason = 3; - // Obtain all gas change reasons under deep mind repository: - // - // ```shell - // ack -ho 'GasChangeReason\(".*"\)' | grep -Eo '".*"' | sort | uniq - // ``` enum Reason { REASON_UNKNOWN = 0; + // REASON_CALL is the amount of gas that will be charged for a 'CALL' opcode executed by the EVM REASON_CALL = 1; + // REASON_CALL_CODE is the amount of gas that will be charged for a 'CALLCODE' opcode executed by the EVM REASON_CALL_CODE = 2; + // REASON_CALL_DATA_COPY is the amount of gas that will be charged for a 'CALLDATACOPY' opcode executed by the EVM REASON_CALL_DATA_COPY = 3; + // REASON_CODE_COPY is the amount of gas that will be charged for a 'CALLDATACOPY' opcode executed by the EVM REASON_CODE_COPY = 4; + // REASON_CODE_STORAGE is the amount of gas that will be charged for code storage REASON_CODE_STORAGE = 5; + // REASON_CONTRACT_CREATION is the amount of gas that will be charged for a 'CREATE' opcode executed by the EVM and for the gas + // burned for a CREATE, today controlled by EIP150 rules REASON_CONTRACT_CREATION = 6; + // REASON_CONTRACT_CREATION2 is the amount of gas that will be charged for a 'CREATE2' opcode executed by the EVM and for the gas + // burned for a CREATE2, today controlled by EIP150 rules REASON_CONTRACT_CREATION2 = 7; + // REASON_DELEGATE_CALL is the amount of gas that will be charged for a 'DELEGATECALL' opcode executed by the EVM REASON_DELEGATE_CALL = 8; + // REASON_EVENT_LOG is the amount of gas that will be charged for a 'LOG' opcode executed by the EVM REASON_EVENT_LOG = 9; + // REASON_EXT_CODE_COPY is the amount of gas that will be charged for a 'LOG' opcode executed by the EVM REASON_EXT_CODE_COPY = 10; + // REASON_FAILED_EXECUTION is the burning of the remaining gas when the execution failed without a revert REASON_FAILED_EXECUTION = 11; + // REASON_INTRINSIC_GAS is the amount of gas that will be charged for the intrinsic cost of the transaction, there is + // always exactly one of those per transaction REASON_INTRINSIC_GAS = 12; + // GasChangePrecompiledContract is the amount of gas that will be charged for a precompiled contract execution REASON_PRECOMPILED_CONTRACT = 13; + // REASON_REFUND_AFTER_EXECUTION is the amount of gas that will be refunded to the caller after the execution of the call, + // if there is left over at the end of execution REASON_REFUND_AFTER_EXECUTION = 14; + // REASON_RETURN is the amount of gas that will be charged for a 'RETURN' opcode executed by the EVM REASON_RETURN = 15; + // REASON_RETURN_DATA_COPY is the amount of gas that will be charged for a 'RETURNDATACOPY' opcode executed by the EVM REASON_RETURN_DATA_COPY = 16; + // REASON_REVERT is the amount of gas that will be charged for a 'REVERT' opcode executed by the EVM REASON_REVERT = 17; + // REASON_SELF_DESTRUCT is the amount of gas that will be charged for a 'SELFDESTRUCT' opcode executed by the EVM REASON_SELF_DESTRUCT = 18; + // REASON_STATIC_CALL is the amount of gas that will be charged for a 'STATICALL' opcode executed by the EVM REASON_STATIC_CALL = 19; + // REASON_STATE_COLD_ACCESS is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules + // // Added in Berlin fork (Geth 1.10+) REASON_STATE_COLD_ACCESS = 20; + + // REASON_TX_INITIAL_BALANCE is the initial balance for the call which will be equal to the gasLimit of the call + // + // Added as new tracing reason in Geth, available only on some chains + REASON_TX_INITIAL_BALANCE = 21; + // REASON_TX_REFUNDS is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared) + // this generates an increase in gas. There is only one such gas change per transaction. + // + // Added as new tracing reason in Geth, available only on some chains + REASON_TX_REFUNDS = 22; + // REASON_TX_LEFT_OVER_RETURNED is the amount of gas left over at the end of transaction's execution that will be returned + // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas + // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. + // There is at most one of such gas change per transaction. + // + // Added as new tracing reason in Geth, available only on some chains + REASON_TX_LEFT_OVER_RETURNED = 23; + + // REASON_CALL_INITIAL_BALANCE is the initial balance for the call which will be equal to the gasLimit of the call. There is only + // one such gas change per call. + // + // Added as new tracing reason in Geth, available only on some chains + REASON_CALL_INITIAL_BALANCE = 24; + // REASON_CALL_LEFT_OVER_RETURNED is the amount of gas left over that will be returned to the caller, this change will always + // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even + // will be emitted. + REASON_CALL_LEFT_OVER_RETURNED = 25; + + // REASON_WITNESS_CONTRACT_INIT flags the event of adding to the witness during the contract creation initialization step. + REASON_WITNESS_CONTRACT_INIT = 26; + + // REASON_WITNESS_CONTRACT_CREATION flags the event of adding to the witness during the contract creation finalization step. + REASON_WITNESS_CONTRACT_CREATION = 27; + // REASON_WITNESS_CODE_CHUNK flags the event of adding one or more contract code chunks to the witness. + REASON_WITNESS_CODE_CHUNK = 28; + // REASON_WITNESS_CONTRACT_COLLISION_CHECK flags the event of adding to the witness when checking for contract address collision. + REASON_WITNESS_CONTRACT_COLLISION_CHECK = 29; + // REASON_TX_DATA_FLOOR is the amount of extra gas the transaction has to pay to reach the minimum gas requirement for the + // transaction data. This change will always be a negative change. + REASON_TX_DATA_FLOOR = 30; } + // The block's global ordinal when the gas change was recorded, refer to [Block] + // documentation for further information about ordinals and total ordering. uint64 ordinal = 4; } + +// HeaderOnlyBlock is used to optimally unpack the [Block] structure (note the +// corresponding message number for the `header` field) while consuming less +// memory, when only the `header` is desired. +// +// WARN: this is a client-side optimization pattern and should be moved in the +// consuming code. +message HeaderOnlyBlock { + BlockHeader header = 5; +} + +// BlockWithRefs is a lightweight block, with traces and transactions +// purged from the `block` within, and only. It is used in transports +// to pass block data around. +message BlockWithRefs { + string id = 1; + Block block = 2; + TransactionRefs transaction_trace_refs = 3; + bool irreversible = 4; +} + +message TransactionTraceWithBlockRef { + TransactionTrace trace = 1; + BlockRef block_ref = 2; +} + +message TransactionRefs { + repeated bytes hashes = 1; +} + +message BlockRef { + bytes hash = 1; + uint64 number = 2; +} + +// Withdrawal represents a validator withdrawal from the beacon chain to the EVM. +// Introduced in EIP-4895 (Shanghai hard fork). +message Withdrawal { + // Index is the monotonically increasing identifier of the withdrawal + uint64 index = 1; + + // ValidatorIndex is the index of the validator that is withdrawing + uint64 validator_index = 2; + + // Address is the Ethereum address receiving the withdrawn funds + bytes address = 3; + + // Amount is the value of the withdrawal in gwei (1 gwei = 1e9 wei) + uint64 amount = 4; +} \ No newline at end of file diff --git a/chain/ethereum/src/adapter.rs b/chain/ethereum/src/adapter.rs index efadb95c089..b9dca9c434c 100644 --- a/chain/ethereum/src/adapter.rs +++ b/chain/ethereum/src/adapter.rs @@ -1,16 +1,17 @@ use anyhow::Error; use async_trait::async_trait; -use ethabi::{Error as ABIError, ParamType, Token}; +use graph::abi; use graph::blockchain::ChainIdentifier; +use graph::components::ethereum::AnyBlock; use graph::components::subgraph::MappingError; use graph::data::store::ethereum::call; use graph::data_source::common::ContractCall; use graph::firehose::CallToFilter; use graph::firehose::CombinedFilter; use graph::firehose::LogFilter; -use graph::prelude::web3::types::Bytes; -use graph::prelude::web3::types::H160; -use graph::prelude::web3::types::U256; +use graph::prelude::alloy::primitives::{Address, B256}; +use graph::prelude::alloy::rpc::types::Log; +use graph::prelude::alloy::transports::{RpcError, TransportErrorKind}; use itertools::Itertools; use prost::Message; use prost_types::Any; @@ -19,7 +20,6 @@ use std::collections::{HashMap, HashSet}; use std::fmt; use thiserror::Error; use tiny_keccak::keccak256; -use web3::types::{Address, Log, H256}; use graph::prelude::*; use graph::{ @@ -28,6 +28,8 @@ use graph::{ petgraph::{self, graphmap::GraphMap}, }; +use graph::blockchain::BlockPtr; + const COMBINED_FILTER_TYPE_URL: &str = "type.googleapis.com/sf.ethereum.transform.v1.CombinedFilter"; @@ -35,7 +37,7 @@ use crate::capabilities::NodeCapabilities; use crate::data_source::{BlockHandlerFilter, DataSource}; use crate::{Chain, Mapping, ENV_VARS}; -pub type EventSignature = H256; +pub type EventSignature = B256; pub type FunctionSelector = [u8; 4]; /// `EventSignatureWithTopics` is used to match events with @@ -44,19 +46,19 @@ pub type FunctionSelector = [u8; 4]; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct EventSignatureWithTopics { pub address: Option
, - pub signature: H256, - pub topic1: Option>, - pub topic2: Option>, - pub topic3: Option>, + pub signature: B256, + pub topic1: Option>, + pub topic2: Option>, + pub topic3: Option>, } impl EventSignatureWithTopics { pub fn new( address: Option
, - signature: H256, - topic1: Option>, - topic2: Option>, - topic3: Option>, + signature: B256, + topic1: Option>, + topic2: Option>, + topic3: Option>, ) -> Self { EventSignatureWithTopics { address, @@ -71,7 +73,7 @@ impl EventSignatureWithTopics { /// If self.address is None, it's considered a wildcard match. /// Otherwise, it must match the provided address. /// It must also match the topics if they are Some - pub fn matches(&self, address: Option<&H160>, sig: H256, topics: &Vec) -> bool { + pub fn matches(&self, address: Option<&Address>, sig: B256, topics: &[B256]) -> bool { // If self.address is None, it's considered a wildcard match. Otherwise, it must match the provided address. let address_matches = match self.address { Some(ref self_addr) => address == Some(self_addr), @@ -95,22 +97,21 @@ impl EventSignatureWithTopics { #[derive(Error, Debug)] pub enum EthereumRpcError { #[error("call error: {0}")] - Web3Error(web3::Error), + AlloyError(RpcError), #[error("ethereum node took too long to perform call")] Timeout, } #[derive(Error, Debug)] pub enum ContractCallError { - #[error("ABI error: {0}")] - ABIError(#[from] ABIError), - /// `Token` is not of expected `ParamType` - #[error("type mismatch, token {0:?} is not of kind {1:?}")] - TypeError(Token, ParamType), - #[error("error encoding input call data: {0}")] - EncodingError(ethabi::Error), + #[error("ABI error: {0:#}")] + ABIError(anyhow::Error), + #[error("type mismatch, decoded value {0:?} is not of kind {1:?}")] + TypeError(abi::DynSolValue, abi::DynSolType), + #[error("error encoding input call data: {0:#}")] + EncodingError(anyhow::Error), #[error("call error: {0}")] - Web3Error(web3::Error), + AlloyError(RpcError), #[error("ethereum node took too long to perform call")] Timeout, #[error("internal error: {0}")] @@ -123,7 +124,7 @@ impl From for MappingError { // Any error reported by the Ethereum node could be due to the block no longer being on // the main chain. This is very unespecific but we don't want to risk failing a // subgraph due to a transient error such as a reorg. - ContractCallError::Web3Error(e) => MappingError::PossibleReorg(anyhow::anyhow!( + ContractCallError::AlloyError(e) => MappingError::PossibleReorg(anyhow::anyhow!( "Ethereum node returned an error for an eth_call: {e}" )), // Also retry on timeouts. @@ -145,13 +146,34 @@ enum LogFilterNode { #[derive(Clone, Debug)] pub struct EthGetLogsFilter { pub contracts: Vec
, - pub event_signatures: Vec, - pub topic1: Option>, - pub topic2: Option>, - pub topic3: Option>, + pub event_signatures: Vec, + pub topic1: Option>, + pub topic2: Option>, + pub topic3: Option>, } impl EthGetLogsFilter { + /// Convert to alloy Filter for the given block range + pub fn to_alloy_filter(&self, from: BlockNumber, to: BlockNumber) -> alloy::rpc::types::Filter { + let mut filter_builder = alloy::rpc::types::Filter::new() + .from_block(alloy::rpc::types::BlockNumberOrTag::Number(from as u64)) + .to_block(alloy::rpc::types::BlockNumberOrTag::Number(to as u64)) + .address(self.contracts.clone()) + .event_signature(self.event_signatures.clone()); + + if let Some(ref topic1) = self.topic1 { + filter_builder = filter_builder.topic1(topic1.clone()); + } + if let Some(ref topic2) = self.topic2 { + filter_builder = filter_builder.topic2(topic2.clone()); + } + if let Some(ref topic3) = self.topic3 { + filter_builder = filter_builder.topic3(topic3.clone()); + } + + filter_builder + } + fn from_contract(address: Address) -> Self { EthGetLogsFilter { contracts: vec![address], @@ -162,7 +184,7 @@ impl EthGetLogsFilter { } } - fn from_event(event: EventSignature) -> Self { + fn from_event(event: B256) -> Self { EthGetLogsFilter { contracts: vec![], event_signatures: vec![event], @@ -202,7 +224,7 @@ impl fmt::Display for EthGetLogsFilter { }; // Helper to format topics as strings - let format_topics = |topics: &Option>| -> String { + let format_topics = |topics: &Option>| -> String { topics.as_ref().map_or_else( || "None".to_string(), |ts| { @@ -348,11 +370,11 @@ impl From for Vec { }| LogFilter { addresses: contracts .iter() - .map(|addr| addr.to_fixed_bytes().to_vec()) + .map(|addr| addr.to_vec()) .collect_vec(), event_signatures: event_signatures .iter() - .map(|sig| sig.to_fixed_bytes().to_vec()) + .map(|sig| sig.to_vec()) .collect_vec(), }, ) @@ -364,14 +386,14 @@ impl EthereumLogFilter { /// Check if this filter matches the specified `Log`. pub fn matches(&self, log: &Log) -> bool { // First topic should be event sig - match log.topics.first() { + match log.topics().first() { None => false, Some(sig) => { // The `Log` matches the filter either if the filter contains // a (contract address, event signature) pair that matches the // `Log`, or if the filter contains wildcard event that matches. - let contract = LogFilterNode::Contract(log.address); + let contract = LogFilterNode::Contract(log.address()); let event = LogFilterNode::Event(*sig); self.contracts_and_events_graph .all_edges() @@ -380,7 +402,7 @@ impl EthereumLogFilter { || self .events_with_topic_filters .iter() - .any(|(e, _)| e.matches(Some(&log.address), *sig, &log.topics)) + .any(|(e, _)| e.matches(Some(&log.address()), *sig, &log.topics())) } } } @@ -388,9 +410,9 @@ impl EthereumLogFilter { /// Similar to [`matches`], checks if a transaction receipt is required for this log filter. pub fn requires_transaction_receipt( &self, - event_signature: &H256, + event_signature: &B256, contract_address: Option<&Address>, - topics: &Vec, + topics: &[B256], ) -> bool { // Check for wildcard events first. if self.wildcard_events.get(event_signature) == Some(&true) { @@ -628,7 +650,7 @@ impl Into> for EthereumCallFilter { let mut filters: Vec = contract_addresses_function_signatures .into_iter() .map(|(addr, (_, sigs))| CallToFilter { - addresses: vec![addr.to_fixed_bytes().to_vec()], + addresses: vec![addr.to_vec()], signatures: sigs.into_iter().map(|x| x.to_vec()).collect_vec(), }) .collect(); @@ -818,7 +840,7 @@ impl Into> for EthereumBlockFilter { .sorted() .dedup_by(|x, y| x == y) .map(|addr| CallToFilter { - addresses: vec![addr.to_fixed_bytes().to_vec()], + addresses: vec![addr.to_vec()], signatures: vec![], }) .collect_vec() @@ -1080,20 +1102,8 @@ pub trait EthereumAdapter: Send + Sync + 'static { /// connected to. async fn net_identifiers(&self) -> Result; - /// Get the latest block, including full transactions. - async fn latest_block(&self, logger: &Logger) -> Result; - /// Get the latest block, with only the header and transaction hashes. - async fn latest_block_header( - &self, - logger: &Logger, - ) -> Result, bc::IngestorError>; - - async fn load_block( - &self, - logger: &Logger, - block_hash: H256, - ) -> Result; + async fn latest_block_ptr(&self, logger: &Logger) -> Result; /// Load Ethereum blocks in bulk, returning results as they come back as a Stream. /// May use the `chain_store` as a cache. @@ -1101,44 +1111,29 @@ pub trait EthereumAdapter: Send + Sync + 'static { &self, logger: Logger, chain_store: Arc, - block_hashes: HashSet, + block_hashes: HashSet, ) -> Result>, Error>; /// Find a block by its hash. async fn block_by_hash( &self, logger: &Logger, - block_hash: H256, - ) -> Result, Error>; + block_hash: B256, + ) -> Result, Error>; async fn block_by_number( &self, logger: &Logger, block_number: BlockNumber, - ) -> Result, Error>; + ) -> Result, Error>; /// Load full information for the specified `block` (in particular, transaction receipts). async fn load_full_block( &self, logger: &Logger, - block: LightEthereumBlock, + block: AnyBlock, ) -> Result; - /// Find a block by its number, according to the Ethereum node. - /// - /// Careful: don't use this function without considering race conditions. - /// Chain reorgs could happen at any time, and could affect the answer received. - /// Generally, it is only safe to use this function with blocks that have received enough - /// confirmations to guarantee no further reorgs, **and** where the Ethereum node is aware of - /// those confirmations. - /// If the Ethereum node is far behind in processing blocks, even old blocks can be subject to - /// reorgs. - async fn block_hash_by_block_number( - &self, - logger: &Logger, - block_number: BlockNumber, - ) -> Result, Error>; - /// Finds the hash and number of the lowest non-null block with height greater than or equal to /// the given number. /// @@ -1158,7 +1153,7 @@ pub trait EthereumAdapter: Send + Sync + 'static { logger: &Logger, call: &ContractCall, cache: Arc, - ) -> Result<(Option>, call::Source), ContractCallError>; + ) -> Result<(Option>, call::Source), ContractCallError>; /// Make multiple contract calls in a single batch. The returned `Vec` /// has results in the same order as the calls in `calls` on input. The @@ -1168,22 +1163,22 @@ pub trait EthereumAdapter: Send + Sync + 'static { logger: &Logger, calls: &[&ContractCall], cache: Arc, - ) -> Result>, call::Source)>, ContractCallError>; + ) -> Result>, call::Source)>, ContractCallError>; async fn get_balance( &self, logger: &Logger, - address: H160, + address: Address, block_ptr: BlockPtr, - ) -> Result; + ) -> Result; // Returns the compiled bytecode of a smart contract async fn get_code( &self, logger: &Logger, - address: H160, + address: Address, block_ptr: BlockPtr, - ) -> Result; + ) -> Result; } #[cfg(test)] @@ -1197,9 +1192,7 @@ mod tests { use graph::blockchain::TriggerFilter as _; use graph::firehose::{CallToFilter, CombinedFilter, LogFilter, MultiLogFilter}; use graph::petgraph::graphmap::GraphMap; - use graph::prelude::ethabi::ethereum_types::H256; - use graph::prelude::web3::types::Address; - use graph::prelude::web3::types::Bytes; + use graph::prelude::alloy::primitives::{Address, Bytes, B256, U256}; use graph::prelude::EthereumCall; use hex::ToHex; use itertools::Itertools; @@ -1226,7 +1219,7 @@ mod tests { .map(|addr| { format!( "0x{}", - H256::from_str(addr) + B256::from_str(addr) .expect("unable to parse addr") .encode_hex::() ) @@ -1237,16 +1230,11 @@ mod tests { let sigs = event_sigs .iter() - .map(|addr| { - H256::from_str(addr) - .expect("unable to parse addr") - .to_fixed_bytes() - .to_vec() - }) + .map(|addr| B256::from_str(addr).expect("unable to parse addr").to_vec()) .collect_vec(); let filter = LogFilter { - addresses: vec![address.to_fixed_bytes().to_vec()], + addresses: vec![address.to_vec()], event_signatures: sigs, }; // This base64 was provided by Streamingfast as a binding example of the expected encoded for the @@ -1275,7 +1263,6 @@ mod tests { let filter = LogFilter { addresses: vec![Address::from_str(hex_addr) .expect("failed to parse address") - .to_fixed_bytes() .to_vec()], event_signatures: vec![fs.to_vec()], }; @@ -1290,8 +1277,7 @@ mod tests { #[test] fn ethereum_trigger_filter_to_firehose() { - let address = Address::from_low_u64_be; - let sig = H256::from_low_u64_le; + let sig = |value: u64| B256::from(U256::from(value)); let mut filter = TriggerFilter { log: EthereumLogFilter { contracts_and_events_graph: GraphMap::new(), @@ -1321,27 +1307,27 @@ mod tests { let expected_call_filters = vec![ CallToFilter { - addresses: vec![address(0).to_fixed_bytes().to_vec()], + addresses: vec![address(0).to_vec()], signatures: vec![[0u8; 4].to_vec()], }, CallToFilter { - addresses: vec![address(1).to_fixed_bytes().to_vec()], + addresses: vec![address(1).to_vec()], signatures: vec![[1u8; 4].to_vec()], }, CallToFilter { - addresses: vec![address(2).to_fixed_bytes().to_vec()], + addresses: vec![address(2).to_vec()], signatures: vec![], }, CallToFilter { - addresses: vec![address(1000).to_fixed_bytes().to_vec()], + addresses: vec![address(1000).to_vec()], signatures: vec![], }, CallToFilter { - addresses: vec![address(2000).to_fixed_bytes().to_vec()], + addresses: vec![address(2000).to_vec()], signatures: vec![], }, CallToFilter { - addresses: vec![address(3000).to_fixed_bytes().to_vec()], + addresses: vec![address(3000).to_vec()], signatures: vec![], }, ]; @@ -1364,15 +1350,12 @@ mod tests { let expected_log_filters = vec![ LogFilter { - addresses: vec![address(10).to_fixed_bytes().to_vec()], - event_signatures: vec![sig(101).to_fixed_bytes().to_vec()], + addresses: vec![address(10).to_vec()], + event_signatures: vec![sig(101).to_vec()], }, LogFilter { - addresses: vec![ - address(10).to_fixed_bytes().to_vec(), - address(20).to_fixed_bytes().to_vec(), - ], - event_signatures: vec![sig(100).to_fixed_bytes().to_vec()], + addresses: vec![address(10).to_vec(), address(20).to_vec()], + event_signatures: vec![sig(100).to_vec()], }, ]; @@ -1416,8 +1399,8 @@ mod tests { #[test] fn ethereum_trigger_filter_to_firehose_every_block_plus_logfilter() { - let address = Address::from_low_u64_be; - let sig = H256::from_low_u64_le; + let address = |value: u64| Address::left_padding_from(&value.to_le_bytes()); + let sig = |value: u64| B256::left_padding_from(&value.to_le_bytes()); let mut filter = TriggerFilter { log: EthereumLogFilter { contracts_and_events_graph: GraphMap::new(), @@ -1442,8 +1425,8 @@ mod tests { ); let expected_log_filters = vec![LogFilter { - addresses: vec![address(10).to_fixed_bytes().to_vec()], - event_signatures: vec![sig(101).to_fixed_bytes().to_vec()], + addresses: vec![address(10).to_vec()], + event_signatures: vec![sig(101).to_vec()], }]; let firehose_filter = filter.clone().to_firehose_filter(); @@ -1733,51 +1716,36 @@ mod tests { fn extending_ethereum_call_filter() { let mut base = EthereumCallFilter { contract_addresses_function_signatures: HashMap::from_iter(vec![ - ( - Address::from_low_u64_be(0), - (0, HashSet::from_iter(vec![[0u8; 4]])), - ), - ( - Address::from_low_u64_be(1), - (1, HashSet::from_iter(vec![[1u8; 4]])), - ), + (address(0), (0, HashSet::from_iter(vec![[0u8; 4]]))), + (address(1), (1, HashSet::from_iter(vec![[1u8; 4]]))), ]), wildcard_signatures: HashSet::new(), }; let extension = EthereumCallFilter { contract_addresses_function_signatures: HashMap::from_iter(vec![ - ( - Address::from_low_u64_be(0), - (2, HashSet::from_iter(vec![[2u8; 4]])), - ), - ( - Address::from_low_u64_be(3), - (3, HashSet::from_iter(vec![[3u8; 4]])), - ), + (address(0), (2, HashSet::from_iter(vec![[2u8; 4]]))), + (address(3), (3, HashSet::from_iter(vec![[3u8; 4]]))), ]), wildcard_signatures: HashSet::new(), }; base.extend(extension); assert_eq!( - base.contract_addresses_function_signatures - .get(&Address::from_low_u64_be(0)), + base.contract_addresses_function_signatures.get(&address(0)), Some(&(0, HashSet::from_iter(vec![[0u8; 4], [2u8; 4]]))) ); assert_eq!( - base.contract_addresses_function_signatures - .get(&Address::from_low_u64_be(3)), + base.contract_addresses_function_signatures.get(&address(3)), Some(&(3, HashSet::from_iter(vec![[3u8; 4]]))) ); assert_eq!( - base.contract_addresses_function_signatures - .get(&Address::from_low_u64_be(1)), + base.contract_addresses_function_signatures.get(&address(1)), Some(&(1, HashSet::from_iter(vec![[1u8; 4]]))) ); } - fn address(id: u64) -> Address { - Address::from_low_u64_be(id) + fn address(value: u64) -> Address { + Address::left_padding_from(&value.to_be_bytes()) } fn bytes(value: Vec) -> Bytes { @@ -1793,10 +1761,10 @@ fn complete_log_filter() { // Test a few combinations of complete graphs. for i in [1, 2] { - let events: BTreeSet<_> = (0..i).map(H256::from_low_u64_le).collect(); + let events: BTreeSet<_> = (0..i).map(|n| B256::from([n as u8; 32])).collect(); for j in [1, 1000, 2000, 3000] { - let contracts: BTreeSet<_> = (0..j).map(Address::from_low_u64_le).collect(); + let contracts: BTreeSet<_> = (0..j).map(|n| Address::from([n as u8; 20])).collect(); // Construct the complete bipartite graph with i events and j contracts. let mut contracts_and_events_graph = GraphMap::new(); @@ -1847,16 +1815,19 @@ fn complete_log_filter() { #[test] fn log_filter_require_transacion_receipt_method() { + let address = |value: u64| Address::left_padding_from(&value.to_be_bytes()); + let b256 = |value: u64| B256::left_padding_from(&value.to_be_bytes()); + // test data - let event_signature_a = H256::zero(); - let event_signature_b = H256::from_low_u64_be(1); - let event_signature_c = H256::from_low_u64_be(2); - let contract_a = Address::from_low_u64_be(3); - let contract_b = Address::from_low_u64_be(4); - let contract_c = Address::from_low_u64_be(5); - - let wildcard_event_with_receipt = H256::from_low_u64_be(6); - let wildcard_event_without_receipt = H256::from_low_u64_be(7); + let event_signature_a = b256(0); + let event_signature_b = b256(1); + let event_signature_c = b256(2); + let contract_a = address(3); + let contract_b = address(4); + let contract_c = address(5); + + let wildcard_event_with_receipt = b256(6); + let wildcard_event_without_receipt = b256(7); let wildcard_events = [ (wildcard_event_with_receipt, true), (wildcard_event_without_receipt, false), @@ -1866,8 +1837,8 @@ fn log_filter_require_transacion_receipt_method() { let events_with_topic_filters = HashMap::new(); // TODO(krishna): Test events with topic filters - let alien_event_signature = H256::from_low_u64_be(8); // those will not be inserted in the graph - let alien_contract_address = Address::from_low_u64_be(9); + let alien_event_signature = b256(8); // those will not be inserted in the graph + let alien_contract_address = address(9); // test graph nodes let event_a_node = LogFilterNode::Event(event_signature_a); @@ -1912,7 +1883,7 @@ fn log_filter_require_transacion_receipt_method() { events_with_topic_filters, }; - let empty_vec: Vec = vec![]; + let empty_vec: Vec = vec![]; // connected contracts and events graph assert!(filter.requires_transaction_receipt(&event_signature_a, Some(&contract_a), &empty_vec)); diff --git a/chain/ethereum/src/call_helper.rs b/chain/ethereum/src/call_helper.rs new file mode 100644 index 00000000000..ca45b6fb09d --- /dev/null +++ b/chain/ethereum/src/call_helper.rs @@ -0,0 +1,138 @@ +use crate::{ContractCallError, ENV_VARS}; +use graph::{ + abi, + data::store::ethereum::call, + prelude::{ + alloy::transports::{RpcError, TransportErrorKind}, + Logger, + }, + slog::info, +}; + +// ------------------------------------------------------------------ +// Constants and helper utilities used across eth_call handling +// ------------------------------------------------------------------ + +// Try to check if the call was reverted. The JSON-RPC response for reverts is +// not standardized, so we have ad-hoc checks for each Ethereum client. + +// 0xfe is the "designated bad instruction" of the EVM, and Solidity uses it for +// asserts. +const PARITY_BAD_INSTRUCTION_FE: &str = "Bad instruction fe"; + +// 0xfd is REVERT, but on some contracts, and only on older blocks, +// this happens. Makes sense to consider it a revert as well. +const PARITY_BAD_INSTRUCTION_FD: &str = "Bad instruction fd"; + +const PARITY_BAD_JUMP_PREFIX: &str = "Bad jump"; +const PARITY_STACK_LIMIT_PREFIX: &str = "Out of stack"; + +// See f0af4ab0-6b7c-4b68-9141-5b79346a5f61. +const PARITY_OUT_OF_GAS: &str = "Out of gas"; + +// Also covers Nethermind reverts +const PARITY_VM_EXECUTION_ERROR: i64 = -32015; +const PARITY_REVERT_PREFIX: &str = "revert"; + +const XDAI_REVERT: &str = "revert"; + +// Deterministic Geth execution errors. We might need to expand this as +// subgraphs come across other errors. See +// https://github.com/ethereum/go-ethereum/blob/cd57d5cd38ef692de8fbedaa56598b4e9fbfbabc/core/vm/errors.go +const GETH_EXECUTION_ERRORS: &[&str] = &[ + // The "revert" substring covers a few known error messages, including: + // Hardhat: "error: transaction reverted", + // Ganache and Moonbeam: "vm exception while processing transaction: revert", + // Geth: "execution reverted" + // And others. + "revert", + "invalid jump destination", + "invalid opcode", + // Ethereum says 1024 is the stack sizes limit, so this is deterministic. + "stack limit reached 1024", + // See f0af4ab0-6b7c-4b68-9141-5b79346a5f61 for why the gas limit is considered deterministic. + "out of gas", + "stack underflow", +]; + +/// Helper that checks if a geth style RPC error message corresponds to a revert. +fn is_geth_revert_message(message: &str) -> bool { + let env_geth_call_errors = ENV_VARS.geth_eth_call_errors.iter(); + let mut execution_errors = GETH_EXECUTION_ERRORS + .iter() + .copied() + .chain(env_geth_call_errors.map(|s| s.as_str())); + execution_errors.any(|e| message.to_lowercase().contains(e)) +} + +/// Decode a Solidity revert(reason) payload, returning the reason string when possible. +fn as_solidity_revert_reason(bytes: &[u8]) -> Option { + let selector = &tiny_keccak::keccak256(b"Error(string)")[..4]; + if bytes.len() >= 4 && &bytes[..4] == selector { + abi::DynSolType::String + .abi_decode(&bytes[4..]) + .ok() + .and_then(|val| val.clone().as_str().map(ToOwned::to_owned)) + } else { + None + } +} + +/// Interpret the error returned by `eth_call`, distinguishing genuine failures from +/// EVM reverts. Returns `Ok(Null)` for reverts or a proper error otherwise. +pub fn interpret_eth_call_error( + logger: &Logger, + err: RpcError, +) -> Result { + fn reverted(logger: &Logger, reason: &str) -> Result { + info!(logger, "Contract call reverted"; "reason" => reason); + Ok(call::Retval::Null) + } + + if let RpcError::ErrorResp(rpc_error) = &err { + if is_geth_revert_message(&rpc_error.message) { + return reverted(logger, &rpc_error.message); + } + } + + if let RpcError::ErrorResp(rpc_error) = &err { + let code = rpc_error.code; + let data = rpc_error.data.as_ref().map(|d| d.to_string()); + + if code == PARITY_VM_EXECUTION_ERROR { + if let Some(data) = data { + if is_parity_revert(&data) { + return reverted(logger, &parity_revert_reason(&data)); + } + } + } + } + + Err(ContractCallError::AlloyError(err)) +} + +fn is_parity_revert(data: &str) -> bool { + data.to_lowercase().starts_with(PARITY_REVERT_PREFIX) + || data.starts_with(PARITY_BAD_JUMP_PREFIX) + || data.starts_with(PARITY_STACK_LIMIT_PREFIX) + || data == PARITY_BAD_INSTRUCTION_FE + || data == PARITY_BAD_INSTRUCTION_FD + || data == PARITY_OUT_OF_GAS + || data == XDAI_REVERT +} + +/// Checks if the given data corresponds to a Parity / Nethermind style EVM +/// revert and, if so, tries to extract a human-readable revert reason. Returns `Some` +/// with the reason when the error is identified as a revert, otherwise `None`. +fn parity_revert_reason(data: &str) -> String { + if data == PARITY_BAD_INSTRUCTION_FE { + return PARITY_BAD_INSTRUCTION_FE.to_owned(); + } + + // Otherwise try to decode a Solidity revert reason payload. + let payload = data.trim_start_matches(PARITY_REVERT_PREFIX); + hex::decode(payload) + .ok() + .and_then(|decoded| as_solidity_revert_reason(&decoded)) + .unwrap_or_else(|| "no reason".to_owned()) +} diff --git a/chain/ethereum/src/chain.rs b/chain/ethereum/src/chain.rs index 11ca025e0e2..3f827220b93 100644 --- a/chain/ethereum/src/chain.rs +++ b/chain/ethereum/src/chain.rs @@ -660,16 +660,6 @@ impl BlockFinality { } } -impl<'a> From<&'a BlockFinality> for BlockPtr { - fn from(block: &'a BlockFinality) -> BlockPtr { - match block { - BlockFinality::Final(b) => BlockPtr::from(&**b), - BlockFinality::NonFinal(b) => BlockPtr::from(&b.ethereum_block), - BlockFinality::Ptr(b) => BlockPtr::new(b.hash.clone(), b.number), - } - } -} - impl Block for BlockFinality { fn ptr(&self) -> BlockPtr { match self { @@ -725,11 +715,11 @@ impl Block for BlockFinality { fn timestamp(&self) -> BlockTime { match self { BlockFinality::Final(block) => { - let ts = i64::try_from(block.timestamp.as_u64()).unwrap(); + let ts = i64::try_from(block.timestamp_u64()).unwrap(); BlockTime::since_epoch(ts, 0) } BlockFinality::NonFinal(block) => { - let ts = i64::try_from(block.ethereum_block.block.timestamp.as_u64()).unwrap(); + let ts = i64::try_from(block.ethereum_block.block.timestamp_u64()).unwrap(); BlockTime::since_epoch(ts, 0) } BlockFinality::Ptr(block) => block.timestamp, @@ -1111,7 +1101,7 @@ impl TriggersAdapterTrait for TriggersAdapter { .load_blocks( self.logger.cheap_clone(), self.chain_store.cheap_clone(), - HashSet::from_iter(Some(block.hash_as_h256())), + HashSet::from_iter(Some(block.hash.as_b256())), ) .await?; assert_eq!(blocks.len(), 1); diff --git a/chain/ethereum/src/codec.rs b/chain/ethereum/src/codec.rs index 114982607ec..2a402d4f5b2 100644 --- a/chain/ethereum/src/codec.rs +++ b/chain/ethereum/src/codec.rs @@ -7,9 +7,16 @@ use graph::{ blockchain::{ self, Block as BlockchainBlock, BlockPtr, BlockTime, ChainStoreBlock, ChainStoreData, }, + components::ethereum::{AnyBlock, AnyHeader, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope}, prelude::{ - web3, - web3::types::{Bytes, H160, H2048, H256, H64, U256, U64}, + alloy::{ + self, + consensus::{ReceiptWithBloom, TxEnvelope, TxType}, + network::AnyReceiptEnvelope, + primitives::{aliases::B2048, Address, Bloom, Bytes, LogData, B256, U256}, + rpc::types::{self as alloy_rpc_types, AccessList, AccessListItem, Transaction}, + serde::WithOtherFields, + }, BlockNumber, Error, EthereumBlock, EthereumBlockWithCalls, EthereumCall, LightEthereumBlock, }, @@ -34,13 +41,13 @@ where } } -impl TryDecodeProto<[u8; 256], H2048> for &[u8] {} -impl TryDecodeProto<[u8; 32], H256> for &[u8] {} -impl TryDecodeProto<[u8; 20], H160> for &[u8] {} +impl TryDecodeProto<[u8; 32], B256> for &[u8] {} +impl TryDecodeProto<[u8; 256], B2048> for &[u8] {} +impl TryDecodeProto<[u8; 20], Address> for &[u8] {} -impl From<&BigInt> for web3::types::U256 { +impl From<&BigInt> for U256 { fn from(val: &BigInt) -> Self { - web3::types::U256::from_big_endian(&val.bytes) + U256::from_be_slice(&val.bytes) } } @@ -68,9 +75,9 @@ impl<'a> TryInto for CallAt<'a> { .value .as_ref() .map_or_else(|| U256::from(0), |v| v.into()), - gas_used: U256::from(self.call.gas_consumed), - input: Bytes(self.call.input.clone()), - output: Bytes(self.call.return_data.clone()), + gas_used: self.call.gas_consumed, + input: Bytes::from(self.call.input.clone()), + output: Bytes::from(self.call.return_data.clone()), block_hash: self.block.hash.try_decode_proto("call block hash")?, block_number: self.block.number as i32, transaction_hash: Some(self.trace.hash.try_decode_proto("call transaction hash")?), @@ -79,41 +86,6 @@ impl<'a> TryInto for CallAt<'a> { } } -impl TryInto for Call { - type Error = Error; - - fn try_into(self) -> Result { - Ok(web3::types::Call { - from: self.caller.try_decode_proto("call from address")?, - to: self.address.try_decode_proto("call to address")?, - value: self - .value - .as_ref() - .map_or_else(|| U256::from(0), |v| v.into()), - gas: U256::from(self.gas_limit), - input: Bytes::from(self.input.clone()), - call_type: CallType::try_from(self.call_type) - .map_err(|_| graph::anyhow::anyhow!("invalid call type: {}", self.call_type))? - .into(), - }) - } -} - -impl From for web3::types::CallType { - fn from(val: CallType) -> Self { - match val { - CallType::Unspecified => web3::types::CallType::None, - CallType::Call => web3::types::CallType::Call, - CallType::Callcode => web3::types::CallType::CallCode, - CallType::Delegate => web3::types::CallType::DelegateCall, - CallType::Static => web3::types::CallType::StaticCall, - - // FIXME (SF): Really not sure what this should map to, we are using None for now, need to revisit - CallType::Create => web3::types::CallType::None, - } - } -} - pub struct LogAt<'a> { log: &'a Log, block: &'a Block, @@ -126,46 +98,39 @@ impl<'a> LogAt<'a> { } } -impl<'a> TryInto for LogAt<'a> { +impl<'a> TryInto for LogAt<'a> { type Error = Error; - fn try_into(self) -> Result { - Ok(web3::types::Log { - address: self.log.address.try_decode_proto("log address")?, - topics: self - .log - .topics - .iter() - .map(|t| t.try_decode_proto("topic")) - .collect::, Error>>()?, - data: Bytes::from(self.log.data.clone()), + fn try_into(self) -> Result { + let topics = self + .log + .topics + .iter() + .map(|t| t.try_decode_proto("topic")) + .collect::, Error>>()?; + + Ok(alloy::rpc::types::Log { + inner: alloy::primitives::Log { + address: self.log.address.try_decode_proto("log address")?, + data: LogData::new(topics, self.log.data.clone().into()) + .ok_or_else(|| format_err!("invalid log data"))?, + }, block_hash: Some(self.block.hash.try_decode_proto("log block hash")?), - block_number: Some(U64::from(self.block.number)), + block_number: Some(self.block.number), transaction_hash: Some(self.trace.hash.try_decode_proto("log transaction hash")?), - transaction_index: Some(U64::from(self.trace.index as u64)), - log_index: Some(U256::from(self.log.block_index)), - transaction_log_index: Some(U256::from(self.log.index)), - log_type: None, - removed: None, + transaction_index: Some(self.trace.index as u64), + log_index: Some(self.log.block_index as u64), + removed: false, + block_timestamp: self + .block + .header + .as_ref() + .map(|h| h.timestamp.as_ref().map(|t| t.seconds as u64)) + .flatten(), }) } } -impl TryFrom for Option { - type Error = Error; - - fn try_from(val: TransactionTraceStatus) -> Result { - match val { - TransactionTraceStatus::Unknown => Err(format_err!( - "Got a transaction trace with status UNKNOWN, datasource is broken" - )), - TransactionTraceStatus::Succeeded => Ok(Some(web3::types::U64::from(1))), - TransactionTraceStatus::Failed => Ok(Some(web3::types::U64::from(0))), - TransactionTraceStatus::Reverted => Ok(Some(web3::types::U64::from(0))), - } - } -} - pub struct TransactionTraceAt<'a> { trace: &'a TransactionTrace, block: &'a Block, @@ -177,34 +142,314 @@ impl<'a> TransactionTraceAt<'a> { } } -impl<'a> TryInto for TransactionTraceAt<'a> { +impl<'a> TryInto> for TransactionTraceAt<'a> { type Error = Error; - fn try_into(self) -> Result { - Ok(web3::types::Transaction { - hash: self.trace.hash.try_decode_proto("transaction hash")?, - nonce: U256::from(self.trace.nonce), - block_hash: Some(self.block.hash.try_decode_proto("transaction block hash")?), - block_number: Some(U64::from(self.block.number)), - transaction_index: Some(U64::from(self.trace.index as u64)), - from: Some( - self.trace - .from - .try_decode_proto("transaction from address")?, - ), - to: get_to_address(self.trace)?, - value: self.trace.value.as_ref().map_or(U256::zero(), |x| x.into()), - gas_price: self.trace.gas_price.as_ref().map(|x| x.into()), - gas: U256::from(self.trace.gas_limit), - input: Bytes::from(self.trace.input.clone()), - v: None, - r: None, - s: None, - raw: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - transaction_type: None, + fn try_into(self) -> Result, Self::Error> { + use alloy::{ + consensus::transaction::Recovered, + consensus::{ + Signed, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip7702, TxLegacy, + }, + network::{AnyTxEnvelope, AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction}, + primitives::{Bytes, TxKind, U256}, + rpc::types::Transaction as AlloyTransaction, + serde::OtherFields, + }; + use std::collections::BTreeMap; + + // Extract data from trace and block + let block_hash = self.block.hash.try_decode_proto("transaction block hash")?; + let block_number = self.block.number; + let transaction_index = Some(self.trace.index as u64); + let from_address = self + .trace + .from + .try_decode_proto("transaction from address")?; + let to = get_to_address(self.trace)?; + let value = self.trace.value.as_ref().map_or(U256::ZERO, |x| x.into()); + let gas_price = self.trace.gas_price.as_ref().map_or(0u128, |x| { + let val: U256 = x.into(); + val.to::() + }); + let gas_limit = self.trace.gas_limit; + let input = Bytes::from(self.trace.input.clone()); + + let tx_type_u64 = u64::try_from(self.trace.r#type).map_err(|_| { + format_err!( + "Invalid transaction type value {} in transaction trace. Transaction type must be a valid u64.", + self.trace.r#type + ) + })?; + + // Try to convert to known Ethereum transaction type + let tx_type_result = TxType::try_from(tx_type_u64); + + // If this is an unknown transaction type, create an UnknownTxEnvelope + if tx_type_result.is_err() { + let mut fields_map = BTreeMap::new(); + + fields_map.insert( + "nonce".to_string(), + jsonrpc_core::serde_json::json!(format!("0x{:x}", self.trace.nonce)), + ); + fields_map.insert( + "from".to_string(), + jsonrpc_core::serde_json::json!(format!("{:?}", from_address)), + ); + if let Some(to_addr) = to { + fields_map.insert( + "to".to_string(), + jsonrpc_core::serde_json::json!(format!("{:?}", to_addr)), + ); + } + fields_map.insert( + "value".to_string(), + jsonrpc_core::serde_json::json!(format!("0x{:x}", value)), + ); + fields_map.insert( + "gas".to_string(), + jsonrpc_core::serde_json::json!(format!("0x{:x}", gas_limit)), + ); + fields_map.insert( + "gasPrice".to_string(), + jsonrpc_core::serde_json::json!(format!("0x{:x}", gas_price)), + ); + fields_map.insert( + "input".to_string(), + jsonrpc_core::serde_json::json!(format!("0x{}", hex::encode(&input))), + ); + + let fields = OtherFields::new(fields_map); + let unknown_tx = UnknownTypedTransaction { + ty: AnyTxType(tx_type_u64 as u8), + fields, + memo: Default::default(), + }; + + let tx_hash = self.trace.hash.try_decode_proto("transaction hash")?; + let unknown_envelope = UnknownTxEnvelope { + hash: tx_hash, + inner: unknown_tx, + }; + + let any_envelope = AnyTxEnvelope::Unknown(unknown_envelope); + let recovered = Recovered::new_unchecked(any_envelope, from_address); + + return Ok(AlloyTransaction { + inner: recovered, + block_hash: Some(block_hash), + block_number: Some(block_number), + transaction_index, + effective_gas_price: if gas_price > 0 { Some(gas_price) } else { None }, + }); + } + + let tx_type = tx_type_result.unwrap(); + let nonce = self.trace.nonce; + + // Extract EIP-1559 fee fields from trace + let max_fee_per_gas_u128 = self.trace.max_fee_per_gas.as_ref().map_or(gas_price, |x| { + let val: U256 = x.into(); + val.to::() + }); + + let max_priority_fee_per_gas_u128 = + self.trace + .max_priority_fee_per_gas + .as_ref() + .map_or(0u128, |x| { + let val: U256 = x.into(); + val.to::() + }); + + // Extract access list from trace + let access_list: AccessList = self + .trace + .access_list + .iter() + .map(|access_tuple| { + let address = Address::from_slice(&access_tuple.address); + let storage_keys = access_tuple + .storage_keys + .iter() + .map(|key| B256::from_slice(key)) + .collect(); + AccessListItem { + address, + storage_keys, + } + }) + .collect::>() + .into(); + + // Extract actual signature components from trace + let signature = extract_signature_from_trace(self.trace, tx_type)?; + + let to_kind = match to { + Some(addr) => TxKind::Call(addr), + None => TxKind::Create, + }; + + let envelope = match tx_type { + TxType::Legacy => { + let tx = TxLegacy { + chain_id: None, + nonce, + gas_price, + gas_limit, + to: to_kind, + value, + input: input.clone(), + }; + let signed_tx = Signed::new_unchecked( + tx, + signature, + self.trace.hash.try_decode_proto("transaction hash")?, + ); + TxEnvelope::Legacy(signed_tx) + } + TxType::Eip2930 => { + let tx = TxEip2930 { + // Firehose protobuf doesn't provide chain_id for transactions. + // Using 0 as placeholder since the transaction has already been validated on-chain. + chain_id: 0, + nonce, + gas_price, + gas_limit, + to: to_kind, + value, + access_list: access_list.clone(), // Use actual access list from trace + input: input.clone(), + }; + let signed_tx = Signed::new_unchecked( + tx, + signature, + self.trace.hash.try_decode_proto("transaction hash")?, + ); + TxEnvelope::Eip2930(signed_tx) + } + TxType::Eip1559 => { + let tx = TxEip1559 { + // Firehose protobuf doesn't provide chain_id for transactions. + // Using 0 as placeholder since the transaction has already been validated on-chain. + chain_id: 0, + nonce, + gas_limit, + max_fee_per_gas: max_fee_per_gas_u128, + max_priority_fee_per_gas: max_priority_fee_per_gas_u128, + to: to_kind, + value, + access_list: access_list.clone(), // Use actual access list from trace + input: input.clone(), + }; + let signed_tx = Signed::new_unchecked( + tx, + signature, + self.trace.hash.try_decode_proto("transaction hash")?, + ); + TxEnvelope::Eip1559(signed_tx) + } + TxType::Eip4844 => { + let to_address = to.ok_or_else(|| { + format_err!("EIP-4844 transactions cannot be contract creation transactions. The 'to' field must contain a valid address.") + })?; + + let blob_versioned_hashes: Vec = self + .trace + .blob_hashes + .iter() + .map(|hash| B256::from_slice(hash)) + .collect(); + + let max_fee_per_blob_gas_u128 = + self.trace.blob_gas_fee_cap.as_ref().map_or(0u128, |x| { + let val: U256 = x.into(); + val.to::() + }); + + let tx_eip4844 = TxEip4844 { + // Firehose protobuf doesn't provide chain_id for transactions. + // Using 0 as placeholder since the transaction has already been validated on-chain. + chain_id: 0, + nonce, + gas_limit, + max_fee_per_gas: max_fee_per_gas_u128, + max_priority_fee_per_gas: max_priority_fee_per_gas_u128, + to: to_address, + value, + access_list: access_list.clone(), // Use actual access list from trace + blob_versioned_hashes, + max_fee_per_blob_gas: max_fee_per_blob_gas_u128, + input: input.clone(), + }; + let tx = TxEip4844Variant::TxEip4844(tx_eip4844); + let signed_tx = Signed::new_unchecked( + tx, + signature, + self.trace.hash.try_decode_proto("transaction hash")?, + ); + TxEnvelope::Eip4844(signed_tx) + } + TxType::Eip7702 => { + let to_address = to.ok_or_else(|| { + format_err!("EIP-7702 transactions cannot be contract creation transactions. The 'to' field must contain a valid address.") + })?; + + // Convert set_code_authorizations to alloy authorization list + let authorization_list: Vec = self + .trace + .set_code_authorizations + .iter() + .map(|auth| { + let inner = alloy::eips::eip7702::Authorization { + chain_id: U256::from_be_slice(&auth.chain_id), + address: Address::from_slice(&auth.address), + nonce: auth.nonce, + }; + + let r = U256::from_be_slice(&auth.r); + let s = U256::from_be_slice(&auth.s); + let y_parity = auth.v as u8; + + alloy::eips::eip7702::SignedAuthorization::new_unchecked( + inner, y_parity, r, s, + ) + }) + .collect(); + + let tx = TxEip7702 { + // Firehose protobuf doesn't provide chain_id for transactions. + // Using 0 as placeholder since the transaction has already been validated on-chain. + chain_id: 0, + nonce, + gas_limit, + max_fee_per_gas: max_fee_per_gas_u128, + max_priority_fee_per_gas: max_priority_fee_per_gas_u128, + to: to_address, + value, + access_list: access_list.clone(), // Use actual access list from trace + authorization_list, + input: input.clone(), + }; + let signed_tx = Signed::new_unchecked( + tx, + signature, + self.trace.hash.try_decode_proto("transaction hash")?, + ); + TxEnvelope::Eip7702(signed_tx) + } + }; + + let any_envelope = AnyTxEnvelope::Ethereum(envelope); + let recovered = Recovered::new_unchecked(any_envelope, from_address); + + Ok(AlloyTransaction { + inner: recovered, + block_hash: Some(block_hash), + block_number: Some(block_number), + transaction_index, + effective_gas_price: if gas_price > 0 { Some(gas_price) } else { None }, // gas_price already contains effective gas price per protobuf spec }) } } @@ -217,143 +462,136 @@ impl TryInto for &Block { } } +impl TryInto for &Block { + type Error = Error; + + fn try_into(self) -> Result { + let header = self.header(); + + let block_hash = self.hash.try_decode_proto("block hash")?; + let consensus_header = alloy::consensus::Header { + number: header.number, + beneficiary: header.coinbase.try_decode_proto("author / coinbase")?, + parent_hash: header.parent_hash.try_decode_proto("parent hash")?, + ommers_hash: header.uncle_hash.try_decode_proto("uncle hash")?, + state_root: header.state_root.try_decode_proto("state root")?, + transactions_root: header + .transactions_root + .try_decode_proto("transactions root")?, + receipts_root: header.receipt_root.try_decode_proto("receipt root")?, + gas_used: header.gas_used, + gas_limit: header.gas_limit, + base_fee_per_gas: header.base_fee_per_gas.as_ref().map(|v| { + let val: U256 = v.into(); + val.to::() + }), + extra_data: Bytes::from(header.extra_data.clone()), + logs_bloom: if header.logs_bloom.is_empty() { + Bloom::ZERO + } else { + Bloom::try_from(header.logs_bloom.as_slice())? + }, + timestamp: header.timestamp.as_ref().map_or(0, |v| v.seconds as u64), + difficulty: header + .difficulty + .as_ref() + .map_or_else(|| U256::ZERO, |v| v.into()), + + mix_hash: header.mix_hash.try_decode_proto("mix hash")?, + nonce: header.nonce.into(), + + withdrawals_root: if header.withdrawals_root.is_empty() { + None + } else { + Some( + header + .withdrawals_root + .try_decode_proto("withdrawals root")?, + ) + }, + blob_gas_used: header.blob_gas_used, + excess_blob_gas: header.excess_blob_gas, + parent_beacon_block_root: if header.parent_beacon_root.is_empty() { + None + } else { + Some( + header + .parent_beacon_root + .try_decode_proto("parent beacon root")?, + ) + }, + requests_hash: if header.requests_hash.is_empty() { + None + } else { + Some(header.requests_hash.try_decode_proto("requests hash")?) + }, + }; + + let rpc_header = alloy::rpc::types::Header { + hash: block_hash, + inner: consensus_header, + total_difficulty: { + #[allow(deprecated)] + let total_difficulty = &header.total_difficulty; + total_difficulty.as_ref().map(|v| v.into()) + }, + size: Some(U256::from(self.size)), + }; + + let transactions = self + .transaction_traces + .iter() + .map(|t| TransactionTraceAt::new(t, self).try_into()) + .collect::>, Error>>()?; + + let uncles = self + .uncles + .iter() + .map(|u| u.hash.try_decode_proto("uncle hash")) + .collect::, _>>()?; + + use alloy::rpc::types::Block; + + let any_header: AnyRpcHeader = rpc_header.map(AnyHeader::from); + + let any_transactions: Vec = transactions + .into_iter() + .map(|tx| AnyRpcTransaction::new(WithOtherFields::new(tx))) + .collect(); + + let any_block = Block { + header: any_header, + transactions: alloy::rpc::types::BlockTransactions::Full(any_transactions), + uncles, + withdrawals: None, + }; + + Ok(AnyBlock::new(WithOtherFields::new(any_block))) + } +} + impl TryInto for &Block { type Error = Error; fn try_into(self) -> Result { - let header = self.header.as_ref().ok_or_else(|| { - format_err!("block header should always be present from gRPC Firehose") - })?; - + let alloy_block: AnyBlock = self.try_into()?; + + let transaction_receipts = self + .transaction_traces + .iter() + .filter_map(|t| transaction_trace_to_alloy_txn_reciept(t, self).transpose()) + .collect::, Error>>()? + .into_iter() + // Transaction receipts will be shared along the code, so we put them into an + // Arc here to avoid excessive cloning. + .map(Arc::new) + .collect(); + + #[allow(unreachable_code)] let block = EthereumBlockWithCalls { ethereum_block: EthereumBlock { - block: Arc::new(LightEthereumBlock { - hash: Some(self.hash.try_decode_proto("block hash")?), - number: Some(U64::from(self.number)), - author: header.coinbase.try_decode_proto("author / coinbase")?, - parent_hash: header.parent_hash.try_decode_proto("parent hash")?, - uncles_hash: header.uncle_hash.try_decode_proto("uncle hash")?, - state_root: header.state_root.try_decode_proto("state root")?, - transactions_root: header - .transactions_root - .try_decode_proto("transactions root")?, - receipts_root: header.receipt_root.try_decode_proto("receipt root")?, - gas_used: U256::from(header.gas_used), - gas_limit: U256::from(header.gas_limit), - base_fee_per_gas: Some( - header - .base_fee_per_gas - .as_ref() - .map_or_else(U256::default, |v| v.into()), - ), - extra_data: Bytes::from(header.extra_data.clone()), - logs_bloom: match &header.logs_bloom.len() { - 0 => None, - _ => Some(header.logs_bloom.try_decode_proto("logs bloom")?), - }, - timestamp: header - .timestamp - .as_ref() - .map_or_else(U256::default, |v| U256::from(v.seconds)), - difficulty: header - .difficulty - .as_ref() - .map_or_else(U256::default, |v| v.into()), - total_difficulty: Some( - header - .total_difficulty - .as_ref() - .map_or_else(U256::default, |v| v.into()), - ), - // FIXME (SF): Firehose does not have seal fields, are they really used? Might be required for POA chains only also, I've seen that stuff on xDai (is this important?) - seal_fields: vec![], - uncles: self - .uncles - .iter() - .map(|u| u.hash.try_decode_proto("uncle hash")) - .collect::, _>>()?, - transactions: self - .transaction_traces - .iter() - .map(|t| TransactionTraceAt::new(t, self).try_into()) - .collect::, Error>>()?, - size: Some(U256::from(self.size)), - mix_hash: Some(header.mix_hash.try_decode_proto("mix hash")?), - nonce: Some(H64::from_low_u64_be(header.nonce)), - }), - transaction_receipts: self - .transaction_traces - .iter() - .filter_map(|t| { - t.receipt.as_ref().map(|r| { - Ok(web3::types::TransactionReceipt { - transaction_hash: t.hash.try_decode_proto("transaction hash")?, - transaction_index: U64::from(t.index), - block_hash: Some( - self.hash.try_decode_proto("transaction block hash")?, - ), - block_number: Some(U64::from(self.number)), - cumulative_gas_used: U256::from(r.cumulative_gas_used), - // FIXME (SF): What is the rule here about gas_used being None, when it's 0? - gas_used: Some(U256::from(t.gas_used)), - contract_address: { - match t.calls.len() { - 0 => None, - _ => { - match CallType::try_from(t.calls[0].call_type).map_err( - |_| { - graph::anyhow::anyhow!( - "invalid call type: {}", - t.calls[0].call_type, - ) - }, - )? { - CallType::Create => { - Some(t.calls[0].address.try_decode_proto( - "transaction contract address", - )?) - } - _ => None, - } - } - } - }, - logs: r - .logs - .iter() - .map(|l| LogAt::new(l, self, t).try_into()) - .collect::, Error>>()?, - status: TransactionTraceStatus::try_from(t.status) - .map_err(|_| { - graph::anyhow::anyhow!( - "invalid transaction trace status: {}", - t.status - ) - })? - .try_into()?, - root: match r.state_root.len() { - 0 => None, // FIXME (SF): should this instead map to [0;32]? - // FIXME (SF): if len < 32, what do we do? - _ => Some( - r.state_root.try_decode_proto("transaction state root")?, - ), - }, - logs_bloom: r - .logs_bloom - .try_decode_proto("transaction logs bloom")?, - from: t.from.try_decode_proto("transaction from")?, - to: get_to_address(t)?, - transaction_type: None, - effective_gas_price: None, - }) - }) - }) - .collect::, Error>>()? - .into_iter() - // Transaction receipts will be shared along the code, so we put them into an - // Arc here to avoid excessive cloning. - .map(Arc::new) - .collect(), + block: Arc::new(LightEthereumBlock::new(alloy_block)), + transaction_receipts, }, // Comment (437a9f17-67cc-478f-80a3-804fe554b227): This Some() will avoid calls in the triggers_in_block // TODO: Refactor in a way that this is no longer needed. @@ -376,12 +614,118 @@ impl TryInto for &Block { } } +fn transaction_trace_to_alloy_txn_reciept( + t: &TransactionTrace, + block: &Block, +) -> Result, Error> { + use alloy::consensus::{Eip658Value, Receipt}; + let r = t.receipt.as_ref(); + + if r.is_none() { + return Ok(None); + } + + let r = r.unwrap(); + + let contract_address = match t.calls.len() { + 0 => None, + _ => { + match CallType::try_from(t.calls[0].call_type).map_err(|_| { + graph::anyhow::anyhow!("invalid call type: {}", t.calls[0].call_type) + })? { + CallType::Create => Some( + t.calls[0] + .address + .try_decode_proto("transaction contract address")?, + ), + _ => None, + } + } + }; + + let state_root = match &r.state_root { + b if b.is_empty() => None, + _ => Some(r.state_root.try_decode_proto("transaction state root")?), + }; + + let status = match TransactionTraceStatus::try_from(t.status) + .map_err(|_| format_err!("invalid transaction trace status: {}", t.status))? + { + TransactionTraceStatus::Unknown => { + return Err(format_err!( + "Transaction trace has UNKNOWN status; datasource is broken" + )) + } + TransactionTraceStatus::Succeeded => true, + TransactionTraceStatus::Failed | TransactionTraceStatus::Reverted => false, + }; + + // [EIP-658]: https://eips.ethereum.org/EIPS/eip-658 + // Before EIP-658, the state root field was used to indicate the status of the transaction. + // After EIP-658, the status field is used to indicate the status of the transaction. + let status = match state_root { + Some(root) => Eip658Value::PostState(root), + None => Eip658Value::Eip658(status), + }; + + let logs: Vec = r + .logs + .iter() + .map(|l| LogAt::new(l, block, t).try_into()) + .collect::, Error>>()?; + + let core_receipt = Receipt { + status, + cumulative_gas_used: r.cumulative_gas_used, + logs, + }; + + let logs_bloom = Bloom::try_from(r.logs_bloom.as_slice())?; + + let receipt_with_bloom = ReceiptWithBloom::new(core_receipt, logs_bloom); + + let tx_type_u64 = u64::try_from(t.r#type).map_err(|_| { + format_err!( + "Invalid transaction type value {} in transaction receipt. Transaction type must be a valid u64.", + t.r#type + ) + })?; + + let any_envelope = AnyReceiptEnvelope { + inner: receipt_with_bloom, + r#type: tx_type_u64 as u8, + }; + + let receipt = alloy_rpc_types::TransactionReceipt { + transaction_hash: t.hash.try_decode_proto("transaction hash")?, + transaction_index: Some(t.index as u64), + block_hash: Some(block.hash.try_decode_proto("transaction block hash")?), + block_number: Some(block.number), + gas_used: t.gas_used, + contract_address, + from: t.from.try_decode_proto("transaction from")?, + to: get_to_address(t)?, + effective_gas_price: t.gas_price.as_ref().map_or(0u128, |x| { + let val: U256 = x.into(); + val.to::() + }), // gas_price already contains effective gas price per protobuf spec + blob_gas_used: r.blob_gas_used, + blob_gas_price: r.blob_gas_price.as_ref().map(|x| { + let val: U256 = x.into(); + val.to::() + }), + inner: any_envelope, + }; + + Ok(Some(WithOtherFields::new(receipt))) +} + impl BlockHeader { pub fn parent_ptr(&self) -> Option { match self.parent_hash.len() { 0 => None, _ => Some(BlockPtr::from(( - H256::from_slice(self.parent_hash.as_ref()), + B256::from_slice(self.parent_hash.as_ref()), self.number - 1, ))), } @@ -390,13 +734,13 @@ impl BlockHeader { impl<'a> From<&'a BlockHeader> for BlockPtr { fn from(b: &'a BlockHeader) -> BlockPtr { - BlockPtr::from((H256::from_slice(b.hash.as_ref()), b.number)) + BlockPtr::from((B256::from_slice(b.hash.as_ref()), b.number)) } } impl<'a> From<&'a Block> for BlockPtr { fn from(b: &'a Block) -> BlockPtr { - BlockPtr::from((H256::from_slice(b.hash.as_ref()), b.number)) + BlockPtr::from((B256::from_slice(b.hash.as_ref()), b.number)) } } @@ -528,9 +872,120 @@ mod test { format!(r#"{{"block":{{"data":null,"timestamp":"{}"}}}}"#, now) ); } + + #[test] + fn test_unknown_transaction_type_conversion() { + use super::TransactionTraceAt; + use crate::codec::TransactionTrace; + use graph::prelude::alloy::network::AnyTxEnvelope; + use graph::prelude::alloy::primitives::B256; + + let mut block = Block::default(); + let mut header = BlockHeader::default(); + header.number = 123456; + header.timestamp = Some(Timestamp { + seconds: 1234567890, + nanos: 0, + }); + block.header = Some(header); + block.number = 123456; + block.hash = vec![0u8; 32]; + + let mut trace = TransactionTrace::default(); + trace.r#type = 126; // 0x7e Optimism deposit transaction + trace.hash = vec![1u8; 32]; + trace.from = vec![2u8; 20]; + trace.to = vec![3u8; 20]; + trace.nonce = 42; + trace.gas_limit = 21000; + trace.index = 0; + + let trace_at = TransactionTraceAt::new(&trace, &block); + let result: Result< + graph::prelude::alloy::rpc::types::Transaction, + graph::prelude::Error, + > = trace_at.try_into(); + + assert!( + result.is_ok(), + "Should successfully convert unknown transaction type" + ); + + let tx = result.unwrap(); + + match tx.inner.inner() { + AnyTxEnvelope::Unknown(unknown_envelope) => { + assert_eq!(unknown_envelope.inner.ty.0, 126); + assert_eq!(unknown_envelope.hash, B256::from_slice(&trace.hash)); + assert!( + !unknown_envelope.inner.fields.is_empty(), + "OtherFields should contain transaction data" + ); + } + _ => panic!("Expected AnyTxEnvelope::Unknown, got Ethereum variant"), + } + + assert_eq!(tx.block_number, Some(123456)); + assert_eq!(tx.transaction_index, Some(0)); + assert_eq!(tx.block_hash, Some(B256::from_slice(&block.hash))); + } + + #[test] + fn test_unknown_receipt_type_conversion() { + use super::transaction_trace_to_alloy_txn_reciept; + use crate::codec::TransactionTrace; + + let mut block = Block::default(); + let mut header = BlockHeader::default(); + header.number = 123456; + block.header = Some(header); + block.hash = vec![0u8; 32]; + + let mut trace = TransactionTrace::default(); + trace.r#type = 126; // 0x7e Optimism deposit transaction + trace.hash = vec![1u8; 32]; + trace.from = vec![2u8; 20]; + trace.to = vec![3u8; 20]; + trace.index = 0; + trace.gas_used = 21000; + trace.status = 1; + + let mut receipt = super::TransactionReceipt::default(); + receipt.cumulative_gas_used = 21000; + receipt.logs_bloom = vec![0u8; 256]; + trace.receipt = Some(receipt); + + let result = transaction_trace_to_alloy_txn_reciept(&trace, &block); + + assert!( + result.is_ok(), + "Should successfully convert receipt with unknown transaction type" + ); + + let receipt_opt = result.unwrap(); + assert!(receipt_opt.is_some(), "Receipt should be present"); + + let receipt = receipt_opt.unwrap(); + + assert_eq!(receipt.inner.inner.r#type, 126); + assert_eq!(receipt.gas_used, 21000); + assert_eq!(receipt.transaction_index, Some(0)); + } +} + +fn extract_signature_from_trace( + _trace: &TransactionTrace, + _tx_type: TxType, +) -> Result { + use alloy::primitives::{Signature as PrimitiveSignature, U256}; + + // Create a dummy signature with r = 0, s = 0 and even y-parity (false) + let dummy = PrimitiveSignature::new(U256::ZERO, U256::ZERO, false); + + Ok(dummy.into()) } -fn get_to_address(trace: &TransactionTrace) -> Result, Error> { +fn get_to_address(trace: &TransactionTrace) -> Result, Error> { // Try to detect contract creation transactions, which have no 'to' address let is_contract_creation = trace.to.len() == 0 || trace.calls.get(0).map_or(false, |call| { diff --git a/chain/ethereum/src/data_source.rs b/chain/ethereum/src/data_source.rs index e314b5a158f..c465e4aed7e 100644 --- a/chain/ethereum/src/data_source.rs +++ b/chain/ethereum/src/data_source.rs @@ -1,7 +1,11 @@ use anyhow::{anyhow, Error}; use anyhow::{ensure, Context}; use async_trait::async_trait; +use graph::abi; +use graph::abi::EventExt; +use graph::abi::FunctionExt; use graph::blockchain::{BlockPtr, TriggerWithHandler}; +use graph::components::ethereum::AnyTransaction; use graph::components::link_resolver::LinkResolverContext; use graph::components::metrics::subgraph::SubgraphInstanceMetrics; use graph::components::store::{EthereumCallCache, StoredDynamicDataSource}; @@ -17,9 +21,13 @@ use graph::env::ENV_VARS; use graph::futures03::future::try_join; use graph::futures03::stream::FuturesOrdered; use graph::futures03::TryStreamExt; -use graph::prelude::ethabi::ethereum_types::H160; -use graph::prelude::ethabi::StateMutability; -use graph::prelude::{Link, SubgraphManifestValidationError}; +use graph::prelude::alloy::{ + consensus::{TxEnvelope, TxLegacy}, + network::TransactionResponse, + primitives::{Address, B256, U256}, + rpc::types::Log, +}; +use graph::prelude::{alloy, Link, SubgraphManifestValidationError}; use graph::slog::{debug, error, o, trace}; use itertools::Itertools; use serde::de::Error as ErrorD; @@ -34,11 +42,8 @@ use tiny_keccak::{keccak256, Keccak}; use graph::{ blockchain::{self, Blockchain}, prelude::{ - ethabi::{Address, Event, Function, LogParam, ParamType, RawLog}, - serde_json, warn, - web3::types::{Log, Transaction, H256}, - BlockNumber, CheapClone, EthereumCall, LightEthereumBlock, LightEthereumBlockExt, - LinkResolver, Logger, + serde_json, warn, BlockNumber, CheapClone, EthereumCall, LightEthereumBlock, + LightEthereumBlockExt, LinkResolver, Logger, }, }; @@ -134,7 +139,7 @@ impl blockchain::DataSource for DataSource { } fn address(&self) -> Option<&[u8]> { - self.address.as_ref().map(|x| x.as_bytes()) + self.address.as_ref().map(|x| x.as_slice()) } fn has_declared_calls(&self) -> bool { @@ -238,7 +243,7 @@ impl blockchain::DataSource for DataSource { } fn as_stored_dynamic_data_source(&self) -> StoredDynamicDataSource { - let param = self.address.map(|addr| addr.0.into()); + let param = self.address.map(|addr| addr.as_slice().into()); StoredDynamicDataSource { manifest_idx: self.manifest_idx, param, @@ -277,7 +282,7 @@ impl blockchain::DataSource for DataSource { let contract_abi = template.mapping.find_abi(&template.source.abi)?; - let address = param.map(|x| H160::from_slice(&x)); + let address = param.map(|x| Address::from_slice(&x)); Ok(DataSource { kind: template.kind.to_string(), network: template.network.as_ref().map(|s| s.to_string()), @@ -432,6 +437,45 @@ impl blockchain::DataSource for DataSource { } } +/// Generic function that creates a mock legacy Transaction from ANY log +fn create_dummy_transaction( + block_number: u64, + block_hash: B256, + transaction_index: Option, + transaction_hash: Option, +) -> Result { + use alloy::serde::WithOtherFields; + use graph::components::ethereum::AnyTxEnvelope; + use graph::prelude::alloy::{ + consensus::transaction::Recovered, consensus::Signed, primitives::Signature, + rpc::types::Transaction, + }; + + let tx = TxLegacy::default(); + + // Create a dummy signature + let signature = Signature::new(U256::ZERO, U256::ZERO, false); + + let tx_hash = transaction_hash.ok_or(anyhow!("Log has no transaction hash"))?; + let signed_tx = Signed::new_unchecked(tx, signature, tx_hash); + let eth_envelope = TxEnvelope::Legacy(signed_tx); + + // Wrap in AnyTxEnvelope + let any_envelope = AnyTxEnvelope::Ethereum(eth_envelope); + + let recovered = Recovered::new_unchecked(any_envelope, Address::ZERO); + + let inner_tx = Transaction { + inner: recovered, + block_hash: Some(block_hash), + block_number: Some(block_number), + transaction_index, + effective_gas_price: None, + }; + + Ok(AnyTransaction::new(WithOtherFields::new(inner_tx))) +} + impl DataSource { fn from_manifest( kind: String, @@ -463,7 +507,7 @@ impl DataSource { }) } - fn handlers_for_log(&self, log: &Log) -> Vec { + fn handlers_for_log(&self, log: &alloy::rpc::types::Log) -> Vec { self.mapping .event_handlers .iter() @@ -528,28 +572,28 @@ impl DataSource { } } - /// Returns the contract event with the given signature, if it exists. A an event from the ABI + /// Returns the contract event with the given signature, if it exists. An event from the ABI /// will be matched if: /// 1. An event signature is equal to `signature`. /// 2. There are no equal matches, but there is exactly one event that equals `signature` if all /// `indexed` modifiers are removed from the parameters. - fn contract_event_with_signature(&self, signature: &str) -> Option<&Event> { + fn contract_event_with_signature(&self, signature: &str) -> Option<&abi::Event> { // Returns an `Event(uint256,address)` signature for an event, without `indexed` hints. - fn ambiguous_event_signature(event: &Event) -> String { + fn ambiguous_event_signature(event: &abi::Event) -> String { format!( "{}({})", event.name, event .inputs .iter() - .map(|input| event_param_type_signature(&input.kind)) + .map(|input| input.selector_type().into_owned()) .collect::>() .join(",") ) } // Returns an `Event(indexed uint256,address)` type signature for an event. - fn event_signature(event: &Event) -> String { + fn event_signature(event: &abi::Event) -> String { format!( "{}({})", event.name, @@ -559,40 +603,13 @@ impl DataSource { .map(|input| format!( "{}{}", if input.indexed { "indexed " } else { "" }, - event_param_type_signature(&input.kind) + input.selector_type() )) .collect::>() .join(",") ) } - // Returns the signature of an event parameter type (e.g. `uint256`). - fn event_param_type_signature(kind: &ParamType) -> String { - use ParamType::*; - - match kind { - Address => "address".into(), - Bytes => "bytes".into(), - Int(size) => format!("int{}", size), - Uint(size) => format!("uint{}", size), - Bool => "bool".into(), - String => "string".into(), - Array(inner) => format!("{}[]", event_param_type_signature(inner)), - FixedBytes(size) => format!("bytes{}", size), - FixedArray(inner, size) => { - format!("{}[{}]", event_param_type_signature(inner), size) - } - Tuple(components) => format!( - "({})", - components - .iter() - .map(event_param_type_signature) - .collect::>() - .join(",") - ), - } - } - self.contract_abi .contract .events() @@ -631,7 +648,9 @@ impl DataSource { }) } - fn contract_function_with_signature(&self, target_signature: &str) -> Option<&Function> { + fn contract_function_with_signature(&self, target_signature: &str) -> Option<&abi::Function> { + use abi::StateMutability; + self.contract_abi .contract .functions() @@ -645,7 +664,7 @@ impl DataSource { let mut arguments = function .inputs .iter() - .map(|input| format!("{}", input.kind)) + .map(|input| input.selector_type().into_owned()) .collect::>() .join(","); // `address,uint256,bool) @@ -735,11 +754,7 @@ impl DataSource { .into_iter() .filter_map(|(event_handler, event_abi)| { event_abi - .parse_log(RawLog { - topics: log.topics.clone(), - data: log.data.clone().0, - }) - .map(|log| log.params) + .decode_log(&log) .map_err(|e| { trace!( logger, @@ -778,17 +793,15 @@ impl DataSource { // See also ca0edc58-0ec5-4c89-a7dd-2241797f5e50. // There is another special case in zkSync-era, where the transaction hash in this case would be zero // See https://docs.zksync.io/zk-stack/concepts/blocks.html#fictive-l2-block-finalizing-the-batch - let transaction = if log.transaction_hash == block.hash - || log.transaction_hash == Some(H256::zero()) + let transaction = if log.transaction_hash == Some(block.hash()) + || log.transaction_hash == Some(B256::ZERO) { - Transaction { - hash: log.transaction_hash.unwrap(), - block_hash: block.hash, - block_number: block.number, - transaction_index: log.transaction_index, - from: Some(H160::zero()), - ..Transaction::default() - } + create_dummy_transaction( + block.number_u64(), + block.hash(), + log.transaction_index, + log.transaction_hash, + )? } else { // This is the general case where the log's transaction hash does not match the block's hash // and is not a special zero hash, implying a real transaction associated with this log. @@ -799,8 +812,8 @@ impl DataSource { let logging_extras = Arc::new(o! { "signature" => event_handler.event.to_string(), - "address" => format!("{}", &log.address), - "transaction" => format!("{}", &transaction.hash), + "address" => format!("{}", &log.address()), + "transaction" => format!("{}", &transaction.inner.tx_hash()), }); let handler = event_handler.handler.clone(); let calls = DeclaredCall::from_log_trigger_with_event( @@ -844,20 +857,15 @@ impl DataSource { ) })?; - // Parse the inputs - // - // Take the input for the call, chop off the first 4 bytes, then call - // `function.decode_input` to get a vector of `Token`s. Match the `Token`s - // with the `Param`s in `function.inputs` to create a `Vec`. - let tokens = match function_abi.decode_input(&call.input.0[4..]).with_context( - || { + let values = match function_abi + .abi_decode_input(&call.input.0[4..]) + .with_context(|| { format!( "Generating function inputs for the call {:?} failed, raw input: {}", &function_abi, hex::encode(&call.input.0) ) - }, - ) { + }) { Ok(val) => val, // See also 280b0108-a96e-4738-bb37-60ce11eeb5bf Err(err) => { @@ -867,27 +875,22 @@ impl DataSource { }; ensure!( - tokens.len() == function_abi.inputs.len(), + values.len() == function_abi.inputs.len(), "Number of arguments in call does not match \ number of inputs in function signature." ); - let inputs = tokens + let inputs = values .into_iter() .enumerate() - .map(|(i, token)| LogParam { + .map(|(i, value)| abi::DynSolParam { name: function_abi.inputs[i].name.clone(), - value: token, + value, }) .collect::>(); - // Parse the outputs - // - // Take the output for the call, then call `function.decode_output` to - // get a vector of `Token`s. Match the `Token`s with the `Param`s in - // `function.outputs` to create a `Vec`. - let tokens = function_abi - .decode_output(&call.output.0) + let values = function_abi + .abi_decode_output(&call.output.0) .with_context(|| { format!( "Decoding function outputs for the call {:?} failed, raw output: {}", @@ -897,17 +900,17 @@ impl DataSource { })?; ensure!( - tokens.len() == function_abi.outputs.len(), + values.len() == function_abi.outputs.len(), "Number of parameters in the call output does not match \ number of outputs in the function signature." ); - let outputs = tokens + let outputs = values .into_iter() .enumerate() - .map(|(i, token)| LogParam { + .map(|(i, value)| abi::DynSolParam { name: function_abi.outputs[i].name.clone(), - value: token, + value, }) .collect::>(); @@ -919,7 +922,7 @@ impl DataSource { let logging_extras = Arc::new(o! { "function" => handler.function.to_string(), "to" => format!("{}", &call.to), - "transaction" => format!("{}", &transaction.hash), + "transaction" => format!("{}", &transaction.inner.tx_hash()), }); Ok(Some(TriggerWithHandler::::new_with_logging_extras( MappingTrigger::Call { @@ -1478,13 +1481,13 @@ pub struct MappingCallHandler { #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] pub struct UnresolvedMappingEventHandler { pub event: String, - pub topic0: Option, - #[serde(deserialize_with = "deserialize_h256_vec", default)] - pub topic1: Option>, - #[serde(deserialize_with = "deserialize_h256_vec", default)] - pub topic2: Option>, - #[serde(deserialize_with = "deserialize_h256_vec", default)] - pub topic3: Option>, + pub topic0: Option, + #[serde(deserialize_with = "deserialize_b256_vec", default)] + pub topic1: Option>, + #[serde(deserialize_with = "deserialize_b256_vec", default)] + pub topic2: Option>, + #[serde(deserialize_with = "deserialize_b256_vec", default)] + pub topic3: Option>, pub handler: String, #[serde(default)] pub receipt: bool, @@ -1518,17 +1521,17 @@ impl UnresolvedMappingEventHandler { #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct MappingEventHandler { pub event: String, - pub topic0: Option, - pub topic1: Option>, - pub topic2: Option>, - pub topic3: Option>, + pub topic0: Option, + pub topic1: Option>, + pub topic2: Option>, + pub topic3: Option>, pub handler: String, pub receipt: bool, pub calls: CallDecls, } // Custom deserializer for H256 fields that removes the '0x' prefix before parsing -fn deserialize_h256_vec<'de, D>(deserializer: D) -> Result>, D::Error> +fn deserialize_b256_vec<'de, D>(deserializer: D) -> Result>, D::Error> where D: Deserializer<'de>, { @@ -1536,40 +1539,40 @@ where match s { Some(vec) => { - let mut h256_vec = Vec::new(); + let mut b256_vec = Vec::new(); for hex_str in vec { // Remove '0x' prefix if present let clean_hex_str = hex_str.trim_start_matches("0x"); // Ensure the hex string is 64 characters long, after removing '0x' let padded_hex_str = format!("{:0>64}", clean_hex_str); // Parse the padded string into H256, handling potential errors - h256_vec.push( - H256::from_str(&padded_hex_str) - .map_err(|e| D::Error::custom(format!("Failed to parse H256: {}", e)))?, + b256_vec.push( + B256::from_str(&padded_hex_str) + .map_err(|e| D::Error::custom(format!("Failed to parse B256: {}", e)))?, ); } - Ok(Some(h256_vec)) + Ok(Some(b256_vec)) } None => Ok(None), } } impl MappingEventHandler { - pub fn topic0(&self) -> H256 { + pub fn topic0(&self) -> B256 { self.topic0 - .unwrap_or_else(|| string_to_h256(&self.event.replace("indexed ", ""))) + .unwrap_or_else(|| string_to_b256(&self.event.replace("indexed ", ""))) } pub fn matches(&self, log: &Log) -> bool { - let matches_topic = |index: usize, topic_opt: &Option>| -> bool { + let matches_topic = |index: usize, topic_opt: &Option>| -> bool { topic_opt.as_ref().map_or(true, |topic_vec| { - log.topics + log.topics() .get(index) .map_or(false, |log_topic| topic_vec.contains(log_topic)) }) }; - if let Some(topic0) = log.topics.get(0) { + if let Some(topic0) = log.topics().get(0) { return self.topic0() == *topic0 && matches_topic(1, &self.topic1) && matches_topic(2, &self.topic2) @@ -1587,18 +1590,15 @@ impl MappingEventHandler { } } -/// Hashes a string to a H256 hash. -fn string_to_h256(s: &str) -> H256 { +/// Hashes a string to a B256 hash. +fn string_to_b256(s: &str) -> B256 { let mut result = [0u8; 32]; let data = s.replace(' ', "").into_bytes(); let mut sponge = Keccak::new_keccak256(); sponge.update(&data); sponge.finalize(&mut result); - // This was deprecated but the replacement seems to not be available in the - // version web3 uses. - #[allow(deprecated)] - H256::from_slice(&result) + B256::from_slice(&result) } #[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Deserialize)] diff --git a/chain/ethereum/src/ethereum_adapter.rs b/chain/ethereum/src/ethereum_adapter.rs index c4dd377fa58..48bc61b943c 100644 --- a/chain/ethereum/src/ethereum_adapter.rs +++ b/chain/ethereum/src/ethereum_adapter.rs @@ -1,13 +1,13 @@ use async_trait::async_trait; use futures03::{future::BoxFuture, stream::FuturesUnordered}; -use tokio::sync::RwLock; -use tokio::time::timeout; - +use graph::abi; +use graph::abi::DynSolValueExt; +use graph::abi::FunctionExt; use graph::blockchain::client::ChainClient; use graph::blockchain::BlockHash; use graph::blockchain::ChainIdentifier; use graph::blockchain::ExtendedBlockPtr; - +use graph::components::ethereum::*; use graph::components::transaction_receipt::LightTransactionReceipt; use graph::data::store::ethereum::call; use graph::data::store::scalar; @@ -21,33 +21,35 @@ use graph::futures03::future::try_join_all; use graph::futures03::{ self, compat::Future01CompatExt, FutureExt, StreamExt, TryFutureExt, TryStreamExt, }; -use graph::prelude::ethabi::ParamType; -use graph::prelude::ethabi::Token; -use graph::prelude::tokio::try_join; -use graph::prelude::web3::types::U256; +use graph::prelude::{ + alloy::{ + self, + network::{AnyNetwork, TransactionResponse}, + primitives::{Address, B256}, + providers::{ + ext::TraceApi, + fillers::{ + BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, + }, + Identity, Provider, RootProvider, + }, + rpc::types::{ + trace::{filter::TraceFilter as AlloyTraceFilter, parity::LocalizedTransactionTrace}, + TransactionInput, TransactionRequest, + }, + transports::{RpcError, TransportErrorKind}, + }, + tokio::try_join, +}; use graph::slog::o; use graph::{ blockchain::{block_stream::BlockWithTriggers, BlockPtr, IngestorError}, prelude::{ anyhow::{self, anyhow, bail, ensure, Context}, - debug, error, ethabi, hex, info, retry, serde_json as json, tiny_keccak, trace, warn, - web3::{ - self, - types::{ - Address, BlockId, BlockNumber as Web3BlockNumber, Bytes, CallRequest, Filter, - FilterBuilder, Log, Transaction, TransactionReceipt, H256, - }, - }, - BlockNumber, ChainStore, CheapClone, DynTryFuture, Error, EthereumCallCache, Logger, - TimeoutError, + debug, error, hex, info, retry, serde_json as json, trace, warn, BlockNumber, ChainStore, + CheapClone, DynTryFuture, Error, EthereumCallCache, Logger, TimeoutError, }, }; -use graph::{ - components::ethereum::*, - prelude::web3::api::Web3, - prelude::web3::transports::Batch, - prelude::web3::types::{Trace, TraceFilter, TraceFilterBuilder, H160}, -}; use itertools::Itertools; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::convert::TryFrom; @@ -55,9 +57,14 @@ use std::iter::FromIterator; use std::pin::Pin; use std::sync::Arc; use std::time::Instant; +use tokio::sync::RwLock; +use tokio::time::timeout; +use tokio_stream; +use crate::adapter::EthGetLogsFilter; use crate::adapter::EthereumRpcError; use crate::adapter::ProviderStatus; +use crate::call_helper::interpret_eth_call_error; use crate::chain::BlockFinality; use crate::trigger::{LogPosition, LogRef}; use crate::Chain; @@ -65,32 +72,54 @@ use crate::NodeCapabilities; use crate::TriggerFilter; use crate::{ adapter::{ - ContractCallError, EthGetLogsFilter, EthereumAdapter as EthereumAdapterTrait, - EthereumBlockFilter, EthereumCallFilter, EthereumLogFilter, ProviderEthRpcMetrics, - SubgraphEthRpcMetrics, + ContractCallError, EthereumAdapter as EthereumAdapterTrait, EthereumBlockFilter, + EthereumCallFilter, EthereumLogFilter, ProviderEthRpcMetrics, SubgraphEthRpcMetrics, }, transport::Transport, trigger::{EthereumBlockTriggerType, EthereumTrigger}, ENV_VARS, }; -#[derive(Debug, Clone)] +type AlloyProvider = FillProvider< + JoinFill< + Identity, + JoinFill>>, + >, + RootProvider, + AnyNetwork, +>; + +#[derive(Clone)] pub struct EthereumAdapter { logger: Logger, provider: String, - web3: Arc>, + alloy: Arc, metrics: Arc, supports_eip_1898: bool, call_only: bool, supports_block_receipts: Arc>>, } +impl std::fmt::Debug for EthereumAdapter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EthereumAdapter") + .field("logger", &self.logger) + .field("provider", &self.provider) + .field("alloy", &"") + .field("metrics", &self.metrics) + .field("supports_eip_1898", &self.supports_eip_1898) + .field("call_only", &self.call_only) + .field("supports_block_receipts", &self.supports_block_receipts) + .finish() + } +} + impl CheapClone for EthereumAdapter { fn cheap_clone(&self) -> Self { Self { logger: self.logger.clone(), provider: self.provider.clone(), - web3: self.web3.cheap_clone(), + alloy: self.alloy.clone(), metrics: self.metrics.cheap_clone(), supports_eip_1898: self.supports_eip_1898, call_only: self.call_only, @@ -112,12 +141,35 @@ impl EthereumAdapter { supports_eip_1898: bool, call_only: bool, ) -> Self { - let web3 = Arc::new(Web3::new(transport)); + let alloy = match &transport { + Transport::RPC { client, .. } => Arc::new( + alloy::providers::ProviderBuilder::<_, _, AnyNetwork>::default() + .network::() + .with_recommended_fillers() + .connect_client(client.clone()), + ), + Transport::IPC(ipc_connect) => Arc::new( + alloy::providers::ProviderBuilder::<_, _, AnyNetwork>::default() + .network::() + .with_recommended_fillers() + .connect_ipc(ipc_connect.clone()) + .await + .unwrap(), + ), + Transport::WS(ws_connect) => Arc::new( + alloy::providers::ProviderBuilder::<_, _, AnyNetwork>::default() + .network::() + .with_recommended_fillers() + .connect_ws(ws_connect.clone()) + .await + .unwrap(), + ), + }; EthereumAdapter { logger, provider, - web3, + alloy, metrics: provider_metrics, supports_eip_1898, call_only, @@ -131,79 +183,26 @@ impl EthereumAdapter { subgraph_metrics: Arc, from: BlockNumber, to: BlockNumber, - addresses: Vec, - ) -> Result, Error> { + addresses: Vec
, + ) -> Result, Error> { assert!(!self.call_only); - let eth = self.clone(); let retry_log_message = format!("trace_filter RPC call for block range: [{}..{}]", from, to); + let eth = self.clone(); + retry(retry_log_message, &logger) .redact_log_urls(true) .limit(ENV_VARS.request_retries) .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let trace_filter: TraceFilter = match addresses.len() { - 0 => TraceFilterBuilder::default() - .from_block(from.into()) - .to_block(to.into()) - .build(), - _ => TraceFilterBuilder::default() - .from_block(from.into()) - .to_block(to.into()) - .to_address(addresses.clone()) - .build(), - }; - - let eth = eth.cheap_clone(); - let logger_for_triggers = logger.clone(); - let logger_for_error = logger.clone(); - let start = Instant::now(); + let eth = eth.clone(); + let logger = logger.clone(); let subgraph_metrics = subgraph_metrics.clone(); - let provider_metrics = eth.metrics.clone(); - let provider = self.provider.clone(); - + let addresses = addresses.clone(); async move { - let result = eth - .web3 - .trace() - .filter(trace_filter) + eth.execute_trace_filter_request(logger, subgraph_metrics, from, to, addresses) .await - .map(move |traces| { - if !traces.is_empty() { - if to == from { - debug!( - logger_for_triggers, - "Received {} traces for block {}", - traces.len(), - to - ); - } else { - debug!( - logger_for_triggers, - "Received {} traces for blocks [{}, {}]", - traces.len(), - from, - to - ); - } - } - traces - }) - .map_err(Error::from); - - let elapsed = start.elapsed().as_secs_f64(); - provider_metrics.observe_request(elapsed, "trace_filter", &provider); - subgraph_metrics.observe_request(elapsed, "trace_filter", &provider); - if let Err(e) = &result { - provider_metrics.add_error("trace_filter", &provider); - subgraph_metrics.add_error("trace_filter", &provider); - debug!( - logger_for_error, - "Error querying traces error = {:#} from = {} to = {}", e, from, to - ); - } - result } }) .map_err(move |e| { @@ -219,12 +218,99 @@ impl EthereumAdapter { .await } + async fn execute_trace_filter_request( + &self, + logger: Logger, + subgraph_metrics: Arc, + from: BlockNumber, + to: BlockNumber, + addresses: Vec
, + ) -> Result, Error> { + let alloy_trace_filter = Self::build_trace_filter(from, to, &addresses); + let start = Instant::now(); + + let result = self.alloy.trace_filter(&alloy_trace_filter).await; + + if let Ok(traces) = &result { + self.log_trace_results(&logger, from, to, traces.len()); + } + + self.record_trace_metrics( + &subgraph_metrics, + start.elapsed().as_secs_f64(), + &result, + from, + to, + &logger, + ); + + result.map_err(Error::from) + } + + fn build_trace_filter( + from: BlockNumber, + to: BlockNumber, + addresses: &[Address], + ) -> AlloyTraceFilter { + let filter = AlloyTraceFilter::default() + .from_block(from as u64) + .to_block(to as u64); + + if !addresses.is_empty() { + filter.to_address(addresses.to_vec()) + } else { + filter + } + } + + fn log_trace_results( + &self, + logger: &Logger, + from: BlockNumber, + to: BlockNumber, + trace_len: usize, + ) { + if trace_len > 0 { + if to == from { + debug!(logger, "Received {} traces for block {}", trace_len, to); + } else { + debug!( + logger, + "Received {} traces for blocks [{}, {}]", trace_len, from, to + ); + } + } + } + + fn record_trace_metrics( + &self, + subgraph_metrics: &Arc, + elapsed: f64, + result: &Result, RpcError>, + from: BlockNumber, + to: BlockNumber, + logger: &Logger, + ) { + self.metrics + .observe_request(elapsed, "trace_filter", &self.provider); + subgraph_metrics.observe_request(elapsed, "trace_filter", &self.provider); + + if let Err(e) = result { + self.metrics.add_error("trace_filter", &self.provider); + subgraph_metrics.add_error("trace_filter", &self.provider); + debug!( + logger, + "Error querying traces error = {:#} from = {} to = {}", e, from, to + ); + } + } + // This is a lazy check for block receipt support. It is only called once and then the result is // cached. The result is not used for anything critical, so it is fine to be lazy. async fn check_block_receipt_support_and_update_cache( &self, - web3: Arc>, - block_hash: H256, + alloy: Arc, + block_hash: B256, supports_eip_1898: bool, call_only: bool, logger: Logger, @@ -241,7 +327,7 @@ impl EthereumAdapter { info!(logger, "Checking eth_getBlockReceipts support"); let result = timeout( ENV_VARS.block_receipts_check_timeout, - check_block_receipt_support(web3, block_hash, supports_eip_1898, call_only), + check_block_receipt_support(alloy, block_hash, supports_eip_1898, call_only), ) .await; @@ -274,6 +360,7 @@ impl EthereumAdapter { result } + /// Alloy-exclusive version of logs_with_sigs using alloy types and methods async fn logs_with_sigs( &self, logger: Logger, @@ -282,19 +369,24 @@ impl EthereumAdapter { to: BlockNumber, filter: Arc, too_many_logs_fingerprints: &'static [&'static str], - ) -> Result, TimeoutError> { + ) -> Result< + Vec, + TimeoutError>, + > { assert!(!self.call_only); let eth_adapter = self.clone(); let retry_log_message = format!("eth_getLogs RPC call for block range: [{}..{}]", from, to); retry(retry_log_message, &logger) .redact_log_urls(true) - .when(move |res: &Result<_, web3::error::Error>| match res { - Ok(_) => false, - Err(e) => !too_many_logs_fingerprints - .iter() - .any(|f| e.to_string().contains(f)), - }) + .when( + move |res: &Result<_, RpcError>| match res { + Ok(_) => false, + Err(e) => !too_many_logs_fingerprints + .iter() + .any(|f| e.to_string().contains(f)), + }, + ) .limit(ENV_VARS.request_retries) .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { @@ -306,21 +398,10 @@ impl EthereumAdapter { async move { let start = Instant::now(); - // Create a log filter - let log_filter: Filter = FilterBuilder::default() - .from_block(from.into()) - .to_block(to.into()) - .address(filter.contracts.clone()) - .topics( - Some(filter.event_signatures.clone()), - filter.topic1.clone(), - filter.topic2.clone(), - filter.topic3.clone(), - ) - .build(); - // Request logs from client - let result = eth_adapter.web3.eth().logs(log_filter).boxed().await; + let alloy_filter = filter.to_alloy_filter(from, to); + + let result = eth_adapter.alloy.get_logs(&alloy_filter).await; let elapsed = start.elapsed().as_secs_f64(); provider_metrics.observe_request(elapsed, "eth_getLogs", &provider); subgraph_metrics.observe_request(elapsed, "eth_getLogs", &provider); @@ -340,8 +421,8 @@ impl EthereumAdapter { subgraph_metrics: Arc, from: BlockNumber, to: BlockNumber, - addresses: Vec, - ) -> impl Stream + Send { + addresses: Vec
, + ) -> impl futures03::Stream> + Send { if from > to { panic!( "Can not produce a call stream on a backwards block range: from = {}, to = {}", @@ -357,34 +438,37 @@ impl EthereumAdapter { let eth = self; let logger = logger.clone(); - stream::unfold(from, move |start| { - if start > to { - return None; - } - let end = (start + step_size - 1).min(to); - let new_start = end + 1; - if start == end { - debug!(logger, "Requesting traces for block {}", start); - } else { - debug!(logger, "Requesting traces for blocks [{}, {}]", start, end); + + futures03::stream::try_unfold(from, move |start| { + let eth = eth.clone(); + let logger = logger.clone(); + let subgraph_metrics = subgraph_metrics.clone(); + let addresses = addresses.clone(); + + async move { + if start > to { + return Ok::, Error>(None); + } + + let end = (start + step_size - 1).min(to); + let new_start = end + 1; + + if start == end { + debug!(logger, "Requesting traces for block {}", start); + } else { + debug!(logger, "Requesting traces for blocks [{}, {}]", start, end); + } + + let traces = eth + .traces(logger, subgraph_metrics, start, end, addresses) + .await?; + Ok(Some(( + futures03::stream::iter(traces.into_iter().map(|t| Ok::<_, Error>(t))), + new_start, + ))) } - Some(graph::futures01::future::ok(( - eth.clone() - .traces( - logger.cheap_clone(), - subgraph_metrics.clone(), - start, - end, - addresses.clone(), - ) - .boxed() - .compat(), - new_start, - ))) }) - .buffered(ENV_VARS.block_batch_size) - .map(stream::iter_ok) - .flatten() + .try_flatten() } fn log_stream( @@ -394,7 +478,7 @@ impl EthereumAdapter { from: BlockNumber, to: BlockNumber, filter: EthGetLogsFilter, - ) -> DynTryFuture<'static, Vec, Error> { + ) -> DynTryFuture<'static, Vec, Error> { // Codes returned by Ethereum node providers if an eth_getLogs request is too heavy. const TOO_MANY_LOGS_FINGERPRINTS: &[&str] = &[ "ServerError(-32005)", // Infura @@ -481,14 +565,11 @@ impl EthereumAdapter { .boxed() } - // Method to determine block_id based on support for EIP-1898 - fn block_ptr_to_id(&self, block_ptr: &BlockPtr) -> BlockId { - // Ganache does not support calls by block hash. - // See https://github.com/trufflesuite/ganache-cli/issues/973 + fn block_ptr_to_id(&self, block_ptr: &BlockPtr) -> alloy::rpc::types::BlockId { if !self.supports_eip_1898 { - BlockId::Number(block_ptr.number.into()) + alloy::rpc::types::BlockId::number(block_ptr.number as u64) } else { - BlockId::Hash(block_ptr.hash_as_h256()) + alloy::rpc::types::BlockId::hash(block_ptr.hash.as_b256()) } } @@ -497,8 +578,8 @@ impl EthereumAdapter { logger: &Logger, address: Address, block_ptr: BlockPtr, - ) -> Result { - let web3 = self.web3.clone(); + ) -> Result { + let alloy = self.alloy.clone(); let logger = Logger::new(&logger, o!("provider" => self.provider.clone())); let block_id = self.block_ptr_to_id(&block_ptr); @@ -513,13 +594,12 @@ impl EthereumAdapter { .limit(ENV_VARS.request_retries) .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let web3 = web3.cheap_clone(); + let alloy = alloy.cheap_clone(); async move { - let result: Result = - web3.eth().code(address, Some(block_id)).boxed().await; + let result = alloy.get_code_at(address).block_id(block_id).await; match result { Ok(code) => Ok(code), - Err(err) => Err(EthereumRpcError::Web3Error(err)), + Err(err) => Err(EthereumRpcError::AlloyError(err)), } } }) @@ -532,8 +612,8 @@ impl EthereumAdapter { logger: &Logger, address: Address, block_ptr: BlockPtr, - ) -> Result { - let web3 = self.web3.clone(); + ) -> Result { + let alloy = self.alloy.clone(); let logger = Logger::new(&logger, o!("provider" => self.provider.clone())); let block_id = self.block_ptr_to_id(&block_ptr); @@ -548,13 +628,12 @@ impl EthereumAdapter { .limit(ENV_VARS.request_retries) .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let web3 = web3.cheap_clone(); + let alloy = alloy.cheap_clone(); async move { - let result: Result = - web3.eth().balance(address, Some(block_id)).boxed().await; + let result = alloy.get_balance(address).block_id(block_id).await; match result { Ok(balance) => Ok(balance), - Err(err) => Err(EthereumRpcError::Web3Error(err)), + Err(err) => Err(EthereumRpcError::AlloyError(err)), } } }) @@ -569,15 +648,10 @@ impl EthereumAdapter { block_ptr: BlockPtr, gas: Option, ) -> Result { - fn reverted(logger: &Logger, reason: &str) -> Result { - info!(logger, "Contract call reverted"; "reason" => reason); - Ok(call::Retval::Null) - } - - let web3 = self.web3.clone(); + let alloy = self.alloy.clone(); let logger = Logger::new(&logger, o!("provider" => self.provider.clone())); - let block_id = self.block_ptr_to_id(&block_ptr); + let alloy_block_id = self.block_ptr_to_id(&block_ptr); let retry_log_message = format!("eth_call RPC call for block {}", block_ptr); retry(retry_log_message, &logger) .redact_log_urls(true) @@ -585,138 +659,29 @@ impl EthereumAdapter { .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { let call_data = call_data.clone(); - let web3 = web3.cheap_clone(); + let alloy = alloy.cheap_clone(); let logger = logger.cheap_clone(); async move { - let req = CallRequest { - to: Some(call_data.address), - gas: gas.map(|val| web3::types::U256::from(val)), - data: Some(Bytes::from(call_data.encoded_call.to_vec())), - from: None, - gas_price: None, - value: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - transaction_type: None, - }; - let result = web3.eth().call(req, Some(block_id)).boxed().await; - - // Try to check if the call was reverted. The JSON-RPC response for reverts is - // not standardized, so we have ad-hoc checks for each Ethereum client. - - // 0xfe is the "designated bad instruction" of the EVM, and Solidity uses it for - // asserts. - const PARITY_BAD_INSTRUCTION_FE: &str = "Bad instruction fe"; - - // 0xfd is REVERT, but on some contracts, and only on older blocks, - // this happens. Makes sense to consider it a revert as well. - const PARITY_BAD_INSTRUCTION_FD: &str = "Bad instruction fd"; - - const PARITY_BAD_JUMP_PREFIX: &str = "Bad jump"; - const PARITY_STACK_LIMIT_PREFIX: &str = "Out of stack"; - - // See f0af4ab0-6b7c-4b68-9141-5b79346a5f61. - const PARITY_OUT_OF_GAS: &str = "Out of gas"; - - // Also covers Nethermind reverts - const PARITY_VM_EXECUTION_ERROR: i64 = -32015; - const PARITY_REVERT_PREFIX: &str = "revert"; - - const XDAI_REVERT: &str = "revert"; - - // Deterministic Geth execution errors. We might need to expand this as - // subgraphs come across other errors. See - // https://github.com/ethereum/go-ethereum/blob/cd57d5cd38ef692de8fbedaa56598b4e9fbfbabc/core/vm/errors.go - const GETH_EXECUTION_ERRORS: &[&str] = &[ - // The "revert" substring covers a few known error messages, including: - // Hardhat: "error: transaction reverted", - // Ganache and Moonbeam: "vm exception while processing transaction: revert", - // Geth: "execution reverted" - // And others. - "revert", - "invalid jump destination", - "invalid opcode", - // Ethereum says 1024 is the stack sizes limit, so this is deterministic. - "stack limit reached 1024", - // See f0af4ab0-6b7c-4b68-9141-5b79346a5f61 for why the gas limit is considered deterministic. - "out of gas", - "stack underflow", - ]; - - let env_geth_call_errors = ENV_VARS.geth_eth_call_errors.iter(); - let mut geth_execution_errors = GETH_EXECUTION_ERRORS - .iter() - .copied() - .chain(env_geth_call_errors.map(|s| s.as_str())); - - let as_solidity_revert_with_reason = |bytes: &[u8]| { - let solidity_revert_function_selector = - &tiny_keccak::keccak256(b"Error(string)")[..4]; - - match bytes.len() >= 4 && &bytes[..4] == solidity_revert_function_selector { - false => None, - true => ethabi::decode(&[ParamType::String], &bytes[4..]) - .ok() - .and_then(|tokens| tokens[0].clone().into_string()), - } - }; - - match result { - // A successful response. - Ok(bytes) => Ok(call::Retval::Value(scalar::Bytes::from(bytes))), - - // Check for Geth revert. - Err(web3::Error::Rpc(rpc_error)) - if geth_execution_errors - .any(|e| rpc_error.message.to_lowercase().contains(e)) => - { - reverted(&logger, &rpc_error.message) - } + let mut req = TransactionRequest::default() + .input(TransactionInput::both(alloy::primitives::Bytes::from( + call_data.encoded_call.to_vec(), + ))) + .to(call_data.address); - // Check for Parity revert. - Err(web3::Error::Rpc(ref rpc_error)) - if rpc_error.code.code() == PARITY_VM_EXECUTION_ERROR => - { - match rpc_error.data.as_ref().and_then(|d| d.as_str()) { - Some(data) - if data.to_lowercase().starts_with(PARITY_REVERT_PREFIX) - || data.starts_with(PARITY_BAD_JUMP_PREFIX) - || data.starts_with(PARITY_STACK_LIMIT_PREFIX) - || data == PARITY_BAD_INSTRUCTION_FE - || data == PARITY_BAD_INSTRUCTION_FD - || data == PARITY_OUT_OF_GAS - || data == XDAI_REVERT => - { - let reason = if data == PARITY_BAD_INSTRUCTION_FE { - PARITY_BAD_INSTRUCTION_FE.to_owned() - } else { - let payload = data.trim_start_matches(PARITY_REVERT_PREFIX); - hex::decode(payload) - .ok() - .and_then(|payload| { - as_solidity_revert_with_reason(&payload) - }) - .unwrap_or("no reason".to_owned()) - }; - reverted(&logger, &reason) - } + if let Some(gas) = gas { + req = req.gas_limit(gas as u64); + } - // The VM execution error was not identified as a revert. - _ => Err(ContractCallError::Web3Error(web3::Error::Rpc( - rpc_error.clone(), - ))), - } - } + let result = alloy.call(req.into()).block(alloy_block_id).await; - // The error was not identified as a revert. - Err(err) => Err(ContractCallError::Web3Error(err)), + match result { + Ok(bytes) => Ok(call::Retval::Value(scalar::Bytes::from(bytes))), + Err(err) => interpret_eth_call_error(&logger, err), } } }) - .map_err(|e| e.into_inner().unwrap_or(ContractCallError::Timeout)) - .boxed() .await + .map_err(|e| e.into_inner().unwrap_or(ContractCallError::Timeout)) } async fn call_and_cache( @@ -754,30 +719,46 @@ impl EthereumAdapter { fn load_blocks_rpc( &self, logger: Logger, - ids: Vec, - ) -> impl Stream, Error = Error> + Send { - let web3 = self.web3.clone(); + ids: Vec, + ) -> impl futures03::Stream, Error>> + Send { + let alloy = self.alloy.clone(); - stream::iter_ok::<_, Error>(ids.into_iter().map(move |hash| { - let web3 = web3.clone(); - retry(format!("load block {}", hash), &logger) - .redact_log_urls(true) - .limit(ENV_VARS.request_retries) - .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) - .run(move || { - Box::pin(web3.eth().block_with_txs(BlockId::Hash(hash))) - .compat() - .from_err::() - .and_then(move |block| { - block.map(Arc::new).ok_or_else(|| { - anyhow::anyhow!("Ethereum node did not find block {:?}", hash) - }) + futures03::stream::iter(ids.into_iter().map(move |hash| { + let alloy = alloy.clone(); + let logger = logger.clone(); + + async move { + retry(format!("load block {}", hash), &logger) + .redact_log_urls(true) + .limit(ENV_VARS.request_retries) + .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) + .run(move || { + let alloy = alloy.cheap_clone(); + async move { + alloy + .get_block_by_hash(hash) + .full() + .await + .map_err(Error::from) + .and_then(|block| { + block + .map(|b| Arc::new(LightEthereumBlock::new(b))) + .ok_or_else(|| { + anyhow::anyhow!( + "Ethereum node did not find block {:?}", + hash + ) + }) + }) + } + }) + .await + .map_err(|e| { + e.into_inner().unwrap_or_else(|| { + anyhow::anyhow!("Ethereum node took too long to return block {}", hash) }) - .compat() - }) - .boxed() - .compat() - .from_err() + }) + } })) .buffered(ENV_VARS.block_batch_size) } @@ -788,10 +769,10 @@ impl EthereumAdapter { logger: Logger, numbers: Vec, ) -> impl futures03::Stream, Error>> + Send { - let web3 = self.web3.clone(); + let alloy = self.alloy.clone(); futures03::stream::iter(numbers.into_iter().map(move |number| { - let web3 = web3.clone(); + let alloy = alloy.clone(); let logger = logger.clone(); async move { @@ -800,21 +781,22 @@ impl EthereumAdapter { .limit(ENV_VARS.request_retries) .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let web3 = web3.clone(); + let alloy = alloy.cheap_clone(); async move { - let block_result = web3 - .eth() - .block(BlockId::Number(Web3BlockNumber::Number(number.into()))) + let block_result = alloy + .get_block_by_number(alloy::rpc::types::BlockNumberOrTag::Number( + number as u64, + )) .await; match block_result { Ok(Some(block)) => { let ptr = ExtendedBlockPtr::try_from(( - block.hash, - block.number, - block.parent_hash, - block.timestamp, + block.header.hash, + block.header.number as i32, + block.header.parent_hash, + block.header.timestamp, )) .map_err(|e| { anyhow::anyhow!("Failed to convert block: {}", e) @@ -849,22 +831,22 @@ impl EthereumAdapter { logger: Logger, block_nums: Vec, ) -> impl Stream + Send { - let web3 = self.web3.clone(); + let alloy = self.alloy.clone(); stream::iter_ok::<_, Error>(block_nums.into_iter().map(move |block_num| { - let web3 = web3.clone(); + let alloy = alloy.clone(); retry(format!("load block ptr {}", block_num), &logger) .redact_log_urls(true) .when(|res| !res.is_ok() && !detect_null_block(res)) .no_limit() .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let web3 = web3.clone(); + let alloy = alloy.cheap_clone(); async move { - let block = web3 - .eth() - .block(BlockId::Number(Web3BlockNumber::Number(block_num.into()))) - .boxed() + let block = alloy + .get_block_by_number(alloy::rpc::types::BlockNumberOrTag::Number( + block_num as u64, + )) .await?; block.ok_or_else(|| { @@ -885,7 +867,7 @@ impl EthereumAdapter { })) .buffered(ENV_VARS.block_batch_size) .filter_map(|b| b) - .map(|b| b.into()) + .map(|b| BlockPtr::from((b.header.hash, b.header.number as i32))) } /// Check if `block_ptr` refers to a block that is on the main chain, according to the Ethereum @@ -918,7 +900,7 @@ impl EthereumAdapter { from: BlockNumber, to: BlockNumber, log_filter: EthereumLogFilter, - ) -> DynTryFuture<'static, Vec, Error> { + ) -> DynTryFuture<'static, Vec, Error> { let eth: Self = self.cheap_clone(); let logger = logger.clone(); @@ -952,13 +934,13 @@ impl EthereumAdapter { wildcard_signatures, } = call_filter; - let mut addresses: Vec = contract_addresses_function_signatures + let mut addresses: Vec
= contract_addresses_function_signatures .iter() .filter(|(_addr, (start_block, _fsigs))| start_block <= &to) .map(|(addr, (_start_block, _fsigs))| *addr) - .collect::>() + .collect::>() .into_iter() - .collect::>(); + .collect::>(); if addresses.is_empty() && wildcard_signatures.is_empty() { // The filter has no started data sources in the requested range, nothing to do. @@ -975,14 +957,13 @@ impl EthereumAdapter { Box::new( eth.trace_stream(logger, subgraph_metrics, from, to, addresses) - .filter_map(|trace| EthereumCall::try_from_trace(&trace)) - .filter(move |call| { - // `trace_filter` can only filter by calls `to` an address and - // a block range. Since subgraphs are subscribing to calls - // for a specific contract function an additional filter needs - // to be applied - call_filter.matches(call) - }), + .try_filter_map(move |trace| { + let maybe_call = EthereumCall::try_from_trace(&trace) + .filter(|call| call_filter.matches(call)); + futures03::future::ready(Ok(maybe_call)) + }) + .boxed() + .compat(), ) } @@ -1061,11 +1042,11 @@ impl EthereumAdapter { logger: &Logger, subgraph_metrics: Arc, block_number: BlockNumber, - block_hash: H256, + block_hash: alloy::primitives::B256, ) -> Result, Error> { let eth = self.clone(); let addresses = Vec::new(); - let traces = eth + let traces: Vec = eth .trace_stream( logger, subgraph_metrics.clone(), @@ -1073,8 +1054,7 @@ impl EthereumAdapter { block_number, addresses, ) - .collect() - .compat() + .try_collect() .await?; // `trace_stream` returns all of the traces for the block, and this @@ -1092,7 +1072,7 @@ impl EthereumAdapter { // all the traces for the block, we need to ensure that the // block hash for the traces is equal to the desired block hash. // Assume all traces are for the same block. - if traces.iter().nth(0).unwrap().block_hash != block_hash { + if traces.iter().nth(0).unwrap().block_hash != Some(block_hash) { return Err(anyhow!( "Trace stream returned traces for an unexpected block: \ number = `{}`, hash = `{}`", @@ -1136,19 +1116,17 @@ impl EthereumAdapter { pub async fn chain_id(&self) -> Result { let logger = self.logger.clone(); - let web3 = self.web3.clone(); - u64::try_from( - retry("chain_id RPC call", &logger) - .redact_log_urls(true) - .no_limit() - .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) - .run(move || { - let web3 = web3.cheap_clone(); - async move { web3.eth().chain_id().await } - }) - .await?, - ) - .map_err(Error::msg) + let alloy = self.alloy.clone(); + retry("chain_id RPC call", &logger) + .redact_log_urls(true) + .no_limit() + .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) + .run(move || { + let alloy = alloy.cheap_clone(); + async move { alloy.get_chain_id().await.map_err(Error::from) } + }) + .await + .map_err(|e| e.into_inner().unwrap_or(EthereumRpcError::Timeout.into())) } } @@ -1171,7 +1149,7 @@ impl EthereumAdapterTrait for EthereumAdapter { async fn net_identifiers(&self) -> Result { let logger = self.logger.clone(); - let web3 = self.web3.clone(); + let alloy = self.alloy.clone(); let metrics = self.metrics.clone(); let provider = self.provider().to_string(); let net_version_future = retry("net_version RPC call", &logger) @@ -1179,11 +1157,11 @@ impl EthereumAdapterTrait for EthereumAdapter { .no_limit() .timeout_secs(20) .run(move || { - let web3 = web3.cheap_clone(); + let alloy = alloy.cheap_clone(); let metrics = metrics.cheap_clone(); let provider = provider.clone(); async move { - web3.net().version().await.map_err(|e| { + alloy.get_net_version().await.map_err(|e| { metrics.set_status(ProviderStatus::VersionFail, &provider); e.into() }) @@ -1196,7 +1174,7 @@ impl EthereumAdapterTrait for EthereumAdapter { }) .boxed(); - let web3 = self.web3.clone(); + let alloy_provider = self.alloy.clone(); let metrics = self.metrics.clone(); let provider = self.provider().to_string(); let retry_log_message = format!( @@ -1208,20 +1186,20 @@ impl EthereumAdapterTrait for EthereumAdapter { .no_limit() .timeout_secs(30) .run(move || { - let web3 = web3.cheap_clone(); + let alloy_genesis = alloy_provider.cheap_clone(); let metrics = metrics.cheap_clone(); let provider = provider.clone(); async move { - web3.eth() - .block(BlockId::Number(Web3BlockNumber::Number( - ENV_VARS.genesis_block_number.into(), - ))) + alloy_genesis + .get_block_by_number(alloy::rpc::types::BlockNumberOrTag::Number( + ENV_VARS.genesis_block_number as u64, + )) .await .map_err(|e| { metrics.set_status(ProviderStatus::GenesisFail, &provider); e })? - .and_then(|gen_block| gen_block.hash.map(BlockHash::from)) + .map(|gen_block| BlockHash::from(gen_block.header.hash)) .ok_or_else(|| anyhow!("Ethereum node could not find genesis block")) } }) @@ -1240,7 +1218,7 @@ impl EthereumAdapterTrait for EthereumAdapter { })?; let ident = ChainIdentifier { - net_version, + net_version: net_version.to_string(), genesis_block_hash, }; @@ -1249,52 +1227,27 @@ impl EthereumAdapterTrait for EthereumAdapter { Ok(ident) } - async fn latest_block_header( - &self, - logger: &Logger, - ) -> Result, IngestorError> { - let web3 = self.web3.clone(); + async fn latest_block_ptr(&self, logger: &Logger) -> Result { + let alloy = self.alloy.clone(); retry("eth_getBlockByNumber(latest) no txs RPC call", logger) .redact_log_urls(true) .no_limit() .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let web3 = web3.cheap_clone(); + let alloy = alloy.cheap_clone(); async move { - let block_opt = web3 - .eth() - .block(Web3BlockNumber::Latest.into()) + let block_opt = alloy + .get_block_by_number(alloy::rpc::types::BlockNumberOrTag::Latest) .await .map_err(|e| anyhow!("could not get latest block from Ethereum: {}", e))?; - block_opt - .ok_or_else(|| anyhow!("no latest block returned from Ethereum").into()) - } - }) - .map_err(move |e| { - e.into_inner().unwrap_or_else(move || { - anyhow!("Ethereum node took too long to return latest block").into() - }) - }) - .await - } + let block = block_opt + .ok_or_else(|| anyhow!("no latest block returned from Ethereum"))?; - async fn latest_block(&self, logger: &Logger) -> Result { - let web3 = self.web3.clone(); - retry("eth_getBlockByNumber(latest) with txs RPC call", logger) - .redact_log_urls(true) - .no_limit() - .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) - .run(move || { - let web3 = web3.cheap_clone(); - async move { - let block_opt = web3 - .eth() - .block_with_txs(Web3BlockNumber::Latest.into()) - .await - .map_err(|e| anyhow!("could not get latest block from Ethereum: {}", e))?; - block_opt - .ok_or_else(|| anyhow!("no latest block returned from Ethereum").into()) + Ok(BlockPtr::from(( + block.header.hash, + block.header.number as i32, + ))) } }) .map_err(move |e| { @@ -1305,27 +1258,12 @@ impl EthereumAdapterTrait for EthereumAdapter { .await } - async fn load_block( - &self, - logger: &Logger, - block_hash: H256, - ) -> Result { - self.block_by_hash(logger, block_hash) - .await? - .ok_or_else(move || { - anyhow!( - "Ethereum node could not find block with hash {}", - block_hash - ) - }) - } - async fn block_by_hash( &self, logger: &Logger, - block_hash: H256, - ) -> Result, Error> { - let web3 = self.web3.clone(); + block_hash: B256, + ) -> Result, Error> { + let alloy = self.alloy.clone(); let logger = logger.clone(); let retry_log_message = format!( "eth_getBlockByHash RPC call for block hash {:?}", @@ -1337,10 +1275,11 @@ impl EthereumAdapterTrait for EthereumAdapter { .limit(ENV_VARS.request_retries) .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let web3 = web3.cheap_clone(); + let alloy = alloy.cheap_clone(); async move { - web3.eth() - .block_with_txs(BlockId::Hash(block_hash)) + alloy + .get_block_by_hash(block_hash) + .full() .await .map_err(Error::from) } @@ -1357,8 +1296,8 @@ impl EthereumAdapterTrait for EthereumAdapter { &self, logger: &Logger, block_number: BlockNumber, - ) -> Result, Error> { - let web3 = self.web3.clone(); + ) -> Result, Error> { + let alloy = self.alloy.clone(); let logger = logger.clone(); let retry_log_message = format!( "eth_getBlockByNumber RPC call for block number {}", @@ -1369,10 +1308,13 @@ impl EthereumAdapterTrait for EthereumAdapter { .no_limit() .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let web3 = web3.cheap_clone(); + let alloy = alloy.clone(); async move { - web3.eth() - .block_with_txs(BlockId::Number(block_number.into())) + alloy + .get_block_by_number(alloy::rpc::types::BlockNumberOrTag::Number( + block_number as u64, + )) + .full() .await .map_err(Error::from) } @@ -1391,26 +1333,26 @@ impl EthereumAdapterTrait for EthereumAdapter { async fn load_full_block( &self, logger: &Logger, - block: LightEthereumBlock, + block: AnyBlock, ) -> Result { - let web3 = Arc::clone(&self.web3); + let alloy = self.alloy.clone(); let logger = logger.clone(); - let block_hash = block.hash.expect("block is missing block hash"); + let block_hash = block.header.hash; // The early return is necessary for correctness, otherwise we'll // request an empty batch which is not valid in JSON-RPC. if block.transactions.is_empty() { trace!(logger, "Block {} contains no transactions", block_hash); return Ok(EthereumBlock { - block: Arc::new(block), + block: Arc::new(LightEthereumBlock::new(block)), transaction_receipts: Vec::new(), }); } - let hashes: Vec<_> = block.transactions.iter().map(|txn| txn.hash).collect(); + let hashes: Vec<_> = block.transactions.hashes().collect(); let supports_block_receipts = self .check_block_receipt_support_and_update_cache( - web3.clone(), + alloy.clone(), block_hash, self.supports_eip_1898, self.call_only, @@ -1418,55 +1360,23 @@ impl EthereumAdapterTrait for EthereumAdapter { ) .await; - fetch_receipts_with_retry(web3, hashes, block_hash, logger, supports_block_receipts) + fetch_receipts_with_retry(alloy, hashes, block_hash, logger, supports_block_receipts) .await .map(|transaction_receipts| EthereumBlock { - block: Arc::new(block), - transaction_receipts, - }) - } - - async fn block_hash_by_block_number( - &self, - logger: &Logger, - block_number: BlockNumber, - ) -> Result, Error> { - let web3 = self.web3.clone(); - let retry_log_message = format!( - "eth_getBlockByNumber RPC call for block number {}", - block_number - ); - retry(retry_log_message, logger) - .redact_log_urls(true) - .no_limit() - .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) - .run(move || { - let web3 = web3.cheap_clone(); - async move { - web3.eth() - .block(BlockId::Number(block_number.into())) - .await - .map(|block_opt| block_opt.and_then(|block| block.hash)) - .map_err(Error::from) - } - }) - .await - .map_err(move |e| { - e.into_inner().unwrap_or_else(move || { - anyhow!( - "Ethereum node took too long to return data for block #{}", - block_number - ) - }) + block: Arc::new(LightEthereumBlock::new(block)), + transaction_receipts: transaction_receipts + .into_iter() + .map(|receipt| receipt) + .collect(), }) } async fn get_balance( &self, logger: &Logger, - address: H160, + address: Address, block_ptr: BlockPtr, - ) -> Result { + ) -> Result { debug!( logger, "eth_getBalance"; "address" => format!("{}", address), @@ -1478,9 +1388,9 @@ impl EthereumAdapterTrait for EthereumAdapter { async fn get_code( &self, logger: &Logger, - address: H160, + address: Address, block_ptr: BlockPtr, - ) -> Result { + ) -> Result { debug!( logger, "eth_getCode"; "address" => format!("{}", address), @@ -1500,7 +1410,7 @@ impl EthereumAdapterTrait for EthereumAdapter { "eth_getBlockByNumber RPC call for block number {}", next_number ); - let web3 = self.web3.clone(); + let alloy = self.alloy.clone(); let logger = logger.clone(); let res = retry(retry_log_message, &logger) .redact_log_urls(true) @@ -1508,12 +1418,16 @@ impl EthereumAdapterTrait for EthereumAdapter { .no_limit() .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let web3 = web3.cheap_clone(); + let alloy = alloy.cheap_clone(); async move { - web3.eth() - .block(BlockId::Number(next_number.into())) + alloy + .get_block_by_number(alloy::rpc::types::BlockNumberOrTag::Number( + next_number as u64, + )) .await - .map(|block_opt| block_opt.and_then(|block| block.hash)) + .map(|block_opt| { + block_opt.map(|block| BlockHash::from(block.header.hash.0.to_vec())) + }) .map_err(Error::from) } }) @@ -1531,7 +1445,7 @@ impl EthereumAdapterTrait for EthereumAdapter { continue; } return match res { - Ok(Some(hash)) => Ok(BlockPtr::new(hash.into(), next_number)), + Ok(Some(hash)) => Ok(BlockPtr::new(hash, next_number)), Ok(None) => Err(anyhow!("Block {} does not contain hash", next_number)), Err(e) => Err(e), }; @@ -1543,7 +1457,7 @@ impl EthereumAdapterTrait for EthereumAdapter { logger: &Logger, inp_call: &ContractCall, cache: Arc, - ) -> Result<(Option>, call::Source), ContractCallError> { + ) -> Result<(Option>, call::Source), ContractCallError> { let mut result = self.contract_calls(logger, &[inp_call], cache).await?; // unwrap: self.contract_calls returns as many results as there were calls Ok(result.pop().unwrap()) @@ -1554,20 +1468,26 @@ impl EthereumAdapterTrait for EthereumAdapter { logger: &Logger, calls: &[&ContractCall], cache: Arc, - ) -> Result>, call::Source)>, ContractCallError> { + ) -> Result>, call::Source)>, ContractCallError> { fn as_req( logger: &Logger, call: &ContractCall, index: u32, ) -> Result { // Emit custom error for type mismatches. - for (token, kind) in call + for (val, kind) in call .args .iter() - .zip(call.function.inputs.iter().map(|p| &p.kind)) + .zip(call.function.inputs.iter().map(|p| p.selector_type())) { - if !token.type_check(kind) { - return Err(ContractCallError::TypeError(token.clone(), kind.clone())); + let kind: abi::DynSolType = kind.parse().map_err(|err| { + ContractCallError::ABIError(anyhow!( + "failed to parse function input type '{kind}': {err}" + )) + })?; + + if !val.type_check(&kind) { + return Err(ContractCallError::TypeError(val.clone(), kind.clone())); } } @@ -1575,8 +1495,8 @@ impl EthereumAdapterTrait for EthereumAdapter { let req = { let encoded_call = call .function - .encode_input(&call.args) - .map_err(ContractCallError::EncodingError)?; + .abi_encode_input(&call.args) + .map_err(|err| ContractCallError::EncodingError(err.into()))?; call::Request::new(call.address, encoded_call, index) }; @@ -1594,15 +1514,14 @@ impl EthereumAdapterTrait for EthereumAdapter { logger: &Logger, resp: call::Response, call: &ContractCall, - ) -> (Option>, call::Source) { + ) -> (Option>, call::Source) { let call::Response { retval, source, req: _, } = resp; - use call::Retval::*; match retval { - Value(output) => match call.function.decode_output(&output) { + call::Retval::Value(output) => match call.function.abi_decode_output(&output) { Ok(tokens) => (Some(tokens), source), Err(e) => { // Decode failures are reverts. The reasoning is that if Solidity fails to @@ -1612,7 +1531,7 @@ impl EthereumAdapterTrait for EthereumAdapter { (None, call::Source::Rpc) } }, - Null => { + call::Retval::Null => { // We got a `0x` response. For old Geth, this can mean a revert. It can also be // that the contract actually returned an empty response. A view call is meant // to return something, so we treat empty responses the same as reverts. @@ -1624,7 +1543,7 @@ impl EthereumAdapterTrait for EthereumAdapter { fn log_call_error(logger: &Logger, e: &ContractCallError, call: &ContractCall) { match e { - ContractCallError::Web3Error(e) => error!(logger, + ContractCallError::AlloyError(e) => error!(logger, "Ethereum node returned an error when calling function \"{}\" of contract \"{}\": {}", call.function.name, call.contract_name, e), ContractCallError::Timeout => error!(logger, @@ -1696,11 +1615,11 @@ impl EthereumAdapterTrait for EthereumAdapter { &self, logger: Logger, chain_store: Arc, - block_hashes: HashSet, + block_hashes: HashSet, ) -> Result>, Error> { let block_hashes: Vec<_> = block_hashes.iter().cloned().collect(); // Search for the block in the store first then use json-rpc as a backup. - let mut blocks: Vec> = chain_store + let mut blocks: Vec<_> = chain_store .cheap_clone() .blocks(block_hashes.iter().map(|&b| b.into()).collect::>()) .await @@ -1708,21 +1627,20 @@ impl EthereumAdapterTrait for EthereumAdapter { .unwrap_or_default() .into_iter() .filter_map(|value| json::from_value(value).ok()) - .map(Arc::new) + .map(|b| Arc::new(LightEthereumBlock::new(b))) .collect(); let missing_blocks = Vec::from_iter( block_hashes .into_iter() - .filter(|hash| !blocks.iter().any(|b| b.hash == Some(*hash))), + .filter(|hash| !blocks.iter().any(|b| b.hash() == *hash)), ); // Return a stream that lazily loads batches of blocks. debug!(logger, "Requesting {} block(s)", missing_blocks.len()); - let new_blocks = self + let new_blocks: Vec<_> = self .load_blocks_rpc(logger.clone(), missing_blocks) - .collect() - .compat() + .try_collect() .await?; let upsert_blocks: Vec<_> = new_blocks .iter() @@ -1736,7 +1654,7 @@ impl EthereumAdapterTrait for EthereumAdapter { error!(logger, "Error writing to block cache {}", e); } blocks.extend(new_blocks); - blocks.sort_by_key(|block| block.number); + blocks.sort_by_key(|block| block.number()); Ok(blocks) } } @@ -1779,7 +1697,7 @@ pub(crate) async fn blocks_with_triggers( debug!(logger, "Finding nearest valid `to` block to {}", to); let to_ptr = eth.next_existing_ptr_to_number(&logger, to).await?; - let to_hash = to_ptr.hash_as_h256(); + let to_hash = to_ptr.hash.as_b256(); let to = to_ptr.block_number(); // This is for `start` triggers which can be initialization handlers which needs to be run @@ -1856,8 +1774,10 @@ pub(crate) async fn blocks_with_triggers( .await .with_context(|| format!("Failed to obtain triggers for block {}", to))?; - let mut block_hashes: HashSet = - triggers.iter().map(EthereumTrigger::block_hash).collect(); + let mut block_hashes: HashSet = triggers + .iter() + .map(|trigger| trigger.block_hash()) + .collect(); let mut triggers_by_block: HashMap> = triggers.into_iter().fold(HashMap::new(), |mut map, t| { map.entry(t.block_number()).or_default().push(t); @@ -1877,7 +1797,7 @@ pub(crate) async fn blocks_with_triggers( .await? .into_iter() .map( - move |block| match triggers_by_block.remove(&(block.number() as BlockNumber)) { + move |block| match triggers_by_block.remove(&(block.number())) { Some(triggers) => Ok(BlockWithTriggers::new( BlockFinality::Final(block), triggers, @@ -1952,9 +1872,8 @@ pub(crate) async fn get_calls( .calls_in_block( &logger, subgraph_metrics.clone(), - BlockNumber::try_from(ethereum_block.block.number.unwrap().as_u64()) - .unwrap(), - ethereum_block.block.hash.unwrap(), + ethereum_block.block.number(), + ethereum_block.block.hash(), ) .await? }; @@ -1981,15 +1900,15 @@ pub(crate) fn parse_log_triggers( .transaction_receipts .iter() .flat_map(move |receipt| { - receipt.logs.iter().enumerate().map(move |(index, log)| { + receipt.logs().iter().enumerate().map(move |(index, log)| { let requires_transaction_receipt = log - .topics + .topics() .first() .map(|signature| { log_filter.requires_transaction_receipt( signature, - Some(&log.address), - &log.topics, + Some(&log.address()), + &log.topics(), ) }) .unwrap_or(false); @@ -2040,7 +1959,7 @@ pub(crate) fn parse_block_triggers( return vec![]; } - let block_ptr = BlockPtr::from(&block.ethereum_block); + let block_ptr = block.ethereum_block.block.block_ptr(); let trigger_every_block = block_filter.trigger_every_block; let call_filter = EthereumCallFilter::from(block_filter); let block_ptr2 = block_ptr.cheap_clone(); @@ -2110,9 +2029,9 @@ pub(crate) fn parse_block_triggers( async fn fetch_receipt_from_ethereum_client( eth: &EthereumAdapter, - transaction_hash: &H256, -) -> anyhow::Result { - match eth.web3.eth().transaction_receipt(*transaction_hash).await { + transaction_hash: B256, +) -> anyhow::Result { + match eth.alloy.get_transaction_receipt(transaction_hash).await { Ok(Some(receipt)) => Ok(receipt), Ok(None) => bail!("Could not find transaction receipt"), Err(error) => bail!("Failed to fetch transaction receipt: {}", error), @@ -2133,14 +2052,14 @@ async fn filter_call_triggers_from_unsuccessful_transactions( let initial_number_of_triggers = block.trigger_data.len(); // Get the transaction hash from each call trigger - let transaction_hashes: BTreeSet = block + let transaction_hashes: BTreeSet = block .trigger_data .iter() .filter_map(|trigger| match trigger.as_chain() { Some(EthereumTrigger::Call(call_trigger)) => Some(call_trigger.transaction_hash), _ => None, }) - .collect::>>() + .collect::>>() .ok_or(anyhow!( "failed to obtain transaction hash from call triggers" ))?; @@ -2151,12 +2070,13 @@ async fn filter_call_triggers_from_unsuccessful_transactions( } // And obtain all Transaction values for the calls in this block. - let transactions: Vec<&Transaction> = { + let transactions: Vec<&AnyTransaction> = { match &block.block { BlockFinality::Final(ref block) => block - .transactions + .transactions() + .ok_or_else(|| anyhow!("Block transactions not available"))? .iter() - .filter(|transaction| transaction_hashes.contains(&transaction.hash)) + .filter(|transaction| transaction_hashes.contains(&transaction.tx_hash())) .collect(), BlockFinality::NonFinal(_block_with_calls) => { unreachable!( @@ -2178,21 +2098,21 @@ async fn filter_call_triggers_from_unsuccessful_transactions( // We'll also need the receipts for those transactions. In this step we collect all receipts // we have in store for the current block. - let mut receipts = chain_store - .transaction_receipts_in_block(&block.ptr().hash_as_h256()) + let mut receipts: BTreeMap = chain_store + .transaction_receipts_in_block(&block.ptr().hash.as_b256()) .await? .into_iter() .map(|receipt| (receipt.transaction_hash, receipt)) - .collect::>(); + .collect::>(); // Do we have a receipt for each transaction under analysis? - let mut receipts_and_transactions: Vec<(&Transaction, LightTransactionReceipt)> = Vec::new(); - let mut transactions_without_receipt: Vec<&Transaction> = Vec::new(); + let mut receipts_and_transactions: Vec<(&AnyTransaction, LightTransactionReceipt)> = Vec::new(); + let mut transactions_without_receipt: Vec<&AnyTransaction> = Vec::new(); for transaction in transactions.iter() { - if let Some(receipt) = receipts.remove(&transaction.hash) { - receipts_and_transactions.push((transaction, receipt)); + if let Some(receipt) = receipts.remove(&transaction.tx_hash()) { + receipts_and_transactions.push((*transaction, receipt)); } else { - transactions_without_receipt.push(transaction); + transactions_without_receipt.push(*transaction); } } @@ -2200,7 +2120,7 @@ async fn filter_call_triggers_from_unsuccessful_transactions( let futures = transactions_without_receipt .iter() .map(|transaction| async move { - fetch_receipt_from_ethereum_client(eth, &transaction.hash) + fetch_receipt_from_ethereum_client(eth, transaction.tx_hash()) .await .map(|receipt| (transaction, receipt)) }); @@ -2215,12 +2135,9 @@ async fn filter_call_triggers_from_unsuccessful_transactions( // additional Ethereum API calls for future scans on this block. // With all transactions and receipts in hand, we can evaluate the success of each transaction - let mut transaction_success: BTreeMap<&H256, bool> = BTreeMap::new(); + let mut transaction_success: BTreeMap = BTreeMap::new(); for (transaction, receipt) in receipts_and_transactions.into_iter() { - transaction_success.insert( - &transaction.hash, - evaluate_transaction_status(receipt.status), - ); + transaction_success.insert(transaction.tx_hash(), receipt.status); } // Confidence check: Did we inspect the status of all transactions? @@ -2262,11 +2179,11 @@ async fn filter_call_triggers_from_unsuccessful_transactions( /// Deprecated. Wraps the [`fetch_transaction_receipts_in_batch`] in a retry loop. async fn fetch_transaction_receipts_in_batch_with_retry( - web3: Arc>, - hashes: Vec, - block_hash: H256, + alloy: Arc, + hashes: Vec, + block_hash: B256, logger: Logger, -) -> Result>, IngestorError> { +) -> Result>, IngestorError> { let retry_log_message = format!( "batch eth_getTransactionReceipt RPC call for block {:?}", block_hash @@ -2277,51 +2194,83 @@ async fn fetch_transaction_receipts_in_batch_with_retry( .no_logging() .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) .run(move || { - let web3 = web3.cheap_clone(); + let alloy = alloy.cheap_clone(); let hashes = hashes.clone(); let logger = logger.cheap_clone(); - fetch_transaction_receipts_in_batch(web3, hashes, block_hash, logger).boxed() + fetch_transaction_receipts_in_batch(alloy, hashes, block_hash, logger).boxed() }) .await .map_err(|_timeout| anyhow!(block_hash).into()) } -/// Deprecated. Attempts to fetch multiple transaction receipts in a batching contex. +/// Deprecated. Attempts to fetch multiple transaction receipts in a batching context. async fn fetch_transaction_receipts_in_batch( - web3: Arc>, - hashes: Vec, - block_hash: H256, + alloy: Arc, + hashes: Vec, + block_hash: B256, logger: Logger, -) -> Result>, IngestorError> { - let batching_web3 = Web3::new(Batch::new(web3.transport().clone())); - let eth = batching_web3.eth(); - let receipt_futures = hashes - .into_iter() - .map(move |hash| { - let logger = logger.cheap_clone(); - eth.transaction_receipt(hash) - .map_err(IngestorError::from) - .and_then(move |some_receipt| async move { - resolve_transaction_receipt(some_receipt, hash, block_hash, logger) - }) - }) - .collect::>(); +) -> Result>, IngestorError> { + // Use the batch method to get all receipts at once + let receipts = batch_get_transaction_receipts(alloy, hashes.clone()) + .await + .map_err(|e| { + IngestorError::Unknown(anyhow::anyhow!("Batch receipt fetch failed: {}", e)) + })?; + + let mut result = Vec::new(); + for receipt in receipts { + if let Some(receipt) = receipt { + // Validate the receipt before adding it + let validated_receipt = resolve_transaction_receipt( + Some(receipt), + hashes[0], + block_hash, + logger.cheap_clone(), + )?; + result.push(Arc::new(validated_receipt)); + } + } - batching_web3.transport().submit_batch().await?; + Ok(result) +} + +async fn batch_get_transaction_receipts( + provider: Arc, + tx_hashes: Vec, +) -> Result>, Box> { + let mut batch = alloy::rpc::client::BatchRequest::new(provider.client()); + let mut receipt_futures = Vec::new(); + + // Add all receipt requests to batch + for tx_hash in &tx_hashes { + let receipt_future = batch + .add_call::<(B256,), Option>( + "eth_getTransactionReceipt", + &(*tx_hash,), + )?; + receipt_futures.push(receipt_future); + } - let mut collected = vec![]; - for receipt in receipt_futures.into_iter() { - collected.push(Arc::new(receipt.await?)) + // Execute batch + batch.send().await?; + + // Collect results in order + let mut results = Vec::new(); + for receipt_future in receipt_futures { + let receipt = receipt_future.await.unwrap_or(None); + results.push(receipt); } - Ok(collected) + + Ok(results) } pub(crate) async fn check_block_receipt_support( - web3: Arc>, - block_hash: H256, + alloy: Arc, + block_hash: B256, supports_eip_1898: bool, call_only: bool, ) -> Result<(), Error> { + use alloy::rpc::types::BlockId; if call_only { return Err(anyhow!("Provider is call-only")); } @@ -2331,7 +2280,7 @@ pub(crate) async fn check_block_receipt_support( } // Fetch block receipts from the provider for the latest block. - let block_receipts_result = web3.eth().block_receipts(BlockId::Hash(block_hash)).await; + let block_receipts_result = alloy.get_block_receipts(BlockId::from(block_hash)).await; // Determine if the provider supports block receipts based on the fetched result. match block_receipts_result { @@ -2345,27 +2294,27 @@ pub(crate) async fn check_block_receipt_support( // based on whether block receipts are supported or individual transaction receipts // need to be fetched. async fn fetch_receipts_with_retry( - web3: Arc>, - hashes: Vec, - block_hash: H256, + alloy: Arc, + hashes: Vec, + block_hash: B256, logger: Logger, supports_block_receipts: bool, -) -> Result>, IngestorError> { +) -> Result>, IngestorError> { if supports_block_receipts { - return fetch_block_receipts_with_retry(web3, hashes, block_hash, logger).await; + return fetch_block_receipts_with_retry(alloy, hashes, block_hash, logger).await; } - fetch_individual_receipts_with_retry(web3, hashes, block_hash, logger).await + fetch_individual_receipts_with_retry(alloy, hashes, block_hash, logger).await } // Fetches receipts for each transaction in the block individually. async fn fetch_individual_receipts_with_retry( - web3: Arc>, - hashes: Vec, - block_hash: H256, + alloy: Arc, + hashes: Vec, + block_hash: B256, logger: Logger, -) -> Result>, IngestorError> { +) -> Result>, IngestorError> { if ENV_VARS.fetch_receipts_in_batches { - return fetch_transaction_receipts_in_batch_with_retry(web3, hashes, block_hash, logger) + return fetch_transaction_receipts_in_batch_with_retry(alloy, hashes, block_hash, logger) .await; } @@ -2374,7 +2323,7 @@ async fn fetch_individual_receipts_with_retry( let receipt_stream = hash_stream .map(move |tx_hash| { fetch_transaction_receipt_with_retry( - web3.cheap_clone(), + alloy.cheap_clone(), tx_hash, block_hash, logger.cheap_clone(), @@ -2382,19 +2331,20 @@ async fn fetch_individual_receipts_with_retry( }) .buffered(ENV_VARS.block_ingestor_max_concurrent_json_rpc_calls); - tokio_stream::StreamExt::collect::>, IngestorError>>( - receipt_stream, - ) + tokio_stream::StreamExt::collect::< + Result>, IngestorError>, + >(receipt_stream) .await } /// Fetches transaction receipts of all transactions in a block with `eth_getBlockReceipts` call. async fn fetch_block_receipts_with_retry( - web3: Arc>, - hashes: Vec, - block_hash: H256, + alloy: Arc, + hashes: Vec, + block_hash: B256, logger: Logger, -) -> Result>, IngestorError> { +) -> Result>, IngestorError> { + use graph::prelude::alloy::rpc::types::BlockId; let logger = logger.cheap_clone(); let retry_log_message = format!("eth_getBlockReceipts RPC call for block {:?}", block_hash); @@ -2403,7 +2353,7 @@ async fn fetch_block_receipts_with_retry( .redact_log_urls(true) .limit(ENV_VARS.request_retries) .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) - .run(move || web3.eth().block_receipts(BlockId::Hash(block_hash)).boxed()) + .run(move || alloy.get_block_receipts(BlockId::from(block_hash)).boxed()) .await .map_err(|_timeout| -> IngestorError { anyhow!(block_hash).into() })?; @@ -2432,23 +2382,27 @@ async fn fetch_block_receipts_with_retry( } } -/// Retries fetching a single transaction receipt. +/// Retries fetching a single transaction receipt using alloy, then converts to web3 format. async fn fetch_transaction_receipt_with_retry( - web3: Arc>, - transaction_hash: H256, - block_hash: H256, + alloy: Arc, + transaction_hash: B256, + block_hash: B256, logger: Logger, -) -> Result, IngestorError> { +) -> Result, IngestorError> { let logger = logger.cheap_clone(); let retry_log_message = format!( "eth_getTransactionReceipt RPC call for transaction {:?}", transaction_hash ); + retry(retry_log_message, &logger) .redact_log_urls(true) .limit(ENV_VARS.request_retries) .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) - .run(move || web3.eth().transaction_receipt(transaction_hash).boxed()) + .run(move || { + let alloy_clone = alloy.clone(); + async move { alloy_clone.get_transaction_receipt(transaction_hash).await }.boxed() + }) .await .map_err(|_timeout| anyhow!(block_hash).into()) .and_then(move |some_receipt| { @@ -2458,11 +2412,11 @@ async fn fetch_transaction_receipt_with_retry( } fn resolve_transaction_receipt( - transaction_receipt: Option, - transaction_hash: H256, - block_hash: H256, + transaction_receipt: Option, + transaction_hash: B256, + block_hash: B256, logger: Logger, -) -> Result { +) -> Result { match transaction_receipt { // A receipt might be missing because the block was uncled, and the transaction never // made it back into the main chain. @@ -2535,12 +2489,16 @@ async fn get_logs_and_transactions( // Not all logs have associated transaction hashes, nor do all triggers require them. // We also restrict receipts retrieval for some api versions. - let transaction_hashes_by_block: HashMap> = logs + let transaction_hashes_by_block: HashMap> = logs .iter() .filter(|_| unified_api_version.equal_or_greater_than(&API_VERSION_0_0_7)) .filter(|log| { - if let Some(signature) = log.topics.first() { - log_filter.requires_transaction_receipt(signature, Some(&log.address), &log.topics) + if let Some(signature) = log.topics().first() { + log_filter.requires_transaction_receipt( + signature, + Some(&log.address()), + &log.topics(), + ) } else { false } @@ -2555,7 +2513,7 @@ async fn get_logs_and_transactions( } }) .fold( - HashMap::>::new(), + HashMap::>::new(), |mut acc, (block_hash, txn_hash)| { acc.entry(block_hash).or_default().insert(txn_hash); acc @@ -2577,6 +2535,7 @@ async fn get_logs_and_transactions( let optional_receipt = log .transaction_hash .and_then(|txn| transaction_receipts_by_hash.get(&txn).cloned()); + let value = EthereumTrigger::Log(LogRef::FullLog(Arc::new(log), optional_receipt)); log_triggers.push(value); } @@ -2587,13 +2546,14 @@ async fn get_logs_and_transactions( /// Tries to retrive all transaction receipts for a set of transaction hashes. async fn get_transaction_receipts_for_transaction_hashes( adapter: &EthereumAdapter, - transaction_hashes_by_block: &HashMap>, + transaction_hashes_by_block: &HashMap>, subgraph_metrics: Arc, logger: Logger, -) -> Result>, anyhow::Error> { +) -> Result>, anyhow::Error> { use std::collections::hash_map::Entry::Vacant; - let mut receipts_by_hash: HashMap> = HashMap::new(); + let mut receipts_by_hash: HashMap> = + HashMap::new(); // Return early if input set is empty if transaction_hashes_by_block.is_empty() { @@ -2602,17 +2562,17 @@ async fn get_transaction_receipts_for_transaction_hashes( // Keep a record of all unique transaction hashes for which we'll request receipts. We will // later use this to check if we have collected the receipts from all required transactions. - let mut unique_transaction_hashes: HashSet<&H256> = HashSet::new(); + let mut unique_transaction_hashes: HashSet<&B256> = HashSet::new(); // Request transaction receipts concurrently let receipt_futures = FuturesUnordered::new(); - let web3 = Arc::clone(&adapter.web3); + let alloy = Arc::clone(&adapter.alloy); for (block_hash, transaction_hashes) in transaction_hashes_by_block { for transaction_hash in transaction_hashes { unique_transaction_hashes.insert(transaction_hash); let receipt_future = fetch_transaction_receipt_with_retry( - web3.cheap_clone(), + alloy.cheap_clone(), *transaction_hash, *block_hash, logger.cheap_clone(), @@ -2674,11 +2634,12 @@ mod tests { EthereumBlockWithCalls, }; use graph::blockchain::BlockPtr; - use graph::prelude::ethabi::ethereum_types::U64; - use graph::prelude::web3::transports::test::TestTransport; - use graph::prelude::web3::types::{Address, Block, Bytes, H256}; - use graph::prelude::web3::Web3; - use graph::prelude::EthereumCall; + use graph::components::ethereum::AnyBlock; + use graph::prelude::alloy::network::AnyNetwork; + use graph::prelude::alloy::primitives::{Address, Bytes, B256}; + use graph::prelude::alloy::providers::mock::Asserter; + use graph::prelude::alloy::providers::ProviderBuilder; + use graph::prelude::{create_minimal_block_for_test, EthereumCall, LightEthereumBlock}; use jsonrpc_core::serde_json::{self, Value}; use std::collections::HashSet; use std::iter::FromIterator; @@ -2686,13 +2647,11 @@ mod tests { #[test] fn parse_block_triggers_every_block() { + let block = create_minimal_block_for_test(2, hash(2)); + let block = EthereumBlockWithCalls { ethereum_block: EthereumBlock { - block: Arc::new(Block { - hash: Some(hash(2)), - number: Some(U64::from(2)), - ..Default::default() - }), + block: Arc::new(LightEthereumBlock::new(AnyBlock::from(block))), ..Default::default() }, calls: Some(vec![EthereumCall { @@ -2724,8 +2683,6 @@ mod tests { #[graph::test] async fn test_check_block_receipts_support() { - let mut transport = TestTransport::default(); - let json_receipts = r#"[{ "blockHash": "0x23f785604642e91613881fc3c9d16740ee416e340fd36f3fa2239f203d68fd33", "blockNumber": "0x12f7f81", @@ -2747,22 +2704,24 @@ mod tests { // Helper function to run a single test case async fn run_test_case( - transport: &mut TestTransport, json_response: &str, expected_err: Option<&str>, supports_eip_1898: bool, call_only: bool, ) -> Result<(), anyhow::Error> { let json_value: Value = serde_json::from_str(json_response).unwrap(); - // let block_json: Value = serde_json::from_str(block).unwrap(); - transport.set_response(json_value); - // transport.set_response(block_json); - // transport.add_response(json_value); - let web3 = Arc::new(Web3::new(transport.clone())); + let asserter = Asserter::new(); + let provider = ProviderBuilder::<_, _, AnyNetwork>::default() + .network::() + .with_recommended_fillers() + .connect_mocked_client(asserter.clone()); + + asserter.push_success(&json_value); + let result = check_block_receipt_support( - web3.clone(), - H256::zero(), + Arc::new(provider), + B256::ZERO, supports_eip_1898, call_only, ) @@ -2787,38 +2746,25 @@ mod tests { } // Test case 1: Valid block receipts - run_test_case(&mut transport, json_receipts, None, true, false) + run_test_case(json_receipts, None, true, false) .await .unwrap(); // Test case 2: Empty block receipts - run_test_case( - &mut transport, - json_empty, - Some("Block receipts are empty"), - true, - false, - ) - .await - .unwrap(); + run_test_case(json_empty, Some("Block receipts are empty"), true, false) + .await + .unwrap(); // Test case 3: Null response - run_test_case( - &mut transport, - "null", - Some("Block receipts are empty"), - true, - false, - ) - .await - .unwrap(); + run_test_case("null", Some("Block receipts are empty"), true, false) + .await + .unwrap(); // Test case 3: Simulating an RPC error // Note: In the context of this test, we cannot directly simulate an RPC error. // Instead, we simulate a response that would cause a decoding error, such as an unexpected key("error"). // The function should handle this as an error case. run_test_case( - &mut transport, r#"{"error":"RPC Error"}"#, Some("Error fetching block receipts:"), true, @@ -2829,7 +2775,6 @@ mod tests { // Test case 5: Does not support EIP-1898 run_test_case( - &mut transport, json_receipts, Some("Provider does not support EIP 1898"), false, @@ -2839,26 +2784,19 @@ mod tests { .unwrap(); // Test case 5: Does not support Call only adapters - run_test_case( - &mut transport, - json_receipts, - Some("Provider is call-only"), - true, - true, - ) - .await - .unwrap(); + run_test_case(json_receipts, Some("Provider is call-only"), true, true) + .await + .unwrap(); } #[test] fn parse_block_triggers_specific_call_not_found() { + let block = create_minimal_block_for_test(2, hash(2)); + + #[allow(unreachable_code)] let block = EthereumBlockWithCalls { ethereum_block: EthereumBlock { - block: Arc::new(Block { - hash: Some(hash(2)), - number: Some(U64::from(2)), - ..Default::default() - }), + block: Arc::new(LightEthereumBlock::new(AnyBlock::from(block))), ..Default::default() }, calls: Some(vec![EthereumCall { @@ -2884,13 +2822,12 @@ mod tests { #[test] fn parse_block_triggers_specific_call_found() { + let block = create_minimal_block_for_test(2, hash(2)); + + #[allow(unreachable_code)] let block = EthereumBlockWithCalls { ethereum_block: EthereumBlock { - block: Arc::new(Block { - hash: Some(hash(2)), - number: Some(U64::from(2)), - ..Default::default() - }), + block: Arc::new(LightEthereumBlock::new(AnyBlock::from(block))), ..Default::default() }, calls: Some(vec![EthereumCall { @@ -2918,11 +2855,11 @@ mod tests { } fn address(id: u64) -> Address { - Address::from_low_u64_be(id) + Address::left_padding_from(&id.to_be_bytes()) } - fn hash(id: u8) -> H256 { - H256::from([id; 32]) + fn hash(id: u8) -> B256 { + B256::from_slice(&[id; 32]) } fn bytes(value: Vec) -> Bytes { diff --git a/chain/ethereum/src/ingestor.rs b/chain/ethereum/src/ingestor.rs index 47cae0b93c5..35676c8c90f 100644 --- a/chain/ethereum/src/ingestor.rs +++ b/chain/ethereum/src/ingestor.rs @@ -4,14 +4,14 @@ use async_trait::async_trait; use graph::blockchain::client::ChainClient; use graph::blockchain::BlockchainKind; use graph::components::network_provider::ChainName; +use graph::prelude::alloy::primitives::B256; use graph::slog::o; use graph::util::backoff::ExponentialBackoff; use graph::{ blockchain::{BlockHash, BlockIngestor, BlockPtr, IngestorError}, cheap_clone::CheapClone, prelude::{ - error, ethabi::ethereum_types::H256, info, tokio, trace, warn, ChainStore, Error, - EthereumBlockWithCalls, LogCode, Logger, + error, info, tokio, trace, warn, ChainStore, Error, EthereumBlockWithCalls, LogCode, Logger, }, }; use std::{sync::Arc, time::Duration}; @@ -173,8 +173,7 @@ impl PollingBlockIngestor { eth_adapter: &Arc, block_hash: &BlockHash, ) -> Result, IngestorError> { - // TODO: H256::from_slice can panic - let block_hash = H256::from_slice(block_hash.as_slice()); + let block_hash = B256::from_slice(block_hash.as_slice()); // Get the fully populated block let block = eth_adapter @@ -211,10 +210,7 @@ impl PollingBlockIngestor { logger: &Logger, eth_adapter: &Arc, ) -> Result { - eth_adapter - .latest_block_header(&logger) - .await - .map(|block| block.into()) + eth_adapter.latest_block_ptr(&logger).await } async fn eth_adapter(&self) -> anyhow::Result> { diff --git a/chain/ethereum/src/lib.rs b/chain/ethereum/src/lib.rs index fa76f70d799..8850764d63b 100644 --- a/chain/ethereum/src/lib.rs +++ b/chain/ethereum/src/lib.rs @@ -1,5 +1,6 @@ mod adapter; mod buffered_call_cache; +mod call_helper; mod capabilities; pub mod codec; mod data_source; diff --git a/chain/ethereum/src/protobuf/sf.ethereum.r#type.v2.rs b/chain/ethereum/src/protobuf/sf.ethereum.r#type.v2.rs index 4ab8d0a1324..bcb068083df 100644 --- a/chain/ethereum/src/protobuf/sf.ethereum.r#type.v2.rs +++ b/chain/ethereum/src/protobuf/sf.ethereum.r#type.v2.rs @@ -1,14 +1,60 @@ // This file is @generated by prost-build. +/// Block is the representation of the tracing of a block in the Ethereum +/// blockchain. A block is a collection of \[TransactionTrace\] that are grouped +/// together and processed as an atomic unit. Each \[TransactionTrace\] is composed +/// of a series of \[Call\] (a.k.a internal transactions) and there is also at +/// least one call per transaction a.k.a the root call which essentially has the +/// same parameters as the transaction itself (e.g. `from`, `to`, `gas`, `value`, +/// etc.). +/// +/// The exact tracing method used to build the block must be checked against +/// \[DetailLevel\] field. There is two levels of details available, `BASE` and +/// `EXTENDED`. The `BASE` level has been extracted using archive node RPC calls +/// and will contain only the block header, transaction receipts and event logs. +/// Refers to the Firehose service provider to know which blocks are offered on +/// each network. +/// +/// The `EXTENDED` level has been extracted using the Firehose tracer and all +/// fields are available in this Protobuf. +/// +/// The Ethereum block model is used across many chains which means that it +/// happen that certain fields are not available in one chain but are available +/// in another. Each field should be documented when necesssary if it's available +/// on a subset of chains. +/// +/// One major concept to get about the Block is the concept of 'ordinal'. The +/// ordinal is a number that is used to globally order every element of execution +/// that happened throughout the processing of the block like +/// \[TransactionTracer\], \[Call\], \[Log\], \[BalanceChange\], \[StateChange\], etc. +/// Element that have a start and end interval, \[Transaction\] and \[Call\], will +/// have two ordinals: `begin_ordinal` and `end_ordinal`. Element that are +/// executed as "point in time" \[Log\], \[BalanceChange\], \[StateChange\], etc. will +/// have only one ordinal named `ordinal`. If you take all of the message in the +/// Block that have an 'ordinal' field in an array and you sort each element +/// against the `ordinal` field, you will get the exact order of execution of +/// each element in the block. +/// +/// All the 'ordinal' fields in a block are globally unique for the given block, +/// it is **not** a chain-wide global ordering. Furthermore, caution must be take +/// with reverted elements due to execution failure. For anything attached to a +/// \[Call\] that has a `state_reverted` field set to `true`, the `ordinal` field +/// is not reliable and should not be used to order the element against other +/// elements in the block as those element might have 0 as the ordinal. Only +/// successful calls have a reliable `ordinal` field. #[derive(Clone, PartialEq, ::prost::Message)] pub struct Block { - #[prost(int32, tag = "1")] - pub ver: i32, + /// Hash is the block's hash. #[prost(bytes = "vec", tag = "2")] pub hash: ::prost::alloc::vec::Vec, + /// Number is the block's height at which this block was mined. #[prost(uint64, tag = "3")] pub number: u64, + /// Size is the size in bytes of the RLP encoding of the block according to Ethereum + /// rules. #[prost(uint64, tag = "4")] pub size: u64, + /// Header contain's the block's header information like its parent hash, the merkel root hash + /// and all other information the form a block. #[prost(message, optional, tag = "5")] pub header: ::core::option::Option, /// Uncles represents block produced with a valid solution but were not actually chosen @@ -18,54 +64,110 @@ pub struct Block { /// field will actually be always empty. #[prost(message, repeated, tag = "6")] pub uncles: ::prost::alloc::vec::Vec, + /// TransactionTraces hold the execute trace of all the transactions that were executed + /// in this block. In in there that you will find most of the Ethereum data model. + /// + /// They are ordered by the order of execution of the transaction in the block. #[prost(message, repeated, tag = "10")] pub transaction_traces: ::prost::alloc::vec::Vec, + /// BalanceChanges here is the array of ETH transfer that happened at the block level + /// outside of the normal transaction flow of a block. The best example of this is mining + /// reward for the block mined, the transfer of ETH to the miner happens outside the normal + /// transaction flow of the chain and is recorded as a `BalanceChange` here since we cannot + /// attached it to any transaction. + /// + /// Only available in DetailLevel: EXTENDED #[prost(message, repeated, tag = "11")] pub balance_changes: ::prost::alloc::vec::Vec, + /// DetailLevel affects the data available in this block. + /// + /// ## DetailLevel_EXTENDED + /// + /// Describes the most complete block, with traces, balance changes, storage + /// changes. It is extracted during the execution of the block. + /// + /// ## DetailLevel_BASE + /// + /// Describes a block that contains only the block header, transaction receipts + /// and event logs: everything that can be extracted using the base JSON-RPC + /// interface + /// () + /// Furthermore, the eth_getTransactionReceipt call has been avoided because it + /// brings only minimal improvements at the cost of requiring an archive node + /// or a full node with complete transaction index. + #[prost(enumeration = "block::DetailLevel", tag = "12")] + pub detail_level: i32, + /// CodeChanges here is the array of smart code change that happened that happened at the block level + /// outside of the normal transaction flow of a block. Some Ethereum's fork like BSC and Polygon + /// has some capabilities to upgrade internal smart contracts used usually to track the validator + /// list. + /// + /// On hard fork, some procedure runs to upgrade the smart contract code to a new version. In those + /// network, a `CodeChange` for each modified smart contract on upgrade would be present here. Note + /// that this happen rarely, so the vast majority of block will have an empty list here. + /// + /// Only available in DetailLevel: EXTENDED #[prost(message, repeated, tag = "20")] pub code_changes: ::prost::alloc::vec::Vec, + /// System calls are introduced in Cancun, along with blobs. They are executed outside of transactions but affect the state. + /// + /// Only available in DetailLevel: EXTENDED + #[prost(message, repeated, tag = "21")] + pub system_calls: ::prost::alloc::vec::Vec, + /// Withdrawals represents the list of validator balance withdrawals processed in this block. + /// Introduced in the Shanghai hard fork (EIP-4895). + /// + /// This field has been added because Geth blocks include withdrawals after Shanghai fork, + /// but our previous Firehose model didn't capture this data. Currently experimental - + /// NOT ready for production use yet as we validate the tracing implementation. + /// + /// Only available when Shanghai fork is active on the chain. + #[prost(message, repeated, tag = "22")] + pub withdrawals: ::prost::alloc::vec::Vec, + /// Ver represents that data model version of the block, it is used internally by Firehose on Ethereum + /// as a validation that we are reading the correct version. + #[prost(int32, tag = "1")] + pub ver: i32, } -/// HeaderOnlyBlock is used to optimally unpack the \[Block\] structure (note the -/// corresponding message number for the `header` field) while consuming less -/// memory, when only the `header` is desired. -/// -/// WARN: this is a client-side optimization pattern and should be moved in the -/// consuming code. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct HeaderOnlyBlock { - #[prost(message, optional, tag = "5")] - pub header: ::core::option::Option, -} -/// BlockWithRefs is a lightweight block, with traces and transactions -/// purged from the `block` within, and only. It is used in transports -/// to pass block data around. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockWithRefs { - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - #[prost(message, optional, tag = "2")] - pub block: ::core::option::Option, - #[prost(message, optional, tag = "3")] - pub transaction_trace_refs: ::core::option::Option, - #[prost(bool, tag = "4")] - pub irreversible: bool, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TransactionRefs { - #[prost(bytes = "vec", repeated, tag = "1")] - pub hashes: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct UnclesHeaders { - #[prost(message, repeated, tag = "1")] - pub uncles: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockRef { - #[prost(bytes = "vec", tag = "1")] - pub hash: ::prost::alloc::vec::Vec, - #[prost(uint64, tag = "2")] - pub number: u64, +/// Nested message and enum types in `Block`. +pub mod block { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum DetailLevel { + DetaillevelExtended = 0, + /// DETAILLEVEL_TRACE = 1; // TBD + DetaillevelBase = 2, + } + impl DetailLevel { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::DetaillevelExtended => "DETAILLEVEL_EXTENDED", + Self::DetaillevelBase => "DETAILLEVEL_BASE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DETAILLEVEL_EXTENDED" => Some(Self::DetaillevelExtended), + "DETAILLEVEL_BASE" => Some(Self::DetaillevelBase), + _ => None, + } + } + } } #[derive(Clone, PartialEq, ::prost::Message)] pub struct BlockHeader { @@ -94,12 +196,10 @@ pub struct BlockHeader { /// consensus algorithm, this field will actually be constant and set to `0x00`. #[prost(message, optional, tag = "8")] pub difficulty: ::core::option::Option, - /// TotalDifficulty is the sum of all previous blocks difficulty including this block difficulty. + /// TotalDifficulty used to be the sum of all previous blocks difficulty including this block difficulty. /// - /// If the Block containing this `BlockHeader` has been produced using the Proof of Stake - /// consensus algorithm, this field will actually be constant and set to the terminal total difficulty - /// that was required to transition to Proof of Stake algorithm, which varies per network. It is set to - /// 58 750 000 000 000 000 000 000 on Ethereum Mainnet and to 10 790 000 on Ethereum Testnet Goerli. + /// It has been deprecated in geth v1.15.0 but was already removed from the JSON-RPC interface for a while + #[deprecated] #[prost(message, optional, tag = "17")] pub total_difficulty: ::core::option::Option, #[prost(uint64, tag = "9")] @@ -147,7 +247,12 @@ pub struct BlockHeader { /// extra_data, /// mix_hash, /// nonce, - /// base_fee_per_gas + /// base_fee_per_gas (to be included only if London fork is active) + /// withdrawals_root (to be included only if Shangai fork is active) + /// blob_gas_used (to be included only if Cancun fork is active) + /// excess_blob_gas (to be included only if Cancun fork is active) + /// parent_beacon_root (to be included only if Cancun fork is active) + /// requests_hash (to be included only if Prague fork is active) /// ])) /// #[prost(bytes = "vec", tag = "16")] @@ -155,12 +260,74 @@ pub struct BlockHeader { /// Base fee per gas according to EIP-1559 (e.g. London Fork) rules, only set if London is present/active on the chain. #[prost(message, optional, tag = "18")] pub base_fee_per_gas: ::core::option::Option, + /// Withdrawals root hash according to EIP-4895 (e.g. Shangai Fork) rules, only set if Shangai is present/active on the chain. + /// + /// Only available in DetailLevel: EXTENDED + #[prost(bytes = "vec", tag = "19")] + pub withdrawals_root: ::prost::alloc::vec::Vec, + /// TxDependency is list of transaction indexes that are dependent on each other in the block + /// header. This is metadata only that was used by the internal Polygon parallel execution engine. + /// + /// This field was available in a few versions on Polygon Mainnet and Polygon Mumbai chains. It was actually + /// removed and is not populated anymore. It's now embedded in the `extraData` field, refer to Polygon source + /// code to determine how to extract it if you need it. + /// + /// Only available in DetailLevel: EXTENDED + #[prost(message, optional, tag = "20")] + pub tx_dependency: ::core::option::Option, + /// BlobGasUsed was added by EIP-4844 and is ignored in legacy headers. + #[prost(uint64, optional, tag = "22")] + pub blob_gas_used: ::core::option::Option, + /// ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers. + #[prost(uint64, optional, tag = "23")] + pub excess_blob_gas: ::core::option::Option, + /// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. + #[prost(bytes = "vec", tag = "24")] + pub parent_beacon_root: ::prost::alloc::vec::Vec, + /// RequestsHash was added by EIP-7685 and is ignored in legacy headers. + #[prost(bytes = "vec", tag = "25")] + pub requests_hash: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Uint64NestedArray { + #[prost(message, repeated, tag = "1")] + pub val: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Uint64Array { + #[prost(uint64, repeated, tag = "1")] + pub val: ::prost::alloc::vec::Vec, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct BigInt { #[prost(bytes = "vec", tag = "1")] pub bytes: ::prost::alloc::vec::Vec, } +/// TransactionTrace is full trace of execution of the transaction when the +/// it actually executed on chain. +/// +/// It contains all the transaction details like `from`, `to`, `gas`, etc. +/// as well as all the internal calls that were made during the transaction. +/// +/// The `calls` vector contains Call objects which have balance changes, events +/// storage changes, etc. +/// +/// If ordering is important between elements, almost each message like `Log`, +/// `Call`, `StorageChange`, etc. have an ordinal field that is represents "execution" +/// order of the said element against all other elements in this block. +/// +/// Due to how the call tree works doing "naively", looping through all calls then +/// through a Call's element like `logs` while not yielding the elements in the order +/// they were executed on chain. A log in call could have been done before or after +/// another in another call depending on the actual call tree. +/// +/// The `calls` are ordered by creation order and the call tree can be re-computing +/// using fields found in `Call` object (parent/child relationship). +/// +/// Another important thing to note is that even if a transaction succeed, some calls +/// within it could have been reverted internally, if this is important to you, you must +/// check the field `state_reverted` on the `Call` to determine if it was fully committed +/// to the chain or not. #[derive(Clone, PartialEq, ::prost::Message)] pub struct TransactionTrace { /// consensus @@ -205,7 +372,7 @@ pub struct TransactionTrace { /// The value is always set even for transaction before Berlin fork because those before the fork are still legacy transactions. #[prost(enumeration = "transaction_trace::Type", tag = "12")] pub r#type: i32, - /// AcccessList represents the storage access this transaction has agreed to do in which case those storage + /// AccessList represents the storage access this transaction has agreed to do in which case those storage /// access cost less gas unit per access. /// /// This will is populated only if `TransactionTrace.Type == TRX_TYPE_ACCESS_LIST || TRX_TYPE_DYNAMIC_FEE` which @@ -216,6 +383,8 @@ pub struct TransactionTrace { /// /// This will is populated only if `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE` which is possible only /// if London fork is active on the chain. + /// + /// Only available in DetailLevel: EXTENDED #[prost(message, optional, tag = "11")] pub max_fee_per_gas: ::core::option::Option, /// MaxPriorityFeePerGas is priority fee per gas the user to pay in extra to the miner on top of the block's @@ -223,6 +392,8 @@ pub struct TransactionTrace { /// /// This will is populated only if `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE` which is possible only /// if London fork is active on the chain. + /// + /// Only available in DetailLevel: EXTENDED #[prost(message, optional, tag = "13")] pub max_priority_fee_per_gas: ::core::option::Option, /// meta @@ -232,20 +403,97 @@ pub struct TransactionTrace { pub hash: ::prost::alloc::vec::Vec, #[prost(bytes = "vec", tag = "22")] pub from: ::prost::alloc::vec::Vec, + /// Only available in DetailLevel: EXTENDED + /// Known Issues + /// - Version 3: + /// Field not populated. It will be empty. + /// + /// Fixed in `Version 4`, see for information about block versions. #[prost(bytes = "vec", tag = "23")] pub return_data: ::prost::alloc::vec::Vec, + /// Only available in DetailLevel: EXTENDED #[prost(bytes = "vec", tag = "24")] pub public_key: ::prost::alloc::vec::Vec, + /// The block's global ordinal when the transaction started executing, refer to + /// \[Block\] documentation for further information about ordinals and total ordering. #[prost(uint64, tag = "25")] pub begin_ordinal: u64, + /// The block's global ordinal when the transaction finished executing, refer to + /// \[Block\] documentation for further information about ordinals and total ordering. #[prost(uint64, tag = "26")] pub end_ordinal: u64, + /// TransactionTraceStatus is the status of the transaction execution and will let you know if the transaction + /// was successful or not. + /// + /// ## Explanation relevant only for blocks with `DetailLevel: EXTENDED` + /// + /// A successful transaction has been recorded to the blockchain's state for calls in it that were successful. + /// This means it's possible only a subset of the calls were properly recorded, refer to \[calls[\].state_reverted] field + /// to determine which calls were reverted. + /// + /// A quirks of the Ethereum protocol is that a transaction `FAILED` or `REVERTED` still affects the blockchain's + /// state for **some** of the state changes. Indeed, in those cases, the transactions fees are still paid to the miner + /// which means there is a balance change for the transaction's emitter (e.g. `from`) to pay the gas fees, an optional + /// balance change for gas refunded to the transaction's emitter (e.g. `from`) and a balance change for the miner who + /// received the transaction fees. There is also a nonce change for the transaction's emitter (e.g. `from`). + /// + /// This means that to properly record the state changes for a transaction, you need to conditionally procees the + /// transaction's status. + /// + /// For a `SUCCEEDED` transaction, you iterate over the `calls` array and record the state changes for each call for + /// which `state_reverted == false` (if a transaction succeeded, the call at #0 will always `state_reverted == false` + /// because it aligns with the transaction). + /// + /// For a `FAILED` or `REVERTED` transaction, you iterate over the root call (e.g. at #0, will always exist) for + /// balance changes you process those where `reason` is either `REASON_GAS_BUY`, `REASON_GAS_REFUND` or + /// `REASON_REWARD_TRANSACTION_FEE` and for nonce change, still on the root call, you pick the nonce change which the + /// smallest ordinal (if more than one). #[prost(enumeration = "TransactionTraceStatus", tag = "30")] pub status: i32, #[prost(message, optional, tag = "31")] pub receipt: ::core::option::Option, + /// Only available in DetailLevel: EXTENDED #[prost(message, repeated, tag = "32")] pub calls: ::prost::alloc::vec::Vec, + /// BlobGas is the amount of gas the transaction is going to pay for the blobs, this is a computed value + /// equivalent to `self.blob_gas_fee_cap * len(self.blob_hashes)` and provided in the model for convenience. + /// + /// This is specified by + /// + /// This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + /// if Cancun fork is active on the chain. + #[prost(uint64, optional, tag = "33")] + pub blob_gas: ::core::option::Option, + /// BlobGasFeeCap is the maximum fee per data gas the user is willing to pay for the data gas used. + /// + /// This is specified by + /// + /// This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + /// if Cancun fork is active on the chain. + #[prost(message, optional, tag = "34")] + pub blob_gas_fee_cap: ::core::option::Option, + /// BlobHashes field represents a list of hash outputs from 'kzg_to_versioned_hash' which + /// essentially is a version byte + the sha256 hash of the blob commitment (e.g. + /// `BLOB_COMMITMENT_VERSION_KZG + sha256(commitment)\[1:\]`. + /// + /// This is specified by + /// + /// This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + /// if Cancun fork is active on the chain. + #[prost(bytes = "vec", repeated, tag = "35")] + pub blob_hashes: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// SetCodeAuthorizations represents the authorizations of a transaction to set code to an EOA (Externally Owned Accounts) + /// as defined in EIP-7702. The list will contain all the authorizations as they were specified in the + /// transaction itself regardless of their validity. If you need to determined if a given authorization was + /// correctly applied on chain's state, refer to \[SetCodeAuthorization.discarded\] field that records + /// if the authorization was discarded or not by the chain due to invalidity. + /// + /// This is specified by + /// + /// This will is populated only if `TransactionTrace.Type == TRX_TYPE_SET_CODE` which is possible only + /// if Prague fork is active on the chain. + #[prost(message, repeated, tag = "36")] + pub set_code_authorizations: ::prost::alloc::vec::Vec, } /// Nested message and enum types in `TransactionTrace`. pub mod transaction_trace { @@ -264,15 +512,38 @@ pub mod transaction_trace { pub enum Type { /// All transactions that ever existed prior Berlin fork before EIP-2718 was implemented. TrxTypeLegacy = 0, - /// Field that specifies an access list of contract/storage_keys that is going to be used + /// Transaction that specicy an access list of contract/storage_keys that is going to be used /// in this transaction. /// /// Added in Berlin fork (EIP-2930). TrxTypeAccessList = 1, - /// Transaction that specifies an access list just like TRX_TYPE_ACCESS_LIST but in addition defines the + /// Transaction that specifis an access list just like TRX_TYPE_ACCESS_LIST but in addition defines the /// max base gas gee and max priority gas fee to pay for this transaction. Transaction's of those type are /// executed against EIP-1559 rules which dictates a dynamic gas cost based on the congestion of the network. TrxTypeDynamicFee = 2, + /// Transaction which contain a large amount of data that cannot be accessed by EVM execution, but whose commitment + /// can be accessed. The format is intended to be fully compatible with the format that will be used in full sharding. + /// + /// Transaction that defines an access list just like TRX_TYPE_ACCESS_LIST and enables dynamic fee just like + /// TRX_TYPE_DYNAMIC_FEE but in addition defines the fields 'max_fee_per_data_gas' of type 'uint256' and the fields + /// 'blob_versioned_hashes' which represents a list of hash outputs from 'kzg_to_versioned_hash'. + /// + /// Activated in Cancun fork (EIP-4844) + TrxTypeBlob = 3, + /// Transaction that sets code to an EOA (Externally Owned Accounts) + /// + /// Activated in Prague (EIP-7702) + TrxTypeSetCode = 4, + /// Arbitrum-specific transactions + TrxTypeArbitrumDeposit = 100, + TrxTypeArbitrumUnsigned = 101, + TrxTypeArbitrumContract = 102, + TrxTypeArbitrumRetry = 104, + TrxTypeArbitrumSubmitRetryable = 105, + TrxTypeArbitrumInternal = 106, + TrxTypeArbitrumLegacy = 120, + /// OPTIMISM-specific transactions + TrxTypeOptimismDeposit = 126, } impl Type { /// String value of the enum field names used in the ProtoBuf definition. @@ -284,6 +555,18 @@ pub mod transaction_trace { Self::TrxTypeLegacy => "TRX_TYPE_LEGACY", Self::TrxTypeAccessList => "TRX_TYPE_ACCESS_LIST", Self::TrxTypeDynamicFee => "TRX_TYPE_DYNAMIC_FEE", + Self::TrxTypeBlob => "TRX_TYPE_BLOB", + Self::TrxTypeSetCode => "TRX_TYPE_SET_CODE", + Self::TrxTypeArbitrumDeposit => "TRX_TYPE_ARBITRUM_DEPOSIT", + Self::TrxTypeArbitrumUnsigned => "TRX_TYPE_ARBITRUM_UNSIGNED", + Self::TrxTypeArbitrumContract => "TRX_TYPE_ARBITRUM_CONTRACT", + Self::TrxTypeArbitrumRetry => "TRX_TYPE_ARBITRUM_RETRY", + Self::TrxTypeArbitrumSubmitRetryable => { + "TRX_TYPE_ARBITRUM_SUBMIT_RETRYABLE" + } + Self::TrxTypeArbitrumInternal => "TRX_TYPE_ARBITRUM_INTERNAL", + Self::TrxTypeArbitrumLegacy => "TRX_TYPE_ARBITRUM_LEGACY", + Self::TrxTypeOptimismDeposit => "TRX_TYPE_OPTIMISM_DEPOSIT", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -292,6 +575,18 @@ pub mod transaction_trace { "TRX_TYPE_LEGACY" => Some(Self::TrxTypeLegacy), "TRX_TYPE_ACCESS_LIST" => Some(Self::TrxTypeAccessList), "TRX_TYPE_DYNAMIC_FEE" => Some(Self::TrxTypeDynamicFee), + "TRX_TYPE_BLOB" => Some(Self::TrxTypeBlob), + "TRX_TYPE_SET_CODE" => Some(Self::TrxTypeSetCode), + "TRX_TYPE_ARBITRUM_DEPOSIT" => Some(Self::TrxTypeArbitrumDeposit), + "TRX_TYPE_ARBITRUM_UNSIGNED" => Some(Self::TrxTypeArbitrumUnsigned), + "TRX_TYPE_ARBITRUM_CONTRACT" => Some(Self::TrxTypeArbitrumContract), + "TRX_TYPE_ARBITRUM_RETRY" => Some(Self::TrxTypeArbitrumRetry), + "TRX_TYPE_ARBITRUM_SUBMIT_RETRYABLE" => { + Some(Self::TrxTypeArbitrumSubmitRetryable) + } + "TRX_TYPE_ARBITRUM_INTERNAL" => Some(Self::TrxTypeArbitrumInternal), + "TRX_TYPE_ARBITRUM_LEGACY" => Some(Self::TrxTypeArbitrumLegacy), + "TRX_TYPE_OPTIMISM_DEPOSIT" => Some(Self::TrxTypeOptimismDeposit), _ => None, } } @@ -306,22 +601,73 @@ pub struct AccessTuple { #[prost(bytes = "vec", repeated, tag = "2")] pub storage_keys: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, } -/// TransactionTraceWithBlockRef +/// SetCodeAuthorization represents the authorization of a transaction to set code of an EOA (Externally Owned Account) +/// as defined in EIP-7702. +/// +/// The 'authority' field is the address that is authorizing the delegation mechanism. The 'authority' value is computed +/// from the signature contained in the message using the computation +/// `authority = ecrecover(keccak(MAGIC || rlp(\[chain_id, address, nonce\])), y_parity, r, s)` +/// where `MAGIC` is `0x5`, `||` is the bytes concatenation operator, `ecrecover` is the Ethereum signature recovery +/// and `y_parity` is the recovery ID value denoted `v` in the message below. Checking the go-ethereum implementation +/// at might prove easier to "read". +/// +/// We do extract the 'authority' value from the signature in the message and store it in the 'authority' field for +/// convenience so you don't need to perform the computation yourself. #[derive(Clone, PartialEq, ::prost::Message)] -pub struct TransactionTraceWithBlockRef { - #[prost(message, optional, tag = "1")] - pub trace: ::core::option::Option, - #[prost(message, optional, tag = "2")] - pub block_ref: ::core::option::Option, +pub struct SetCodeAuthorization { + /// Discarded determines if this authorization was skipped due to being invalid. As EIP-7702 states, + /// if the authorization is invalid (invalid signature, nonce mismatch, etc.) it must be simply + /// discarded and the transaction is processed as if the authorization was not present in the + /// authorization list. + /// + /// This boolean records if the authorization was discarded or not by the chain due to invalidity. + #[prost(bool, tag = "1")] + pub discarded: bool, + /// ChainID is the chain ID of the chain where the transaction was executed, used + /// to recover the authority from the signature. + #[prost(bytes = "vec", tag = "2")] + pub chain_id: ::prost::alloc::vec::Vec, + /// Address contains the address this account is delegating to. This address usually + /// contain code that this account essentially "delegates" to. + /// + /// Note: This was missing when EIP-7702 was first activated on Holesky, Sepolia, BSC Chapel, + /// BSC Mainnet and Arbitrum Sepolia but was ready for Ethereum Mainnet hard fork. We will backfill + /// those missing values in the near future at which point we will remove this note. + #[prost(bytes = "vec", tag = "8")] + pub address: ::prost::alloc::vec::Vec, + /// Nonce is the nonce of the account that is authorizing delegation mechanism, EIP-7702 rules + /// states that nonce should be verified using this rule: + /// + /// - Verify the nonce of authority is equal to nonce. In case authority does not exist in the trie, + /// verify that nonce is equal to 0. + /// + /// Read SetCodeAuthorization to know how to recover the `authority` value. + #[prost(uint64, tag = "3")] + pub nonce: u64, + /// V is the recovery ID value for the signature Y point. While it's defined as a + /// `uint32`, it's actually bounded by a `uint8` data type withing the Ethereum protocol. + #[prost(uint32, tag = "4")] + pub v: u32, + /// R is the signature's X point on the elliptic curve (32 bytes). + #[prost(bytes = "vec", tag = "5")] + pub r: ::prost::alloc::vec::Vec, + /// S is the signature's Y point on the elliptic curve (32 bytes). + #[prost(bytes = "vec", tag = "6")] + pub s: ::prost::alloc::vec::Vec, + /// Authority is the address of the account that is authorizing delegation mechanism, it + /// is computed from the signature contained in the message and stored for convenience. + /// + /// If the authority cannot be recovered from the signature, this field will be empty and + /// the `discarded` field will be set to `true`. + #[prost(bytes = "vec", optional, tag = "7")] + pub authority: ::core::option::Option<::prost::alloc::vec::Vec>, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct TransactionReceipt { /// State root is an intermediate state_root hash, computed in-between transactions to make /// **sure** you could build a proof and point to state in the middle of a block. Geth client /// uses `PostState + root + PostStateOrStatus`` while Parity used `status_code, root...`` this piles - /// hardforks, see (read the EIPs first): - /// - - /// - + /// hard forks, see (read the EIPs first): /// - /// /// Moreover, the notion of `Outcome`` in parity, which segregates the two concepts, which are @@ -337,6 +683,23 @@ pub struct TransactionReceipt { pub logs_bloom: ::prost::alloc::vec::Vec, #[prost(message, repeated, tag = "4")] pub logs: ::prost::alloc::vec::Vec, + /// BlobGasUsed is the amount of blob gas that has been used within this transaction. At time + /// of writing, this is equal to `self.blob_gas_fee_cap * len(self.blob_hashes)`. + /// + /// This is specified by + /// + /// This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + /// if Cancun fork is active on the chain. + #[prost(uint64, optional, tag = "5")] + pub blob_gas_used: ::core::option::Option, + /// BlobGasPrice is the amount to pay per blob item in the transaction. + /// + /// This is specified by + /// + /// This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + /// if Cancun fork is active on the chain. + #[prost(message, optional, tag = "6")] + pub blob_gas_price: ::core::option::Option, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct Log { @@ -347,8 +710,10 @@ pub struct Log { #[prost(bytes = "vec", tag = "3")] pub data: ::prost::alloc::vec::Vec, /// Index is the index of the log relative to the transaction. This index - /// is always populated regardless of the state reversion of the call + /// is always populated regardless of the state reversion of the the call /// that emitted this log. + /// + /// Only available in DetailLevel: EXTENDED #[prost(uint32, tag = "4")] pub index: u32, /// BlockIndex represents the index of the log relative to the Block. @@ -356,7 +721,7 @@ pub struct Log { /// An **important** notice is that this field will be 0 when the call /// that emitted the log has been reverted by the chain. /// - /// Currently, there are two locations where a Log can be obtained: + /// Currently, there is two locations where a Log can be obtained: /// - block.transaction_traces\[\].receipt.logs\[\] /// - block.transaction_traces\[\].calls\[\].logs\[\] /// @@ -368,6 +733,8 @@ pub struct Log { /// the `blockIndex` value will always be 0. #[prost(uint32, tag = "6")] pub block_index: u32, + /// The block's global ordinal when the log was recorded, refer to \[Block\] + /// documentation for further information about ordinals and total ordering. #[prost(uint64, tag = "7")] pub ordinal: u64, } @@ -385,6 +752,25 @@ pub struct Call { pub caller: ::prost::alloc::vec::Vec, #[prost(bytes = "vec", tag = "6")] pub address: ::prost::alloc::vec::Vec, + /// AddressDelegatesTo contains the address from which the actual code to execute will be loaded + /// as defined per EIP-7702 rules. If the Call's address value resolves to a code + /// that delegates to another address, this field will be populated with the address + /// that the call is delegated to. It will be empty in all other situations. + /// + /// Assumes that a 'SetCode' transaction set address `0xA` to delegates to address `0xB`, + /// then when a call is made to `0xA`, the Call object would have: + /// + /// - caller = + /// - address = 0xA + /// - address_delegates_to = 0xB + /// + /// Again, it's important to emphasize that this field relates to EIP-7702, if the call is + /// a DELEGATE or CALLCODE type, this field will not be populated and will remain empty. + /// + /// It will be populated only if EIP-7702 is active on the chain (Prague fork) and if the + /// 'address' of the call was pointing to another address at time of execution. + #[prost(bytes = "vec", optional, tag = "34")] + pub address_delegates_to: ::core::option::Option<::prost::alloc::vec::Vec>, #[prost(message, optional, tag = "7")] pub value: ::core::option::Option, #[prost(uint64, tag = "8")] @@ -393,8 +779,25 @@ pub struct Call { pub gas_consumed: u64, #[prost(bytes = "vec", tag = "13")] pub return_data: ::prost::alloc::vec::Vec, + /// Known Issues + /// - Version 3: + /// When call is `CREATE` or `CREATE2`, this field is not populated. A couple of suggestions: + /// 1. You can get the contract's code in the `code_changes` field. + /// 2. In the root `CREATE` call, you can directly use the `TransactionTrace`'s input field. + /// + /// Fixed in `Version 4`, see for information about block versions. #[prost(bytes = "vec", tag = "14")] pub input: ::prost::alloc::vec::Vec, + /// Indicates whether the call executed code. + /// + /// Known Issues + /// - Version 3: + /// This may be incorrectly set to `false` for accounts with code handling native value transfers, + /// as well as for certain precompiles with no input. + /// The value is initially set based on `call.type != CREATE && len(call.input) > 0` + /// and later adjusted if the tracer detects an account without code. + /// + /// Fixed in `Version 4`, see for information about block versions. #[prost(bool, tag = "15")] pub executed_code: bool, #[prost(bool, tag = "16")] @@ -405,6 +808,11 @@ pub struct Call { ::prost::alloc::string::String, ::prost::alloc::string::String, >, + /// Known Issues + /// - Version 3: + /// The data might be not be in order. + /// + /// Fixed in `Version 4`, see for information about block versions. #[prost(message, repeated, tag = "21")] pub storage_changes: ::prost::alloc::vec::Vec, #[prost(message, repeated, tag = "22")] @@ -415,6 +823,13 @@ pub struct Call { pub logs: ::prost::alloc::vec::Vec, #[prost(message, repeated, tag = "26")] pub code_changes: ::prost::alloc::vec::Vec, + /// Known Issues + /// - Version 3: + /// Some gas changes are not correctly tracked: + /// 1. Gas refunded due to data returned to the chain (occurs at the end of a transaction, before buyback). + /// 2. Initial gas allocation (0 -> GasLimit) at the start of a call. + /// 3. Final gas deduction (LeftOver -> 0) at the end of a call (if applicable). + /// Fixed in `Version 4`, see for information about block versions. #[prost(message, repeated, tag = "28")] pub gas_changes: ::prost::alloc::vec::Vec, /// In Ethereum, a call can be either: @@ -453,13 +868,42 @@ pub struct Call { /// ``` /// /// In the transaction above, while Call #2 and Call #3 would have the - /// status `EXECUTED` + /// status `EXECUTED`. + /// + /// If you check all calls and check only `state_reverted` flag, you might be missing + /// some balance changes and nonce changes. This is because when a full transaction fails + /// in ethereum (e.g. `calls.all(x.state_reverted == true)`), there is still the transaction + /// fee that are recorded to the chain. + /// + /// Refer to \[TransactionTrace#status\] field for more details about the handling you must + /// perform. #[prost(bool, tag = "30")] pub state_reverted: bool, + /// Known Issues + /// - Version 3: + /// 1. The block's global ordinal when the call started executing, refer to + /// \[Block\] documentation for further information about ordinals and total ordering. + /// 2. The transaction root call `begin_ordial` is always `0` (also in the GENESIS block), which can cause issues + /// when sorting by this field. To ensure proper execution order, set it as follows: + /// `trx.Calls\[0\].BeginOrdinal = trx.BeginOrdinal`. + /// + /// Fixed in `Version 4`, see for information about block versions. #[prost(uint64, tag = "31")] pub begin_ordinal: u64, + /// Known Issues + /// - Version 3: + /// 1. The block's global ordinal when the call finished executing, refer to + /// \[Block\] documentation for further information about ordinals and total ordering. + /// 2. The root call of the GENESIS block is always `0`. To fix it, you can set it as follows: + /// `rx.Calls\[0\].EndOrdinal = max.Uint64`. + /// + /// Fixed in `Version 4`, see for information about block versions. #[prost(uint64, tag = "32")] pub end_ordinal: u64, + /// Known Issues + /// - Version 4: + /// AccountCreations are NOT SUPPORTED anymore. DO NOT rely on them. + #[deprecated] #[prost(message, repeated, tag = "33")] pub account_creations: ::prost::alloc::vec::Vec, } @@ -473,29 +917,50 @@ pub struct StorageChange { pub old_value: ::prost::alloc::vec::Vec, #[prost(bytes = "vec", tag = "4")] pub new_value: ::prost::alloc::vec::Vec, + /// The block's global ordinal when the storage change was recorded, refer to \[Block\] + /// documentation for further information about ordinals and total ordering. #[prost(uint64, tag = "5")] pub ordinal: u64, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct BalanceChange { + /// Address is the address of the account that has changed balance. #[prost(bytes = "vec", tag = "1")] pub address: ::prost::alloc::vec::Vec, + /// OldValue is the balance of the address before the change. This value + /// can be **nil/null/None** if there was no previous balance for the address. + /// It is safe in those case(s) to consider the balance as being 0. + /// + /// If you consume this from a Substreams, you can safely use: + /// + /// ```ignore + /// let old_value = old_value.unwrap_or_default(); + /// ``` #[prost(message, optional, tag = "2")] pub old_value: ::core::option::Option, + /// NewValue is the balance of the address after the change. This value + /// can be **nil/null/None** if there was no previous balance for the address + /// after the change. It is safe in those case(s) to consider the balance as being + /// 0. + /// + /// If you consume this from a Substreams, you can safely use: + /// + /// ```ignore + /// let new_value = new_value.unwrap_or_default(); + /// ``` #[prost(message, optional, tag = "3")] pub new_value: ::core::option::Option, + /// Reason is the reason why the balance has changed. This is useful to determine + /// why the balance has changed and what is the context of the change. #[prost(enumeration = "balance_change::Reason", tag = "4")] pub reason: i32, + /// The block's global ordinal when the balance change was recorded, refer to \[Block\] + /// documentation for further information about ordinals and total ordering. #[prost(uint64, tag = "5")] pub ordinal: u64, } /// Nested message and enum types in `BalanceChange`. pub mod balance_change { - /// Obtain all balance change reasons under deep mind repository: - /// - /// ```shell - /// ack -ho 'BalanceChangeReason\(".*"\)' | grep -Eo '".*"' | sort | uniq - /// ``` #[derive( Clone, Copy, @@ -526,6 +991,14 @@ pub mod balance_change { CallBalanceOverride = 12, /// Used on chain(s) where some Ether burning happens Burn = 15, + Withdrawal = 16, + /// Rewards for Blob processing on BNB chain added in Tycho hard-fork, refers + /// to BNB documentation to check the timestamp at which it was activated. + RewardBlobFee = 17, + /// This reason is used only on Optimism chain. + IncreaseMint = 18, + /// This reason is used only on Optimism chain. + Revert = 19, } impl Reason { /// String value of the enum field names used in the ProtoBuf definition. @@ -550,6 +1023,10 @@ pub mod balance_change { Self::SuicideWithdraw => "REASON_SUICIDE_WITHDRAW", Self::CallBalanceOverride => "REASON_CALL_BALANCE_OVERRIDE", Self::Burn => "REASON_BURN", + Self::Withdrawal => "REASON_WITHDRAWAL", + Self::RewardBlobFee => "REASON_REWARD_BLOB_FEE", + Self::IncreaseMint => "REASON_INCREASE_MINT", + Self::Revert => "REASON_REVERT", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -571,6 +1048,10 @@ pub mod balance_change { "REASON_SUICIDE_WITHDRAW" => Some(Self::SuicideWithdraw), "REASON_CALL_BALANCE_OVERRIDE" => Some(Self::CallBalanceOverride), "REASON_BURN" => Some(Self::Burn), + "REASON_WITHDRAWAL" => Some(Self::Withdrawal), + "REASON_REWARD_BLOB_FEE" => Some(Self::RewardBlobFee), + "REASON_INCREASE_MINT" => Some(Self::IncreaseMint), + "REASON_REVERT" => Some(Self::Revert), _ => None, } } @@ -584,6 +1065,8 @@ pub struct NonceChange { pub old_value: u64, #[prost(uint64, tag = "3")] pub new_value: u64, + /// The block's global ordinal when the nonce change was recorded, refer to \[Block\] + /// documentation for further information about ordinals and total ordering. #[prost(uint64, tag = "4")] pub ordinal: u64, } @@ -591,6 +1074,8 @@ pub struct NonceChange { pub struct AccountCreation { #[prost(bytes = "vec", tag = "1")] pub account: ::prost::alloc::vec::Vec, + /// The block's global ordinal when the account creation was recorded, refer to \[Block\] + /// documentation for further information about ordinals and total ordering. #[prost(uint64, tag = "2")] pub ordinal: u64, } @@ -606,6 +1091,8 @@ pub struct CodeChange { pub new_hash: ::prost::alloc::vec::Vec, #[prost(bytes = "vec", tag = "5")] pub new_code: ::prost::alloc::vec::Vec, + /// The block's global ordinal when the code change was recorded, refer to \[Block\] + /// documentation for further information about ordinals and total ordering. #[prost(uint64, tag = "6")] pub ordinal: u64, } @@ -613,7 +1100,7 @@ pub struct CodeChange { /// The gas is computed per actual op codes. Doing them completely might prove /// overwhelming in most cases. /// -/// Hence, we only index some of them, those that are costly like all the calls +/// Hence, we only index some of them, those that are costy like all the calls /// one, log events, return data, etc. #[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct GasChange { @@ -623,16 +1110,13 @@ pub struct GasChange { pub new_value: u64, #[prost(enumeration = "gas_change::Reason", tag = "3")] pub reason: i32, + /// The block's global ordinal when the gas change was recorded, refer to \[Block\] + /// documentation for further information about ordinals and total ordering. #[prost(uint64, tag = "4")] pub ordinal: u64, } /// Nested message and enum types in `GasChange`. pub mod gas_change { - /// Obtain all gas change reasons under deep mind repository: - /// - /// ```shell - /// ack -ho 'GasChangeReason\(".*"\)' | grep -Eo '".*"' | sort | uniq - /// ``` #[derive( Clone, Copy, @@ -647,27 +1131,88 @@ pub mod gas_change { #[repr(i32)] pub enum Reason { Unknown = 0, + /// REASON_CALL is the amount of gas that will be charged for a 'CALL' opcode executed by the EVM Call = 1, + /// REASON_CALL_CODE is the amount of gas that will be charged for a 'CALLCODE' opcode executed by the EVM CallCode = 2, + /// REASON_CALL_DATA_COPY is the amount of gas that will be charged for a 'CALLDATACOPY' opcode executed by the EVM CallDataCopy = 3, + /// REASON_CODE_COPY is the amount of gas that will be charged for a 'CALLDATACOPY' opcode executed by the EVM CodeCopy = 4, + /// REASON_CODE_STORAGE is the amount of gas that will be charged for code storage CodeStorage = 5, + /// REASON_CONTRACT_CREATION is the amount of gas that will be charged for a 'CREATE' opcode executed by the EVM and for the gas + /// burned for a CREATE, today controlled by EIP150 rules ContractCreation = 6, + /// REASON_CONTRACT_CREATION2 is the amount of gas that will be charged for a 'CREATE2' opcode executed by the EVM and for the gas + /// burned for a CREATE2, today controlled by EIP150 rules ContractCreation2 = 7, + /// REASON_DELEGATE_CALL is the amount of gas that will be charged for a 'DELEGATECALL' opcode executed by the EVM DelegateCall = 8, + /// REASON_EVENT_LOG is the amount of gas that will be charged for a 'LOG' opcode executed by the EVM EventLog = 9, + /// REASON_EXT_CODE_COPY is the amount of gas that will be charged for a 'LOG' opcode executed by the EVM ExtCodeCopy = 10, + /// REASON_FAILED_EXECUTION is the burning of the remaining gas when the execution failed without a revert FailedExecution = 11, + /// REASON_INTRINSIC_GAS is the amount of gas that will be charged for the intrinsic cost of the transaction, there is + /// always exactly one of those per transaction IntrinsicGas = 12, + /// GasChangePrecompiledContract is the amount of gas that will be charged for a precompiled contract execution PrecompiledContract = 13, + /// REASON_REFUND_AFTER_EXECUTION is the amount of gas that will be refunded to the caller after the execution of the call, + /// if there is left over at the end of execution RefundAfterExecution = 14, + /// REASON_RETURN is the amount of gas that will be charged for a 'RETURN' opcode executed by the EVM Return = 15, + /// REASON_RETURN_DATA_COPY is the amount of gas that will be charged for a 'RETURNDATACOPY' opcode executed by the EVM ReturnDataCopy = 16, + /// REASON_REVERT is the amount of gas that will be charged for a 'REVERT' opcode executed by the EVM Revert = 17, + /// REASON_SELF_DESTRUCT is the amount of gas that will be charged for a 'SELFDESTRUCT' opcode executed by the EVM SelfDestruct = 18, + /// REASON_STATIC_CALL is the amount of gas that will be charged for a 'STATICALL' opcode executed by the EVM StaticCall = 19, + /// REASON_STATE_COLD_ACCESS is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules + /// /// Added in Berlin fork (Geth 1.10+) StateColdAccess = 20, + /// REASON_TX_INITIAL_BALANCE is the initial balance for the call which will be equal to the gasLimit of the call + /// + /// Added as new tracing reason in Geth, available only on some chains + TxInitialBalance = 21, + /// REASON_TX_REFUNDS is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared) + /// this generates an increase in gas. There is only one such gas change per transaction. + /// + /// Added as new tracing reason in Geth, available only on some chains + TxRefunds = 22, + /// REASON_TX_LEFT_OVER_RETURNED is the amount of gas left over at the end of transaction's execution that will be returned + /// to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas + /// left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. + /// There is at most one of such gas change per transaction. + /// + /// Added as new tracing reason in Geth, available only on some chains + TxLeftOverReturned = 23, + /// REASON_CALL_INITIAL_BALANCE is the initial balance for the call which will be equal to the gasLimit of the call. There is only + /// one such gas change per call. + /// + /// Added as new tracing reason in Geth, available only on some chains + CallInitialBalance = 24, + /// REASON_CALL_LEFT_OVER_RETURNED is the amount of gas left over that will be returned to the caller, this change will always + /// be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even + /// will be emitted. + CallLeftOverReturned = 25, + /// REASON_WITNESS_CONTRACT_INIT flags the event of adding to the witness during the contract creation initialization step. + WitnessContractInit = 26, + /// REASON_WITNESS_CONTRACT_CREATION flags the event of adding to the witness during the contract creation finalization step. + WitnessContractCreation = 27, + /// REASON_WITNESS_CODE_CHUNK flags the event of adding one or more contract code chunks to the witness. + WitnessCodeChunk = 28, + /// REASON_WITNESS_CONTRACT_COLLISION_CHECK flags the event of adding to the witness when checking for contract address collision. + WitnessContractCollisionCheck = 29, + /// REASON_TX_DATA_FLOOR is the amount of extra gas the transaction has to pay to reach the minimum gas requirement for the + /// transaction data. This change will always be a negative change. + TxDataFloor = 30, } impl Reason { /// String value of the enum field names used in the ProtoBuf definition. @@ -697,6 +1242,18 @@ pub mod gas_change { Self::SelfDestruct => "REASON_SELF_DESTRUCT", Self::StaticCall => "REASON_STATIC_CALL", Self::StateColdAccess => "REASON_STATE_COLD_ACCESS", + Self::TxInitialBalance => "REASON_TX_INITIAL_BALANCE", + Self::TxRefunds => "REASON_TX_REFUNDS", + Self::TxLeftOverReturned => "REASON_TX_LEFT_OVER_RETURNED", + Self::CallInitialBalance => "REASON_CALL_INITIAL_BALANCE", + Self::CallLeftOverReturned => "REASON_CALL_LEFT_OVER_RETURNED", + Self::WitnessContractInit => "REASON_WITNESS_CONTRACT_INIT", + Self::WitnessContractCreation => "REASON_WITNESS_CONTRACT_CREATION", + Self::WitnessCodeChunk => "REASON_WITNESS_CODE_CHUNK", + Self::WitnessContractCollisionCheck => { + "REASON_WITNESS_CONTRACT_COLLISION_CHECK" + } + Self::TxDataFloor => "REASON_TX_DATA_FLOOR", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -723,11 +1280,84 @@ pub mod gas_change { "REASON_SELF_DESTRUCT" => Some(Self::SelfDestruct), "REASON_STATIC_CALL" => Some(Self::StaticCall), "REASON_STATE_COLD_ACCESS" => Some(Self::StateColdAccess), + "REASON_TX_INITIAL_BALANCE" => Some(Self::TxInitialBalance), + "REASON_TX_REFUNDS" => Some(Self::TxRefunds), + "REASON_TX_LEFT_OVER_RETURNED" => Some(Self::TxLeftOverReturned), + "REASON_CALL_INITIAL_BALANCE" => Some(Self::CallInitialBalance), + "REASON_CALL_LEFT_OVER_RETURNED" => Some(Self::CallLeftOverReturned), + "REASON_WITNESS_CONTRACT_INIT" => Some(Self::WitnessContractInit), + "REASON_WITNESS_CONTRACT_CREATION" => Some(Self::WitnessContractCreation), + "REASON_WITNESS_CODE_CHUNK" => Some(Self::WitnessCodeChunk), + "REASON_WITNESS_CONTRACT_COLLISION_CHECK" => { + Some(Self::WitnessContractCollisionCheck) + } + "REASON_TX_DATA_FLOOR" => Some(Self::TxDataFloor), _ => None, } } } } +/// HeaderOnlyBlock is used to optimally unpack the \[Block\] structure (note the +/// corresponding message number for the `header` field) while consuming less +/// memory, when only the `header` is desired. +/// +/// WARN: this is a client-side optimization pattern and should be moved in the +/// consuming code. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct HeaderOnlyBlock { + #[prost(message, optional, tag = "5")] + pub header: ::core::option::Option, +} +/// BlockWithRefs is a lightweight block, with traces and transactions +/// purged from the `block` within, and only. It is used in transports +/// to pass block data around. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockWithRefs { + #[prost(string, tag = "1")] + pub id: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub block: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub transaction_trace_refs: ::core::option::Option, + #[prost(bool, tag = "4")] + pub irreversible: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionTraceWithBlockRef { + #[prost(message, optional, tag = "1")] + pub trace: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub block_ref: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionRefs { + #[prost(bytes = "vec", repeated, tag = "1")] + pub hashes: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockRef { + #[prost(bytes = "vec", tag = "1")] + pub hash: ::prost::alloc::vec::Vec, + #[prost(uint64, tag = "2")] + pub number: u64, +} +/// Withdrawal represents a validator withdrawal from the beacon chain to the EVM. +/// Introduced in EIP-4895 (Shanghai hard fork). +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Withdrawal { + /// Index is the monotonically increasing identifier of the withdrawal + #[prost(uint64, tag = "1")] + pub index: u64, + /// ValidatorIndex is the index of the validator that is withdrawing + #[prost(uint64, tag = "2")] + pub validator_index: u64, + /// Address is the Ethereum address receiving the withdrawn funds + #[prost(bytes = "vec", tag = "3")] + pub address: ::prost::alloc::vec::Vec, + /// Amount is the value of the withdrawal in gwei (1 gwei = 1e9 wei) + #[prost(uint64, tag = "4")] + pub amount: u64, +} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum TransactionTraceStatus { diff --git a/chain/ethereum/src/runtime/abi.rs b/chain/ethereum/src/runtime/abi.rs index a716c4ea3a8..5641a501a6e 100644 --- a/chain/ethereum/src/runtime/abi.rs +++ b/chain/ethereum/src/runtime/abi.rs @@ -2,16 +2,15 @@ use super::runtime_adapter::UnresolvedContractCall; use crate::trigger::{ EthereumBlockData, EthereumCallData, EthereumEventData, EthereumTransactionData, }; +use anyhow::anyhow; use async_trait::async_trait; +use graph::abi; +use graph::prelude::alloy; +use graph::prelude::alloy::network::ReceiptResponse; +use graph::prelude::alloy::rpc::types::{Log, TransactionReceipt}; +use graph::prelude::alloy::serde::WithOtherFields; use graph::{ - prelude::{ - ethabi, - web3::{ - self, - types::{Log, TransactionReceipt, H256}, - }, - BigInt, - }, + prelude::BigInt, runtime::{ asc_get, asc_new, asc_new_or_null, gas::GasCounter, AscHeap, AscIndexId, AscPtr, AscType, DeterministicHostError, FromAscObj, HostExportError, IndexForAscTypeId, ToAscObj, @@ -24,7 +23,7 @@ use graph_runtime_wasm::asc_abi::class::{ }; use semver::Version; -type AscH256 = Uint8Array; +type AscB256 = Uint8Array; type AscH2048 = Uint8Array; pub struct AscLogParamArray(Array>); @@ -42,7 +41,7 @@ impl AscType for AscLogParamArray { } #[async_trait] -impl ToAscObj for &[ethabi::LogParam] { +impl ToAscObj for &[abi::DynSolParam] { async fn to_asc_obj( &self, heap: &mut H, @@ -60,7 +59,7 @@ impl AscIndexId for AscLogParamArray { const INDEX_ASC_TYPE_ID: IndexForAscTypeId = IndexForAscTypeId::ArrayEventParam; } -pub struct AscTopicArray(Array>); +pub struct AscTopicArray(Array>); impl AscType for AscTopicArray { fn to_asc_bytes(&self) -> Result, DeterministicHostError> { @@ -76,14 +75,14 @@ impl AscType for AscTopicArray { } #[async_trait] -impl ToAscObj for Vec { +impl ToAscObj for &[alloy::primitives::B256] { async fn to_asc_obj( &self, heap: &mut H, gas: &GasCounter, ) -> Result { let mut topics = Vec::with_capacity(self.len()); - for topic in self { + for topic in *self { topics.push(asc_new(heap, topic, gas).await?); } Ok(AscTopicArray(Array::new(&topics, heap, gas).await?)) @@ -91,7 +90,7 @@ impl ToAscObj for Vec { } impl AscIndexId for AscTopicArray { - const INDEX_ASC_TYPE_ID: IndexForAscTypeId = IndexForAscTypeId::ArrayH256; + const INDEX_ASC_TYPE_ID: IndexForAscTypeId = IndexForAscTypeId::ArrayB256; } pub struct AscLogArray(Array>); @@ -110,14 +109,14 @@ impl AscType for AscLogArray { } #[async_trait] -impl ToAscObj for Vec { +impl ToAscObj for &[Log] { async fn to_asc_obj( &self, heap: &mut H, gas: &GasCounter, ) -> Result { let mut logs = Vec::with_capacity(self.len()); - for log in self { + for log in *self { logs.push(asc_new(heap, log, gas).await?); } @@ -190,13 +189,13 @@ impl FromAscObj for UnresolvedContractCall { #[repr(C)] #[derive(AscType)] pub(crate) struct AscEthereumBlock { - pub hash: AscPtr, - pub parent_hash: AscPtr, - pub uncles_hash: AscPtr, + pub hash: AscPtr, + pub parent_hash: AscPtr, + pub uncles_hash: AscPtr, pub author: AscPtr, - pub state_root: AscPtr, - pub transactions_root: AscPtr, - pub receipts_root: AscPtr, + pub state_root: AscPtr, + pub transactions_root: AscPtr, + pub receipts_root: AscPtr, pub number: AscPtr, pub gas_used: AscPtr, pub gas_limit: AscPtr, @@ -214,13 +213,13 @@ impl AscIndexId for AscEthereumBlock { #[derive(AscType)] #[allow(non_camel_case_types)] pub(crate) struct AscEthereumBlock_0_0_6 { - pub hash: AscPtr, - pub parent_hash: AscPtr, - pub uncles_hash: AscPtr, + pub hash: AscPtr, + pub parent_hash: AscPtr, + pub uncles_hash: AscPtr, pub author: AscPtr, - pub state_root: AscPtr, - pub transactions_root: AscPtr, - pub receipts_root: AscPtr, + pub state_root: AscPtr, + pub transactions_root: AscPtr, + pub receipts_root: AscPtr, pub number: AscPtr, pub gas_used: AscPtr, pub gas_limit: AscPtr, @@ -239,7 +238,7 @@ impl AscIndexId for AscEthereumBlock_0_0_6 { #[derive(AscType)] #[allow(non_camel_case_types)] pub(crate) struct AscEthereumTransaction_0_0_1 { - pub hash: AscPtr, + pub hash: AscPtr, pub index: AscPtr, pub from: AscPtr, pub to: AscPtr, @@ -256,7 +255,7 @@ impl AscIndexId for AscEthereumTransaction_0_0_1 { #[derive(AscType)] #[allow(non_camel_case_types)] pub(crate) struct AscEthereumTransaction_0_0_2 { - pub hash: AscPtr, + pub hash: AscPtr, pub index: AscPtr, pub from: AscPtr, pub to: AscPtr, @@ -274,7 +273,7 @@ impl AscIndexId for AscEthereumTransaction_0_0_2 { #[derive(AscType)] #[allow(non_camel_case_types)] pub(crate) struct AscEthereumTransaction_0_0_6 { - pub hash: AscPtr, + pub hash: AscPtr, pub index: AscPtr, pub from: AscPtr, pub to: AscPtr, @@ -323,9 +322,9 @@ pub(crate) struct AscEthereumLog { pub address: AscPtr, pub topics: AscPtr, pub data: AscPtr, - pub block_hash: AscPtr, - pub block_number: AscPtr, - pub transaction_hash: AscPtr, + pub block_hash: AscPtr, + pub block_number: AscPtr, + pub transaction_hash: AscPtr, pub transaction_index: AscPtr, pub log_index: AscPtr, pub transaction_log_index: AscPtr, @@ -340,16 +339,16 @@ impl AscIndexId for AscEthereumLog { #[repr(C)] #[derive(AscType)] pub(crate) struct AscEthereumTransactionReceipt { - pub transaction_hash: AscPtr, + pub transaction_hash: AscPtr, pub transaction_index: AscPtr, - pub block_hash: AscPtr, + pub block_hash: AscPtr, pub block_number: AscPtr, pub cumulative_gas_used: AscPtr, pub gas_used: AscPtr, pub contract_address: AscPtr, pub logs: AscPtr, pub status: AscPtr, - pub root: AscPtr, + pub root: AscPtr, pub logs_bloom: AscPtr, } @@ -437,10 +436,7 @@ impl<'a> ToAscObj for EthereumBlockData<'a> { heap: &mut H, gas: &GasCounter, ) -> Result { - let size = match self.size() { - Some(size) => asc_new(heap, &BigInt::from_unsigned_u256(&size), gas).await?, - None => AscPtr::null(), - }; + let size = asc_new_or_null_u256(heap, &self.size(), gas).await?; Ok(AscEthereumBlock { hash: asc_new(heap, self.hash(), gas).await?, @@ -451,9 +447,9 @@ impl<'a> ToAscObj for EthereumBlockData<'a> { transactions_root: asc_new(heap, self.transactions_root(), gas).await?, receipts_root: asc_new(heap, self.receipts_root(), gas).await?, number: asc_new(heap, &BigInt::from(self.number()), gas).await?, - gas_used: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_used()), gas).await?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_limit()), gas).await?, - timestamp: asc_new(heap, &BigInt::from_unsigned_u256(self.timestamp()), gas).await?, + gas_used: asc_new(heap, &BigInt::from(self.gas_used()), gas).await?, + gas_limit: asc_new(heap, &BigInt::from(self.gas_limit()), gas).await?, + timestamp: asc_new(heap, &BigInt::from(self.timestamp()), gas).await?, difficulty: asc_new(heap, &BigInt::from_unsigned_u256(self.difficulty()), gas).await?, total_difficulty: asc_new( heap, @@ -473,14 +469,8 @@ impl<'a> ToAscObj for EthereumBlockData<'a> { heap: &mut H, gas: &GasCounter, ) -> Result { - let size = match self.size() { - Some(size) => asc_new(heap, &BigInt::from_unsigned_u256(&size), gas).await?, - None => AscPtr::null(), - }; - let base_fee_per_block = match self.base_fee_per_gas() { - Some(base_fee) => asc_new(heap, &BigInt::from_unsigned_u256(&base_fee), gas).await?, - None => AscPtr::null(), - }; + let size = asc_new_or_null_u256(heap, &self.size(), gas).await?; + let base_fee_per_block = asc_new_or_null_u64(heap, &self.base_fee_per_gas(), gas).await?; Ok(AscEthereumBlock_0_0_6 { hash: asc_new(heap, self.hash(), gas).await?, @@ -491,9 +481,9 @@ impl<'a> ToAscObj for EthereumBlockData<'a> { transactions_root: asc_new(heap, self.transactions_root(), gas).await?, receipts_root: asc_new(heap, self.receipts_root(), gas).await?, number: asc_new(heap, &BigInt::from(self.number()), gas).await?, - gas_used: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_used()), gas).await?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_limit()), gas).await?, - timestamp: asc_new(heap, &BigInt::from_unsigned_u256(self.timestamp()), gas).await?, + gas_used: asc_new(heap, &BigInt::from(self.gas_used()), gas).await?, + gas_limit: asc_new(heap, &BigInt::from(self.gas_limit()), gas).await?, + timestamp: asc_new(heap, &BigInt::from(self.timestamp()), gas).await?, difficulty: asc_new(heap, &BigInt::from_unsigned_u256(self.difficulty()), gas).await?, total_difficulty: asc_new( heap, @@ -515,13 +505,16 @@ impl<'a> ToAscObj for EthereumTransactionData<'a> gas: &GasCounter, ) -> Result { Ok(AscEthereumTransaction_0_0_1 { - hash: asc_new(heap, self.hash(), gas).await?, - index: asc_new(heap, &BigInt::from_unsigned_u128(self.index()), gas).await?, - from: asc_new(heap, self.from(), gas).await?, - to: asc_new_or_null(heap, self.to(), gas).await?, - value: asc_new(heap, &BigInt::from_unsigned_u256(self.value()), gas).await?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_limit()), gas).await?, - gas_price: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_price()), gas).await?, + hash: asc_new(heap, &self.hash(), gas).await?, + index: asc_new(heap, &BigInt::from(self.index()), gas).await?, + from: asc_new(heap, &self.from(), gas).await?, + to: match self.to() { + Some(to) => asc_new(heap, &to, gas).await?, + None => AscPtr::null(), + }, + value: asc_new(heap, &BigInt::from_unsigned_u256(&self.value()), gas).await?, + gas_limit: asc_new(heap, &BigInt::from(self.gas_limit()), gas).await?, + gas_price: asc_new(heap, &BigInt::from(self.gas_price()), gas).await?, }) } } @@ -534,13 +527,16 @@ impl<'a> ToAscObj for EthereumTransactionData<'a> gas: &GasCounter, ) -> Result { Ok(AscEthereumTransaction_0_0_2 { - hash: asc_new(heap, self.hash(), gas).await?, - index: asc_new(heap, &BigInt::from_unsigned_u128(self.index()), gas).await?, - from: asc_new(heap, self.from(), gas).await?, - to: asc_new_or_null(heap, self.to(), gas).await?, - value: asc_new(heap, &BigInt::from_unsigned_u256(self.value()), gas).await?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_limit()), gas).await?, - gas_price: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_price()), gas).await?, + hash: asc_new(heap, &self.hash(), gas).await?, + index: asc_new(heap, &BigInt::from(self.index()), gas).await?, + from: asc_new(heap, &self.from(), gas).await?, + to: match self.to() { + Some(to) => asc_new(heap, &to, gas).await?, + None => AscPtr::null(), + }, + value: asc_new(heap, &BigInt::from_unsigned_u256(&self.value()), gas).await?, + gas_limit: asc_new(heap, &BigInt::from(self.gas_limit()), gas).await?, + gas_price: asc_new(heap, &BigInt::from(self.gas_price()), gas).await?, input: asc_new(heap, self.input(), gas).await?, }) } @@ -554,15 +550,15 @@ impl<'a> ToAscObj for EthereumTransactionData<'a> gas: &GasCounter, ) -> Result { Ok(AscEthereumTransaction_0_0_6 { - hash: asc_new(heap, self.hash(), gas).await?, - index: asc_new(heap, &BigInt::from_unsigned_u128(self.index()), gas).await?, - from: asc_new(heap, self.from(), gas).await?, - to: asc_new_or_null(heap, self.to(), gas).await?, - value: asc_new(heap, &BigInt::from_unsigned_u256(self.value()), gas).await?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_limit()), gas).await?, - gas_price: asc_new(heap, &BigInt::from_unsigned_u256(self.gas_price()), gas).await?, + hash: asc_new(heap, &self.hash(), gas).await?, + index: asc_new(heap, &BigInt::from(self.index()), gas).await?, + from: asc_new(heap, &self.from(), gas).await?, + to: asc_new_or_null(heap, &self.to(), gas).await?, + value: asc_new(heap, &BigInt::from_unsigned_u256(&self.value()), gas).await?, + gas_limit: asc_new(heap, &BigInt::from(self.gas_limit()), gas).await?, + gas_price: asc_new(heap, &BigInt::from(self.gas_price()), gas).await?, input: asc_new(heap, self.input(), gas).await?, - nonce: asc_new(heap, &BigInt::from_unsigned_u256(self.nonce()), gas).await?, + nonce: asc_new(heap, &BigInt::from(self.nonce()), gas).await?, }) } } @@ -582,14 +578,10 @@ where ) -> Result, HostExportError> { Ok(AscEthereumEvent { address: asc_new(heap, self.address(), gas).await?, - log_index: asc_new(heap, &BigInt::from_unsigned_u256(self.log_index()), gas).await?, - transaction_log_index: asc_new( - heap, - &BigInt::from_unsigned_u256(self.transaction_log_index()), - gas, - ) - .await?, - log_type: asc_new_or_null(heap, self.log_type(), gas).await?, + log_index: asc_new(heap, &BigInt::from(self.log_index()), gas).await?, + transaction_log_index: asc_new(heap, &BigInt::from(self.transaction_log_index()), gas) + .await?, + log_type: asc_new_or_null(heap, &self.log_type().as_ref(), gas).await?, block: asc_new::(heap, &self.block, gas).await?, transaction: asc_new::(heap, &self.transaction, gas) .await?, @@ -599,13 +591,18 @@ where } #[async_trait] -impl<'a, T, B> ToAscObj> - for (EthereumEventData<'a>, Option<&TransactionReceipt>) +impl<'a, T, B, Inner> ToAscObj> + for ( + EthereumEventData<'a>, + Option<&WithOtherFields>>, + ) where T: AscType + AscIndexId + Send, B: AscType + AscIndexId + Send, EthereumTransactionData<'a>: ToAscObj, EthereumBlockData<'a>: ToAscObj, + Inner: Send + Sync, + TransactionReceipt: ToAscObj, { async fn to_asc_obj( &self, @@ -623,7 +620,7 @@ where params, } = event_data.to_asc_obj(heap, gas).await?; let receipt = if let Some(receipt_data) = optional_receipt { - asc_new(heap, receipt_data, gas).await? + asc_new(heap, &receipt_data.inner(), gas).await? } else { AscPtr::null() }; @@ -642,7 +639,7 @@ where async fn asc_new_or_null_u256( heap: &mut H, - value: &Option, + value: &Option, gas: &GasCounter, ) -> Result, HostExportError> { match value { @@ -653,7 +650,7 @@ async fn asc_new_or_null_u256( async fn asc_new_or_null_u64( heap: &mut H, - value: &Option, + value: &Option, gas: &GasCounter, ) -> Result, HostExportError> { match value { @@ -669,51 +666,55 @@ impl ToAscObj for Log { heap: &mut H, gas: &GasCounter, ) -> Result { - let removed = match self.removed { - Some(removed) => asc_new(heap, &AscWrapped { inner: removed }, gas).await?, - None => AscPtr::null(), - }; Ok(AscEthereumLog { - address: asc_new(heap, &self.address, gas).await?, - topics: asc_new(heap, &self.topics, gas).await?, - data: asc_new(heap, self.data.0.as_slice(), gas).await?, + address: asc_new(heap, &self.address(), gas).await?, + topics: asc_new(heap, &self.topics(), gas).await?, + data: asc_new(heap, self.data().data.as_ref(), gas).await?, block_hash: asc_new_or_null(heap, &self.block_hash, gas).await?, block_number: asc_new_or_null_u64(heap, &self.block_number, gas).await?, transaction_hash: asc_new_or_null(heap, &self.transaction_hash, gas).await?, transaction_index: asc_new_or_null_u64(heap, &self.transaction_index, gas).await?, - log_index: asc_new_or_null_u256(heap, &self.log_index, gas).await?, - transaction_log_index: asc_new_or_null_u256(heap, &self.transaction_log_index, gas) - .await?, - log_type: asc_new_or_null(heap, &self.log_type, gas).await?, - removed, + log_index: asc_new_or_null_u64(heap, &self.log_index, gas).await?, + transaction_log_index: AscPtr::null(), // TODO(alloy): figure out how to get transaction log index + log_type: AscPtr::null(), // TODO(alloy): figure out how to get log type + removed: asc_new( + heap, + &AscWrapped { + inner: self.removed, + }, + gas, + ) + .await?, }) } } #[async_trait] -impl ToAscObj for &TransactionReceipt { +impl ToAscObj + for TransactionReceipt> +{ async fn to_asc_obj( &self, heap: &mut H, gas: &GasCounter, ) -> Result { + let transaction_index = self + .transaction_index + .ok_or(HostExportError::Unknown(anyhow!( + "Transaction index is missing" + )))?; Ok(AscEthereumTransactionReceipt { transaction_hash: asc_new(heap, &self.transaction_hash, gas).await?, - transaction_index: asc_new(heap, &BigInt::from(self.transaction_index), gas).await?, + transaction_index: asc_new(heap, &BigInt::from(transaction_index), gas).await?, block_hash: asc_new_or_null(heap, &self.block_hash, gas).await?, block_number: asc_new_or_null_u64(heap, &self.block_number, gas).await?, - cumulative_gas_used: asc_new( - heap, - &BigInt::from_unsigned_u256(&self.cumulative_gas_used), - gas, - ) - .await?, - gas_used: asc_new_or_null_u256(heap, &self.gas_used, gas).await?, + cumulative_gas_used: asc_new(heap, &BigInt::from(self.gas_used), gas).await?, + gas_used: asc_new(heap, &BigInt::from(self.gas_used), gas).await?, contract_address: asc_new_or_null(heap, &self.contract_address, gas).await?, - logs: asc_new(heap, &self.logs, gas).await?, - status: asc_new_or_null_u64(heap, &self.status, gas).await?, - root: asc_new_or_null(heap, &self.root, gas).await?, - logs_bloom: asc_new(heap, self.logs_bloom.as_bytes(), gas).await?, + logs: asc_new(heap, &self.logs(), gas).await?, + status: asc_new(heap, &BigInt::from(self.status() as u64), gas).await?, + root: asc_new_or_null(heap, &self.state_root(), gas).await?, + logs_bloom: asc_new(heap, self.inner.bloom().as_slice(), gas).await?, }) } } @@ -782,7 +783,7 @@ impl<'a> ToAscObj for ethabi::LogParam { +impl ToAscObj for abi::DynSolParam { async fn to_asc_obj( &self, heap: &mut H, diff --git a/chain/ethereum/src/runtime/runtime_adapter.rs b/chain/ethereum/src/runtime/runtime_adapter.rs index 8b11ada37cc..f74790146d1 100644 --- a/chain/ethereum/src/runtime/runtime_adapter.rs +++ b/chain/ethereum/src/runtime/runtime_adapter.rs @@ -7,6 +7,8 @@ use crate::{ }; use anyhow::{anyhow, Context, Error}; use blockchain::HostFn; +use graph::abi; +use graph::abi::DynSolValueExt; use graph::blockchain::ChainIdentifier; use graph::components::subgraph::HostMetrics; use graph::data::store::ethereum::call; @@ -14,18 +16,14 @@ use graph::data::store::scalar::BigInt; use graph::data::subgraph::{API_VERSION_0_0_4, API_VERSION_0_0_9}; use graph::data_source; use graph::data_source::common::{ContractCall, MappingABI}; -use graph::futures03::FutureExt as _; -use graph::prelude::web3::types::H160; use graph::runtime::gas::Gas; use graph::runtime::{AscIndexId, IndexForAscTypeId}; use graph::slog::debug; use graph::{ blockchain::{self, BlockPtr, HostFnCtx}, cheap_clone::CheapClone, - prelude::{ - ethabi::{self, Address, Token}, - EthereumCallCache, - }, + futures03::FutureExt, + prelude::{alloy::primitives::Address, EthereumCallCache}, runtime::{asc_get, asc_new, AscPtr, HostExportError}, slog::Logger, }; @@ -243,7 +241,7 @@ async fn eth_get_balance( let logger = &ctx.logger; let block_ptr = &ctx.block_ptr; - let address: H160 = asc_get(ctx.heap, wasm_ptr.into(), &ctx.gas, 0)?; + let address: Address = asc_get(ctx.heap, wasm_ptr.into(), &ctx.gas, 0)?; let result = eth_adapter .get_balance(logger, address, block_ptr.clone()) @@ -255,7 +253,7 @@ async fn eth_get_balance( Ok(asc_new(ctx.heap, &bigint, &ctx.gas).await?) } // Retry on any kind of error - Err(EthereumRpcError::Web3Error(e)) => Err(HostExportError::PossibleReorg(e.into())), + Err(EthereumRpcError::AlloyError(e)) => Err(HostExportError::PossibleReorg(e.into())), Err(EthereumRpcError::Timeout) => Err(HostExportError::PossibleReorg( EthereumRpcError::Timeout.into(), )), @@ -279,7 +277,7 @@ async fn eth_has_code( let logger = &ctx.logger; let block_ptr = &ctx.block_ptr; - let address: H160 = asc_get(ctx.heap, wasm_ptr.into(), &ctx.gas, 0)?; + let address: Address = asc_get(ctx.heap, wasm_ptr.into(), &ctx.gas, 0)?; let result = eth_adapter .get_code(logger, address, block_ptr.clone()) @@ -289,7 +287,7 @@ async fn eth_has_code( match result { Ok(v) => Ok(asc_new(ctx.heap, &AscWrapped { inner: v }, &ctx.gas).await?), // Retry on any kind of error - Err(EthereumRpcError::Web3Error(e)) => Err(HostExportError::PossibleReorg(e.into())), + Err(EthereumRpcError::AlloyError(e)) => Err(HostExportError::PossibleReorg(e.into())), Err(EthereumRpcError::Timeout) => Err(HostExportError::PossibleReorg( EthereumRpcError::Timeout.into(), )), @@ -306,20 +304,7 @@ async fn eth_call( abis: &[Arc], eth_call_gas: Option, metrics: Arc, -) -> Result>, HostExportError> { - // Helpers to log the result of the call at the end - fn tokens_as_string(tokens: &[Token]) -> String { - tokens.iter().map(|arg| arg.to_string()).join(", ") - } - - fn result_as_string(result: &Result>, HostExportError>) -> String { - match result { - Ok(Some(tokens)) => format!("({})", tokens_as_string(&tokens)), - Ok(None) => "none".to_string(), - Err(_) => "error".to_string(), - } - } - +) -> Result>, HostExportError> { let start_time = Instant::now(); // Obtain the path to the contract ABI @@ -365,7 +350,7 @@ async fn eth_call( // Any error reported by the Ethereum node could be due to the block no longer being on // the main chain. This is very unespecific but we don't want to risk failing a // subgraph due to a transient error such as a reorg. - Err(ContractCallError::Web3Error(e)) => Err(HostExportError::PossibleReorg(anyhow::anyhow!( + Err(ContractCallError::AlloyError(e)) => Err(HostExportError::PossibleReorg(anyhow::anyhow!( "Ethereum node returned an error when calling function \"{}\" of contract \"{}\": {}", unresolved_call.function_name, unresolved_call.contract_name, @@ -397,16 +382,26 @@ async fn eth_call( ); } - debug!(logger, "Contract call finished"; - "address" => format!("0x{:x}", &unresolved_call.contract_address), - "contract" => &unresolved_call.contract_name, - "signature" => &unresolved_call.function_signature, - "args" => format!("[{}]", tokens_as_string(&unresolved_call.function_args)), - "time_ms" => format!("{}ms", elapsed.as_millis()), - "result" => result_as_string(&result), - "block_hash" => block_ptr.hash_hex(), - "block_number" => block_ptr.block_number(), - "source" => source.to_string()); + let args_as_string = format!("[{}]", values_to_string(&unresolved_call.function_args)); + + let result_as_string = match &result { + Ok(Some(values)) => format!("({})", values_to_string(values)), + Ok(None) => "none".to_owned(), + Err(_err) => "error".to_owned(), + }; + + debug!( + logger, "Contract call finished"; + "address" => format!("0x{:x}", &unresolved_call.contract_address), + "contract" => &unresolved_call.contract_name, + "signature" => &unresolved_call.function_signature, + "args" => args_as_string, + "time_ms" => format!("{}ms", elapsed.as_millis()), + "result" => result_as_string, + "block_hash" => block_ptr.hash_hex(), + "block_number" => block_ptr.block_number(), + "source" => source.to_string(), + ); result } @@ -417,9 +412,18 @@ pub struct UnresolvedContractCall { pub contract_address: Address, pub function_name: String, pub function_signature: Option, - pub function_args: Vec, + pub function_args: Vec, } impl AscIndexId for AscUnresolvedContractCall { const INDEX_ASC_TYPE_ID: IndexForAscTypeId = IndexForAscTypeId::SmartContractCall; } + +#[inline] +fn values_to_string(values: &[abi::DynSolValue]) -> String { + values + .iter() + .map(|x| x.to_string()) + .collect_vec() + .join(", ") +} diff --git a/chain/ethereum/src/tests.rs b/chain/ethereum/src/tests.rs index 00873f8ea87..5dd8266a588 100644 --- a/chain/ethereum/src/tests.rs +++ b/chain/ethereum/src/tests.rs @@ -3,7 +3,12 @@ use std::sync::Arc; use graph::{ blockchain::{block_stream::BlockWithTriggers, BlockPtr, Trigger}, prelude::{ - web3::types::{Address, Bytes, Log, H160, H256, U64}, + alloy::{ + self, + primitives::{Address, Bytes, LogData, B256}, + rpc::types::{Block, Log}, + }, + rand::{self, Rng}, EthereumCall, LightEthereumBlock, }, slog::{self, o, Logger}, @@ -14,15 +19,55 @@ use crate::{ trigger::{EthereumBlockTriggerType, EthereumTrigger, LogRef}, }; +pub trait Random { + fn random() -> Self; +} + +impl Random for B256 { + fn random() -> Self { + let mut rng = rand::rng(); + let mut bytes = [0u8; 32]; + rng.fill(&mut bytes); + Self::from(bytes) + } +} + +impl Random for Address { + fn random() -> Self { + let mut rng = rand::rng(); + let mut bytes = [0u8; 20]; + rng.fill(&mut bytes); + Self::from(bytes) + } +} + +fn create_log(tx_index: u64, log_index: u64) -> Arc { + let log = Log { + inner: alloy::primitives::Log { + address: Address::default(), + data: LogData::new_unchecked(vec![], Bytes::from(vec![])), + }, + block_hash: Some(B256::ZERO), + block_number: Some(0), + block_timestamp: Some(0), + transaction_hash: Some(B256::ZERO), + transaction_index: Some(tx_index), + log_index: Some(log_index), + removed: false, + }; + + Arc::new(log) +} + #[test] fn test_trigger_ordering() { let block1 = EthereumTrigger::Block( - BlockPtr::from((H256::random(), 1u64)), + BlockPtr::from((B256::random(), 1u64)), EthereumBlockTriggerType::End, ); let block2 = EthereumTrigger::Block( - BlockPtr::from((H256::random(), 0u64)), + BlockPtr::from((B256::random(), 0u64)), EthereumBlockTriggerType::WithCallTo(Address::random()), ); @@ -32,7 +77,7 @@ fn test_trigger_ordering() { let mut call2 = EthereumCall::default(); call2.transaction_index = 2; - call2.input = Bytes(vec![0]); + call2.input = Bytes::from(vec![0]); let call2 = EthereumTrigger::Call(Arc::new(call2)); let mut call3 = EthereumCall::default(); @@ -43,25 +88,9 @@ fn test_trigger_ordering() { let mut call4 = EthereumCall::default(); call4.transaction_index = 2; // different than call2 so they don't get mistaken as the same - call4.input = Bytes(vec![1]); + call4.input = Bytes::from(vec![1]); let call4 = EthereumTrigger::Call(Arc::new(call4)); - fn create_log(tx_index: u64, log_index: u64) -> Arc { - Arc::new(Log { - address: H160::default(), - topics: vec![], - data: Bytes::default(), - block_hash: Some(H256::zero()), - block_number: Some(U64::zero()), - transaction_hash: Some(H256::zero()), - transaction_index: Some(tx_index.into()), - log_index: Some(log_index.into()), - transaction_log_index: Some(log_index.into()), - log_type: Some("".into()), - removed: Some(false), - }) - } - // Event with transaction_index 1 and log_index 0; // should be the first element after sorting let log1 = EthereumTrigger::Log(LogRef::FullLog(create_log(1, 0), None)); @@ -92,13 +121,9 @@ fn test_trigger_ordering() { let logger = Logger::root(slog::Discard, o!()); - let mut b: LightEthereumBlock = Default::default(); + let b = Block::default(); - // This is necessary because inside of BlockWithTriggers::new - // there's a log for both fields. So just using Default above - // gives None on them. - b.number = Some(Default::default()); - b.hash = Some(Default::default()); + let b = LightEthereumBlock::new(graph::components::ethereum::AnyBlock::from(b)); // Test that `BlockWithTriggers` sorts the triggers. let block_with_triggers = BlockWithTriggers::::new( @@ -118,12 +143,12 @@ fn test_trigger_ordering() { #[test] fn test_trigger_dedup() { let block1 = EthereumTrigger::Block( - BlockPtr::from((H256::random(), 1u64)), + BlockPtr::from((B256::random(), 1u64)), EthereumBlockTriggerType::End, ); let block2 = EthereumTrigger::Block( - BlockPtr::from((H256::random(), 0u64)), + BlockPtr::from((B256::random(), 0u64)), EthereumBlockTriggerType::WithCallTo(Address::random()), ); @@ -147,22 +172,6 @@ fn test_trigger_dedup() { call4.transaction_index = 2; let call4 = EthereumTrigger::Call(Arc::new(call4)); - fn create_log(tx_index: u64, log_index: u64) -> Arc { - Arc::new(Log { - address: H160::default(), - topics: vec![], - data: Bytes::default(), - block_hash: Some(H256::zero()), - block_number: Some(U64::zero()), - transaction_hash: Some(H256::zero()), - transaction_index: Some(tx_index.into()), - log_index: Some(log_index.into()), - transaction_log_index: Some(log_index.into()), - log_type: Some("".into()), - removed: Some(false), - }) - } - let log1 = EthereumTrigger::Log(LogRef::FullLog(create_log(1, 0), None)); let log2 = EthereumTrigger::Log(LogRef::FullLog(create_log(1, 1), None)); let log3 = EthereumTrigger::Log(LogRef::FullLog(create_log(2, 5), None)); @@ -190,13 +199,11 @@ fn test_trigger_dedup() { let logger = Logger::root(slog::Discard, o!()); - let mut b: LightEthereumBlock = Default::default(); + #[allow(unused_variables)] + let b = Block::default(); - // This is necessary because inside of BlockWithTriggers::new - // there's a log for both fields. So just using Default above - // gives None on them. - b.number = Some(Default::default()); - b.hash = Some(Default::default()); + #[allow(unreachable_code)] + let b = LightEthereumBlock::new(graph::components::ethereum::AnyBlock::from(b)); // Test that `BlockWithTriggers` sorts the triggers. let block_with_triggers = BlockWithTriggers::::new( diff --git a/chain/ethereum/src/transport.rs b/chain/ethereum/src/transport.rs index ef571efacb8..16f62c8a4dc 100644 --- a/chain/ethereum/src/transport.rs +++ b/chain/ethereum/src/transport.rs @@ -1,35 +1,36 @@ use graph::components::network_provider::ProviderName; -use graph::endpoint::{EndpointMetrics, RequestLabels}; -use jsonrpc_core::types::Call; -use jsonrpc_core::Value; - -use web3::transports::{http, ipc, ws}; -use web3::RequestId; - +use graph::endpoint::{ConnectionType, EndpointMetrics, RequestLabels}; +use graph::prelude::alloy::rpc::json_rpc::{RequestPacket, ResponsePacket}; use graph::prelude::*; use graph::url::Url; -use std::future::Future; +use std::sync::Arc; +use std::task::{Context, Poll}; +use tower::Service; + +use alloy::transports::{TransportError, TransportFut}; -/// Abstraction over the different web3 transports. +use graph::prelude::alloy::transports::{http::Http, ipc::IpcConnect, ws::WsConnect}; + +/// Abstraction over different transport types for Alloy providers. #[derive(Clone, Debug)] pub enum Transport { RPC { - client: http::Http, + client: alloy::rpc::client::RpcClient, metrics: Arc, provider: ProviderName, + url: String, }, - IPC(ipc::Ipc), - WS(ws::WebSocket), + IPC(IpcConnect), + WS(WsConnect), } impl Transport { /// Creates an IPC transport. #[cfg(unix)] pub async fn new_ipc(ipc: &str) -> Self { - ipc::Ipc::new(ipc) - .await - .map(Transport::IPC) - .expect("Failed to connect to Ethereum IPC") + let transport = IpcConnect::new(ipc.to_string()); + + Transport::IPC(transport) } #[cfg(not(unix))] @@ -39,16 +40,12 @@ impl Transport { /// Creates a WebSocket transport. pub async fn new_ws(ws: &str) -> Self { - ws::WebSocket::new(ws) - .await - .map(Transport::WS) - .expect("Failed to connect to Ethereum WS") + let transport = WsConnect::new(ws.to_string()); + + Transport::WS(transport) } /// Creates a JSON-RPC over HTTP transport. - /// - /// Note: JSON-RPC over HTTP doesn't always support subscribing to new - /// blocks (one such example is Infura's HTTP endpoint). pub fn new_rpc( rpc: Url, headers: graph::http::HeaderMap, @@ -61,85 +58,85 @@ impl Transport { .build() .unwrap(); + let rpc_url = rpc.to_string(); + + // Create HTTP transport with metrics collection + let http_transport = Http::with_client(client, rpc); + let metrics_transport = + MetricsHttp::new(http_transport, metrics.clone(), provider.as_ref().into()); + let rpc_client = alloy::rpc::client::RpcClient::new(metrics_transport, false); + Transport::RPC { - client: http::Http::with_client(client, rpc), + client: rpc_client, metrics, provider: provider.as_ref().into(), + url: rpc_url, } } } -impl web3::Transport for Transport { - type Out = Pin> + Send + 'static>>; - - fn prepare(&self, method: &str, params: Vec) -> (RequestId, Call) { - match self { - Transport::RPC { - client, - metrics: _, - provider: _, - } => client.prepare(method, params), - Transport::IPC(ipc) => ipc.prepare(method, params), - Transport::WS(ws) => ws.prepare(method, params), +/// Custom HTTP transport wrapper that collects metrics +#[derive(Clone)] +pub struct MetricsHttp { + inner: Http, + metrics: Arc, + provider: ProviderName, +} + +impl MetricsHttp { + pub fn new( + inner: Http, + metrics: Arc, + provider: ProviderName, + ) -> Self { + Self { + inner, + metrics, + provider, } } +} + +// Implement tower::Service trait for MetricsHttp to intercept RPC calls +impl Service for MetricsHttp { + type Response = ResponsePacket; + type Error = TransportError; + type Future = TransportFut<'static>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } - fn send(&self, id: RequestId, request: Call) -> Self::Out { - match self { - Transport::RPC { - client, - metrics, + fn call(&mut self, request: RequestPacket) -> Self::Future { + let metrics = self.metrics.clone(); + let provider = self.provider.clone(); + let mut inner = self.inner.clone(); + + Box::pin(async move { + // Extract method name from request + let method = match &request { + RequestPacket::Single(req) => req.method().to_string(), + RequestPacket::Batch(reqs) => reqs + .first() + .map(|r| r.method().to_string()) + .unwrap_or_else(|| "batch".to_string()), + }; + + let labels = RequestLabels { provider, - } => { - let metrics = metrics.cheap_clone(); - let client = client.clone(); - let method = match request { - Call::MethodCall(ref m) => m.method.as_str(), - _ => "unknown", - }; - - let labels = RequestLabels { - provider: provider.clone(), - req_type: method.into(), - conn_type: graph::endpoint::ConnectionType::Rpc, - }; - let out = async move { - let out = client.send(id, request).await; - match out { - Ok(_) => metrics.success(&labels), - Err(_) => metrics.failure(&labels), - } - - out - }; - - Box::pin(out) + req_type: method.into(), + conn_type: ConnectionType::Rpc, + }; + + // Call inner transport and track metrics + let result = inner.call(request).await; + + match &result { + Ok(_) => metrics.success(&labels), + Err(_) => metrics.failure(&labels), } - Transport::IPC(ipc) => Box::pin(ipc.send(id, request)), - Transport::WS(ws) => Box::pin(ws.send(id, request)), - } - } -} -impl web3::BatchTransport for Transport { - type Batch = Box< - dyn Future>, web3::error::Error>> - + Send - + Unpin, - >; - - fn send_batch(&self, requests: T) -> Self::Batch - where - T: IntoIterator, - { - match self { - Transport::RPC { - client, - metrics: _, - provider: _, - } => Box::new(client.send_batch(requests)), - Transport::IPC(ipc) => Box::new(ipc.send_batch(requests)), - Transport::WS(ws) => Box::new(ws.send_batch(requests)), - } + result + }) } } diff --git a/chain/ethereum/src/trigger.rs b/chain/ethereum/src/trigger.rs index bbbaa69a8d2..f969fa7063c 100644 --- a/chain/ethereum/src/trigger.rs +++ b/chain/ethereum/src/trigger.rs @@ -1,23 +1,20 @@ use async_trait::async_trait; +use graph::abi; use graph::blockchain::MappingTriggerTrait; use graph::blockchain::TriggerData; +use graph::components::ethereum::AnyTransaction; use graph::data::subgraph::API_VERSION_0_0_2; use graph::data::subgraph::API_VERSION_0_0_6; use graph::data::subgraph::API_VERSION_0_0_7; use graph::data_source::common::DeclaredCall; -use graph::prelude::ethabi::ethereum_types::H160; -use graph::prelude::ethabi::ethereum_types::H256; -use graph::prelude::ethabi::ethereum_types::U128; -use graph::prelude::ethabi::ethereum_types::U256; -use graph::prelude::ethabi::ethereum_types::U64; -use graph::prelude::ethabi::Address; -use graph::prelude::ethabi::LogParam; -use graph::prelude::web3::types::Block; -use graph::prelude::web3::types::Log; -use graph::prelude::web3::types::Transaction; -use graph::prelude::web3::types::TransactionReceipt; +use graph::prelude::alloy::consensus::Transaction as TransactionTrait; +use graph::prelude::alloy::network::AnyTransactionReceipt as AlloyTransactionReceipt; +use graph::prelude::alloy::network::TransactionResponse; +use graph::prelude::alloy::primitives::{Address, B256, U256}; +use graph::prelude::alloy::rpc::types::Log; use graph::prelude::BlockNumber; use graph::prelude::BlockPtr; +use graph::prelude::LightEthereumBlock; use graph::prelude::{CheapClone, EthereumCall}; use graph::runtime::asc_new; use graph::runtime::gas::GasCounter; @@ -38,26 +35,23 @@ use crate::runtime::abi::AscEthereumTransaction_0_0_1; use crate::runtime::abi::AscEthereumTransaction_0_0_2; use crate::runtime::abi::AscEthereumTransaction_0_0_6; -// ETHDEP: This should be defined in only one place. -type LightEthereumBlock = Block; - -static U256_DEFAULT: U256 = U256::zero(); +static U256_DEFAULT: U256 = U256::ZERO; pub enum MappingTrigger { Log { block: Arc, - transaction: Arc, + transaction: Arc, log: Arc, - params: Vec, - receipt: Option>, + params: Vec, + receipt: Option>, calls: Vec, }, Call { block: Arc, - transaction: Arc, + transaction: Arc, call: Arc, - inputs: Vec, - outputs: Vec, + inputs: Vec, + outputs: Vec, }, Block { block: Arc, @@ -65,7 +59,7 @@ pub enum MappingTrigger { } impl MappingTriggerTrait for MappingTrigger { - fn error_context(&self) -> std::string::String { + fn error_context(&self) -> String { let transaction_id = match self { MappingTrigger::Log { log, .. } => log.transaction_hash, MappingTrigger::Call { call, .. } => call.transaction_hash, @@ -85,15 +79,15 @@ impl std::fmt::Debug for MappingTrigger { #[derive(Debug)] enum MappingTriggerWithoutBlock { Log { - _transaction: Arc, + _transaction: Arc, _log: Arc, - _params: Vec, + _params: Vec, }, Call { - _transaction: Arc, + _transaction: Arc, _call: Arc, - _inputs: Vec, - _outputs: Vec, + _inputs: Vec, + _outputs: Vec, }, Block, } @@ -238,13 +232,13 @@ impl ToAscPtr for MappingTrigger { #[derive(Clone, Debug)] pub struct LogPosition { pub index: usize, - pub receipt: Arc, + pub receipt: Arc, pub requires_transaction_receipt: bool, } #[derive(Clone, Debug)] pub enum LogRef { - FullLog(Arc, Option>), + FullLog(Arc, Option>), LogPosition(LogPosition), } @@ -252,7 +246,7 @@ impl LogRef { pub fn log(&self) -> &Log { match self { LogRef::FullLog(log, _) => log.as_ref(), - LogRef::LogPosition(pos) => pos.receipt.logs.get(pos.index).unwrap(), + LogRef::LogPosition(pos) => pos.receipt.logs().get(pos.index).unwrap(), } } @@ -262,7 +256,7 @@ impl LogRef { /// For `LogPosition` variants, only returns the receipt if the /// `requires_transaction_receipt` flag is true, otherwise returns None /// even though the receipt is stored internally. - pub fn receipt(&self) -> Option<&Arc> { + pub fn receipt(&self) -> Option<&Arc> { match self { LogRef::FullLog(_, receipt) => receipt.as_ref(), LogRef::LogPosition(pos) => { @@ -275,28 +269,28 @@ impl LogRef { } } - pub fn log_index(&self) -> Option { + pub fn log_index(&self) -> Option { self.log().log_index } - pub fn transaction_index(&self) -> Option { + pub fn transaction_index(&self) -> Option { self.log().transaction_index } - fn transaction_hash(&self) -> Option { + fn transaction_hash(&self) -> Option { self.log().transaction_hash } - pub fn block_hash(&self) -> Option { + pub fn block_hash(&self) -> Option { self.log().block_hash } - pub fn block_number(&self) -> Option { + pub fn block_number(&self) -> Option { self.log().block_number } - pub fn address(&self) -> &H160 { - &self.log().address + pub fn address(&self) -> &Address { + &self.log().inner.address } } @@ -339,14 +333,14 @@ impl EthereumTrigger { EthereumTrigger::Block(block_ptr, _) => block_ptr.number, EthereumTrigger::Call(call) => call.block_number, EthereumTrigger::Log(log_ref) => { - i32::try_from(log_ref.block_number().unwrap().as_u64()).unwrap() + i32::try_from(log_ref.block_number().unwrap()).unwrap() } } } - pub fn block_hash(&self) -> H256 { + pub fn block_hash(&self) -> B256 { match self { - EthereumTrigger::Block(block_ptr, _) => block_ptr.hash_as_h256(), + EthereumTrigger::Block(block_ptr, _) => block_ptr.hash.as_b256(), EthereumTrigger::Call(call) => call.block_hash, EthereumTrigger::Log(log_ref) => log_ref.block_hash().unwrap(), } @@ -359,7 +353,7 @@ impl EthereumTrigger { Some(address) } EthereumTrigger::Call(call) => Some(&call.to), - EthereumTrigger::Log(log_ref) => Some(&log_ref.address()), + EthereumTrigger::Log(log_ref) => Some(log_ref.address()), // Unfiltered block triggers match any data source address. EthereumTrigger::Block(_, EthereumBlockTriggerType::End) => None, EthereumTrigger::Block(_, EthereumBlockTriggerType::Start) => None, @@ -390,23 +384,21 @@ impl Ord for EthereumTrigger { // Calls vs. events are logged by their tx index; // if they are from the same transaction, events come first (Self::Call(a), Self::Log(b)) - if a.transaction_index == b.transaction_index().unwrap().as_u64() => + if a.transaction_index == b.transaction_index().unwrap() => { Ordering::Greater } (Self::Log(a), Self::Call(b)) - if a.transaction_index().unwrap().as_u64() == b.transaction_index => + if a.transaction_index().unwrap() == b.transaction_index => { Ordering::Less } - (Self::Call(a), Self::Log(b)) => a - .transaction_index - .cmp(&b.transaction_index().unwrap().as_u64()), - (Self::Log(a), Self::Call(b)) => a - .transaction_index() - .unwrap() - .as_u64() - .cmp(&b.transaction_index), + (Self::Call(a), Self::Log(b)) => { + a.transaction_index.cmp(&b.transaction_index().unwrap()) + } + (Self::Log(a), Self::Call(b)) => { + a.transaction_index().unwrap().cmp(&b.transaction_index) + } } } } @@ -437,137 +429,140 @@ impl TriggerData for EthereumTrigger { } fn address_match(&self) -> Option<&[u8]> { - self.address().map(|address| address.as_bytes()) + self.address().map(|address| address.as_slice()) } } /// Ethereum block data. #[derive(Clone, Debug)] pub struct EthereumBlockData<'a> { - block: &'a Block, + block: &'a LightEthereumBlock, } -impl<'a> From<&'a Block> for EthereumBlockData<'a> { - fn from(block: &'a Block) -> EthereumBlockData<'a> { +impl<'a> From<&'a LightEthereumBlock> for EthereumBlockData<'a> { + fn from(block: &'a LightEthereumBlock) -> EthereumBlockData<'a> { EthereumBlockData { block } } } impl<'a> EthereumBlockData<'a> { - pub fn hash(&self) -> &H256 { - self.block.hash.as_ref().unwrap() + pub fn hash(&self) -> &B256 { + &self.block.inner().header.hash } - pub fn parent_hash(&self) -> &H256 { - &self.block.parent_hash + pub fn parent_hash(&self) -> &B256 { + &self.block.inner().header.parent_hash } - pub fn uncles_hash(&self) -> &H256 { - &self.block.uncles_hash + pub fn uncles_hash(&self) -> &B256 { + &self.block.inner().header.ommers_hash } - pub fn author(&self) -> &H160 { - &self.block.author + pub fn author(&self) -> &Address { + &self.block.inner().header.beneficiary } - pub fn state_root(&self) -> &H256 { - &self.block.state_root + pub fn state_root(&self) -> &B256 { + &self.block.inner().header.state_root } - pub fn transactions_root(&self) -> &H256 { - &self.block.transactions_root + pub fn transactions_root(&self) -> &B256 { + &self.block.inner().header.transactions_root } - pub fn receipts_root(&self) -> &H256 { - &self.block.receipts_root + pub fn receipts_root(&self) -> &B256 { + &self.block.inner().header.receipts_root } - pub fn number(&self) -> U64 { - self.block.number.unwrap() + pub fn number(&self) -> u64 { + self.block.number_u64() } - pub fn gas_used(&self) -> &U256 { - &self.block.gas_used + pub fn gas_used(&self) -> u64 { + self.block.inner().header.gas_used } - pub fn gas_limit(&self) -> &U256 { - &self.block.gas_limit + pub fn gas_limit(&self) -> u64 { + self.block.inner().header.gas_limit } - pub fn timestamp(&self) -> &U256 { - &self.block.timestamp + pub fn timestamp(&self) -> u64 { + self.block.inner().header.timestamp } pub fn difficulty(&self) -> &U256 { - &self.block.difficulty + &self.block.inner().header.difficulty } pub fn total_difficulty(&self) -> &U256 { self.block + .inner() + .header .total_difficulty .as_ref() .unwrap_or(&U256_DEFAULT) } pub fn size(&self) -> &Option { - &self.block.size + &self.block.inner().header.size } - pub fn base_fee_per_gas(&self) -> &Option { - &self.block.base_fee_per_gas + pub fn base_fee_per_gas(&self) -> &Option { + &self.block.inner().header.base_fee_per_gas } } /// Ethereum transaction data. #[derive(Clone, Debug)] pub struct EthereumTransactionData<'a> { - tx: &'a Transaction, + tx: &'a AnyTransaction, + base_fee_per_gas: Option, } impl<'a> EthereumTransactionData<'a> { // We don't implement `From` because it causes confusion with the `from` // accessor method - fn new(tx: &'a Transaction) -> EthereumTransactionData<'a> { - EthereumTransactionData { tx } + fn new(tx: &'a AnyTransaction, base_fee_per_gas: Option) -> EthereumTransactionData<'a> { + EthereumTransactionData { + tx, + base_fee_per_gas, + } } - pub fn hash(&self) -> &H256 { - &self.tx.hash + pub fn hash(&self) -> B256 { + self.tx.tx_hash() } - pub fn index(&self) -> U128 { - self.tx.transaction_index.unwrap().as_u64().into() + pub fn index(&self) -> u64 { + self.tx.transaction_index.unwrap() } - pub fn from(&self) -> &H160 { - // unwrap: this is always `Some` for txns that have been mined - // (see https://github.com/tomusdrw/rust-web3/pull/407) - self.tx.from.as_ref().unwrap() + pub fn from(&self) -> Address { + self.tx.from() } - pub fn to(&self) -> &Option { - &self.tx.to + pub fn to(&self) -> Option
{ + self.tx.to() } - pub fn value(&self) -> &U256 { - &self.tx.value + pub fn value(&self) -> U256 { + self.tx.value() } - pub fn gas_limit(&self) -> &U256 { - &self.tx.gas + pub fn gas_limit(&self) -> u64 { + self.tx.gas_limit() } - pub fn gas_price(&self) -> &U256 { - // EIP-1559 made this optional. - self.tx.gas_price.as_ref().unwrap_or(&U256_DEFAULT) + pub fn gas_price(&self) -> u128 { + self.tx.effective_gas_price(self.base_fee_per_gas) } pub fn input(&self) -> &[u8] { - &self.tx.input.0 + self.tx.input() } - pub fn nonce(&self) -> &U256 { - &self.tx.nonce + pub fn nonce(&self) -> u64 { + self.tx.nonce() } } @@ -576,45 +571,46 @@ impl<'a> EthereumTransactionData<'a> { pub struct EthereumEventData<'a> { pub block: EthereumBlockData<'a>, pub transaction: EthereumTransactionData<'a>, - pub params: &'a [LogParam], + pub params: &'a [abi::DynSolParam], log: &'a Log, } impl<'a> EthereumEventData<'a> { pub fn new( - block: &'a Block, - tx: &'a Transaction, + block: &'a LightEthereumBlock, + tx: &'a AnyTransaction, log: &'a Log, - params: &'a [LogParam], + params: &'a [abi::DynSolParam], ) -> Self { EthereumEventData { block: EthereumBlockData::from(block), - transaction: EthereumTransactionData::new(tx), + transaction: EthereumTransactionData::new(tx, block.base_fee_per_gas()), log, params, } } pub fn address(&self) -> &Address { - &self.log.address + &self.log.inner.address } - pub fn log_index(&self) -> &U256 { - self.log.log_index.as_ref().unwrap_or(&U256_DEFAULT) + pub fn log_index(&self) -> u64 { + self.log.log_index.unwrap_or(0) } - pub fn transaction_log_index(&self) -> &U256 { + pub fn transaction_log_index(&self) -> u64 { // We purposely use the `log_index` here. Geth does not support // `transaction_log_index`, and subgraphs that use it only care that // it identifies the log, the specific value is not important. Still // this will change the output of subgraphs that use this field. // // This was initially changed in commit b95c6953 - self.log.log_index.as_ref().unwrap_or(&U256_DEFAULT) + self.log.log_index.unwrap_or(0) } - pub fn log_type(&self) -> &Option { - &self.log.log_type + pub fn log_type(&self) -> Option { + // This field was present in old rust-web3 Block, but alloy doesn't have it. + None } } @@ -623,22 +619,22 @@ impl<'a> EthereumEventData<'a> { pub struct EthereumCallData<'a> { pub block: EthereumBlockData<'a>, pub transaction: EthereumTransactionData<'a>, - pub inputs: &'a [LogParam], - pub outputs: &'a [LogParam], + pub inputs: &'a [abi::DynSolParam], + pub outputs: &'a [abi::DynSolParam], call: &'a EthereumCall, } impl<'a> EthereumCallData<'a> { fn new( - block: &'a Block, - transaction: &'a Transaction, + block: &'a LightEthereumBlock, + transaction: &'a AnyTransaction, call: &'a EthereumCall, - inputs: &'a [LogParam], - outputs: &'a [LogParam], + inputs: &'a [abi::DynSolParam], + outputs: &'a [abi::DynSolParam], ) -> EthereumCallData<'a> { EthereumCallData { block: EthereumBlockData::from(block), - transaction: EthereumTransactionData::new(transaction), + transaction: EthereumTransactionData::new(transaction, block.base_fee_per_gas()), inputs, outputs, call, diff --git a/chain/near/src/codec.rs b/chain/near/src/codec.rs index 6f0f2f7af4d..905495e7ae3 100644 --- a/chain/near/src/codec.rs +++ b/chain/near/src/codec.rs @@ -7,18 +7,17 @@ pub mod pbcodec; pub mod substreams_triggers; use graph::{ - blockchain::Block as BlockchainBlock, - blockchain::{BlockPtr, BlockTime}, - prelude::{hex, web3::types::H256, BlockNumber}, + blockchain::{Block as BlockchainBlock, BlockPtr, BlockTime}, + prelude::{alloy::primitives::B256, hex, BlockNumber}, }; use std::convert::TryFrom; use std::fmt::LowerHex; pub use pbcodec::*; -impl From<&CryptoHash> for H256 { +impl From<&CryptoHash> for B256 { fn from(input: &CryptoHash) -> Self { - H256::from_slice(&input.bytes) + B256::from_slice(&input.bytes) } } @@ -31,7 +30,7 @@ impl LowerHex for &CryptoHash { impl BlockHeader { pub fn parent_ptr(&self) -> Option { match (self.prev_hash.as_ref(), self.prev_height) { - (Some(hash), number) => Some(BlockPtr::from((H256::from(hash), number))), + (Some(hash), number) => Some(BlockPtr::from((B256::from(hash), number))), _ => None, } } @@ -39,7 +38,7 @@ impl BlockHeader { impl<'a> From<&'a BlockHeader> for BlockPtr { fn from(b: &'a BlockHeader) -> BlockPtr { - BlockPtr::from((H256::from(b.hash.as_ref().unwrap()), b.height)) + BlockPtr::from((B256::from(b.hash.as_ref().unwrap()), b.height)) } } diff --git a/chain/near/src/trigger.rs b/chain/near/src/trigger.rs index d604f97bc14..a36e640b7c1 100644 --- a/chain/near/src/trigger.rs +++ b/chain/near/src/trigger.rs @@ -3,8 +3,8 @@ use graph::blockchain::Block; use graph::blockchain::MappingTriggerTrait; use graph::blockchain::TriggerData; use graph::derive::CheapClone; +use graph::prelude::alloy::primitives::B256; use graph::prelude::hex; -use graph::prelude::web3::types::H256; use graph::prelude::BlockNumber; use graph::runtime::HostExportError; use graph::runtime::{asc_new, gas::GasCounter, AscHeap, AscPtr}; @@ -80,10 +80,10 @@ impl NearTrigger { } } - pub fn block_hash(&self) -> H256 { + pub fn block_hash(&self) -> B256 { match self { - NearTrigger::Block(block) => block.ptr().hash_as_h256(), - NearTrigger::Receipt(receipt) => receipt.block.ptr().hash_as_h256(), + NearTrigger::Block(block) => block.ptr().hash.as_b256(), + NearTrigger::Receipt(receipt) => receipt.block.ptr().hash.as_b256(), } } diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 6a5edc5f56b..cfecf144f1b 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] +alloy = { workspace = true } base64 = "=0.21.7" anyhow = "1.0" async-trait = { workspace = true } @@ -83,12 +84,6 @@ parking_lot = "0.12.5" itertools = "0.14.0" defer = "0.2" -# Our fork contains patches to make some fields optional for Celo and Fantom compatibility. -# Without the "arbitrary_precision" feature, we get the error `data did not match any variant of untagged enum Response`. -web3 = { git = "https://github.com/graphprotocol/rust-web3", branch = "graph-patches-onto-0.18", features = [ - "arbitrary_precision", - "test", -] } serde_plain = "1.0.2" csv = "1.3.1" object_store = { version = "0.12.4", features = ["gcp"] } diff --git a/graph/src/abi/event_ext.rs b/graph/src/abi/event_ext.rs new file mode 100644 index 00000000000..94088dfcaae --- /dev/null +++ b/graph/src/abi/event_ext.rs @@ -0,0 +1,169 @@ +use std::collections::VecDeque; + +use alloy::json_abi::Event; +use alloy::rpc::types::Log; +use anyhow::anyhow; +use anyhow::Result; + +use crate::abi::{DynSolParam, DynSolValue}; + +pub trait EventExt { + fn decode_log(&self, log: &Log) -> Result>; +} + +impl EventExt for Event { + fn decode_log(&self, log: &Log) -> Result> { + let log_data = log.data(); + let decoded_event = alloy::dyn_abi::EventExt::decode_log(self, &log_data)?; + let mut indexed: VecDeque = decoded_event.indexed.into(); + let mut body: VecDeque = decoded_event.body.into(); + + if self.inputs.len() != indexed.len() + body.len() { + return Err(anyhow!( + "unexpected number of decoded event inputs; expected {}, got {}", + self.inputs.len(), + indexed.len() + body.len(), + )); + } + + let mut decoded_params = Vec::with_capacity(self.inputs.len()); + + for input in &self.inputs { + decoded_params.push(DynSolParam { + name: input.name.clone(), + value: { + if input.indexed { + indexed.pop_front().unwrap() + } else { + body.pop_front().unwrap() + } + }, + }); + } + + Ok(decoded_params) + } +} + +#[cfg(test)] +mod tests { + use alloy::dyn_abi::DynSolValue; + use alloy::primitives::{LogData, U256}; + + use super::*; + + fn make_log(topics: &[[u8; 32]], data: Vec) -> Log { + Log { + inner: alloy::primitives::Log { + address: [1; 20].into(), + data: LogData::new_unchecked(topics.iter().map(Into::into).collect(), data.into()), + }, + block_hash: None, + block_number: None, + block_timestamp: None, + transaction_hash: None, + transaction_index: None, + log_index: None, + removed: false, + } + } + + #[test] + fn decode_log_no_topic_0() { + let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap(); + let a = U256::from(10).to_be_bytes::<32>(); + let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode(); + + let log = make_log(&[a], b); + let err = event.decode_log(&log).unwrap_err(); + + assert_eq!( + err.to_string(), + "invalid log topic list length: expected 2 topics, got 1", + ); + } + + #[test] + fn decode_log_invalid_topic_0() { + let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap(); + let a = U256::from(10).to_be_bytes::<32>(); + let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode(); + + let log = make_log(&[[0; 32], a], b); + let err = event.decode_log(&log).unwrap_err(); + + assert!(err.to_string().starts_with("invalid event signature:")); + } + + #[test] + fn decode_log_success() { + let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap(); + let topic_0 = event.selector().0; + let a = U256::from(10).to_be_bytes::<32>(); + let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode(); + + let log = make_log(&[topic_0, a], b); + let resp = event.decode_log(&log).unwrap(); + + assert_eq!( + resp, + vec![ + DynSolParam { + name: "a".to_owned(), + value: DynSolValue::Uint(U256::from(10), 256), + }, + DynSolParam { + name: "b".to_owned(), + value: DynSolValue::FixedBytes([10; 32].into(), 32), + } + ], + ); + } + + #[test] + fn decode_log_too_many_topics() { + let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap(); + let topic_0 = event.selector().0; + let a = U256::from(10).to_be_bytes::<32>(); + let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode(); + + let log = make_log(&[topic_0, a, a, a, a], b); + let err = event.decode_log(&log).unwrap_err(); + + assert_eq!( + err.to_string(), + "invalid log topic list length: expected 2 topics, got 5" + ); + } + + #[test] + fn decode_log_when_indexed_param_is_not_the_first() { + let event = Event::parse("event X(uint256 a, uint256 indexed b, bytes32 c)").unwrap(); + let topic_0 = event.selector().0; + let a = DynSolValue::Uint(U256::from(10), 32); + let b = U256::from(20).to_be_bytes::<32>(); + let c = DynSolValue::FixedBytes([30; 32].into(), 32); + let data = DynSolValue::Tuple(vec![a, c]).abi_encode(); + + let log = make_log(&[topic_0, b], data); + let resp = event.decode_log(&log).unwrap(); + + assert_eq!( + resp, + vec![ + DynSolParam { + name: "a".to_owned(), + value: DynSolValue::Uint(U256::from(10), 256), + }, + DynSolParam { + name: "b".to_owned(), + value: DynSolValue::Uint(U256::from(20), 256), + }, + DynSolParam { + name: "c".to_owned(), + value: DynSolValue::FixedBytes([30; 32].into(), 32), + } + ], + ); + } +} diff --git a/graph/src/abi/function_ext.rs b/graph/src/abi/function_ext.rs new file mode 100644 index 00000000000..3264dd10a35 --- /dev/null +++ b/graph/src/abi/function_ext.rs @@ -0,0 +1,303 @@ +use std::borrow::Cow; + +use alloy::dyn_abi::DynSolType; +use alloy::dyn_abi::DynSolValue; +use alloy::dyn_abi::Specifier; +use alloy::json_abi::Function; +use alloy::json_abi::Param; +use anyhow::anyhow; +use anyhow::Result; +use itertools::Itertools; + +use crate::abi::DynSolValueExt; + +pub trait FunctionExt { + /// Returns the signature of this function in the following formats: + /// - if the function has no outputs: `$name($($inputs),*)` + /// - if the function has outputs: `$name($($inputs),*):($(outputs),*)` + /// + /// Examples: + /// - `functionName()` + /// - `functionName():(uint256)` + /// - `functionName(bool):(uint256,string)` + /// - `functionName(uint256,bytes32):(string,uint256)` + fn signature_compat(&self) -> String; + + /// ABI-decodes the given data according to the function's input types. + fn abi_decode_input(&self, data: &[u8]) -> Result>; + + /// ABI-decodes the given data according to the function's output types. + fn abi_decode_output(&self, data: &[u8]) -> Result>; + + /// ABI-encodes the given values, prefixed by the function's selector, if any. + /// + /// This behaviour is to ensure consistency with `ethabi`. + fn abi_encode_input(&self, values: &[DynSolValue]) -> Result>; +} + +impl FunctionExt for Function { + fn signature_compat(&self) -> String { + let name = &self.name; + let inputs = &self.inputs; + let outputs = &self.outputs; + + // This is what `alloy` uses internally when creating signatures. + const MAX_SOL_TYPE_LEN: usize = 32; + + let mut sig_cap = name.len() + 1 + inputs.len() * MAX_SOL_TYPE_LEN + 1; + + if !outputs.is_empty() { + sig_cap = sig_cap + 2 + outputs.len() * MAX_SOL_TYPE_LEN + 1; + } + + let mut sig = String::with_capacity(sig_cap); + + sig.push_str(&name); + signature_part(&inputs, &mut sig); + + if !outputs.is_empty() { + sig.push(':'); + signature_part(&outputs, &mut sig); + } + + sig + } + + fn abi_decode_input(&self, data: &[u8]) -> Result> { + (self as &dyn alloy::dyn_abi::FunctionExt) + .abi_decode_input(data) + .map_err(Into::into) + } + + fn abi_decode_output(&self, data: &[u8]) -> Result> { + (self as &dyn alloy::dyn_abi::FunctionExt) + .abi_decode_output(data) + .map_err(Into::into) + } + + fn abi_encode_input(&self, values: &[DynSolValue]) -> Result> { + let inputs = &self.inputs; + + if inputs.len() != values.len() { + return Err(anyhow!( + "unexpected number of values; expected {}, got {}", + inputs.len(), + values.len(), + )); + } + + let mut fixed_values = Vec::with_capacity(values.len()); + + for (i, input) in inputs.iter().enumerate() { + let ty = input.resolve()?; + let val = &values[i]; + + fixed_values.push(fix_type_size(&ty, val)?); + } + + if fixed_values.iter().all(|x| matches!(x, Cow::Borrowed(_))) { + return (self as &dyn alloy::dyn_abi::JsonAbiExt) + .abi_encode_input(values) + .map_err(Into::into); + } + + // Required because of `alloy::dyn_abi::JsonAbiExt::abi_encode_input` API; + let owned_fixed_values = fixed_values + .into_iter() + .map(|x| x.into_owned()) + .collect_vec(); + + (self as &dyn alloy::dyn_abi::JsonAbiExt) + .abi_encode_input(&owned_fixed_values) + .map_err(Into::into) + } +} + +// An efficient way to compute a part of the signature without new allocations. +fn signature_part(params: &[Param], out: &mut String) { + out.push('('); + + match params.len() { + 0 => {} + 1 => { + params[0].selector_type_raw(out); + } + n => { + params[0].selector_type_raw(out); + + for i in 1..n { + out.push(','); + params[i].selector_type_raw(out); + } + } + } + + out.push(')'); +} + +// Alloy is stricter in type checking than `ehtabi` and requires that the decoded values have +// exactly the same number of bits / bytes as the type used for checking. +// +// This is a problem because in some ASC conversions we lose the original number of bits / bytes +// if the actual data takes less memory. +// +// This method fixes that in a simple but not very cheap way, by encoding the value and trying +// to decode it again using the given type. The result fixes the number of bits / bytes in the +// decoded values, so we can use `alloy` methods that have strict type checking internally. +fn fix_type_size<'a>(ty: &DynSolType, val: &'a DynSolValue) -> Result> { + if val.matches(ty) { + return Ok(Cow::Borrowed(val)); + } + + if !val.type_check(ty) { + return Err(anyhow!( + "invalid value type; expected '{}', got '{:?}'", + ty.sol_type_name(), + val.sol_type_name(), + )); + } + + let bytes = val.abi_encode(); + let new_val = ty.abi_decode(&bytes)?; + + Ok(Cow::Owned(new_val)) +} + +#[cfg(test)] +mod tests { + use alloy::primitives::I256; + use alloy::primitives::U256; + + use super::*; + + fn s(f: &str) -> String { + Function::parse(f).unwrap().signature_compat() + } + + fn u256(u: u64) -> U256 { + U256::from(u) + } + + fn i256(i: i32) -> I256 { + I256::try_from(i).unwrap() + } + + #[test] + fn signature_compat_no_inputs_no_outputs() { + assert_eq!(s("x()"), "x()"); + } + + #[test] + fn signature_compat_one_input_no_outputs() { + assert_eq!(s("x(uint256 a)"), "x(uint256)"); + } + + #[test] + fn signature_compat_multiple_inputs_no_outputs() { + assert_eq!(s("x(uint256 a, bytes32 b)"), "x(uint256,bytes32)"); + } + + #[test] + fn signature_compat_no_inputs_one_output() { + assert_eq!(s("x() returns (uint256)"), "x():(uint256)"); + } + + #[test] + fn signature_compat_no_inputs_multiple_outputs() { + assert_eq!(s("x() returns (uint256, bytes32)"), "x():(uint256,bytes32)"); + } + + #[test] + fn signature_compat_multiple_inputs_multiple_outputs() { + assert_eq!( + s("x(bytes32 a, uint256 b) returns (uint256, bytes32)"), + "x(bytes32,uint256):(uint256,bytes32)", + ); + } + + #[test] + fn abi_decode_input() { + use DynSolValue::{Int, Tuple, Uint}; + + let f = Function::parse("x(uint256 a, int256 b)").unwrap(); + let data = Tuple(vec![Uint(u256(10), 256), Int(i256(-10), 256)]).abi_encode_params(); + let inputs = f.abi_decode_input(&data).unwrap(); + + assert_eq!(inputs, vec![Uint(u256(10), 256), Int(i256(-10), 256)]); + } + + #[test] + fn abi_decode_output() { + use DynSolValue::{Int, Tuple, Uint}; + + let f = Function::parse("x() returns (uint256 a, int256 b)").unwrap(); + let data = Tuple(vec![Uint(u256(10), 256), Int(i256(-10), 256)]).abi_encode_params(); + let outputs = f.abi_decode_output(&data).unwrap(); + + assert_eq!(outputs, vec![Uint(u256(10), 256), Int(i256(-10), 256)]); + } + + #[test] + fn abi_encode_input_no_values() { + let f = Function::parse("x(uint256 a, int256 b)").unwrap(); + let err = f.abi_encode_input(&[]).unwrap_err(); + + assert_eq!( + err.to_string(), + "unexpected number of values; expected 2, got 0", + ); + } + + #[test] + fn abi_encode_input_too_many_values() { + use DynSolValue::Bool; + + let f = Function::parse("x(uint256 a, int256 b)").unwrap(); + + let err = f + .abi_encode_input(&[Bool(true), Bool(false), Bool(true)]) + .unwrap_err(); + + assert_eq!( + err.to_string(), + "unexpected number of values; expected 2, got 3", + ); + } + + #[test] + fn abi_encode_input_invalid_types() { + use DynSolValue::Bool; + + let f = Function::parse("x(uint256 a, int256 b)").unwrap(); + let err = f.abi_encode_input(&[Bool(true), Bool(false)]).unwrap_err(); + assert!(err.to_string().starts_with("invalid value type;")); + } + + #[test] + fn abi_encode_success() { + use DynSolValue::{Bool, Uint}; + + let f = Function::parse("x(uint256 a, bool b)").unwrap(); + let a = Uint(u256(10), 256); + let b = Bool(true); + + let data = f.abi_encode_input(&[a.clone(), b.clone()]).unwrap(); + let inputs = f.abi_decode_input(&data[4..]).unwrap(); + + assert_eq!(inputs, vec![a, b]); + } + + #[test] + fn abi_encode_success_with_size_fix() { + use DynSolValue::{Int, Uint}; + + let f = Function::parse("x(uint256 a, int256 b)").unwrap(); + let a = Uint(u256(10), 32); + let b = Int(i256(-10), 32); + + let data = f.abi_encode_input(&[a, b]).unwrap(); + let inputs = f.abi_decode_input(&data[4..]).unwrap(); + + assert_eq!(inputs, vec![Uint(u256(10), 256), Int(i256(-10), 256)]); + } +} diff --git a/graph/src/abi/mod.rs b/graph/src/abi/mod.rs new file mode 100644 index 00000000000..9bedeb0e3b2 --- /dev/null +++ b/graph/src/abi/mod.rs @@ -0,0 +1,20 @@ +mod event_ext; +mod function_ext; +mod param; +mod value_ext; + +pub use alloy::dyn_abi::DynSolType; +pub use alloy::dyn_abi::DynSolValue; + +pub use alloy::json_abi::Event; +pub use alloy::json_abi::Function; +pub use alloy::json_abi::JsonAbi; +pub use alloy::json_abi::StateMutability; + +pub use alloy::primitives::I256; +pub use alloy::primitives::U256 as AlloyU256; + +pub use self::event_ext::EventExt; +pub use self::function_ext::FunctionExt; +pub use self::param::DynSolParam; +pub use self::value_ext::DynSolValueExt; diff --git a/graph/src/abi/param.rs b/graph/src/abi/param.rs new file mode 100644 index 00000000000..49e0f0878ea --- /dev/null +++ b/graph/src/abi/param.rs @@ -0,0 +1,7 @@ +use alloy::dyn_abi::DynSolValue; + +#[derive(Clone, Debug, PartialEq)] +pub struct DynSolParam { + pub name: String, + pub value: DynSolValue, +} diff --git a/graph/src/abi/value_ext.rs b/graph/src/abi/value_ext.rs new file mode 100644 index 00000000000..cb0f220e036 --- /dev/null +++ b/graph/src/abi/value_ext.rs @@ -0,0 +1,277 @@ +use alloy::dyn_abi::DynSolType; +use alloy::dyn_abi::DynSolValue; +use anyhow::anyhow; +use anyhow::Result; +use itertools::Itertools; + +pub trait DynSolValueExt { + /// Creates a fixed-byte decoded value from a slice. + /// + /// Fails if the source slice exceeds 32 bytes. + fn fixed_bytes_from_slice(s: &[u8]) -> Result; + + /// Returns the decoded value as a string. + /// + /// The resulting string contains no type information. + fn to_string(&self) -> String; + + /// Checks whether the value is of the specified type. + /// + /// For types with additional size information, returns true if the size of the value is less + /// than or equal to the size of the specified type. + #[must_use] + fn type_check(&self, ty: &DynSolType) -> bool; +} + +impl DynSolValueExt for DynSolValue { + fn fixed_bytes_from_slice(s: &[u8]) -> Result { + let num_bytes = s.len(); + + if num_bytes > 32 { + return Err(anyhow!( + "input slice must contain a maximum of 32 bytes, got {num_bytes}" + )); + } + + let mut bytes = [0u8; 32]; + + // Access: If `x` is of type `bytesI`, then `x[k]` for `0 <= k < I` returns the `k`th byte. + // Ref: + bytes[..num_bytes].copy_from_slice(s); + + Ok(Self::FixedBytes(bytes.into(), num_bytes)) + } + + fn to_string(&self) -> String { + let s = |v: &[Self]| v.iter().map(|x| x.to_string()).collect_vec().join(","); + + // Output format is taken from `ethabi`; + // See: + match self { + Self::Bool(v) => v.to_string(), + Self::Int(v, _) => format!("{v:x}"), + Self::Uint(v, _) => format!("{v:x}"), + Self::FixedBytes(v, _) => hex::encode(v), + Self::Address(v) => format!("{v:x}"), + Self::Function(v) => format!("{v:x}"), + Self::Bytes(v) => hex::encode(v), + Self::String(v) => v.to_owned(), + Self::Array(v) => format!("[{}]", s(v)), + Self::FixedArray(v) => format!("[{}]", s(v)), + Self::Tuple(v) => format!("({})", s(v)), + } + } + + fn type_check(&self, ty: &DynSolType) -> bool { + match self { + Self::Bool(_) => *ty == DynSolType::Bool, + Self::Int(_, a) => { + if let DynSolType::Int(b) = ty { + b >= a + } else { + false + } + } + Self::Uint(_, a) => { + if let DynSolType::Uint(b) = ty { + b >= a + } else { + false + } + } + Self::FixedBytes(_, a) => { + if let DynSolType::FixedBytes(b) = ty { + b >= a + } else { + false + } + } + Self::Address(_) => *ty == DynSolType::Address, + Self::Function(_) => *ty == DynSolType::Function, + Self::Bytes(_) => *ty == DynSolType::Bytes, + Self::String(_) => *ty == DynSolType::String, + Self::Array(values) => { + if let DynSolType::Array(ty) = ty { + values.iter().all(|x| x.type_check(ty)) + } else { + false + } + } + Self::FixedArray(values) => { + if let DynSolType::FixedArray(ty, size) = ty { + *size == values.len() && values.iter().all(|x| x.type_check(ty)) + } else { + false + } + } + Self::Tuple(values) => { + if let DynSolType::Tuple(types) = ty { + types.len() == values.len() + && values + .iter() + .enumerate() + .all(|(i, x)| x.type_check(&types[i])) + } else { + false + } + } + } + } +} + +#[cfg(test)] +mod tests { + use alloy::primitives::I256; + use alloy::primitives::U256; + + use super::*; + + #[test] + fn fixed_bytes_from_slice_empty_slice() { + let val = DynSolValue::fixed_bytes_from_slice(&[]).unwrap(); + let bytes = [0; 32]; + + assert_eq!(val, DynSolValue::FixedBytes(bytes.into(), 0)); + } + + #[test] + fn fixed_bytes_from_slice_one_byte() { + let val = DynSolValue::fixed_bytes_from_slice(&[10]).unwrap(); + let mut bytes = [0; 32]; + bytes[0] = 10; + + assert_eq!(val, DynSolValue::FixedBytes(bytes.into(), 1)); + } + + #[test] + fn fixed_bytes_from_slice_multiple_bytes() { + let val = DynSolValue::fixed_bytes_from_slice(&[10, 20, 30]).unwrap(); + let mut bytes = [0; 32]; + bytes[0] = 10; + bytes[1] = 20; + bytes[2] = 30; + + assert_eq!(val, DynSolValue::FixedBytes(bytes.into(), 3)); + } + + #[test] + fn fixed_bytes_from_slice_max_bytes() { + let val = DynSolValue::fixed_bytes_from_slice(&[10; 32]).unwrap(); + let bytes = [10; 32]; + + assert_eq!(val, DynSolValue::FixedBytes(bytes.into(), 32)); + } + + #[test] + fn fixed_bytes_from_slice_too_many_bytes() { + DynSolValue::fixed_bytes_from_slice(&[10; 33]).unwrap_err(); + } + + #[test] + fn to_string() { + use DynSolValue::*; + + assert_eq!(Bool(false).to_string(), "false"); + assert_eq!(Bool(true).to_string(), "true"); + + assert_eq!( + Int(I256::try_from(-10).unwrap(), 256).to_string(), + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6", + ); + + assert_eq!(Uint(U256::from(10), 256).to_string(), "a"); + + assert_eq!( + FixedBytes([10; 32].into(), 32).to_string(), + "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + ); + + assert_eq!( + Address([10; 20].into()).to_string(), + "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + ); + + assert_eq!( + Function([10; 24].into()).to_string(), + "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + ); + + assert_eq!(Bytes(vec![10, 20, 30]).to_string(), "0a141e"); + + assert_eq!( + String("one two three".to_owned()).to_string(), + "one two three" + ); + + assert_eq!( + Array(vec![String("one".to_owned()), String("two".to_owned())]).to_string(), + "[one,two]", + ); + + assert_eq!( + FixedArray(vec![String("one".to_owned()), String("two".to_owned())]).to_string(), + "[one,two]" + ); + + assert_eq!( + Tuple(vec![String("one".to_owned()), String("two".to_owned())]).to_string(), + "(one,two)" + ); + } + + #[test] + fn type_check() { + use DynSolType as T; + use DynSolValue::*; + + assert!(Bool(true).type_check(&T::Bool)); + assert!(!Bool(true).type_check(&T::Int(256))); + + assert!(!Int(I256::try_from(-10).unwrap(), 32).type_check(&T::Int(24))); + assert!(Int(I256::try_from(-10).unwrap(), 32).type_check(&T::Int(32))); + assert!(Int(I256::try_from(-10).unwrap(), 32).type_check(&T::Int(256))); + assert!(!Int(I256::try_from(-10).unwrap(), 32).type_check(&T::Uint(256))); + + assert!(!Uint(U256::from(10), 32).type_check(&T::Uint(24))); + assert!(Uint(U256::from(10), 32).type_check(&T::Uint(32))); + assert!(Uint(U256::from(10), 32).type_check(&T::Uint(256))); + assert!(!Uint(U256::from(10), 32).type_check(&T::FixedBytes(32))); + + assert!(!FixedBytes([0; 32].into(), 16).type_check(&T::FixedBytes(8))); + assert!(FixedBytes([0; 32].into(), 16).type_check(&T::FixedBytes(16))); + assert!(FixedBytes([0; 32].into(), 16).type_check(&T::FixedBytes(32))); + assert!(!FixedBytes([0; 32].into(), 32).type_check(&T::Address)); + + assert!(Address([0; 20].into()).type_check(&T::Address)); + assert!(!Address([0; 20].into()).type_check(&T::Function)); + + assert!(Function([0; 24].into()).type_check(&T::Function)); + assert!(!Function([0; 24].into()).type_check(&T::Bytes)); + + assert!(Bytes(vec![0, 0, 0]).type_check(&T::Bytes)); + assert!(!Bytes(vec![0, 0, 0]).type_check(&T::String)); + + assert!(String("".to_owned()).type_check(&T::String)); + assert!(!String("".to_owned()).type_check(&T::Array(Box::new(T::Bool)))); + + assert!(Array(vec![Bool(true)]).type_check(&T::Array(Box::new(T::Bool)))); + assert!(!Array(vec![Bool(true)]).type_check(&T::Array(Box::new(T::String)))); + assert!(!Array(vec![Bool(true)]).type_check(&T::FixedArray(Box::new(T::Bool), 1))); + + assert!(!FixedArray(vec![String("".to_owned())]) + .type_check(&T::FixedArray(Box::new(T::Bool), 1))); + assert!(!FixedArray(vec![Bool(true), Bool(false)]) + .type_check(&T::FixedArray(Box::new(T::Bool), 1))); + assert!(FixedArray(vec![Bool(true), Bool(false)]) + .type_check(&T::FixedArray(Box::new(T::Bool), 2))); + assert!(!FixedArray(vec![Bool(true), Bool(false)]) + .type_check(&T::FixedArray(Box::new(T::Bool), 3))); + assert!(!FixedArray(vec![Bool(true), Bool(false)]) + .type_check(&T::Tuple(vec![T::Bool, T::Bool]))); + + assert!(!Tuple(vec![Bool(true), Bool(false)]).type_check(&T::Tuple(vec![T::Bool]))); + assert!(Tuple(vec![Bool(true), Bool(false)]).type_check(&T::Tuple(vec![T::Bool, T::Bool]))); + assert!(!Tuple(vec![Bool(true)]).type_check(&T::Tuple(vec![T::Bool, T::Bool]))); + assert!(!Tuple(vec![Bool(true)]).type_check(&T::Bool)); + } +} diff --git a/graph/src/blockchain/mock.rs b/graph/src/blockchain/mock.rs index 577b5fbc816..d35958d80ad 100644 --- a/graph/src/blockchain/mock.rs +++ b/graph/src/blockchain/mock.rs @@ -16,6 +16,7 @@ use crate::{ DataSourceTemplateInfo, StoreError, }, }; +use alloy::primitives::{B256, U256}; use anyhow::{Error, Result}; use async_trait::async_trait; use serde::Deserialize; @@ -26,7 +27,6 @@ use std::{ convert::TryFrom, sync::Arc, }; -use web3::types::H256; use super::{ block_stream::{self, BlockStream, FirehoseCursor}, @@ -73,7 +73,7 @@ pub fn test_ptr(n: BlockNumber) -> BlockPtr { } pub fn test_ptr_reorged(n: BlockNumber, reorg_n: u32) -> BlockPtr { - let mut hash = H256::from_low_u64_be(n as u64); + let mut hash = B256::from(U256::from(n as u64)); hash[0..4].copy_from_slice(&reorg_n.to_be_bytes()); BlockPtr { hash: hash.into(), @@ -524,7 +524,7 @@ impl ChainStore for MockChainStore { async fn attempt_chain_head_update( self: Arc, _ancestor_count: BlockNumber, - ) -> Result, Error> { + ) -> Result, Error> { unimplemented!() } async fn blocks(self: Arc, _hashes: Vec) -> Result, Error> { @@ -571,7 +571,7 @@ impl ChainStore for MockChainStore { } async fn transaction_receipts_in_block( &self, - _block_ptr: &H256, + _block_ptr: &B256, ) -> Result, StoreError> { unimplemented!() } diff --git a/graph/src/blockchain/mod.rs b/graph/src/blockchain/mod.rs index 0b86f416c3f..10742213c04 100644 --- a/graph/src/blockchain/mod.rs +++ b/graph/src/blockchain/mod.rs @@ -33,6 +33,7 @@ use crate::{ components::store::BlockNumber, prelude::{thiserror::Error, LinkResolver}, }; +use alloy::primitives::B256; use anyhow::{anyhow, Context, Error}; use async_trait::async_trait; use futures03::future::BoxFuture; @@ -47,7 +48,6 @@ use std::{ str::FromStr, sync::Arc, }; -use web3::types::H256; pub use block_stream::{ChainHeadUpdateListener, ChainHeadUpdateStream, TriggersAdapter}; pub use builder::{BasicBlockchainBuilder, BlockchainBuilder}; @@ -225,32 +225,26 @@ pub enum IngestorError { /// The Ethereum node does not know about this block for some reason, probably because it /// disappeared in a chain reorg. #[error("Block data unavailable, block was likely uncled (block hash = {0:?})")] - BlockUnavailable(H256), + BlockUnavailable(B256), /// The Ethereum node does not know about this block for some reason, probably because it /// disappeared in a chain reorg. #[error("Receipt for tx {1:?} unavailable, block was likely uncled (block hash = {0:?})")] - ReceiptUnavailable(H256, H256), + ReceiptUnavailable(B256, B256), /// The Ethereum node does not know about this block for some reason #[error("Transaction receipts for block (block hash = {0:?}) is unavailable")] - BlockReceiptsUnavailable(H256), + BlockReceiptsUnavailable(B256), /// The Ethereum node does not know about this block for some reason #[error("Received confliciting block receipts for block (block hash = {0:?})")] - BlockReceiptsMismatched(H256), + BlockReceiptsMismatched(B256), /// An unexpected error occurred. #[error("Ingestor error: {0:#}")] Unknown(#[from] Error), } -impl From for IngestorError { - fn from(e: web3::Error) -> Self { - IngestorError::Unknown(anyhow::anyhow!(e)) - } -} - /// The `TriggerFilterWrapper` is a higher-level wrapper around the chain-specific `TriggerFilter`, /// enabling subgraph-based trigger filtering for subgraph datasources. This abstraction is necessary /// because subgraph filtering operates at a higher level than chain-based filtering. By using this wrapper, diff --git a/graph/src/blockchain/types.rs b/graph/src/blockchain/types.rs index 081fff4eea5..290fb6bb1aa 100644 --- a/graph/src/blockchain/types.rs +++ b/graph/src/blockchain/types.rs @@ -1,3 +1,4 @@ +use alloy::primitives::B256; use anyhow::anyhow; use diesel::deserialize::FromSql; use diesel::pg::Pg; @@ -9,7 +10,8 @@ use serde::{Deserialize, Deserializer}; use std::convert::TryFrom; use std::time::Duration; use std::{fmt, str::FromStr}; -use web3::types::{Block, H256, U256, U64}; + +use crate::components::ethereum::BlockWrapper; use crate::cheap_clone::CheapClone; use crate::components::store::BlockNumber; @@ -32,8 +34,8 @@ impl BlockHash { &self.0 } - pub fn as_h256(&self) -> H256 { - H256::from_slice(self.as_slice()) + pub fn as_b256(&self) -> B256 { + B256::from_slice(self.as_slice()) } /// Encodes the block hash into a hexadecimal string **without** a "0x" @@ -45,7 +47,7 @@ impl BlockHash { } pub fn zero() -> Self { - Self::from(H256::zero()) + Self::from(B256::ZERO) } } @@ -83,18 +85,18 @@ impl fmt::LowerHex for BlockHash { } } -impl From for BlockHash { - fn from(hash: H256) -> Self { - BlockHash(hash.as_bytes().into()) - } -} - impl From> for BlockHash { fn from(bytes: Vec) -> Self { BlockHash(bytes.as_slice().into()) } } +impl From for BlockHash { + fn from(hash: B256) -> Self { + BlockHash(hash.as_slice().into()) + } +} + impl TryFrom<&str> for BlockHash { type Error = anyhow::Error; @@ -170,13 +172,6 @@ impl BlockPtr { self.number } - // FIXME: - // - // workaround for arweave - pub fn hash_as_h256(&self) -> H256 { - H256::from_slice(&self.hash_slice()[..32]) - } - pub fn hash_slice(&self) -> &[u8] { self.hash.0.as_ref() } @@ -205,15 +200,15 @@ impl slog::Value for BlockPtr { } } -impl From> for BlockPtr { - fn from(b: Block) -> BlockPtr { - BlockPtr::from((b.hash.unwrap(), b.number.unwrap().as_u64())) +impl From for BlockPtr { + fn from(b: BlockWrapper) -> BlockPtr { + BlockPtr::from((b.hash(), b.number_u64() as i32)) } } -impl<'a, T> From<&'a Block> for BlockPtr { - fn from(b: &'a Block) -> BlockPtr { - BlockPtr::from((b.hash.unwrap(), b.number.unwrap().as_u64())) +impl From<&BlockWrapper> for BlockPtr { + fn from(b: &BlockWrapper) -> BlockPtr { + BlockPtr::from((b.hash(), b.number_u64() as i32)) } } @@ -226,50 +221,51 @@ impl From<(Vec, i32)> for BlockPtr { } } -impl From<(H256, i32)> for BlockPtr { - fn from((hash, number): (H256, i32)) -> BlockPtr { +impl From<(B256, i32)> for BlockPtr { + fn from((hash, number): (B256, i32)) -> BlockPtr { BlockPtr { hash: hash.into(), number, } } } - -impl From<(Vec, u64)> for BlockPtr { - fn from((bytes, number): (Vec, u64)) -> Self { +impl From<(B256, u64)> for BlockPtr { + fn from((hash, number): (B256, u64)) -> BlockPtr { let number = i32::try_from(number).unwrap(); BlockPtr { - hash: BlockHash::from(bytes), + hash: hash.into(), number, } } } -impl From<(Vec, i64)> for BlockPtr { - fn from((bytes, number): (Vec, i64)) -> Self { +impl From<(B256, i64)> for BlockPtr { + fn from((hash, number): (B256, i64)) -> BlockPtr { let number = i32::try_from(number).unwrap(); BlockPtr { - hash: BlockHash::from(bytes), + hash: hash.into(), number, } } } -impl From<(H256, u64)> for BlockPtr { - fn from((hash, number): (H256, u64)) -> BlockPtr { +impl From<(Vec, u64)> for BlockPtr { + fn from((bytes, number): (Vec, u64)) -> Self { let number = i32::try_from(number).unwrap(); - - BlockPtr::from((hash, number)) + BlockPtr { + hash: BlockHash::from(bytes), + number, + } } } -impl From<(H256, i64)> for BlockPtr { - fn from((hash, number): (H256, i64)) -> BlockPtr { - if number < 0 { - panic!("block number out of range: {}", number); +impl From<(Vec, i64)> for BlockPtr { + fn from((bytes, number): (Vec, i64)) -> Self { + let number = i32::try_from(number).unwrap(); + BlockPtr { + hash: BlockHash::from(bytes), + number, } - - BlockPtr::from((hash, number as u64)) } } @@ -288,14 +284,14 @@ impl TryFrom<(&[u8], i64)> for BlockPtr { type Error = anyhow::Error; fn try_from((bytes, number): (&[u8], i64)) -> Result { - let hash = if bytes.len() == H256::len_bytes() { - H256::from_slice(bytes) + let hash = if bytes.len() == B256::len_bytes() { + B256::from_slice(bytes) } else { return Err(anyhow!( - "invalid H256 value `{}` has {} bytes instead of {}", + "invalid B256 value `{}` has {} bytes instead of {}", hex::encode(bytes), bytes.len(), - H256::len_bytes() + B256::len_bytes() )); }; Ok(BlockPtr::from((hash, number))) @@ -312,9 +308,9 @@ impl IntoValue for BlockPtr { } } -impl From for H256 { +impl From for B256 { fn from(ptr: BlockPtr) -> Self { - ptr.hash_as_h256() + ptr.hash.as_b256() } } @@ -398,22 +394,6 @@ impl ExtendedBlockPtr { pub fn block_number(&self) -> BlockNumber { self.number } - - pub fn hash_as_h256(&self) -> H256 { - H256::from_slice(&self.hash_slice()[..32]) - } - - pub fn parent_hash_as_h256(&self) -> H256 { - H256::from_slice(&self.parent_hash_slice()[..32]) - } - - pub fn hash_slice(&self) -> &[u8] { - self.hash.0.as_ref() - } - - pub fn parent_hash_slice(&self) -> &[u8] { - self.parent_hash.0.as_ref() - } } impl fmt::Display for ExtendedBlockPtr { @@ -463,44 +443,15 @@ impl IntoValue for ExtendedBlockPtr { } } -impl TryFrom<(Option, Option, H256, U256)> for ExtendedBlockPtr { - type Error = anyhow::Error; - - fn try_from(tuple: (Option, Option, H256, U256)) -> Result { - let (hash_opt, number_opt, parent_hash, timestamp_u256) = tuple; - - let hash = hash_opt.ok_or_else(|| anyhow!("Block hash is missing"))?; - let number = number_opt - .ok_or_else(|| anyhow!("Block number is missing"))? - .as_u64(); - - let block_number = - i32::try_from(number).map_err(|_| anyhow!("Block number out of range"))?; - - // Convert `U256` to `BlockTime` - let secs = - i64::try_from(timestamp_u256).map_err(|_| anyhow!("Timestamp out of range for i64"))?; - let block_time = BlockTime::since_epoch(secs, 0); - - Ok(ExtendedBlockPtr { - hash: hash.into(), - number: block_number, - parent_hash: parent_hash.into(), - timestamp: block_time, - }) - } -} - -impl TryFrom<(H256, i32, H256, U256)> for ExtendedBlockPtr { +impl TryFrom<(B256, i32, B256, u64)> for ExtendedBlockPtr { type Error = anyhow::Error; - fn try_from(tuple: (H256, i32, H256, U256)) -> Result { - let (hash, block_number, parent_hash, timestamp_u256) = tuple; + fn try_from(tuple: (B256, i32, B256, u64)) -> Result { + let (hash, block_number, parent_hash, timestamp) = tuple; - // Convert `U256` to `BlockTime` - let secs = - i64::try_from(timestamp_u256).map_err(|_| anyhow!("Timestamp out of range for i64"))?; - let block_time = BlockTime::since_epoch(secs, 0); + // Convert timestamp to `BlockTime` + let secs = timestamp; + let block_time = BlockTime::since_epoch(secs as i64, 0); Ok(ExtendedBlockPtr { hash: hash.into(), @@ -510,11 +461,6 @@ impl TryFrom<(H256, i32, H256, U256)> for ExtendedBlockPtr { }) } } -impl From for H256 { - fn from(ptr: ExtendedBlockPtr) -> Self { - ptr.hash_as_h256() - } -} impl From for BlockNumber { fn from(ptr: ExtendedBlockPtr) -> Self { @@ -539,7 +485,7 @@ impl Default for ChainIdentifier { fn default() -> Self { Self { net_version: String::default(), - genesis_block_hash: BlockHash::from(H256::zero()), + genesis_block_hash: BlockHash::from(B256::ZERO), } } } diff --git a/graph/src/cheap_clone.rs b/graph/src/cheap_clone.rs index b8863d3918e..c56e621cb4c 100644 --- a/graph/src/cheap_clone.rs +++ b/graph/src/cheap_clone.rs @@ -118,4 +118,4 @@ cheap_clone_is_copy!( &'static str, std::time::Duration ); -cheap_clone_is_copy!(ethabi::Address); +cheap_clone_is_copy!(alloy::primitives::Address); diff --git a/graph/src/components/ethereum/mod.rs b/graph/src/components/ethereum/mod.rs index 45f1f5d98ad..5fc0baf10cd 100644 --- a/graph/src/components/ethereum/mod.rs +++ b/graph/src/components/ethereum/mod.rs @@ -1,6 +1,9 @@ mod types; pub use self::types::{ - evaluate_transaction_status, EthereumBlock, EthereumBlockWithCalls, EthereumCall, - LightEthereumBlock, LightEthereumBlockExt, + evaluate_transaction_status, AnyBlock, AnyTransaction, BlockWrapper, EthereumBlock, + EthereumBlockWithCalls, EthereumCall, LightEthereumBlock, LightEthereumBlockExt, }; + +// Re-export Alloy network types for convenience +pub use alloy::network::{AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope}; diff --git a/graph/src/components/ethereum/types.rs b/graph/src/components/ethereum/types.rs index b43730590d4..931ca02b44e 100644 --- a/graph/src/components/ethereum/types.rs +++ b/graph/src/components/ethereum/types.rs @@ -1,68 +1,160 @@ -use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, sync::Arc}; -use web3::types::{ - Action, Address, Block, Bytes, Log, Res, Trace, Transaction, TransactionReceipt, H256, U256, - U64, +use alloy::{ + network::{AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, ReceiptResponse, TransactionResponse}, + primitives::{Address, Bytes, B256, U256, U64}, + rpc::types::{ + trace::parity::{Action, LocalizedTransactionTrace, TraceOutput}, + Log, + }, }; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; use crate::{ blockchain::{BlockPtr, BlockTime}, prelude::BlockNumber, }; -pub type LightEthereumBlock = Block; +// Use Alloy's official types for handling any transaction type +pub type AnyTransaction = AnyRpcTransaction; +pub type AnyBlock = AnyRpcBlock; + +// BlockWrapper wraps AnyBlock +#[allow(dead_code)] +#[derive(Debug, Deserialize, Serialize)] +pub struct BlockWrapper(AnyBlock); + +impl Default for BlockWrapper { + fn default() -> Self { + use alloy::rpc::types::{Block, BlockTransactions}; + use alloy::serde::WithOtherFields; + + let default_block = Block { + header: AnyRpcHeader::default(), + transactions: BlockTransactions::Full(vec![]), + uncles: vec![], + withdrawals: None, + }; + Self(AnyBlock::new(WithOtherFields::new(default_block))) + } +} + +impl BlockWrapper { + pub fn new(block: AnyBlock) -> Self { + Self(block) + } + + pub fn hash(&self) -> B256 { + self.0.header.hash + } + + pub fn number_u64(&self) -> u64 { + self.0.header.number + } + + pub fn timestamp_u64(&self) -> u64 { + self.0.header.timestamp + } + + pub fn transactions(&self) -> Option<&[AnyTransaction]> { + self.0.transactions.as_transactions() + } + + pub fn inner(&self) -> &AnyBlock { + &self.0 + } + + pub fn base_fee_per_gas(&self) -> Option { + self.0.header.base_fee_per_gas + } +} + +pub type LightEthereumBlock = BlockWrapper; pub trait LightEthereumBlockExt { fn number(&self) -> BlockNumber; - fn transaction_for_log(&self, log: &Log) -> Option; - fn transaction_for_call(&self, call: &EthereumCall) -> Option; + fn transaction_for_log(&self, log: &Log) -> Option; + fn transaction_for_call(&self, call: &EthereumCall) -> Option; fn parent_ptr(&self) -> Option; fn format(&self) -> String; fn block_ptr(&self) -> BlockPtr; fn timestamp(&self) -> BlockTime; } -impl LightEthereumBlockExt for LightEthereumBlock { +impl LightEthereumBlockExt for AnyBlock { fn number(&self) -> BlockNumber { - BlockNumber::try_from(self.number.unwrap().as_u64()).unwrap() + self.header.number as BlockNumber + } + + fn timestamp(&self) -> BlockTime { + let time = self.header.timestamp; + let time = i64::try_from(time).unwrap(); + BlockTime::since_epoch(time, 0) } - fn transaction_for_log(&self, log: &Log) -> Option { - log.transaction_hash - .and_then(|hash| self.transactions.iter().find(|tx| tx.hash == hash)) - .cloned() + fn transaction_for_log(&self, log: &Log) -> Option { + log.transaction_hash.and_then(|hash| { + self.transactions + .txns() + .find(|tx| &tx.tx_hash() == &hash) + .cloned() + }) } - fn transaction_for_call(&self, call: &EthereumCall) -> Option { - call.transaction_hash - .and_then(|hash| self.transactions.iter().find(|tx| tx.hash == hash)) - .cloned() + fn transaction_for_call(&self, call: &EthereumCall) -> Option { + call.transaction_hash.and_then(|hash| { + self.transactions + .txns() + .find(|tx| &tx.tx_hash() == &hash) + .cloned() + }) } fn parent_ptr(&self) -> Option { - match self.number() { + match self.header.number { 0 => None, - n => Some(BlockPtr::from((self.parent_hash, n - 1))), + n => { + let number = i32::try_from(n - 1).unwrap(); + Some(BlockPtr::new(self.header.parent_hash.into(), number)) + } } } fn format(&self) -> String { - format!( - "{} ({})", - self.number - .map_or(String::from("none"), |number| format!("#{}", number)), - self.hash - .map_or(String::from("-"), |hash| format!("{:x}", hash)) - ) + format!("{} ({})", self.header.number, self.header.hash) + } + + fn block_ptr(&self) -> BlockPtr { + BlockPtr::new(self.header.hash.into(), self.header.number as i32) + } +} + +impl LightEthereumBlockExt for LightEthereumBlock { + fn number(&self) -> BlockNumber { + self.0.header.number.try_into().unwrap() + } + + fn transaction_for_log(&self, log: &alloy::rpc::types::Log) -> Option { + self.0.transaction_for_log(log) + } + + fn transaction_for_call(&self, call: &EthereumCall) -> Option { + self.0.transaction_for_call(call) + } + + fn parent_ptr(&self) -> Option { + self.0.parent_ptr() + } + + fn format(&self) -> String { + self.0.format() } fn block_ptr(&self) -> BlockPtr { - BlockPtr::from((self.hash.unwrap(), self.number.unwrap().as_u64())) + self.0.block_ptr() } fn timestamp(&self) -> BlockTime { - let ts = i64::try_from(self.timestamp.as_u64()).unwrap(); - BlockTime::since_epoch(ts, 0) + self.0.timestamp() } } @@ -90,7 +182,7 @@ impl EthereumBlockWithCalls { "failed to find the receipt for this transaction" ))?; - Ok(evaluate_transaction_status(receipt.status)) + Ok(receipt.status()) } } @@ -104,10 +196,10 @@ pub fn evaluate_transaction_status(receipt_status: Option) -> bool { .unwrap_or(true) } -#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct EthereumBlock { pub block: Arc, - pub transaction_receipts: Vec>, + pub transaction_receipts: Vec>, } #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -115,31 +207,34 @@ pub struct EthereumCall { pub from: Address, pub to: Address, pub value: U256, - pub gas_used: U256, + pub gas_used: u64, pub input: Bytes, pub output: Bytes, pub block_number: BlockNumber, - pub block_hash: H256, - pub transaction_hash: Option, + pub block_hash: B256, + pub transaction_hash: Option, pub transaction_index: u64, } impl EthereumCall { - pub fn try_from_trace(trace: &Trace) -> Option { + pub fn try_from_trace(trace: &LocalizedTransactionTrace) -> Option { // The parity-ethereum tracing api returns traces for operations which had execution errors. // Filter errorful traces out, since call handlers should only run on successful CALLs. - if trace.error.is_some() { + + let tx_trace = &trace.trace; + + if tx_trace.error.is_some() { return None; } // We are only interested in traces from CALLs - let call = match &trace.action { + let call = match &tx_trace.action { // Contract to contract value transfers compile to the CALL opcode // and have no input. Call handlers are for triggering on explicit method calls right now. Action::Call(call) if call.input.0.len() >= 4 => call, _ => return None, }; - let (output, gas_used) = match &trace.result { - Some(Res::Call(result)) => (result.output.clone(), result.gas_used), + let (output, gas_used) = match &tx_trace.result { + Some(TraceOutput::Call(result)) => (result.output.clone(), result.gas_used), _ => return None, }; @@ -151,29 +246,17 @@ impl EthereumCall { from: call.from, to: call.to, value: call.value, - gas_used, + gas_used: gas_used, input: call.input.clone(), - output, - block_number: trace.block_number as BlockNumber, - block_hash: trace.block_hash, + output: output, + block_number: trace.block_number? as BlockNumber, + block_hash: trace.block_hash?, transaction_hash: trace.transaction_hash, transaction_index, }) } } -impl From for BlockPtr { - fn from(b: EthereumBlock) -> BlockPtr { - BlockPtr::from((b.block.hash.unwrap(), b.block.number.unwrap().as_u64())) - } -} - -impl<'a> From<&'a EthereumBlock> for BlockPtr { - fn from(b: &'a EthereumBlock) -> BlockPtr { - BlockPtr::from((b.block.hash.unwrap(), b.block.number.unwrap().as_u64())) - } -} - impl<'a> From<&'a EthereumCall> for BlockPtr { fn from(call: &'a EthereumCall) -> BlockPtr { BlockPtr::from((call.block_hash, call.block_number)) diff --git a/graph/src/components/store/mod.rs b/graph/src/components/store/mod.rs index 818718a5f74..c5ac4002224 100644 --- a/graph/src/components/store/mod.rs +++ b/graph/src/components/store/mod.rs @@ -3,6 +3,7 @@ mod err; mod traits; pub mod write; +use alloy::primitives::Address; use diesel::deserialize::FromSql; use diesel::pg::Pg; use diesel::serialize::{Output, ToSql}; @@ -1140,7 +1141,7 @@ pub struct CachedEthereumCall { pub block_ptr: BlockPtr, /// The address to the called contract. - pub contract_address: ethabi::Address, + pub contract_address: Address, /// The encoded return value of this call. pub return_value: Vec, diff --git a/graph/src/components/store/traits.rs b/graph/src/components/store/traits.rs index 2e26a3c96ee..61347a1e8db 100644 --- a/graph/src/components/store/traits.rs +++ b/graph/src/components/store/traits.rs @@ -3,7 +3,6 @@ use std::ops::Range; use anyhow::Error; use async_trait::async_trait; -use web3::types::{Address, H256}; use super::*; use crate::blockchain::block_stream::{EntitySourceOperation, FirehoseCursor}; @@ -19,7 +18,10 @@ use crate::data::store::ethereum::call; use crate::data::store::{QueryObject, SqlQueryObject}; use crate::data::subgraph::{status, DeploymentFeatures}; use crate::data::{query::QueryTarget, subgraph::schema::*}; -use crate::prelude::{DeploymentState, NodeId, QueryExecutionError, SubgraphName}; +use crate::prelude::{ + alloy::primitives::{Address, B256}, + DeploymentState, NodeId, QueryExecutionError, SubgraphName, +}; use crate::schema::{ApiSchema, InputSchema}; pub trait SubscriptionManager: Send + Sync + 'static { @@ -539,7 +541,7 @@ pub trait ChainStore: ChainHeadStore { async fn attempt_chain_head_update( self: Arc, ancestor_count: BlockNumber, - ) -> Result, Error>; + ) -> Result, Error>; /// Returns the blocks present in the store. async fn blocks( @@ -616,7 +618,7 @@ pub trait ChainStore: ChainHeadStore { /// Tries to retrieve all transactions receipts for a given block. async fn transaction_receipts_in_block( &self, - block_ptr: &H256, + block_ptr: &B256, ) -> Result, StoreError>; /// Clears call cache of the chain for the given `from` and `to` block number. diff --git a/graph/src/components/subgraph/proof_of_indexing/mod.rs b/graph/src/components/subgraph/proof_of_indexing/mod.rs index 718a3a5cecd..6121eccf60e 100644 --- a/graph/src/components/subgraph/proof_of_indexing/mod.rs +++ b/graph/src/components/subgraph/proof_of_indexing/mod.rs @@ -87,6 +87,7 @@ mod tests { prelude::{BlockPtr, DeploymentHash, Value}, schema::InputSchema, }; + use alloy::primitives::{Address, B256}; use maplit::hashmap; use online::ProofOfIndexingFinisher; use reference::*; @@ -96,7 +97,6 @@ mod tests { use stable_hash_legacy::utils::stable_hash as stable_hash_legacy; use std::collections::HashMap; use std::convert::TryInto; - use web3::types::{Address, H256}; /// The PoI is the StableHash of this struct. This reference implementation is /// mostly here just to make sure that the online implementation is @@ -106,22 +106,22 @@ mod tests { pub struct PoI<'a> { pub causality_regions: HashMap>, pub subgraph_id: DeploymentHash, - pub block_hash: H256, + pub block_hash: B256, pub indexer: Option
, } - fn h256_as_bytes(val: &H256) -> AsBytes<&[u8]> { - AsBytes(val.as_bytes()) + fn b256_as_bytes(val: &B256) -> AsBytes<&[u8]> { + AsBytes(val.as_slice()) } fn indexer_opt_as_bytes(val: &Option
) -> Option> { - val.as_ref().map(|v| AsBytes(v.as_bytes())) + val.as_ref().map(|v| AsBytes(v.as_slice())) } impl_stable_hash!(PoI<'_> { causality_regions, subgraph_id, - block_hash: h256_as_bytes, + block_hash: b256_as_bytes, indexer: indexer_opt_as_bytes }); @@ -247,7 +247,7 @@ mod tests { fast: "dced49c45eac68e8b3d8f857928e7be6c270f2db8b56b0d7f27ce725100bae01", data: PoI { subgraph_id: DeploymentHash::new("test").unwrap(), - block_hash: H256::repeat_byte(1), + block_hash: B256::repeat_byte(1), causality_regions: HashMap::new(), indexer: None, }, @@ -259,7 +259,7 @@ mod tests { fast: "8bb3373fb55e02bde3202bac0eeecf1bd9a676856a4dd6667bd809aceda41885", data: PoI { subgraph_id: DeploymentHash::new("test").unwrap(), - block_hash: H256::repeat_byte(1), + block_hash: B256::repeat_byte(1), causality_regions: hashmap! { "eth".to_owned() => PoICausalityRegion { blocks: vec! [ @@ -286,7 +286,7 @@ mod tests { fast: "8b0097ad96b21f7e4bd8dcc41985e6e5506b808f1185016ab1073dd8745238ce", data: PoI { subgraph_id: DeploymentHash::new("b").unwrap(), - block_hash: H256::repeat_byte(3), + block_hash: B256::repeat_byte(3), causality_regions: hashmap! { "eth".to_owned() => PoICausalityRegion { blocks: vec! [ @@ -324,7 +324,7 @@ mod tests { fast: "2041af28678e68406247a5cfb5fe336947da75256c79b35c2f61fc7985091c0e", data: PoI { subgraph_id: DeploymentHash::new("b").unwrap(), - block_hash: H256::repeat_byte(3), + block_hash: B256::repeat_byte(3), causality_regions: hashmap! { "eth".to_owned() => PoICausalityRegion { blocks: vec! [ @@ -386,7 +386,7 @@ mod tests { fast: "421ef30a03be64014b9eef2b999795dcabfc601368040df855635e7886eb3822", data: PoI { subgraph_id: DeploymentHash::new("test").unwrap(), - block_hash: H256::repeat_byte(1), + block_hash: B256::repeat_byte(1), causality_regions: hashmap! { "eth".to_owned() => PoICausalityRegion { blocks: vec! [ diff --git a/graph/src/components/subgraph/proof_of_indexing/online.rs b/graph/src/components/subgraph/proof_of_indexing/online.rs index ebf7a65e2f9..f9bd8b5f18d 100644 --- a/graph/src/components/subgraph/proof_of_indexing/online.rs +++ b/graph/src/components/subgraph/proof_of_indexing/online.rs @@ -9,6 +9,7 @@ use crate::{ prelude::{debug, BlockNumber, DeploymentHash, Logger, ENV_VARS}, util::stable_hash_glue::AsBytes, }; +use alloy::primitives::Address; use sha2::{Digest, Sha256}; use stable_hash::{fast::FastStableHasher, FieldAddress, StableHash, StableHasher}; use stable_hash_legacy::crypto::{Blake3SeqNo, SetHasher}; @@ -18,7 +19,6 @@ use stable_hash_legacy::prelude::{ use std::collections::HashMap; use std::convert::TryInto; use std::fmt; -use web3::types::Address; pub struct BlockEventStream { vec_length: u64, @@ -278,7 +278,7 @@ impl ProofOfIndexingFinisher { state.write(&AsBytes(block.hash_slice()), &[2]); // Add PoI.indexer - state.write(&indexer.as_ref().map(|i| AsBytes(i.as_bytes())), &[3]); + state.write(&indexer.as_ref().map(|i| AsBytes(i.as_slice())), &[3]); ProofOfIndexingFinisher { block_number: block.number, diff --git a/graph/src/components/transaction_receipt.rs b/graph/src/components/transaction_receipt.rs index dc8eaf6a730..526a0487180 100644 --- a/graph/src/components/transaction_receipt.rs +++ b/graph/src/components/transaction_receipt.rs @@ -3,37 +3,28 @@ //! This module exposes the [`LightTransactionReceipt`] type, which holds basic information about //! the retrieved transaction receipts. -use web3::types::{TransactionReceipt, H256, U256, U64}; +use alloy::network::ReceiptResponse; +use alloy::primitives::B256; -/// Like web3::types::Receipt, but with fewer fields. #[derive(Debug, PartialEq, Eq)] pub struct LightTransactionReceipt { - pub transaction_hash: H256, - pub transaction_index: U64, - pub block_hash: Option, - pub block_number: Option, - pub gas_used: Option, - pub status: Option, + pub transaction_hash: B256, + pub transaction_index: u64, + pub block_hash: Option, + pub block_number: Option, + pub gas_used: u64, + pub status: bool, } -impl From for LightTransactionReceipt { - fn from(receipt: TransactionReceipt) -> Self { - let TransactionReceipt { - transaction_hash, - transaction_index, - block_hash, - block_number, - gas_used, - status, - .. - } = receipt; +impl From for LightTransactionReceipt { + fn from(receipt: alloy::network::AnyTransactionReceipt) -> Self { LightTransactionReceipt { - transaction_hash, - transaction_index, - block_hash, - block_number, - gas_used, - status, + transaction_hash: receipt.transaction_hash, + transaction_index: receipt.transaction_index.unwrap(), // unwrap is safe because its None only for pending transactions, graph-node does not ingest pending transactions + block_hash: receipt.block_hash, + block_number: receipt.block_number, + gas_used: receipt.gas_used, + status: receipt.status(), } } } diff --git a/graph/src/data/graphql/values.rs b/graph/src/data/graphql/values.rs index 7f15d26dc98..b4923d6ce26 100644 --- a/graph/src/data/graphql/values.rs +++ b/graph/src/data/graphql/values.rs @@ -1,3 +1,4 @@ +use alloy::primitives::Address; use anyhow::{anyhow, Error}; use std::collections::HashMap; use std::convert::TryFrom; @@ -6,7 +7,6 @@ use std::str::FromStr; use crate::blockchain::BlockHash; use crate::data::value::Object; use crate::prelude::{r, BigInt}; -use web3::types::H160; pub trait TryFromValue: Sized { fn try_from_value(value: &r::Value) -> Result; @@ -74,16 +74,11 @@ impl TryFromValue for i32 { } } -impl TryFromValue for H160 { +impl TryFromValue for Address { fn try_from_value(value: &r::Value) -> Result { match value { - r::Value::String(s) => { - // `H160::from_str` takes a hex string with no leading `0x`. - let string = s.trim_start_matches("0x"); - H160::from_str(string).map_err(|e| { - anyhow!("Cannot parse Address/H160 value from string `{}`: {}", s, e) - }) - } + r::Value::String(s) => Address::from_str(s) + .map_err(|e| anyhow!("Cannot parse Address/H160 value from string `{}`: {}", s, e)), _ => Err(anyhow!( "Cannot parse value into an Address/H160: {:?}", value diff --git a/graph/src/data/store/ethereum.rs b/graph/src/data/store/ethereum.rs index 12d48f992df..469b4b14551 100644 --- a/graph/src/data/store/ethereum.rs +++ b/graph/src/data/store/ethereum.rs @@ -1,7 +1,7 @@ use super::scalar; use crate::derive::CheapClone; use crate::prelude::*; -use web3::types::{Address, Bytes, H2048, H256, H64, U64}; +use alloy::primitives::{aliases::B2048, Address, Bytes, B256, B64, U64}; impl From
for Value { fn from(address: Address) -> Value { @@ -9,27 +9,27 @@ impl From
for Value { } } -impl From for Value { - fn from(hash: H64) -> Value { +impl From for Value { + fn from(hash: B64) -> Value { Value::Bytes(scalar::Bytes::from(hash.as_ref())) } } -impl From for Value { - fn from(hash: H256) -> Value { +impl From for Value { + fn from(hash: B256) -> Value { Value::Bytes(scalar::Bytes::from(hash.as_ref())) } } -impl From for Value { - fn from(hash: H2048) -> Value { +impl From for Value { + fn from(hash: B2048) -> Value { Value::Bytes(scalar::Bytes::from(hash.as_ref())) } } impl From for Value { fn from(bytes: Bytes) -> Value { - Value::Bytes(scalar::Bytes::from(bytes.0.as_slice())) + Value::Bytes(scalar::Bytes::from(bytes.as_ref())) } } @@ -43,6 +43,8 @@ impl From for Value { pub mod call { use std::sync::Arc; + use alloy::primitives::Address; + use crate::data::store::scalar::Bytes; use super::CheapClone; @@ -104,7 +106,7 @@ pub mod call { /// on the call's return value #[derive(Debug, Clone, CheapClone)] pub struct Request { - pub address: ethabi::Address, + pub address: Address, pub encoded_call: Arc, /// The index is set by the caller and is used to identify the /// request in related data structures that the caller might have @@ -112,7 +114,7 @@ pub mod call { } impl Request { - pub fn new(address: ethabi::Address, encoded_call: Vec, index: u32) -> Self { + pub fn new(address: Address, encoded_call: Vec, index: u32) -> Self { Request { address, encoded_call: Arc::new(Bytes::from(encoded_call)), diff --git a/graph/src/data/store/scalar/bigint.rs b/graph/src/data/store/scalar/bigint.rs index 526ca5ba390..a69391e7fc1 100644 --- a/graph/src/data/store/scalar/bigint.rs +++ b/graph/src/data/store/scalar/bigint.rs @@ -4,8 +4,8 @@ use serde::{self, Deserialize, Serialize}; use stable_hash::utils::AsInt; use stable_hash::StableHash; use thiserror::Error; -use web3::types::*; +use crate::prelude::alloy::primitives::{U128, U256, U64}; use std::convert::{TryFrom, TryInto}; use std::fmt; use std::ops::{Add, BitAnd, BitOr, Div, Mul, Rem, Shl, Shr, Sub}; @@ -174,22 +174,19 @@ impl BigInt { } pub fn from_unsigned_u128(n: U128) -> Self { - let mut bytes: [u8; 16] = [0; 16]; - n.to_little_endian(&mut bytes); + let bytes: [u8; U128::BYTES] = n.to_le_bytes(); // Unwrap: 128 bits is much less than BigInt::MAX_BITS BigInt::from_unsigned_bytes_le(&bytes).unwrap() } pub fn from_unsigned_u256(n: &U256) -> Self { - let mut bytes: [u8; 32] = [0; 32]; - n.to_little_endian(&mut bytes); + let bytes: [u8; U256::BYTES] = n.to_le_bytes(); // Unwrap: 256 bits is much less than BigInt::MAX_BITS BigInt::from_unsigned_bytes_le(&bytes).unwrap() } pub fn from_signed_u256(n: &U256) -> Self { - let mut bytes: [u8; 32] = [0; 32]; - n.to_little_endian(&mut bytes); + let bytes: [u8; U256::BYTES] = n.to_le_bytes(); BigInt::from_signed_bytes_le(&bytes).unwrap() } @@ -202,9 +199,9 @@ impl BigInt { ); let mut i_bytes: [u8; 32] = [255; 32]; i_bytes[..bytes.len()].copy_from_slice(&bytes); - U256::from_little_endian(&i_bytes) + U256::from_le_slice(&i_bytes) } else { - U256::from_little_endian(&bytes) + U256::from_le_slice(&bytes) } } @@ -216,7 +213,7 @@ impl BigInt { self ); } - Ok(U256::from_little_endian(&bytes)) + Ok(U256::from_le_slice(&bytes)) } pub fn pow(self, exponent: u8) -> Result { @@ -238,6 +235,12 @@ impl From for BigInt { } } +impl From for BigInt { + fn from(i: u128) -> BigInt { + BigInt::unchecked_new(i.into()) + } +} + impl From for BigInt { fn from(i: i64) -> BigInt { BigInt::unchecked_new(i.into()) @@ -251,7 +254,7 @@ impl From for BigInt { /// handle signed U64s, we should add the same /// `{to,from}_{signed,unsigned}_u64` methods that we have for U64. fn from(n: U64) -> BigInt { - BigInt::from(n.as_u64()) + BigInt::from(n.to::()) } } @@ -367,8 +370,9 @@ impl GasSizeOf for BigInt { #[cfg(test)] mod test { + use alloy::primitives::U64; + use super::{super::test::same_stable_hash, BigInt}; - use web3::types::U64; #[test] fn bigint_to_from_u64() { diff --git a/graph/src/data/store/scalar/bytes.rs b/graph/src/data/store/scalar/bytes.rs index 585b548f931..c7742f412a2 100644 --- a/graph/src/data/store/scalar/bytes.rs +++ b/graph/src/data/store/scalar/bytes.rs @@ -1,9 +1,9 @@ +use alloy::primitives::Address; use diesel::deserialize::FromSql; use diesel::pg::PgValue; use diesel::serialize::ToSql; use hex; use serde::{self, Deserialize, Serialize}; -use web3::types::*; use std::fmt::{self, Display, Formatter}; use std::ops::Deref; @@ -64,12 +64,6 @@ impl From
for Bytes { } } -impl From for Bytes { - fn from(bytes: web3::types::Bytes) -> Bytes { - Bytes::from(bytes.0.as_slice()) - } -} - impl From for Bytes { fn from(hash: BlockHash) -> Self { Bytes(hash.0) @@ -123,3 +117,9 @@ impl FromSql for Bytes { as FromSql>::from_sql(value).map(Bytes::from) } } + +impl From for Bytes { + fn from(bytes: alloy::primitives::Bytes) -> Bytes { + Bytes::from(bytes.as_ref()) + } +} diff --git a/graph/src/data/subgraph/mod.rs b/graph/src/data/subgraph/mod.rs index 25287a94e95..c19926bd813 100644 --- a/graph/src/data/subgraph/mod.rs +++ b/graph/src/data/subgraph/mod.rs @@ -3,6 +3,7 @@ pub mod schema; /// API version and spec version. pub mod api_version; +use alloy::primitives::Address; pub use api_version::*; pub mod features; @@ -29,7 +30,6 @@ use std::{ }; use thiserror::Error; use wasmparser; -use web3::types::Address; use crate::{ bail, diff --git a/graph/src/data_source/common.rs b/graph/src/data_source/common.rs index f89739c6ab7..8090f9334b7 100644 --- a/graph/src/data_source/common.rs +++ b/graph/src/data_source/common.rs @@ -1,3 +1,6 @@ +use crate::abi; +use crate::abi::DynSolValueExt; +use crate::abi::FunctionExt; use crate::blockchain::block_stream::EntitySourceOperation; use crate::data::subgraph::SPEC_VERSION_1_4_0; use crate::prelude::{BlockPtr, Value}; @@ -7,8 +10,9 @@ use crate::{ data::value::Word, prelude::Link, }; +use alloy::primitives::{Address, U256}; +use alloy::rpc::types::Log; use anyhow::{anyhow, Context, Error}; -use ethabi::{Address, Contract, Function, LogParam, ParamType, Token}; use graph_derive::CheapClone; use lazy_static::lazy_static; use num_bigint::Sign; @@ -19,12 +23,11 @@ use serde_json; use slog::Logger; use std::collections::HashMap; use std::{str::FromStr, sync::Arc}; -use web3::types::{Log, H160}; #[derive(Clone, Debug, PartialEq)] pub struct MappingABI { pub name: String, - pub contract: Contract, + pub contract: abi::JsonAbi, } impl MappingABI { @@ -33,24 +36,27 @@ impl MappingABI { contract_name: &str, name: &str, signature: Option<&str>, - ) -> Result<&Function, Error> { + ) -> Result<&abi::Function, Error> { let contract = &self.contract; let function = match signature { // Behavior for apiVersion < 0.0.4: look up function by name; for overloaded // functions this always picks the same overloaded variant, which is incorrect // and may lead to encoding/decoding errors - None => contract.function(name).with_context(|| { - format!( - "Unknown function \"{}::{}\" called from WASM runtime", - contract_name, name - ) - })?, + None => contract + .function(name) + .and_then(|matches| matches.first()) + .with_context(|| { + format!( + "Unknown function \"{}::{}\" called from WASM runtime", + contract_name, name + ) + })?, // Behavior for apiVersion >= 0.0.04: look up function by signature of // the form `functionName(uint256,string) returns (bytes32,string)`; this // correctly picks the correct variant of an overloaded function Some(ref signature) => contract - .functions_by_name(name) + .function(name) .with_context(|| { format!( "Unknown function \"{}::{}\" called from WASM runtime", @@ -58,7 +64,7 @@ impl MappingABI { ) })? .iter() - .find(|f| signature == &f.signature()) + .find(|f| signature == &f.signature_compat()) .with_context(|| { format!( "Unknown function \"{}::{}\" with signature `{}` \ @@ -120,7 +126,7 @@ impl AbiJson { if param_type == "tuple" { if let Some(components) = input.get("components") { // Parse the ParamType from the JSON (simplified for now) - let param_type = ParamType::Tuple(vec![]); + let param_type = abi::DynSolType::Tuple(vec![]); return StructFieldInfo::from_components( param_name.to_string(), param_type, @@ -358,7 +364,7 @@ impl UnresolvedMappingABI { self.name, self.file.link ) })?; - let contract = Contract::load(&*contract_bytes) + let contract = serde_json::from_slice(&*contract_bytes) .with_context(|| format!("failed to load ABI {}", self.name))?; // Parse ABI JSON for on-demand struct field extraction @@ -408,17 +414,25 @@ impl CallDecl { self.expr.validate_args() } - pub fn address_for_log(&self, log: &Log, params: &[LogParam]) -> Result { + pub fn address_for_log( + &self, + log: &Log, + params: &[abi::DynSolParam], + ) -> Result { self.address_for_log_with_abi(log, params) } - pub fn address_for_log_with_abi(&self, log: &Log, params: &[LogParam]) -> Result { + pub fn address_for_log_with_abi( + &self, + log: &Log, + params: &[abi::DynSolParam], + ) -> Result { let address = match &self.expr.address { CallArg::HexAddress(address) => *address, CallArg::Ethereum(arg) => match arg { - EthereumArg::Address => log.address, + EthereumArg::Address => log.address(), EthereumArg::Param(name) => { - let value = params + let value = ¶ms .iter() .find(|param| ¶m.name == name.as_str()) .ok_or_else(|| { @@ -428,15 +442,17 @@ impl CallDecl { name ) })? - .value - .clone(); - value.into_address().ok_or_else(|| { + .value; + + let address = value.as_address().ok_or_else(|| { anyhow!( "In declarative call '{}': param {} is not an address", self.label, name ) - })? + })?; + + Address::from(address.into_array()) } EthereumArg::StructField(param_name, field_accesses) => { let param = params @@ -467,22 +483,27 @@ impl CallDecl { Ok(address) } - pub fn args_for_log(&self, log: &Log, params: &[LogParam]) -> Result, Error> { + pub fn args_for_log( + &self, + log: &Log, + params: &[abi::DynSolParam], + ) -> Result, Error> { self.args_for_log_with_abi(log, params) } pub fn args_for_log_with_abi( &self, log: &Log, - params: &[LogParam], - ) -> Result, Error> { + params: &[abi::DynSolParam], + ) -> Result, Error> { + use abi::DynSolValue; self.expr .args .iter() .map(|arg| match arg { - CallArg::HexAddress(address) => Ok(Token::Address(*address)), + CallArg::HexAddress(address) => Ok(DynSolValue::Address(*address)), CallArg::Ethereum(arg) => match arg { - EthereumArg::Address => Ok(Token::Address(log.address)), + EthereumArg::Address => Ok(DynSolValue::Address(log.address())), EthereumArg::Param(name) => { let value = params .iter() @@ -513,7 +534,10 @@ impl CallDecl { .collect() } - pub fn get_function(&self, mapping: &dyn FindMappingABI) -> Result { + pub fn get_function( + &self, + mapping: &dyn FindMappingABI, + ) -> Result { let contract_name = self.expr.abi.to_string(); let function_name = self.expr.func.as_str(); let abi = mapping.find_abi(&contract_name)?; @@ -524,6 +548,7 @@ impl CallDecl { // and may lead to encoding/decoding errors abi.contract .function(function_name) + .and_then(|matches| matches.first()) .cloned() .with_context(|| { format!( @@ -536,7 +561,7 @@ impl CallDecl { pub fn address_for_entity_handler( &self, entity: &EntitySourceOperation, - ) -> Result { + ) -> Result { match &self.expr.address { // Static hex address - just return it directly CallArg::HexAddress(address) => Ok(*address), @@ -557,7 +582,7 @@ impl CallDecl { // Make sure it's a bytes value and convert to address match value { Value::Bytes(bytes) => { - let address = H160::from_slice(bytes.as_slice()); + let address = Address::from_slice(bytes.as_slice()); Ok(address) } _ => Err(anyhow!("param '{name}' must be an address")), @@ -571,8 +596,8 @@ impl CallDecl { pub fn args_for_entity_handler( &self, entity: &EntitySourceOperation, - param_types: Vec, - ) -> Result, Error> { + param_types: Vec, + ) -> Result, Error> { self.validate_entity_handler_args(¶m_types)?; self.expr @@ -586,7 +611,7 @@ impl CallDecl { } /// Validates that the number of provided arguments matches the expected parameter types. - fn validate_entity_handler_args(&self, param_types: &[ParamType]) -> Result<(), Error> { + fn validate_entity_handler_args(&self, param_types: &[abi::DynSolType]) -> Result<(), Error> { if self.expr.args.len() != param_types.len() { return Err(anyhow!( "mismatched number of arguments: expected {}, got {}", @@ -602,9 +627,9 @@ impl CallDecl { fn process_entity_handler_arg( &self, arg: &CallArg, - expected_type: &ParamType, + expected_type: &abi::DynSolType, entity: &EntitySourceOperation, - ) -> Result { + ) -> Result { match arg { CallArg::HexAddress(address) => self.process_hex_address(*address, expected_type), CallArg::Ethereum(_) => Err(anyhow!( @@ -619,11 +644,11 @@ impl CallDecl { /// Converts a hex address to a token, ensuring it matches the expected parameter type. fn process_hex_address( &self, - address: H160, - expected_type: &ParamType, - ) -> Result { + address: Address, + expected_type: &abi::DynSolType, + ) -> Result { match expected_type { - ParamType::Address => Ok(Token::Address(address)), + abi::DynSolType::Address => Ok(abi::DynSolValue::Address(address)), _ => Err(anyhow!( "type mismatch: hex address provided for non-address parameter" )), @@ -634,9 +659,9 @@ impl CallDecl { fn process_entity_param( &self, name: &str, - expected_type: &ParamType, + expected_type: &abi::DynSolType, entity: &EntitySourceOperation, - ) -> Result { + ) -> Result { let value = entity .entity .get(name) @@ -650,27 +675,44 @@ impl CallDecl { fn convert_entity_value_to_token( &self, value: &Value, - expected_type: &ParamType, + expected_type: &abi::DynSolType, param_name: &str, - ) -> Result { + ) -> Result { + use abi::DynSolType; + use abi::DynSolValue; + match (expected_type, value) { - (ParamType::Address, Value::Bytes(b)) => { - Ok(Token::Address(H160::from_slice(b.as_slice()))) + (DynSolType::Address, Value::Bytes(b)) => { + Ok(DynSolValue::Address(b.as_slice().try_into()?)) + } + (DynSolType::Bytes, Value::Bytes(b)) => Ok(DynSolValue::Bytes(b.as_ref().to_vec())), + (DynSolType::FixedBytes(size), Value::Bytes(b)) if b.len() == *size => { + DynSolValue::fixed_bytes_from_slice(b.as_ref()) + } + (DynSolType::String, Value::String(s)) => Ok(DynSolValue::String(s.to_string())), + (DynSolType::Bool, Value::Bool(b)) => Ok(DynSolValue::Bool(*b)), + (DynSolType::Int(_), Value::Int(i)) => { + let x = abi::I256::try_from(*i)?; + Ok(DynSolValue::Int(x, x.bits() as usize)) + } + (DynSolType::Int(_), Value::Int8(i)) => { + let x = abi::I256::try_from(*i)?; + Ok(DynSolValue::Int(x, x.bits() as usize)) } - (ParamType::Bytes, Value::Bytes(b)) => Ok(Token::Bytes(b.as_ref().to_vec())), - (ParamType::FixedBytes(size), Value::Bytes(b)) if b.len() == *size => { - Ok(Token::FixedBytes(b.as_ref().to_vec())) + (DynSolType::Int(_), Value::BigInt(i)) => { + let x = + abi::I256::from_le_bytes(i.to_signed_u256().to_le_bytes::<{ U256::BYTES }>()); + Ok(DynSolValue::Int(x, x.bits() as usize)) } - (ParamType::String, Value::String(s)) => Ok(Token::String(s.to_string())), - (ParamType::Bool, Value::Bool(b)) => Ok(Token::Bool(*b)), - (ParamType::Int(_), Value::Int(i)) => Ok(Token::Int((*i).into())), - (ParamType::Int(_), Value::Int8(i)) => Ok(Token::Int((*i).into())), - (ParamType::Int(_), Value::BigInt(i)) => Ok(Token::Int(i.to_signed_u256())), - (ParamType::Uint(_), Value::Int(i)) if *i >= 0 => Ok(Token::Uint((*i).into())), - (ParamType::Uint(_), Value::BigInt(i)) if i.sign() == Sign::Plus => { - Ok(Token::Uint(i.to_unsigned_u256()?)) + (DynSolType::Uint(_), Value::Int(i)) if *i >= 0 => { + let x = U256::try_from(*i)?; + Ok(DynSolValue::Uint(x, x.bit_len())) } - (ParamType::Array(inner_type), Value::List(values)) => { + (DynSolType::Uint(_), Value::BigInt(i)) if i.sign() == Sign::Plus => { + let x = i.to_unsigned_u256()?; + Ok(DynSolValue::Uint(x, x.bit_len())) + } + (DynSolType::Array(inner_type), Value::List(values)) => { self.process_entity_array_values(values, inner_type.as_ref(), param_name) } _ => Err(anyhow!( @@ -684,41 +726,42 @@ impl CallDecl { fn process_entity_array_values( &self, values: &[Value], - inner_type: &ParamType, + inner_type: &abi::DynSolType, param_name: &str, - ) -> Result { - let tokens: Result, Error> = values + ) -> Result { + let tokens: Result, Error> = values .iter() .enumerate() .map(|(idx, v)| { self.convert_entity_value_to_token(v, inner_type, &format!("{param_name}[{idx}]")) }) .collect(); - Ok(Token::Array(tokens?)) + Ok(abi::DynSolValue::Array(tokens?)) } /// Extracts a nested field value from a struct parameter with mixed numeric/named access fn extract_nested_struct_field_as_address( - struct_token: &Token, + struct_token: &abi::DynSolValue, field_accesses: &[usize], call_label: &str, - ) -> Result { + ) -> Result { let field_token = Self::extract_nested_struct_field(struct_token, field_accesses, call_label)?; - field_token.into_address().ok_or_else(|| { + let address = field_token.as_address().ok_or_else(|| { anyhow!( "In declarative call '{}': nested struct field is not an address", call_label ) - }) + })?; + Ok(address) } /// Extracts a nested field value from a struct parameter using numeric indices fn extract_nested_struct_field( - struct_token: &Token, + struct_token: &abi::DynSolValue, field_accesses: &[usize], call_label: &str, - ) -> Result { + ) -> Result { assert!( !field_accesses.is_empty(), "Internal error: empty field access path should be caught at parse time" @@ -728,7 +771,7 @@ impl CallDecl { for (index, &field_index) in field_accesses.iter().enumerate() { match current_token { - Token::Tuple(fields) => { + abi::DynSolValue::Tuple(fields) => { let field_token = fields .get(field_index) .ok_or_else(|| { @@ -998,15 +1041,15 @@ pub struct StructFieldInfo { pub param_name: String, /// Mapping from field names to their indices in the tuple pub field_mappings: HashMap, - /// The ethabi ParamType for type validation - pub param_type: ParamType, + /// The alloy DynSolType for type validation + pub param_type: abi::DynSolType, } impl StructFieldInfo { /// Create a new StructFieldInfo from ABI JSON components pub fn from_components( param_name: String, - param_type: ParamType, + param_type: abi::DynSolType, components: &serde_json::Value, ) -> Result { let mut field_mappings = HashMap::new(); @@ -1189,16 +1232,16 @@ pub struct DeclaredCall { label: String, contract_name: String, address: Address, - function: Function, - args: Vec, + function: abi::Function, + args: Vec, } impl DeclaredCall { pub fn from_log_trigger( mapping: &dyn FindMappingABI, call_decls: &CallDecls, - log: &Log, - params: &[LogParam], + log: &alloy::rpc::types::Log, + params: &[abi::DynSolParam], ) -> Result, anyhow::Error> { Self::from_log_trigger_with_event(mapping, call_decls, log, params) } @@ -1207,7 +1250,7 @@ impl DeclaredCall { mapping: &dyn FindMappingABI, call_decls: &CallDecls, log: &Log, - params: &[LogParam], + params: &[abi::DynSolParam], ) -> Result, anyhow::Error> { Self::create_calls(mapping, call_decls, |decl, _| { Ok(( @@ -1221,13 +1264,13 @@ impl DeclaredCall { mapping: &dyn FindMappingABI, call_decls: &CallDecls, entity: &EntitySourceOperation, - ) -> Result, anyhow::Error> { + ) -> Result, Error> { Self::create_calls(mapping, call_decls, |decl, function| { let param_types = function .inputs .iter() - .map(|param| param.kind.clone()) - .collect::>(); + .map(|param| param.selector_type().parse()) + .collect::, _>>()?; Ok(( decl.address_for_entity_handler(entity)?, @@ -1247,7 +1290,7 @@ impl DeclaredCall { get_address_and_args: F, ) -> Result, anyhow::Error> where - F: Fn(&CallDecl, &Function) -> Result<(Address, Vec), anyhow::Error>, + F: Fn(&CallDecl, &abi::Function) -> Result<(Address, Vec), anyhow::Error>, { let mut calls = Vec::new(); for decl in call_decls.decls.iter() { @@ -1285,13 +1328,15 @@ pub struct ContractCall { pub contract_name: String, pub address: Address, pub block_ptr: BlockPtr, - pub function: Function, - pub args: Vec, + pub function: abi::Function, + pub args: Vec, pub gas: Option, } #[cfg(test)] mod tests { + use alloy::primitives::B256; + use crate::data::subgraph::SPEC_VERSION_1_3_0; use super::*; @@ -1530,7 +1575,7 @@ mod tests { let parser = ExprParser::new(); let addr = "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"; - let hex_address = CallArg::HexAddress(web3::types::H160::from_str(addr).unwrap()); + let hex_address = CallArg::HexAddress(Address::from_str(addr).unwrap()); // Test HexAddress in address position let expr: CallExpr = parser.ok(&format!("Pool[{}].growth()", addr)); @@ -1597,18 +1642,19 @@ mod tests { #[test] fn test_struct_field_access_functions() { - use ethabi::Token; + use crate::abi::DynSolValue; + use alloy::primitives::{Address, U256}; let parser = ExprParser::new(); let tuple_fields = vec![ - Token::Uint(ethabi::Uint::from(8u8)), // index 0: uint8 - Token::Address([1u8; 20].into()), // index 1: address - Token::Uint(ethabi::Uint::from(1000u64)), // index 2: uint256 + DynSolValue::Uint(U256::from(8u8), 8), // index 0: uint8 + DynSolValue::Address(Address::from([1u8; 20])), // index 1: address + DynSolValue::Uint(U256::from(1000u64), 256), // index 2: uint256 ]; // Test extract_struct_field with numeric indices - let struct_token = Token::Tuple(tuple_fields.clone()); + let struct_token = DynSolValue::Tuple(tuple_fields.clone()); // Test accessing index 0 (uint8) let result = @@ -1646,8 +1692,9 @@ mod tests { #[test] fn test_declarative_call_error_context() { - use crate::prelude::web3::types::{Log, H160, H256}; - use ethabi::{LogParam, Token}; + use crate::abi::{DynSolParam, DynSolValue}; + use alloy::primitives::U256; + use alloy::rpc::types::Log; let parser = ExprParser::new(); @@ -1659,18 +1706,19 @@ mod tests { }; // Test scenario 1: Unknown parameter + let inner_log = alloy::primitives::Log { + address: Address::ZERO, + data: alloy::primitives::LogData::new_unchecked(vec![].into(), vec![].into()), + }; let log = Log { - address: H160::zero(), - topics: vec![], - data: vec![].into(), - block_hash: Some(H256::zero()), - block_number: Some(1.into()), - transaction_hash: Some(H256::zero()), - transaction_index: Some(0.into()), - log_index: Some(0.into()), - transaction_log_index: Some(0.into()), - log_type: None, - removed: Some(false), + inner: inner_log, + block_hash: Some(B256::ZERO), + block_number: Some(1), + block_timestamp: None, + transaction_hash: Some(B256::ZERO), + transaction_index: Some(0), + log_index: Some(0), + removed: false, }; let params = vec![]; // Empty params - 'asset' param is missing @@ -1681,9 +1729,9 @@ mod tests { assert!(error_msg.contains("unknown param asset")); // Test scenario 2: Struct field access error - let params = vec![LogParam { + let params = vec![DynSolParam { name: "asset".to_string(), - value: Token::Tuple(vec![Token::Uint(ethabi::Uint::from(1u8))]), // Only 1 field, but trying to access index 1 + value: DynSolValue::Tuple(vec![DynSolValue::Uint(U256::from(1u8), 8)]), // Only 1 field, but trying to access index 1 }]; let result = call_decl.address_for_log(&log, ¶ms); @@ -1694,11 +1742,11 @@ mod tests { assert!(error_msg.contains("struct has 1 fields")); // Test scenario 3: Non-address field access - let params = vec![LogParam { + let params = vec![DynSolParam { name: "asset".to_string(), - value: Token::Tuple(vec![ - Token::Uint(ethabi::Uint::from(1u8)), - Token::Uint(ethabi::Uint::from(2u8)), // Index 1 is uint, not address + value: DynSolValue::Tuple(vec![ + DynSolValue::Uint(U256::from(1u8), 8), + DynSolValue::Uint(U256::from(2u8), 8), // Index 1 is uint, not address ]), }]; @@ -1724,18 +1772,18 @@ mod tests { // Create a structure where base has only 2 fields instead of 3 // The parser thinks there should be 3 fields based on ABI, but at runtime we provide only 2 - let base_struct = Token::Tuple(vec![ - Token::Address([1u8; 20].into()), // addr at index 0 - Token::Uint(ethabi::Uint::from(100u64)), // amount at index 1 - // Missing the active field at index 2! + let base_struct = DynSolValue::Tuple(vec![ + DynSolValue::Address(Address::from([1u8; 20])), // addr at index 0 + DynSolValue::Uint(U256::from(100u64), 256), // amount at index 1 + // Missing the active field at index 2! ]); - let params = vec![LogParam { + let params = vec![DynSolParam { name: "complexAsset".to_string(), - value: Token::Tuple(vec![ - base_struct, // base with only 2 fields - Token::String("metadata".to_string()), // metadata at index 1 - Token::Array(vec![]), // values at index 2 + value: DynSolValue::Tuple(vec![ + base_struct, // base with only 2 fields + DynSolValue::String("metadata".to_string()), // metadata at index 1 + DynSolValue::Array(vec![]), // values at index 2 ]), }]; @@ -1749,7 +1797,8 @@ mod tests { #[test] fn test_struct_field_extraction_comprehensive() { - use ethabi::Token; + use crate::abi::DynSolValue; + use alloy::primitives::{Address, U256}; // Create a complex nested structure for comprehensive testing: // struct Asset { @@ -1761,37 +1810,37 @@ mod tests { // address addr; // index 0 // string name; // index 1 // } - let inner_struct = Token::Tuple(vec![ - Token::Address([0x42; 20].into()), // token.addr - Token::String("TokenName".to_string()), // token.name + let inner_struct = DynSolValue::Tuple(vec![ + DynSolValue::Address(Address::from([0x42; 20])), // token.addr + DynSolValue::String("TokenName".to_string()), // token.name ]); - let outer_struct = Token::Tuple(vec![ - Token::Uint(ethabi::Uint::from(1u8)), // asset.kind - inner_struct, // asset.token - Token::Uint(ethabi::Uint::from(1000u64)), // asset.amount + let outer_struct = DynSolValue::Tuple(vec![ + DynSolValue::Uint(U256::from(1u8), 8), // asset.kind + inner_struct, // asset.token + DynSolValue::Uint(U256::from(1000u64), 256), // asset.amount ]); // Test cases: (path, expected_value, description) let test_cases = vec![ ( vec![0], - Token::Uint(ethabi::Uint::from(1u8)), + DynSolValue::Uint(U256::from(1u8), 8), "Simple field access", ), ( vec![1, 0], - Token::Address([0x42; 20].into()), + DynSolValue::Address(Address::from([0x42; 20])), "Nested field access", ), ( vec![1, 1], - Token::String("TokenName".to_string()), + DynSolValue::String("TokenName".to_string()), "Nested string field", ), ( vec![2], - Token::Uint(ethabi::Uint::from(1000u64)), + DynSolValue::Uint(U256::from(1000u64), 256), "Last field access", ), ]; diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 3166db5971b..639cced50a0 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -37,6 +37,8 @@ pub mod env; pub mod ipfs; +pub mod abi; + /// Wrapper for spawning tasks that abort on panic, which is our default. mod tokio; #[cfg(debug_assertions)] @@ -76,12 +78,12 @@ pub use url; /// ``` pub mod prelude { pub use ::anyhow; + pub use alloy; pub use anyhow::{anyhow, Context as _, Error}; pub use atty; pub use chrono; pub use diesel; pub use envconfig; - pub use ethabi; pub use hex; pub use lazy_static::lazy_static; pub use prost; @@ -105,7 +107,6 @@ pub mod prelude { pub use tokio; pub use toml; pub use tonic; - pub use web3; pub type DynTryFuture<'a, Ok = (), Err = Error> = Pin> + Send + 'a>>; @@ -174,6 +175,7 @@ pub mod prelude { pub use crate::util::cache_weight::CacheWeight; pub use crate::util::futures::{retry, TimeoutError}; pub use crate::util::stats::MovingStats; + pub use crate::util::test_utils::*; macro_rules! static_graphql { ($m:ident, $m2:ident, {$($n:ident,)*}) => { diff --git a/graph/src/runtime/mod.rs b/graph/src/runtime/mod.rs index 7958b991598..ac94d33c32f 100644 --- a/graph/src/runtime/mod.rs +++ b/graph/src/runtime/mod.rs @@ -260,7 +260,7 @@ pub enum IndexForAscTypeId { // Reserved discriminant space for more Ethereum type IDs: [1000, 1499] TransactionReceipt = 1000, Log = 1001, - ArrayH256 = 1002, + ArrayB256 = 1002, ArrayLog = 1003, ArrayTypedMapStringStoreValue = 1004, // Continue to add more Ethereum type IDs here. diff --git a/graph/src/util/mod.rs b/graph/src/util/mod.rs index 4cdf52a82a5..7a67c984a63 100644 --- a/graph/src/util/mod.rs +++ b/graph/src/util/mod.rs @@ -35,3 +35,6 @@ pub mod monitored; pub mod intern; pub mod herd_cache; + +/// Test utilities for creating mock blockchain data structures +pub mod test_utils; diff --git a/graph/src/util/test_utils.rs b/graph/src/util/test_utils.rs new file mode 100644 index 00000000000..38b618560c4 --- /dev/null +++ b/graph/src/util/test_utils.rs @@ -0,0 +1,57 @@ +use alloy::consensus::{TxEnvelope, TxLegacy}; +use alloy::primitives::Address; +use alloy::rpc::types::Transaction; + +use crate::prelude::alloy::consensus::Header as ConsensusHeader; +use crate::prelude::alloy::primitives::B256; +use crate::prelude::alloy::rpc::types::{Block, Header}; + +/// Creates a minimal Alloy Block for testing purposes. +pub fn create_minimal_block_for_test(block_number: u64, block_hash: B256) -> Block { + // Create consensus header with defaults, but set the specific number + let mut consensus_header = ConsensusHeader::default(); + consensus_header.number = block_number; + + // Create RPC header with the specific hash + let rpc_header = Header { + hash: block_hash, + inner: consensus_header, + total_difficulty: None, + size: None, + }; + + // Create an empty block with this header + Block::empty(rpc_header) +} + +/// Generic function that creates a mock legacy Transaction from ANY log +pub fn create_dummy_transaction( + block_number: u64, + block_hash: B256, + transaction_index: Option, + transaction_hash: B256, +) -> Transaction { + use alloy::{ + consensus::transaction::Recovered, + consensus::Signed, + primitives::{Signature, U256}, + }; + + let tx = TxLegacy::default(); + + // Create a dummy signature + let signature = Signature::new(U256::from(0x1111), U256::from(0x2222), false); + + let signed_tx = Signed::new_unchecked(tx, signature, transaction_hash); + let envelope = TxEnvelope::Legacy(signed_tx); + + let recovered = Recovered::new_unchecked(envelope, Address::ZERO); + + Transaction { + inner: recovered, + block_hash: Some(block_hash), + block_number: Some(block_number), + transaction_index: transaction_index, + effective_gas_price: None, + } +} diff --git a/graphql/src/store/resolver.rs b/graphql/src/store/resolver.rs index 500964ea7a2..f2a42c1c824 100644 --- a/graphql/src/store/resolver.rs +++ b/graphql/src/store/resolver.rs @@ -10,6 +10,7 @@ use graph::data::query::{CacheStatus, QueryResults, Trace}; use graph::data::store::ID; use graph::data::value::{Object, Word}; use graph::derive::CheapClone; +use graph::prelude::alloy::primitives::B256; use graph::prelude::*; use graph::schema::{ ast as sast, INTROSPECTION_SCHEMA_FIELD_NAME, INTROSPECTION_TYPE_FIELD_NAME, META_FIELD_NAME, @@ -204,11 +205,11 @@ impl StoreResolver { // locate_block indicates that we do not have a block hash // by setting the hash to `zero` // See 7a7b9708-adb7-4fc2-acec-88680cb07ec1 - let hash_h256 = ptr.hash_as_h256(); - if hash_h256 == web3::types::H256::zero() { + let hash_b256 = ptr.hash.as_b256(); + if hash_b256 == B256::ZERO { None } else { - Some(r::Value::String(format!("0x{:x}", hash_h256))) + Some(r::Value::String(format!("0x{:x}", hash_b256))) } }) .unwrap_or(r::Value::Null); diff --git a/node/src/manager/commands/chain.rs b/node/src/manager/commands/chain.rs index 12b69d0bf4e..ad6d7bdd543 100644 --- a/node/src/manager/commands/chain.rs +++ b/node/src/manager/commands/chain.rs @@ -12,7 +12,7 @@ use graph::components::store::ChainIdStore; use graph::components::store::StoreError; use graph::prelude::BlockNumber; use graph::prelude::ChainStore as _; -use graph::prelude::LightEthereumBlockExt; +use graph::prelude::LightEthereumBlock; use graph::prelude::{anyhow, anyhow::bail}; use graph::slog::Logger; use graph::{ @@ -300,16 +300,18 @@ pub async fn ingest( else { bail!("block number {number} not found"); }; - let ptr = block.block_ptr(); + let hash = block.header.hash; + let number = block.header.number; // For inserting the block, it doesn't matter whether the block is final or not. - let block = Arc::new(BlockFinality::Final(Arc::new(block))); + let block = Arc::new(BlockFinality::Final(Arc::new(LightEthereumBlock::new( + block, + )))); chain_store.upsert_block(block).await?; - let rows = chain_store - .confirm_block_hash(ptr.number, &ptr.hash) - .await?; + let hash = hash.into(); + let rows = chain_store.confirm_block_hash(number as i32, &hash).await?; - println!("Inserted block {}", ptr); + println!("Inserted block {}", hash); if rows > 0 { println!(" (also deleted {rows} duplicate row(s) with that number)"); } diff --git a/node/src/manager/commands/check_blocks.rs b/node/src/manager/commands/check_blocks.rs index 15314067a49..f6a4506a2f8 100644 --- a/node/src/manager/commands/check_blocks.rs +++ b/node/src/manager/commands/check_blocks.rs @@ -4,8 +4,8 @@ use graph::{ cheap_clone::CheapClone, components::store::ChainStore as ChainStoreTrait, prelude::{ + alloy::primitives::B256, anyhow::{self, anyhow, Context}, - web3::types::H256, }, slog::Logger, }; @@ -105,7 +105,7 @@ pub async fn truncate(chain_store: Arc, skip_confirmation: bool) -> } async fn run( - block_hash: &H256, + block_hash: &B256, chain_store: Arc, ethereum_adapter: &EthereumAdapter, logger: &Logger, @@ -124,7 +124,7 @@ async fn run( async fn handle_multiple_block_hashes( block_number: i32, - block_hashes: &[H256], + block_hashes: &[B256], chain_store: &ChainStore, delete_duplicates: bool, ) -> anyhow::Result<()> { @@ -157,7 +157,10 @@ mod steps { use graph::{ anyhow::bail, - prelude::serde_json::{self, Value}, + prelude::{ + alloy::primitives::B256, + serde_json::{self, Value}, + }, }; use json_structural_diff::{colorize as diff_to_string, JsonDiff}; @@ -169,11 +172,11 @@ mod steps { pub(super) async fn resolve_block_hash_from_block_number( number: i32, chain_store: &ChainStore, - ) -> anyhow::Result> { + ) -> anyhow::Result> { let block_hashes = chain_store.block_hashes_by_block_number(number).await?; Ok(block_hashes .into_iter() - .map(|x| H256::from_slice(&x.as_slice()[..32])) + .map(|x| B256::from_slice(&x.as_slice()[..32])) .collect()) } @@ -181,7 +184,7 @@ mod steps { /// /// Errors on a non-unary result. pub(super) async fn fetch_single_cached_block( - block_hash: H256, + block_hash: B256, chain_store: Arc, ) -> anyhow::Result { let blocks = chain_store.blocks(vec![block_hash.into()]).await?; @@ -199,7 +202,7 @@ mod steps { /// Errors on provider failure or if the returned block has a different hash than the one /// requested. pub(super) async fn fetch_single_provider_block( - block_hash: &H256, + block_hash: &B256, ethereum_adapter: &EthereumAdapter, logger: &Logger, ) -> anyhow::Result { @@ -209,7 +212,7 @@ mod steps { .with_context(|| format!("failed to fetch block {block_hash}"))? .ok_or_else(|| anyhow!("JRPC provider found no block with hash {block_hash:?}"))?; ensure!( - provider_block.hash == Some(*block_hash), + provider_block.header.hash == *block_hash, "Provider responded with a different block hash" ); serde_json::to_value(provider_block) @@ -237,7 +240,7 @@ mod steps { } /// Prints the difference between two [`serde_json::Value`] values to the user. - pub(super) fn report_difference(difference: Option<&str>, hash: &H256) { + pub(super) fn report_difference(difference: Option<&str>, hash: &B256) { if let Some(diff) = difference { eprintln!("block {hash} diverges from cache:"); eprintln!("{diff}"); @@ -247,7 +250,7 @@ mod steps { } /// Attempts to delete a block from the block cache. - pub(super) async fn delete_block(hash: &H256, chain_store: &ChainStore) -> anyhow::Result<()> { + pub(super) async fn delete_block(hash: &B256, chain_store: &ChainStore) -> anyhow::Result<()> { println!("Deleting block {hash} from cache."); chain_store.delete_blocks(&[hash]).await?; println!("Done."); @@ -263,13 +266,13 @@ mod steps { mod helpers { use super::*; - use graph::prelude::hex; + use graph::prelude::{alloy::primitives::B256, hex}; - /// Tries to parse a [`H256`] from a hex string. - pub(super) fn parse_block_hash(hash: &str) -> anyhow::Result { + /// Tries to parse a [`B256`] from a hex string. + pub(super) fn parse_block_hash(hash: &str) -> anyhow::Result { let hash = hash.trim_start_matches("0x"); let hash = hex::decode(hash)?; - Ok(H256::from_slice(&hash)) + Ok(B256::from_slice(&hash)) } } diff --git a/runtime/test/src/common.rs b/runtime/test/src/common.rs index b0ec8018db2..dec0d7046ce 100644 --- a/runtime/test/src/common.rs +++ b/runtime/test/src/common.rs @@ -1,4 +1,3 @@ -use ethabi::Contract; use graph::blockchain::BlockTime; use graph::components::store::DeploymentLocator; use graph::components::subgraph::SharedProofOfIndexing; @@ -8,6 +7,7 @@ use graph::data_source::common::MappingABI; use graph::env::EnvVars; use graph::ipfs::{IpfsMetrics, IpfsRpcClient, ServerAddress}; use graph::log; +use graph::prelude::alloy::primitives::Address; use graph::prelude::*; use graph_chain_ethereum::{Chain, DataSource, DataSourceTemplate, Mapping, TemplateSource}; use graph_runtime_wasm::host_exports::DataSourceDetails; @@ -15,7 +15,6 @@ use graph_runtime_wasm::{HostExports, MappingContext}; use semver::Version; use std::env; use std::str::FromStr; -use web3::types::Address; lazy_static! { pub static ref LOGGER: Logger = match env::var_os("GRAPH_LOG") { @@ -83,7 +82,7 @@ fn mock_host_exports( fn mock_abi() -> MappingABI { MappingABI { name: "mock_abi".to_string(), - contract: Contract::load( + contract: serde_json::from_str( r#"[ { "inputs": [ @@ -94,8 +93,7 @@ fn mock_abi() -> MappingABI { ], "type": "constructor" } - ]"# - .as_bytes(), + ]"#, ) .unwrap(), } diff --git a/runtime/test/src/test.rs b/runtime/test/src/test.rs index 4e65c236500..2e3f671cfe6 100644 --- a/runtime/test/src/test.rs +++ b/runtime/test/src/test.rs @@ -6,7 +6,7 @@ use graph::data::store::{scalar, Id, IdType}; use graph::data::subgraph::*; use graph::data::value::Word; use graph::ipfs::test_utils::add_files_to_local_ipfs_node_for_testing; -use graph::prelude::web3::types::U256; +use graph::prelude::alloy::primitives::U256; use graph::runtime::gas::GasCounter; use graph::runtime::{AscIndexId, AscType, HostExportError}; use graph::runtime::{AscPtr, ToAscObj}; @@ -22,7 +22,6 @@ use std::collections::{BTreeMap, HashMap}; use std::str::FromStr; use test_store::{LOGGER, STORE}; use wasmtime::{AsContext, AsContextMut}; -use web3::types::H160; use crate::common::{mock_context, mock_data_source}; @@ -715,19 +714,19 @@ async fn test_big_int_to_hex(api_version: Version, gas_used: u64) { .await; // Convert zero to hex - let zero = BigInt::from_unsigned_u256(&U256::zero()); + let zero = BigInt::from_unsigned_u256(&U256::ZERO); let zero_hex_ptr: AscPtr = instance.invoke_export1("big_int_to_hex", &zero).await; let zero_hex_str: String = instance.asc_get(zero_hex_ptr).unwrap(); assert_eq!(zero_hex_str, "0x0"); // Convert 1 to hex - let one = BigInt::from_unsigned_u256(&U256::one()); + let one = BigInt::from_unsigned_u256(&U256::ONE); let one_hex_ptr: AscPtr = instance.invoke_export1("big_int_to_hex", &one).await; let one_hex_str: String = instance.asc_get(one_hex_ptr).unwrap(); assert_eq!(one_hex_str, "0x1"); // Convert U256::max_value() to hex - let u256_max = BigInt::from_unsigned_u256(&U256::max_value()); + let u256_max = BigInt::from_unsigned_u256(&U256::MAX); let u256_max_hex_ptr: AscPtr = instance.invoke_export1("big_int_to_hex", &u256_max).await; let u256_max_hex_str: String = instance.asc_get(u256_max_hex_ptr).unwrap(); diff --git a/runtime/test/src/test/abi.rs b/runtime/test/src/test/abi.rs index 886626a2871..5aa7df7b132 100644 --- a/runtime/test/src/test/abi.rs +++ b/runtime/test/src/test/abi.rs @@ -1,4 +1,4 @@ -use graph::prelude::{ethabi::Token, web3::types::U256}; +use graph::{abi, prelude::alloy::primitives::Address}; use graph_runtime_wasm::asc_abi::class::{ ArrayBuffer, AscAddress, AscEnum, AscEnumArray, EthereumValueKind, StoreValueKind, TypedArray, }; @@ -182,9 +182,9 @@ async fn abi_bytes_and_fixed_bytes_v0_0_5() { test_abi_bytes_and_fixed_bytes(API_VERSION_0_0_5).await; } -async fn test_abi_ethabi_token_identity(api_version: Version) { +async fn test_abi_alloy_token_identity(api_version: Version) { let mut instance = test_module( - "abiEthabiTokenIdentity", + "abiAlloyTokenIdentity", mock_data_source( &wasm_file_path("abi_token.wasm", api_version.clone()), api_version.clone(), @@ -194,8 +194,8 @@ async fn test_abi_ethabi_token_identity(api_version: Version) { .await; // Token::Address - let address = H160([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - let token_address = Token::Address(address); + let address = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; + let token_address = abi::DynSolValue::Address(address.into()); let new_address_obj: AscPtr = instance .invoke_export1("token_to_address", &token_address) @@ -209,7 +209,7 @@ async fn test_abi_ethabi_token_identity(api_version: Version) { assert_eq!(token_address, new_token); // Token::Bytes - let token_bytes = Token::Bytes(vec![42, 45, 7, 245, 45]); + let token_bytes = abi::DynSolValue::Bytes(vec![42, 45, 7, 245, 45]); let new_bytes_obj: AscPtr = instance .invoke_export1("token_to_bytes", &token_bytes) .await; @@ -221,7 +221,8 @@ async fn test_abi_ethabi_token_identity(api_version: Version) { assert_eq!(token_bytes, new_token); // Token::Int - let int_token = Token::Int(U256([256, 453452345, 0, 42])); + let int = abi::I256::from_limbs([256, 453452345, 0, 42]); + let int_token = abi::DynSolValue::Int(int, int.bits() as usize); let new_int_obj: AscPtr = instance.invoke_export1("token_to_int", &int_token).await; @@ -233,7 +234,8 @@ async fn test_abi_ethabi_token_identity(api_version: Version) { assert_eq!(int_token, new_token); // Token::Uint - let uint_token = Token::Uint(U256([256, 453452345, 0, 42])); + let uint = U256::from_limbs([256, 453452345, 0, 42]); + let uint_token = abi::DynSolValue::Uint(uint, uint.bit_len()); let new_uint_obj: AscPtr = instance.invoke_export1("token_to_uint", &uint_token).await; @@ -246,7 +248,7 @@ async fn test_abi_ethabi_token_identity(api_version: Version) { assert_ne!(uint_token, int_token); // Token::Bool - let token_bool = Token::Bool(true); + let token_bool = abi::DynSolValue::Bool(true); let token_bool_ptr = instance.asc_new(&token_bool).await.unwrap(); let func = instance @@ -270,7 +272,7 @@ async fn test_abi_ethabi_token_identity(api_version: Version) { assert_eq!(token_bool, new_token); // Token::String - let token_string = Token::String("漢字Go🇧🇷".into()); + let token_string = abi::DynSolValue::String("漢字Go🇧🇷".into()); let new_string_obj: AscPtr = instance .invoke_export1("token_to_string", &token_string) .await; @@ -282,8 +284,8 @@ async fn test_abi_ethabi_token_identity(api_version: Version) { assert_eq!(token_string, new_token); // Token::Array - let token_array = Token::Array(vec![token_address, token_bytes, token_bool]); - let token_array_nested = Token::Array(vec![token_string, token_array]); + let token_array = abi::DynSolValue::Array(vec![token_address, token_bytes, token_bool]); + let token_array_nested = abi::DynSolValue::Array(vec![token_string, token_array]); let new_array_obj: AscEnumArray = instance .invoke_export1("token_to_array", &token_array_nested) .await; @@ -291,7 +293,7 @@ async fn test_abi_ethabi_token_identity(api_version: Version) { let new_token_ptr = instance .takes_ptr_returns_ptr("token_from_array", new_array_obj) .await; - let new_token: Token = instance.asc_get(new_token_ptr).unwrap(); + let new_token: abi::DynSolValue = instance.asc_get(new_token_ptr).unwrap(); assert_eq!(new_token, token_array_nested); } @@ -300,14 +302,14 @@ async fn test_abi_ethabi_token_identity(api_version: Version) { /// and assert the final token is the same as the starting one. #[graph::test] async fn abi_ethabi_token_identity_v0_0_4() { - test_abi_ethabi_token_identity(API_VERSION_0_0_4).await; + test_abi_alloy_token_identity(API_VERSION_0_0_4).await; } /// Test a roundtrip Token -> Payload -> Token identity conversion through asc, /// and assert the final token is the same as the starting one. #[graph::test] async fn abi_ethabi_token_identity_v0_0_5() { - test_abi_ethabi_token_identity(API_VERSION_0_0_5).await; + test_abi_alloy_token_identity(API_VERSION_0_0_5).await; } async fn test_abi_store_value(api_version: Version) { @@ -447,17 +449,17 @@ async fn test_abi_h160(api_version: Version) { api_version, ) .await; - let address = H160::zero(); + let address = Address::ZERO; // As an `Uint8Array` let new_address_obj: AscPtr = module.invoke_export1("test_address", &address).await; // This should have 1 added to the first and last byte. - let new_address: H160 = module.asc_get(new_address_obj).unwrap(); + let new_address: Address = module.asc_get(new_address_obj).unwrap(); assert_eq!( new_address, - H160([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) + Address::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) ) } @@ -509,14 +511,14 @@ async fn test_abi_big_int(api_version: Version) { .await; // Test passing in 0 and increment it by 1 - let old_uint = U256::zero(); + let old_uint = U256::ZERO; let new_uint_obj: AscPtr = module .invoke_export1("test_uint", &BigInt::from_unsigned_u256(&old_uint)) .await; let new_uint: BigInt = module.asc_get(new_uint_obj).unwrap(); assert_eq!(new_uint, BigInt::from(1_i32)); let new_uint = new_uint.to_unsigned_u256().unwrap(); - assert_eq!(new_uint, U256([1, 0, 0, 0])); + assert_eq!(new_uint, U256::from_limbs([1, 0, 0, 0])); // Test passing in -50 and increment it by 1 let old_uint = BigInt::from(-50); diff --git a/runtime/wasm/Cargo.toml b/runtime/wasm/Cargo.toml index e2260a7bb59..d446bd0fbe1 100644 --- a/runtime/wasm/Cargo.toml +++ b/runtime/wasm/Cargo.toml @@ -5,7 +5,6 @@ edition.workspace = true [dependencies] async-trait = { workspace = true } -ethabi = "17.2" hex = "0.4.3" graph = { path = "../../graph" } bs58 = "0.4.0" diff --git a/runtime/wasm/src/asc_abi/class.rs b/runtime/wasm/src/asc_abi/class.rs index 4fe5b3192cd..805e30de895 100644 --- a/runtime/wasm/src/asc_abi/class.rs +++ b/runtime/wasm/src/asc_abi/class.rs @@ -1,6 +1,5 @@ use async_trait::async_trait; -use ethabi; - +use graph::abi; use graph::{ data::{ store::{self, scalar::Timestamp}, @@ -540,21 +539,25 @@ pub enum EthereumValueKind { FixedArray, Array, Tuple, + Function, } impl EthereumValueKind { - pub(crate) fn get_kind(token: ðabi::Token) -> Self { - match token { - ethabi::Token::Address(_) => EthereumValueKind::Address, - ethabi::Token::FixedBytes(_) => EthereumValueKind::FixedBytes, - ethabi::Token::Bytes(_) => EthereumValueKind::Bytes, - ethabi::Token::Int(_) => EthereumValueKind::Int, - ethabi::Token::Uint(_) => EthereumValueKind::Uint, - ethabi::Token::Bool(_) => EthereumValueKind::Bool, - ethabi::Token::String(_) => EthereumValueKind::String, - ethabi::Token::FixedArray(_) => EthereumValueKind::FixedArray, - ethabi::Token::Array(_) => EthereumValueKind::Array, - ethabi::Token::Tuple(_) => EthereumValueKind::Tuple, + pub(crate) fn get_kind(value: &abi::DynSolValue) -> Self { + use graph::abi::DynSolValue; + + match value { + DynSolValue::Bool(_) => Self::Bool, + DynSolValue::Int(_, _) => Self::Int, + DynSolValue::Uint(_, _) => Self::Uint, + DynSolValue::FixedBytes(_, _) => Self::FixedBytes, + DynSolValue::Address(_) => Self::Address, + DynSolValue::Function(_) => Self::Function, + DynSolValue::Bytes(_) => Self::Bytes, + DynSolValue::String(_) => Self::String, + DynSolValue::Array(_) => Self::Array, + DynSolValue::FixedArray(_) => Self::FixedArray, + DynSolValue::Tuple(_) => Self::Tuple, } } } diff --git a/runtime/wasm/src/host_exports.rs b/runtime/wasm/src/host_exports.rs index 43e235c6299..4054766f026 100644 --- a/runtime/wasm/src/host_exports.rs +++ b/runtime/wasm/src/host_exports.rs @@ -5,12 +5,13 @@ use std::time::{Duration, Instant}; use graph::data::subgraph::API_VERSION_0_0_8; use graph::data::value::Word; -use graph::futures03::StreamExt; +use graph::futures03::stream::StreamExt; +use graph::prelude::alloy::primitives::Address; use graph::schema::EntityType; use never::Never; use semver::Version; -use web3::types::H160; +use graph::abi; use graph::blockchain::BlockTime; use graph::blockchain::Blockchain; use graph::components::link_resolver::LinkResolverContext; @@ -21,8 +22,6 @@ use graph::components::subgraph::{ use graph::data::store::{self}; use graph::data_source::{CausalityRegion, DataSource, EntityTypeAccess}; use graph::ensure; -use graph::prelude::ethabi::param_type::Reader; -use graph::prelude::ethabi::{decode, encode, Token}; use graph::prelude::serde_json; use graph::prelude::{slog::b, slog::record_static, *}; use graph::runtime::gas::{self, complexity, Gas, GasCounter}; @@ -1173,19 +1172,19 @@ impl HostExports { .map_err(|e| DeterministicHostError::from(Error::from(e))) } - pub(crate) fn string_to_h160( + pub(crate) fn string_to_address( &self, string: &str, gas: &GasCounter, state: &mut BlockState, - ) -> Result { + ) -> Result { Self::track_gas_and_ops( gas, state, gas::DEFAULT_GAS_OP.with_args(complexity::Size, &string), "string_to_h160", )?; - string_to_h160(string) + string_to_address(string) } pub(crate) fn bytes_to_string( @@ -1207,11 +1206,11 @@ impl HostExports { pub(crate) fn ethereum_encode( &self, - token: Token, + value: abi::DynSolValue, gas: &GasCounter, state: &mut BlockState, ) -> Result, DeterministicHostError> { - let encoded = encode(&[token]); + let encoded = value.abi_encode(); Self::track_gas_and_ops( gas, @@ -1229,7 +1228,7 @@ impl HostExports { data: Vec, gas: &GasCounter, state: &mut BlockState, - ) -> Result { + ) -> Result { Self::track_gas_and_ops( gas, state, @@ -1237,15 +1236,9 @@ impl HostExports { "ethereum_decode", )?; - let param_types = - Reader::read(&types).map_err(|e| anyhow::anyhow!("Failed to read types: {}", e))?; + let ty: abi::DynSolType = types.parse().context("Failed to read types")?; - decode(&[param_types], &data) - // The `.pop().unwrap()` here is ok because we're always only passing one - // `param_types` to `decode`, so the returned `Vec` has always size of one. - // We can't do `tokens[0]` because the value can't be moved out of the `Vec`. - .map(|mut tokens| tokens.pop().unwrap()) - .context("Failed to decode") + ty.abi_decode(&data).context("Failed to decode") } pub(crate) fn yaml_from_bytes( @@ -1279,11 +1272,9 @@ impl HostExports { } } -fn string_to_h160(string: &str) -> Result { - // `H160::from_str` takes a hex string with no leading `0x`. - let s = string.trim_start_matches("0x"); - H160::from_str(s) - .with_context(|| format!("Failed to convert string to Address/H160: '{}'", s)) +fn string_to_address(string: &str) -> Result { + Address::from_str(string) + .with_context(|| format!("Failed to convert string to Address: '{}'", string)) .map_err(DeterministicHostError::from) } @@ -1382,8 +1373,8 @@ pub mod test_support { #[test] fn test_string_to_h160_with_0x() { assert_eq!( - H160::from_str("A16081F360e3847006dB660bae1c6d1b2e17eC2A").unwrap(), - string_to_h160("0xA16081F360e3847006dB660bae1c6d1b2e17eC2A").unwrap() + Address::from_str("A16081F360e3847006dB660bae1c6d1b2e17eC2A").unwrap(), + string_to_address("0xA16081F360e3847006dB660bae1c6d1b2e17eC2A").unwrap() ) } diff --git a/runtime/wasm/src/module/context.rs b/runtime/wasm/src/module/context.rs index 9ecb04782ef..ce5a7c1109f 100644 --- a/runtime/wasm/src/module/context.rs +++ b/runtime/wasm/src/module/context.rs @@ -455,7 +455,7 @@ impl WasmInstanceContext<'_> { let s: String = asc_get(self, str_ptr, gas)?; let host_exports = self.as_ref().ctx.host_exports.cheap_clone(); let ctx = &mut self.as_mut().ctx; - let h160 = host_exports.string_to_h160(&s, gas, &mut ctx.state)?; + let h160 = host_exports.string_to_address(&s, gas, &mut ctx.state)?; asc_new(self, &h160, gas).await } diff --git a/runtime/wasm/src/module/instance.rs b/runtime/wasm/src/module/instance.rs index 21560bb4fe5..c57ec0e9da9 100644 --- a/runtime/wasm/src/module/instance.rs +++ b/runtime/wasm/src/module/instance.rs @@ -2,8 +2,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Instant; use anyhow::Error; +use graph::futures03::future::BoxFuture; use graph::futures03::FutureExt as _; -use graph::prelude::web3::futures::future::BoxFuture; use graph::slog::SendSyncRefUnwindSafeKV; use semver::Version; diff --git a/runtime/wasm/src/to_from/external.rs b/runtime/wasm/src/to_from/external.rs index 022c07b0bc8..1ae4e6426fe 100644 --- a/runtime/wasm/src/to_from/external.rs +++ b/runtime/wasm/src/to_from/external.rs @@ -1,54 +1,58 @@ use async_trait::async_trait; -use ethabi; - +use graph::abi::DynSolValueExt; +use graph::abi::{self}; use graph::data::store::scalar::Timestamp; use graph::data::value::Word; +use graph::prelude::alloy::primitives::{Address, B256}; use graph::prelude::{BigDecimal, BigInt}; use graph::runtime::gas::GasCounter; +use graph::runtime::AscHeap; use graph::runtime::{ asc_get, asc_new, AscIndexId, AscPtr, AscType, AscValue, HostExportError, ToAscObj, }; use graph::{data::store, runtime::DeterministicHostError}; -use graph::{prelude::serde_json, runtime::FromAscObj}; -use graph::{prelude::web3::types as web3, runtime::AscHeap}; +use graph::{ + prelude::{alloy::primitives::U256, serde_json}, + runtime::FromAscObj, +}; use crate::asc_abi::class::*; +impl FromAscObj for Address { + fn from_asc_obj( + typed_array: Uint8Array, + heap: &H, + gas: &GasCounter, + depth: usize, + ) -> Result { + let data = <[u8; 20]>::from_asc_obj(typed_array, heap, gas, depth)?; + Ok(Self::from(data)) + } +} + #[async_trait] -impl ToAscObj for web3::H160 { +impl ToAscObj for Address { async fn to_asc_obj( &self, heap: &mut H, gas: &GasCounter, ) -> Result { - self.0.to_asc_obj(heap, gas).await + self.as_slice().to_asc_obj(heap, gas).await } } #[async_trait] -impl ToAscObj for web3::Bytes { +impl ToAscObj for B256 { async fn to_asc_obj( &self, heap: &mut H, gas: &GasCounter, ) -> Result { - self.0.to_asc_obj(heap, gas).await + self.as_slice().to_asc_obj(heap, gas).await } } -impl FromAscObj for web3::H160 { - fn from_asc_obj( - typed_array: Uint8Array, - heap: &H, - gas: &GasCounter, - depth: usize, - ) -> Result { - let data = <[u8; 20]>::from_asc_obj(typed_array, heap, gas, depth)?; - Ok(Self(data)) - } -} - -impl FromAscObj for web3::H256 { +impl FromAscObj for B256 { fn from_asc_obj( typed_array: Uint8Array, heap: &H, @@ -60,30 +64,6 @@ impl FromAscObj for web3::H256 { } } -#[async_trait] -impl ToAscObj for web3::H256 { - async fn to_asc_obj( - &self, - heap: &mut H, - gas: &GasCounter, - ) -> Result { - self.0.to_asc_obj(heap, gas).await - } -} - -#[async_trait] -impl ToAscObj for web3::U128 { - async fn to_asc_obj( - &self, - heap: &mut H, - gas: &GasCounter, - ) -> Result { - let mut bytes: [u8; 16] = [0; 16]; - self.to_little_endian(&mut bytes); - bytes.to_asc_obj(heap, gas).await - } -} - #[async_trait] impl ToAscObj for BigInt { async fn to_asc_obj( @@ -173,34 +153,47 @@ impl ToAscObj>> for Vec { } #[async_trait] -impl ToAscObj> for ethabi::Token { +impl ToAscObj> for abi::DynSolValue { async fn to_asc_obj( &self, heap: &mut H, gas: &GasCounter, ) -> Result, HostExportError> { - use ethabi::Token::*; - let kind = EthereumValueKind::get_kind(self); + let payload = match self { - Address(address) => asc_new::(heap, address, gas) - .await? - .to_payload(), - FixedBytes(bytes) | Bytes(bytes) => asc_new::(heap, &**bytes, gas) - .await? - .to_payload(), - Int(uint) => { - let n = BigInt::from_signed_u256(uint); + Self::Bool(val) => *val as u64, + Self::Int(val, _) => { + let bytes = val.to_le_bytes::<32>(); + let n = BigInt::from_signed_bytes_le(&bytes)?; + asc_new(heap, &n, gas).await?.to_payload() } - Uint(uint) => { - let n = BigInt::from_unsigned_u256(uint); + Self::Uint(val, _) => { + let bytes = val.to_le_bytes::<32>(); + let n = BigInt::from_unsigned_bytes_le(&bytes)?; + asc_new(heap, &n, gas).await?.to_payload() } - Bool(b) => *b as u64, - String(string) => asc_new(heap, &**string, gas).await?.to_payload(), - FixedArray(tokens) | Array(tokens) => asc_new(heap, &**tokens, gas).await?.to_payload(), - Tuple(tokens) => asc_new(heap, &**tokens, gas).await?.to_payload(), + Self::FixedBytes(val, size) => { + // FixedBytes stores the value in a 32-byte word, but we only want the first `size` bytes + asc_new::(heap, &val.as_slice()[..*size], gas) + .await? + .to_payload() + } + Self::Address(val) => asc_new::(heap, val.as_slice(), gas) + .await? + .to_payload(), + Self::Function(val) => asc_new::(heap, val.as_slice(), gas) + .await? + .to_payload(), + Self::Bytes(val) => asc_new::(heap, &**val, gas) + .await? + .to_payload(), + Self::String(val) => asc_new(heap, &**val, gas).await?.to_payload(), + Self::Array(values) => asc_new(heap, &**values, gas).await?.to_payload(), + Self::FixedArray(values) => asc_new(heap, &**values, gas).await?.to_payload(), + Self::Tuple(values) => asc_new(heap, &**values, gas).await?.to_payload(), }; Ok(AscEnum { @@ -211,34 +204,41 @@ impl ToAscObj> for ethabi::Token { } } -impl FromAscObj> for ethabi::Token { +impl FromAscObj> for abi::DynSolValue { fn from_asc_obj( asc_enum: AscEnum, heap: &H, gas: &GasCounter, depth: usize, ) -> Result { - use ethabi::Token; - let payload = asc_enum.payload; - Ok(match asc_enum.kind { - EthereumValueKind::Bool => Token::Bool(bool::from(payload)), + + let value = match asc_enum.kind { EthereumValueKind::Address => { let ptr: AscPtr = AscPtr::from(payload); - Token::Address(asc_get(heap, ptr, gas, depth)?) + let bytes: [u8; 20] = asc_get(heap, ptr, gas, depth)?; + + Self::Address(bytes.into()) } EthereumValueKind::FixedBytes => { let ptr: AscPtr = AscPtr::from(payload); - Token::FixedBytes(asc_get(heap, ptr, gas, depth)?) + let bytes: Vec = asc_get(heap, ptr, gas, depth)?; + + Self::fixed_bytes_from_slice(&bytes)? } EthereumValueKind::Bytes => { let ptr: AscPtr = AscPtr::from(payload); - Token::Bytes(asc_get(heap, ptr, gas, depth)?) + let bytes: Vec = asc_get(heap, ptr, gas, depth)?; + + Self::Bytes(bytes) } EthereumValueKind::Int => { let ptr: AscPtr = AscPtr::from(payload); let n: BigInt = asc_get(heap, ptr, gas, depth)?; - Token::Int(n.to_signed_u256()) + let x = + abi::I256::from_le_bytes(n.to_signed_u256().to_le_bytes::<{ U256::BYTES }>()); + + Self::Int(x, x.bits() as usize) } EthereumValueKind::Uint => { let ptr: AscPtr = AscPtr::from(payload); @@ -246,25 +246,38 @@ impl FromAscObj> for ethabi::Token { let uint = n .to_unsigned_u256() .map_err(DeterministicHostError::Other)?; - Token::Uint(uint) + Self::Uint(uint, uint.bit_len()) } + EthereumValueKind::Bool => Self::Bool(bool::from(payload)), EthereumValueKind::String => { let ptr: AscPtr = AscPtr::from(payload); - Token::String(asc_get(heap, ptr, gas, depth)?) + + Self::String(asc_get(heap, ptr, gas, depth)?) } EthereumValueKind::FixedArray => { let ptr: AscEnumArray = AscPtr::from(payload); - Token::FixedArray(asc_get(heap, ptr, gas, depth)?) + + Self::FixedArray(asc_get(heap, ptr, gas, depth)?) } EthereumValueKind::Array => { let ptr: AscEnumArray = AscPtr::from(payload); - Token::Array(asc_get(heap, ptr, gas, depth)?) + + Self::Array(asc_get(heap, ptr, gas, depth)?) } EthereumValueKind::Tuple => { let ptr: AscEnumArray = AscPtr::from(payload); - Token::Tuple(asc_get(heap, ptr, gas, depth)?) + + Self::Tuple(asc_get(heap, ptr, gas, depth)?) } - }) + EthereumValueKind::Function => { + let ptr: AscPtr = AscPtr::from(payload); + let bytes: [u8; 24] = asc_get(heap, ptr, gas, depth)?; + + Self::Function(bytes.into()) + } + }; + + Ok(value) } } diff --git a/server/index-node/src/resolver.rs b/server/index-node/src/resolver.rs index e07e0dc62da..ff0d25d4e38 100644 --- a/server/index-node/src/resolver.rs +++ b/server/index-node/src/resolver.rs @@ -4,8 +4,8 @@ use std::convert::TryInto; use async_trait::async_trait; use graph::data::query::Trace; use graph::data::store::Id; +use graph::prelude::alloy::primitives::Address; use graph::schema::EntityType; -use web3::types::Address; use git_testament::{git_testament, CommitKind}; use graph::blockchain::{Blockchain, BlockchainKind, BlockchainMap}; @@ -370,7 +370,7 @@ impl IndexNodeResolver { if !poi_protection.validate_access_token(self.bearer_token.as_deref()) { // Let's sign the POI with a zero'd address when the access token is // invalid. - indexer = Some(Address::zero()); + indexer = Some(Address::ZERO); } let poi_fut = self diff --git a/store/postgres/src/chain_store.rs b/store/postgres/src/chain_store.rs index eadde677f96..7d49d0750b2 100644 --- a/store/postgres/src/chain_store.rs +++ b/store/postgres/src/chain_store.rs @@ -10,6 +10,7 @@ use graph::data::store::ethereum::call; use graph::derive::CheapClone; use graph::env::ENV_VARS; use graph::parking_lot::RwLock; +use graph::prelude::alloy::primitives::B256; use graph::prelude::MetricsRegistry; use graph::prometheus::{CounterVec, GaugeVec}; use graph::slog::Logger; @@ -26,7 +27,6 @@ use std::{ use graph::blockchain::{Block, BlockHash, ChainIdentifier, ExtendedBlockPtr}; use graph::cheap_clone::CheapClone; -use graph::prelude::web3::types::{H256, U256}; use graph::prelude::{ serde_json as json, transaction_receipt::LightTransactionReceipt, BlockNumber, BlockPtr, CachedEthereumCall, ChainStore as ChainStoreTrait, Error, EthereumCallCache, StoreError, @@ -56,12 +56,12 @@ impl JsonBlock { } } - fn timestamp(&self) -> Option { + fn timestamp(&self) -> Option { self.data .as_ref() .and_then(|data| data.get("timestamp")) .and_then(|ts| ts.as_str()) - .and_then(|ts| U256::from_dec_str(ts).ok()) + .and_then(|ts| ts.parse::().ok()) } } @@ -103,9 +103,8 @@ mod data { use graph::blockchain::{Block, BlockHash}; use graph::data::store::scalar::Bytes; use graph::internal_error; - use graph::prelude::ethabi::ethereum_types::H160; + use graph::prelude::alloy::primitives::{Address, B256}; use graph::prelude::transaction_receipt::LightTransactionReceipt; - use graph::prelude::web3::types::H256; use graph::prelude::{ info, serde_json as json, BlockNumber, BlockPtr, CachedEthereumCall, Error, Logger, StoreError, @@ -175,17 +174,17 @@ mod data { hash: Vec, } - // Like H256::from_slice, but returns an error instead of panicking + // Like B256::from_slice, but returns an error instead of panicking // when `bytes` does not have the right length - fn h256_from_bytes(bytes: &[u8]) -> Result { - if bytes.len() == H256::len_bytes() { - Ok(H256::from_slice(bytes)) + fn b256_from_bytes(bytes: &[u8]) -> Result { + if bytes.len() == B256::len_bytes() { + Ok(B256::from_slice(bytes)) } else { Err(internal_error!( "invalid H256 value `{}` has {} bytes instead of {}", graph::prelude::hex::encode(bytes), bytes.len(), - H256::len_bytes() + B256::len_bytes() )) } } @@ -892,9 +891,9 @@ mod data { conn: &mut AsyncPgConnection, chain: &str, first_block: i64, - hash: H256, - genesis: H256, - ) -> Result, Error> { + hash: B256, + genesis: B256, + ) -> Result, Error> { match self { Storage::Shared => { // We recursively build a temp table 'chain' containing the hash and @@ -978,15 +977,15 @@ mod data { ); let missing = sql_query(query) - .bind::(hash.as_bytes()) - .bind::(genesis.as_bytes()) + .bind::(hash.as_slice()) + .bind::(genesis.as_slice()) .bind::(first_block) .load::(conn) .await?; let missing = match missing.len() { 0 => None, - 1 => Some(h256_from_bytes(&missing[0].hash)?), + 1 => Some(b256_from_bytes(&missing[0].hash)?), _ => { unreachable!("the query can only return no or one row") } @@ -1226,7 +1225,7 @@ mod data { &self, conn: &mut AsyncPgConnection, chain: &str, - block_hashes: &[&H256], + block_hashes: &[&B256], ) -> Result { match self { Storage::Shared => { @@ -1252,7 +1251,7 @@ mod data { ); let hashes: Vec<&[u8]> = - block_hashes.iter().map(|hash| hash.as_bytes()).collect(); + block_hashes.iter().map(|hash| hash.as_slice()).collect(); sql_query(query) .bind::, _>(hashes) @@ -1404,7 +1403,7 @@ mod data { .map(|row| CachedEthereumCall { blake3_id: row.0, block_ptr: block_ptr.clone(), - contract_address: H160::from_slice(&row.2[..]), + contract_address: Address::from_slice(&row.2[..]), return_value: row.1, }) .collect()) @@ -1819,7 +1818,7 @@ mod data { pub(crate) async fn find_transaction_receipts_in_block( &self, conn: &mut AsyncPgConnection, - block_hash: H256, + block_hash: B256, ) -> anyhow::Result> { let query = sql_query(format!( " @@ -1853,7 +1852,7 @@ from ( } Storage::Private(_) => { query - .bind::(block_hash.as_bytes()) + .bind::(block_hash.as_slice()) .get_results(conn) .await } @@ -2195,7 +2194,7 @@ impl ChainStore { self.recent_blocks_cache.blocks() } - pub async fn delete_blocks(&self, block_hashes: &[&H256]) -> Result { + pub async fn delete_blocks(&self, block_hashes: &[&B256]) -> Result { let mut conn = self.pool.get_permitted().await?; self.storage .delete_blocks_by_hash(&mut conn, &self.chain, block_hashes) @@ -2257,10 +2256,10 @@ impl ChainStore { async fn attempt_chain_head_update_inner( &self, ancestor_count: BlockNumber, - ) -> Result<(Option, Option<(String, i64)>), StoreError> { + ) -> Result<(Option, Option<(String, i64)>), StoreError> { use public::ethereum_networks as n; - let genesis_block_ptr = self.genesis_block_ptr().await?.hash_as_h256(); + let genesis_block_ptr = self.genesis_block_ptr().await?.hash.as_b256(); let mut conn = self.pool.get_permitted().await?; let candidate = self @@ -2278,7 +2277,7 @@ impl ChainStore { &mut conn, &self.chain, first_block as i64, - ptr.hash_as_h256(), + ptr.hash.as_b256(), genesis_block_ptr, ) .await? @@ -2291,7 +2290,7 @@ impl ChainStore { let hash = ptr.hash_hex(); let number = ptr.number as i64; - conn.transaction::<(Option, Option<(String, i64)>), StoreError, _>(|conn| { + conn.transaction::<(Option, Option<(String, i64)>), StoreError, _>(|conn| { async move { update(n::table.filter(n::name.eq(&self.chain))) .set(( @@ -2326,7 +2325,7 @@ fn json_block_to_block_ptr_ext(json_block: &JsonBlock) -> Result, Option)>(&mut conn) @@ -2352,7 +2351,7 @@ impl ChainHeadStore for ChainStore { // FIXME: // // workaround for arweave - H256::from_slice(&hex::decode(hash).unwrap()[..32]), + B256::from_slice(&hex::decode(hash).unwrap()[..32]), *number, ) .into(), @@ -2361,7 +2360,8 @@ impl ChainHeadStore for ChainStore { _ => unreachable!(), }) .and_then(|opt: Option| opt) - })?) + }) + .map_err(Error::from) } async fn chain_head_cursor(&self) -> Result, Error> { @@ -2461,7 +2461,7 @@ impl ChainStoreTrait for ChainStore { async fn attempt_chain_head_update( self: Arc, ancestor_count: BlockNumber, - ) -> Result, Error> { + ) -> Result, Error> { let (missing, ptr) = self.attempt_chain_head_update_inner(ancestor_count).await?; if let Some((hash, number)) = ptr { @@ -2847,7 +2847,7 @@ impl ChainStoreTrait for ChainStore { async fn transaction_receipts_in_block( &self, - block_hash: &H256, + block_hash: &B256, ) -> Result, StoreError> { let mut conn = self.pool.get_permitted().await?; self.storage diff --git a/store/postgres/src/deployment.rs b/store/postgres/src/deployment.rs index 1a8cf9586c8..70c67c1ed43 100644 --- a/store/postgres/src/deployment.rs +++ b/store/postgres/src/deployment.rs @@ -12,6 +12,7 @@ use diesel::{ sql_types::{Nullable, Text}, }; use diesel_async::{RunQueryDsl, SimpleAsyncConnection}; +use graph::prelude::alloy::primitives::B256; use graph::{ blockchain::block_stream::FirehoseCursor, data::subgraph::schema::SubgraphError, @@ -20,18 +21,14 @@ use graph::{ slog::{debug, Logger}, }; use graph::{components::store::StoreResult, semver::Version}; -use graph::{ - data::store::scalar::ToPrimitive, - prelude::{ - anyhow, hex, web3::types::H256, BlockNumber, BlockPtr, DeploymentHash, DeploymentState, - StoreError, - }, - schema::InputSchema, -}; use graph::{ data::subgraph::schema::{DeploymentCreate, SubgraphManifestEntity}, util::backoff::ExponentialBackoff, }; +use graph::{ + prelude::{anyhow, hex, BlockNumber, BlockPtr, DeploymentHash, DeploymentState, StoreError}, + schema::InputSchema, +}; use stable_hash_legacy::crypto::SetHasher; use std::sync::Arc; use std::{convert::TryFrom, ops::Bound, time::Duration}; @@ -263,8 +260,7 @@ async fn graft( // FIXME: // // workaround for arweave - let hash = H256::from_slice(&hash.as_slice()[..32]); - let block = block.to_u64().expect("block numbers fit into a u64"); + let hash = B256::from_slice(&hash.as_slice()[..32]); let subgraph = DeploymentHash::new(subgraph.clone()).map_err(|_| { StoreError::Unknown(anyhow!( "the base subgraph for a graft must be a valid subgraph id but is `{}`", diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index 1df16c155b5..3c1e98ff1d2 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -20,6 +20,7 @@ use graph::data::subgraph::{status, SPEC_VERSION_0_0_6}; use graph::data_source::CausalityRegion; use graph::derive::CheapClone; use graph::futures03::FutureExt; +use graph::prelude::alloy::primitives::Address; use graph::prelude::{ApiVersion, EntityOperation, PoolWaitStats, SubgraphDeploymentEntity}; use graph::semver::Version; use itertools::Itertools; @@ -38,12 +39,11 @@ use graph::components::subgraph::{ProofOfIndexingFinisher, ProofOfIndexingVersio use graph::data::subgraph::schema::{DeploymentCreate, SubgraphError}; use graph::internal_error; use graph::prelude::{ - anyhow, debug, info, o, warn, web3, AttributeNames, BlockNumber, BlockPtr, CheapClone, + anyhow, debug, info, o, warn, AttributeNames, BlockNumber, BlockPtr, CheapClone, DeploymentHash, DeploymentState, Entity, EntityQuery, Error, Logger, QueryExecutionError, StopwatchMetrics, StoreError, UnfailOutcome, Value, ENV_VARS, }; use graph::schema::{ApiSchema, EntityKey, EntityType, InputSchema}; -use web3::types::Address; use crate::block_range::{BLOCK_COLUMN, BLOCK_RANGE_COLUMN}; use crate::deployment::{self, OnSync}; diff --git a/store/postgres/src/detail.rs b/store/postgres/src/detail.rs index 7dcac2f3bd8..8eff3ecb2d2 100644 --- a/store/postgres/src/detail.rs +++ b/store/postgres/src/detail.rs @@ -14,13 +14,14 @@ use git_testament::{git_testament, git_testament_macros}; use graph::blockchain::BlockHash; use graph::data::store::scalar::ToPrimitive; use graph::data::subgraph::schema::{SubgraphError, SubgraphManifestEntity}; +use graph::prelude::alloy::primitives::B256; use graph::prelude::BlockNumber; use graph::prelude::{ chrono::{DateTime, Utc}, BlockPtr, DeploymentHash, StoreError, SubgraphDeploymentEntity, }; use graph::schema::InputSchema; -use graph::{data::subgraph::status, internal_error, prelude::web3::types::H256}; +use graph::{data::subgraph::status, internal_error}; use itertools::Itertools; use std::collections::HashMap; use std::convert::TryFrom; @@ -191,7 +192,7 @@ impl TryFrom for SubgraphError { // FIXME: // // workaround for arweave - let block_hash = block_hash.map(|hash| H256::from_slice(&hash.as_slice()[..32])); + let block_hash = block_hash.map(|hash| B256::from_slice(&hash.as_slice()[..32])); // In existing databases, we have errors that have a `block_range` of // `UNVERSIONED_RANGE`, which leads to `None` as the block number, but // has a hash. Conversely, it is also possible for an error to not have a diff --git a/store/postgres/src/store.rs b/store/postgres/src/store.rs index 5acac2691b2..4adec80ab5b 100644 --- a/store/postgres/src/store.rs +++ b/store/postgres/src/store.rs @@ -12,8 +12,8 @@ use graph::{ data::subgraph::status, internal_error, prelude::{ - web3::types::Address, BlockNumber, BlockPtr, CheapClone, DeploymentHash, PartialBlockPtr, - QueryExecutionError, StoreError, + alloy::primitives::Address, BlockNumber, BlockPtr, CheapClone, DeploymentHash, + PartialBlockPtr, QueryExecutionError, StoreError, }, }; diff --git a/store/postgres/src/subgraph_store.rs b/store/postgres/src/subgraph_store.rs index 1cc89a69233..28d9580a17e 100644 --- a/store/postgres/src/subgraph_store.rs +++ b/store/postgres/src/subgraph_store.rs @@ -27,16 +27,16 @@ use graph::{ internal_error, prelude::StoreEvent, prelude::{ - anyhow, lazy_static, o, web3::types::Address, ApiVersion, BlockNumber, BlockPtr, - ChainStore, DeploymentHash, EntityOperation, Logger, MetricsRegistry, NodeId, - PartialBlockPtr, StoreError, SubgraphDeploymentEntity, SubgraphName, - SubgraphStore as SubgraphStoreTrait, SubgraphVersionSwitchingMode, + anyhow, lazy_static, o, ApiVersion, BlockNumber, BlockPtr, ChainStore, DeploymentHash, + EntityOperation, Logger, MetricsRegistry, NodeId, PartialBlockPtr, StoreError, + SubgraphDeploymentEntity, SubgraphName, SubgraphStore as SubgraphStoreTrait, + SubgraphVersionSwitchingMode, }, schema::{ApiSchema, InputSchema}, url::Url, util::timed_cache::TimedCache, }; -use graph::{derive::CheapClone, futures03::future::join_all}; +use graph::{derive::CheapClone, futures03::future::join_all, prelude::alloy::primitives::Address}; use crate::{ deployment::{OnSync, SubgraphHealth}, @@ -1153,7 +1153,7 @@ impl Inner { }; let block_for_poi_query = BlockPtr::new(block_hash.clone(), block_number); - let indexer = Some(Address::zero()); + let indexer = Some(Address::ZERO); let poi = store .get_proof_of_indexing(site, &indexer, block_for_poi_query) .await?; diff --git a/store/postgres/src/transaction_receipt.rs b/store/postgres/src/transaction_receipt.rs index 115a32f1cc2..73b11c9c400 100644 --- a/store/postgres/src/transaction_receipt.rs +++ b/store/postgres/src/transaction_receipt.rs @@ -39,15 +39,32 @@ impl TryFrom for LightTransactionReceipt { let block_hash = block_hash.map(drain_vector).transpose()?; let block_number = block_number.map(drain_vector).transpose()?; let gas_used = gas_used.map(drain_vector).transpose()?; - let status = status.map(drain_vector).transpose()?; + + // Convert big-endian bytes to numbers + let transaction_index = u64::from_be_bytes(transaction_index); + let block_number = block_number.map(u64::from_be_bytes); + let gas_used = gas_used.map(u64::from_be_bytes).unwrap_or(0); + + // Handle both old U64 format and new boolean format + let status = status + .map(|bytes| { + match bytes.len() { + 1 => bytes[0] != 0, // New format: single byte + 8 => { + u64::from_be_bytes(drain_vector::<8>(bytes.to_vec()).unwrap_or([0; 8])) != 0 + } // Old format: U64 + _ => false, // Fallback + } + }) + .unwrap_or(false); Ok(LightTransactionReceipt { transaction_hash: transaction_hash.into(), - transaction_index: transaction_index.into(), + transaction_index, block_hash: block_hash.map(Into::into), - block_number: block_number.map(Into::into), - gas_used: gas_used.map(Into::into), - status: status.map(Into::into), + block_number, + gas_used, + status, }) } } diff --git a/store/test-store/Cargo.toml b/store/test-store/Cargo.toml index fd6f9ba0566..9cf617c3083 100644 --- a/store/test-store/Cargo.toml +++ b/store/test-store/Cargo.toml @@ -22,3 +22,4 @@ tokio = { workspace = true } [dev-dependencies] hex = "0.4.3" pretty_assertions = "1.4.1" +serde_json = { workspace = true } diff --git a/store/test-store/src/block_store.rs b/store/test-store/src/block_store.rs index f085e2dbd9d..ed3c091ad07 100644 --- a/store/test-store/src/block_store.rs +++ b/store/test-store/src/block_store.rs @@ -1,17 +1,18 @@ use std::{convert::TryFrom, str::FromStr, sync::Arc}; use graph::blockchain::{BlockTime, ChainIdentifier}; +use graph::prelude::alloy::consensus::Header as ConsensusHeader; +use graph::prelude::alloy::primitives::{Bloom, B256, U256}; +use graph::prelude::alloy::rpc::types::{Block, Header}; +use graph::prelude::LightEthereumBlock; use lazy_static::lazy_static; use graph::components::store::BlockStore; use graph::{ blockchain::Block as BlockchainBlock, - prelude::{ - serde_json, web3::types::H256, web3::types::U256, BlockHash, BlockNumber, BlockPtr, - EthereumBlock, LightEthereumBlock, - }, + prelude::{serde_json, BlockHash, BlockNumber, BlockPtr, EthereumBlock}, }; -use graph_chain_ethereum::codec::{Block, BlockHeader}; +use graph_chain_ethereum::codec::{Block as FirehoseBlock, BlockHeader}; use prost_types::Timestamp; use crate::{GENESIS_PTR, NETWORK_VERSION}; @@ -103,24 +104,34 @@ impl FakeBlock { } pub fn as_ethereum_block(&self) -> EthereumBlock { - let parent_hash = H256::from_str(self.parent_hash.as_str()).expect("invalid parent hash"); + let parent_hash = B256::from_str(self.parent_hash.as_str()).expect("invalid parent hash"); + let block_hash = B256::from_str(self.hash.as_str()).expect("invalid block hash"); - let mut block = LightEthereumBlock::default(); - block.number = Some(self.number.into()); - block.parent_hash = parent_hash; - block.hash = Some(H256(self.block_hash().as_slice().try_into().unwrap())); + let mut consensus_header = ConsensusHeader::default(); + consensus_header.number = self.number as u64; + consensus_header.parent_hash = parent_hash; + consensus_header.logs_bloom = Bloom::default(); // Empty bloom filter for test blocks if let Some(ts) = self.timestamp { - block.timestamp = ts; + consensus_header.timestamp = ts.to::(); } + let rpc_header = Header { + hash: block_hash, + inner: consensus_header, + total_difficulty: None, + size: None, + }; + + let block = Block::empty(rpc_header); + EthereumBlock { - block: Arc::new(block), + block: Arc::new(LightEthereumBlock::new(block.into())), transaction_receipts: Vec::new(), } } - pub fn as_firehose_block(&self) -> Block { - let mut block = Block::default(); + pub fn as_firehose_block(&self) -> FirehoseBlock { + let mut block = FirehoseBlock::default(); block.hash = self.hash.clone().into_bytes(); block.number = self.number as u64; diff --git a/store/test-store/src/store.rs b/store/test-store/src/store.rs index a671e770a6f..1778bf46813 100644 --- a/store/test-store/src/store.rs +++ b/store/test-store/src/store.rs @@ -10,6 +10,7 @@ use graph::data::subgraph::schema::{DeploymentCreate, SubgraphError}; use graph::data::subgraph::SubgraphFeature; use graph::data_source::DataSource; use graph::log; +use graph::prelude::alloy::primitives::B256; use graph::prelude::{QueryStoreManager as _, SubgraphStore as _, *}; use graph::schema::EntityType; use graph::schema::InputSchema; @@ -37,7 +38,6 @@ use std::collections::BTreeSet; use std::collections::HashMap; use std::time::Instant; use std::{marker::PhantomData, sync::Mutex}; -use web3::types::H256; pub const NETWORK_NAME: &str = "fake_network"; pub const DATA_SOURCE_KIND: &str = "mock/kind"; @@ -69,14 +69,14 @@ lazy_static! { pub static ref SUBGRAPH_STORE: Arc = STORE.subgraph_store(); static ref BLOCK_STORE: DieselBlockStore = STORE.block_store(); pub static ref GENESIS_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "bd34884280958002c51d3f7b5f853e6febeba33de0f40d15b0363006533c924f" )), 0u64 ) .into(); pub static ref BLOCK_ONE: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "8511fa04b64657581e3f00e14543c1d522d5d7e771b54aa3060b662ade47da13" )), 1u64 @@ -84,14 +84,15 @@ lazy_static! { .into(); pub static ref BLOCKS: [BlockPtr; 4] = { let two: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1" )), 2u64, ) .into(); + let three: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "977c084229c72a0fa377cae304eda9099b6a2cb5d83b25cdf0f0969b69874255" )), 3u64, diff --git a/store/test-store/tests/chain/ethereum/manifest.rs b/store/test-store/tests/chain/ethereum/manifest.rs index 172068add9c..e001dcb5696 100644 --- a/store/test-store/tests/chain/ethereum/manifest.rs +++ b/store/test-store/tests/chain/ethereum/manifest.rs @@ -18,7 +18,7 @@ use graph::data_source::offchain::OffchainDataSourceKind; use graph::data_source::{DataSourceEnum, DataSourceTemplate}; use graph::entity; use graph::env::ENV_VARS; -use graph::prelude::web3::types::H256; +use graph::prelude::alloy::primitives::B256; use graph::prelude::{ anyhow, serde_yaml, BigDecimal, BigInt, DeploymentHash, Link, SubgraphManifest, SubgraphManifestResolveError, SubgraphManifestValidationError, SubgraphStore, @@ -796,18 +796,18 @@ specVersion: 1.2.0 assert_eq!( Some(vec![ - H256::from_str("0000000000000000000000000000000000000000000000000000000000000000") + B256::from_str("0000000000000000000000000000000000000000000000000000000000000000") .unwrap(), - H256::from_str("0000000000000000000000000000000000000000000000000000000000000001") + B256::from_str("0000000000000000000000000000000000000000000000000000000000000001") .unwrap(), - H256::from_str("0000000000000000000000000000000000000000000000000000000000000002") + B256::from_str("0000000000000000000000000000000000000000000000000000000000000002") .unwrap() ]), topic1.clone() ); assert_eq!( - Some(vec![H256::from_str( + Some(vec![B256::from_str( "0000000000000000000000000000000000000000000000000000000000000001" ) .unwrap()]), @@ -815,7 +815,7 @@ specVersion: 1.2.0 ); assert_eq!( - Some(vec![H256::from_str( + Some(vec![B256::from_str( "0000000000000000000000000000000000000000000000000000000000000002" ) .unwrap()]), diff --git a/store/test-store/tests/graph/entity_cache.rs b/store/test-store/tests/graph/entity_cache.rs index be27d111fa8..37c183d91c2 100644 --- a/store/test-store/tests/graph/entity_cache.rs +++ b/store/test-store/tests/graph/entity_cache.rs @@ -8,6 +8,7 @@ use graph::components::store::{ use graph::data::store::Id; use graph::data::subgraph::schema::{DeploymentCreate, SubgraphError, SubgraphHealth}; use graph::data_source::CausalityRegion; +use graph::prelude::alloy::primitives::B256; use graph::schema::{EntityKey, EntityType, InputSchema}; use graph::{ components::store::{DeploymentId, DeploymentLocator}, @@ -22,7 +23,6 @@ use slog::Logger; use std::collections::{BTreeMap, BTreeSet}; use std::marker::PhantomData; use std::sync::Arc; -use web3::types::H256; use graph_store_postgres::SubgraphStore as DieselSubgraphStore; use test_store::*; @@ -399,7 +399,7 @@ lazy_static! { InputSchema::parse_latest(ACCOUNT_GQL, LOAD_RELATED_ID.clone()) .expect("Failed to parse user schema"); static ref TEST_BLOCK_1_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "8511fa04b64657581e3f00e14543c1d522d5d7e771b54aa3060b662ade47da13" )), 1u64 diff --git a/store/test-store/tests/postgres/chain_head.rs b/store/test-store/tests/postgres/chain_head.rs index 98f1045de7a..f41446df6a1 100644 --- a/store/test-store/tests/postgres/chain_head.rs +++ b/store/test-store/tests/postgres/chain_head.rs @@ -7,14 +7,14 @@ use graph::data::store::ethereum::call; use graph::data::store::scalar::Bytes; use graph::env::ENV_VARS; use graph::futures03::executor; +use graph::prelude::alloy::primitives::B256; use std::future::Future; use std::sync::Arc; -use graph::prelude::web3::types::H256; +use graph::cheap_clone::CheapClone; +use graph::prelude::{alloy, serde_json as json, EthereumBlock}; use graph::prelude::{anyhow::anyhow, anyhow::Error}; -use graph::prelude::{serde_json as json, EthereumBlock}; use graph::prelude::{BlockNumber, QueryStoreManager, QueryTarget}; -use graph::{cheap_clone::CheapClone, prelude::web3::types::H160}; use graph::{components::store::BlockStore as _, prelude::DeploymentHash}; use graph::{ components::store::ChainHeadStore as _, components::store::ChainStore as _, @@ -329,7 +329,7 @@ fn check_ancestor( } let act_block = json::from_value::(act.0)?; - let act_hash = format!("{:x}", act_block.block.hash.unwrap()); + let act_hash = format!("{:x}", act_block.block.hash()); let exp_hash = &exp.hash; if &act_hash != exp_hash { @@ -441,7 +441,9 @@ fn eth_call_cache() { call::Retval::Value(Bytes::from(value)) } - let address = H160([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + let address = alloy::primitives::Address::from_slice(&[ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + ]); let call: [u8; 6] = [1, 2, 3, 4, 5, 6]; let return_value: [u8; 3] = [7, 8, 9]; @@ -542,7 +544,9 @@ fn test_clear_stale_call_cache() { run_test_async(chain, |chain_store, _, _| async move { let logger = LOGGER.cheap_clone(); - let address = H160([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3]); + let address = alloy::primitives::Address::from([ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + ]); let call: [u8; 6] = [1, 2, 3, 4, 5, 6]; let return_value: [u8; 3] = [7, 8, 9]; @@ -593,7 +597,7 @@ fn test_clear_stale_call_cache() { diesel::sql_query(format!( "UPDATE {meta_table} SET accessed_at = NOW() - INTERVAL '8 days' WHERE contract_address = $1" )) - .bind::(address.as_bytes()) + .bind::(address.as_slice()) .execute(&mut conn) .await .unwrap(); @@ -616,7 +620,7 @@ fn test_transaction_receipts_in_block_function() { let chain = vec![]; run_test_async(chain, move |store, _, _| async move { let receipts = store - .transaction_receipts_in_block(&H256::zero()) + .transaction_receipts_in_block(&B256::ZERO) .await .unwrap(); assert!(receipts.is_empty()) diff --git a/store/test-store/tests/postgres/relational.rs b/store/test-store/tests/postgres/relational.rs index 483be514504..a2fda3e3f1e 100644 --- a/store/test-store/tests/postgres/relational.rs +++ b/store/test-store/tests/postgres/relational.rs @@ -3,9 +3,10 @@ use diesel_async::SimpleAsyncConnection; use graph::components::store::write::{EntityModification, RowGroup}; use graph::data::store::scalar; use graph::entity; +use graph::prelude::alloy::primitives::B256; use graph::prelude::{ - o, slog, web3::types::H256, DeploymentHash, Entity, EntityCollection, EntityFilter, - EntityOrder, EntityQuery, Logger, StopwatchMetrics, Value, ValueType, BLOCK_NUMBER_MAX, + o, slog, DeploymentHash, Entity, EntityCollection, EntityFilter, EntityOrder, EntityQuery, + Logger, StopwatchMetrics, Value, ValueType, BLOCK_NUMBER_MAX, }; use graph::prelude::{BlockNumber, MetricsRegistry}; use graph::schema::{EntityKey, EntityType, InputSchema}; @@ -185,13 +186,13 @@ lazy_static! { static ref LARGE_INT: BigInt = BigInt::from(std::i64::MAX).pow(17).unwrap(); static ref LARGE_DECIMAL: BigDecimal = BigDecimal::from(1) / BigDecimal::new(LARGE_INT.clone(), 1); - static ref BYTES_VALUE: H256 = H256::from(hex!( + static ref BYTES_VALUE: B256 = B256::from(hex!( "e8b3b02b936c4a4a331ac691ac9a86e197fb7731f14e3108602c87d4dac55160" )); - static ref BYTES_VALUE2: H256 = H256::from(hex!( + static ref BYTES_VALUE2: B256 = B256::from(hex!( "b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1" )); - static ref BYTES_VALUE3: H256 = H256::from(hex!( + static ref BYTES_VALUE3: B256 = B256::from(hex!( "977c084229c72a0fa377cae304eda9099b6a2cb5d83b25cdf0f0969b69874255" )); static ref SCALAR_ENTITY: Entity = { diff --git a/store/test-store/tests/postgres/relational_bytes.rs b/store/test-store/tests/postgres/relational_bytes.rs index c42bdc2eef4..eb449dc3021 100644 --- a/store/test-store/tests/postgres/relational_bytes.rs +++ b/store/test-store/tests/postgres/relational_bytes.rs @@ -5,6 +5,7 @@ use graph::components::store::write::RowGroup; use graph::data::store::scalar; use graph::data_source::CausalityRegion; use graph::entity; +use graph::prelude::alloy::primitives::B256; use graph::prelude::{BlockNumber, EntityModification, EntityQuery, MetricsRegistry, StoreError}; use graph::schema::{EntityKey, EntityType, InputSchema}; use graph_store_postgres::AsyncPgConnection; @@ -17,9 +18,9 @@ use std::{collections::BTreeMap, sync::Arc}; use graph::data::store::scalar::{BigDecimal, BigInt}; use graph::data::store::IdList; use graph::prelude::{ - o, slog, web3::types::H256, AttributeNames, ChildMultiplicity, DeploymentHash, Entity, - EntityCollection, EntityLink, EntityWindow, Logger, ParentLink, StopwatchMetrics, - WindowAttribute, BLOCK_NUMBER_MAX, + o, slog, AttributeNames, ChildMultiplicity, DeploymentHash, Entity, EntityCollection, + EntityLink, EntityWindow, Logger, ParentLink, StopwatchMetrics, WindowAttribute, + BLOCK_NUMBER_MAX, }; use graph_store_postgres::{ layout_for_tests::make_dummy_site, @@ -46,13 +47,13 @@ lazy_static! { static ref LARGE_INT: BigInt = BigInt::from(std::i64::MAX).pow(17).unwrap(); static ref LARGE_DECIMAL: BigDecimal = BigDecimal::from(1) / BigDecimal::new(LARGE_INT.clone(), 1); - static ref BYTES_VALUE: H256 = H256::from(hex!( + static ref BYTES_VALUE: B256 = B256::from(hex!( "e8b3b02b936c4a4a331ac691ac9a86e197fb7731f14e3108602c87d4dac55160" )); - static ref BYTES_VALUE2: H256 = H256::from(hex!( + static ref BYTES_VALUE2: B256 = B256::from(hex!( "b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1" )); - static ref BYTES_VALUE3: H256 = H256::from(hex!( + static ref BYTES_VALUE3: B256 = B256::from(hex!( "977c084229c72a0fa377cae304eda9099b6a2cb5d83b25cdf0f0969b69874255" )); static ref BEEF_ENTITY: Entity = entity! { THINGS_SCHEMA => diff --git a/store/test-store/tests/postgres/store.rs b/store/test-store/tests/postgres/store.rs index 60fb746fbe8..81c277a8854 100644 --- a/store/test-store/tests/postgres/store.rs +++ b/store/test-store/tests/postgres/store.rs @@ -3,6 +3,7 @@ use graph::blockchain::BlockTime; use graph::data::graphql::ext::TypeDefinitionExt; use graph::data::subgraph::schema::DeploymentCreate; use graph::data_source::common::MappingABI; +use graph::prelude::alloy::primitives::{Address, B256}; use graph::schema::{EntityType, InputSchema}; use graph_chain_ethereum::Mapping; use hex_literal::hex; @@ -15,13 +16,11 @@ use graph::data::subgraph::*; use graph::{ blockchain::DataSource, components::store::{BlockStore as _, EntityFilter, EntityOrder, EntityQuery, StatusStore}, - prelude::ethabi::Contract, }; use graph::{data::store::scalar, semver::Version}; use graph::{entity, prelude::*}; use graph_store_postgres::layout_for_tests::STRING_PREFIX_SIZE; use graph_store_postgres::{Store as DieselStore, SubgraphStore as DieselSubgraphStore}; -use web3::types::{Address, H256}; const USER_GQL: &str = " interface ColorAndAge { @@ -65,56 +64,56 @@ lazy_static! { InputSchema::parse_latest(USER_GQL, TEST_SUBGRAPH_ID.clone()) .expect("Failed to parse user schema"); static ref TEST_BLOCK_0_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "bd34884280958002c51d3f7b5f853e6febeba33de0f40d15b0363006533c924f" )), 0u64 ) .into(); static ref TEST_BLOCK_1_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "8511fa04b64657581e3f00e14543c1d522d5d7e771b54aa3060b662ade47da13" )), 1u64 ) .into(); static ref TEST_BLOCK_2_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1" )), 2u64 ) .into(); static ref TEST_BLOCK_3_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "977c084229c72a0fa377cae304eda9099b6a2cb5d83b25cdf0f0969b69874255" )), 3u64 ) .into(); static ref TEST_BLOCK_3A_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "d163aec0592c7cb00c2700ab65dcaac93289f5d250b3b889b39198b07e1fbe4a" )), 3u64 ) .into(); static ref TEST_BLOCK_4_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "007a03cdf635ebb66f5e79ae66cc90ca23d98031665649db056ff9c6aac2d74d" )), 4u64 ) .into(); static ref TEST_BLOCK_4A_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "8fab27e9e9285b0a39110f4d9877f05d0f43d2effa157e55f4dcc49c3cf8cbd7" )), 4u64 ) .into(); static ref TEST_BLOCK_5_PTR: BlockPtr = ( - H256::from(hex!( + B256::from(hex!( "e8b3b02b936c4a4a331ac691ac9a86e197fb7731f14e3108602c87d4dac55160" )), 5u64 @@ -1133,19 +1132,18 @@ fn mock_data_source() -> graph_chain_ethereum::DataSource { fn mock_abi() -> MappingABI { MappingABI { name: "mock_abi".to_string(), - contract: Contract::load( + contract: serde_json::from_str( r#"[ - { - "inputs": [ - { - "name": "a", - "type": "address" - } - ], - "type": "constructor" - } - ]"# - .as_bytes(), + { + "inputs": [ + { + "name": "a", + "type": "address" + } + ], + "type": "constructor" + } + ]"#, ) .unwrap(), } diff --git a/store/test-store/tests/postgres/writable.rs b/store/test-store/tests/postgres/writable.rs index da828e8784f..feaae3e5c4e 100644 --- a/store/test-store/tests/postgres/writable.rs +++ b/store/test-store/tests/postgres/writable.rs @@ -2,6 +2,7 @@ use graph::blockchain::block_stream::{EntitySourceOperation, FirehoseCursor}; use graph::data::subgraph::schema::DeploymentCreate; use graph::data::value::Word; use graph::data_source::CausalityRegion; +use graph::prelude::alloy::primitives::B256; use graph::schema::{EntityKey, EntityType, InputSchema}; use lazy_static::lazy_static; use std::collections::{BTreeMap, BTreeSet}; @@ -18,7 +19,6 @@ use graph::semver::Version; use graph::{entity, prelude::*}; use graph_store_postgres::layout_for_tests::writable; use graph_store_postgres::{Store as DieselStore, SubgraphStore as DieselSubgraphStore}; -use web3::types::H256; const SCHEMA_GQL: &str = " type Counter @entity { @@ -145,7 +145,7 @@ where } fn block_pointer(number: u8) -> BlockPtr { - let hash = H256::from([number; 32]); + let hash = B256::from([number; 32]); BlockPtr::from((hash, number as BlockNumber)) } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index ed44ad4888c..0a00813d2c5 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -25,6 +25,10 @@ tokio = { version = "1.45.1", features = ["rt", "macros", "process"] } # here needs to be kept in sync with the web3 version that the graph crate # uses until then secp256k1 = { version = "0.21", features = ["recovery"] } +web3 = { git = "https://github.com/graphprotocol/rust-web3", branch = "graph-patches-onto-0.18", features = [ + "arbitrary_precision", + "test", +] } [dev-dependencies] anyhow = "1.0.100" diff --git a/tests/src/contract.rs b/tests/src/contract.rs index 2d3d72216f3..80a9ba57031 100644 --- a/tests/src/contract.rs +++ b/tests/src/contract.rs @@ -3,12 +3,13 @@ use std::str::FromStr; use graph::prelude::{ lazy_static, serde_json::{self, Value}, - web3::{ - api::{Eth, Namespace}, - contract::{tokens::Tokenize, Contract as Web3Contract, Options}, - transports::Http, - types::{Address, Block, BlockId, BlockNumber, Bytes, TransactionReceipt, H256}, - }, +}; + +use web3::{ + api::{Eth, Namespace}, + contract::{tokens::Tokenize, Contract as Web3Contract, Options}, + transports::Http, + types::{Address, Block, BlockId, BlockNumber, Bytes, TransactionReceipt, H256}, }; // web3 version 0.18 does not expose this; once the graph crate updates to // version 0.19, we can use web3::signing::SecretKey from the graph crate @@ -161,16 +162,16 @@ impl Contract { if contract.name == "DeclaredCallsContract" { status!("contracts", "Emitting transfers from DeclaredCallsContract"); let addr1 = "0x1111111111111111111111111111111111111111" - .parse::() + .parse::() .unwrap(); let addr2 = "0x2222222222222222222222222222222222222222" - .parse::() + .parse::() .unwrap(); let addr3 = "0x3333333333333333333333333333333333333333" - .parse::() + .parse::() .unwrap(); let addr4 = "0x4444444444444444444444444444444444444444" - .parse::() + .parse::() .unwrap(); contract diff --git a/tests/src/fixture/ethereum.rs b/tests/src/fixture/ethereum.rs index ddf950bd273..11470ca52d4 100644 --- a/tests/src/fixture/ethereum.rs +++ b/tests/src/fixture/ethereum.rs @@ -6,16 +6,20 @@ use super::{ test_ptr, CommonChainConfig, MutexBlockStreamBuilder, NoopAdapterSelector, NoopRuntimeAdapterBuilder, StaticBlockRefetcher, StaticStreamBuilder, Stores, TestChain, }; +use graph::abi; +use graph::blockchain::block_stream::BlockWithTriggers; use graph::blockchain::block_stream::{EntityOperationKind, EntitySourceOperation}; use graph::blockchain::client::ChainClient; use graph::blockchain::{BlockPtr, Trigger, TriggersAdapterSelector}; use graph::cheap_clone::CheapClone; use graph::data_source::subgraph; -use graph::prelude::ethabi::ethereum_types::H256; -use graph::prelude::web3::types::{Address, Log, Transaction, H160}; -use graph::prelude::{ethabi, tiny_keccak, DeploymentHash, Entity, LightEthereumBlock, ENV_VARS}; +use graph::prelude::alloy::primitives::{Address, B256, U256}; +use graph::prelude::alloy::rpc::types::BlockTransactions; +use graph::prelude::{ + create_dummy_transaction, create_minimal_block_for_test, tiny_keccak, DeploymentHash, Entity, + LightEthereumBlock, ENV_VARS, +}; use graph::schema::EntityType; -use graph::{blockchain::block_stream::BlockWithTriggers, prelude::ethabi::ethereum_types::U64}; use graph_chain_ethereum::network::EthereumNetworkAdapters; use graph_chain_ethereum::trigger::LogRef; use graph_chain_ethereum::Chain; @@ -76,12 +80,11 @@ pub async fn chain( pub fn genesis() -> BlockWithTriggers { let ptr = test_ptr(0); + + let block = create_minimal_block_for_test(ptr.number as u64, ptr.hash.as_b256()); + BlockWithTriggers:: { - block: BlockFinality::Final(Arc::new(LightEthereumBlock { - hash: Some(H256::from_slice(ptr.hash.as_slice())), - number: Some(U64::from(ptr.number)), - ..Default::default() - })), + block: BlockFinality::Final(Arc::new(LightEthereumBlock::new(block.into()))), trigger_data: vec![Trigger::Chain(EthereumTrigger::Block( ptr, EthereumBlockTriggerType::End, @@ -101,7 +104,7 @@ pub fn generate_empty_blocks_for_range( let parent_ptr = blocks.last().map(|b| b.ptr()).unwrap_or(parent_ptr.clone()); let ptr = BlockPtr { number: i, - hash: H256::from_low_u64_be(i as u64 + add_to_hash).into(), + hash: B256::from(U256::from(i as u64 + add_to_hash)).into(), }; blocks.push(empty_block(parent_ptr, ptr)); } @@ -113,25 +116,19 @@ pub fn empty_block(parent_ptr: BlockPtr, ptr: BlockPtr) -> BlockWithTriggers parent_ptr.number); - // A 0x000.. transaction is used so `push_test_log` can use it - let transactions = vec![Transaction { - hash: H256::zero(), - block_hash: Some(H256::from_slice(ptr.hash.as_slice())), - block_number: Some(ptr.number.into()), - transaction_index: Some(0.into()), - from: Some(H160::zero()), - to: Some(H160::zero()), - ..Default::default() - }]; + let dummy_txn = + create_dummy_transaction(ptr.number as u64, ptr.hash.as_b256(), Some(0), B256::ZERO); + let transactions = BlockTransactions::Full(vec![dummy_txn]); + let alloy_block = create_minimal_block_for_test(ptr.number as u64, ptr.hash.as_b256()) + .map_header(|mut header| { + // Ensure the parent hash matches the given parent_ptr so that parent_ptr() lookups succeed + header.inner.parent_hash = parent_ptr.hash.as_b256(); + header + }) + .with_transactions(transactions); BlockWithTriggers:: { - block: BlockFinality::Final(Arc::new(LightEthereumBlock { - hash: Some(H256::from_slice(ptr.hash.as_slice())), - number: Some(U64::from(ptr.number)), - parent_hash: H256::from_slice(parent_ptr.hash.as_slice()), - transactions, - ..Default::default() - })), + block: BlockFinality::Final(Arc::new(LightEthereumBlock::new(alloy_block.into()))), trigger_data: vec![Trigger::Chain(EthereumTrigger::Block( ptr, EthereumBlockTriggerType::End, @@ -140,19 +137,25 @@ pub fn empty_block(parent_ptr: BlockPtr, ptr: BlockPtr) -> BlockWithTriggers, payload: impl Into) { + use graph::prelude::alloy::{self, primitives::LogData, rpc::types::Log}; + let log = Arc::new(Log { - address: Address::zero(), - topics: vec![tiny_keccak::keccak256(b"TestEvent(string)").into()], - data: ethabi::encode(&[ethabi::Token::String(payload.into())]).into(), - block_hash: Some(H256::from_slice(block.ptr().hash.as_slice())), - block_number: Some(block.ptr().number.into()), - transaction_hash: Some(H256::from_low_u64_be(0)), - transaction_index: Some(0.into()), - log_index: Some(0.into()), - transaction_log_index: Some(0.into()), - log_type: None, - removed: None, + inner: alloy::primitives::Log { + address: Address::ZERO, + data: LogData::new_unchecked( + vec![tiny_keccak::keccak256(b"TestEvent(string)").into()], + abi::DynSolValue::String(payload.into()).abi_encode().into(), + ), + }, + block_hash: Some(B256::from_slice(block.ptr().hash.as_slice())), + block_number: Some(block.ptr().number as u64), + transaction_hash: Some(B256::from(U256::from(0))), + transaction_index: Some(0), + log_index: Some(0), + block_timestamp: None, + removed: false, }); + block .trigger_data .push(Trigger::Chain(EthereumTrigger::Log(LogRef::FullLog( @@ -190,23 +193,30 @@ pub fn push_test_command( test_command: impl Into, data: impl Into, ) { + use graph::prelude::alloy::{self, primitives::LogData, rpc::types::Log}; + let log = Arc::new(Log { - address: Address::zero(), - topics: vec![tiny_keccak::keccak256(b"TestEvent(string,string)").into()], - data: ethabi::encode(&[ - ethabi::Token::String(test_command.into()), - ethabi::Token::String(data.into()), - ]) - .into(), - block_hash: Some(H256::from_slice(block.ptr().hash.as_slice())), - block_number: Some(block.ptr().number.into()), - transaction_hash: Some(H256::from_low_u64_be(0)), - transaction_index: Some(0.into()), - log_index: Some(0.into()), - transaction_log_index: Some(0.into()), - log_type: None, - removed: None, + inner: alloy::primitives::Log { + address: Address::ZERO, + data: LogData::new_unchecked( + vec![tiny_keccak::keccak256(b"TestEvent(string,string)").into()], + abi::DynSolValue::Tuple(vec![ + abi::DynSolValue::String(test_command.into()), + abi::DynSolValue::String(data.into()), + ]) + .abi_encode_params() + .into(), + ), + }, + block_hash: Some(block.ptr().hash.as_b256()), + block_number: Some(block.ptr().number as u64), + transaction_hash: Some(B256::from(U256::from(0))), + transaction_index: Some(0), + log_index: Some(0), + block_timestamp: None, + removed: false, }); + block .trigger_data .push(Trigger::Chain(EthereumTrigger::Log(LogRef::FullLog( diff --git a/tests/src/fixture/mod.rs b/tests/src/fixture/mod.rs index 3554624d477..2a71436e2b9 100644 --- a/tests/src/fixture/mod.rs +++ b/tests/src/fixture/mod.rs @@ -38,7 +38,8 @@ use graph::http_body_util::Full; use graph::hyper::body::Bytes; use graph::hyper::Request; use graph::ipfs::{IpfsClient, IpfsMetrics}; -use graph::prelude::ethabi::ethereum_types::H256; +use graph::prelude::alloy::primitives::B256; +use graph::prelude::alloy::primitives::U256; use graph::prelude::serde_json::{self, json}; use graph::prelude::{ lazy_static, q, r, ApiVersion, BigInt, BlockNumber, DeploymentHash, GraphQlRunner as _, @@ -76,7 +77,7 @@ pub fn test_ptr(n: BlockNumber) -> BlockPtr { // Set n as the low bits and `reorg_n` as the high bits of the hash. pub fn test_ptr_reorged(n: BlockNumber, reorg_n: u32) -> BlockPtr { - let mut hash = H256::from_low_u64_be(n as u64); + let mut hash = B256::from(U256::from(n as u64)); hash[0..4].copy_from_slice(&reorg_n.to_be_bytes()); BlockPtr { hash: hash.into(), diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index db459972bc3..5505771e79d 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -17,13 +17,13 @@ use anyhow::{anyhow, bail, Context, Result}; use graph::futures03::StreamExt; use graph::itertools::Itertools; use graph::prelude::serde_json::{json, Value}; -use graph::prelude::web3::types::U256; use graph_tests::contract::Contract; use graph_tests::subgraph::Subgraph; use graph_tests::{error, status, CONFIG}; use tokio::process::Child; use tokio::task::JoinError; use tokio::time::sleep; +use web3::types::U256; const SUBGRAPH_LAST_GRAFTING_BLOCK: i32 = 3; diff --git a/tests/tests/runner_tests.rs b/tests/tests/runner_tests.rs index 83d9625ae8a..2d1f9de1ce7 100644 --- a/tests/tests/runner_tests.rs +++ b/tests/tests/runner_tests.rs @@ -14,8 +14,7 @@ use graph::data_source::CausalityRegion; use graph::env::{EnvVars, TEST_WITH_NO_REORG}; use graph::ipfs::test_utils::add_files_to_local_ipfs_node_for_testing; use graph::object; -use graph::prelude::ethabi::ethereum_types::H256; -use graph::prelude::web3::types::Address; +use graph::prelude::alloy::primitives::{Address, B256, U256}; use graph::prelude::{hex, CheapClone, SubgraphAssignmentProvider, SubgraphName, SubgraphStore}; use graph_tests::fixture::ethereum::{ chain, empty_block, generate_empty_blocks_for_range, genesis, push_test_command, push_test_log, @@ -67,7 +66,7 @@ async fn data_source_revert() -> anyhow::Result<()> { let block1 = empty_block(block0.ptr(), test_ptr(1)); let block1_reorged_ptr = BlockPtr { number: 1, - hash: H256::from_low_u64_be(12).into(), + hash: B256::from(U256::from(12)).into(), }; let block1_reorged = empty_block(block0.ptr(), block1_reorged_ptr.clone()); let block2 = empty_block(block1_reorged_ptr, test_ptr(2)); @@ -184,7 +183,7 @@ async fn typename() -> anyhow::Result<()> { let block_1 = empty_block(block_0.ptr(), test_ptr(1)); let block_1_reorged_ptr = BlockPtr { number: 1, - hash: H256::from_low_u64_be(12).into(), + hash: B256::from(U256::from(12)).into(), }; let block_1_reorged = empty_block(block_0.ptr(), block_1_reorged_ptr); let block_2 = empty_block(block_1_reorged.ptr(), test_ptr(2)); @@ -1049,7 +1048,7 @@ async fn retry_create_ds() { let block1 = empty_block(block0.ptr(), test_ptr(1)); let block1_reorged_ptr = BlockPtr { number: 1, - hash: H256::from_low_u64_be(12).into(), + hash: B256::from(U256::from(12)).into(), }; let block1_reorged = empty_block(block0.ptr(), block1_reorged_ptr); let block2 = empty_block(block1_reorged.ptr(), test_ptr(2)); From 05550877fa1d4b5b7f8f3935479bd31e794317e1 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Wed, 10 Dec 2025 15:31:08 +0530 Subject: [PATCH 2/3] Add warning logs on block deserialization failures --- chain/ethereum/src/chain.rs | 35 ++++++++++++++++++++++---- chain/ethereum/src/ethereum_adapter.rs | 14 ++++++++++- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/chain/ethereum/src/chain.rs b/chain/ethereum/src/chain.rs index 3f827220b93..bb444441448 100644 --- a/chain/ethereum/src/chain.rs +++ b/chain/ethereum/src/chain.rs @@ -1036,13 +1036,26 @@ impl TriggersAdapterTrait for TriggersAdapter { offset: BlockNumber, root: Option, ) -> Result, Error> { + let ptr_for_log = ptr.clone(); let block: Option = self .chain_store .cheap_clone() .ancestor_block(ptr, offset, root) .await? - .map(|x| x.0) - .map(json::from_value) + .map(|(json_value, block_ptr)| { + json::from_value(json_value.clone()).map_err(|e| { + warn!( + self.logger, + "Failed to deserialize cached ancestor block {} (offset {} from {}): {}. \ + This may indicate stale cache data from a previous version.", + block_ptr.hash_hex(), + offset, + ptr_for_log.hash_hex(), + e + ); + e + }) + }) .transpose()?; Ok(block.map(|block| { BlockFinality::NonFinal(EthereumBlockWithCalls { @@ -1060,9 +1073,21 @@ impl TriggersAdapterTrait for TriggersAdapter { let chain_store = self.chain_store.cheap_clone(); // First try to get the block from the store if let Ok(blocks) = chain_store.blocks(vec![block.hash.clone()]).await { - if let Some(block) = blocks.first() { - if let Ok(block) = json::from_value::(block.clone()) { - return Ok(block.parent_ptr()); + if let Some(cached_json) = blocks.first() { + match json::from_value::(cached_json.clone()) { + Ok(block) => { + return Ok(block.parent_ptr()); + } + Err(e) => { + warn!( + self.logger, + "Failed to deserialize cached block {}: {}. \ + This may indicate stale cache data from a previous version. \ + Falling back to Firehose.", + block.hash_hex(), + e + ); + } } } } diff --git a/chain/ethereum/src/ethereum_adapter.rs b/chain/ethereum/src/ethereum_adapter.rs index 48bc61b943c..057d9af46cd 100644 --- a/chain/ethereum/src/ethereum_adapter.rs +++ b/chain/ethereum/src/ethereum_adapter.rs @@ -1626,7 +1626,19 @@ impl EthereumAdapterTrait for EthereumAdapter { .map_err(|e| error!(&logger, "Error accessing block cache {}", e)) .unwrap_or_default() .into_iter() - .filter_map(|value| json::from_value(value).ok()) + .filter_map(|value| { + json::from_value(value.clone()) + .map_err(|e| { + warn!( + &logger, + "Failed to deserialize cached block: {}. \ + This may indicate stale cache data from a previous version. \ + Block will be re-fetched from RPC.", + e + ); + }) + .ok() + }) .map(|b| Arc::new(LightEthereumBlock::new(b))) .collect(); From cf5a56640d4e1eb603f82c760c2a8562d521101b Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Wed, 24 Dec 2025 07:11:34 -0800 Subject: [PATCH 3/3] add Firehose/RPC fallback for stale block cache in ancestor_block --- chain/ethereum/src/chain.rs | 166 ++++++++++++++++++++----------- chain/ethereum/src/codec.rs | 30 +++--- graph/src/ipfs/server_address.rs | 2 +- 3 files changed, 124 insertions(+), 74 deletions(-) diff --git a/chain/ethereum/src/chain.rs b/chain/ethereum/src/chain.rs index bb444441448..4624675efb8 100644 --- a/chain/ethereum/src/chain.rs +++ b/chain/ethereum/src/chain.rs @@ -10,7 +10,7 @@ use graph::blockchain::{ use graph::components::network_provider::ChainName; use graph::components::store::{DeploymentCursorTracker, SourceableStore}; use graph::data::subgraph::UnifiedMappingApiVersion; -use graph::firehose::{FirehoseEndpoint, ForkStep}; +use graph::firehose::{FirehoseEndpoint, FirehoseEndpoints, ForkStep}; use graph::futures03::TryStreamExt; use graph::prelude::{ retry, BlockHash, ComponentLoggerConfig, ElasticComponentLoggerConfig, EthereumBlock, @@ -1037,32 +1037,62 @@ impl TriggersAdapterTrait for TriggersAdapter { root: Option, ) -> Result, Error> { let ptr_for_log = ptr.clone(); - let block: Option = self + let cached = self .chain_store .cheap_clone() .ancestor_block(ptr, offset, root) - .await? - .map(|(json_value, block_ptr)| { - json::from_value(json_value.clone()).map_err(|e| { - warn!( - self.logger, - "Failed to deserialize cached ancestor block {} (offset {} from {}): {}. \ - This may indicate stale cache data from a previous version.", - block_ptr.hash_hex(), - offset, - ptr_for_log.hash_hex(), - e - ); - e - }) - }) - .transpose()?; - Ok(block.map(|block| { - BlockFinality::NonFinal(EthereumBlockWithCalls { + .await?; + + let Some((json_value, block_ptr)) = cached else { + return Ok(None); + }; + + match json::from_value::(json_value.clone()) { + Ok(block) => Ok(Some(BlockFinality::NonFinal(EthereumBlockWithCalls { ethereum_block: block, calls: None, - }) - })) + }))), + Err(e) => { + warn!( + self.logger, + "Failed to deserialize cached ancestor block {} (offset {} from {}): {}. \ + This may indicate stale cache data from a previous version. \ + Falling back to Firehose/RPC.", + block_ptr.hash_hex(), + offset, + ptr_for_log.hash_hex(), + e + ); + + match self.chain_client.as_ref() { + ChainClient::Firehose(endpoints) => { + let block = self + .fetch_block_with_firehose(endpoints, &block_ptr) + .await?; + let ethereum_block: EthereumBlockWithCalls = (&block).try_into()?; + Ok(Some(BlockFinality::NonFinal(ethereum_block))) + } + ChainClient::Rpc(adapters) => { + match self + .fetch_light_block_with_rpc(adapters, &block_ptr) + .await? + { + Some(light_block) => { + let ethereum_block = EthereumBlock { + block: light_block, + transaction_receipts: vec![], + }; + Ok(Some(BlockFinality::NonFinal(EthereumBlockWithCalls { + ethereum_block, + calls: None, + }))) + } + None => Ok(None), + } + } + } + } + } } async fn parent_ptr(&self, block: &BlockPtr) -> Result, Error> { @@ -1093,52 +1123,70 @@ impl TriggersAdapterTrait for TriggersAdapter { } // If not in store, fetch from Firehose - let endpoint = endpoints.endpoint().await?; - let logger = self.logger.clone(); - let retry_log_message = - format!("get_block_by_ptr for block {} with firehose", block); - let block = block.clone(); - - retry(retry_log_message, &logger) - .limit(ENV_VARS.request_retries) - .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) - .run(move || { - let endpoint = endpoint.cheap_clone(); - let logger = logger.cheap_clone(); - let block = block.clone(); - async move { - endpoint - .get_block_by_ptr::(&block, &logger) - .await - .context(format!( - "Failed to fetch block by ptr {} from firehose", - block - )) - } - }) + self.fetch_block_with_firehose(endpoints, block) .await? .parent_ptr() } - ChainClient::Rpc(adapters) => { - let blocks = adapters - .cheapest_with(&self.capabilities) - .await? - .load_blocks( - self.logger.cheap_clone(), - self.chain_store.cheap_clone(), - HashSet::from_iter(Some(block.hash.as_b256())), - ) - .await?; - assert_eq!(blocks.len(), 1); - - blocks[0].parent_ptr() - } + ChainClient::Rpc(adapters) => self + .fetch_light_block_with_rpc(adapters, block) + .await? + .expect("block must exist for parent_ptr") + .parent_ptr(), }; Ok(block) } } +impl TriggersAdapter { + async fn fetch_block_with_firehose( + &self, + endpoints: &FirehoseEndpoints, + block_ptr: &BlockPtr, + ) -> Result { + let endpoint = endpoints.endpoint().await?; + let logger = self.logger.clone(); + let retry_log_message = format!("fetch_block_with_firehose {}", block_ptr); + let block_ptr = block_ptr.clone(); + + let block = retry(retry_log_message, &logger) + .limit(ENV_VARS.request_retries) + .timeout_secs(ENV_VARS.json_rpc_timeout.as_secs()) + .run(move || { + let endpoint = endpoint.cheap_clone(); + let logger = logger.cheap_clone(); + let block_ptr = block_ptr.clone(); + async move { + endpoint + .get_block_by_ptr::(&block_ptr, &logger) + .await + .context(format!("Failed to fetch block {} from firehose", block_ptr)) + } + }) + .await?; + + Ok(block) + } + + async fn fetch_light_block_with_rpc( + &self, + adapters: &EthereumNetworkAdapters, + block_ptr: &BlockPtr, + ) -> Result>, Error> { + let blocks = adapters + .cheapest_with(&self.capabilities) + .await? + .load_blocks( + self.logger.cheap_clone(), + self.chain_store.cheap_clone(), + HashSet::from_iter(Some(block_ptr.hash.as_b256())), + ) + .await?; + + Ok(blocks.into_iter().next()) + } +} + pub struct FirehoseMapper { adapter: Arc>, filter: Arc, diff --git a/chain/ethereum/src/codec.rs b/chain/ethereum/src/codec.rs index 2a402d4f5b2..f7d1af103bc 100644 --- a/chain/ethereum/src/codec.rs +++ b/chain/ethereum/src/codec.rs @@ -268,19 +268,21 @@ impl<'a> TryInto> for TransactionTraceAt<'a> { .trace .access_list .iter() - .map(|access_tuple| { - let address = Address::from_slice(&access_tuple.address); + .map(|access_tuple| -> Result<_, Error> { + let address = access_tuple + .address + .try_decode_proto("access tuple address")?; let storage_keys = access_tuple .storage_keys .iter() - .map(|key| B256::from_slice(key)) - .collect(); - AccessListItem { + .map(|key| key.try_decode_proto("storage key")) + .collect::, _>>()?; + Ok(AccessListItem { address, storage_keys, - } + }) }) - .collect::>() + .collect::, Error>>()? .into(); // Extract actual signature components from trace @@ -359,8 +361,8 @@ impl<'a> TryInto> for TransactionTraceAt<'a> { .trace .blob_hashes .iter() - .map(|hash| B256::from_slice(hash)) - .collect(); + .map(|hash| hash.try_decode_proto("blob hash")) + .collect::, _>>()?; let max_fee_per_blob_gas_u128 = self.trace.blob_gas_fee_cap.as_ref().map_or(0u128, |x| { @@ -401,10 +403,10 @@ impl<'a> TryInto> for TransactionTraceAt<'a> { .trace .set_code_authorizations .iter() - .map(|auth| { + .map(|auth| -> Result<_, Error> { let inner = alloy::eips::eip7702::Authorization { chain_id: U256::from_be_slice(&auth.chain_id), - address: Address::from_slice(&auth.address), + address: auth.address.try_decode_proto("authorization address")?, nonce: auth.nonce, }; @@ -412,11 +414,11 @@ impl<'a> TryInto> for TransactionTraceAt<'a> { let s = U256::from_be_slice(&auth.s); let y_parity = auth.v as u8; - alloy::eips::eip7702::SignedAuthorization::new_unchecked( + Ok(alloy::eips::eip7702::SignedAuthorization::new_unchecked( inner, y_parity, r, s, - ) + )) }) - .collect(); + .collect::, Error>>()?; let tx = TxEip7702 { // Firehose protobuf doesn't provide chain_id for transactions. diff --git a/graph/src/ipfs/server_address.rs b/graph/src/ipfs/server_address.rs index c7c8bc109f6..556997406ef 100644 --- a/graph/src/ipfs/server_address.rs +++ b/graph/src/ipfs/server_address.rs @@ -119,7 +119,7 @@ mod tests { assert_eq!( err.to_string(), - "'https://' is not a valid IPFS server address: invalid format", + "'https://' is not a valid IPFS server address: empty string", ); }