diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..dc9cc0d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,101 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +env: + CARGO_TERM_COLOR: always + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + test: + name: test ${{ matrix.rust }} ${{ matrix.flags }} + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + rust: ["stable", "beta", "nightly", "1.81"] # MSRV + flags: ["--no-default-features", "", "--all-features"] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: build + run: cargo build --workspace ${{ matrix.flags }} + - name: test + if: ${{ matrix.rust != '1.81' }} # MSRV + run: cargo test --workspace ${{ matrix.flags }} + + check-no-std: + name: check no_std ${{ matrix.features }} + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + targets: riscv32imac-unknown-none-elf + - run: cargo check --target riscv32imac-unknown-none-elf --no-default-features + + feature-checks: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: taiki-e/install-action@cargo-hack + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: cargo hack + run: cargo hack check --feature-powerset --depth 2 + + clippy: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@clippy + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - run: cargo clippy --workspace --all-targets --all-features + env: + RUSTFLAGS: -Dwarnings + + docs: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: Build documentation + run: cargo doc --workspace --all-features --no-deps --document-private-items + env: + RUSTDOCFLAGS: --cfg docsrs -D warnings -Zunstable-options --show-type-layout --generate-link-to-definition + + fmt: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + - run: cargo fmt --all --check + + deny: + uses: ithacaxyz/ci/.github/workflows/deny.yml@main diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..d1eb3fa --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1587 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "alloy-chains" +version = "0.1.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e39f295f876b61a1222d937e1dd31f965e4a1acc3bba98e448dd7e84b1a4566" +dependencies = [ + "alloy-primitives", + "arbitrary", + "num_enum", + "proptest", + "serde", + "strum", +] + +[[package]] +name = "alloy-eip2124" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675264c957689f0fd75f5993a73123c2cc3b5c235a38f5b9037fe6c826bfb2c0" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "crc", + "rand", + "serde", + "thiserror", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "788bb18e8f61d5d9340b52143f27771daf7e1dccbaf2741621d2493f9debf52e" +dependencies = [ + "alloy-rlp", + "arbitrary", + "bytes", + "cfg-if", + "const-hex", + "derive_arbitrary", + "derive_more", + "foldhash", + "hashbrown", + "indexmap", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "proptest-derive", + "rand", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f542548a609dca89fcd72b3b9f355928cf844d4363c5eed9c5273a3dd225e097" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[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", + "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", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "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-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "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", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[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 = "bitflags" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0cf6e91fde44c773c6ee7ec6bba798504641a8bc2eb7e37a04ffbf4dfaa55a" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +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 = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[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", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +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", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", + "serde", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "arbitrary", + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +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 = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +dependencies = [ + "critical-section", + "portable-atomic", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pest" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "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.96", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "serde", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reth-ethereum-forks" +version = "1.1.5" +dependencies = [ + "alloy-chains", + "alloy-eip2124", + "alloy-primitives", + "arbitrary", + "auto_impl", + "dyn-clone", + "once_cell", + "rustc-hash", + "serde", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +dependencies = [ + "alloy-rlp", + "arbitrary", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "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-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +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 = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.24", +] + +[[package]] +name = "rustix" +version = "0.38.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[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", + "subtle", + "zeroize", +] + +[[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.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "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", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[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", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.96", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +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.96", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a1ae3d9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "reth-ethereum-forks" +version = "1.1.5" +edition = "2021" +rust-version = "1.82" +license = "MIT OR Apache-2.0" +homepage = "https://paradigmxyz.github.io/reth" +repository = "https://github.com/paradigmxyz/reth" +description = "Ethereum fork types used in reth." + +[dependencies] +# ethereum +alloy-chains = { version = "0.1.32", default-features = false } +alloy-eip2124 = { version = "0.1.0", default-features = false } +alloy-primitives = { version = "0.8.15", default-features = false, features = ["serde", "rlp"] } + +# misc +serde = { version = "1.0", features = ["derive"], optional = true } +dyn-clone = "1.0.17" +once_cell = { version = "1.19", default-features = false, features = ["critical-section"] } +rustc-hash = { version = "2.0", optional = true, default-features = false } + +# arbitrary utils +arbitrary = { version = "1.3", features = ["derive"], optional = true } +auto_impl = "1.0" + +[dev-dependencies] +arbitrary = { version = "1.3", features = ["derive"] } + +[features] +default = ["std", "serde", "rustc-hash"] +arbitrary = [ + "dep:arbitrary", + "alloy-chains/arbitrary", + "alloy-primitives/arbitrary", + "alloy-eip2124/arbitrary" +] +serde = [ + "dep:serde", + "alloy-chains/serde", + "alloy-primitives/serde", + "alloy-eip2124/serde" +] +std = [ + "alloy-chains/std", + "alloy-primitives/std", + "rustc-hash/std", + "serde?/std", + "alloy-eip2124/std", + "once_cell/std" +] +rustc-hash = ["dep:rustc-hash"] \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..f443e84 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Alloy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/SECURITY.MD b/SECURITY.MD new file mode 100644 index 0000000..5260d52 --- /dev/null +++ b/SECURITY.MD @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +Contact georgios at paradigm.xyz. diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..8c0bc00 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +msrv = "1.81" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3063df7 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,12 @@ +reorder_imports = true +use_field_init_shorthand = true +use_small_heuristics = "Max" + +# Nightly +max_width = 100 +comment_width = 100 +imports_granularity = "Crate" +wrap_comments = true +format_code_in_doc_comments = true +doc_comment_code_block_width = 100 +format_macro_matchers = true diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..e3da7eb --- /dev/null +++ b/src/display.rs @@ -0,0 +1,169 @@ +use crate::{hardforks::Hardforks, ForkCondition}; +use alloc::{ + format, + string::{String, ToString}, + vec::Vec, +}; + +/// A container to pretty-print a hardfork. +/// +/// The fork is formatted depending on its fork condition: +/// +/// - Block and timestamp based forks are formatted in the same manner (`{name} <({eip})> +/// @{condition}`) +/// - TTD based forks are formatted separately as `{name} <({eip})> @{ttd} (network is known +/// to be merged)` +/// +/// An optional EIP can be attached to the fork to display as well. This should generally be in the +/// form of just `EIP-x`, e.g. `EIP-1559`. +#[derive(Debug)] +struct DisplayFork { + /// The name of the hardfork (e.g. Frontier) + name: String, + /// The fork condition + activated_at: ForkCondition, + /// An optional EIP (e.g. `EIP-1559`). + eip: Option, +} + +impl core::fmt::Display for DisplayFork { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let name_with_eip = if let Some(eip) = &self.eip { + format!("{} ({})", self.name, eip) + } else { + self.name.clone() + }; + + match self.activated_at { + ForkCondition::Block(at) | ForkCondition::Timestamp(at) => { + write!(f, "{name_with_eip:32} @{at}")?; + } + ForkCondition::TTD { total_difficulty, .. } => { + // All networks that have merged are finalized. + write!( + f, + "{:32} @{} (network is known to be merged)", + name_with_eip, total_difficulty, + )?; + } + ForkCondition::Never => unreachable!(), + } + + Ok(()) + } +} + +// # Examples +// +// ``` +// # use reth_chainspec::MAINNET; +// println!("{}", MAINNET.display_hardforks()); +// ``` +// +/// A container for pretty-printing a list of hardforks. +/// +/// An example of the output: +/// +/// ```text +/// Pre-merge hard forks (block based): +// - Frontier @0 +// - Homestead @1150000 +// - Dao @1920000 +// - Tangerine @2463000 +// - SpuriousDragon @2675000 +// - Byzantium @4370000 +// - Constantinople @7280000 +// - Petersburg @7280000 +// - Istanbul @9069000 +// - MuirGlacier @9200000 +// - Berlin @12244000 +// - London @12965000 +// - ArrowGlacier @13773000 +// - GrayGlacier @15050000 +// Merge hard forks: +// - Paris @58750000000000000000000 (network is known to be merged) +// Post-merge hard forks (timestamp based): +// - Shanghai @1681338455 +// - Cancun @1710338135" +/// ``` +#[derive(Debug)] +pub struct DisplayHardforks { + /// A list of pre-merge (block based) hardforks + pre_merge: Vec, + /// A list of merge (TTD based) hardforks + with_merge: Vec, + /// A list of post-merge (timestamp based) hardforks + post_merge: Vec, +} + +impl core::fmt::Display for DisplayHardforks { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + fn format( + header: &str, + forks: &[DisplayFork], + next_is_empty: bool, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + writeln!(f, "{header}:")?; + let mut iter = forks.iter().peekable(); + while let Some(fork) = iter.next() { + write!(f, "- {fork}")?; + if !next_is_empty || iter.peek().is_some() { + writeln!(f)?; + } + } + Ok(()) + } + + format( + "Pre-merge hard forks (block based)", + &self.pre_merge, + self.with_merge.is_empty(), + f, + )?; + + if !self.with_merge.is_empty() { + format("Merge hard forks", &self.with_merge, self.post_merge.is_empty(), f)?; + } + + if !self.post_merge.is_empty() { + format("Post-merge hard forks (timestamp based)", &self.post_merge, true, f)?; + } + + Ok(()) + } +} + +impl DisplayHardforks { + /// Creates a new [`DisplayHardforks`] from an iterator of hardforks. + pub fn new(hardforks: &H) -> Self { + let mut pre_merge = Vec::new(); + let mut with_merge = Vec::new(); + let mut post_merge = Vec::new(); + + for (fork, condition) in hardforks.forks_iter() { + let mut display_fork = + DisplayFork { name: fork.name().to_string(), activated_at: condition, eip: None }; + + match condition { + ForkCondition::Block(_) => { + pre_merge.push(display_fork); + } + ForkCondition::TTD { activation_block_number, total_difficulty, fork_block } => { + display_fork.activated_at = ForkCondition::TTD { + activation_block_number, + fork_block, + total_difficulty, + }; + with_merge.push(display_fork); + } + ForkCondition::Timestamp(_) => { + post_merge.push(display_fork); + } + ForkCondition::Never => {} + } + } + + Self { pre_merge, with_merge, post_merge } + } +} \ No newline at end of file diff --git a/src/forkcondition.rs b/src/forkcondition.rs new file mode 100644 index 0000000..5a30fd9 --- /dev/null +++ b/src/forkcondition.rs @@ -0,0 +1,319 @@ +use crate::Head; +use alloy_primitives::{BlockNumber, U256}; + +/// The condition at which a fork is activated. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum ForkCondition { + /// The fork is activated after a certain block. + Block(BlockNumber), + /// The fork is activated after a total difficulty has been reached. + TTD { + /// The activation block number for the merge. + /// + /// This should represent the first post-merge block for the given network. Sepolia and + /// mainnet are the only networks that have merged, and they have both finalized + /// post-merge, so total difficulty is effectively deprecated. + activation_block_number: BlockNumber, + /// The block number at which TTD is reached, if it is known. + /// + /// This should **NOT** be set unless you want this block advertised as [EIP-2124][eip2124] + /// `FORK_NEXT`. This is currently only the case for Sepolia and Holesky. + /// + /// [eip2124]: https://eips.ethereum.org/EIPS/eip-2124 + fork_block: Option, + /// The total difficulty after which the fork is activated. + total_difficulty: U256, + }, + /// The fork is activated after a specific timestamp. + Timestamp(u64), + /// The fork is never activated + #[default] + Never, +} + +impl ForkCondition { + /// Returns true if the fork condition is timestamp based. + pub const fn is_timestamp(&self) -> bool { + matches!(self, Self::Timestamp(_)) + } + + /// Checks whether the fork condition is satisfied at the given block. + /// + /// This will return true if the block number is equal or greater than the activation block of: + /// - [`ForkCondition::Block`] + /// - [`ForkCondition::TTD`] + /// + /// For timestamp conditions, this will always return false. + pub const fn active_at_block(&self, current_block: BlockNumber) -> bool { + matches!(self, Self::Block(block) + | Self::TTD { fork_block: Some(block), .. } if current_block >= *block) + } + + /// Checks if the given block is the first block that satisfies the fork condition. + /// + /// This will return false for any condition that is not block based. + pub const fn transitions_at_block(&self, current_block: BlockNumber) -> bool { + matches!(self, Self::Block(block) if current_block == *block) + } + + /// Checks whether the fork condition is satisfied at the given total difficulty and difficulty + /// of a current block. + /// + /// The fork is considered active if the _previous_ total difficulty is above the threshold. + /// To achieve that, we subtract the passed `difficulty` from the current block's total + /// difficulty, and check if it's above the Fork Condition's total difficulty (here: + /// `58_750_000_000_000_000_000_000`) + /// + /// This will return false for any condition that is not TTD-based. + pub fn active_at_ttd(&self, ttd: U256, difficulty: U256) -> bool { + matches!(self, Self::TTD { total_difficulty, .. } + if ttd.saturating_sub(difficulty) >= *total_difficulty) + } + + /// Checks whether the fork condition is satisfied at the given timestamp. + /// + /// This will return false for any condition that is not timestamp-based. + pub const fn active_at_timestamp(&self, timestamp: u64) -> bool { + matches!(self, Self::Timestamp(time) if timestamp >= *time) + } + + /// Checks if the given block is the first block that satisfies the fork condition. + /// + /// This will return false for any condition that is not timestamp based. + pub const fn transitions_at_timestamp(&self, timestamp: u64, parent_timestamp: u64) -> bool { + matches!(self, Self::Timestamp(time) if timestamp >= *time && parent_timestamp < *time) + } + + /// Checks whether the fork condition is satisfied at the given timestamp or number. + pub const fn active_at_timestamp_or_number(&self, timestamp: u64, block_number: u64) -> bool { + self.active_at_timestamp(timestamp) || self.active_at_block(block_number) + } + + /// Checks whether the fork condition is satisfied at the given head block. + /// + /// This will return true if: + /// + /// - The condition is satisfied by the block number; + /// - The condition is satisfied by the timestamp; + /// - or the condition is satisfied by the total difficulty + pub fn active_at_head(&self, head: &Head) -> bool { + self.active_at_timestamp_or_number(head.timestamp, head.number) || + self.active_at_ttd(head.total_difficulty, head.difficulty) + } + + /// Get the total terminal difficulty for this fork condition. + /// + /// Returns `None` for fork conditions that are not TTD based. + pub const fn ttd(&self) -> Option { + match self { + Self::TTD { total_difficulty, .. } => Some(*total_difficulty), + _ => None, + } + } + + /// Returns the timestamp of the fork condition, if it is timestamp based. + pub const fn as_timestamp(&self) -> Option { + match self { + Self::Timestamp(timestamp) => Some(*timestamp), + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::U256; + + #[test] + fn test_active_at_block() { + // Test if the condition is active at the current block number + let fork_condition = ForkCondition::Block(10); + assert!(fork_condition.active_at_block(10), "The condition should be active at block 10"); + + // Test if the condition is not active at a lower block number + assert!( + !fork_condition.active_at_block(9), + "The condition should not be active at block 9" + ); + + // Test if TTD-based condition with known block activates + let fork_condition = ForkCondition::TTD { + activation_block_number: 10, + fork_block: Some(10), + total_difficulty: U256::from(1000), + }; + assert!( + fork_condition.active_at_block(10), + "The TTD condition should be active at block 10" + ); + + // Test if TTD-based condition with unknown block does not activate + let fork_condition = ForkCondition::TTD { + activation_block_number: 10, + fork_block: None, + total_difficulty: U256::from(1000), + }; + assert!( + !fork_condition.active_at_block(10), + "The TTD condition should not be active at block 10 with an unknown block number" + ); + } + + #[test] + fn test_transitions_at_block() { + // Test if the condition transitions at the correct block number + let fork_condition = ForkCondition::Block(10); + assert!( + fork_condition.transitions_at_block(10), + "The condition should transition at block 10" + ); + + // Test if the condition does not transition at a different block number + assert!( + !fork_condition.transitions_at_block(9), + "The condition should not transition at a different block number" + ); + assert!( + !fork_condition.transitions_at_block(11), + "The condition should not transition at a different block number" + ); + } + + #[test] + fn test_active_at_ttd() { + // Test if the condition activates at the correct total difficulty + let fork_condition = ForkCondition::TTD { + activation_block_number: 10, + fork_block: Some(10), + total_difficulty: U256::from(1000), + }; + assert!( + fork_condition.active_at_ttd(U256::from(1000000), U256::from(100)), + "The TTD condition should be active when the total difficulty matches" + ); + + // Test if the condition does not activate when the total difficulty is lower + assert!( + !fork_condition.active_at_ttd(U256::from(900), U256::from(100)), + "The TTD condition should not be active when the total difficulty is lower" + ); + + // Test with a saturated subtraction + assert!( + !fork_condition.active_at_ttd(U256::from(900), U256::from(1000)), + "The TTD condition should not be active when the subtraction saturates" + ); + } + + #[test] + fn test_active_at_timestamp() { + // Test if the condition activates at the correct timestamp + let fork_condition = ForkCondition::Timestamp(12345); + assert!( + fork_condition.active_at_timestamp(12345), + "The condition should be active at timestamp 12345" + ); + + // Test if the condition does not activate at an earlier timestamp + assert!( + !fork_condition.active_at_timestamp(12344), + "The condition should not be active at an earlier timestamp" + ); + } + + #[test] + fn test_transitions_at_timestamp() { + // Test if the condition transitions at the correct timestamp + let fork_condition = ForkCondition::Timestamp(12345); + assert!( + fork_condition.transitions_at_timestamp(12345, 12344), + "The condition should transition at timestamp 12345" + ); + + // Test if the condition does not transition if the parent timestamp is already the same + assert!( + !fork_condition.transitions_at_timestamp(12345, 12345), + "The condition should not transition if the parent timestamp is already 12345" + ); + // Test with earlier timestamp + assert!( + !fork_condition.transitions_at_timestamp(123, 122), + "The condition should not transition if the parent timestamp is earlier" + ); + } + + #[test] + fn test_active_at_head() { + let head = Head { + hash: Default::default(), + number: 10, + timestamp: 12345, + total_difficulty: U256::from(1000), + difficulty: U256::from(100), + }; + + // Test if the condition activates based on block number + let fork_condition = ForkCondition::Block(10); + assert!( + fork_condition.active_at_head(&head), + "The condition should be active at the given head block number" + ); + let fork_condition = ForkCondition::Block(11); + assert!( + !fork_condition.active_at_head(&head), + "The condition should not be active at the given head block number" + ); + + // Test if the condition activates based on timestamp + let fork_condition = ForkCondition::Timestamp(12345); + assert!( + fork_condition.active_at_head(&head), + "The condition should be active at the given head timestamp" + ); + let fork_condition = ForkCondition::Timestamp(12346); + assert!( + !fork_condition.active_at_head(&head), + "The condition should not be active at the given head timestamp" + ); + + // Test if the condition activates based on total difficulty and block number + let fork_condition = ForkCondition::TTD { + activation_block_number: 10, + fork_block: Some(9), + total_difficulty: U256::from(900), + }; + assert!( + fork_condition.active_at_head(&head), + "The condition should be active at the given head total difficulty" + ); + let fork_condition = ForkCondition::TTD { + activation_block_number: 10, + fork_block: None, + total_difficulty: U256::from(900), + }; + assert!( + fork_condition.active_at_head(&head), + "The condition should be active at the given head total difficulty as the block number is unknown" + ); + let fork_condition = ForkCondition::TTD { + activation_block_number: 10, + fork_block: Some(11), + total_difficulty: U256::from(900), + }; + assert!( + fork_condition.active_at_head(&head), + "The condition should be active as the total difficulty is higher" + ); + let fork_condition = ForkCondition::TTD { + activation_block_number: 10, + fork_block: Some(10), + total_difficulty: U256::from(9000), + }; + assert!( + fork_condition.active_at_head(&head), + "The condition should be active as the total difficulty is higher than head" + ); + } +} \ No newline at end of file diff --git a/src/hardfork/dev.rs b/src/hardfork/dev.rs new file mode 100644 index 0000000..057d6d3 --- /dev/null +++ b/src/hardfork/dev.rs @@ -0,0 +1,38 @@ +use alloc::vec; + +use alloy_primitives::U256; + +use once_cell as _; +#[cfg(not(feature = "std"))] +use once_cell::sync::Lazy as LazyLock; +#[cfg(feature = "std")] +use std::sync::LazyLock; + +use crate::{ChainHardforks, EthereumHardfork, ForkCondition, Hardfork}; + +/// Dev hardforks +pub static DEV_HARDFORKS: LazyLock = LazyLock::new(|| { + ChainHardforks::new(vec![ + (EthereumHardfork::Frontier.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Dao.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::London.boxed(), ForkCondition::Block(0)), + ( + EthereumHardfork::Paris.boxed(), + ForkCondition::TTD { + activation_block_number: 0, + fork_block: None, + total_difficulty: U256::ZERO, + }, + ), + (EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(0)), + (EthereumHardfork::Cancun.boxed(), ForkCondition::Timestamp(0)), + ]) +}); \ No newline at end of file diff --git a/src/hardfork/ethereum.rs b/src/hardfork/ethereum.rs new file mode 100644 index 0000000..a2454d1 --- /dev/null +++ b/src/hardfork/ethereum.rs @@ -0,0 +1,430 @@ +use crate::{hardfork, ChainHardforks, ForkCondition, Hardfork}; +use alloc::{boxed::Box, format, string::String}; +use alloy_chains::Chain; +use alloy_primitives::{uint, U256}; +use core::{ + fmt, + fmt::{Display, Formatter}, + str::FromStr, +}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +hardfork!( + /// The name of an Ethereum hardfork. + EthereumHardfork { + /// Frontier: . + Frontier, + /// Homestead: . + Homestead, + /// The DAO fork: . + Dao, + /// Tangerine: . + Tangerine, + /// Spurious Dragon: . + SpuriousDragon, + /// Byzantium: . + Byzantium, + /// Constantinople: . + Constantinople, + /// Petersburg: . + Petersburg, + /// Istanbul: . + Istanbul, + /// Muir Glacier: . + MuirGlacier, + /// Berlin: . + Berlin, + /// London: . + London, + /// Arrow Glacier: . + ArrowGlacier, + /// Gray Glacier: . + GrayGlacier, + /// Paris: . + Paris, + /// Shanghai: . + Shanghai, + /// Cancun. + Cancun, + /// Prague: + Prague, + /// Osaka: + Osaka, + } +); + +impl EthereumHardfork { + /// Retrieves the activation block for the specified hardfork on the given chain. + pub fn activation_block(&self, chain: Chain) -> Option { + if chain == Chain::mainnet() { + return self.mainnet_activation_block() + } + if chain == Chain::sepolia() { + return self.sepolia_activation_block() + } + if chain == Chain::holesky() { + return self.holesky_activation_block() + } + + None + } + + /// Retrieves the activation block for the specified hardfork on the Ethereum mainnet. + pub const fn mainnet_activation_block(&self) -> Option { + match self { + Self::Frontier => Some(0), + Self::Homestead => Some(1150000), + Self::Dao => Some(1920000), + Self::Tangerine => Some(2463000), + Self::SpuriousDragon => Some(2675000), + Self::Byzantium => Some(4370000), + Self::Constantinople | Self::Petersburg => Some(7280000), + Self::Istanbul => Some(9069000), + Self::MuirGlacier => Some(9200000), + Self::Berlin => Some(12244000), + Self::London => Some(12965000), + Self::ArrowGlacier => Some(13773000), + Self::GrayGlacier => Some(15050000), + Self::Paris => Some(15537394), + Self::Shanghai => Some(17034870), + Self::Cancun => Some(19426587), + _ => None, + } + } + + /// Retrieves the activation block for the specified hardfork on the Sepolia testnet. + pub const fn sepolia_activation_block(&self) -> Option { + match self { + Self::Paris => Some(1450409), + Self::Shanghai => Some(2990908), + Self::Cancun => Some(5187023), + Self::Frontier | + Self::Homestead | + Self::Dao | + Self::Tangerine | + Self::SpuriousDragon | + Self::Byzantium | + Self::Constantinople | + Self::Petersburg | + Self::Istanbul | + Self::MuirGlacier | + Self::Berlin | + Self::London | + Self::ArrowGlacier | + Self::GrayGlacier => Some(0), + _ => None, + } + } + + /// Retrieves the activation block for the specified hardfork on the holesky testnet. + const fn holesky_activation_block(&self) -> Option { + match self { + Self::Dao | + Self::Tangerine | + Self::SpuriousDragon | + Self::Byzantium | + Self::Constantinople | + Self::Petersburg | + Self::Istanbul | + Self::MuirGlacier | + Self::Berlin | + Self::London | + Self::ArrowGlacier | + Self::GrayGlacier | + Self::Paris => Some(0), + Self::Shanghai => Some(6698), + Self::Cancun => Some(894733), + _ => None, + } + } + + /// Retrieves the activation block for the specified hardfork on the Arbitrum Sepolia testnet. + pub const fn arbitrum_sepolia_activation_block(&self) -> Option { + match self { + Self::Frontier | + Self::Homestead | + Self::Dao | + Self::Tangerine | + Self::SpuriousDragon | + Self::Byzantium | + Self::Constantinople | + Self::Petersburg | + Self::Istanbul | + Self::MuirGlacier | + Self::Berlin | + Self::London | + Self::ArrowGlacier | + Self::GrayGlacier | + Self::Paris => Some(0), + Self::Shanghai => Some(10653737), + // Hardfork::ArbOS11 => Some(10653737), + Self::Cancun => Some(18683405), + // Hardfork::ArbOS20Atlas => Some(18683405), + _ => None, + } + } + + /// Retrieves the activation block for the specified hardfork on the Arbitrum One mainnet. + pub const fn arbitrum_activation_block(&self) -> Option { + match self { + Self::Frontier | + Self::Homestead | + Self::Dao | + Self::Tangerine | + Self::SpuriousDragon | + Self::Byzantium | + Self::Constantinople | + Self::Petersburg | + Self::Istanbul | + Self::MuirGlacier | + Self::Berlin | + Self::London | + Self::ArrowGlacier | + Self::GrayGlacier | + Self::Paris => Some(0), + Self::Shanghai => Some(184097479), + // Hardfork::ArbOS11 => Some(184097479), + Self::Cancun => Some(190301729), + // Hardfork::ArbOS20Atlas => Some(190301729), + _ => None, + } + } + + /// Retrieves the activation timestamp for the specified hardfork on the given chain. + pub fn activation_timestamp(&self, chain: Chain) -> Option { + if chain == Chain::mainnet() { + return self.mainnet_activation_timestamp() + } + if chain == Chain::sepolia() { + return self.sepolia_activation_timestamp() + } + if chain == Chain::holesky() { + return self.holesky_activation_timestamp() + } + + None + } + + /// Retrieves the activation timestamp for the specified hardfork on the Ethereum mainnet. + pub const fn mainnet_activation_timestamp(&self) -> Option { + match self { + Self::Frontier => Some(1438226773), + Self::Homestead => Some(1457938193), + Self::Dao => Some(1468977640), + Self::Tangerine => Some(1476753571), + Self::SpuriousDragon => Some(1479788144), + Self::Byzantium => Some(1508131331), + Self::Constantinople | Self::Petersburg => Some(1551340324), + Self::Istanbul => Some(1575807909), + Self::MuirGlacier => Some(1577953849), + Self::Berlin => Some(1618481223), + Self::London => Some(1628166822), + Self::ArrowGlacier => Some(1639036523), + Self::GrayGlacier => Some(1656586444), + Self::Paris => Some(1663224162), + Self::Shanghai => Some(1681338455), + Self::Cancun => Some(1710338135), + + // upcoming hardforks + _ => None, + } + } + + /// Retrieves the activation timestamp for the specified hardfork on the Sepolia testnet. + pub const fn sepolia_activation_timestamp(&self) -> Option { + match self { + Self::Frontier | + Self::Homestead | + Self::Dao | + Self::Tangerine | + Self::SpuriousDragon | + Self::Byzantium | + Self::Constantinople | + Self::Petersburg | + Self::Istanbul | + Self::MuirGlacier | + Self::Berlin | + Self::London | + Self::ArrowGlacier | + Self::GrayGlacier | + Self::Paris => Some(1633267481), + Self::Shanghai => Some(1677557088), + Self::Cancun => Some(1706655072), + _ => None, + } + } + + /// Retrieves the activation timestamp for the specified hardfork on the Holesky testnet. + pub const fn holesky_activation_timestamp(&self) -> Option { + match self { + Self::Shanghai => Some(1696000704), + Self::Cancun => Some(1707305664), + Self::Frontier | + Self::Homestead | + Self::Dao | + Self::Tangerine | + Self::SpuriousDragon | + Self::Byzantium | + Self::Constantinople | + Self::Petersburg | + Self::Istanbul | + Self::MuirGlacier | + Self::Berlin | + Self::London | + Self::ArrowGlacier | + Self::GrayGlacier | + Self::Paris => Some(1695902100), + _ => None, + } + } + + /// Retrieves the activation timestamp for the specified hardfork on the Arbitrum Sepolia + /// testnet. + pub const fn arbitrum_sepolia_activation_timestamp(&self) -> Option { + match self { + Self::Frontier | + Self::Homestead | + Self::Dao | + Self::Tangerine | + Self::SpuriousDragon | + Self::Byzantium | + Self::Constantinople | + Self::Petersburg | + Self::Istanbul | + Self::MuirGlacier | + Self::Berlin | + Self::London | + Self::ArrowGlacier | + Self::GrayGlacier | + Self::Paris => Some(1692726996), + Self::Shanghai => Some(1706634000), + // Hardfork::ArbOS11 => Some(1706634000), + Self::Cancun => Some(1709229600), + // Hardfork::ArbOS20Atlas => Some(1709229600), + _ => None, + } + } + + /// Retrieves the activation timestamp for the specified hardfork on the Arbitrum One mainnet. + pub const fn arbitrum_activation_timestamp(&self) -> Option { + match self { + Self::Frontier | + Self::Homestead | + Self::Dao | + Self::Tangerine | + Self::SpuriousDragon | + Self::Byzantium | + Self::Constantinople | + Self::Petersburg | + Self::Istanbul | + Self::MuirGlacier | + Self::Berlin | + Self::London | + Self::ArrowGlacier | + Self::GrayGlacier | + Self::Paris => Some(1622240000), + Self::Shanghai => Some(1708804873), + // Hardfork::ArbOS11 => Some(1708804873), + Self::Cancun => Some(1710424089), + // Hardfork::ArbOS20Atlas => Some(1710424089), + _ => None, + } + } + + /// Ethereum mainnet list of hardforks. + pub const fn mainnet() -> [(Self, ForkCondition); 17] { + [ + (Self::Frontier, ForkCondition::Block(0)), + (Self::Homestead, ForkCondition::Block(1150000)), + (Self::Dao, ForkCondition::Block(1920000)), + (Self::Tangerine, ForkCondition::Block(2463000)), + (Self::SpuriousDragon, ForkCondition::Block(2675000)), + (Self::Byzantium, ForkCondition::Block(4370000)), + (Self::Constantinople, ForkCondition::Block(7280000)), + (Self::Petersburg, ForkCondition::Block(7280000)), + (Self::Istanbul, ForkCondition::Block(9069000)), + (Self::MuirGlacier, ForkCondition::Block(9200000)), + (Self::Berlin, ForkCondition::Block(12244000)), + (Self::London, ForkCondition::Block(12965000)), + (Self::ArrowGlacier, ForkCondition::Block(13773000)), + (Self::GrayGlacier, ForkCondition::Block(15050000)), + ( + Self::Paris, + ForkCondition::TTD { + activation_block_number: 15537394, + fork_block: None, + total_difficulty: uint!(58_750_000_000_000_000_000_000_U256), + }, + ), + (Self::Shanghai, ForkCondition::Timestamp(1681338455)), + (Self::Cancun, ForkCondition::Timestamp(1710338135)), + ] + } + + /// Ethereum sepolia list of hardforks. + pub const fn sepolia() -> [(Self, ForkCondition); 15] { + [ + (Self::Frontier, ForkCondition::Block(0)), + (Self::Homestead, ForkCondition::Block(0)), + (Self::Dao, ForkCondition::Block(0)), + (Self::Tangerine, ForkCondition::Block(0)), + (Self::SpuriousDragon, ForkCondition::Block(0)), + (Self::Byzantium, ForkCondition::Block(0)), + (Self::Constantinople, ForkCondition::Block(0)), + (Self::Petersburg, ForkCondition::Block(0)), + (Self::Istanbul, ForkCondition::Block(0)), + (Self::MuirGlacier, ForkCondition::Block(0)), + (Self::Berlin, ForkCondition::Block(0)), + (Self::London, ForkCondition::Block(0)), + ( + Self::Paris, + ForkCondition::TTD { + activation_block_number: 1735371, + fork_block: Some(1735371), + total_difficulty: uint!(17_000_000_000_000_000_U256), + }, + ), + (Self::Shanghai, ForkCondition::Timestamp(1677557088)), + (Self::Cancun, ForkCondition::Timestamp(1706655072)), + ] + } + + /// Ethereum holesky list of hardforks. + pub const fn holesky() -> [(Self, ForkCondition); 15] { + [ + (Self::Frontier, ForkCondition::Block(0)), + (Self::Homestead, ForkCondition::Block(0)), + (Self::Dao, ForkCondition::Block(0)), + (Self::Tangerine, ForkCondition::Block(0)), + (Self::SpuriousDragon, ForkCondition::Block(0)), + (Self::Byzantium, ForkCondition::Block(0)), + (Self::Constantinople, ForkCondition::Block(0)), + (Self::Petersburg, ForkCondition::Block(0)), + (Self::Istanbul, ForkCondition::Block(0)), + (Self::MuirGlacier, ForkCondition::Block(0)), + (Self::Berlin, ForkCondition::Block(0)), + (Self::London, ForkCondition::Block(0)), + ( + Self::Paris, + ForkCondition::TTD { + activation_block_number: 0, + fork_block: Some(0), + total_difficulty: U256::ZERO, + }, + ), + (Self::Shanghai, ForkCondition::Timestamp(1696000704)), + (Self::Cancun, ForkCondition::Timestamp(1707305664)), + ] + } +} + +impl From<[(EthereumHardfork, ForkCondition); N]> for ChainHardforks { + fn from(list: [(EthereumHardfork, ForkCondition); N]) -> Self { + Self::new( + list.into_iter() + .map(|(fork, cond)| (Box::new(fork) as Box, cond)) + .collect(), + ) + } +} \ No newline at end of file diff --git a/src/hardfork/macros.rs b/src/hardfork/macros.rs new file mode 100644 index 0000000..893f5c4 --- /dev/null +++ b/src/hardfork/macros.rs @@ -0,0 +1,47 @@ +/// Macro that defines different variants of a chain specific enum. See [`crate::Hardfork`] as an +/// example. +#[macro_export] +macro_rules! hardfork { + ($(#[$enum_meta:meta])* $enum:ident { $( $(#[$meta:meta])* $variant:ident ),* $(,)? }) => { + $(#[$enum_meta])* + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] + pub enum $enum { + $( $(#[$meta])* $variant ),* + } + + impl $enum { + /// Returns variant as `str`. + pub const fn name(&self) -> &'static str { + match self { + $( $enum::$variant => stringify!($variant), )* + } + } + } + + impl FromStr for $enum { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + $( + s if s == stringify!($variant).to_lowercase() => Ok($enum::$variant), + )* + _ => return Err(format!("Unknown hardfork: {s}")), + } + } + } + + impl Hardfork for $enum { + fn name(&self) -> &'static str { + self.name() + } + } + + impl Display for $enum { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{self:?}") + } + } + } +} \ No newline at end of file diff --git a/src/hardfork/mod.rs b/src/hardfork/mod.rs new file mode 100644 index 0000000..3e12623 --- /dev/null +++ b/src/hardfork/mod.rs @@ -0,0 +1,108 @@ +mod macros; + +mod ethereum; +pub use ethereum::EthereumHardfork; + +mod dev; +pub use dev::DEV_HARDFORKS; + +use alloc::boxed::Box; +use core::{ + any::Any, + hash::{Hash, Hasher}, +}; +use dyn_clone::DynClone; + +/// Generic hardfork trait. +#[auto_impl::auto_impl(&, Box)] +pub trait Hardfork: Any + DynClone + Send + Sync + 'static { + /// Fork name. + fn name(&self) -> &'static str; + + /// Returns boxed value. + fn boxed(&self) -> Box { + Box::new(self) + } +} + +dyn_clone::clone_trait_object!(Hardfork); + +impl core::fmt::Debug for dyn Hardfork + 'static { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct(self.name()).finish() + } +} + +impl PartialEq for dyn Hardfork + 'static { + fn eq(&self, other: &Self) -> bool { + self.name() == other.name() + } +} + +impl Eq for dyn Hardfork + 'static {} + +impl Hash for dyn Hardfork + 'static { + fn hash(&self, state: &mut H) { + self.name().hash(state) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[test] + fn check_hardfork_from_str() { + let hardfork_str = [ + "frOntier", + "homEstead", + "dao", + "tAngerIne", + "spurIousdrAgon", + "byzAntium", + "constantinople", + "petersburg", + "istanbul", + "muirglacier", + "bErlin", + "lonDon", + "arrowglacier", + "grayglacier", + "PARIS", + "ShAnGhAI", + "CaNcUn", + "PrAguE", + ]; + let expected_hardforks = [ + EthereumHardfork::Frontier, + EthereumHardfork::Homestead, + EthereumHardfork::Dao, + EthereumHardfork::Tangerine, + EthereumHardfork::SpuriousDragon, + EthereumHardfork::Byzantium, + EthereumHardfork::Constantinople, + EthereumHardfork::Petersburg, + EthereumHardfork::Istanbul, + EthereumHardfork::MuirGlacier, + EthereumHardfork::Berlin, + EthereumHardfork::London, + EthereumHardfork::ArrowGlacier, + EthereumHardfork::GrayGlacier, + EthereumHardfork::Paris, + EthereumHardfork::Shanghai, + EthereumHardfork::Cancun, + EthereumHardfork::Prague, + ]; + + let hardforks: Vec = + hardfork_str.iter().map(|h| EthereumHardfork::from_str(h).unwrap()).collect(); + + assert_eq!(hardforks, expected_hardforks); + } + + #[test] + fn check_nonexistent_hardfork_from_str() { + assert!(EthereumHardfork::from_str("not a hardfork").is_err()); + } +} \ No newline at end of file diff --git a/src/hardforks/ethereum.rs b/src/hardforks/ethereum.rs new file mode 100644 index 0000000..1696f26 --- /dev/null +++ b/src/hardforks/ethereum.rs @@ -0,0 +1,94 @@ +use alloy_primitives::U256; + +use crate::{EthereumHardfork, ForkCondition}; + +/// Helper methods for Ethereum forks. +#[auto_impl::auto_impl(&, Arc)] +pub trait EthereumHardforks: Clone { + /// Retrieves [`ForkCondition`] by an [`EthereumHardfork`]. If `fork` is not present, returns + /// [`ForkCondition::Never`]. + fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition; + + /// Convenience method to check if an [`EthereumHardfork`] is active at a given timestamp. + fn is_ethereum_fork_active_at_timestamp(&self, fork: EthereumHardfork, timestamp: u64) -> bool { + self.ethereum_fork_activation(fork).active_at_timestamp(timestamp) + } + + /// Convenience method to check if an [`EthereumHardfork`] is active at a given block number. + fn is_ethereum_fork_active_at_block(&self, fork: EthereumHardfork, block_number: u64) -> bool { + self.ethereum_fork_activation(fork).active_at_block(block_number) + } + + /// Convenience method to check if [`EthereumHardfork::Shanghai`] is active at a given + /// timestamp. + fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool { + self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Shanghai, timestamp) + } + + /// Convenience method to check if [`EthereumHardfork::Cancun`] is active at a given timestamp. + fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool { + self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Cancun, timestamp) + } + + /// Convenience method to check if [`EthereumHardfork::Prague`] is active at a given timestamp. + fn is_prague_active_at_timestamp(&self, timestamp: u64) -> bool { + self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Prague, timestamp) + } + + /// Convenience method to check if [`EthereumHardfork::Osaka`] is active at a given timestamp. + fn is_osaka_active_at_timestamp(&self, timestamp: u64) -> bool { + self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Osaka, timestamp) + } + + /// Convenience method to check if [`EthereumHardfork::Byzantium`] is active at a given block + /// number. + fn is_byzantium_active_at_block(&self, block_number: u64) -> bool { + self.is_ethereum_fork_active_at_block(EthereumHardfork::Byzantium, block_number) + } + + /// Convenience method to check if [`EthereumHardfork::SpuriousDragon`] is active at a given + /// block number. + fn is_spurious_dragon_active_at_block(&self, block_number: u64) -> bool { + self.is_ethereum_fork_active_at_block(EthereumHardfork::SpuriousDragon, block_number) + } + + /// Convenience method to check if [`EthereumHardfork::Homestead`] is active at a given block + /// number. + fn is_homestead_active_at_block(&self, block_number: u64) -> bool { + self.is_ethereum_fork_active_at_block(EthereumHardfork::Homestead, block_number) + } + + /// Convenience method to check if [`EthereumHardfork::London`] is active at a given block + /// number. + fn is_london_active_at_block(&self, block_number: u64) -> bool { + self.is_ethereum_fork_active_at_block(EthereumHardfork::London, block_number) + } + + /// Convenience method to check if [`EthereumHardfork::Constantinople`] is active at a given + /// block number. + fn is_constantinople_active_at_block(&self, block_number: u64) -> bool { + self.is_ethereum_fork_active_at_block(EthereumHardfork::Constantinople, block_number) + } + + /// The Paris hardfork (merge) is activated via block number. If we have knowledge of the block, + /// this function will return true if the block number is greater than or equal to the Paris + /// (merge) block. + fn is_paris_active_at_block(&self, block_number: u64) -> Option { + match self.ethereum_fork_activation(EthereumHardfork::Paris) { + ForkCondition::TTD { activation_block_number, .. } => { + Some(block_number >= activation_block_number) + } + ForkCondition::Block(paris_block) => Some(block_number >= paris_block), + _ => None, + } + } + + /// Returns the final total difficulty if the Paris hardfork is known. + fn get_final_paris_total_difficulty(&self) -> Option; + + /// Returns the final total difficulty if the given block number is after the Paris hardfork. + /// + /// Note: technically this would also be valid for the block before the paris upgrade, but this + /// edge case is omitted here. + fn final_paris_total_difficulty(&self, block_number: u64) -> Option; +} \ No newline at end of file diff --git a/src/hardforks/mod.rs b/src/hardforks/mod.rs new file mode 100644 index 0000000..5342e34 --- /dev/null +++ b/src/hardforks/mod.rs @@ -0,0 +1,147 @@ +/// Ethereum helper methods +mod ethereum; +pub use ethereum::EthereumHardforks; + +use crate::{ForkCondition, ForkFilter, ForkId, Hardfork, Head}; +#[cfg(feature = "std")] +use rustc_hash::FxHashMap; +#[cfg(feature = "std")] +use std::collections::hash_map::Entry; + +#[cfg(not(feature = "std"))] +use alloc::collections::btree_map::Entry; +use alloc::{boxed::Box, vec::Vec}; + +/// Generic trait over a set of ordered hardforks +#[auto_impl::auto_impl(&, Arc)] +pub trait Hardforks: Clone { + /// Retrieves [`ForkCondition`] from `fork`. If `fork` is not present, returns + /// [`ForkCondition::Never`]. + fn fork(&self, fork: H) -> ForkCondition; + + /// Get an iterator of all hardforks with their respective activation conditions. + fn forks_iter(&self) -> impl Iterator; + + /// Convenience method to check if a fork is active at a given timestamp. + fn is_fork_active_at_timestamp(&self, fork: H, timestamp: u64) -> bool { + self.fork(fork).active_at_timestamp(timestamp) + } + + /// Convenience method to check if a fork is active at a given block number. + fn is_fork_active_at_block(&self, fork: H, block_number: u64) -> bool { + self.fork(fork).active_at_block(block_number) + } + + /// Compute the [`ForkId`] for the given [`Head`] following eip-6122 spec + fn fork_id(&self, head: &Head) -> ForkId; + + /// Returns the [`ForkId`] for the last fork. + fn latest_fork_id(&self) -> ForkId; + + /// Creates a [`ForkFilter`] for the block described by [Head]. + fn fork_filter(&self, head: Head) -> ForkFilter; +} + +/// Ordered list of a chain hardforks that implement [`Hardfork`]. +#[derive(Default, Clone, PartialEq, Eq)] +pub struct ChainHardforks { + forks: Vec<(Box, ForkCondition)>, + #[cfg(feature = "std")] + map: FxHashMap<&'static str, ForkCondition>, + #[cfg(not(feature = "std"))] + map: alloc::collections::BTreeMap<&'static str, ForkCondition>, +} + +impl ChainHardforks { + /// Creates a new [`ChainHardforks`] from a list which **must be ordered** by activation. + /// + /// Equivalent Ethereum hardforks **must be included** as well. + pub fn new(forks: Vec<(Box, ForkCondition)>) -> Self { + let map = forks.iter().map(|(fork, condition)| (fork.name(), *condition)).collect(); + + Self { forks, map } + } + + /// Total number of hardforks. + pub fn len(&self) -> usize { + self.forks.len() + } + + /// Checks if the fork list is empty. + pub fn is_empty(&self) -> bool { + self.forks.is_empty() + } + + /// Retrieves [`ForkCondition`] from `fork`. If `fork` is not present, returns + /// [`ForkCondition::Never`]. + pub fn fork(&self, fork: H) -> ForkCondition { + self.get(fork).unwrap_or_default() + } + + /// Retrieves [`ForkCondition`] from `fork` if it exists, otherwise `None`. + pub fn get(&self, fork: H) -> Option { + self.map.get(fork.name()).copied() + } + + /// Retrieves the fork block number or timestamp from `fork` if it exists, otherwise `None`. + pub fn fork_block(&self, fork: H) -> Option { + match self.fork(fork) { + ForkCondition::Block(block) => Some(block), + ForkCondition::TTD { fork_block, .. } => fork_block, + ForkCondition::Timestamp(ts) => Some(ts), + ForkCondition::Never => None, + } + } + + /// Get an iterator of all hardforks with their respective activation conditions. + pub fn forks_iter(&self) -> impl Iterator { + self.forks.iter().map(|(f, b)| (&**f, *b)) + } + + /// Get last hardfork from the list. + pub fn last(&self) -> Option<(Box, ForkCondition)> { + self.forks.last().map(|(f, b)| (f.clone(), *b)) + } + + /// Convenience method to check if a fork is active at a given timestamp. + pub fn is_fork_active_at_timestamp(&self, fork: H, timestamp: u64) -> bool { + self.fork(fork).active_at_timestamp(timestamp) + } + + /// Convenience method to check if a fork is active at a given block number. + pub fn is_fork_active_at_block(&self, fork: H, block_number: u64) -> bool { + self.fork(fork).active_at_block(block_number) + } + + /// Inserts `fork` into list, updating with a new [`ForkCondition`] if it already exists. + pub fn insert(&mut self, fork: H, condition: ForkCondition) { + match self.map.entry(fork.name()) { + Entry::Occupied(mut entry) => { + *entry.get_mut() = condition; + if let Some((_, inner)) = + self.forks.iter_mut().find(|(inner, _)| inner.name() == fork.name()) + { + *inner = condition; + } + } + Entry::Vacant(entry) => { + entry.insert(condition); + self.forks.push((Box::new(fork), condition)); + } + } + } + + /// Removes `fork` from list. + pub fn remove(&mut self, fork: H) { + self.forks.retain(|(inner_fork, _)| inner_fork.name() != fork.name()); + self.map.remove(fork.name()); + } +} + +impl core::fmt::Debug for ChainHardforks { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("ChainHardforks") + .field("0", &self.forks_iter().map(|(hf, cond)| (hf.name(), cond)).collect::>()) + .finish() + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..dbde7df --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,35 @@ +//! Ethereum fork types used in reth. +//! +//! This crate contains Ethereum fork types and helper functions. +//! +//! ## Feature Flags +//! +//! - `arbitrary`: Adds `arbitrary` support for primitive types. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" +)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +/// Re-exported EIP-2124 forkid types. +pub use alloy_eip2124::*; + +mod display; +mod forkcondition; +mod hardfork; +mod hardforks; + +pub use hardfork::{EthereumHardfork, Hardfork, DEV_HARDFORKS}; + +pub use display::DisplayHardforks; +pub use forkcondition::ForkCondition; +pub use hardforks::*; + +#[cfg(any(test, feature = "arbitrary"))] +pub use arbitrary; \ No newline at end of file