From daca3894fcc0fb2b353a0e7a8f2ff00e5b5701d6 Mon Sep 17 00:00:00 2001 From: dylanhuang Date: Tue, 21 May 2024 11:52:27 +0800 Subject: [PATCH] feat: implement precompile contract for opBNB (#6) * feat: implement precompile contract for opBNB * fix: unused import warning in test * ci: add develop branch to the list of branches to run * chore: fmt codes * chore: fix no default feature importing * chore: cargo clippy fix * ci: disable checks on riscv32imac-unknown-none-elf * chore: setup vec with capacity * fix: cometbft_light_block_validate input conversion * chore: fix fmt * fix: add opbnb feature to ECOTONE * fix: sepc definition * chore: fix opBNB feature tags * chore(ci): bump action/deploy (#1372) * fix: light block header validation in cometbft * chore: fix fmt * fix: BLS_SIGNATURE_VALIDATION_BASE to 1000 * update greenfield-cometbft-rs version --------- Co-authored-by: rakita Co-authored-by: Keefe Liu --- .github/workflows/book.yml | 4 +- .github/workflows/cachegrind.yml | 2 +- .github/workflows/ci.yml | 24 +- .github/workflows/ethereum-tests.yml | 4 +- Cargo.lock | 528 ++++++++++++++++++- Cargo.toml | 2 +- crates/interpreter/Cargo.toml | 8 + crates/precompile/Cargo.toml | 14 + crates/precompile/src/bls.rs | 248 +++++++++ crates/precompile/src/cometbft.rs | 675 +++++++++++++++++++++++++ crates/precompile/src/lib.rs | 30 ++ crates/primitives/Cargo.toml | 4 + crates/primitives/src/precompile.rs | 15 + crates/primitives/src/specification.rs | 23 +- crates/revm/Cargo.toml | 7 + 15 files changed, 1551 insertions(+), 37 deletions(-) create mode 100644 crates/precompile/src/bls.rs create mode 100644 crates/precompile/src/cometbft.rs diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index fb41e3ee..2f8f06bf 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -6,9 +6,9 @@ concurrency: on: push: - branches: [main] + branches: [main, develop] pull_request: - branches: [main] + branches: [main, develop] merge_group: jobs: diff --git a/.github/workflows/cachegrind.yml b/.github/workflows/cachegrind.yml index ad79a05a..74c93576 100644 --- a/.github/workflows/cachegrind.yml +++ b/.github/workflows/cachegrind.yml @@ -2,7 +2,7 @@ name: Valgrind Cachegrind on: pull_request: - branches: [main] + branches: [main, develop] jobs: valgrind: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cbea7ba5..a6b82262 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,9 +6,9 @@ concurrency: on: push: - branches: [main, "release/**"] + branches: [main, develop, "release/**"] pull_request: - branches: [main, "release/**"] + branches: [main, develop, "release/**"] env: CARGO_TERM_COLOR: always @@ -31,16 +31,16 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: cargo test --workspace ${{ matrix.flags }} - test-no-std: - name: test no_std - 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 + # test-no-std: + # name: test no_std + # 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 check-no-default-features: name: check no-default-features diff --git a/.github/workflows/ethereum-tests.yml b/.github/workflows/ethereum-tests.yml index c4c1c8df..6875e9d1 100644 --- a/.github/workflows/ethereum-tests.yml +++ b/.github/workflows/ethereum-tests.yml @@ -6,9 +6,9 @@ concurrency: on: push: - branches: [main, "release/**"] + branches: [main, develop, "release/**"] pull_request: - branches: [main, "release/**"] + branches: [main, develop, "release/**"] jobs: tests-stable: diff --git a/Cargo.lock b/Cargo.lock index 8250353b..6b941a1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ dependencies = [ "c-kzg", "once_cell", "serde", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -384,6 +384,35 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff 0.4.2", + "ark-poly", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -467,6 +496,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", +] + [[package]] name = "ark-serialize" version = "0.3.0" @@ -483,11 +525,23 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ + "ark-serialize-derive", "ark-std 0.4.0", "digest 0.10.7", "num-bigint", ] +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-std" version = "0.3.0" @@ -700,6 +754,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -709,6 +772,23 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bls_on_arkworks" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4107cadb31de0d0c1282dd57efe395706b1d81feb697799871f95c3324af7c44" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "hkdf", + "hmac", + "libm", + "sha2 0.10.8", +] + [[package]] name = "blst" version = "0.3.11" @@ -813,7 +893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half", + "half 2.4.1", ] [[package]] @@ -856,6 +936,132 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "cometbft" +version = "0.1.0-alpha.2" +source = "git+https://github.com/bnb-chain/greenfield-cometbft-rs.git?rev=1282547#12825470f8b2df8e960e39a84ad4c9921492f512" +dependencies = [ + "bytes", + "cometbft-proto", + "digest 0.10.7", + "ed25519", + "ed25519-consensus", + "flex-error", + "futures", + "num-traits", + "once_cell", + "prost", + "prost-types", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "signature", + "subtle", + "subtle-encoding", + "time", + "zeroize", +] + +[[package]] +name = "cometbft-config" +version = "0.1.0-alpha.2" +source = "git+https://github.com/bnb-chain/greenfield-cometbft-rs.git?rev=1282547#12825470f8b2df8e960e39a84ad4c9921492f512" +dependencies = [ + "cometbft", + "flex-error", + "serde", + "serde_json", + "toml", + "url", +] + +[[package]] +name = "cometbft-light-client" +version = "0.1.0-alpha.2" +source = "git+https://github.com/bnb-chain/greenfield-cometbft-rs.git?rev=1282547#12825470f8b2df8e960e39a84ad4c9921492f512" +dependencies = [ + "cometbft", + "cometbft-light-client-verifier", + "cometbft-rpc", + "contracts", + "crossbeam-channel", + "derive_more", + "flex-error", + "futures", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "static_assertions", + "time", + "tokio", + "tracing", +] + +[[package]] +name = "cometbft-light-client-verifier" +version = "0.1.0-alpha.2" +source = "git+https://github.com/bnb-chain/greenfield-cometbft-rs.git?rev=1282547#12825470f8b2df8e960e39a84ad4c9921492f512" +dependencies = [ + "cometbft", + "derive_more", + "flex-error", + "serde", + "time", +] + +[[package]] +name = "cometbft-proto" +version = "0.1.0-alpha.2" +source = "git+https://github.com/bnb-chain/greenfield-cometbft-rs.git?rev=1282547#12825470f8b2df8e960e39a84ad4c9921492f512" +dependencies = [ + "bytes", + "flex-error", + "num-derive", + "num-traits", + "prost", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "cometbft-rpc" +version = "0.1.0-alpha.2" +source = "git+https://github.com/bnb-chain/greenfield-cometbft-rs.git?rev=1282547#12825470f8b2df8e960e39a84ad4c9921492f512" +dependencies = [ + "async-trait", + "bytes", + "cometbft", + "cometbft-config", + "cometbft-proto", + "flex-error", + "futures", + "getrandom", + "peg", + "pin-project", + "rand", + "reqwest 0.11.27", + "semver 1.0.23", + "serde", + "serde_bytes", + "serde_json", + "subtle", + "subtle-encoding", + "thiserror", + "time", + "tokio", + "tracing", + "url", + "uuid", + "walkdir", +] + [[package]] name = "console" version = "0.15.8" @@ -888,6 +1094,17 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "contracts" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d1429e3bd78171c65aa010eabcdf8f863ba3254728dbfb0ad4b1545beac15c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -955,6 +1172,15 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -1008,6 +1234,19 @@ dependencies = [ "typenum", ] +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core", + "subtle-ng", + "zeroize", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1015,7 +1254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -1096,7 +1335,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", @@ -1128,6 +1367,29 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "either" version = "1.12.0" @@ -1403,6 +1665,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flex-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" +dependencies = [ + "paste", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1571,8 +1842,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1648,6 +1921,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + [[package]] name = "half" version = "2.4.1" @@ -1664,6 +1943,15 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1735,6 +2023,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1967,7 +2264,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -2066,7 +2363,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2", + "sha2 0.10.8", "signature", ] @@ -2138,7 +2435,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -2236,6 +2533,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -2335,6 +2643,12 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "open-fastrlp" version = "0.1.4" @@ -2413,7 +2727,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -2461,6 +2775,33 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "peg" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a625d12ad770914cbf7eff6f9314c3ef803bfe364a1b20bc36ddf56673e71e5" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f241d42067ed3ab6a4fece1db720838e1418f36d868585a27931f95d6bc03582" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" + [[package]] name = "pem" version = "1.1.1" @@ -2671,7 +3012,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit", + "toml_edit 0.21.1", ] [[package]] @@ -2738,6 +3079,38 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2886,6 +3259,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls", + "rustls-native-certs", "rustls-pemfile 1.0.4", "serde", "serde_json", @@ -2988,13 +3362,19 @@ name = "revm-precompile" version = "7.0.0" dependencies = [ "aurora-engine-modexp", + "bls_on_arkworks", "blst", "c-kzg", + "cometbft", + "cometbft-light-client", + "cometbft-light-client-verifier", + "cometbft-proto", "criterion", "eyre", "k256", "once_cell", "p256", + "prost", "rand", "revm-primitives", "ripemd", @@ -3003,7 +3383,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha2", + "sha2 0.10.8", "substrate-bn", ] @@ -3020,7 +3400,7 @@ dependencies = [ "derive_more", "dyn-clone", "enumn", - "hashbrown", + "hashbrown 0.14.5", "hex", "once_cell", "serde", @@ -3046,7 +3426,7 @@ version = "0.5.0" dependencies = [ "alloy-rlp", "hash-db", - "hashbrown", + "hashbrown 0.14.5", "hex", "indicatif", "k256", @@ -3247,6 +3627,18 @@ dependencies = [ "sct", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -3465,6 +3857,25 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half 1.8.3", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.203" @@ -3488,6 +3899,26 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3511,6 +3942,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.8" @@ -3694,6 +4138,21 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "syn" version = "1.0.109" @@ -3964,11 +4423,26 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.13", +] + [[package]] name = "toml_datetime" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -3978,7 +4452,20 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.9", ] [[package]] @@ -4186,6 +4673,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" + [[package]] name = "valuable" version = "0.1.0" @@ -4505,6 +4998,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index 426d6e40..7001c19d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = [ "crates/revm", "crates/primitives", "crates/interpreter", - "crates/precompile", + "crates/precompile", ] resolver = "2" default-members = ["crates/revm"] diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index cd6b3d96..87ecceb7 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -81,3 +81,11 @@ optional_eip3607 = ["revm-primitives/optional_eip3607"] optional_gas_refund = ["revm-primitives/optional_gas_refund"] optional_no_base_fee = ["revm-primitives/optional_no_base_fee"] optional_beneficiary_reward = ["revm-primitives/optional_beneficiary_reward"] +opbnb = ["revm-primitives/opbnb"] +opbnb-default-handler = [ + "opbnb", + "revm-primitives/opbnb-default-handler", +] +negate-opbnb-default-handler = [ + "revm-primitives/negate-opbnb-default-handler", +] \ No newline at end of file diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index 08abf55b..761d4b03 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -40,6 +40,12 @@ secp256k1 = { version = "0.29.0", default-features = false, features = [ "rand", "global-context", ], optional = true } +cometbft = { git = "https://github.com/bnb-chain/greenfield-cometbft-rs.git", rev = "1282547" } +cometbft-light-client-verifier = { git = "https://github.com/bnb-chain/greenfield-cometbft-rs.git", rev = "1282547" } +cometbft-proto = { git = "https://github.com/bnb-chain/greenfield-cometbft-rs.git", rev = "1282547" } +cometbft-light-client = { git = "https://github.com/bnb-chain/greenfield-cometbft-rs.git", rev = "1282547" } +prost = { version = "0.12.3" } +bls_on_arkworks = "0.3.0" # BLS12-381 precompiles blst = { version = "0.3.11", optional = true } @@ -96,6 +102,14 @@ secp256k1 = ["dep:secp256k1"] # Enables the BLS12-381 precompiles. blst = ["dep:blst"] +opbnb = ["revm-primitives/opbnb"] +opbnb-default-handler = [ + "opbnb", + "revm-primitives/opbnb-default-handler", +] +negate-opbnb-default-handler = [ + "revm-primitives/negate-opbnb-default-handler", +] [[bench]] name = "bench" diff --git a/crates/precompile/src/bls.rs b/crates/precompile/src/bls.rs new file mode 100644 index 00000000..3d5ccfb1 --- /dev/null +++ b/crates/precompile/src/bls.rs @@ -0,0 +1,248 @@ +use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress}; +use bls_on_arkworks as bls; +use revm_primitives::Bytes; +use std::vec::Vec; + +pub const BLS_SIGNATURE_VALIDATION: PrecompileWithAddress = PrecompileWithAddress( + crate::u64_to_address(102), + Precompile::Standard(bls_signature_validation_run), +); + +const BLS_MSG_HASH_LENGTH: u64 = 32; +const BLS_SIGNATURE_LENGTH: u64 = 96; +const BLS_SINGLE_PUBKEY_LENGTH: u64 = 48; +const BLS_DST: &[u8] = bls::DST_ETHEREUM.as_bytes(); + +/// Run bls signature validation precompile. +/// +/// The input is encoded as follows: +/// | msg_hash | signature | [{bls pubkey}] | +/// | 32 | 96 | [{48}] | +fn bls_signature_validation_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { + let cost = calc_gas_cost(input); + if cost > gas_limit { + return Err(Error::OutOfGas); + } + + let msg_and_sig_length = BLS_MSG_HASH_LENGTH + BLS_SIGNATURE_LENGTH; + let input_length = input.len() as u64; + if (input_length <= msg_and_sig_length) + || ((input_length - msg_and_sig_length) % BLS_SINGLE_PUBKEY_LENGTH != 0) + { + return Err(Error::BLSInvalidInputLength); + } + + let msg_hash: &Vec = &input[..BLS_MSG_HASH_LENGTH as usize].to_vec(); + let signature = &input[BLS_MSG_HASH_LENGTH as usize..msg_and_sig_length as usize].to_vec(); + let pub_keys_data = &input[msg_and_sig_length as usize..].to_vec(); + + // check signature format + let _ = bls::signature_to_point(&signature.to_vec()).map_err(|_| Error::BLSInvalidSignature)?; + + let pub_key_count = (input_length - msg_and_sig_length) / BLS_SINGLE_PUBKEY_LENGTH; + let mut pub_keys = Vec::with_capacity(pub_key_count as usize); + let mut msg_hashes = Vec::with_capacity(pub_key_count as usize); + + // check pubkey format and push to pub_keys + for i in 0..pub_key_count { + let pub_key = &pub_keys_data[i as usize * BLS_SINGLE_PUBKEY_LENGTH as usize + ..(i + 1) as usize * BLS_SINGLE_PUBKEY_LENGTH as usize]; + if !bls::key_validate(&pub_key.to_vec()) { + return Err(Error::BLSInvalidPublicKey); + } + pub_keys.push(pub_key.to_vec()); + msg_hashes.push(msg_hash.clone().to_vec()); + } + if pub_keys.is_empty() { + return Err(Error::BLSInvalidPublicKey); + } + + // verify signature + let mut output = Bytes::from(vec![1]); + if (pub_keys.len() == 1 && !bls::verify(&pub_keys[0], msg_hash, signature, &BLS_DST.to_vec())) + || !bls::aggregate_verify(pub_keys, msg_hashes, signature, &BLS_DST.to_vec()) + { + output = Bytes::from(vec![0]); + } + + Ok((cost, output)) +} + +fn calc_gas_cost(input: &Bytes) -> u64 { + const BLS_SIGNATURE_VALIDATION_BASE: u64 = 1_000; + const BLS_SIGNATURE_VALIDATION_PER_KER: u64 = 3_500; + + let msg_length = BLS_MSG_HASH_LENGTH + BLS_SIGNATURE_LENGTH; + let single_pubkey_length = BLS_SINGLE_PUBKEY_LENGTH; + let input_length = input.len() as u64; + + if (input_length <= msg_length) || ((input_length - msg_length) % single_pubkey_length != 0) { + return BLS_SIGNATURE_VALIDATION_BASE; + } + + let pub_key_number = (input_length - msg_length) / single_pubkey_length; + + BLS_SIGNATURE_VALIDATION_BASE + BLS_SIGNATURE_VALIDATION_PER_KER * pub_key_number +} + +#[cfg(test)] +mod tests { + use super::*; + use revm_primitives::hex; + + #[test] + fn test_bls_signature_validation_with_single_key() { + let msg_hash = hex!("6377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + let signature = hex!("8325fccd4ff01e6e0e73de4955d3cb2c6678c6a6abfc465c2991e375c5cf68841ac7847ac51c32a26bd99828bc99f2f6082c41986097e0f6e6711e57c5bd5b18fa6f8f44bf416617cf192a2ff6d4edf0890315d87e3c04f04f0d1611b64bbe0a"); + let pub_key = hex!("a842801f14464ce36470737dc159cb13191e3ad8a49f4f3a38e6a94ea5594ff65753f74661fb7ec944b98fc673bb8230"); + let mut input = Vec::::new(); + input.extend_from_slice(&msg_hash); + input.extend_from_slice(&signature); + input.extend_from_slice(&pub_key); + + let excepted_output = Bytes::from(vec![1]); + let result = match bls_signature_validation_run(&Bytes::from(input.clone()), 100_000_000) { + Ok((_, output)) => output, + Err(e) => panic!("BLS signature validation failed, {:?}", e), + }; + assert_eq!(result, excepted_output); + + // wrong msg hash + let msg_hash = hex!("1377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + let signature = hex!("8325fccd4ff01e6e0e73de4955d3cb2c6678c6a6abfc465c2991e375c5cf68841ac7847ac51c32a26bd99828bc99f2f6082c41986097e0f6e6711e57c5bd5b18fa6f8f44bf416617cf192a2ff6d4edf0890315d87e3c04f04f0d1611b64bbe0a"); + let pub_key = hex!("a842801f14464ce36470737dc159cb13191e3ad8a49f4f3a38e6a94ea5594ff65753f74661fb7ec944b98fc673bb8230"); + let mut input = Vec::::new(); + input.extend_from_slice(&msg_hash); + input.extend_from_slice(&signature); + input.extend_from_slice(&pub_key); + + let excepted_output = Bytes::from(vec![0]); + let result = match bls_signature_validation_run(&Bytes::from(input.clone()), 100_000_000) { + Ok((_, output)) => output, + Err(e) => panic!("BLS signature validation failed, {:?}", e), + }; + assert_eq!(result, excepted_output); + + // wrong signature + let msg_hash = hex!("6377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + let signature = hex!("1325fccd4ff01e6e0e73de4955d3cb2c6678c6a6abfc465c2991e375c5cf68841ac7847ac51c32a26bd99828bc99f2f6082c41986097e0f6e6711e57c5bd5b18fa6f8f44bf416617cf192a2ff6d4edf0890315d87e3c04f04f0d1611b64bbe0a"); + let pub_key = hex!("a842801f14464ce36470737dc159cb13191e3ad8a49f4f3a38e6a94ea5594ff65753f74661fb7ec944b98fc673bb8230"); + let mut input = Vec::::new(); + input.extend_from_slice(&msg_hash); + input.extend_from_slice(&signature); + input.extend_from_slice(&pub_key); + + match bls_signature_validation_run(&Bytes::from(input.clone()), 100_000_000) { + Ok(_) => panic!("BLS signature validation failed, expect error"), + Err(e) => assert_eq!(e, Error::BLSInvalidSignature), + } + + // wrong pubkey + let msg_hash = hex!("6377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + let signature = hex!("1325fccd4ff01e6e0e73de4955d3cb2c6678c6a6abfc465c2991e375c5cf68841ac7847ac51c32a26bd99828bc99f2f6082c41986097e0f6e6711e57c5bd5b18fa6f8f44bf416617cf192a2ff6d4edf0890315d87e3c04f04f0d1611b64bbe0a"); + let pub_key = hex!("1842801f14464ce36470737dc159cb13191e3ad8a49f4f3a38e6a94ea5594ff65753f74661fb7ec944b98fc673bb8230"); + let mut input = Vec::::new(); + input.extend_from_slice(&msg_hash); + input.extend_from_slice(&signature); + input.extend_from_slice(&pub_key); + + match bls_signature_validation_run(&Bytes::from(input.clone()), 100_000_000) { + Ok(_) => panic!("BLS signature validation failed, expect error"), + Err(e) => assert_eq!(e, Error::BLSInvalidSignature), + } + } + + #[test] + fn test_bls_signature_validation_with_multiple_keys() { + let msg_hash = hex!("6377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + let signature = hex!("876ac46847e82a2f2887bbeb855916e05bd259086fda5553e4e5e5ee0dcda6869c10e2aa539e265b492015d1bd1b553815a42ea9daf6713b6a0002c6f1aacfa51e55b931745638c0552d7fab4a499bbd6ba71f9be36d35ffa527f77b2a6cebda"); + let pub_key1 = hex!("80223255a26d81a8e1cd94df746f45e87a91d28f408a037804062910b7db68a724cfd204b7f9337bcecac25de86d5515"); + let pub_key2 = hex!("8bec004e938668c67aa0fab6f555282efdf213817e455d9eaa6a0897211eae5f79db5cdc626d1cd5759a0c1c10cf7aa0"); + let pub_key3 = hex!("af952757f442d7240a4cec62de638973a24fde8eb0ad5217be61eea53211c19859c03a299125ea8520f015f6f8865076"); + let mut input = Vec::::new(); + input.extend_from_slice(&msg_hash); + input.extend_from_slice(&signature); + input.extend_from_slice(&pub_key1); + input.extend_from_slice(&pub_key2); + input.extend_from_slice(&pub_key3); + + let excepted_output = Bytes::from(vec![1]); + let result = match bls_signature_validation_run(&Bytes::from(input.clone()), 100_000_000) { + Ok((_, output)) => output, + Err(e) => panic!("BLS signature validation failed, {:?}", e), + }; + assert_eq!(result, excepted_output); + + // wrong msg hash + let msg_hash = hex!("1377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + let signature = hex!("876ac46847e82a2f2887bbeb855916e05bd259086fda5553e4e5e5ee0dcda6869c10e2aa539e265b492015d1bd1b553815a42ea9daf6713b6a0002c6f1aacfa51e55b931745638c0552d7fab4a499bbd6ba71f9be36d35ffa527f77b2a6cebda"); + let pub_key1 = hex!("80223255a26d81a8e1cd94df746f45e87a91d28f408a037804062910b7db68a724cfd204b7f9337bcecac25de86d5515"); + let pub_key2 = hex!("8bec004e938668c67aa0fab6f555282efdf213817e455d9eaa6a0897211eae5f79db5cdc626d1cd5759a0c1c10cf7aa0"); + let pub_key3 = hex!("af952757f442d7240a4cec62de638973a24fde8eb0ad5217be61eea53211c19859c03a299125ea8520f015f6f8865076"); + let mut input = Vec::::new(); + input.extend_from_slice(&msg_hash); + input.extend_from_slice(&signature); + input.extend_from_slice(&pub_key1); + input.extend_from_slice(&pub_key2); + input.extend_from_slice(&pub_key3); + let excepted_output = Bytes::from(vec![0]); + match bls_signature_validation_run(&Bytes::from(input.clone()), 100_000_000) { + Ok((_, output)) => assert_eq!(output, excepted_output), + Err(e) => panic!("BLS signature validation failed, {:?}", e), + } + + // wrong signature + let msg_hash = hex!("1377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + let signature = hex!("276ac46847e82a2f2887bbeb855916e05bd259086fda5553e4e5e5ee0dcda6869c10e2aa539e265b492015d1bd1b553815a42ea9daf6713b6a0002c6f1aacfa51e55b931745638c0552d7fab4a499bbd6ba71f9be36d35ffa527f77b2a6cebda"); + let pub_key1 = hex!("80223255a26d81a8e1cd94df746f45e87a91d28f408a037804062910b7db68a724cfd204b7f9337bcecac25de86d5515"); + let pub_key2 = hex!("8bec004e938668c67aa0fab6f555282efdf213817e455d9eaa6a0897211eae5f79db5cdc626d1cd5759a0c1c10cf7aa0"); + let pub_key3 = hex!("af952757f442d7240a4cec62de638973a24fde8eb0ad5217be61eea53211c19859c03a299125ea8520f015f6f8865076"); + let mut input = Vec::::new(); + input.extend_from_slice(&msg_hash); + input.extend_from_slice(&signature); + input.extend_from_slice(&pub_key1); + input.extend_from_slice(&pub_key2); + input.extend_from_slice(&pub_key3); + + match bls_signature_validation_run(&Bytes::from(input.clone()), 100_000_000) { + Ok(_) => panic!("BLS signature validation failed, expect error"), + Err(e) => assert_eq!(e, Error::BLSInvalidSignature), + } + + // invalid pubkey + let msg_hash = hex!("1377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + let signature = hex!("276ac46847e82a2f2887bbeb855916e05bd259086fda5553e4e5e5ee0dcda6869c10e2aa539e265b492015d1bd1b553815a42ea9daf6713b6a0002c6f1aacfa51e55b931745638c0552d7fab4a499bbd6ba71f9be36d35ffa527f77b2a6cebda"); + let pub_key1 = hex!("10223255a26d81a8e1cd94df746f45e87a91d28f408a037804062910b7db68a724cfd204b7f9337bcecac25de86d5515"); + let pub_key2 = hex!("8bec004e938668c67aa0fab6f555282efdf213817e455d9eaa6a0897211eae5f79db5cdc626d1cd5759a0c1c10cf7aa0"); + let pub_key3 = hex!("af952757f442d7240a4cec62de638973a24fde8eb0ad5217be61eea53211c19859c03a299125ea8520f015f6f8865076"); + let mut input = Vec::::new(); + input.extend_from_slice(&msg_hash); + input.extend_from_slice(&signature); + input.extend_from_slice(&pub_key1); + input.extend_from_slice(&pub_key2); + input.extend_from_slice(&pub_key3); + + match bls_signature_validation_run(&Bytes::from(input.clone()), 100_000_000) { + Ok(_) => panic!("BLS signature validation failed, expect error"), + Err(e) => assert_eq!(e, Error::BLSInvalidSignature), + } + + // duplicate pubkey + let msg_hash = hex!("6377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + let signature = hex!("876ac46847e82a2f2887bbeb855916e05bd259086fda5553e4e5e5ee0dcda6869c10e2aa539e265b492015d1bd1b553815a42ea9daf6713b6a0002c6f1aacfa51e55b931745638c0552d7fab4a499bbd6ba71f9be36d35ffa527f77b2a6cebda"); + let pub_key1 = hex!("8bec004e938668c67aa0fab6f555282efdf213817e455d9eaa6a0897211eae5f79db5cdc626d1cd5759a0c1c10cf7aa0"); + let pub_key2 = hex!("8bec004e938668c67aa0fab6f555282efdf213817e455d9eaa6a0897211eae5f79db5cdc626d1cd5759a0c1c10cf7aa0"); + let pub_key3 = hex!("af952757f442d7240a4cec62de638973a24fde8eb0ad5217be61eea53211c19859c03a299125ea8520f015f6f8865076"); + let mut input = Vec::::new(); + input.extend_from_slice(&msg_hash); + input.extend_from_slice(&signature); + input.extend_from_slice(&pub_key1); + input.extend_from_slice(&pub_key2); + input.extend_from_slice(&pub_key3); + let excepted_output = Bytes::from(vec![0]); + match bls_signature_validation_run(&Bytes::from(input.clone()), 100_000_000) { + Ok((_, output)) => assert_eq!(output, excepted_output), + Err(e) => panic!("BLS signature validation failed, {:?}", e), + } + } +} diff --git a/crates/precompile/src/cometbft.rs b/crates/precompile/src/cometbft.rs new file mode 100644 index 00000000..f2865d82 --- /dev/null +++ b/crates/precompile/src/cometbft.rs @@ -0,0 +1,675 @@ +use crate::{Error, Precompile, PrecompileError, PrecompileResult, PrecompileWithAddress}; +use cometbft::vote::Power; +use cometbft::PublicKey; +use cometbft::{block::signed_header::SignedHeader, validator::Set}; +use cometbft_light_client::{ + predicates::VerificationPredicates, + types::{LightBlock, TrustThreshold}, +}; +use cometbft_light_client_verifier::{ + operations::voting_power::ProdVotingPowerCalculator, + predicates::ProdPredicates, + types::{Validator, ValidatorSet}, +}; +use cometbft_proto::types::v1::LightBlock as TmLightBlock; +use prost::Message; +use revm_primitives::Bytes; +use std::borrow::ToOwned; +use std::string::String; +use std::vec::Vec; + +pub const COMETBFT_LIGHT_BLOCK_VALIDATION: PrecompileWithAddress = PrecompileWithAddress( + crate::u64_to_address(103), + Precompile::Standard(cometbft_light_block_validation_run), +); + +const UINT64_TYPE_LENGTH: u64 = 8; +const CONSENSUS_STATE_LENGTH_BYTES_LENGTH: u64 = 32; +const VALIDATE_RESULT_METADATA_LENGTH: u64 = 32; + +const CHAIN_ID_LENGTH: u64 = 32; +const HEIGHT_LENGTH: u64 = 8; +const VALIDATOR_SET_HASH_LENGTH: u64 = 32; +const VALIDATOR_PUBKEY_LENGTH: u64 = 32; +const VALIDATOR_VOTING_POWER_LENGTH: u64 = 8; +const RELAYER_ADDRESS_LENGTH: u64 = 20; +const RELAYER_BLS_KEY_LENGTH: u64 = 48; + +const SINGLE_VALIDATOR_BYTES_LENGTH: u64 = VALIDATOR_PUBKEY_LENGTH + + VALIDATOR_VOTING_POWER_LENGTH + + RELAYER_ADDRESS_LENGTH + + RELAYER_BLS_KEY_LENGTH; + +const MAX_CONSENSUS_STATE_LENGTH: u64 = CHAIN_ID_LENGTH + + HEIGHT_LENGTH + + VALIDATOR_SET_HASH_LENGTH + + 99 * SINGLE_VALIDATOR_BYTES_LENGTH; + +fn cometbft_light_block_validation_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { + const COMETBFT_LIGHT_BLOCK_VALIDATION_BASE: u64 = 3_000; + + if COMETBFT_LIGHT_BLOCK_VALIDATION_BASE > gas_limit { + return Err(Error::OutOfGas); + } + + let (mut consensus_state, tm_light_block) = match decode_light_block_validation_input(input) { + Ok(result) => result, + Err(e) => return Err(e), + }; + + let light_block = match convert_light_block_from_proto(&tm_light_block) { + Ok(lb) => lb, + Err(e) => return Err(e), + }; + + let validator_set_changed = match consensus_state.apply_light_block(&light_block) { + Ok(validator_set_changed) => validator_set_changed, + Err(e) => return Err(e), + }; + + let consensus_state_bytes = match consensus_state.encode() { + Ok(cs) => cs, + Err(e) => return Err(e), + }; + + Ok(( + COMETBFT_LIGHT_BLOCK_VALIDATION_BASE, + encode_light_block_validation_result(validator_set_changed, consensus_state_bytes), + )) +} + +type ConvertLightBlockResult = Result; +fn convert_light_block_from_proto(light_block_proto: &TmLightBlock) -> ConvertLightBlockResult { + let signed_header = + match SignedHeader::try_from(light_block_proto.signed_header.as_ref().unwrap().clone()) { + Ok(sh) => sh.clone(), + Err(_) => return Err(Error::CometBftInvalidInput), + }; + + let validator_set = + match Set::try_from(light_block_proto.validator_set.as_ref().unwrap().clone()) { + Ok(vs) => vs.clone(), + Err(_) => return Err(Error::CometBftInvalidInput), + }; + + let next_validator_set = validator_set.clone(); + let peer_id = cometbft::node::Id::new([0u8; 20]); + Ok(LightBlock::new( + signed_header, + validator_set, + next_validator_set, + peer_id, + )) +} + +type DecodeLightBlockResult = Result<(ConsensusState, TmLightBlock), PrecompileError>; +fn decode_light_block_validation_input(input: &Bytes) -> DecodeLightBlockResult { + let input_length = input.len() as u64; + if input_length < CONSENSUS_STATE_LENGTH_BYTES_LENGTH { + return Err(Error::CometBftInvalidInput); + } + + let cs_length = u64::from_be_bytes( + input[CONSENSUS_STATE_LENGTH_BYTES_LENGTH as usize - UINT64_TYPE_LENGTH as usize + ..CONSENSUS_STATE_LENGTH_BYTES_LENGTH as usize] + .try_into() + .unwrap(), + ); + let input_length_checked = CONSENSUS_STATE_LENGTH_BYTES_LENGTH.checked_add(cs_length); + if input_length_checked.is_none() { + // overflow + return Err(Error::CometBftInvalidInput); + } + + if input_length < input_length_checked.unwrap() { + return Err(Error::CometBftInvalidInput); + } + + let decode_input = Bytes::from( + input[CONSENSUS_STATE_LENGTH_BYTES_LENGTH as usize + ..(CONSENSUS_STATE_LENGTH_BYTES_LENGTH + cs_length) as usize] + .to_vec(), + ); + let consensus_state = match decode_consensus_state(&decode_input) { + Ok(cs) => cs, + Err(e) => return Err(e), + }; + + let mut light_block_pb: TmLightBlock = TmLightBlock::default(); + match light_block_pb + .merge(&input[CONSENSUS_STATE_LENGTH_BYTES_LENGTH as usize + cs_length as usize..]) + { + Ok(pb) => pb, + Err(_) => return Err(Error::CometBftInvalidInput), + }; + + Ok((consensus_state, light_block_pb)) +} + +struct ConsensusState { + chain_id: String, + height: u64, + next_validator_set_hash: Bytes, + validators: ValidatorSet, + relayer_address: Vec, + relayer_bls_key: Vec, +} + +impl ConsensusState { + fn new( + chain_id: String, + height: u64, + next_validator_set_hash: Bytes, + validators: ValidatorSet, + relayer_address: Vec, + relayer_bls_key: Vec, + ) -> Self { + Self { + chain_id, + height, + next_validator_set_hash, + validators, + relayer_address, + relayer_bls_key, + } + } + + fn apply_light_block(&mut self, light_block: &LightBlock) -> Result { + if light_block.signed_header.header().chain_id.as_str() != self.chain_id { + return Ok(false); + } + + let vp = ProdPredicates; + let voting_power_calculator = ProdVotingPowerCalculator::default(); + let trust_threshold_two_third = TrustThreshold::TWO_THIRDS; + let trust_threshold_one_third = TrustThreshold::ONE_THIRD; + if self.height + 1 == light_block.height().value() { + if self.next_validator_set_hash.ne(light_block + .signed_header + .header() + .validators_hash + .as_bytes()) + { + return Ok(false); + } + // Verify Commit Light Trusted + let result = vp.has_sufficient_validators_overlap( + &light_block.signed_header, + &light_block.validators, + &trust_threshold_two_third, + &voting_power_calculator, + ); + if result.is_err() { + return Ok(false); + } + } else { + // Verify Commit Light Trusting + let result = vp.has_sufficient_validators_overlap( + &light_block.signed_header, + &self.validators, + &trust_threshold_one_third, + &voting_power_calculator, + ); + if result.is_err() { + return Ok(false); + } + + // Verify Commit Light + let result = vp.has_sufficient_validators_overlap( + &light_block.signed_header, + &light_block.validators, + &trust_threshold_two_third, + &voting_power_calculator, + ); + if result.is_err() { + return Ok(false); + } + } + + let validator_set_changed = self.validators.hash().as_bytes().ne(light_block + .signed_header + .header() + .validators_hash + .as_bytes()); + self.height = light_block.height().value(); + self.next_validator_set_hash = Bytes::from( + light_block + .signed_header + .header() + .next_validators_hash + .as_bytes() + .to_vec(), + ); + self.validators = light_block.validators.clone(); + + Ok(validator_set_changed) + } + + fn encode(&self) -> Result { + let validator_set_length = self.validators.validators().len(); + let serialize_length = (CHAIN_ID_LENGTH + + HEIGHT_LENGTH + + VALIDATOR_SET_HASH_LENGTH + + validator_set_length as u64 * SINGLE_VALIDATOR_BYTES_LENGTH) + as usize; + if serialize_length > MAX_CONSENSUS_STATE_LENGTH as usize { + return Err(Error::CometBftEncodeConsensusStateFailed); + } + if self.chain_id.len() > CHAIN_ID_LENGTH as usize { + return Err(Error::CometBftEncodeConsensusStateFailed); + } + + let mut output = vec![0; serialize_length]; + let mut pos: usize = 0; + let chain_id_bytes = self.chain_id.as_bytes(); + if chain_id_bytes.len() > CHAIN_ID_LENGTH as usize { + return Err(Error::CometBftEncodeConsensusStateFailed); + } + let mut filled_chain_id = [0u8; 32]; + filled_chain_id[..chain_id_bytes.len()].copy_from_slice(chain_id_bytes); + output[pos..pos + CHAIN_ID_LENGTH as usize] + .copy_from_slice(filled_chain_id.to_vec().as_slice()); + pos += CHAIN_ID_LENGTH as usize; + + output[pos..pos + HEIGHT_LENGTH as usize].copy_from_slice(&self.height.to_be_bytes()); + pos += HEIGHT_LENGTH as usize; + + output[pos..pos + VALIDATOR_SET_HASH_LENGTH as usize] + .copy_from_slice(self.next_validator_set_hash.as_ref()); + pos += VALIDATOR_SET_HASH_LENGTH as usize; + + for i in 0..validator_set_length { + let validator = &self.validators.validators()[i]; + let voting_power = validator.power(); + + output[pos..pos + VALIDATOR_PUBKEY_LENGTH as usize] + .copy_from_slice(&validator.pub_key.to_bytes()); + pos += VALIDATOR_PUBKEY_LENGTH as usize; + + output[pos..pos + VALIDATOR_VOTING_POWER_LENGTH as usize] + .copy_from_slice(&voting_power.to_be_bytes()); + pos += VALIDATOR_VOTING_POWER_LENGTH as usize; + + output[pos..pos + RELAYER_ADDRESS_LENGTH as usize] + .copy_from_slice(self.relayer_address[i].as_ref()); + pos += RELAYER_ADDRESS_LENGTH as usize; + + output[pos..pos + RELAYER_BLS_KEY_LENGTH as usize] + .copy_from_slice(self.relayer_bls_key[i].as_ref()); + pos += RELAYER_BLS_KEY_LENGTH as usize; + } + + Ok(Bytes::from(output)) + } +} + +type DecodeConsensusStateResult = Result; +/// input: +/// | chainID | height | nextValidatorSetHash | [{validator pubkey, voting power, relayer address, relayer bls pubkey}] | +/// | 32 bytes | 8 bytes | 32 bytes | [{32 bytes, 8 bytes, 20 bytes, 48 bytes}] +fn decode_consensus_state(input: &Bytes) -> DecodeConsensusStateResult { + let minimum_length = CHAIN_ID_LENGTH + HEIGHT_LENGTH + VALIDATOR_SET_HASH_LENGTH; + let input_length = input.len() as u64; + if input_length <= minimum_length + || (input_length - minimum_length) % SINGLE_VALIDATOR_BYTES_LENGTH != 0 + { + return Err(Error::CometBftInvalidInput); + } + + let mut pos = 0_u64; + let chain_id = &input[..CHAIN_ID_LENGTH as usize]; + let chain_id = String::from_utf8_lossy(chain_id); + let chain_id = chain_id.trim_end_matches('\0').to_owned(); + pos += CHAIN_ID_LENGTH; + + let height = u64::from_be_bytes( + input[pos as usize..(pos + HEIGHT_LENGTH) as usize] + .try_into() + .unwrap(), + ); + pos += HEIGHT_LENGTH; + + let next_validator_set_hash = + Bytes::from(input[pos as usize..(pos + VALIDATOR_SET_HASH_LENGTH) as usize].to_vec()); + pos += VALIDATOR_SET_HASH_LENGTH; + + let validator_set_length = (input_length - minimum_length) / SINGLE_VALIDATOR_BYTES_LENGTH; + let validator_set_bytes = input[pos as usize..].to_vec(); + let mut validator_set = Vec::with_capacity(validator_set_length as usize); + let mut relayer_address_set = Vec::with_capacity(validator_set_length as usize); + let mut relayer_bls_key_set = Vec::with_capacity(validator_set_length as usize); + for i in 0..validator_set_length { + let validator = &validator_set_bytes[i as usize * SINGLE_VALIDATOR_BYTES_LENGTH as usize + ..(i + 1) as usize * SINGLE_VALIDATOR_BYTES_LENGTH as usize]; + + let voting_power = u64::from_be_bytes( + validator[VALIDATOR_PUBKEY_LENGTH as usize + ..(VALIDATOR_PUBKEY_LENGTH + VALIDATOR_VOTING_POWER_LENGTH) as usize] + .try_into() + .unwrap(), + ); + let relayer_address = Bytes::from( + validator[(VALIDATOR_PUBKEY_LENGTH + VALIDATOR_VOTING_POWER_LENGTH) as usize + ..(VALIDATOR_PUBKEY_LENGTH + VALIDATOR_VOTING_POWER_LENGTH + RELAYER_ADDRESS_LENGTH) + as usize] + .to_vec(), + ); + let relayer_bls_key = Bytes::from( + validator[(VALIDATOR_PUBKEY_LENGTH + + VALIDATOR_VOTING_POWER_LENGTH + + RELAYER_ADDRESS_LENGTH) as usize..] + .to_vec(), + ); + let pk = match PublicKey::from_raw_ed25519(&validator[..VALIDATOR_PUBKEY_LENGTH as usize]) { + Some(pk) => pk, + None => return Err(Error::CometBftInvalidInput), + }; + let vp = Power::from(voting_power as u32); + let validator_info = Validator::new_with_bls_and_relayer( + pk, + vp, + relayer_bls_key.to_vec(), + relayer_address.to_vec(), + ); + validator_set.push(validator_info); + relayer_address_set.push(relayer_address); + relayer_bls_key_set.push(relayer_bls_key); + } + + Ok(ConsensusState::new( + chain_id, + height, + next_validator_set_hash, + ValidatorSet::without_proposer(validator_set), + relayer_address_set, + relayer_bls_key_set, + )) +} + +/// output: +/// | validatorSetChanged | empty | consensusStateBytesLength | new consensusState | +/// | 1 byte | 23 bytes | 8 bytes | | +fn encode_light_block_validation_result( + validator_set_changed: bool, + consensus_state_bytes: Bytes, +) -> Bytes { + let mut output = + vec![0; (VALIDATE_RESULT_METADATA_LENGTH + consensus_state_bytes.len() as u64) as usize]; + output[0] = if validator_set_changed { 1 } else { 0 }; + output[24..32].copy_from_slice(consensus_state_bytes.len().to_be_bytes().as_ref()); + output[32..].copy_from_slice(consensus_state_bytes.as_ref()); + Bytes::from(output) +} + +#[cfg(test)] +mod tests { + use super::*; + use revm_primitives::hex; + + #[test] + fn test_cometbft_light_block_validate() { + let input = Bytes::from(hex!( + "000000000000000000000000000000000000000000000000000000000000018c677265656e6669656c645f393030302d3132310000000000000000000000000000000000000000013c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb40e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e8423000000000098968015154514f68ce65a0d9eecc578c0ab12da0a2a28a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86451c5363d89052fde8351895eeea166ce5373c36e31b518ed191d0c599aa0f5b0000000000989680432f6c4908a9aa5f3444421f466b11645235c99b831b2a2de9e504d7ea299e52a202ce529808618eb3bfc0addf13d8c5f2df821d81e18f9bc61583510b322d067d46323b0a572635c06a049c0a2a929e3c8184a50cf6a8b95708c25834ade456f399015a0000000000989680864cb9828254d712f8e59b164fc6a9402dc4e6c59065e38cff24f5323c8c5da888a0f97e5ee4ba1e11b0674b0a0d06204c1dfa247c370cd4be3e799fc4f6f48d977ac7ca0aeb060adb030a02080b1213677265656e6669656c645f393030302d3132311802220c08b2d7f3a10610e8d2adb3032a480a20ec6ecb5db4ffb17fabe40c60ca7b8441e9c5d77585d0831186f3c37aa16e9c15122408011220a2ab9e1eb9ea52812f413526e424b326aff2f258a56e00d690db9f805b60fe7e32200f40aeff672e8309b7b0aefbb9a1ae3d4299b5c445b7d54e8ff398488467f0053a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85542203c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb404a203c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb405220294d8fbd0b94b767a7eba9840f299a3586da7fe6b5dead3b7eecba193c400f935a20bc50557c12d7392b0d07d75df0b61232d48f86a74fdea6d1485d9be6317d268c6220e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8556a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85572146699336aa109d1beab3946198c8e59f3b2cbd92f7a4065e3cd89e315ca39d87dee92835b98f8b8ec0861d6d9bb2c60156df5d375b3ceb1fbe71af6a244907d62548a694165caa660fec7a9b4e7b9198191361c71be0b128a0308021a480a20726abd0fdbfb6f779b0483e6e4b4b6f12241f6ea2bf374233ab1a316692b6415122408011220159f10ff15a8b58fc67a92ffd7f33c8cd407d4ce81b04ca79177dfd00ca19a67226808021214050cff76cc632760ba9db796c046004c900967361a0c08b3d7f3a10610808cadba03224080713027ffb776a702d78fd0406205c629ba473e1f8d6af646190f6eb9262cd67d69be90d10e597b91e06d7298eb6fa4b8f1eb7752ebf352a1f51560294548042268080212146699336aa109d1beab3946198c8e59f3b2cbd92f1a0c08b3d7f3a10610b087c1c00322405e2ddb70acfe4904438be3d9f4206c0ace905ac4fc306a42cfc9e86268950a0fbfd6ec5f526d3e41a3ef52bf9f9f358e3cb4c3feac76c762fa3651c1244fe004226808021214c55765fd2d0570e869f6ac22e7f2916a35ea300d1a0c08b3d7f3a10610f0b3d492032240ca17898bd22232fc9374e1188636ee321a396444a5b1a79f7628e4a11f265734b2ab50caf21e8092c55d701248e82b2f011426cb35ba22043b497a6b4661930612a0050aa8010a14050cff76cc632760ba9db796c046004c9009673612220a20e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e84231880ade2042080a6bbf6ffffffffff012a30a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86321415154514f68ce65a0d9eecc578c0ab12da0a2a283a14ee7a2a6a44d427f6949eeb8f12ea9fbb2501da880aa2010a146699336aa109d1beab3946198c8e59f3b2cbd92f12220a20451c5363d89052fde8351895eeea166ce5373c36e31b518ed191d0c599aa0f5b1880ade2042080ade2042a30831b2a2de9e504d7ea299e52a202ce529808618eb3bfc0addf13d8c5f2df821d81e18f9bc61583510b322d067d46323b3214432f6c4908a9aa5f3444421f466b11645235c99b3a14a0a7769429468054e19059af4867da0a495567e50aa2010a14c55765fd2d0570e869f6ac22e7f2916a35ea300d12220a200a572635c06a049c0a2a929e3c8184a50cf6a8b95708c25834ade456f399015a1880ade2042080ade2042a309065e38cff24f5323c8c5da888a0f97e5ee4ba1e11b0674b0a0d06204c1dfa247c370cd4be3e799fc4f6f48d977ac7ca3214864cb9828254d712f8e59b164fc6a9402dc4e6c53a143139916d97df0c589312b89950b6ab9795f34d1a12a8010a14050cff76cc632760ba9db796c046004c9009673612220a20e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e84231880ade2042080a6bbf6ffffffffff012a30a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86321415154514f68ce65a0d9eecc578c0ab12da0a2a283a14ee7a2a6a44d427f6949eeb8f12ea9fbb2501da88" + )); + let except_output = Bytes::from(hex!( + "000000000000000000000000000000000000000000000000000000000000018c677265656e6669656c645f393030302d3132310000000000000000000000000000000000000000023c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb40e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e8423000000000098968015154514f68ce65a0d9eecc578c0ab12da0a2a28a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86451c5363d89052fde8351895eeea166ce5373c36e31b518ed191d0c599aa0f5b0000000000989680432f6c4908a9aa5f3444421f466b11645235c99b831b2a2de9e504d7ea299e52a202ce529808618eb3bfc0addf13d8c5f2df821d81e18f9bc61583510b322d067d46323b0a572635c06a049c0a2a929e3c8184a50cf6a8b95708c25834ade456f399015a0000000000989680864cb9828254d712f8e59b164fc6a9402dc4e6c59065e38cff24f5323c8c5da888a0f97e5ee4ba1e11b0674b0a0d06204c1dfa247c370cd4be3e799fc4f6f48d977ac7ca" + )); + + let result = cometbft_light_block_validation_run(&input, 100_000); + let (gas_used, output) = match result { + Ok(result) => result, + Err(_) => panic!("cometbft_light_block_validation_run failed"), + }; + assert_eq!(gas_used, 3_000); + assert_eq!(output, except_output); + } + + #[test] + fn test_encode_consensus_state() { + { + let chain_id = "chain_9000-121".to_string(); + let height = 1; + let next_validator_set_hash = Bytes::from(hex!( + "0CE856B1DC9CDCF3BF2478291CF02C62AEEB3679889E9866931BF1FB05A10EDA" + )); + let mut validators_info = Vec::new(); + validators_info.push(cometbft::validator::Info::new( + PublicKey::from_raw_ed25519(&hex!( + "c3d9a1082f42ca161402f8668f8e39ec9e30092affd8d3262267ac7e248a959e" + )) + .unwrap(), + cometbft::vote::Power::from(10000_u32), + )); + let validator_set = ValidatorSet::without_proposer(validators_info); + let bls_pub_key = Bytes::from(hex!("a60afe627fd78b19e07e07e19d446009dd53a18c6c8744176a5d851a762bbb51198e7e006f2a6ea7225661a61ecd832d")); + let relayer_address = Bytes::from(hex!("B32d0723583040F3A16D1380D1e6AA874cD1bdF7")); + let cs = ConsensusState::new( + chain_id, + height, + next_validator_set_hash, + validator_set, + vec![relayer_address.clone()], + vec![bls_pub_key.clone()], + ); + + let expected_output = Bytes::from(hex!("636861696e5f393030302d31323100000000000000000000000000000000000000000000000000010ce856b1dc9cdcf3bf2478291cf02c62aeeb3679889e9866931bf1fb05a10edac3d9a1082f42ca161402f8668f8e39ec9e30092affd8d3262267ac7e248a959e0000000000002710b32d0723583040f3a16d1380d1e6aa874cd1bdf7a60afe627fd78b19e07e07e19d446009dd53a18c6c8744176a5d851a762bbb51198e7e006f2a6ea7225661a61ecd832d")); + let cs_bytes = cs.encode().unwrap(); + assert_eq!(cs_bytes, expected_output); + } + { + let chain_id = "chain_9000-121".to_string(); + let height = 1; + let next_validator_set_hash = Bytes::from(hex!( + "A5F1AF4874227F1CDBE5240259A365AD86484A4255BFD65E2A0222D733FCDBC3" + )); + let mut validators_info = Vec::new(); + let mut bls_pub_keys = Vec::new(); + let mut relayer_addresses = Vec::new(); + validators_info.push(cometbft::validator::Info::new( + PublicKey::from_raw_ed25519(&hex!( + "20cc466ee9412ddd49e0fff04cdb41bade2b7622f08b6bdacac94d4de03bdb97" + )) + .unwrap(), + cometbft::vote::Power::from(10000_u32), + )); + bls_pub_keys.push(Bytes::from(hex!("aa2d28cbcd1ea3a63479f6fb260a3d755853e6a78cfa6252584fee97b2ec84a9d572ee4a5d3bc1558bb98a4b370fb861"))); + relayer_addresses.push(Bytes::from(hex!( + "d5e63aeee6e6fa122a6a23a6e0fca87701ba1541" + ))); + validators_info.push(cometbft::validator::Info::new( + PublicKey::from_raw_ed25519(&hex!( + "6b0b523ee91ad18a63d63f21e0c40a83ef15963f4260574ca5159fd90a1c5270" + )) + .unwrap(), + cometbft::vote::Power::from(10000_u32), + )); + bls_pub_keys.push(Bytes::from(hex!("b31e74a881fc78681e3dfa440978d2b8be0708a1cbbca2c660866216975fdaf0e9038d9b7ccbf9731f43956dba7f2451"))); + relayer_addresses.push(Bytes::from(hex!( + "6fd1ceb5a48579f322605220d4325bd9ff90d5fa" + ))); + validators_info.push(cometbft::validator::Info::new( + PublicKey::from_raw_ed25519(&hex!( + "919606ae20bf5d248ee353821754bcdb456fd3950618fda3e32d3d0fb990eeda" + )) + .unwrap(), + cometbft::vote::Power::from(10000_u32), + )); + bls_pub_keys.push(Bytes::from(hex!("b32979580ea04984a2be033599c20c7a0c9a8d121b57f94ee05f5eda5b36c38f6e354c89328b92cdd1de33b64d3a0867"))); + relayer_addresses.push(Bytes::from(hex!( + "97376a436bbf54e0f6949b57aa821a90a749920a" + ))); + let validator_set = ValidatorSet::without_proposer(validators_info); + let cs = ConsensusState::new( + chain_id, + height, + next_validator_set_hash, + validator_set, + relayer_addresses, + bls_pub_keys, + ); + + let expected_output = Bytes::from(hex!("636861696e5f393030302d3132310000000000000000000000000000000000000000000000000001a5f1af4874227f1cdbe5240259a365ad86484a4255bfd65e2a0222d733fcdbc320cc466ee9412ddd49e0fff04cdb41bade2b7622f08b6bdacac94d4de03bdb970000000000002710d5e63aeee6e6fa122a6a23a6e0fca87701ba1541aa2d28cbcd1ea3a63479f6fb260a3d755853e6a78cfa6252584fee97b2ec84a9d572ee4a5d3bc1558bb98a4b370fb8616b0b523ee91ad18a63d63f21e0c40a83ef15963f4260574ca5159fd90a1c527000000000000027106fd1ceb5a48579f322605220d4325bd9ff90d5fab31e74a881fc78681e3dfa440978d2b8be0708a1cbbca2c660866216975fdaf0e9038d9b7ccbf9731f43956dba7f2451919606ae20bf5d248ee353821754bcdb456fd3950618fda3e32d3d0fb990eeda000000000000271097376a436bbf54e0f6949b57aa821a90a749920ab32979580ea04984a2be033599c20c7a0c9a8d121b57f94ee05f5eda5b36c38f6e354c89328b92cdd1de33b64d3a0867")); + let cs_bytes = cs.encode().unwrap(); + assert_eq!(cs_bytes, expected_output); + } + } + + #[test] + fn test_decode_consensus_state() { + { + let chain_id = "chain_9000-121".to_string(); + let height = 1; + let next_validator_set_hash = Bytes::from(hex!( + "0CE856B1DC9CDCF3BF2478291CF02C62AEEB3679889E9866931BF1FB05A10EDA" + )); + let mut validators_info = Vec::new(); + validators_info.push(Validator::new_with_bls_and_relayer( + PublicKey::from_raw_ed25519(&hex!( + "c3d9a1082f42ca161402f8668f8e39ec9e30092affd8d3262267ac7e248a959e" + )) + .unwrap(), + cometbft::vote::Power::from(10000_u32), + Bytes::from(hex!("a60afe627fd78b19e07e07e19d446009dd53a18c6c8744176a5d851a762bbb51198e7e006f2a6ea7225661a61ecd832d")).to_vec(), + Bytes::from(hex!("B32d0723583040F3A16D1380D1e6AA874cD1bdF7")).to_vec(), + )); + let validator_set = ValidatorSet::without_proposer(validators_info); + let bls_pub_key = Bytes::from(hex!("a60afe627fd78b19e07e07e19d446009dd53a18c6c8744176a5d851a762bbb51198e7e006f2a6ea7225661a61ecd832d")); + let relayer_address = Bytes::from(hex!("B32d0723583040F3A16D1380D1e6AA874cD1bdF7")); + let cs_bytes = Bytes::from(hex!("636861696e5f393030302d31323100000000000000000000000000000000000000000000000000010ce856b1dc9cdcf3bf2478291cf02c62aeeb3679889e9866931bf1fb05a10edac3d9a1082f42ca161402f8668f8e39ec9e30092affd8d3262267ac7e248a959e0000000000002710b32d0723583040f3a16d1380d1e6aa874cd1bdf7a60afe627fd78b19e07e07e19d446009dd53a18c6c8744176a5d851a762bbb51198e7e006f2a6ea7225661a61ecd832d")); + let cs = match decode_consensus_state(&cs_bytes) { + Ok(cs) => cs, + Err(_) => panic!("decode consensus state failed"), + }; + assert_eq!(cs.chain_id, chain_id); + assert_eq!(cs.height, height); + assert_eq!(cs.next_validator_set_hash, next_validator_set_hash); + assert_eq!(cs.validators, validator_set); + assert_eq!(cs.relayer_address[0], relayer_address); + assert_eq!(cs.relayer_bls_key[0], bls_pub_key); + } + { + let chain_id = "chain_9000-121".to_string(); + let height = 1; + let next_validator_set_hash = Bytes::from(hex!( + "A5F1AF4874227F1CDBE5240259A365AD86484A4255BFD65E2A0222D733FCDBC3" + )); + let mut validators_info = Vec::new(); + let mut bls_pub_keys = Vec::new(); + let mut relayer_addresses = Vec::new(); + validators_info.push(Validator::new_with_bls_and_relayer( + PublicKey::from_raw_ed25519(&hex!( + "20cc466ee9412ddd49e0fff04cdb41bade2b7622f08b6bdacac94d4de03bdb97" + )) + .unwrap(), + cometbft::vote::Power::from(10000_u32), + Bytes::from(hex!("aa2d28cbcd1ea3a63479f6fb260a3d755853e6a78cfa6252584fee97b2ec84a9d572ee4a5d3bc1558bb98a4b370fb861")).to_vec(), + Bytes::from(hex!("d5e63aeee6e6fa122a6a23a6e0fca87701ba1541")).to_vec(), + )); + bls_pub_keys.push(Bytes::from(hex!("aa2d28cbcd1ea3a63479f6fb260a3d755853e6a78cfa6252584fee97b2ec84a9d572ee4a5d3bc1558bb98a4b370fb861"))); + relayer_addresses.push(Bytes::from(hex!( + "d5e63aeee6e6fa122a6a23a6e0fca87701ba1541" + ))); + validators_info.push(Validator::new_with_bls_and_relayer( + PublicKey::from_raw_ed25519(&hex!( + "6b0b523ee91ad18a63d63f21e0c40a83ef15963f4260574ca5159fd90a1c5270" + )) + .unwrap(), + cometbft::vote::Power::from(10000_u32), + Bytes::from(hex!("b31e74a881fc78681e3dfa440978d2b8be0708a1cbbca2c660866216975fdaf0e9038d9b7ccbf9731f43956dba7f2451")).to_vec(), + Bytes::from(hex!("6fd1ceb5a48579f322605220d4325bd9ff90d5fa")).to_vec(), + )); + bls_pub_keys.push(Bytes::from(hex!("b31e74a881fc78681e3dfa440978d2b8be0708a1cbbca2c660866216975fdaf0e9038d9b7ccbf9731f43956dba7f2451"))); + relayer_addresses.push(Bytes::from(hex!( + "6fd1ceb5a48579f322605220d4325bd9ff90d5fa" + ))); + validators_info.push(Validator::new_with_bls_and_relayer( + PublicKey::from_raw_ed25519(&hex!( + "919606ae20bf5d248ee353821754bcdb456fd3950618fda3e32d3d0fb990eeda" + )) + .unwrap(), + cometbft::vote::Power::from(10000_u32), + Bytes::from(hex!("b32979580ea04984a2be033599c20c7a0c9a8d121b57f94ee05f5eda5b36c38f6e354c89328b92cdd1de33b64d3a0867")).to_vec(), + Bytes::from(hex!("97376a436bbf54e0f6949b57aa821a90a749920a")).to_vec(), + )); + bls_pub_keys.push(Bytes::from(hex!("b32979580ea04984a2be033599c20c7a0c9a8d121b57f94ee05f5eda5b36c38f6e354c89328b92cdd1de33b64d3a0867"))); + relayer_addresses.push(Bytes::from(hex!( + "97376a436bbf54e0f6949b57aa821a90a749920a" + ))); + let validator_set = ValidatorSet::without_proposer(validators_info); + let cs_bytes = Bytes::from(hex!("636861696e5f393030302d3132310000000000000000000000000000000000000000000000000001a5f1af4874227f1cdbe5240259a365ad86484a4255bfd65e2a0222d733fcdbc320cc466ee9412ddd49e0fff04cdb41bade2b7622f08b6bdacac94d4de03bdb970000000000002710d5e63aeee6e6fa122a6a23a6e0fca87701ba1541aa2d28cbcd1ea3a63479f6fb260a3d755853e6a78cfa6252584fee97b2ec84a9d572ee4a5d3bc1558bb98a4b370fb8616b0b523ee91ad18a63d63f21e0c40a83ef15963f4260574ca5159fd90a1c527000000000000027106fd1ceb5a48579f322605220d4325bd9ff90d5fab31e74a881fc78681e3dfa440978d2b8be0708a1cbbca2c660866216975fdaf0e9038d9b7ccbf9731f43956dba7f2451919606ae20bf5d248ee353821754bcdb456fd3950618fda3e32d3d0fb990eeda000000000000271097376a436bbf54e0f6949b57aa821a90a749920ab32979580ea04984a2be033599c20c7a0c9a8d121b57f94ee05f5eda5b36c38f6e354c89328b92cdd1de33b64d3a0867")); + let cs = match decode_consensus_state(&cs_bytes) { + Ok(cs) => cs, + Err(_) => panic!("decode consensus state failed"), + }; + + assert_eq!(cs.chain_id, chain_id); + assert_eq!(cs.height, height); + assert_eq!(cs.next_validator_set_hash, next_validator_set_hash); + assert_eq!(cs.validators, validator_set); + assert_eq!(cs.relayer_address[0], relayer_addresses[0]); + assert_eq!(cs.relayer_bls_key[0], bls_pub_keys[0]); + assert_eq!(cs.relayer_address[1], relayer_addresses[1]); + assert_eq!(cs.relayer_bls_key[1], bls_pub_keys[1]); + assert_eq!(cs.relayer_address[2], relayer_addresses[2]); + assert_eq!(cs.relayer_bls_key[2], bls_pub_keys[2]); + } + } + + #[test] + fn test_apply_light_block() { + { + let cs_bytes = Bytes::from(hex!("677265656e6669656c645f393030302d3132310000000000000000000000000000000000000000013c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb40e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e8423000000000098968015154514f68ce65a0d9eecc578c0ab12da0a2a28a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86451c5363d89052fde8351895eeea166ce5373c36e31b518ed191d0c599aa0f5b0000000000989680432f6c4908a9aa5f3444421f466b11645235c99b831b2a2de9e504d7ea299e52a202ce529808618eb3bfc0addf13d8c5f2df821d81e18f9bc61583510b322d067d46323b0a572635c06a049c0a2a929e3c8184a50cf6a8b95708c25834ade456f399015a0000000000989680864cb9828254d712f8e59b164fc6a9402dc4e6c59065e38cff24f5323c8c5da888a0f97e5ee4ba1e11b0674b0a0d06204c1dfa247c370cd4be3e799fc4f6f48d977ac7ca")); + let mut cs = match decode_consensus_state(&cs_bytes) { + Ok(cs) => cs, + Err(_) => panic!("decode consensus state failed"), + }; + let light_block_bytes = Bytes::from(hex!("0aeb060adb030a02080b1213677265656e6669656c645f393030302d3132311802220c08b2d7f3a10610e8d2adb3032a480a20ec6ecb5db4ffb17fabe40c60ca7b8441e9c5d77585d0831186f3c37aa16e9c15122408011220a2ab9e1eb9ea52812f413526e424b326aff2f258a56e00d690db9f805b60fe7e32200f40aeff672e8309b7b0aefbb9a1ae3d4299b5c445b7d54e8ff398488467f0053a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85542203c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb404a203c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb405220294d8fbd0b94b767a7eba9840f299a3586da7fe6b5dead3b7eecba193c400f935a20bc50557c12d7392b0d07d75df0b61232d48f86a74fdea6d1485d9be6317d268c6220e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8556a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85572146699336aa109d1beab3946198c8e59f3b2cbd92f7a4065e3cd89e315ca39d87dee92835b98f8b8ec0861d6d9bb2c60156df5d375b3ceb1fbe71af6a244907d62548a694165caa660fec7a9b4e7b9198191361c71be0b128a0308021a480a20726abd0fdbfb6f779b0483e6e4b4b6f12241f6ea2bf374233ab1a316692b6415122408011220159f10ff15a8b58fc67a92ffd7f33c8cd407d4ce81b04ca79177dfd00ca19a67226808021214050cff76cc632760ba9db796c046004c900967361a0c08b3d7f3a10610808cadba03224080713027ffb776a702d78fd0406205c629ba473e1f8d6af646190f6eb9262cd67d69be90d10e597b91e06d7298eb6fa4b8f1eb7752ebf352a1f51560294548042268080212146699336aa109d1beab3946198c8e59f3b2cbd92f1a0c08b3d7f3a10610b087c1c00322405e2ddb70acfe4904438be3d9f4206c0ace905ac4fc306a42cfc9e86268950a0fbfd6ec5f526d3e41a3ef52bf9f9f358e3cb4c3feac76c762fa3651c1244fe004226808021214c55765fd2d0570e869f6ac22e7f2916a35ea300d1a0c08b3d7f3a10610f0b3d492032240ca17898bd22232fc9374e1188636ee321a396444a5b1a79f7628e4a11f265734b2ab50caf21e8092c55d701248e82b2f011426cb35ba22043b497a6b4661930612a0050aa8010a14050cff76cc632760ba9db796c046004c9009673612220a20e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e84231880ade2042080a6bbf6ffffffffff012a30a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86321415154514f68ce65a0d9eecc578c0ab12da0a2a283a14ee7a2a6a44d427f6949eeb8f12ea9fbb2501da880aa2010a146699336aa109d1beab3946198c8e59f3b2cbd92f12220a20451c5363d89052fde8351895eeea166ce5373c36e31b518ed191d0c599aa0f5b1880ade2042080ade2042a30831b2a2de9e504d7ea299e52a202ce529808618eb3bfc0addf13d8c5f2df821d81e18f9bc61583510b322d067d46323b3214432f6c4908a9aa5f3444421f466b11645235c99b3a14a0a7769429468054e19059af4867da0a495567e50aa2010a14c55765fd2d0570e869f6ac22e7f2916a35ea300d12220a200a572635c06a049c0a2a929e3c8184a50cf6a8b95708c25834ade456f399015a1880ade2042080ade2042a309065e38cff24f5323c8c5da888a0f97e5ee4ba1e11b0674b0a0d06204c1dfa247c370cd4be3e799fc4f6f48d977ac7ca3214864cb9828254d712f8e59b164fc6a9402dc4e6c53a143139916d97df0c589312b89950b6ab9795f34d1a12a8010a14050cff76cc632760ba9db796c046004c9009673612220a20e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e84231880ade2042080a6bbf6ffffffffff012a30a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86321415154514f68ce65a0d9eecc578c0ab12da0a2a283a14ee7a2a6a44d427f6949eeb8f12ea9fbb2501da88")); + let mut light_block_pb: TmLightBlock = TmLightBlock::default(); + match light_block_pb.merge(light_block_bytes) { + Ok(_) => (), + Err(_) => panic!("merge light block failed"), + }; + let light_block = match convert_light_block_from_proto(&light_block_pb) { + Ok(light_block) => light_block, + Err(_) => panic!("convert light block from proto failed"), + }; + let expected_height = 2_u64; + let expected_validator_set_changed = false; + + match cs.apply_light_block(&light_block) { + Ok(validator_set_changed) => { + assert_eq!(validator_set_changed, expected_validator_set_changed); + assert_eq!(cs.height, expected_height); + } + Err(_) => panic!("apply light block failed"), + } + } + { + let cs_bytes = Bytes::from(hex!("677265656e6669656c645f393030302d313734310000000000000000000000000000000000000001af6b801dda578dddfa4da1d5d67fd1b32510db24ec271346fc573e9242b01c9a112b51dda2d336246bdc0cc51407ba0cb0e5087be0db5f1cdc3285bbaa8e647500000000000003e84202722cf6a34d727be762b46825b0d26b6263a0a9355ebf3c24bedac5a357a56feeb2cd8b6fed9f14cca15c3091f523b9fb21183b4bb31eb482a0321885e3f57072156448e2b2f7d9a3e7b668757d9cc0bbd28cd674c34ed1c2ed75c5de3b6a8f8cad4600000000000003e8668a0acd8f6db5cae959a0e02132f4d6a672c4d7a4726b542012cc8023ee07b29ab3971cc999d8751bbd16f23413968afcdb070ed66ab47e6e1842bf875bef21dfc5b8af6813bfd82860d361e339bd1ae2f801b6d6ee46b8497a3d51c80b50b6160ea1cc00000000000003e80dfa99423d3084c596c5e3bd6bcb4f654516517b8d4786703c56b300b70f085c0d0482e5d6a3c7208883f0ec8abd2de893f71d18e8f919e7ab198499201d87f92c57ebce83ed2b763bb872e9bc148fb216fd5c93b18819670d9a946ae4b3075672d726b800000000000003e824aab6f85470ff73e3048c64083a09e980d4cb7f8146d231a7b2051c5f7a9c07ab6e6bfe277bd5f4a94f901fe6ee7a6b6bd8479e9e5e448de4b1b33d5ddd74194c86b3852cc140a3f08a9c4149efd45643202f8bef2ad7eecf53e58951c6df6fd932004b00000000000003e84998f6ef8d999a0f36a851bfa29dbcf0364dd65695c286deb3f1657664859d59876bf1ec5a288f6e66e18b37b8a2a1e6ee4a3ef8fa50784d8b758d0c3e70a7cdfe65ab5d")); + let mut cs = match decode_consensus_state(&cs_bytes) { + Ok(cs) => cs, + Err(_) => panic!("decode consensus state failed"), + }; + let light_block_bytes = Bytes::from(hex!("0aeb070ade030a02080b1214677265656e6669656c645f393030302d3137343118e9d810220c08f2f2b6a30610af9fcc8e022a480a20315130cf3a10f78c5f7633e3941f605151a6901910713c84da0d7929898e9b9e122408011220f09b2290e56b59a7286c2144a811c780f0fd5f631614a9f7ec2dec43f14ac5d63220d15354fdbcc6c7d3e8c5ede34f4f71e896599ba67773605eb6579e10e09254773a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8554220311b22582926e7833b72904605441ed602896e8aeb093bca5f2e8170cea5ed6a4a20311b22582926e7833b72904605441ed602896e8aeb093bca5f2e8170cea5ed6a5220048091bc7ddc283f77bfbf91d73c44da58c3df8a9cbc867405d8b7f3daada22f5a20ee2da802b95c55e551291d96fe6ee4fe8074ddfa2df110042d6809acb665628a6220e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8556a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214793cee4b478e537592c40ecfb2148ebe32b8f6057a4034248b04af30e0d302cf8cedff585d5e1c6ff8db526bcf298d665cf301ca938a874c76ba9a1fd9fae302b2ec49a335930cf0242762c92843d7f9f7963d60580a12870408e9d8101a480a20452e1984f64c79550ac23db0c408b3eb021675d678ad94f9206ad7a2dec83a181224080112205224c29260b6c220685b29f593bac728e522e3e3675ec7edd92c12251acfe4b4226808021214d742fa5318dc3986e075e2b050529a22c6fa3b8b1a0c08f4f2b6a306109898f6a70322409762b7abd4dd63bb8858673dffd5795b1a87532d3719458d12fbbd1fd2443ca76bd36c4c09fa8952a440de4904f1b6b9270037a147431892c8ace96ad43bf90b2268080212145fa8b3f3fcd4a3ea2495e11dd5dbd399b3d8d4f81a0c08f4f2b6a30610f8f2fd9e03224093f2fc21a41492a34ed3b31ff2eba571ca752ae989f2e47728740bb1eec0f20eb59f59d390ce3d67734ab49a72bc2e97e185d21a4b00f3288ea50b0f1383220a226808021214793cee4b478e537592c40ecfb2148ebe32b8f6051a0c08f4f2b6a306108e8ed7a7032240a4a3c047ca75aeb6e9a21fbc3742f4339c64ead15d117675a2757f7db965aae3e6901f81a3707a67d91c61d6c842b95009e132e7fab187965dc04861d7faa902226808021214f0f07dc2f5e159a35b9662553c6b4e51868502f71a0c08f4f2b6a30610bfed829f032240e23ddc98b0bf7cc6cd494fd8ec96d440d29193910a6eca3dc7e41cdb14efa32471feb1ea2d613bb5acdd8623e8372ed3a36e1838bc75646bdfe9d2ef96647400220f08011a0b088092b8c398feffffff0112d0060a90010a14d742fa5318dc3986e075e2b050529a22c6fa3b8b12220a2083ed2b763bb872e9bc148fb216fd5c93b18819670d9a946ae4b3075672d726b818880820abe8ffffffffffffff012a308146d231a7b2051c5f7a9c07ab6e6bfe277bd5f4a94f901fe6ee7a6b6bd8479e9e5e448de4b1b33d5ddd74194c86b385321424aab6f85470ff73e3048c64083a09e980d4cb7f0a88010a145fa8b3f3fcd4a3ea2495e11dd5dbd399b3d8d4f812220a2048e2b2f7d9a3e7b668757d9cc0bbd28cd674c34ed1c2ed75c5de3b6a8f8cad4618fc0720fc072a30a4726b542012cc8023ee07b29ab3971cc999d8751bbd16f23413968afcdb070ed66ab47e6e1842bf875bef21dfc5b8af3214668a0acd8f6db5cae959a0e02132f4d6a672c4d70a88010a14793cee4b478e537592c40ecfb2148ebe32b8f60512220a206813bfd82860d361e339bd1ae2f801b6d6ee46b8497a3d51c80b50b6160ea1cc18ec0720ec072a308d4786703c56b300b70f085c0d0482e5d6a3c7208883f0ec8abd2de893f71d18e8f919e7ab198499201d87f92c57ebce32140dfa99423d3084c596c5e3bd6bcb4f654516517b0a88010a14f0f07dc2f5e159a35b9662553c6b4e51868502f712220a202cc140a3f08a9c4149efd45643202f8bef2ad7eecf53e58951c6df6fd932004b18ec0720ec072a3095c286deb3f1657664859d59876bf1ec5a288f6e66e18b37b8a2a1e6ee4a3ef8fa50784d8b758d0c3e70a7cdfe65ab5d32144998f6ef8d999a0f36a851bfa29dbcf0364dd6560a86010a1468478c1a37bc01c3acb7470cc6a78f1009a14f7012220a20de83e10566b038855254800b5b0ebf7c21aede9883c11e5cf289979e233b3efe180120012a3089063607696a9e6dbddbe6c23b4634a7c02b80212afc7ec65fb0d379d55d2d0cb25df19c0252356ffa2e2252eedd8f57321400000000000000000000000000000000000000001290010a14d742fa5318dc3986e075e2b050529a22c6fa3b8b12220a2083ed2b763bb872e9bc148fb216fd5c93b18819670d9a946ae4b3075672d726b818880820abe8ffffffffffffff012a308146d231a7b2051c5f7a9c07ab6e6bfe277bd5f4a94f901fe6ee7a6b6bd8479e9e5e448de4b1b33d5ddd74194c86b385321424aab6f85470ff73e3048c64083a09e980d4cb7f")); + let mut light_block_pb: TmLightBlock = TmLightBlock::default(); + match light_block_pb.merge(light_block_bytes) { + Ok(_) => (), + Err(_) => panic!("merge light block failed"), + }; + let light_block = match convert_light_block_from_proto(&light_block_pb) { + Ok(light_block) => light_block, + Err(_) => panic!("convert light block from proto failed"), + }; + let expected_height = 273513_u64; + let expected_validator_set_changed = true; + + match cs.apply_light_block(&light_block) { + Ok(validator_set_changed) => { + assert_eq!(validator_set_changed, expected_validator_set_changed); + assert_eq!(cs.height, expected_height); + } + Err(_) => panic!("apply light block failed"), + } + } + } +} diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index ca797c81..ef8c04c2 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -11,7 +11,9 @@ extern crate alloc as std; pub mod blake2; #[cfg(feature = "blst")] pub mod bls12_381; +mod bls; pub mod bn128; +mod cometbft; pub mod hash; pub mod identity; #[cfg(feature = "c-kzg")] @@ -66,6 +68,7 @@ impl Precompiles { PrecompileSpecId::BYZANTIUM => Self::byzantium(), PrecompileSpecId::ISTANBUL => Self::istanbul(), PrecompileSpecId::BERLIN => Self::berlin(), + PrecompileSpecId::FERMAT => Self::fermat(), PrecompileSpecId::CANCUN => Self::cancun(), PrecompileSpecId::PRAGUE => Self::prague(), PrecompileSpecId::LATEST => Self::latest(), @@ -135,6 +138,26 @@ impl Precompiles { }) } + /// Returns precompiles for Fermat spec. + /// + /// effectively making this the same as Berlin. + pub fn fermat() -> &'static Self { + static INSTANCE: OnceBox = OnceBox::new(); + INSTANCE.get_or_init(|| { + let precompiles = Self::berlin().clone(); + let precompiles = { + let mut precompiles = precompiles; + precompiles.extend([ + bls::BLS_SIGNATURE_VALIDATION, + cometbft::COMETBFT_LIGHT_BLOCK_VALIDATION, + ]); + precompiles + }; + + Box::new(precompiles) + }) + } + /// Returns precompiles for Cancun spec. /// /// If the `c-kzg` feature is not enabled KZG Point Evaluation precompile will not be included, @@ -151,6 +174,10 @@ impl Precompiles { precompiles.extend([ // EIP-4844: Shard Blob Transactions kzg_point_evaluation::POINT_EVALUATION, + #[cfg(feature = "opbnb")] + bls::BLS_SIGNATURE_VALIDATION, + #[cfg(feature = "opbnb")] + cometbft::COMETBFT_LIGHT_BLOCK_VALIDATION, ]); precompiles }; @@ -251,6 +278,7 @@ pub enum PrecompileSpecId { BYZANTIUM, ISTANBUL, BERLIN, + FERMAT, CANCUN, PRAGUE, LATEST, @@ -274,6 +302,8 @@ impl PrecompileSpecId { BEDROCK | REGOLITH | CANYON => Self::BERLIN, #[cfg(feature = "optimism")] ECOTONE | FJORD => Self::CANCUN, + #[cfg(feature = "opbnb")] + FERMAT => Self::FERMAT, } } } diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index a9d009d2..ee77dd34 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -97,3 +97,7 @@ rand = ["alloy-primitives/rand"] # See comments in `revm-precompile` c-kzg = ["dep:c-kzg", "dep:once_cell", "dep:derive_more"] + +opbnb = [] +opbnb-default-handler = ["opbnb"] +negate-opbnb-default-handler = [] \ No newline at end of file diff --git a/crates/primitives/src/precompile.rs b/crates/primitives/src/precompile.rs index e5853775..a2a6107d 100644 --- a/crates/primitives/src/precompile.rs +++ b/crates/primitives/src/precompile.rs @@ -125,6 +125,16 @@ pub enum PrecompileError { BlobMismatchedVersion, /// The proof verification failed. BlobVerifyKzgProofFailed, + /// The input length is not matching the expected length. + BLSInvalidInputLength, + /// The bls signature is invalid. + BLSInvalidSignature, + /// The bls public key is invalid. + BLSInvalidPublicKey, + /// The cometbft validation input is invalid. + CometBftInvalidInput, + /// The cometbft consensus state encoding failed. + CometBftEncodeConsensusStateFailed, /// Catch-all variant for other errors. Other(String), } @@ -153,6 +163,11 @@ impl fmt::Display for PrecompileError { Self::BlobInvalidInputLength => "invalid blob input length", Self::BlobMismatchedVersion => "mismatched blob version", Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed", + Self::BLSInvalidInputLength => "invalid input length for BLS", + Self::BLSInvalidSignature => "invalid BLS signature", + Self::BLSInvalidPublicKey => "invalid BLS public key", + Self::CometBftInvalidInput => "invalid cometbft light block validation input", + Self::CometBftEncodeConsensusStateFailed => "failed to encode cometbft consensus state", Self::Other(s) => s, }; f.write_str(s) diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index d7c1d3e6..5b193fe0 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -59,12 +59,13 @@ pub enum SpecId { MERGE = 15, BEDROCK = 16, REGOLITH = 17, - SHANGHAI = 18, - CANYON = 19, - CANCUN = 20, - ECOTONE = 21, - FJORD = 22, - PRAGUE = 23, + FERMAT = 18, + SHANGHAI = 19, + CANYON = 20, + CANCUN = 21, + ECOTONE = 22, + FJORD = 23, + PRAGUE = 24, #[default] LATEST = u8::MAX, } @@ -111,6 +112,8 @@ impl From<&str> for SpecId { "Bedrock" => SpecId::BEDROCK, #[cfg(feature = "optimism")] "Regolith" => SpecId::REGOLITH, + #[cfg(feature = "opbnb")] + "Fermat" => SpecId::FERMAT, #[cfg(feature = "optimism")] "Canyon" => SpecId::CANYON, #[cfg(feature = "optimism")] @@ -148,6 +151,8 @@ impl From for &'static str { SpecId::BEDROCK => "Bedrock", #[cfg(feature = "optimism")] SpecId::REGOLITH => "Regolith", + #[cfg(feature = "opbnb")] + SpecId::FERMAT => "Fermat", #[cfg(feature = "optimism")] SpecId::CANYON => "Canyon", #[cfg(feature = "optimism")] @@ -214,6 +219,8 @@ spec!(CANYON, CanyonSpec); spec!(ECOTONE, EcotoneSpec); #[cfg(feature = "optimism")] spec!(FJORD, FjordSpec); +#[cfg(feature = "opbnb")] +spec!(FERMAT, FermatSpec); #[cfg(not(feature = "optimism"))] #[macro_export] @@ -363,6 +370,10 @@ macro_rules! spec_to_generic { } $crate::SpecId::FJORD => { use $crate::FjordSpec as SPEC; + } + #[cfg(feature = "opbnb")] + $crate::SpecId::FERMAT => { + use $crate::FermatSpec as SPEC; $e } } diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 1a93a83d..aaddcb58 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -138,6 +138,13 @@ secp256k1 = ["revm-precompile/secp256k1"] c-kzg = ["revm-precompile/c-kzg"] blst = ["revm-precompile/blst"] +opbnb = ["revm-interpreter/opbnb", "revm-precompile/opbnb"] +opbnb-default-handler = [ + "opbnb", + "revm-precompile/opbnb-default-handler", + "revm-interpreter/opbnb-default-handler", +] + [[example]] name = "fork_ref_transact" path = "../../examples/fork_ref_transact.rs"