diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d1eee5923..622d61f08 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -69,6 +69,10 @@ jobs: steps: - uses: actions/checkout@v3 - uses: rui314/setup-mold@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v2 + with: + version: "23.2" - name: Install Rust run: rustup show && rustup install nightly && rustup component add rustfmt --toolchain nightly-x86_64-unknown-linux-gnu # Nightly is needed for our configuration of cargo fmt - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code @@ -103,6 +107,10 @@ jobs: steps: - uses: actions/checkout@v3 - uses: rui314/setup-mold@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v2 + with: + version: "23.2" - name: Install Rust run: rustup show - name: cargo install cargo-hack @@ -125,6 +133,10 @@ jobs: steps: - uses: actions/checkout@v3 - uses: rui314/setup-mold@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v2 + with: + version: "23.2" - name: Install Rust run: rustup show - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code @@ -145,6 +157,10 @@ jobs: steps: - uses: actions/checkout@v3 - uses: rui314/setup-mold@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v2 + with: + version: "23.2" - name: Install Rust run: rustup show - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code @@ -168,6 +184,10 @@ jobs: with: submodules: true - uses: rui314/setup-mold@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v2 + with: + version: "23.2" - name: Install Rust run: rustup show - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code @@ -211,6 +231,10 @@ jobs: # `check` job does and their caches are shared, so it's best to keep # things as similar as possible. - uses: rui314/setup-mold@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v2 + with: + version: "23.2" - name: Install Rust run: rustup show - uses: Swatinem/rust-cache@v2 @@ -271,6 +295,10 @@ jobs: steps: - uses: actions/checkout@v3 - uses: rui314/setup-mold@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v2 + with: + version: "23.2" - name: Install Rust run: rustup show - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code diff --git a/.gitignore b/.gitignore index c484edb21..183badd0d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,7 @@ fuzz/Cargo.lock demo_data/ /.vscode/* + +/docker/credentials/* +!/docker/credentials/bridge-0.addr +!/docker/credentials/bridge-0.key diff --git a/Cargo.lock b/Cargo.lock index a4ee5db92..13f865241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -587,6 +587,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base16ct" version = "0.2.0" @@ -969,7 +975,7 @@ dependencies = [ "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.37", - "synstructure", + "synstructure 0.13.0", ] [[package]] @@ -1088,16 +1094,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "bstr" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.14.0" @@ -1247,6 +1243,56 @@ dependencies = [ "libc", ] +[[package]] +name = "celestia-proto" +version = "0.1.0" +source = "git+https://github.com/eigerco/celestia-node-rs.git?rev=dfe8f2f#dfe8f2f20737d9a6556e43e4dfefd63c4824f936" +dependencies = [ + "anyhow", + "prost 0.12.1", + "prost-build", + "prost-types 0.12.1", + "serde", + "tendermint-proto 0.32.0", +] + +[[package]] +name = "celestia-rpc" +version = "0.1.0" +source = "git+https://github.com/eigerco/celestia-node-rs.git?rev=dfe8f2f#dfe8f2f20737d9a6556e43e4dfefd63c4824f936" +dependencies = [ + "celestia-types", + "http", + "jsonrpsee 0.20.1", + "serde", + "thiserror", + "tracing", +] + +[[package]] +name = "celestia-types" +version = "0.1.0" +source = "git+https://github.com/eigerco/celestia-node-rs.git?rev=dfe8f2f#dfe8f2f20737d9a6556e43e4dfefd63c4824f936" +dependencies = [ + "base64 0.21.4", + "bech32", + "bytes", + "celestia-proto", + "cid", + "const_format", + "enum_dispatch", + "libp2p-identity", + "multiaddr", + "nmt-rs", + "ruint", + "serde", + "serde_repr", + "sha2 0.10.8", + "tendermint 0.32.0", + "tendermint-proto 0.32.0", + "thiserror", +] + [[package]] name = "cexpr" version = "0.6.0" @@ -1304,6 +1350,19 @@ dependencies = [ "half", ] +[[package]] +name = "cid" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3" +dependencies = [ + "core2", + "multibase", + "multihash 0.18.1", + "serde", + "unsigned-varint", +] + [[package]] name = "cipher" version = "0.3.0" @@ -1497,6 +1556,26 @@ checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" name = "const-rollup-config" version = "0.2.0" +[[package]] +name = "const_format" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "unicode-xid 0.2.4", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -1540,6 +1619,15 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpp_demangle" version = "0.3.5" @@ -1953,6 +2041,26 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +[[package]] +name = "data-encoding-macro" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + [[package]] name = "deadpool" version = "0.9.5" @@ -2002,7 +2110,7 @@ dependencies = [ "clap 4.4.6", "demo-stf", "hex", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "rand 0.8.5", "reth-primitives", "serde", @@ -2505,6 +2613,18 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "enum_dispatch" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +dependencies = [ + "once_cell", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.37", +] + [[package]] name = "enumn" version = "0.1.12" @@ -3297,28 +3417,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] -name = "globset" -version = "0.4.13" +name = "gloo-net" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.1.7", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] name = "gloo-net" -version = "0.2.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +checksum = "8ac9e8288ae2c632fa9f8657ac70bfe38a1530f345282d7ba66a1f70b72b7dc4" dependencies = [ "futures-channel", "futures-core", "futures-sink", - "gloo-utils", + "gloo-utils 0.2.0", + "http", "js-sys", "pin-project", "serde", @@ -3354,6 +3482,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "group" version = "0.13.0" @@ -4165,18 +4306,19 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.18.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1822d18e4384a5e79d94dc9e4d1239cfa9fad24e55b44d2efeff5b394c9fece4" +checksum = "9ad9b31183a8bcbe843e32ca8554ad2936633548d95a7bb6a8e14c767dea6b05" dependencies = [ - "jsonrpsee-client-transport 0.18.2", - "jsonrpsee-core 0.18.2", - "jsonrpsee-http-client 0.18.2", - "jsonrpsee-proc-macros 0.18.2", + "jsonrpsee-client-transport 0.20.1", + "jsonrpsee-core 0.20.1", + "jsonrpsee-http-client 0.20.1", + "jsonrpsee-proc-macros 0.20.1", "jsonrpsee-server", - "jsonrpsee-types 0.18.2", - "jsonrpsee-wasm-client 0.18.2", - "jsonrpsee-ws-client 0.18.2", + "jsonrpsee-types 0.20.1", + "jsonrpsee-wasm-client 0.20.1", + "jsonrpsee-ws-client 0.20.1", + "tokio", "tracing", ] @@ -4190,7 +4332,7 @@ dependencies = [ "futures-channel", "futures-timer", "futures-util", - "gloo-net", + "gloo-net 0.2.6", "http", "jsonrpsee-core 0.16.3", "jsonrpsee-types 0.16.3", @@ -4207,15 +4349,15 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.18.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11aa5766d5c430b89cb26a99b88f3245eb91534be8126102cea9e45ee3891b22" +checksum = "97f2743cad51cc86b0dbfe316309eeb87a9d96a3d7f4dd7a99767c4b5f065335" dependencies = [ "futures-channel", "futures-util", - "gloo-net", + "gloo-net 0.4.0", "http", - "jsonrpsee-core 0.18.2", + "jsonrpsee-core 0.20.1", "pin-project", "rustls-native-certs", "soketto", @@ -4224,7 +4366,8 @@ dependencies = [ "tokio-rustls", "tokio-util", "tracing", - "webpki-roots 0.23.1", + "url", + "webpki-roots 0.25.2", ] [[package]] @@ -4253,9 +4396,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.18.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c6832a55f662b5a6ecc844db24b8b9c387453f923de863062c60ce33d62b81" +checksum = "35dc957af59ce98373bcdde0c1698060ca6c2d2e9ae357b459c7158b6df33330" dependencies = [ "anyhow", "async-lock", @@ -4263,9 +4406,8 @@ dependencies = [ "beef", "futures-timer", "futures-util", - "globset", "hyper", - "jsonrpsee-types 0.18.2", + "jsonrpsee-types 0.20.1", "parking_lot 0.12.1", "rand 0.8.5", "rustc-hash", @@ -4274,7 +4416,6 @@ dependencies = [ "soketto", "thiserror", "tokio", - "tokio-stream", "tracing", "wasm-bindgen-futures", ] @@ -4300,21 +4441,22 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.18.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1705c65069729e3dccff6fd91ee431d5d31cabcf00ce68a62a2c6435ac713af9" +checksum = "0dd865d0072764cb937b0110a92b5f53e995f7101cb346beca03d93a2dea79de" dependencies = [ "async-trait", "hyper", "hyper-rustls", - "jsonrpsee-core 0.18.2", - "jsonrpsee-types 0.18.2", + "jsonrpsee-core 0.20.1", + "jsonrpsee-types 0.20.1", "serde", "serde_json", "thiserror", "tokio", "tower", "tracing", + "url", ] [[package]] @@ -4324,7 +4466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44e8ab85614a08792b9bff6c8feee23be78c98d0182d4c622c05256ab553892a" dependencies = [ "heck 0.4.1", - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", @@ -4332,12 +4474,12 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.18.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6027ac0b197ce9543097d02a290f550ce1d9432bf301524b013053c0b75cc94" +checksum = "cef91b1017a4edb63f65239381c18de39f88d0e0760ab626d806e196f7f51477" dependencies = [ "heck 0.4.1", - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", @@ -4345,17 +4487,20 @@ dependencies = [ [[package]] name = "jsonrpsee-server" -version = "0.18.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f06661d1a6b6e5b85469dc9c29acfbb9b3bb613797a6fd10a3ebb8a70754057" +checksum = "24f4e2f3d223d810e363fb8b5616ec4c6254243ee7f452d05ac281cdc9cf76b2" dependencies = [ "futures-util", + "http", "hyper", - "jsonrpsee-core 0.18.2", - "jsonrpsee-types 0.18.2", + "jsonrpsee-core 0.20.1", + "jsonrpsee-types 0.20.1", + "route-recognizer", "serde", "serde_json", "soketto", + "thiserror", "tokio", "tokio-stream", "tokio-util", @@ -4377,20 +4522,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "jsonrpsee-types" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5bf6c75ce2a4217421154adfc65a24d2b46e77286e59bba5d9fa6544ccc8f4" -dependencies = [ - "anyhow", - "beef", - "serde", - "serde_json", - "thiserror", - "tracing", -] - [[package]] name = "jsonrpsee-types" version = "0.20.1" @@ -4418,13 +4549,13 @@ dependencies = [ [[package]] name = "jsonrpsee-wasm-client" -version = "0.18.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34e6ea7c6d862e60f8baebd946c037b70c6808a4e4e31e792a4029184e3ce13a" +checksum = "010306151579898dc1000bab239ef7a73a73f04cb8ef267ee28b9a000267e813" dependencies = [ - "jsonrpsee-client-transport 0.18.2", - "jsonrpsee-core 0.18.2", - "jsonrpsee-types 0.18.2", + "jsonrpsee-client-transport 0.20.1", + "jsonrpsee-core 0.20.1", + "jsonrpsee-types 0.20.1", ] [[package]] @@ -4441,14 +4572,15 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.18.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64b2589680ba1ad7863f279cd2d5083c1dc0a7c0ea959d22924553050f8ab9f" +checksum = "d88e35e9dfa89248ae3e92f689c1f0a190ce12d377eba7d2d08e5a7f6cc5694a" dependencies = [ "http", - "jsonrpsee-client-transport 0.18.2", - "jsonrpsee-core 0.18.2", - "jsonrpsee-types 0.18.2", + "jsonrpsee-client-transport 0.20.1", + "jsonrpsee-core 0.20.1", + "jsonrpsee-types 0.20.1", + "url", ] [[package]] @@ -4576,6 +4708,22 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +[[package]] +name = "libp2p-identity" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bf6e730ec5e7022958da53ffb03b326e681b7316939012ae9b3c7449a812d4" +dependencies = [ + "bs58 0.5.0", + "hkdf", + "log", + "multihash 0.19.1", + "quick-protobuf", + "rand 0.8.5", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "librocksdb-sys" version = "0.11.0+8.1.1" @@ -4981,6 +5129,71 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" +[[package]] +name = "multiaddr" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92a651988b3ed3ad1bc8c87d016bb92f6f395b84ed1db9b926b32b1fc5a2c8b5" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash 0.19.1", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd8a792c1694c6da4f68db0a9d707c72bd260994da179e6030a5dcee00bb815" +dependencies = [ + "core2", + "multihash-derive", + "unsigned-varint", +] + +[[package]] +name = "multihash" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" +dependencies = [ + "core2", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", + "synstructure 0.12.6", +] + [[package]] name = "multimap" version = "0.8.3" @@ -5037,7 +5250,7 @@ dependencies = [ [[package]] name = "nmt-rs" version = "0.1.0" -source = "git+https://github.com/Sovereign-Labs/nmt-rs.git?rev=dd37588444fca72825d11fe4a46838f66525c49f#dd37588444fca72825d11fe4a46838f66525c49f" +source = "git+https://github.com/Sovereign-Labs/nmt-rs.git?rev=d821332#d821332baa03aea625d23060dc239af57b9121f5" dependencies = [ "borsh", "bytes", @@ -5241,7 +5454,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", @@ -5253,7 +5466,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.37", @@ -5265,7 +5478,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.37", @@ -5461,7 +5674,7 @@ version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", @@ -5909,12 +6122,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ - "once_cell", - "toml_edit 0.19.15", + "thiserror", + "toml 0.5.11", ] [[package]] @@ -6131,7 +6344,7 @@ dependencies = [ "demo-stf", "env_logger", "hex", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "log", "log4rs", "once_cell", @@ -6193,6 +6406,15 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + [[package]] name = "quote" version = "0.6.13" @@ -7197,6 +7419,12 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rrs-lib" version = "0.1.0" @@ -7444,7 +7672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4391f0dfbb6690f035f6d2a15d6a12f88cc5395c36bcc056db07ffa2a90870ec" dependencies = [ "darling 0.14.4", - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", @@ -7472,7 +7700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "316e0fb10ec0fee266822bd641bab5e332a4ab80ef8c5b5ff35e5401a394f5a6" dependencies = [ "darling 0.14.4", - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", @@ -7498,7 +7726,7 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", @@ -8016,7 +8244,7 @@ dependencies = [ "anyhow", "borsh", "clap 4.4.6", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "schemars", "serde", "serde_json", @@ -8139,7 +8367,7 @@ name = "sov-accessory-state" version = "0.2.0" dependencies = [ "borsh", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "serde", "sov-modules-api", "sov-state", @@ -8154,7 +8382,7 @@ dependencies = [ "arbitrary", "borsh", "clap 4.4.6", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "schemars", "serde", "serde_json", @@ -8217,7 +8445,7 @@ dependencies = [ "anyhow", "borsh", "clap 4.4.6", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "schemars", "serde", "serde_json", @@ -8238,7 +8466,7 @@ dependencies = [ "borsh", "clap 4.4.6", "hex", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "schemars", "serde", "serde_json", @@ -8262,15 +8490,15 @@ dependencies = [ "base64 0.21.4", "bech32", "borsh", + "celestia-proto", + "celestia-rpc", + "celestia-types", "hex", - "hex-literal", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "nmt-rs", "postcard", "proptest", - "prost 0.11.9", - "prost-build", - "prost-types 0.11.9", + "prost 0.12.1", "risc0-zkvm", "risc0-zkvm-platform", "serde", @@ -8279,8 +8507,8 @@ dependencies = [ "sov-celestia-adapter", "sov-rollup-interface", "sov-zk-cycle-macros", - "tendermint", - "tendermint-proto", + "tendermint 0.32.0", + "tendermint-proto 0.32.0", "thiserror", "tokio", "tracing", @@ -8293,7 +8521,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "serde", "serde_json", "sov-chain-state", @@ -8313,7 +8541,7 @@ dependencies = [ "demo-stf", "directories", "hex", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "serde", "serde_json", "sov-accounts", @@ -8377,7 +8605,7 @@ dependencies = [ "ethers-providers", "ethers-signers", "hex", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "log", "log4rs", "prettytable-rs", @@ -8409,7 +8637,7 @@ dependencies = [ "sov-stf-runner", "sov-zk-cycle-macros", "tempfile", - "tendermint", + "tendermint 0.32.2", "tokio", "tracing", "tracing-subscriber 0.3.17", @@ -8423,7 +8651,7 @@ dependencies = [ "borsh", "demo-stf", "ethers", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "reth-primitives", "reth-rpc-types", "serde_json", @@ -8449,7 +8677,7 @@ dependencies = [ "ethers-middleware", "ethers-signers", "hex", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "lazy_static", "reth-interfaces", "reth-primitives", @@ -8506,7 +8734,7 @@ dependencies = [ "ed25519-dalek 2.0.0", "hex", "jmt", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "rand 0.8.5", "risc0-zkvm", "risc0-zkvm-platform", @@ -8532,7 +8760,7 @@ dependencies = [ "anyhow", "borsh", "clap 4.4.6", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "proc-macro2 1.0.69", "quote 1.0.33", "schemars", @@ -8573,7 +8801,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "postgres", "schemars", "serde", @@ -8667,7 +8895,7 @@ dependencies = [ "async-trait", "borsh", "hex", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "rand 0.8.5", "serde", "sov-modules-api", @@ -8686,7 +8914,7 @@ dependencies = [ "anyhow", "borsh", "clap 4.4.6", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "risc0-zkvm", "risc0-zkvm-platform", "schemars", @@ -8734,7 +8962,7 @@ dependencies = [ "borsh", "futures", "hex", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "serde", "sov-accounts", "sov-bank", @@ -8759,7 +8987,7 @@ dependencies = [ "anyhow", "borsh", "clap 4.4.6", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "schemars", "serde", "serde_json", @@ -8777,7 +9005,7 @@ dependencies = [ "anyhow", "borsh", "clap 4.4.6", - "jsonrpsee 0.18.2", + "jsonrpsee 0.20.1", "schemars", "serde", "serde_json", @@ -9139,7 +9367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d5bd5566fe5633ec48dfa35ab152fd29f8a577c21971e1c6db9f28afb9bbb9" dependencies = [ "Inflector", - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.37", @@ -9152,7 +9380,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b232943ee7ca83a6d56face33b8af12e9fb470a15a53835f4e12a6e452a41c1c" dependencies = [ "Inflector", - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.37", @@ -9669,6 +9897,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", + "unicode-xid 0.2.4", +] + [[package]] name = "synstructure" version = "0.13.0" @@ -9727,6 +9967,34 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "tendermint" +version = "0.32.0" +source = "git+https://github.com/eigerco/celestia-tendermint-rs.git?rev=1f8b574#1f8b574809a43b892f4beb59b887919b484fe232" +dependencies = [ + "bytes", + "digest 0.10.7", + "ed25519 2.2.2", + "ed25519-consensus", + "flex-error", + "futures", + "num-traits", + "once_cell", + "prost 0.12.1", + "prost-types 0.12.1", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "signature 2.1.0", + "subtle", + "subtle-encoding", + "tendermint-proto 0.32.0", + "time", + "zeroize", +] + [[package]] name = "tendermint" version = "0.32.2" @@ -9751,11 +10019,28 @@ dependencies = [ "signature 2.1.0", "subtle", "subtle-encoding", - "tendermint-proto", + "tendermint-proto 0.32.2", "time", "zeroize", ] +[[package]] +name = "tendermint-proto" +version = "0.32.0" +source = "git+https://github.com/eigerco/celestia-tendermint-rs.git?rev=1f8b574#1f8b574809a43b892f4beb59b887919b484fe232" +dependencies = [ + "bytes", + "flex-error", + "num-derive 0.3.3", + "num-traits", + "prost 0.12.1", + "prost-types 0.12.1", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + [[package]] name = "tendermint-proto" version = "0.32.2" @@ -10400,7 +10685,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.7", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] @@ -10537,6 +10822,12 @@ dependencies = [ "destructure_traitobject", ] +[[package]] +name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" + [[package]] name = "untrusted" version = "0.7.1" @@ -11241,7 +11532,7 @@ dependencies = [ "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.37", - "synstructure", + "synstructure 0.13.0", ] [[package]] @@ -11262,7 +11553,7 @@ dependencies = [ "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.37", - "synstructure", + "synstructure 0.13.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 011ab90e4..c3563059d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ bech32 = "0.9.1" derive_more = "0.99.11" clap = { version = "4.2.7", features = ["derive"] } toml = "0.8.0" -jsonrpsee = { version = "0.18.2", features = ["jsonrpsee-types"] } +jsonrpsee = { version = "0.20.1", features = ["jsonrpsee-types"] } schemars = { version = "0.8.12", features = ["derive"] } tempfile = "3.5" tokio = { version = "1", features = ["full"] } @@ -124,3 +124,14 @@ secp256k1 = { version = "0.27.0", default-features = false, features = [ "rand-std", "recovery", ] } + +[patch.'https://github.com/eigerco/celestia-node-rs.git'] +# Uncomment to apply local changes +# celestia-proto = { path = "../celestia-node-rs/proto" } +# celestia-rpc = { path = "../celestia-node-rs/rpc" } +# celestia-types = { path = "../celestia-node-rs/types" } + +[patch.'https://github.com/eigerco/celestia-tendermint-rs.git'] +# Uncomment to apply local changes +# tendermint = { path = "../celestia-tendermint-rs/tendermint" } +# tendermint-proto = { path = "../celestia-tendermint-rs/proto" } diff --git a/adapters/celestia/Cargo.toml b/adapters/celestia/Cargo.toml index b422888c6..8baa79558 100644 --- a/adapters/celestia/Cargo.toml +++ b/adapters/celestia/Cargo.toml @@ -10,12 +10,19 @@ publish = false [dependencies] borsh = { workspace = true, features = ["bytes"] } bech32 = { workspace = true } -prost = "0.11" -prost-types = "0.11" +prost = "0.12" # I keep this commented as a reminder to opportunity to optimze this crate for non native compilation #tendermint = { version = "0.32", default-features = false, features = ["std"] } -tendermint = "0.32" -tendermint-proto = "0.32" + +celestia-proto = { git = "https://github.com/eigerco/celestia-node-rs.git", rev = "dfe8f2f" } +celestia-rpc = { git = "https://github.com/eigerco/celestia-node-rs.git", rev = "dfe8f2f", default-features = false, optional = true } +celestia-types = { git = "https://github.com/eigerco/celestia-node-rs.git", rev = "dfe8f2f" } +tendermint = { git = "https://github.com/eigerco/celestia-tendermint-rs.git", rev = "1f8b574", default-features = false } +tendermint-proto = { git = "https://github.com/eigerco/celestia-tendermint-rs.git", rev = "1f8b574" } +nmt-rs = { git = "https://github.com/Sovereign-Labs/nmt-rs.git", rev = "d821332", features = [ + "serde", + "borsh", +] } # Convenience async-trait = { workspace = true } @@ -23,7 +30,6 @@ anyhow = { workspace = true } sha2 = { workspace = true } base64 = "0.21.2" hex = { workspace = true, features = ["serde"] } -hex-literal = "0.4.1" jsonrpsee = { workspace = true, features = ["http-client"], optional = true } serde = { workspace = true } serde_json = { workspace = true, optional = true } @@ -31,14 +37,12 @@ tokio = { workspace = true, optional = true } thiserror = { workspace = true } tracing = { workspace = true } sov-zk-cycle-macros = { path = "../../utils/zk-cycle-macros", version = "0.2", optional = true } -risc0-zkvm = { workspace = true, default-features = false, features = ["std"], optional = true } +risc0-zkvm = { workspace = true, default-features = false, features = [ + "std", +], optional = true } risc0-zkvm-platform = { workspace = true, optional = true } sov-rollup-interface = { path = "../../rollup-interface", version = "0.2" } -nmt-rs = { git = "https://github.com/Sovereign-Labs/nmt-rs.git", rev = "dd37588444fca72825d11fe4a46838f66525c49f", features = [ - "serde", - "borsh", -] } [dev-dependencies] @@ -47,12 +51,15 @@ proptest = { version = "1.3" } sov-celestia-adapter = { path = ".", features = ["native"] } wiremock = "0.5" - -[build-dependencies] -prost-build = { version = "0.12" } - [features] default = [] -native = ["dep:tokio", "dep:jsonrpsee", "dep:serde_json", "tendermint/default", "sov-rollup-interface/native"] +native = [ + "dep:tokio", + "dep:jsonrpsee", + "dep:serde_json", + "dep:celestia-rpc", + "tendermint/default", + "sov-rollup-interface/native", +] bench = ["sov-zk-cycle-macros/bench", "risc0-zkvm", "risc0-zkvm-platform"] verifier = [] diff --git a/adapters/celestia/src/celestia.rs b/adapters/celestia/src/celestia.rs index 1affaabcf..e77284e5d 100644 --- a/adapters/celestia/src/celestia.rs +++ b/adapters/celestia/src/celestia.rs @@ -1,10 +1,11 @@ use std::ops::Range; use std::sync::{Arc, Mutex}; -use base64::engine::general_purpose::STANDARD as B64_ENGINE; -use base64::Engine; +use anyhow::Context; use borsh::{BorshDeserialize, BorshSerialize}; -use nmt_rs::NamespacedHash; +use celestia_proto::celestia::blob::v1::MsgPayForBlobs; +use celestia_proto::cosmos::tx::v1beta1::Tx; +use celestia_types::DataAvailabilityHeader; use prost::bytes::Buf; use prost::Message; use serde::{Deserialize, Serialize}; @@ -16,24 +17,16 @@ use tendermint::crypto::default::Sha256; use tendermint::merkle::simple_hash_from_byte_vectors; use tendermint::Hash; pub use tendermint_proto::v0_34 as celestia_tm_version; +use tendermint_proto::v0_34::types::IndexWrapper; use tendermint_proto::Protobuf; use tracing::debug; -const NAMESPACED_HASH_LEN: usize = 48; - -pub const GENESIS_PLACEHOLDER_HASH: &[u8; 32] = &[255; 32]; - -use crate::pfb::{BlobTx, MsgPayForBlobs, Tx}; -use crate::shares::{read_varint, BlobIterator, BlobRefIterator, NamespaceGroup}; -use crate::utils::BoxError; +use crate::shares::{BlobIterator, BlobRefIterator, NamespaceGroup}; +use crate::utils::{read_varint, BoxError}; use crate::verifier::address::CelestiaAddress; use crate::verifier::{ChainValidityCondition, TmHash, PFB_NAMESPACE}; -#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)] -pub struct MarshalledDataAvailabilityHeader { - pub row_roots: Vec, - pub column_roots: Vec, -} +pub const GENESIS_PLACEHOLDER_HASH: &[u8; 32] = &[255; 32]; /// A partially serialized tendermint header. Only fields which are actually inspected by /// Jupiter are included in their raw form. Other fields are pre-encoded as protobufs. @@ -42,7 +35,7 @@ pub struct MarshalledDataAvailabilityHeader { /// a tendermint::block::Header from being deserialized in most formats except JSON. However /// it also provides a significant efficiency benefit over the standard tendermint type, which /// performs a complete protobuf serialization every time `.hash()` is called. -#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] +#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] // TODO: , BorshDeserialize, BorshSerialize)] pub struct CompactHeader { /// Header version pub version: Vec, @@ -93,13 +86,9 @@ trait EncodeTm34 { impl From for CompactHeader { fn from(value: TendermintHeader) -> Self { - let data_hash = if let Some(h) = value.data_hash { - match h { - Hash::Sha256(value) => Some(ProtobufHash(value)), - Hash::None => None, - } - } else { - None + let data_hash = match value.data_hash { + Hash::Sha256(value) => Some(ProtobufHash(value)), + Hash::None => None, }; Self { version: Protobuf::::encode_vec( @@ -113,26 +102,14 @@ impl From for CompactHeader { &value.last_block_id.unwrap_or_default(), ) .unwrap(), - last_commit_hash: value - .last_commit_hash - .unwrap_or_default() - .encode_vec() - .unwrap(), + last_commit_hash: value.last_commit_hash.encode_vec().unwrap(), data_hash, validators_hash: value.validators_hash.encode_vec().unwrap(), next_validators_hash: value.next_validators_hash.encode_vec().unwrap(), consensus_hash: value.consensus_hash.encode_vec().unwrap(), app_hash: value.app_hash.encode_vec().unwrap(), - last_results_hash: value - .last_results_hash - .unwrap_or_default() - .encode_vec() - .unwrap(), - evidence_hash: value - .evidence_hash - .unwrap_or_default() - .encode_vec() - .unwrap(), + last_results_hash: value.last_results_hash.encode_vec().unwrap(), + evidence_hash: value.evidence_hash.encode_vec().unwrap(), proposer_address: value.proposer_address.encode_vec().unwrap(), } } @@ -181,57 +158,11 @@ impl CompactHeader { } } -#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] -pub struct DataAvailabilityHeader { - pub row_roots: Vec, - pub column_roots: Vec, -} - -// Danger! This method panics if the provided bas64 is longer than a namespaced hash -fn decode_to_ns_hash(b64: &str) -> Result { - let mut out = [0u8; NAMESPACED_HASH_LEN]; - B64_ENGINE.decode_slice(b64.as_bytes(), &mut out)?; - Ok(NamespacedHash(out)) -} - -impl TryFrom for DataAvailabilityHeader { - type Error = base64::DecodeSliceError; - - fn try_from(value: MarshalledDataAvailabilityHeader) -> Result { - let mut row_roots = Vec::with_capacity(value.row_roots.len()); - for root in value.row_roots { - row_roots.push(decode_to_ns_hash(&root)?); - } - let mut column_roots = Vec::with_capacity(value.column_roots.len()); - for root in value.column_roots { - column_roots.push(decode_to_ns_hash(&root)?); - } - Ok(Self { - row_roots, - column_roots, - }) - } -} - -/// The response from the celestia `/header` endpoint. Must be converted to a -/// [`CelestiaHeader`] before use. -#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)] -pub struct CelestiaHeaderResponse { - pub header: tendermint::block::Header, - pub dah: MarshalledDataAvailabilityHeader, -} - -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -pub struct NamespacedSharesResponse { - pub shares: Option>, - pub height: u64, -} - -#[derive(Debug, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] // TODO:, BorshSerialize, BorshDeserialize)] pub struct CelestiaHeader { pub dah: DataAvailabilityHeader, pub header: CompactHeader, - #[borsh_skip] + // #[borsh_skip] #[serde(skip)] cached_prev_hash: Arc>>, } @@ -256,7 +187,7 @@ impl CelestiaHeader { } } -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] +#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] // TODO: , BorshDeserialize, BorshSerialize)] pub struct BlobWithSender { pub blob: CountedBufReader, pub sender: CelestiaAddress, @@ -388,31 +319,25 @@ pub struct TxPosition { } pub(crate) fn pfb_from_iter(data: impl Buf, pfb_len: usize) -> Result { - debug!("Decoding blob tx"); - let mut blob_tx = BlobTx::decode(data.take(pfb_len))?; - debug!("Decoding cosmos sdk tx"); - let cosmos_tx = Tx::decode(&mut blob_tx.tx)?; - let messages = cosmos_tx - .body - .ok_or(anyhow::format_err!("No body in cosmos tx"))? - .messages; - if messages.len() != 1 { - return Err(anyhow::format_err!("Expected 1 message in cosmos tx")); - } - debug!("Decoding PFB from blob tx value"); - Ok(MsgPayForBlobs::decode(&mut &messages[0].value[..])?) + let blob_tx = IndexWrapper::decode(data.take(pfb_len)).context("failed decoding blob tx")?; + let cosmos_tx = + Tx::decode(&blob_tx.tx[..]).context("failed decoding cosmos tx from blob tx")?; + let messages = cosmos_tx.body.context("No body in cosmos tx")?.messages; + + anyhow::ensure!(messages.len() == 1, "Expected 1 message in cosmos tx"); + MsgPayForBlobs::decode(&messages[0].value[..]).context("failed decoding PFB frob cosmos tx") } fn next_pfb(mut data: &mut BlobRefIterator) -> Result<(MsgPayForBlobs, TxPosition), BoxError> { let (start_idx, start_offset) = data.current_position(); - let (len, len_of_len) = read_varint(&mut data).expect("Varint must be valid"); + let (len, len_of_len) = read_varint(&mut data).context("failed decoding varint")?; debug!( "Decoding wrapped PFB of length {}. Stripped {} bytes of prefix metadata", len, len_of_len ); let current_share_idx = data.current_position().0; - let pfb = pfb_from_iter(&mut data, len as usize)?; + let pfb = pfb_from_iter(data, len as usize)?; Ok(( pfb, @@ -425,37 +350,87 @@ fn next_pfb(mut data: &mut BlobRefIterator) -> Result<(MsgPayForBlobs, TxPositio #[cfg(test)] mod tests { - use crate::{CelestiaHeaderResponse, CompactHeader}; + use celestia_types::ExtendedHeader; + use sov_rollup_interface::services::da::SlotData; + use sov_rollup_interface::zk::ValidityCondition; + + use crate::{CelestiaHeader, CompactHeader}; - const HEADER_RESPONSE_JSON: &[u8] = include_bytes!("./header_response.json"); + const HEADER_JSON_RESPONSES: &[&str] = &[ + include_str!("../test_data/block_with_rollup_data/header.json"), + include_str!("../test_data/block_without_rollup_data/header.json"), + ]; + + #[test] + fn test_validity_condition() { + let headers: Vec<_> = HEADER_JSON_RESPONSES + .iter() + .map(|header| { + let eh: ExtendedHeader = serde_json::from_str(header).unwrap(); + CelestiaHeader::new(eh.dah, eh.header.into()) + }) + .collect(); + + let former_validity_cond = headers[0].validity_condition(); + let latter_validity_cond = headers[1].validity_condition(); + + assert!(former_validity_cond + .combine::(latter_validity_cond) + .is_ok()); + assert!(latter_validity_cond + .combine::(former_validity_cond) + .is_err()); + } #[test] fn test_compact_header_serde() { - let original_header: CelestiaHeaderResponse = - serde_json::from_slice(HEADER_RESPONSE_JSON).unwrap(); + for header_json in HEADER_JSON_RESPONSES { + let original_header: ExtendedHeader = serde_json::from_str(header_json).unwrap(); - let header: CompactHeader = original_header.header.into(); + let header: CompactHeader = original_header.header.into(); - let serialized_header = postcard::to_stdvec(&header).unwrap(); - let deserialized_header: CompactHeader = postcard::from_bytes(&serialized_header).unwrap(); - assert_eq!(deserialized_header, header) + let serialized_header = postcard::to_stdvec(&header).unwrap(); + let deserialized_header: CompactHeader = + postcard::from_bytes(&serialized_header).unwrap(); + assert_eq!(deserialized_header, header) + } } #[test] fn test_compact_header_hash() { - let original_header: CelestiaHeaderResponse = - serde_json::from_slice(HEADER_RESPONSE_JSON).unwrap(); + let expected_hashes = [ + "C839E720DA55CC6E43EC7CE00744D6151D79E84C81D7F6995F3B13B7AE532456", + "F769490DC768E7678160384070727533B7AE809477EA5D191CF7AF5C917A7973", + ]; + for (header_json, expected_hash) in HEADER_JSON_RESPONSES.iter().zip(expected_hashes.iter()) + { + let original_header: ExtendedHeader = serde_json::from_str(header_json).unwrap(); + + let tm_header = original_header.header.clone(); + let compact_header: CompactHeader = original_header.header.into(); - let tm_header = original_header.header.clone(); - let compact_header: CompactHeader = original_header.header.into(); + assert_eq!(tm_header.hash(), compact_header.hash()); + assert_eq!( + hex::decode(expected_hash).unwrap(), + compact_header.hash().as_bytes() + ); - assert_eq!(tm_header.hash(), compact_header.hash()); - assert_eq!( - hex::decode("32381A0B7262F15F081ACEF769EE59E6BB4C42C1013A3EEE23967FBF32B86AE6") - .unwrap(), - compact_header.hash().as_bytes() - ); + assert_eq!(tm_header.hash(), compact_header.hash(),); + } + } - assert_eq!(tm_header.hash(), compact_header.hash(),); + #[test] + fn test_zkvm_serde_celestia_header() { + // regression https://github.com/eigerco/celestia-tendermint-rs/pull/12 + for header_json in HEADER_JSON_RESPONSES { + let original_header: ExtendedHeader = serde_json::from_str(header_json).unwrap(); + let cel_header = + CelestiaHeader::new(original_header.dah, original_header.header.into()); + + let serialized = risc0_zkvm::serde::to_vec(&cel_header).unwrap(); + let deserialized = risc0_zkvm::serde::from_slice(&serialized).unwrap(); + + assert_eq!(cel_header, deserialized); + } } } diff --git a/adapters/celestia/src/da_service.rs b/adapters/celestia/src/da_service.rs index 99637742b..e90181b74 100644 --- a/adapters/celestia/src/da_service.rs +++ b/adapters/celestia/src/da_service.rs @@ -1,40 +1,34 @@ -use std::collections::HashMap; -use std::str::FromStr; - use async_trait::async_trait; -use base64::engine::general_purpose::STANDARD as B64_ENGINE; -use base64::Engine; -use jsonrpsee::core::client::ClientT; -use jsonrpsee::core::params::ArrayParams; +use celestia_rpc::prelude::*; +use celestia_types::blob::{Blob as JsonBlob, Commitment, SubmitOptions}; +use celestia_types::consts::appconsts::{ + CONTINUATION_SPARSE_SHARE_CONTENT_SIZE, FIRST_SPARSE_SHARE_CONTENT_SIZE, SHARE_SIZE, +}; +use celestia_types::nmt::Namespace; use jsonrpsee::http_client::{HeaderMap, HttpClient}; -use nmt_rs::NamespaceId; use sov_rollup_interface::da::CountedBufReader; use sov_rollup_interface::services::da::DaService; -use tracing::{debug, info, span, Level}; +use tracing::{debug, info, instrument, trace}; -use crate::share_commit::recreate_commitment; -use crate::shares::{Blob, NamespaceGroup, Share}; -use crate::types::{ExtendedDataSquare, FilteredCelestiaBlock, Row, RpcNamespacedSharesResponse}; +use crate::shares::Blob; +use crate::types::FilteredCelestiaBlock; use crate::utils::BoxError; -use crate::verifier::address::CelestiaAddress; use crate::verifier::proofs::{CompletenessProof, CorrectnessProof}; use crate::verifier::{CelestiaSpec, CelestiaVerifier, RollupParams, PFB_NAMESPACE}; -use crate::{ - parse_pfb_namespace, BlobWithSender, CelestiaHeader, CelestiaHeaderResponse, - DataAvailabilityHeader, -}; +use crate::BlobWithSender; // Approximate value, just to make it work. -const GAS_PER_BYTE: usize = 120; +const GAS_PER_BYTE: usize = 20; +const GAS_PRICE: usize = 1; #[derive(Debug, Clone)] pub struct CelestiaService { client: HttpClient, - rollup_namespace: NamespaceId, + rollup_namespace: Namespace, } impl CelestiaService { - pub fn with_client(client: HttpClient, nid: NamespaceId) -> Self { + pub fn with_client(client: HttpClient, nid: Namespace) -> Self { Self { client, rollup_namespace: nid, @@ -42,50 +36,6 @@ impl CelestiaService { } } -/// Fetch the rollup namespace shares and etx data. Returns a tuple `(rollup_shares, etx_shares)` -async fn fetch_needed_shares_by_header( - rollup_namespace: NamespaceId, - client: &HttpClient, - header: &serde_json::Value, -) -> Result<(NamespaceGroup, NamespaceGroup), BoxError> { - let dah = header - .get("dah") - .ok_or(BoxError::msg("missing dah in block header"))?; - let rollup_namespace_str = B64_ENGINE.encode(rollup_namespace).into(); - let rollup_shares_future = { - let params: Vec<&serde_json::Value> = vec![dah, &rollup_namespace_str]; - client.request::("share.GetSharesByNamespace", params) - }; - - let etx_namespace_str = B64_ENGINE.encode(PFB_NAMESPACE).into(); - let etx_shares_future = { - let params: Vec<&serde_json::Value> = vec![dah, &etx_namespace_str]; - client.request::("share.GetSharesByNamespace", params) - }; - - let (rollup_shares_resp, etx_shares_resp) = - tokio::join!(rollup_shares_future, etx_shares_future); - - let rollup_shares = NamespaceGroup::Sparse( - rollup_shares_resp? - .0 - .unwrap_or_default() - .into_iter() - .flat_map(|resp| resp.shares) - .collect(), - ); - let tx_data = NamespaceGroup::Compact( - etx_shares_resp? - .0 - .unwrap_or_default() - .into_iter() - .flat_map(|resp| resp.shares) - .collect(), - ); - - Ok((rollup_shares, tx_data)) -} - /// Runtime configuration for the DA service #[derive(Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)] pub struct DaServiceConfig { @@ -149,66 +99,33 @@ impl DaService for CelestiaService { type Error = BoxError; + #[instrument(skip(self), err)] async fn get_finalized_at(&self, height: u64) -> Result { let client = self.client.clone(); let rollup_namespace = self.rollup_namespace; - let _span = span!(Level::TRACE, "fetching finalized block", height = height); // Fetch the header and relevant shares via RPC - debug!("Fetching header at height={}...", height); - let header = client - .request::("header.GetByHeight", vec![height]) - .await?; - debug!(header_result = ?header); - debug!("Fetching shares..."); - let (rollup_shares, tx_data) = - fetch_needed_shares_by_header(rollup_namespace, &client, &header).await?; - - debug!("Fetching EDS..."); - // Fetch entire extended data square - let data_square = client - .request::( - "share.GetEDS", - vec![header - .get("dah") - .ok_or(BoxError::msg("missing 'dah' in block header"))?], - ) - .await?; - - let unmarshalled_header: CelestiaHeaderResponse = serde_json::from_value(header)?; - let dah: DataAvailabilityHeader = unmarshalled_header.dah.try_into()?; - debug!("Parsing namespaces..."); - // Parse out all of the rows containing etxs - let etx_rows = - get_rows_containing_namespace(PFB_NAMESPACE, &dah, data_square.rows()?.into_iter()) - .await?; - // Parse out all of the rows containing rollup data - let rollup_rows = - get_rows_containing_namespace(rollup_namespace, &dah, data_square.rows()?.into_iter()) - .await?; - - debug!("Decoding pfb protobufs..."); - // Parse out the pfds and store them for later retrieval - let pfds = parse_pfb_namespace(tx_data)?; - let mut pfd_map = HashMap::new(); - for tx in pfds { - for (idx, nid) in tx.0.namespace_ids.iter().enumerate() { - if nid == &rollup_namespace.0[..] { - // TODO: Retool this map to avoid cloning txs - pfd_map.insert(tx.0.share_commitments[idx].clone(), tx.clone()); - } - } - } - - let filtered_block = FilteredCelestiaBlock { - header: CelestiaHeader::new(dah, unmarshalled_header.header.into()), - rollup_data: rollup_shares, - relevant_pfbs: pfd_map, + debug!("Fetching header"); + let header = client.header_get_by_height(height).await?; + trace!(header_result = ?header); + + // Fetch the rollup namespace shares, etx data and extended data square + debug!("Fetching rollup data..."); + let rollup_rows_future = + client.share_get_shares_by_namespace(&header.dah, rollup_namespace); + let etx_rows_future = client.share_get_shares_by_namespace(&header.dah, PFB_NAMESPACE); + let data_square_future = client.share_get_eds(&header.dah); + + let (rollup_rows, etx_rows, data_square) = + tokio::try_join!(rollup_rows_future, etx_rows_future, data_square_future)?; + + FilteredCelestiaBlock::new( + self.rollup_namespace, + header, rollup_rows, - pfb_rows: etx_rows, - }; - - Ok::(filtered_block) + etx_rows, + data_square, + ) } async fn get_block_at(&self, height: u64) -> Result { @@ -221,11 +138,12 @@ impl DaService for CelestiaService { ) -> Vec<::BlobTransaction> { let mut output = Vec::new(); for blob_ref in block.rollup_data.blobs() { - let commitment = recreate_commitment(block.square_size(), blob_ref.clone()) + let commitment = Commitment::from_shares(self.rollup_namespace, blob_ref.0) .expect("blob must be valid"); + info!("Blob: {:?}", commitment); let sender = block .relevant_pfbs - .get(&commitment[..]) + .get(&commitment.0[..]) .expect("blob must be relevant") .0 .signer @@ -235,8 +153,8 @@ impl DaService for CelestiaService { let blob_tx = BlobWithSender { blob: CountedBufReader::new(blob.into_iter()), - sender: CelestiaAddress::from_str(&sender).expect("Incorrect sender address"), - hash: commitment, + sender: sender.parse().expect("Incorrect sender address"), + hash: commitment.0, }; output.push(blob_tx) @@ -253,131 +171,97 @@ impl DaService for CelestiaService { ::CompletenessProof, ) { let etx_proofs = CorrectnessProof::for_block(block, blobs); - let rollup_row_proofs = - CompletenessProof::from_filtered_block(block, self.rollup_namespace); + let rollup_row_proofs = CompletenessProof::from_filtered_block(block); (etx_proofs.0, rollup_row_proofs.0) } + #[instrument(skip_all, err)] async fn send_transaction(&self, blob: &[u8]) -> Result<(), Self::Error> { - // https://node-rpc-docs.celestia.org/ - let client = self.client.clone(); debug!("Sending {} bytes of raw data to Celestia.", blob.len()); - let fee: u64 = 2000; - let namespace = self.rollup_namespace.0.to_vec(); - let blob = blob.to_vec(); - // We factor extra share to be occupied for namespace, which is pessimistic - let gas_limit = get_gas_limit_for_bytes(blob.len()); - let mut params = ArrayParams::new(); - params.insert(namespace)?; - params.insert(blob)?; - params.insert(fee.to_string())?; - params.insert(gas_limit)?; - // Note, we only deserialize what we can use, other fields might be left over - let response = client - .request::("state.SubmitPayForBlob", params) + let gas_limit = get_gas_limit_for_bytes(blob.len()) as u64; + let fee = gas_limit * GAS_PRICE as u64; + + let blob = JsonBlob::new(self.rollup_namespace, blob.to_vec())?; + info!("Submiting: {:?}", blob.commitment); + + let height = self + .client + .blob_submit( + &[blob], + SubmitOptions { + fee: Some(fee), + gas_limit: Some(gas_limit), + }, + ) .await?; - if !response.is_success() { - anyhow::bail!("Error returned from Celestia node: {:?}", response); - } - debug!("Response after submitting blob: {:?}", response); info!( - "Blob has been submitted to Celestia. tx-hash={}", - response.tx_hash, + "Blob has been submitted to Celestia. block-height={}", + height, ); - Ok::<(), BoxError>(()) + Ok(()) } } +// https://docs.celestia.org/learn/submit-data/#fees-and-gas-limits fn get_gas_limit_for_bytes(n: usize) -> usize { - (n + 512) * GAS_PER_BYTE + 1060 -} + let fixed_cost = 75000; -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -struct CelestiaBasicResponse { - raw_log: String, - #[serde(rename = "code")] - error_code: Option, - #[serde(rename = "txhash")] - tx_hash: String, - gas_wanted: u64, - gas_used: u64, -} + let continuation_shares_needed = + n.saturating_sub(FIRST_SPARSE_SHARE_CONTENT_SIZE) / CONTINUATION_SPARSE_SHARE_CONTENT_SIZE; + let shares_needed = 1 + continuation_shares_needed + 1; // add one extra, pessimistic -impl CelestiaBasicResponse { - /// We assume that absence of `code` indicates that request was successful - pub fn is_success(&self) -> bool { - self.error_code.is_none() - } -} - -async fn get_rows_containing_namespace( - nid: NamespaceId, - dah: &DataAvailabilityHeader, - data_square_rows: impl Iterator, -) -> Result, BoxError> { - let mut output = vec![]; - - for (row, root) in data_square_rows.zip(dah.row_roots.iter()) { - if root.contains(nid) { - output.push(Row { - shares: row.to_vec(), - root: root.clone(), - }) - } - } - Ok(output) + fixed_cost + shares_needed * SHARE_SIZE * GAS_PER_BYTE } #[cfg(test)] mod tests { use std::time::Duration; - use nmt_rs::NamespaceId; + use celestia_types::nmt::Namespace; + use celestia_types::{Blob as JsonBlob, NamespacedShares}; use serde_json::json; + use sov_rollup_interface::da::{BlockHeaderTrait, DaVerifier}; use sov_rollup_interface::services::da::DaService; use wiremock::matchers::{bearer_token, body_json, method, path}; use wiremock::{Mock, MockServer, Request, ResponseTemplate}; use super::default_request_timeout_seconds; - use crate::da_service::{CelestiaService, DaServiceConfig}; + use crate::da_service::{get_gas_limit_for_bytes, CelestiaService, DaServiceConfig, GAS_PRICE}; use crate::parse_pfb_namespace; - use crate::shares::{NamespaceGroup, Share}; - use crate::verifier::RollupParams; + use crate::shares::NamespaceGroup; + use crate::types::tests::{with_rollup_data, without_rollup_data}; + use crate::verifier::{CelestiaVerifier, RollupParams}; - const SERIALIZED_PFB_SHARES: &str = r#"["AAAAAAAAAAQBAAABRQAAABHDAgq3AgqKAQqHAQogL2NlbGVzdGlhLmJsb2IudjEuTXNnUGF5Rm9yQmxvYnMSYwovY2VsZXN0aWExemZ2cnJmYXE5dWQ2Zzl0NGt6bXNscGYyNHlzYXhxZm56ZWU1dzkSCHNvdi10ZXN0GgEoIiCB8FoaUuOPrX2wFBbl4MnWY3qE72tns7sSY8xyHnQtr0IBABJmClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDmXaTf6RVIgUVdG0XZ6bqecEn8jWeAi+LjzTis5QZdd4SBAoCCAEYARISCgwKBHV0aWESBDIwMDAQgPEEGkAhq2CzD1DqxsVXIriANXYyLAmJlnnt8YTNXiwHgMQQGUbl65QUe37UhnbNVrOzDVYK/nQV9TgI+5NetB2JbIz6EgEBGgRJTkRYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="]"#; - const SERIALIZED_ROLLUP_DATA_SHARES: &str = r#"["c292LXRlc3QBAAAAKHsia2V5IjogInRlc3RrZXkiLCAidmFsdWUiOiAidGVzdHZhbHVlIn0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="]"#; + const ROLLUP_ROWS_JSON: &str = with_rollup_data::ROLLUP_ROWS_JSON; + const ETX_ROWS_JSON: &str = with_rollup_data::ETX_ROWS_JSON; #[test] fn test_get_pfbs() { - // the following test case is taken from arabica-6, block 275345 - let shares: Vec = - serde_json::from_str(SERIALIZED_PFB_SHARES).expect("failed to deserialize pfb shares"); - - assert_eq!(shares.len(), 1); + let rows: NamespacedShares = + serde_json::from_str(ETX_ROWS_JSON).expect("failed to deserialize pfb shares"); - let pfb_ns = NamespaceGroup::Compact(shares); + let pfb_ns = NamespaceGroup::from(&rows); let pfbs = parse_pfb_namespace(pfb_ns).expect("failed to parse pfb shares"); - assert_eq!(pfbs.len(), 1); + assert_eq!(pfbs.len(), 3); } #[test] fn test_get_rollup_data() { - let shares: Vec = serde_json::from_str(SERIALIZED_ROLLUP_DATA_SHARES) - .expect("failed to deserialize pfb shares"); + let rows: NamespacedShares = + serde_json::from_str(ROLLUP_ROWS_JSON).expect("failed to deserialize pfb shares"); - let rollup_ns_group = NamespaceGroup::Sparse(shares); + let rollup_ns_group = NamespaceGroup::from(&rows); let mut blobs = rollup_ns_group.blobs(); let first_blob = blobs .next() .expect("iterator should contain exactly one blob"); - let found_data: Vec = first_blob.data().collect(); - assert_eq!( - found_data, - r#"{"key": "testkey", "value": "testvalue"}"#.as_bytes() - ); + // this is a batch submitted by sequencer, consisting of a single + // "CreateToken" transaction, but we verify only length there to + // not make this test depend on deserialization logic + assert_eq!(first_blob.data().count(), 252); assert!(blobs.next().is_none()); } @@ -385,7 +269,7 @@ mod tests { // Last return value is namespace async fn setup_service( timeout_sec: Option, - ) -> (MockServer, DaServiceConfig, CelestiaService, [u8; 8]) { + ) -> (MockServer, DaServiceConfig, CelestiaService, Namespace) { // Start a background HTTP server on a random local port let mock_server = MockServer::start().await; @@ -396,20 +280,14 @@ mod tests { max_celestia_response_body_size: 120_000, celestia_rpc_timeout_seconds: timeout_sec, }; - let namespace = [9u8; 8]; - let da_service = CelestiaService::new( - config.clone(), - RollupParams { - namespace: NamespaceId(namespace), - }, - ) - .await; + let namespace = Namespace::new_v0(b"sov-test").unwrap(); + let da_service = CelestiaService::new(config.clone(), RollupParams { namespace }).await; (mock_server, config, da_service, namespace) } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] - struct BasicJsonRpcResponse { + struct BasicJsonRpcRequest { jsonrpc: String, id: u64, method: String, @@ -420,18 +298,20 @@ mod tests { async fn test_submit_blob_correct() -> anyhow::Result<()> { let (mock_server, config, da_service, namespace) = setup_service(None).await; - let blob: Vec = vec![1, 2, 3, 4, 5, 11, 12, 13, 14, 15]; + let blob = [1, 2, 3, 4, 5, 11, 12, 13, 14, 15]; + let gas_limit = get_gas_limit_for_bytes(blob.len()); // TODO: Fee is hardcoded for now let expected_body = json!({ "id": 0, "jsonrpc": "2.0", - "method": "state.SubmitPayForBlob", + "method": "blob.Submit", "params": [ - namespace, - blob, - "2000", - 63700 + [JsonBlob::new(namespace, blob.to_vec()).unwrap()], + { + "GasLimit": gas_limit, + "Fee": gas_limit * GAS_PRICE, + }, ] }); @@ -440,22 +320,11 @@ mod tests { .and(bearer_token(config.celestia_rpc_auth_token)) .and(body_json(&expected_body)) .respond_with(|req: &Request| { - let request: BasicJsonRpcResponse = serde_json::from_slice(&req.body).unwrap(); + let request: BasicJsonRpcRequest = serde_json::from_slice(&req.body).unwrap(); let response_json = json!({ "jsonrpc": "2.0", "id": request.id, - "result": { - "data": "122A0A282F365", - "events": ["some event"], - "gas_used": 70522, - "gas_wanted": 133540, - "height": 26, - "logs": [ - "some log" - ], - "raw_log": "some raw logs", - "txhash": "C9FEFD6D35FCC73F9E7D5C74E1D33F0B7666936876F2AD75E5D0FB2944BFADF2" - } + "result": 14, // just some block-height }); ResponseTemplate::new(200) @@ -483,17 +352,13 @@ mod tests { Mock::given(method("POST")) .and(path("/")) .respond_with(|req: &Request| { - let request: BasicJsonRpcResponse = serde_json::from_slice(&req.body).unwrap(); + let request: BasicJsonRpcRequest = serde_json::from_slice(&req.body).unwrap(); let response_json = json!({ "jsonrpc": "2.0", "id": request.id, - "result": { - "code": 11, - "codespace": "sdk", - "gas_used": 10_000, - "gas_wanted": 12_000, - "raw_log": "out of gas in location: ReadFlat; gasWanted: 10, gasUsed: 1000: out of gas", - "txhash": "C9FEFD6D35FCC73F9E7D5C74E1D33F0B7666936876F2AD75E5D0FB2944BFADF2" + "error": { + "code": 1, + "message": ": out of gas" } }); ResponseTemplate::new(200) @@ -504,14 +369,13 @@ mod tests { .mount(&mock_server) .await; - let result = da_service.send_transaction(&blob).await; + let error = da_service + .send_transaction(&blob) + .await + .unwrap_err() + .to_string(); - assert!(result.is_err()); - let error_string = result.err().unwrap().to_string(); - assert!(error_string.contains("Error returned from Celestia node:")); - assert!(error_string.contains( - "out of gas in location: ReadFlat; gasWanted: 10, gasUsed: 1000: out of gas" - )); + assert!(error.contains("out of gas")); Ok(()) } @@ -532,11 +396,13 @@ mod tests { .mount(&mock_server) .await; - let result = da_service.send_transaction(&blob).await; + let error = da_service + .send_transaction(&blob) + .await + .unwrap_err() + .to_string(); - assert!(result.is_err()); - let error_string = result.err().unwrap().to_string(); - assert!(error_string.contains( + assert!(error.contains( "Networking or low-level protocol error: Server returned an error status code: 500" )); Ok(()) @@ -583,11 +449,122 @@ mod tests { .mount(&mock_server) .await; - let result = da_service.send_transaction(&blob).await; + let error = da_service + .send_transaction(&blob) + .await + .unwrap_err() + .to_string(); - assert!(result.is_err()); - let error_string = result.err().unwrap().to_string(); - assert!(error_string.contains("Request timeout")); + assert!(error.contains("Request timeout")); Ok(()) } + + #[tokio::test] + async fn verification_succeeds_for_correct_blocks() { + let blocks = [ + with_rollup_data::filtered_block(), + without_rollup_data::filtered_block(), + ]; + + for block in blocks { + let (_, _, da_service, namespace) = setup_service(None).await; + + let txs = da_service.extract_relevant_blobs(&block); + let (correctness_proof, completeness_proof) = + da_service.get_extraction_proof(&block, &txs).await; + + let verifier = CelestiaVerifier::new(RollupParams { namespace }); + + let validity_cond = verifier + .verify_relevant_tx_list(&block.header, &txs, correctness_proof, completeness_proof) + .unwrap(); + + assert_eq!(validity_cond.prev_hash, *block.header.prev_hash().inner()); + assert_eq!(validity_cond.block_hash, *block.header.hash().inner()); + } + } + + #[tokio::test] + async fn verification_fails_if_tx_missing() { + let block = with_rollup_data::filtered_block(); + let (_, _, da_service, namespace) = setup_service(None).await; + + let txs = da_service.extract_relevant_blobs(&block); + let (correctness_proof, completeness_proof) = + da_service.get_extraction_proof(&block, &txs).await; + + let verifier = CelestiaVerifier::new(RollupParams { namespace }); + + // give verifier empty txs list + let error = verifier + .verify_relevant_tx_list(&block.header, &[], correctness_proof, completeness_proof) + .unwrap_err(); + + assert!(error.to_string().contains("Transaction missing")); + } + + #[tokio::test] + async fn verification_fails_if_not_all_etxs_are_proven() { + let block = with_rollup_data::filtered_block(); + let (_, _, da_service, namespace) = setup_service(None).await; + + let txs = da_service.extract_relevant_blobs(&block); + let (mut correctness_proof, completeness_proof) = + da_service.get_extraction_proof(&block, &txs).await; + + // drop the proof for last etx + correctness_proof.pop(); + + let verifier = CelestiaVerifier::new(RollupParams { namespace }); + + let error = verifier + .verify_relevant_tx_list(&block.header, &txs, correctness_proof, completeness_proof) + .unwrap_err(); + + assert!(error.to_string().contains("not all blobs proven")); + } + + #[tokio::test] + async fn verification_fails_if_there_is_less_blobs_than_proofs() { + let block = with_rollup_data::filtered_block(); + let (_, _, da_service, namespace) = setup_service(None).await; + + let txs = da_service.extract_relevant_blobs(&block); + let (mut correctness_proof, completeness_proof) = + da_service.get_extraction_proof(&block, &txs).await; + + // push one extra etx proof + correctness_proof.push(correctness_proof[0].clone()); + + let verifier = CelestiaVerifier::new(RollupParams { namespace }); + + let error = verifier + .verify_relevant_tx_list(&block.header, &txs, correctness_proof, completeness_proof) + .unwrap_err(); + + assert!(error.to_string().contains("more proofs than blobs")); + } + + #[tokio::test] + #[should_panic] + async fn verification_fails_for_incorrect_namespace() { + let block = with_rollup_data::filtered_block(); + let (_, _, da_service, _) = setup_service(None).await; + + let txs = da_service.extract_relevant_blobs(&block); + let (correctness_proof, completeness_proof) = + da_service.get_extraction_proof(&block, &txs).await; + + // create a verifier with a different namespace than the da_service + let verifier = CelestiaVerifier::new(RollupParams { + namespace: Namespace::new_v0(b"abc").unwrap(), + }); + + let _panics = verifier.verify_relevant_tx_list( + &block.header, + &txs, + correctness_proof, + completeness_proof, + ); + } } diff --git a/adapters/celestia/src/header_response.json b/adapters/celestia/src/header_response.json deleted file mode 100644 index 0e38a057d..000000000 --- a/adapters/celestia/src/header_response.json +++ /dev/null @@ -1,1566 +0,0 @@ -{ - "validator_set": { - "validators": [ - { - "address": "762CBA617226A799D898F134DD12661C7F1129EB", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "6bdjjKHELaN9colwYy/ad+xh3MUgOVq106ZFucK46LE=" - }, - "voting_power": "21002592", - "proposer_priority": "-180122426" - }, - { - "address": "F5BB6F48121E1C99D26BCD27B8F00893C1DCADCF", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "vfOT6bT2LXrfBsjpirOcVxdHhTAjVqoD9Q0TOKFAbkA=" - }, - "voting_power": "20772846", - "proposer_priority": "20772846" - }, - { - "address": "4B7A5D6E4A2AC4753C1B7645D784DEC12C5383AA", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "68dDxVgm9Ahj8BW82g3nY1gfvq2muEeoeDdnJ340LWs=" - }, - "voting_power": "20440785", - "proposer_priority": "20440785" - }, - { - "address": "BA8A15ED6466EBE3B132D91CD86F98867B28B006", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "MYb6LWgYFVekDhYajl/nI8KDGa7Stv2YsEZkrWcrvIw=" - }, - "voting_power": "20394270", - "proposer_priority": "20394270" - }, - { - "address": "2F192A85E9117A3C166584F8AB20B315D8788C5E", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "eBoP7WNwXdpB1AGb2Ny4Rwi1DP3x/3m72h2GVZqdwik=" - }, - "voting_power": "20181600", - "proposer_priority": "20181600" - }, - { - "address": "8FBEF0DBAAE51D918F9ABFD96653F4932171870E", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "J8wAKpZrD9kRbewJDMLQAuYkYNh33PhCg+wcdVcGxIw=" - }, - "voting_power": "19900303", - "proposer_priority": "19900303" - }, - { - "address": "F14BA4525613719E6E8DD72BB40628B585D5CA53", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "g/tHi4xmmiav/N012t0TsqyWuJzWEb13h+VO0kbjEwQ=" - }, - "voting_power": "19819201", - "proposer_priority": "19819201" - }, - { - "address": "E1570712868BE0B12622BBAE08D96F5840F9D018", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "OHADjBcHrd2mld9DQre1MkiFKYN+7vlFou+eB6Ia/qc=" - }, - "voting_power": "19633245", - "proposer_priority": "19633245" - }, - { - "address": "0B76107110A486E8767FA1997EA0C4B40B7851AF", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "D3wP4IWMZcMt9UHVzFP2J19FDCP/732nvtT/f3CIatQ=" - }, - "voting_power": "19432240", - "proposer_priority": "19432240" - }, - { - "address": "3ACD2DA41FFBB07743A99A74A0FACEACF3A9E474", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "z4VR8LSO7jeupkwifkLevWc+dSloLq9dMME7k0nVqOQ=" - }, - "voting_power": "18000056", - "proposer_priority": "18000056" - }, - { - "address": "10C13814B94303B35EFFFAB319301714A4D69399", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "FULjq8Uf1zsGbs3izSZSBYRvr6k597hLmIEeBwHuEEc=" - }, - "voting_power": "500422", - "proposer_priority": "500422" - }, - { - "address": "6BDDFED21432C64D5F71E325CD6AF6FB16790391", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "fe6/iNJr7px2M5mr1aXkvt7f26+2qDM+LHo3eZHA/38=" - }, - "voting_power": "210358", - "proposer_priority": "210358" - }, - { - "address": "4875AD200AADE12185455050EA07E424D945FF64", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "gGsm+RV9RdfpNnrXbg8eZamm2fMtMh1P4E/eZRtFk1g=" - }, - "voting_power": "51092", - "proposer_priority": "51092" - }, - { - "address": "BB408FA902A7B6A938C788957B2A874153261EC5", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "7rE47Ol0HqaZhnQQqlx5BD8rNGv6yTcRQANXtMY7y4g=" - }, - "voting_power": "20856", - "proposer_priority": "20856" - }, - { - "address": "41FE068E3E8537EB35932A517836C1694C804746", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "2UV7v+AE3lX51fGs+h42FgLimv/SIYW0twAdOekck78=" - }, - "voting_power": "11693", - "proposer_priority": "11693" - }, - { - "address": "2E008FC8A683B806F82206DE091A9433515319CA", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "v5y5IraSyBzvIfcInHZ4groGSn1hokTe1k71QHtxbzE=" - }, - "voting_power": "11599", - "proposer_priority": "11599" - }, - { - "address": "6C3E4F3C92DE15FBC788F91747D8944586E9A1C0", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "y67VON5o65OicItNcd0S8MNd/22e1CGlRZpZXetC6DA=" - }, - "voting_power": "11366", - "proposer_priority": "11366" - }, - { - "address": "793B51BF408858FD72EC4198140D385C4076A208", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "x+JRxnYtuwPeHmNQOZ541uZzxoJd1cTUUGQ2+YmkMO8=" - }, - "voting_power": "11339", - "proposer_priority": "11339" - }, - { - "address": "7E2CB2FA8C46CDCA5BE02B0D656A0D61533445EC", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "yU84HuMOysbfwrWM6TA0PnCvn5jptbo5mYFHuZwo67c=" - }, - "voting_power": "11216", - "proposer_priority": "11216" - }, - { - "address": "22089FFD99FBBED4B77B88948F7D749C2784A1CF", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "MlFR9fiA74zWCEViKydSIlUo1igsgyq0JRhUQ7wi7WA=" - }, - "voting_power": "11196", - "proposer_priority": "11196" - }, - { - "address": "7C9538967D87E25FAD17A1180EEB6F174E8BAD84", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "I8gELA5RucvoH6gYz6iNErFmaQ4ZeMQkHWpAOe7NoGM=" - }, - "voting_power": "11158", - "proposer_priority": "11158" - }, - { - "address": "5808D8604443E8C1B11835FA8D7250FCFDAAB8AE", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "sJfNT8M3V8mMTvzHFJhKcgH5OgsTP9TCTPU/mgAMM3o=" - }, - "voting_power": "11027", - "proposer_priority": "11027" - }, - { - "address": "9C4395A21313734E9E322D55655EC55DEB9ABA81", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "IvkUe2jzMHymGsbvo0Ctu42gv7kS371g2VsC7PKA+ms=" - }, - "voting_power": "10860", - "proposer_priority": "10860" - }, - { - "address": "0590EF6F6AB31A98C786B2D4B639C62CB39F954B", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "VlBSILXJpT55YzMfgibI49GTnDqhzHZsfRtn457kMlI=" - }, - "voting_power": "10815", - "proposer_priority": "10815" - }, - { - "address": "22EFAD458C05DDDD14A001C52FEF84EC3D17A688", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "dD3BcTI3BXW8fEytVHFHOPcb759/02vxBju3fzeMSRA=" - }, - "voting_power": "10697", - "proposer_priority": "10697" - }, - { - "address": "80726DCD4E975716843F213C7E5A36400ABCFDFE", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "sAD409SvbABZBr0vCmjL6JK6XVQgQP4RM7v/Oiz/f88=" - }, - "voting_power": "10646", - "proposer_priority": "10646" - }, - { - "address": "7763A9F623F458DF0FEB7298E3FE1D67CECDA1FD", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "UQrlm/vvxj/tEKUf/G05seIT09ygG7q29r4czyemwSQ=" - }, - "voting_power": "10625", - "proposer_priority": "10625" - }, - { - "address": "CF238AB6BAF500A7623789F8EA7F3BCB95150574", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "jI1cMvsmHEU86V0N5JsTV5o+Yv3eqBal+QgjGRhb/4w=" - }, - "voting_power": "10592", - "proposer_priority": "10592" - }, - { - "address": "38A9BFEBAD0FA8A5E9E5AFB1A0AE01C1EA85729C", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "Igw5QFJdLJEqOrsYxYVUdcWi1bbQDIIFLxbigfEXmqk=" - }, - "voting_power": "10464", - "proposer_priority": "10464" - }, - { - "address": "87697AD05E817F183CBAC23D6F711EFFC73E7CB1", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "yUsnxCQpmHyY7/UHKzgtx/k4l+n2g1OlP+DOpkdQztU=" - }, - "voting_power": "10386", - "proposer_priority": "10386" - }, - { - "address": "FD17755AE1B0FE464B4F9996598EF9A51F602DB3", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "yLHp1YFCKUSWf51N58x2aG6TRs9GKZdtE+InCL1Zj0o=" - }, - "voting_power": "10362", - "proposer_priority": "10362" - }, - { - "address": "813D78C65701B7A792FB5F2DB7D4DBDEFCD7EE66", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "Wn8yx1RrmPdby+gqI7ErqCOJlJP05T4alnvdnu7vnxY=" - }, - "voting_power": "10342", - "proposer_priority": "10342" - }, - { - "address": "0003D94C13CDF1FC0A119AB6CDF454E6BFBCA4C3", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "NfIcNuLvvTabv/qz+/rgozADT/B+Wc6Aw2SzxrijaqY=" - }, - "voting_power": "10327", - "proposer_priority": "10327" - }, - { - "address": "284DBBD8B2BBF32E4004A817473D05E3A66C5C4A", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "b7Y258ExeJsXOai7gP9DupujCVbf2AlrJv1mltTGi30=" - }, - "voting_power": "10280", - "proposer_priority": "10280" - }, - { - "address": "372FF124AF7D42DCE4FF40952E3E21D43C33C60D", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "Kbv0oySUstYPOiDcJyJypDSWHHkgbUdWvItYruU3jSw=" - }, - "voting_power": "10266", - "proposer_priority": "10266" - }, - { - "address": "38CECDD91DAE3D8F536C080742A69FEBB6EFDBFF", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "RYrMwDTpJWv67sFVwKtmS+UxlN7hljKGWJqOsutcB1s=" - }, - "voting_power": "10263", - "proposer_priority": "10263" - }, - { - "address": "21AECD90BAD73E77875F641299469C2A348D79CB", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "G6ZLhPaJHQCBqUvs1M2K3QJT9vcXM7Qi73Jjo28d9Dg=" - }, - "voting_power": "10213", - "proposer_priority": "10213" - }, - { - "address": "268BCEBBE4D9075F9DE971DE34A636B4316B5EC5", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "cYlH04/HHOVMAN/vOkS3R+jP2z4dyDQmnFKSnvOgPts=" - }, - "voting_power": "10196", - "proposer_priority": "10196" - }, - { - "address": "312ED92F9E7A65BC80DFDB59AB05BD85754E7E95", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "CWxVE0hu9cUZ4fGJrxkxW9ZYT1pH5mA4O04Y0JOnkvM=" - }, - "voting_power": "10133", - "proposer_priority": "10133" - }, - { - "address": "68D747F35AE07631AF2E94CFF06BA30A9EFC8712", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "ILj5kerxa/UjJ9L4eAgi3rpteIp5b63Smyqysn4T3WU=" - }, - "voting_power": "10131", - "proposer_priority": "10131" - }, - { - "address": "EA14DE7F3FE756943AFDFDC23A2C8720E80F2C79", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "JAgIlYqwwU+nW/CoSnXPVn1YV8Wg4zb19i84kKMbIwk=" - }, - "voting_power": "10127", - "proposer_priority": "10127" - }, - { - "address": "B5698E7725F2D206A6652D945F75E9FDA2219F9D", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "Y+07AMlGx0La8Sr9aTMTbMMI9cuv9mrHQCWom921Uug=" - }, - "voting_power": "10112", - "proposer_priority": "10112" - }, - { - "address": "887BB5E1B4DBFF7117434FE7567CB8F2DC1B1F38", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "FjLtJraEKp4JoQEy8cI6L6haRkgO9CHh9dTZwbQk3gM=" - }, - "voting_power": "10110", - "proposer_priority": "10110" - }, - { - "address": "407DF17CD5EB1967597FC14A15A9D883DE884C93", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "VkA34n9aydhX5Gl0mBga06j7/ThNuBljC/T33ZP/Cdk=" - }, - "voting_power": "10108", - "proposer_priority": "10108" - }, - { - "address": "EE927F115AD4FD4BC131CA311D332ADDAC62CCAC", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "SfFXATPCbIeRUsb9RP+BfdYOH0v7H2aGbPGx1HlR1I0=" - }, - "voting_power": "10107", - "proposer_priority": "10107" - }, - { - "address": "A133A0F5CF118D107DC6326322460783F380C0E5", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "bb8zgZaww2FRaACEhEB+R6P9N1CRuRW+mHP1vKOqc/0=" - }, - "voting_power": "10099", - "proposer_priority": "10099" - }, - { - "address": "E1F1D0F31F8DB86B829C8502AD1F6F1576C08A6D", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "4TpozS4FflJmfBg3MYhLw12UeVUSELod0fdnX4tAQgs=" - }, - "voting_power": "10099", - "proposer_priority": "10099" - }, - { - "address": "7EA692099F6AC9243306305AC28F565716FA742E", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "CVnpHUel7h9T/ABhMZeBD6gY93vA1fQAHhtSVwBJZ4E=" - }, - "voting_power": "10094", - "proposer_priority": "10094" - }, - { - "address": "CAE9614B39D1778BE2CD1762A64483560E5534B5", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "tZbAmUfwTL4+11BLl37z0TWm30T3FiX0YFb+jAP/RQ0=" - }, - "voting_power": "10068", - "proposer_priority": "10068" - }, - { - "address": "1C9D8799534B609225D4D82A44170DAF701353BC", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "veSz82g7u1EDkIy/6njEouXOn9SsUvPIdM2Lq17y/uE=" - }, - "voting_power": "10063", - "proposer_priority": "10063" - }, - { - "address": "6B52C0F9B4B0DF280F2B5D03BCA20A35C401DAD4", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "A6uzLPMTtomXe/MJedCaag1Ab6/c3+quxY0+aqsb3PY=" - }, - "voting_power": "10059", - "proposer_priority": "10059" - }, - { - "address": "65D4634E13C159BAA5614AE04AB89C7DF4B2942C", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "C4GYT5RIkZq70YG5YKQEksjrNdmf/7gfqVSVzgbjsJE=" - }, - "voting_power": "10056", - "proposer_priority": "10056" - }, - { - "address": "9B2FB5ABCB3B0289D03AF5A8577C76D9A37A32D3", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "D9iPx6VTH+xeewFFWfnI+a7dM6Y5Y9h/5S4WHro5wUQ=" - }, - "voting_power": "10056", - "proposer_priority": "10056" - }, - { - "address": "FD32C8FD6F4E01D67824BC6651F5848FD9833B60", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "G2oUHMcHFiLj80Xos7eZ79cmuckcGaVjvxJu6mo493s=" - }, - "voting_power": "10051", - "proposer_priority": "10051" - }, - { - "address": "EE60B0B33489E72FE69363A1D7F0C78626CD2D39", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "/WPPLH7f2moY5LysZFN/JTz4k7dWw9pXLM13OKAgFAs=" - }, - "voting_power": "10042", - "proposer_priority": "10042" - }, - { - "address": "435A073DEBA3A3C6A183D9CDA397B38B6AAEEAB2", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "qy+/bkSpy3ECas02SU9WTW54EASXH5LCVN59gQJCLRo=" - }, - "voting_power": "10039", - "proposer_priority": "10039" - }, - { - "address": "3AFA32AE613CCD73ACF136BC86FF4DEF87A33067", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "ub0Db6WsbwOhhJTTEcbMvXEIQzhU3PeAPtQ8thwfOy4=" - }, - "voting_power": "10020", - "proposer_priority": "10020" - }, - { - "address": "F9D6A9FA1C7090F710DD3AC5CEB0E9005C327C13", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "GF+zUO8Y2cj77V0PFYXh79w69fSBRyeU3aANKdEqAnI=" - }, - "voting_power": "10019", - "proposer_priority": "10019" - }, - { - "address": "F271741FA4B163F3204A5BC2570E74F146AA33D9", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "Y0SMVwQK9FHbFi7ykZSwah3TG56saeI0uTMXYvyb9dY=" - }, - "voting_power": "10011", - "proposer_priority": "10011" - }, - { - "address": "4520D7FA09B49EA0C0F39E6788E6EB3FD8065690", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "27PwyrHHY/ndxqNrL6SdHomL4hR3JAJ5QMdQFYRlS9I=" - }, - "voting_power": "10010", - "proposer_priority": "10010" - }, - { - "address": "DFE827625C00547FA79217CB67CF589918218C48", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "WnsbPkUFx6VaBJbCdVcxUTFrJCDTL+F62GvY4RcZlGk=" - }, - "voting_power": "10004", - "proposer_priority": "10004" - }, - { - "address": "F26493B9ECA784977CC180B87D63FFE6BAC17D7A", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "gGUWARrFV6eOXFztsDx3M0OkithkyB8dIwGcCZ09fDA=" - }, - "voting_power": "9997", - "proposer_priority": "9997" - }, - { - "address": "4267ED67A7D1BF6794AE82F8D753E2A0C3D64DC6", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "w8dRnys1OowhERYxNXutFVTmyO4OrGVnZvqp0rvjnyA=" - }, - "voting_power": "9993", - "proposer_priority": "9993" - }, - { - "address": "C1B5400696B35C32195A495D65EEFAC0384AF5CC", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "3ykhgEeQmtBkH5m41lVTsNUI6YbI1RfZQbf2N2alJ3Y=" - }, - "voting_power": "9961", - "proposer_priority": "9961" - }, - { - "address": "4DB394CEDF34FC204B77DB197D6109328976D60F", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "FFC5Gn/KC44bJbP7MsXXEU+TqcPQmr5WbTYyjSMFPU8=" - }, - "voting_power": "9540", - "proposer_priority": "9540" - }, - { - "address": "7F5741D855DEF211FD849490B7A3145E77B426D7", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "jScQj1XJP5LWrC2d2/K1rE7wQBJYKD1EUq/iSG8uA1I=" - }, - "voting_power": "9493", - "proposer_priority": "9493" - }, - { - "address": "362F53ADD7675FB953B8B3A464970DA4646C4E15", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "3FPbUrTA67ulv315kwLG7LKDYdCg116uZASh03XLe6k=" - }, - "voting_power": "9474", - "proposer_priority": "9474" - }, - { - "address": "872E62AC3C2FD1D15DCEF5EF6682D9FE9351D5DB", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "k6BfqvfCoF71+2qIPW93JDnMZCyq8Eqc6V7gH2e/gmE=" - }, - "voting_power": "9317", - "proposer_priority": "9317" - }, - { - "address": "621BC4A3D36FE764F265ADC923F61F5EA166535B", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "540IwDgI8G001UeKp/TVCmiVz4De1smsjPtX4VJFYWk=" - }, - "voting_power": "9202", - "proposer_priority": "9202" - }, - { - "address": "C2FE6292FC9E194D4CA1A7BF94EEB73AF9BFC942", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "MeCdisSprXXkpHbmMeyGahixpKGc/MqOykJ9lvM2B48=" - }, - "voting_power": "9015", - "proposer_priority": "9015" - }, - { - "address": "13E2D73B12F6A5D5EE0392D934FCCEDA180279F2", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "WI2fnwW67ArvQKJNygzaJtjfQxfHo4nRIliAMdXvDLE=" - }, - "voting_power": "9009", - "proposer_priority": "9009" - }, - { - "address": "110F48CDE021C66B9B00441D0A6B24D358608607", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "eXFJj8jJ0TwvoB1sqiDIIFy4UywE95w2YDLPaaVHxrk=" - }, - "voting_power": "8263", - "proposer_priority": "8263" - }, - { - "address": "D441D1654E5D553AA3ABFE79C1A83BD05D235481", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "jNcAWPD4QUo40mPd4gdN05SCz7J5lM2ul+HpKaXGZIk=" - }, - "voting_power": "8241", - "proposer_priority": "8241" - }, - { - "address": "E10AF7D9C4B73147556BD60F7F00D4329A6621D3", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "BM6w4poaymqrZxhVLanZVv9wYgUy14aNlU2xLsMCtlc=" - }, - "voting_power": "8090", - "proposer_priority": "8090" - }, - { - "address": "391992B82F055BFF434D33F77A9F948BD9E2589A", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "nzYiQ54mcjTNZKutjCMN2IOcrDOL9LIYz8g8TJWUPGM=" - }, - "voting_power": "8040", - "proposer_priority": "8040" - }, - { - "address": "2712C8EDD3AD2C26C172778DBBBB61A7526F6E42", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "XLHu1GTKY2fYQvuBNcXjTvLKehA6GHihyjuFwsjYtf4=" - }, - "voting_power": "8019", - "proposer_priority": "8019" - }, - { - "address": "01876159421B01400B604A934795331E8FA47B4C", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "f8IbP3iRCQcZwjRxbLFAg7zsqBFZ1nd+ZJQnba4gUdg=" - }, - "voting_power": "8013", - "proposer_priority": "8013" - }, - { - "address": "87DAF996F5A1DA31AED86F5DD561061B8FEDCC54", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "XW11O9X36REBo1s2UsCz9OvdtUACj3wuDNDYahZXZZ0=" - }, - "voting_power": "7539", - "proposer_priority": "7539" - }, - { - "address": "B6BAE3EF71F632D2413A2B885774E55BE6F80DCC", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "sMJkChpVRPjW4Q1Fu8uOaeWTiUkoY8Da6SnM9xqz7oA=" - }, - "voting_power": "7396", - "proposer_priority": "7396" - }, - { - "address": "508C89522C08EA0D8055169BB385049E7CA17713", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "3Jhy2NQuGHQJm+CG5w9eZCMqAEa9lDE9qDpEm+Q8oZY=" - }, - "voting_power": "7360", - "proposer_priority": "7360" - }, - { - "address": "78C3EA77FAAE609808CD9B370747FE91AC975975", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "Jxx3aSz4P5vVNGutYBgnqRHzFadFWZU4k+Rw3/0pgvI=" - }, - "voting_power": "7197", - "proposer_priority": "7197" - }, - { - "address": "7F7262D42CB524A4CFB6FDEEEB12E544AE646AA6", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "W957lcHW6J+Go+flDcF1ieAWceEgAAdzsFFscLqTxjk=" - }, - "voting_power": "7167", - "proposer_priority": "7167" - }, - { - "address": "E81805AE862CBCA6340C601B6B760C9EF81E9924", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "ALaMW6hL7rhphdP+40F6Un6B1WZx37F+aYb7W0X3zSs=" - }, - "voting_power": "6633", - "proposer_priority": "6633" - }, - { - "address": "982DF85B40A0A57A9E497B8EC99896514AFD2AC1", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "XiXvtiUtyK7MgaxXxLTGKjZOEn8diBeoX29tfZsgdWM=" - }, - "voting_power": "6602", - "proposer_priority": "6602" - }, - { - "address": "7EFA1652F535CB4656BF875A989108A5D1516A58", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "576sT4WYt917pvGKNWwQAx8JkqkJTQGApvm1I06yVcE=" - }, - "voting_power": "6482", - "proposer_priority": "6482" - }, - { - "address": "AFC1C10252EEA4DE4D58C79B5E0CEDFE1B563B31", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "nDxk+ZJCcu11EnARaAVL4pWqZcVw3ZiDuERZnv/STpA=" - }, - "voting_power": "6135", - "proposer_priority": "6135" - }, - { - "address": "003CE561FE12D19F3724E383AD34B09163000048", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "RwRwkuOZrgQeQE+h/oHQN1GxM/1L0O+yDIWk/znMDPE=" - }, - "voting_power": "6024", - "proposer_priority": "6024" - }, - { - "address": "D77BA150C3AF6A439A2FA633335A279A09233B88", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "D8w1PQzJE4TQf5D9tcAT1koqBl/5kEe3xJVaunmrJ20=" - }, - "voting_power": "6000", - "proposer_priority": "6000" - }, - { - "address": "455A139106AC6CBDB8B37FC122484A18D3F08C07", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "3eIOFHYma7RENPkxPinJPriZj7t8fQtjdkPv4f3h4KY=" - }, - "voting_power": "5839", - "proposer_priority": "5839" - }, - { - "address": "4BC89C831A51124E7369972522A87F40B32EA123", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "zCqGKgAvyU35q74Fk07cQYu7NEnVV62vWoznhnyEF3c=" - }, - "voting_power": "5590", - "proposer_priority": "5590" - }, - { - "address": "FE589F7693F67ABF93BE9A09F86E2A88F78D98FE", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "ZLKB7N7d4GpLkpbeY+UHxCIpTUVA0ZUsChC7lef9I1Q=" - }, - "voting_power": "5529", - "proposer_priority": "5529" - }, - { - "address": "EB34C75A2B3770CD51E44DDB3B05CA6E4EE91092", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "Bv8W8qEPSg0VHE31zUQg33MOonibOUeFrqsbRqv3MjY=" - }, - "voting_power": "5238", - "proposer_priority": "5238" - }, - { - "address": "CD10B5095AC4541972E0C0FDE35A39CF92A040DA", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "cPgsoC0NIVUAe42qmzfbrLaznvYZCh1z92Q35D0KZOc=" - }, - "voting_power": "5083", - "proposer_priority": "5083" - }, - { - "address": "FD255FBDD520E45A8FDD0759023666F1FAEB2717", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "d4bugP3aJbyT8wzjTN9F3KgevJyc0sDe0UlcwPey39A=" - }, - "voting_power": "5022", - "proposer_priority": "5022" - }, - { - "address": "7258C2395933ABD935E87892938C524479C719BA", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "5dgQvunUGMrVCEoxcfzyhFaobgLcQhzzYDxD1dXGtYY=" - }, - "voting_power": "4931", - "proposer_priority": "4931" - }, - { - "address": "84E6AD7091614E72C39B6535E6202CDB21B80B75", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "4cH1l1egPZMt1zwy74++mxsgOosswUTcV15++xdkb6g=" - }, - "voting_power": "4616", - "proposer_priority": "4616" - }, - { - "address": "FAE6061472E8D5D9F334D84F616C9EF7AFDD7791", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "XzN7zj5Z3CVtfxgae1Xy4iGUpMB8xebR467TVfulfzM=" - }, - "voting_power": "4237", - "proposer_priority": "4237" - }, - { - "address": "19DF075073BF8F05041672EE46FE8CC8D4D76E7B", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "S2viPopSwW43/wJSp3npRR69Ct86quYn8s8mV75anvo=" - }, - "voting_power": "4168", - "proposer_priority": "4168" - }, - { - "address": "A156D613D6B5A5552FBCE6E1FDD4F2DE440C8EDE", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "D5XC1wIyYlxDM5aaTght0t2893hDe4xTzwEnU5QBo5w=" - }, - "voting_power": "4011", - "proposer_priority": "4011" - }, - { - "address": "843B76EBB527F82BB7A31B553745BD2675DB9FFE", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "qEOZMGp7fJ5pBa10fZfv5770XkJCBwhZwqGftFKeRdo=" - }, - "voting_power": "3140", - "proposer_priority": "3140" - } - ], - "proposer": { - "address": "762CBA617226A799D898F134DD12661C7F1129EB", - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "6bdjjKHELaN9colwYy/ad+xh3MUgOVq106ZFucK46LE=" - }, - "voting_power": "21002592", - "proposer_priority": "-180122426" - } - }, - "header": { - "version": { - "block": "11" - }, - "chain_id": "mocha", - "height": "428545", - "time": "2023-03-11T15:43:19.088215294Z", - "last_block_id": { - "hash": "8FAB396B01B0781B309D2EB438F41FA6A76AA28308AF2FB84D200C756AB48975", - "parts": { - "total": 1, - "hash": "76A87FAAA0D8AF60A6D3DD8DE57FED54E9068B633D11A089ADC38D3C18922741" - } - }, - "last_commit_hash": "4FC44DDEF86A36A3AA544F7A913E2F1675A27CEF312E0034747A02D4A560251A", - "data_hash": "C6FA94EA5B4640A69C830C6A1BB6B86F04800B852F8650B28CDD5CE7E3A307DD", - "validators_hash": "FA8B443035B476A1D6B704C36CF1460D229D5927BCFD93197E680B2EB63F4568", - "next_validators_hash": "FA8B443035B476A1D6B704C36CF1460D229D5927BCFD93197E680B2EB63F4568", - "consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F", - "app_hash": "6B4205AE7CEE329C7E49E8C4A102D7B8107CB604AFAA0EE87CFAC1704E8D6461", - "last_results_hash": "EF4931FB9F6CCCA0C5FE8367BCF5044E785247ADDE925C6FE6B7C200E43EEFA5", - "evidence_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", - "proposer_address": "E1570712868BE0B12622BBAE08D96F5840F9D018" - }, - "commit": { - "height": "428545", - "round": 0, - "block_id": { - "hash": "32381A0B7262F15F081ACEF769EE59E6BB4C42C1013A3EEE23967FBF32B86AE6", - "parts": { - "total": 1, - "hash": "9CDA9660AB08F3DF418A00E7C26B80C7B10D0D8373DA0FAC079E07822404AA64" - } - }, - "signatures": [ - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "F5BB6F48121E1C99D26BCD27B8F00893C1DCADCF", - "timestamp": "2023-03-11T15:43:28.180810651Z", - "signature": "nWCjsA6fce/udBn+NhxuDFSNQEF8TBC0INjtCmaKib3ubgPDJPMU3RCccFnEu0c9RP+pzpE211BcPT/5tloVDw==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "BA8A15ED6466EBE3B132D91CD86F98867B28B006", - "timestamp": "2023-03-11T15:43:28.166418838Z", - "signature": "mKQmIV/y41YAlDToPkfr4bMtCnug3QLQjWSnNW0XjgSVjEUThP6yS3o5B71g2nyJ4CKcTGo0xj773YckxZWXCg==" - }, - { - "block_id_flag": 2, - "validator_address": "2F192A85E9117A3C166584F8AB20B315D8788C5E", - "timestamp": "2023-03-11T15:43:28.159821271Z", - "signature": "Zn7Qb8m5TRE1HyzmLl2pxpwPo/oE73N2A5MCvtPM0N28udvX5Cg6w507zx12wGmLgCjY3PA37v7vgL+c2CYQAQ==" - }, - { - "block_id_flag": 2, - "validator_address": "8FBEF0DBAAE51D918F9ABFD96653F4932171870E", - "timestamp": "2023-03-11T15:43:28.116803321Z", - "signature": "ObzC3chz1Ive1NKot4ObuF3678U4pSsHhWHeYdbArW6xwt1ed5y0ZYAYCjXmO/HAWG53i9N5BShe3WrR/F5UCg==" - }, - { - "block_id_flag": 2, - "validator_address": "F14BA4525613719E6E8DD72BB40628B585D5CA53", - "timestamp": "2023-03-11T15:43:28.110211047Z", - "signature": "JOuPaoMU0/ab4vx6IeZ/hfNQeD/8TbTI1ar60abs1XEynX3z6rHA0az/j8V6pFtXdzFNj6sDkvBexXI/RHFzCg==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "0B76107110A486E8767FA1997EA0C4B40B7851AF", - "timestamp": "2023-03-11T15:43:28.18262002Z", - "signature": "ZvEydFwPzG4j0wFw/yqprZYKxO281GiZZvE6h5QZ9TyJ0bikOkvtTGDgOfro/11RQguCChlk0pUH1nreEiXFCA==" - }, - { - "block_id_flag": 2, - "validator_address": "3ACD2DA41FFBB07743A99A74A0FACEACF3A9E474", - "timestamp": "2023-03-11T15:43:28.191434275Z", - "signature": "DtoPOkMC9IoCE1ClAHuoPPOB7ebQo49aAgS3SiHNZFHBxj8GzTHhzfifH5oo26fORF3hf92coIP1Ls+wd/00Dg==" - }, - { - "block_id_flag": 2, - "validator_address": "10C13814B94303B35EFFFAB319301714A4D69399", - "timestamp": "2023-03-11T15:43:28.082386636Z", - "signature": "11zo5OsAXtkNGrVTv3M4FX/hFr/CN1+azqPxYWkBittjMctIxmPHIujqxBjdsGv4e6SY+OGxlkSAsiaqHSuuBw==" - }, - { - "block_id_flag": 2, - "validator_address": "6BDDFED21432C64D5F71E325CD6AF6FB16790391", - "timestamp": "2023-03-11T15:43:28.797197235Z", - "signature": "N0somrqRykjuNofRVcMU7FUBziSwjdkm9iG6SsOnmUKy+dPTAfZdH2DMteNGpuSb1HV+/CtKDKUVo8G2Iq6FBQ==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "41FE068E3E8537EB35932A517836C1694C804746", - "timestamp": "2023-03-11T15:43:28.197002267Z", - "signature": "AtTZZyheuxt0qDD3OIOlFdET0qHZ/oI2xQRgYSAfSD2JJj7rmF92hZkQsimigbtADL1HSez2FpFz1FhXZaqNDQ==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "7C9538967D87E25FAD17A1180EEB6F174E8BAD84", - "timestamp": "2023-03-11T15:43:28.184183273Z", - "signature": "/+h7w6+wqAA/RnIV83jzlP02O14G1meD4Yne75eHbyQA7NoH7wqGpqZybELkroZC8lwBt82uPHsSmiCDRQb/DA==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "0590EF6F6AB31A98C786B2D4B639C62CB39F954B", - "timestamp": "2023-03-11T15:43:28.187945292Z", - "signature": "159pGRUzMiNthepaM7A2Iv05J8Ge7sRaoc3NqcRv0s0wolq/u86LOGLv2OmjyZz3Cc4Ym97qMI0iu8KtUCppAA==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "7763A9F623F458DF0FEB7298E3FE1D67CECDA1FD", - "timestamp": "2023-03-11T15:43:28.172179653Z", - "signature": "KXWBW4M5ksRDlfcTi9FdOD82iw6CH7/rCjunvEyDlEBP703kFFoD4ctXht+Fym1HdgIiaZH09TadvkpKCXUeAQ==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "38A9BFEBAD0FA8A5E9E5AFB1A0AE01C1EA85729C", - "timestamp": "2023-03-11T15:43:28.154111321Z", - "signature": "tBpuE1BtK3HjacyLVtUt9U2mN61AcIQGEmMNktaeXIZQiYWMuelSMU8VcIR1pqgzZSPqQU6zHQgN4Xm5yle1BA==" - }, - { - "block_id_flag": 2, - "validator_address": "87697AD05E817F183CBAC23D6F711EFFC73E7CB1", - "timestamp": "2023-03-11T15:43:28.111707597Z", - "signature": "qvsWM5V9ukiIv7AJXEZ5bsITlmU21x+LGVq3ewX+tqHK+uSKChVrMZK8qn3Lu2n4NUE5zGk9ppCbrwskK+CxCg==" - }, - { - "block_id_flag": 2, - "validator_address": "FD17755AE1B0FE464B4F9996598EF9A51F602DB3", - "timestamp": "2023-03-11T15:43:28.171876321Z", - "signature": "74FIf3xxDBMOHLQWILZ6PyyIWRh62v/T30T2ZuPkOrvPLVhptq/K8ddXe2z3/4WNCqBLI+EDQprI5O0ZzhY7Bw==" - }, - { - "block_id_flag": 2, - "validator_address": "813D78C65701B7A792FB5F2DB7D4DBDEFCD7EE66", - "timestamp": "2023-03-11T15:43:28.12952191Z", - "signature": "CVLnhHgWeUyCk+qaJM/2+g+nhwT//6PtMooSP7L/OExSnbWaWzKAYwKuq/kS8CxDIAnCklzQjha+cd+zEAIGCA==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "372FF124AF7D42DCE4FF40952E3E21D43C33C60D", - "timestamp": "2023-03-11T15:43:28.184599672Z", - "signature": "nDuFMIdBd57TUl80KVtdPpP9ZFtWfCwm2scfpEm2LHk37OnquYWjyoWzNDK55zKd1g2DONjGxaRRbbUEvxygDQ==" - }, - { - "block_id_flag": 2, - "validator_address": "38CECDD91DAE3D8F536C080742A69FEBB6EFDBFF", - "timestamp": "2023-03-11T15:43:28.102479327Z", - "signature": "Ve+nNvgiYH14ULfORC9wA0uLEAGxIKH1RPOSrzREw4Lg4ddFS25u3fpuWytU6p4c7ia4mgT2qCTc1vEvKsVRAw==" - }, - { - "block_id_flag": 2, - "validator_address": "21AECD90BAD73E77875F641299469C2A348D79CB", - "timestamp": "2023-03-11T15:43:28.15950634Z", - "signature": "+GdAN+eKIkh2jH9RwPrij9QMUDInPyv335Yj3gWKJFe3fPzsPuQ3h1I1uvQE+OGwh56Ll9Y516qZIlM7X+hSCg==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "312ED92F9E7A65BC80DFDB59AB05BD85754E7E95", - "timestamp": "2023-03-11T15:43:28.102779303Z", - "signature": "0R2w+uwVsBpcVHCLs43VoXf3w2PW9N7/JcIdCH6NIRV9np3cPRSyRFY6eLPnhahPNRW4NahFeiy4sZ4bDzzAAw==" - }, - { - "block_id_flag": 2, - "validator_address": "68D747F35AE07631AF2E94CFF06BA30A9EFC8712", - "timestamp": "2023-03-11T15:43:28.140514568Z", - "signature": "I6pJWs2LxzI7e6sCa2iucMhaKdle/Aey7uIR7k7kF/5XbJUwKzfuuAARZnQtp90pUh5YEvbMWeYrPE3ELg0rBw==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "B5698E7725F2D206A6652D945F75E9FDA2219F9D", - "timestamp": "2023-03-11T15:43:28.091291962Z", - "signature": "p6Zch1EezvNoBKqxOhNL8cDq4ZQd5SCTdQUAgu4AfZdtm6CfXepYKCdOlxZ5csBD4mJYjn4+gVt6U0c9cyDADQ==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "407DF17CD5EB1967597FC14A15A9D883DE884C93", - "timestamp": "2023-03-11T15:43:28.122551515Z", - "signature": "+NauxoI5IcY5Jp51ve04pailWNhadZdPUkmXA49QW26KsO1LH12gMg3KVyKtng2fINFbdFIi+kMfFYgR6duRCw==" - }, - { - "block_id_flag": 2, - "validator_address": "EE927F115AD4FD4BC131CA311D332ADDAC62CCAC", - "timestamp": "2023-03-11T15:43:28.128870097Z", - "signature": "AdUGKiCKKM4hIVUKwG4YIbDddZg4qMSpQHHedhevlRVia3e3Veot6Rm2K3HNTD0vduqZIEnnLlv0nB5eQnpFAw==" - }, - { - "block_id_flag": 2, - "validator_address": "A133A0F5CF118D107DC6326322460783F380C0E5", - "timestamp": "2023-03-11T15:43:28.125689429Z", - "signature": "DYE8EzkbBbLHpG58WQn9i4+3qgD3IOalZLM7F5UyMn2ruotswhlsoNdpIGG7Uh9MvQ6WGPWBbPsElzHJD7FXAg==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "CAE9614B39D1778BE2CD1762A64483560E5534B5", - "timestamp": "2023-03-11T15:43:28.133336889Z", - "signature": "Ui4VoxJg4p2/Qvqg5ntUhw8ijGIskrelG42Q2Cw2uy4KWofuWMi5muFzsg+vBRUp+HN1Lm0jh9C0a5rOfyG9CQ==" - }, - { - "block_id_flag": 2, - "validator_address": "1C9D8799534B609225D4D82A44170DAF701353BC", - "timestamp": "2023-03-11T15:43:28.145620704Z", - "signature": "2HMa6MJDd5LEOvJ4xlHzFlqObwFcLIyXZp7zAabsrhQvrbt5O5NV6iOfEySXnz9yK+vWeEyHoPjVzB58NAbuBw==" - }, - { - "block_id_flag": 2, - "validator_address": "6B52C0F9B4B0DF280F2B5D03BCA20A35C401DAD4", - "timestamp": "2023-03-11T15:43:28.134630891Z", - "signature": "qR/s2VoNl+sh30C+zX9Caf5uRztCSEnVY90Lxz+PbHQRJptddo7+j/GnNgcTfb2p0w6RzzbXDecuYPY48iKvCg==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "9B2FB5ABCB3B0289D03AF5A8577C76D9A37A32D3", - "timestamp": "2023-03-11T15:43:28.161802909Z", - "signature": "uG+3qzkV8qfFsjCZ486wFjN02sQqhlYU3I9ToNqqywKVxLg0Xs8t6mLEVrn8lDhZPjBNVkQntr1206W8SouhBA==" - }, - { - "block_id_flag": 2, - "validator_address": "FD32C8FD6F4E01D67824BC6651F5848FD9833B60", - "timestamp": "2023-03-11T15:43:28.114619598Z", - "signature": "i7EI8cWaYmViBjR7d4BcQYAMECg1x6G+mzmC7PnvShFndQtgPiamMO5xDn3J+XI2p5vk0topje70NUSbAltuDg==" - }, - { - "block_id_flag": 2, - "validator_address": "EE60B0B33489E72FE69363A1D7F0C78626CD2D39", - "timestamp": "2023-03-11T15:43:28.169993508Z", - "signature": "zIAeotO89zC+MWqB53YvLGw+NrgXnJCgStoiQzH6ztmEgzkp50s9uxIy1XrFNRWqhoVwHrhu1wIExHEv26bZAQ==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "F9D6A9FA1C7090F710DD3AC5CEB0E9005C327C13", - "timestamp": "2023-03-11T15:43:28.121051699Z", - "signature": "W0PHa1m6PeHgnutNB8Bb61Py6lYgN90sa5oobOFcMgB4U4mvTA1DacspyNpF/qiELsE8IEELgNMFXzyBLV15Dw==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "DFE827625C00547FA79217CB67CF589918218C48", - "timestamp": "2023-03-11T15:43:28.143278939Z", - "signature": "g8TuSiSquR9j5xADm/B9FdDx0LIGd0djgl9kWxeDvRQ2MFhZbzLy+TiY6MJVMcM4OI0X9bl6B+LG1ZgwX5MoCA==" - }, - { - "block_id_flag": 2, - "validator_address": "F26493B9ECA784977CC180B87D63FFE6BAC17D7A", - "timestamp": "2023-03-11T15:43:28.122476314Z", - "signature": "tcOfmQnS8VG/S8LHhwE4RkKqGdMJZL2HgqEUyJPJ8nPIqni6iJ8Cw/VwmuQ6MZZKoJbynmwuulUXpB2nh9ElBA==" - }, - { - "block_id_flag": 2, - "validator_address": "4267ED67A7D1BF6794AE82F8D753E2A0C3D64DC6", - "timestamp": "2023-03-11T15:43:28.174180977Z", - "signature": "wLSFI5fnXdAyD/4ETtaIW65gtotj7X7nhbyadiRYqjKaLDTelgVHLhEcewC5y3U+uE5mlpS98QTVjqsYA2F3Ag==" - }, - { - "block_id_flag": 2, - "validator_address": "C1B5400696B35C32195A495D65EEFAC0384AF5CC", - "timestamp": "2023-03-11T15:43:28.175152676Z", - "signature": "rMdkgjnbsNUucD4EleHmOX0b1yJjizQxwZW6LguBJusA2guwP+Gt290OkEA3BRoKhwkkEgUiRCawHWBSuB3jAg==" - }, - { - "block_id_flag": 2, - "validator_address": "4DB394CEDF34FC204B77DB197D6109328976D60F", - "timestamp": "2023-03-11T15:43:28.149473816Z", - "signature": "xL3qkwlIxqLwDAAbjJ6nsxSnOqmblbACi2F/sP6Mu0N9dCR1YqXL/NBWTjuIHYOGqouqsRz4gPgAat4wxyILCg==" - }, - { - "block_id_flag": 2, - "validator_address": "7F5741D855DEF211FD849490B7A3145E77B426D7", - "timestamp": "2023-03-11T15:43:28.174092385Z", - "signature": "T6xLvnkyLx44vrsAv6u3I7OW7XpWDj5VL6fsLYzUSwr+LuUl+nqijP5aZ5eMMJNhK0Fa2+aY1ewl2Tz999FeCA==" - }, - { - "block_id_flag": 2, - "validator_address": "362F53ADD7675FB953B8B3A464970DA4646C4E15", - "timestamp": "2023-03-11T15:43:28.186962924Z", - "signature": "XE1guOo/Y4Rw3WhPaOxEmrg9nXeMVt4VqmZwdDQOWjLNw4481w+kupM2yNeaX19dEtkKBXXHfAZlLL1l+CZfCQ==" - }, - { - "block_id_flag": 2, - "validator_address": "872E62AC3C2FD1D15DCEF5EF6682D9FE9351D5DB", - "timestamp": "2023-03-11T15:43:28.151013058Z", - "signature": "OrHdcGzn36iTYwht9l0FOfITaUNhlPmzP2YDvFpDmspoLmL+o+rAn7XHJkzgEjFgEZGRR3b8EKhnzVYfVRlyAQ==" - }, - { - "block_id_flag": 2, - "validator_address": "621BC4A3D36FE764F265ADC923F61F5EA166535B", - "timestamp": "2023-03-11T15:43:28.085265928Z", - "signature": "W6QbJZm1boSBjcYkEeI9flOnlDNdvYEXzfYP9rLZVT4/g7ccpa0V6rpFMXazzbqKduAbzEE6t/Flj4/SvdILAg==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "110F48CDE021C66B9B00441D0A6B24D358608607", - "timestamp": "2023-03-11T15:43:28.104358197Z", - "signature": "KYcf3YtU5WrNMjgMBsn+szYgOtQeygpXWkIR07+11wJgdX2+sNDBgfI5iXHfJTBI4LxMC0y33HGOyOGZHknSCg==" - }, - { - "block_id_flag": 2, - "validator_address": "D441D1654E5D553AA3ABFE79C1A83BD05D235481", - "timestamp": "2023-03-11T15:43:28.06374813Z", - "signature": "ctCt8z/GIQq7AaCiMW8CnYZikpfj4RWgePYK0rvgv5VeheFlr51V9DSDSVSOe+VTrJ/xrHGFKef2gFbYqqNNBA==" - }, - { - "block_id_flag": 2, - "validator_address": "E10AF7D9C4B73147556BD60F7F00D4329A6621D3", - "timestamp": "2023-03-11T15:43:28.203523322Z", - "signature": "Khn2Mtb6EWykAwPJcHuPv/dAjWYx/c24eGev99JP/GO6hTCkRTYdPIZZDqY5/URVkx8FAVwm/mYr9QpNH2zNAg==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "E81805AE862CBCA6340C601B6B760C9EF81E9924", - "timestamp": "2023-03-11T15:43:28.210441238Z", - "signature": "nnbtOvu1aVCNrwFyUwxL5zW9RvPA58yE+iAvIlK+Ye4oyfttj6ZmEKYc28yIoZeaj5QV6GvcK56rMY2ss4c/DQ==" - }, - { - "block_id_flag": 2, - "validator_address": "982DF85B40A0A57A9E497B8EC99896514AFD2AC1", - "timestamp": "2023-03-11T15:43:28.157904077Z", - "signature": "cks6RYIFjuAa8MhoQ6pPM60GQnuF+Xs4LfLRlFPdmoUKDieAqXVqg2NQIl0rP1vZ0TUOBvQoBy3xRap8DZQsCg==" - }, - { - "block_id_flag": 2, - "validator_address": "7EFA1652F535CB4656BF875A989108A5D1516A58", - "timestamp": "2023-03-11T15:43:28.155662397Z", - "signature": "VJyY2A1uVsAhZipMbEaTL2kAUoZFTcWYgB9uG0ba0R1zjluyp0TAloyojYVVpVNhDMSYyg8MLzPwUMlqGqbUBg==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "D77BA150C3AF6A439A2FA633335A279A09233B88", - "timestamp": "2023-03-11T15:43:28.159412663Z", - "signature": "Ra/rTH319KQ+bJXjs3sUbEGydUcL91HrekFLBw4H7IVDfZU8lyEaKoULVztzc3QyoqRVqKVWb4UgrtP94W0MCw==" - }, - { - "block_id_flag": 2, - "validator_address": "455A139106AC6CBDB8B37FC122484A18D3F08C07", - "timestamp": "2023-03-11T15:43:28.094776708Z", - "signature": "KhsvGtrUbabKp0QWjb3Bs1M/fSq5v9yrlSfPEAqmMMI4tBHQihLW9eqD6h+mURsPA0E0Z/3BbC/67WsNoVl1CA==" - }, - { - "block_id_flag": 2, - "validator_address": "4BC89C831A51124E7369972522A87F40B32EA123", - "timestamp": "2023-03-11T15:43:28.164019635Z", - "signature": "IrdYdBW+gM6W9pEs/VVR4FAt2VhjWqEdudgSLtd1gMFPW1sLHqvur4NBB36Wbu0kBdfXoItVwDu2S5W03wirCA==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "EB34C75A2B3770CD51E44DDB3B05CA6E4EE91092", - "timestamp": "2023-03-11T15:43:28.195390071Z", - "signature": "Go19bP0BNxVrKuvYlHh9Nv3D9rBYMlsBpjFoZng0YoZ/0RFLgXwUSKJb8pkAkWXkeMCDTRJjl7ooC4bBZvxKBg==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 2, - "validator_address": "84E6AD7091614E72C39B6535E6202CDB21B80B75", - "timestamp": "2023-03-11T15:43:28.124138509Z", - "signature": "D4q33fyR8mLi150PdejvM8OvGHgNnrKCZBTKZSLY2I7PzsCiIrH9/YwR775LovalrTAT1laxx0TKFMtq65ipBg==" - }, - { - "block_id_flag": 2, - "validator_address": "FAE6061472E8D5D9F334D84F616C9EF7AFDD7791", - "timestamp": "2023-03-11T15:43:28.108977997Z", - "signature": "c3/0C4P/upPe2u3kU53o0OLTIvIhUWveZUC26aTXb15PzmhO/sRHKFAjCjFgxdy1yy74bk+qSuSGbUS7rwuvDQ==" - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - }, - { - "block_id_flag": 1, - "validator_address": "", - "timestamp": "0001-01-01T00:00:00Z", - "signature": null - } - ] - }, - "dah": { - "row_roots": [ - "AAAAAAAAAAGpbEw4Tw+RieW43CdU+gLFx/8YYdMc49a7Kooxz3D4QwJ0QRqkfxky", - "//////////7//////////u49pdZvjLX9NygV7pZlYPtcUao36hMUtpGXKS6YNfLD", - "/////////////////////xR57uHxvfFHajjhen1YKqiYkVXZc86mB+rfzmhQzKkm", - "/////////////////////9SJm2MM6D9mhN7wqTID0ij7cUE2TycrUvZsniEXxFaQ" - ], - "column_roots": [ - "AAAAAAAAAAH//////////lDn69+UwNpzCmbc5R5ycngM6mZvYPlcPsfGxxidwOR9", - "qWxMOE8PkYn//////////hhrybvNXHGF3HvpX5bwHcA5afrU/E/XsXNrVfUtiJzw", - "/////////////////////8f5M8znb8YBW3FotHFI0KKkGhttXscg5y7sez/r6u1d", - "/////////////////////wrtjZVtTDxn4TBn2wxTwWa1oMjhuHRFA4OxzrV5qcXA" - ] - } -} diff --git a/adapters/celestia/src/lib.rs b/adapters/celestia/src/lib.rs index 7638de48e..9dc631aa9 100644 --- a/adapters/celestia/src/lib.rs +++ b/adapters/celestia/src/lib.rs @@ -1,13 +1,12 @@ pub mod celestia; -pub mod shares; -pub use crate::celestia::*; - #[cfg(feature = "native")] mod da_service; -pub mod pfb; -pub mod share_commit; +pub mod shares; pub mod types; mod utils; pub mod verifier; + #[cfg(feature = "native")] pub use da_service::{CelestiaService, DaServiceConfig}; + +pub use crate::celestia::*; diff --git a/adapters/celestia/src/pfb.rs b/adapters/celestia/src/pfb.rs deleted file mode 100644 index f48f8b72d..000000000 --- a/adapters/celestia/src/pfb.rs +++ /dev/null @@ -1,60 +0,0 @@ -/// BlobTx wraps an encoded sdk.Tx with a second field to contain blobs of data. -/// The raw bytes of the blobs are not signed over, instead we verify each blob -/// using the relevant MsgPayForBlobs that is signed over in the encoded sdk.Tx. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlobTx { - #[prost(bytes = "bytes", tag = "1")] - pub tx: ::prost::bytes::Bytes, -} - -/// Tx is the standard type used for broadcasting transactions. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Tx { - /// body is the processable content of the transaction - #[prost(message, optional, tag = "1")] - pub body: ::core::option::Option, -} - -/// TxBody is the body of a transaction that all signers sign over. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TxBody { - /// messages is a list of messages to be executed. The required signers of - /// those messages define the number and order of elements in AuthInfo's - /// signer_infos and Tx's signatures. Each required signer address is added to - /// the list only the first time it occurs. - /// By convention, the first required signer (usually from the first message) - /// is referred to as the primary signer and pays the fee for the whole - /// transaction. - #[prost(message, repeated, tag = "1")] - pub messages: ::prost::alloc::vec::Vec<::prost_types::Any>, -} - -// @generated -/// MsgPayForBlobs pays for the inclusion of a blob in the block. -#[derive( - Clone, - PartialEq, - ::prost::Message, - serde::Deserialize, - serde::Serialize, - borsh::BorshDeserialize, - borsh::BorshSerialize, -)] -pub struct MsgPayForBlobs { - #[prost(string, tag = "1")] - pub signer: ::prost::alloc::string::String, - #[prost(bytes = "bytes", repeated, tag = "2")] - pub namespace_ids: ::prost::alloc::vec::Vec<::prost::bytes::Bytes>, - #[prost(uint32, repeated, tag = "3")] - pub blob_sizes: ::prost::alloc::vec::Vec, - /// share_commitments is a list of share commitments (one per blob). - #[prost(bytes = "bytes", repeated, tag = "4")] - pub share_commitments: ::prost::alloc::vec::Vec<::prost::bytes::Bytes>, - /// share_versions are the versions of the share format that the blobs - /// associated with this message should use when included in a block. The - /// share_versions specified must match the share_versions used to generate the - /// share_commitment in this message. - #[prost(uint32, repeated, tag = "8")] - pub share_versions: ::prost::alloc::vec::Vec, -} -// @@protoc_insertion_point(module) diff --git a/adapters/celestia/src/share_commit.rs b/adapters/celestia/src/share_commit.rs deleted file mode 100644 index 3dafc0653..000000000 --- a/adapters/celestia/src/share_commit.rs +++ /dev/null @@ -1,117 +0,0 @@ -use tendermint::crypto::default::Sha256; -use tendermint::merkle::simple_hash_from_byte_vectors; - -use crate::shares::{self, Share}; - -// /// Calculates the size of the smallest square that could be used to commit -// /// to this message, following Celestia's "non-interactive default rules" -// /// https://github.com/celestiaorg/celestia-app/blob/fbfbf111bcaa056e53b0bc54d327587dee11a945/docs/architecture/adr-008-blocksize-independent-commitment.md -// fn min_square_size(message: &[u8]) -> usize { -// let square_size = message.len().next_power_of_two(); -// if message.len() < (square_size * square_size - 1) { -// return square_size; -// } else { -// return square_size << 1; -// } -// } - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum CommitmentError { - ErrMessageTooLarge, -} - -impl std::fmt::Display for CommitmentError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("ErrMessageTooLarge") - } -} - -impl std::error::Error for CommitmentError {} - -/// Derived from -pub fn recreate_commitment( - square_size: usize, - shares: shares::BlobRef, -) -> Result<[u8; 32], CommitmentError> { - if shares.0.len() > (square_size * square_size) - 1 { - return Err(CommitmentError::ErrMessageTooLarge); - } - - let heights = power_of_2_mountain_range(shares.0.len(), square_size); - let mut leaf_sets: Vec<&[Share]> = Vec::with_capacity(heights.len()); - let mut cursor = 0; - for height in heights { - leaf_sets.push(&shares.0[cursor..cursor + height]); - cursor += height; - } - - let mut subtree_roots = Vec::with_capacity(leaf_sets.len()); - for set in leaf_sets { - let mut tree = nmt_rs::CelestiaNmt::new(); - for share in set { - let nid = share.namespace(); - tree.push_leaf(share.as_serialized(), nid) - .expect("Leaves are pushed in order"); - } - subtree_roots.push(tree.root()); - } - let h = simple_hash_from_byte_vectors::(&subtree_roots); - Ok(h) -} - -// power_of_2_mountain_range returns the heights of the subtrees for binary merkle -// mountain range -fn power_of_2_mountain_range(mut len: usize, square_size: usize) -> Vec { - let mut output = Vec::new(); - - while len != 0 { - if len >= square_size { - output.push(square_size); - len -= square_size; - } else { - let p = next_lower_power_of_2(len); - output.push(p); - len -= p; - } - } - output -} - -/// returns the largest power of 2 that is less than or equal to the input -/// Examples: -/// - next_lower_power_of_2(2): 2 -/// - next_lower_power_of_2(3): 2 -/// - next_lower_power_of_2(7): 4 -/// - next_lower_power_of_2(8): 8 -fn next_lower_power_of_2(num: usize) -> usize { - if num.is_power_of_two() { - num - } else { - num.next_power_of_two() >> 1 - } -} - -mod nmt { - // /// Build an nmt from leaves that are already prefixed with their namespace - // pub fn build_nmt_from_namespaced_leaves(namespaced_leaves: &[impl AsRef<[u8]>]) -> [u8; 48] { - // let mut tree = CelestiaNmt::new(); - // for leaf in namespaced_leaves.iter() { - // let namespace: NamespaceId = leaf.as_ref()[..8] - // .as_ref() - // .try_into() - // .expect("Namespace length is correct"); - // tree.push_leaf(&leaf.as_ref()[8..], namespace) - // .expect("Leaves are pushed in order"); - // } - // tree.root().0 - // } - - // pub fn build_nmt(leaves: &[(impl AsRef<[u8]>, NamespaceId)]) -> [u8; 48] { - // let mut tree = CelestiaNmt::new(); - // for (leaf, ns) in leaves { - // tree.push_leaf(leaf.as_ref(), *ns); - // } - - // tree.root().0 - // } -} diff --git a/adapters/celestia/src/shares.rs b/adapters/celestia/src/shares.rs index 3f543bfca..ace2a97a7 100644 --- a/adapters/celestia/src/shares.rs +++ b/adapters/celestia/src/shares.rs @@ -1,123 +1,29 @@ -use std::fmt::Display; - -use base64::engine::general_purpose::STANDARD as B64_ENGINE; -use base64::Engine; use borsh::{BorshDeserialize, BorshSerialize}; -use nmt_rs::{NamespaceId, NAMESPACE_ID_LEN}; -use prost::bytes::{Buf, BytesMut}; -use prost::encoding::decode_varint; -use prost::DecodeError; -use serde::de::Error; -use serde::{Deserialize, Serialize, Serializer}; +use celestia_types::nmt::{Namespace, NS_SIZE}; +use celestia_types::NamespacedShares; +use prost::bytes::Buf; +use serde::{Deserialize, Serialize}; use sov_rollup_interface::Bytes; -use tracing::{error, info}; +use crate::utils::read_varint; use crate::verifier::PFB_NAMESPACE; /// The length of the "reserved bytes" field in a compact share -pub const RESERVED_BYTES_LEN: usize = 4; +pub const RESERVED_BYTES_LEN: usize = + celestia_types::consts::appconsts::COMPACT_SHARE_RESERVED_BYTES; /// The length of the "info byte" field in a compact share -pub const INFO_BYTE_LEN: usize = 1; +pub const INFO_BYTE_LEN: usize = celestia_types::consts::appconsts::SHARE_INFO_BYTES; /// The length of the "sequence length" field -pub const SEQUENCE_LENGTH_BYTES: usize = 4; - -/// Skip over a varint. Returns the number of bytes read -pub fn skip_varint(mut bytes: impl Buf) -> Result { - // A varint may contain up to 10 bytes - for i in 0..10 { - // If the continuation bit is not set, we're done - if bytes.get_u8() < 0x80 { - return Ok(i + 1); - } - } - Err(ErrInvalidVarint) -} - -/// Read a varint. Returns the value (as a u64) and the number of bytes read -pub fn read_varint(mut bytes: impl Buf) -> Result<(u64, usize), DecodeError> { - let original_len = bytes.remaining(); - let varint = decode_varint(&mut bytes)?; - Ok((varint, original_len - bytes.remaining())) -} - -#[derive(Debug, PartialEq)] -pub struct ErrInvalidVarint; - +pub const SEQUENCE_LENGTH_BYTES: usize = celestia_types::consts::appconsts::SEQUENCE_LEN_BYTES; /// The size of a share, in bytes -const SHARE_SIZE: usize = 512; -/// The size of base64 encoded share, in bytes -const B64_SHARE_SIZE: usize = 684; - -#[derive( - Debug, Clone, PartialEq, serde::Serialize, Deserialize, BorshDeserialize, BorshSerialize, -)] -/// A group of shares, in a single namespace -pub enum NamespaceGroup { - Compact(Vec), - Sparse(Vec), -} +pub const SHARE_SIZE: usize = celestia_types::consts::appconsts::SHARE_SIZE; -#[derive(Debug, Clone, PartialEq, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] pub enum Share { Continuation(Bytes), Start(Bytes), } -impl AsRef<[u8]> for Share { - fn as_ref(&self) -> &[u8] { - match self { - Share::Continuation(c) => c.as_ref(), - Share::Start(s) => s.as_ref(), - } - } -} - -impl Serialize for Share { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let inner_bytes = match self { - Share::Continuation(b) => b, - Share::Start(b) => b, - }; - serializer.serialize_bytes(inner_bytes.as_ref()) - } -} - -impl<'de> Deserialize<'de> for Share { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let mut share = ::deserialize(deserializer)?; - if share.len() == B64_SHARE_SIZE { - let mut decoded = BytesMut::zeroed(SHARE_SIZE); - // TODO: https://github.com/marshallpierce/rust-base64/issues/210 - B64_ENGINE - .decode_slice_unchecked(share, &mut decoded[..]) - .map_err(|_| Error::custom("Invalid base64 encoding"))?; - share = decoded.freeze() - } - if share.len() != SHARE_SIZE { - // let expected = Unexpected::Bytes(&share); - return Err(Error::invalid_length(share.len(), &"A share of length 512")); - } - if is_continuation_unchecked(share.as_ref()) { - return Ok(Share::Continuation(share)); - } - Ok(Share::Start(share)) - } -} - -fn is_continuation_unchecked(share: &[u8]) -> bool { - share[8] & 0x01 == 0 -} - -fn enforce_version_zero(share: &[u8]) { - assert_eq!(share[8] & !0x01, 0) -} - #[derive(Debug, Clone, PartialEq, Copy)] pub enum ShareError { NotAStartShare, @@ -134,6 +40,10 @@ impl Share { } } + pub fn from_slice(slice: impl AsRef<[u8]>) -> Self { + Self::new(Bytes::copy_from_slice(slice.as_ref())) + } + pub fn as_serialized(&self) -> &[u8] { self.raw_inner_ref() } @@ -150,7 +60,7 @@ impl Share { Share::Continuation(_) => Err(ShareError::NotAStartShare), Share::Start(inner) => { let mut inner = inner.clone(); - inner.advance(9); + inner.advance(NS_SIZE + INFO_BYTE_LEN); Ok(inner.get_u32() as u64) } } @@ -184,7 +94,7 @@ impl Share { // FIXME: account for continuation vs. start shares match self { Share::Continuation(_) => { - let reserved_bytes_offset = NAMESPACE_ID_LEN + INFO_BYTE_LEN; + let reserved_bytes_offset = NS_SIZE + INFO_BYTE_LEN; let mut raw = self.raw_inner(); raw.advance(reserved_bytes_offset); let idx_of_next_start = raw.get_u32() as usize; @@ -201,7 +111,7 @@ impl Share { fn get_data_offset(&self) -> usize { // All shares are prefixed with metadata including the namespace (8 bytes), and info byte (1 byte) - let mut offset = NAMESPACE_ID_LEN + INFO_BYTE_LEN; + let mut offset = NS_SIZE + INFO_BYTE_LEN; // Start shares are also prefixed with a sequence length if let Self::Start(_) = self { offset += SEQUENCE_LENGTH_BYTES; @@ -226,10 +136,11 @@ impl Share { } /// Get the namespace associated with this share - pub fn namespace(&self) -> NamespaceId { - let mut out = [0u8; 8]; - out.copy_from_slice(&self.raw_inner_ref()[..8]); - NamespaceId(out) + pub fn namespace(&self) -> Namespace { + let out: [_; NS_SIZE] = self.raw_inner_ref()[..NS_SIZE] + .try_into() + .expect("can't fail for correct size"); + nmt_rs::NamespaceId(out).into() } pub fn is_valid_tx_start(&self, idx: usize) -> bool { @@ -257,96 +168,40 @@ impl Share { } } -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum ShareParsingError { - ErrInvalidBase64, - ErrWrongLength, -} - -impl Display for ShareParsingError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl AsRef<[u8]> for Share { + fn as_ref(&self) -> &[u8] { match self { - ShareParsingError::ErrInvalidBase64 => { - f.write_str("ShareParsingError::ErrInvalidBase64") - } - ShareParsingError::ErrWrongLength => f.write_str("ShareParsingError::ErrWrongLength"), + Share::Continuation(c) => c.as_ref(), + Share::Start(s) => s.as_ref(), } } } -impl std::error::Error for ShareParsingError {} - -impl NamespaceGroup { - pub fn from_b64(b64: &str) -> Result { - if b64.is_empty() { - error!("Empty input"); - return Err(ShareParsingError::ErrWrongLength); - } - - let mut decoded = Vec::with_capacity((b64.len() + 3) / 4 * 3); - - // unsafe { decoded.set_len((b64.len() / 4 * 3)) } - if let Err(err) = B64_ENGINE.decode_slice(b64, &mut decoded) { - info!("Error decoding NamespaceGroup from base64: {}", err); - return Err(ShareParsingError::ErrInvalidBase64); - } - let mut output: Bytes = decoded.into(); - if output.len() % SHARE_SIZE != 0 { - error!( - "Wrong length: Expected a multiple of 512, got: {}", - output.len() - ); - return Err(ShareParsingError::ErrWrongLength); - } - let mut shares = Vec::with_capacity((output.len() / 512) + 1); - while output.len() > SHARE_SIZE { - shares.push(Share::new(output.split_to(SHARE_SIZE))); - } - shares.push(Share::new(output)); - // Check whether these shares come from a reserved (compact) namespace - - if shares[0].namespace().is_reserved() { - Ok(Self::Compact(shares)) - } else { - Ok(Self::Sparse(shares)) - } - } +fn is_continuation_unchecked(share: &[u8]) -> bool { + share[NS_SIZE] & 0x01 == 0 +} - pub fn from_b64_shares(encoded_shares: &Vec) -> Result { - if encoded_shares.is_empty() { - return Ok(Self::Sparse(vec![])); - } - let mut shares = Vec::with_capacity(encoded_shares.len()); - for share in encoded_shares { - let decoded_vec = B64_ENGINE - .decode(share) - .map_err(|_| ShareParsingError::ErrInvalidBase64)?; - if decoded_vec.len() != 512 { - return Err(ShareParsingError::ErrWrongLength); - } - let share = Share::new(decoded_vec.into()); - shares.push(share) - } +fn enforce_version_zero(share: &[u8]) { + assert_eq!(share[NS_SIZE] & !0x01, 0) +} - if shares[0].namespace().is_reserved() { - Ok(Self::Compact(shares)) - } else { - Ok(Self::Sparse(shares)) - } - } +/// A group of shares, in a single namespace +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] +pub enum NamespaceGroup { + Compact(Vec), + Sparse(Vec), +} - // Panics if less than 1 share is provided - pub fn from_shares_unchecked(shares: Vec>) -> Self { - let shares: Vec = shares +impl NamespaceGroup { + pub fn from_shares(shares: I) -> Self + where + I: IntoIterator, + B: AsRef<[u8]>, + { + shares .into_iter() - .map(|share| Share::new(Bytes::from(share))) - .collect(); - - if shares[0].namespace().is_reserved() { - Self::Compact(shares) - } else { - Self::Sparse(shares) - } + .map(|bytes| Share::new(Bytes::copy_from_slice(bytes.as_ref()))) + .collect() } pub fn shares(&self) -> &Vec { @@ -362,7 +217,34 @@ impl NamespaceGroup { shares: self, } } + + pub fn is_empty(&self) -> bool { + self.shares().is_empty() + } +} + +impl FromIterator for NamespaceGroup { + fn from_iter>(iter: T) -> Self { + let shares: Vec<_> = iter.into_iter().collect(); + if shares.is_empty() { + // if there are no shares at all return sparse empty group + return NamespaceGroup::Sparse(vec![]); + } + + if shares[0].namespace().is_reserved() { + NamespaceGroup::Compact(shares) + } else { + NamespaceGroup::Sparse(shares) + } + } } + +impl From<&NamespacedShares> for NamespaceGroup { + fn from(value: &NamespacedShares) -> Self { + Self::from_shares(value.rows.iter().flat_map(|row| row.shares.iter())) + } +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] pub struct Blob(pub Vec); @@ -604,16 +486,12 @@ impl<'a> std::iter::Iterator for NamespaceIterator<'a> { return Some(BlobRef::with(&shares[start..self.offset])); } } - // let start = self.offset; - // let length = 0; - // loop { - - // } } } #[cfg(test)] mod tests { + use celestia_types::nmt::NS_ID_V0_SIZE; use postcard::{from_bytes, to_allocvec, Result}; use proptest::collection::vec; use proptest::prelude::*; @@ -635,11 +513,19 @@ mod tests { assert_eq!(share, decoded_share); } - fn share_bytes_strategy() -> impl Strategy> { - vec(0u8.., 9..=SHARE_SIZE).prop_map(|mut vec| { - vec[8] &= 0x01; - vec - }) + prop_compose! { + fn share_bytes_strategy()( + ns in vec(0u8.., NS_ID_V0_SIZE), + mut share in vec(0u8.., SHARE_SIZE), + ) -> Vec { + let namespace = Namespace::new_v0(&ns).expect("doesn't exceed size"); + // overwrite namespace + share[..NS_SIZE].copy_from_slice(namespace.as_ref()); + // set version to zero + share[NS_SIZE] &= 0x01; + + share + } } proptest! { diff --git a/adapters/celestia/src/types.rs b/adapters/celestia/src/types.rs index 951fca0d7..b606d801b 100644 --- a/adapters/celestia/src/types.rs +++ b/adapters/celestia/src/types.rs @@ -1,81 +1,36 @@ use std::collections::HashMap; - -use anyhow::ensure; -use base64::engine::general_purpose::STANDARD as B64_ENGINE; -use base64::Engine; -use borsh::{BorshDeserialize, BorshSerialize}; -pub use nmt_rs::NamespaceId; +use std::slice::Chunks; + +use anyhow::{bail, ensure}; +// use borsh::{BorshDeserialize, BorshSerialize}; +use celestia_proto::celestia::blob::v1::MsgPayForBlobs; +use celestia_types::consts::appconsts::SHARE_SIZE; +/// Reexport the [`Namespace`] from `celestia-types` +pub use celestia_types::nmt::Namespace; +use celestia_types::nmt::{NamespacedHash, Nmt, NS_SIZE}; +use celestia_types::{ + DataAvailabilityHeader, ExtendedDataSquare, ExtendedHeader, NamespacedShares, ValidateBasic, +}; use serde::{Deserialize, Serialize}; use sov_rollup_interface::da::BlockHeaderTrait; use sov_rollup_interface::services::da::SlotData; use sov_rollup_interface::Bytes; -use tendermint::crypto::default::Sha256; -use tendermint::merkle; +use tracing::debug; -use crate::pfb::MsgPayForBlobs; -use crate::shares::{NamespaceGroup, Share}; +use crate::shares::NamespaceGroup; use crate::utils::BoxError; -use crate::verifier::{ChainValidityCondition, PARITY_SHARES_NAMESPACE}; -use crate::{CelestiaHeader, TxPosition}; - -#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] -pub struct RpcNamespacedShares { - #[serde(rename = "Proof")] - pub proof: JsonNamespaceProof, - #[serde(rename = "Shares")] - pub shares: Vec, -} - -#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] -pub struct JsonNamespaceProof { - #[serde(rename = "Start")] - start: usize, - #[serde(rename = "End")] - end: usize, - #[serde(rename = "Nodes")] - nodes: Option>, -} - -#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] -pub struct ExtendedDataSquare { - pub data_square: Vec, - pub codec: String, -} - -impl ExtendedDataSquare { - pub fn square_size(&self) -> Result { - let len = self.data_square.len(); - let square_size = (len as f64).sqrt() as usize; - ensure!( - square_size * square_size == len, - "eds size {} is not a perfect square", - len - ); - Ok(square_size) - } +use crate::verifier::{ChainValidityCondition, PARITY_SHARES_NAMESPACE, PFB_NAMESPACE}; +use crate::{parse_pfb_namespace, CelestiaHeader, TxPosition}; - pub fn rows(&self) -> Result, BoxError> { - let square_size = self.square_size()?; - - let mut output = Vec::with_capacity(square_size); - for i in 0..square_size { - let row_start = i * square_size; - let row_end = (i + 1) * square_size; - output.push(&self.data_square[row_start..row_end]) - } - Ok(output) - } -} - -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] // TODO: , BorshSerialize, BorshDeserialize)] pub struct FilteredCelestiaBlock { pub header: CelestiaHeader, pub rollup_data: NamespaceGroup, /// A mapping from blob commitment to the PFB containing that commitment /// for each blob addressed to the rollup namespace pub relevant_pfbs: HashMap, - /// All rows in the extended data square which contain rollup data - pub rollup_rows: Vec, + /// All rollup shares as they appear in extended data square, with proofs + pub rollup_rows: NamespacedShares, /// All rows in the extended data square which contain pfb data pub pfb_rows: Vec, } @@ -104,6 +59,46 @@ impl SlotData for FilteredCelestiaBlock { } impl FilteredCelestiaBlock { + pub fn new( + rollup_ns: Namespace, + header: ExtendedHeader, + rollup_rows: NamespacedShares, + etx_rows: NamespacedShares, + data_square: ExtendedDataSquare, + ) -> Result { + // validate the extended data square + data_square.validate()?; + + let rollup_data = NamespaceGroup::from(&rollup_rows); + let tx_data = NamespaceGroup::from(&etx_rows); + + // Parse out all of the rows containing etxs + debug!("Parsing namespaces..."); + let pfb_rows = + get_rows_containing_namespace(PFB_NAMESPACE, &header.dah, data_square.rows()?)?; + + // Parse out the pfds and store them for later retrieval + debug!("Decoding pfb protobufs..."); + let pfbs = parse_pfb_namespace(tx_data)?; + let mut pfb_map = HashMap::new(); + for tx in pfbs { + for (idx, nid) in tx.0.namespaces.iter().enumerate() { + if nid == rollup_ns.as_bytes() { + // TODO: Retool this map to avoid cloning txs + pfb_map.insert(tx.0.share_commitments[idx].clone().into(), tx.clone()); + } + } + } + + Ok(FilteredCelestiaBlock { + header: CelestiaHeader::new(header.dah, header.header.into()), + rollup_data, + relevant_pfbs: pfb_map, + rollup_rows, + pfb_rows, + }) + } + pub fn square_size(&self) -> usize { self.header.square_size() } @@ -124,128 +119,257 @@ impl FilteredCelestiaBlock { } } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, thiserror::Error)] pub enum ValidationError { + #[error("Missing data hash in header")] MissingDataHash, + + #[error("Data root hash doesn't match computed one")] InvalidDataRoot, + + #[error("Invalid etx proof: {0}")] InvalidEtxProof(&'static str), + + #[error("Transaction missing")] MissingTx, + + #[error("Invalid row proof")] InvalidRowProof, + + #[error("Invalid signer")] InvalidSigner, + + #[error("Incomplete data")] IncompleteData, + + #[error(transparent)] + DahValidation(#[from] celestia_types::ValidationError), } impl CelestiaHeader { pub fn validate_dah(&self) -> Result<(), ValidationError> { - let rows_iter = self.dah.row_roots.iter(); - let cols_iter = self.dah.column_roots.iter(); - let byte_vecs: Vec<&NamespacedHash> = rows_iter.chain(cols_iter).collect(); - let root = merkle::simple_hash_from_byte_vectors::(&byte_vecs); + self.dah.validate_basic()?; let data_hash = self .header .data_hash .as_ref() .ok_or(ValidationError::MissingDataHash)?; - if root != data_hash.0 { + if self.dah.hash != data_hash.0 { return Err(ValidationError::InvalidDataRoot); } Ok(()) } } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] +pub trait ExtendedDataSquareExt { + fn square_size(&self) -> Result; + + fn rows(&self) -> Result>, BoxError>; + + fn validate(&self) -> Result<(), BoxError>; +} + +impl ExtendedDataSquareExt for ExtendedDataSquare { + fn square_size(&self) -> Result { + let len = self.data_square.len(); + let square_size = (len as f64).sqrt() as usize; + ensure!( + square_size * square_size == len, + "eds size {} is not a perfect square", + len + ); + Ok(square_size) + } + + fn rows(&self) -> Result>, BoxError> { + let square_size = self.square_size()?; + Ok(self.data_square.chunks(square_size)) + } + + fn validate(&self) -> Result<(), BoxError> { + let len = self.square_size()?; + ensure!(len * len == self.data_square.len(), "Invalid square size"); + + if let Some(share) = self + .rows() + .expect("after first check this must succeed") + .flatten() + .find(|shares| shares.len() != SHARE_SIZE) + { + bail!("Invalid share size: {}", share.len()) + } + Ok(()) + } +} + +#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)] // TODO: , BorshSerialize, BorshDeserialize)] pub struct Row { - pub shares: Vec, + pub shares: Vec>, pub root: NamespacedHash, } impl Row { - pub fn merklized(&self) -> CelestiaNmt { - let mut nmt = CelestiaNmt::new(); + pub fn merklized(&self) -> Nmt { + let mut nmt = Nmt::new(); for (idx, share) in self.shares.iter().enumerate() { // Shares in the two left-hand quadrants are prefixed with their namespace, while parity - // shares (in the right-hand) quadrants always have the PARITY_SHARES_NAMESPACE + // shares (in the right-hand) quadrants should always be treated as PARITY_SHARES_NAMESPACE let namespace = if idx < self.shares.len() / 2 { - share.namespace() + share_namespace_unchecked(share) } else { PARITY_SHARES_NAMESPACE }; - nmt.push_leaf(share.as_serialized(), namespace) + nmt.push_leaf(share.as_ref(), *namespace) .expect("shares are pushed in order"); } - assert_eq!(&nmt.root(), &self.root); nmt } } -#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)] -pub struct StringWrapper { - #[serde(rename = "/")] - pub inner: String, +/// get namespace from a share without verifying if it's a correct namespace +/// (version 0 or parity ns). +fn share_namespace_unchecked(share: &[u8]) -> Namespace { + nmt_rs::NamespaceId( + share[..NS_SIZE] + .try_into() + .expect("must succeed for correct size"), + ) + .into() } -#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)] -pub struct RpcNamespacedSharesResponse(pub Option>); +fn get_rows_containing_namespace<'a>( + nid: Namespace, + dah: &'a DataAvailabilityHeader, + data_square_rows: impl Iterator]>, +) -> Result, BoxError> { + let mut output = vec![]; + + for (row, root) in data_square_rows.zip(dah.row_roots.iter()) { + if root.contains(*nid) { + output.push(Row { + shares: row.to_vec(), + root: root.clone(), + }) + } + } + Ok(output) +} -use nmt_rs::simple_merkle::proof::Proof; -use nmt_rs::{ - CelestiaNmt, NamespaceProof, NamespacedHash, NamespacedSha2Hasher, NAMESPACED_HASH_LEN, -}; +#[cfg(test)] +pub mod tests { + use celestia_types::nmt::Namespace; + use celestia_types::{ExtendedDataSquare, ExtendedHeader, NamespacedShares}; + + use super::FilteredCelestiaBlock; + use crate::verifier::PFB_NAMESPACE; + + pub const ROLLUP_NAMESPACE: Namespace = Namespace::const_v0(*b"\0\0sov-test"); + + pub mod with_rollup_data { + use super::*; + + pub const HEADER_JSON: &str = + include_str!("../test_data/block_with_rollup_data/header.json"); + pub const ROLLUP_ROWS_JSON: &str = + include_str!("../test_data/block_with_rollup_data/rollup_rows.json"); + pub const ETX_ROWS_JSON: &str = + include_str!("../test_data/block_with_rollup_data/etx_rows.json"); + pub const EDS_JSON: &str = include_str!("../test_data/block_with_rollup_data/eds.json"); + + pub fn filtered_block() -> FilteredCelestiaBlock { + filtered_block_from_jsons( + ROLLUP_NAMESPACE, + HEADER_JSON, + ROLLUP_ROWS_JSON, + ETX_ROWS_JSON, + EDS_JSON, + ) + } + } -impl From for NamespaceProof { - fn from(val: JsonNamespaceProof) -> Self { - NamespaceProof::PresenceProof { - proof: Proof { - siblings: val - .nodes - .unwrap_or_default() - .into_iter() - .map(|v| ns_hash_from_b64(&v.inner)) - .collect(), - start_idx: val.start as u32, - }, - ignore_max_ns: true, + pub mod without_rollup_data { + use super::*; + + pub const HEADER_JSON: &str = + include_str!("../test_data/block_without_rollup_data/header.json"); + pub const ROLLUP_ROWS_JSON: &str = + include_str!("../test_data/block_without_rollup_data/rollup_rows.json"); + pub const ETX_ROWS_JSON: &str = + include_str!("../test_data/block_without_rollup_data/etx_rows.json"); + pub const EDS_JSON: &str = include_str!("../test_data/block_without_rollup_data/eds.json"); + + pub fn filtered_block() -> FilteredCelestiaBlock { + filtered_block_from_jsons( + ROLLUP_NAMESPACE, + HEADER_JSON, + ROLLUP_ROWS_JSON, + ETX_ROWS_JSON, + EDS_JSON, + ) } } -} -fn ns_hash_from_b64(input: &str) -> NamespacedHash { - let mut output = [0u8; NAMESPACED_HASH_LEN]; - B64_ENGINE - .decode_slice(input, &mut output[..]) - .expect("must be valid b64"); - NamespacedHash(output) -} + fn filtered_block_from_jsons( + ns: Namespace, + header: &str, + rollup_rows: &str, + etx_rows: &str, + eds: &str, + ) -> FilteredCelestiaBlock { + let header: ExtendedHeader = serde_json::from_str(header).unwrap(); + let rollup_rows: NamespacedShares = serde_json::from_str(rollup_rows).unwrap(); + let etx_rows: NamespacedShares = serde_json::from_str(etx_rows).unwrap(); + let eds: ExtendedDataSquare = serde_json::from_str(eds).unwrap(); + + FilteredCelestiaBlock::new(ns, header, rollup_rows, etx_rows, eds).unwrap() + } -#[cfg(test)] -mod tests { - - // use nmt_rs::{NamespaceProof, NamespacedSha2Hasher}; - - // use super::{ns_hash_from_b64, RpcNamespacedSharesResponse}; - - // const ROW_ROOTS: &[&'static str] = &[ - // "AAAAAAAAAAEAAAAAAAAAAT4A1HvHQCYkf1sQ7zmTJH11jd1Hxn+YCcC9mIGbl1WJ", - // "c292LXRlc3T//////////vSMLQPlgfwCOf4QTkOhMnQxk6ra3lI+ybCMfUyanYSd", - // "/////////////////////wp55V2JEu8z3LhdNIIqxbq6uvpyGSGu7prq67ajVVAt", - // "/////////////////////7gaLStbqIBiy2pxi1D68MFUpq6sVxWBB4zdQHWHP/Tl", - // ]; - - // TODO: Re-enable this test after Celestia releases an endpoint which returns nmt proofs instead of - // ipld.Proofs - // #[test] - // fn test_known_good_msg() { - // let msg = r#"[{"Proof":{"End":1,"Nodes":[{"/":"bagao4amb5yatb7777777777773777777777777tjxe2jqsatxobgu3jqwkwsefsxscursxyaqzvvrxzv73aphwunua"},{"/":"bagao4amb5yatb77777777777777777777777776yvm54zu2vfqwyhd2nsebctxar7pxutz6uya7z3m2tzsmdtshjbm"}],"Start":0},"Shares":["c292LXRlc3QBKHsia2V5IjogInRlc3RrZXkiLCAidmFsdWUiOiAidGVzdHZhbHVlIn0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="]}]"#; - // let deserialized: RpcNamespacedSharesResponse = - // serde_json::from_str(msg).expect("message must deserialize"); - - // let root = ns_hash_from_b64(ROW_ROOTS[0]); - - // for row in deserialized.0.expect("shares response is not empty") { - // let proof: NamespaceProof = row.proof.into(); - // proof - // .verify_range(&root, &row.shares, ROLLUP_NAMESPACE) - // .expect("proof should be valid"); - // } - // } + #[test] + fn filtered_block_with_rollup_data() { + let block = with_rollup_data::filtered_block(); + + // valid dah + block.header.validate_dah().unwrap(); + + // single rollup share + assert_eq!(block.rollup_data.shares().len(), 1); + assert_eq!(block.rollup_rows.rows.len(), 1); + assert_eq!(block.rollup_rows.rows[0].shares.len(), 1); + assert!(block.rollup_rows.rows[0].proof.is_of_presence()); + + // 3 pfbs at all but only one belongs to rollup + assert_eq!(block.pfb_rows.len(), 1); + let pfbs_count = block.pfb_rows[0] + .shares + .iter() + .filter(|share| share.starts_with(PFB_NAMESPACE.as_ref())) + .count(); + assert_eq!(pfbs_count, 3); + assert_eq!(block.relevant_pfbs.len(), 1); + } + + #[test] + fn filtered_block_without_rollup_data() { + let block = without_rollup_data::filtered_block(); + + // valid dah + block.header.validate_dah().unwrap(); + + // no rollup shares + assert_eq!(block.rollup_data.shares().len(), 0); + // we still get single row, but with absence proof and no shares + assert_eq!(block.rollup_rows.rows.len(), 1); + assert_eq!(block.rollup_rows.rows[0].shares.len(), 0); + assert!(block.rollup_rows.rows[0].proof.is_of_absence()); + + // 2 pfbs at all and no relevant + assert_eq!(block.pfb_rows.len(), 1); + let pfbs_count = block.pfb_rows[0] + .shares + .iter() + .filter(|share| share.starts_with(PFB_NAMESPACE.as_ref())) + .count(); + assert_eq!(pfbs_count, 2); + assert_eq!(block.relevant_pfbs.len(), 0); + } } diff --git a/adapters/celestia/src/utils.rs b/adapters/celestia/src/utils.rs index 87a9f17a1..2e0714c94 100644 --- a/adapters/celestia/src/utils.rs +++ b/adapters/celestia/src/utils.rs @@ -1 +1,12 @@ +use prost::encoding::decode_varint; +use prost::DecodeError; +use sov_rollup_interface::Buf; + pub type BoxError = anyhow::Error; + +/// Read a varint. Returns the value (as a u64) and the number of bytes read +pub fn read_varint(mut bytes: impl Buf) -> Result<(u64, usize), DecodeError> { + let original_len = bytes.remaining(); + let varint = decode_varint(&mut bytes)?; + Ok((varint, original_len - bytes.remaining())) +} diff --git a/adapters/celestia/src/verifier/address.rs b/adapters/celestia/src/verifier/address.rs index 064422ec4..fc7455564 100644 --- a/adapters/celestia/src/verifier/address.rs +++ b/adapters/celestia/src/verifier/address.rs @@ -1,135 +1,52 @@ use std::fmt::{Display, Formatter}; use std::str::FromStr; -use bech32::WriteBase32; -use borsh::{BorshDeserialize, BorshSerialize}; -use thiserror::Error; +use anyhow::Context; +use celestia_types::state::{AccAddress, AddressKind, AddressTrait}; +// use borsh::{BorshDeserialize, BorshSerialize}; +use serde::{Deserialize, Serialize}; -/// Human Readable Part: "celestia" for Celestia network -const HRP: &str = "celestia"; -/// Bech32 variant is used for Celestia and CosmosSDK -const VARIANT: bech32::Variant = bech32::Variant::Bech32; - -/// Representation of the address in the Celestia network -/// -/// Spec says: "Addresses have a length of 32 bytes.", but in reality it is 32 `u5` elements, which can be compressed as 20 bytes. -/// TODO: Switch to bech32::u5 when it has repr transparent: -#[derive(Debug, PartialEq, Clone, Eq, BorshDeserialize, BorshSerialize, Hash)] -pub struct CelestiaAddress([u8; 32]); - -impl serde::Serialize for CelestiaAddress { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - if serializer.is_human_readable() { - let serialized = format!("{}", &self); - serializer.serialize_str(&serialized) - } else { - serde::Serialize::serialize(&self.0, serializer) - } - } -} - -impl<'de> serde::Deserialize<'de> for CelestiaAddress { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - if deserializer.is_human_readable() { - let address_bech32: String = serde::Deserialize::deserialize(deserializer)?; - address_bech32 - .as_bytes() - .try_into() - .map_err(serde::de::Error::custom) - } else { - let addr = <[u8; 32] as serde::Deserialize>::deserialize(deserializer)?; - Ok(CelestiaAddress(addr)) - } - } -} +#[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize, Hash)] // TODO: , BorshDeserialize, BorshSerialize)] +pub struct CelestiaAddress(AccAddress); impl AsRef<[u8]> for CelestiaAddress { fn as_ref(&self) -> &[u8] { - self.0.as_ref() + self.0.id_ref().as_ref() } } /// Decodes slice of bytes into CelestiaAddress /// Treats it as string if it starts with HRP and the rest is valid ASCII -/// Otherwise just checks if it contains valid `u5` elements and has the correct length. +/// Otherwise just decodes the tendermint Id and creates address from that. impl<'a> TryFrom<&'a [u8]> for CelestiaAddress { type Error = anyhow::Error; fn try_from(value: &'a [u8]) -> Result { - if value.starts_with(HRP.as_bytes()) && value.is_ascii() { + let hrp = AddressKind::Account.prefix(); + + if value.starts_with(hrp.as_bytes()) && value.is_ascii() { // safety, because we checked that it is ASCII let s = unsafe { std::str::from_utf8_unchecked(value) }; - return CelestiaAddress::from_str(s).map_err(|e| anyhow::anyhow!("{}", e)); - } - if value.len() != 32 { - anyhow::bail!("An address must be 32 u5 long"); - } - let mut raw_address = [0u8; 32]; - for (idx, &item) in value.iter().enumerate() { - bech32::u5::try_from_u8(item) - .map_err(|e| anyhow::anyhow!("Element at {} is not u5: {}", idx, e))?; - raw_address[idx] = item; + s.parse().context("failed parsing celestia address") + } else { + let array = value.try_into().context("invalid slice length")?; + let id = tendermint::account::Id::new(array); + Ok(Self(AccAddress::new(id))) } - Ok(Self(raw_address)) } } impl Display for CelestiaAddress { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut w = bech32::Bech32Writer::new(HRP, VARIANT, f)?; - for elem in self.0.iter() { - // It is ok to unwrap, because we always sanitize data - w.write_u5(bech32::u5::try_from_u8(*elem).unwrap())?; - } - w.finalize() + write!(f, "{}", self.0) } } -#[derive(Clone, Debug, Error, PartialEq)] -/// An error which occurs while decoding a `CelestialAddress` from a string. -pub enum CelestiaAddressFromStrError { - /// The address has an invalid human-readable prefix. - /// Valid addresses must start with the prefix 'celestia'. - #[error("The address has an invalid human-readable prefix. Valid addresses must start with the prefix 'celestia', but this one began with {0}")] - InvalidHumanReadablePrefix(String), - /// The address has an invalid human-readable prefix. - /// Valid addresses must start with the prefix 'celestia'. - #[error("The address has an invalid bech32 variant. Valid addresses must be encoded in Bech32, but this is encoded in Bech32m")] - InvalidVariant, - /// The address could not be decoded as valid bech32 - #[error("The address could not be decoded as valid bech32: {0}")] - InvalidBech32(#[from] bech32::Error), -} - impl FromStr for CelestiaAddress { - type Err = CelestiaAddressFromStrError; + type Err = ::Err; fn from_str(s: &str) -> Result { - let (hrp, raw_address_u5, variant) = bech32::decode(s)?; - if hrp != HRP { - return Err(CelestiaAddressFromStrError::InvalidHumanReadablePrefix(hrp)); - } - if variant != VARIANT { - return Err(CelestiaAddressFromStrError::InvalidVariant); - } - if raw_address_u5.len() != 32 { - return Err(CelestiaAddressFromStrError::InvalidBech32( - bech32::Error::InvalidLength, - )); - } - - let mut value: [u8; 32] = [0; 32]; - - for (idx, &item) in raw_address_u5.iter().enumerate() { - value[idx] = item.to_u8(); - } - Ok(Self(value)) + Ok(Self(s.parse()?)) } } @@ -174,7 +91,8 @@ mod tests { // 20 u8 -> 32 u5 fn check_from_bytes_as_ascii(input: [u8; 20]) { - let encoded = bech32::encode("celestia", input.to_base32(), VARIANT).unwrap(); + let encoded = + bech32::encode("celestia", input.to_base32(), bech32::Variant::Bech32).unwrap(); let bytes = encoded.as_bytes(); let address = CelestiaAddress::try_from(bytes); assert!(address.is_ok()); @@ -185,7 +103,8 @@ mod tests { // 20 u8 -> 32 u5 fn check_from_as_ref(input: [u8; 20]) { - let encoded = bech32::encode("celestia", input.to_base32(), VARIANT).unwrap(); + let encoded = + bech32::encode("celestia", input.to_base32(), bech32::Variant::Bech32).unwrap(); let address1 = CelestiaAddress::from_str(&encoded).unwrap(); let bytes = address1.as_ref(); let address = CelestiaAddress::try_from(bytes); @@ -195,46 +114,6 @@ mod tests { assert_eq!(encoded, output); } - // 20 u8 -> 32 u5 - fn check_borsh(input: [u8; 20]) { - let address_str = bech32::encode("celestia", input.to_base32(), VARIANT).unwrap(); - - let address = CelestiaAddress::from_str(&address_str).unwrap(); - let serialized = BorshSerialize::try_to_vec(&address).unwrap(); - let deserialized = CelestiaAddress::try_from_slice(&serialized).unwrap(); - - assert_eq!(deserialized, address); - - let address_str2 = format!("{}", deserialized); - assert_eq!(address_str2, address_str); - } - - #[test] - fn test_human_readable_address_serialization() { - let address = CelestiaAddress([11; 32]); - let data: String = serde_json::to_string(&address).unwrap(); - let deserialized_address = serde_json::from_str::(&data).unwrap(); - - assert_eq!(address, deserialized_address); - assert_eq!( - deserialized_address.to_string(), - "celestia1tttttttttttttttttttttttttttttttt9grhcq" - ); - } - - #[test] - fn test_binary_address_serialization() { - let address = CelestiaAddress([11; 32]); - let data = postcard::to_allocvec(&address).unwrap(); - let deserialized_address = postcard::from_bytes::(&data).unwrap(); - - assert_eq!(address, deserialized_address); - assert_eq!( - deserialized_address.to_string(), - "celestia1tttttttttttttttttttttttttttttttt9grhcq" - ); - } - proptest! { #[test] fn test_try_from_any_slice(input in prop::collection::vec(any::(), 0..100)) { @@ -250,14 +129,9 @@ mod tests { // According to spec, alphanumeric characters excluding "1" "b" "i" and "o" fn test_from_str_lowercase_ascii(input in "celestia1[023456789ac-hj-np-z]{38}") { let result = CelestiaAddress::from_str(&input); - match result { - Ok(address) => { - let output = format!("{}", address); - assert_eq!(input, output); - } - Err(err) => { - assert_eq!(CelestiaAddressFromStrError::InvalidBech32(bech32::Error::InvalidChecksum), err); - }, + if let Ok(address) = result { + let output = format!("{}", address); + assert_eq!(input, output); } } @@ -270,10 +144,5 @@ mod tests { fn test_try_as_ref_from(input in proptest::array::uniform20(0u8..=255)) { check_from_as_ref(input); } - - #[test] - fn test_borsh(input in proptest::array::uniform20(0u8..=255)) { - check_borsh(input); - } } } diff --git a/adapters/celestia/src/verifier/mod.rs b/adapters/celestia/src/verifier/mod.rs index 1e3c9ea7f..a63e73b26 100644 --- a/adapters/celestia/src/verifier/mod.rs +++ b/adapters/celestia/src/verifier/mod.rs @@ -1,5 +1,6 @@ use borsh::{BorshDeserialize, BorshSerialize}; -use nmt_rs::NamespaceId; +use celestia_types::nmt::Namespace; +use celestia_types::{Commitment, DataAvailabilityHeader, NamespacedShares}; use serde::{Deserialize, Serialize}; use sov_rollup_interface::da::{ self, BlobReaderTrait, BlockHashTrait as BlockHash, BlockHeaderTrait, DaSpec, @@ -12,22 +13,22 @@ use thiserror::Error; pub mod address; pub mod proofs; -use proofs::*; #[cfg(all(target_os = "zkvm", feature = "bench"))] use sov_zk_cycle_macros::cycle_tracker; use self::address::CelestiaAddress; -use crate::share_commit::recreate_commitment; -use crate::shares::{read_varint, NamespaceGroup, Share}; +use self::proofs::*; +use crate::shares::{NamespaceGroup, Share}; use crate::types::ValidationError; -use crate::{pfb_from_iter, BlobWithSender, CelestiaHeader, DataAvailabilityHeader}; +use crate::utils::read_varint; +use crate::{pfb_from_iter, BlobWithSender, CelestiaHeader}; pub struct CelestiaVerifier { - pub rollup_namespace: NamespaceId, + pub rollup_namespace: Namespace, } -pub const PFB_NAMESPACE: NamespaceId = NamespaceId(hex_literal::hex!("0000000000000004")); -pub const PARITY_SHARES_NAMESPACE: NamespaceId = NamespaceId(hex_literal::hex!("ffffffffffffffff")); +pub const PFB_NAMESPACE: Namespace = Namespace::const_v0([0, 0, 0, 0, 0, 0, 0, 0, 0, 4]); +pub const PARITY_SHARES_NAMESPACE: Namespace = Namespace::MAX; impl BlobReaderTrait for BlobWithSender { type Address = CelestiaAddress; @@ -111,14 +112,14 @@ impl DaSpec for CelestiaSpec { type InclusionMultiProof = Vec; - type CompletenessProof = Vec; + type CompletenessProof = NamespacedShares; type ChainParams = RollupParams; } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RollupParams { - pub namespace: NamespaceId, + pub namespace: Namespace, } #[derive( @@ -138,6 +139,7 @@ pub struct ChainValidityCondition { pub prev_hash: [u8; 32], pub block_hash: [u8; 32], } + #[derive(Error, Debug)] pub enum ValidityConditionError { #[error("conditions for validity can only be combined if the blocks are consecutive")] @@ -182,20 +184,25 @@ impl da::DaVerifier for CelestiaVerifier { // Check the validity and completeness of the rollup row proofs, against the DAH. // Extract the data from the row proofs and build a namespace_group from it - let rollup_shares_u8 = self.verify_row_proofs(completeness_proof, &block_header.dah)?; - if rollup_shares_u8.is_empty() { + let verified_shares = self.verify_row_proofs(completeness_proof, &block_header.dah)?; + if verified_shares.is_empty() { if txs.is_empty() { return Ok(validity_condition); } return Err(ValidationError::MissingTx); } - let namespace = NamespaceGroup::from_shares_unchecked(rollup_shares_u8); // Check the e-tx proofs... // TODO(@preston-evans98): Remove this logic if Celestia adds blob.sender metadata directly into blob let mut tx_iter = txs.iter(); + let mut tx_proofs = inclusion_proof.into_iter(); let square_size = block_header.dah.row_roots.len(); - for (blob, tx_proof) in namespace.blobs().zip(inclusion_proof.into_iter()) { + for blob in verified_shares.blobs() { + // Get the etx proof for this blob + let Some(tx_proof) = tx_proofs.next() else { + return Err(ValidationError::InvalidEtxProof("not all blobs proven")); + }; + // Force the row number to be monotonically increasing let start_offset = tx_proof.proof[0].start_offset; @@ -213,7 +220,7 @@ impl da::DaVerifier for CelestiaVerifier { let root = &block_header.dah.row_roots[row_num]; sub_proof .proof - .verify_range(root, &sub_proof.shares, PFB_NAMESPACE) + .verify_range(root, &sub_proof.shares, PFB_NAMESPACE.into()) .map_err(|_| ValidationError::InvalidEtxProof("invalid sub proof"))?; tx_shares.extend( sub_proof @@ -249,8 +256,8 @@ impl da::DaVerifier for CelestiaVerifier { .map_err(|_| ValidationError::InvalidEtxProof("invalid pfb"))?; // Verify the sender and data of each blob which was sent into this namespace - for (blob_idx, nid) in pfb.namespace_ids.iter().enumerate() { - if nid != &self.rollup_namespace.0[..] { + for (blob_idx, nid) in pfb.namespaces.iter().enumerate() { + if nid != self.rollup_namespace.as_bytes() { continue; } let tx: &BlobWithSender = tx_iter.next().ok_or(ValidationError::MissingTx)?; @@ -276,14 +283,18 @@ impl da::DaVerifier for CelestiaVerifier { // Link blob commitment to e-tx commitment let expected_commitment = - recreate_commitment(square_size, blob_ref).map_err(|_| { + Commitment::from_shares(self.rollup_namespace, blob_ref.0).map_err(|_| { ValidationError::InvalidEtxProof("failed to recreate commitment") })?; - assert_eq!(&pfb.share_commitments[blob_idx][..], &expected_commitment); + assert_eq!(&pfb.share_commitments[blob_idx][..], &expected_commitment.0); } } + if tx_proofs.next().is_some() { + return Err(ValidationError::InvalidEtxProof("more proofs than blobs")); + } + Ok(validity_condition) } } @@ -291,26 +302,30 @@ impl da::DaVerifier for CelestiaVerifier { impl CelestiaVerifier { pub fn verify_row_proofs( &self, - row_proofs: Vec, + row_proofs: NamespacedShares, dah: &DataAvailabilityHeader, - ) -> Result>, ValidationError> { - let mut row_proofs = row_proofs.into_iter(); + ) -> Result { + let mut row_proofs = row_proofs.rows.into_iter(); // Check the validity and completeness of the rollup share proofs - let mut rollup_shares_u8: Vec> = Vec::new(); + let mut verified_shares = Vec::new(); for row_root in dah.row_roots.iter() { // TODO: short circuit this loop at the first row after the rollup namespace - if row_root.contains(self.rollup_namespace) { + if row_root.contains(self.rollup_namespace.into()) { let row_proof = row_proofs.next().ok_or(ValidationError::InvalidRowProof)?; row_proof .proof - .verify_complete_namespace(row_root, &row_proof.leaves, self.rollup_namespace) + .verify_complete_namespace( + row_root, + &row_proof.shares, + self.rollup_namespace.into(), + ) .expect("Proofs must be valid"); - for leaf in row_proof.leaves { - rollup_shares_u8.push(leaf) + for leaf in row_proof.shares { + verified_shares.push(leaf) } } } - Ok(rollup_shares_u8) + Ok(NamespaceGroup::from_shares(verified_shares)) } } diff --git a/adapters/celestia/src/verifier/proofs.rs b/adapters/celestia/src/verifier/proofs.rs index 4d4599710..9e09a33d7 100644 --- a/adapters/celestia/src/verifier/proofs.rs +++ b/adapters/celestia/src/verifier/proofs.rs @@ -1,42 +1,30 @@ -use borsh::{BorshDeserialize, BorshSerialize}; -use nmt_rs::{NamespaceId, NamespaceProof, NamespacedSha2Hasher}; +// use borsh::{BorshDeserialize, BorshSerialize}; +use celestia_types::nmt::NamespaceProof; +use celestia_types::NamespacedShares; use serde::{Deserialize, Serialize}; use super::CelestiaSpec; use crate::types::FilteredCelestiaBlock; -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] // TODO:, BorshDeserialize, BorshSerialize)] pub struct EtxProof { pub proof: Vec, } -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] // TODO:, BorshDeserialize, BorshSerialize)] pub struct EtxRangeProof { pub shares: Vec>, - pub proof: NamespaceProof, + pub proof: NamespaceProof, pub start_share_idx: usize, pub start_offset: usize, } -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] -pub struct RelevantRowProof { - pub leaves: Vec>, - pub proof: NamespaceProof, -} - #[derive(Debug, PartialEq, Clone)] -pub struct CompletenessProof(pub Vec); +pub struct CompletenessProof(pub NamespacedShares); impl CompletenessProof { - pub fn from_filtered_block(block: &FilteredCelestiaBlock, namespace: NamespaceId) -> Self { - let mut row_proofs = Vec::new(); - for row in block.rollup_rows.iter() { - let mut nmt = row.merklized(); - let (leaves, proof) = nmt.get_namespace_with_proof(namespace); - let row_proof = RelevantRowProof { leaves, proof }; - row_proofs.push(row_proof) - } - Self(row_proofs) + pub fn from_filtered_block(block: &FilteredCelestiaBlock) -> Self { + Self(block.rollup_rows.clone()) } } @@ -76,7 +64,7 @@ impl CorrectnessProof { current_tx_proof.proof.push(EtxRangeProof { shares, - proof, + proof: proof.into(), start_offset: next_needed_share.start_offset, start_share_idx: next_needed_share.share_range.start, }); @@ -89,7 +77,7 @@ impl CorrectnessProof { current_tx_proof.proof.push(EtxRangeProof { shares, - proof, + proof: proof.into(), start_offset: next_needed_share.start_offset, start_share_idx: next_needed_share.share_range.start, }); diff --git a/adapters/celestia/test_data/block_with_rollup_data/eds.json b/adapters/celestia/test_data/block_with_rollup_data/eds.json new file mode 100644 index 000000000..d5328b93e --- /dev/null +++ b/adapters/celestia/test_data/block_with_rollup_data/eds.json @@ -0,0 +1,69 @@ +{ + "data_square": [ + "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", + "AAAAAAAAAAAAAAAAAAAAAAAAALlkcwhxpLnnHREA62fKvWN35mmx+kFRfXbHhHCp9V3OANhFJt70AAenMM0nrZco4oTaW2sXJZD0YU6Htz6AGdTc2eEquXHIz5CwPu4Qp8ziJ0LUlQ5i7Td+dkLfulH4RcWu0CcujQuGsDIv5hsI00HkDPWkMLq+9IlBN+WlfV1sxS8ZPZLOI9wsMMg6WNKMr0P65Fmyrz+yWkWUZMJQF/+VIMksJCA6/AHrywtghYGGvPkREHEQ9eP+RuaIyijP37VvWSQeIu51teaWxCemoMnImw1EORXr3k9il/gUGz4s4Lkz0BVd13s4v2E1H047nXxDxgiO19Am1Z2xW3stV6Fi1IXyYREFF5fewK+adL8ihjFvfi1NH21N62cPET+cz0T3vH3aR8WsS0a+L4IP8xoVRuN8IYgjXLAakgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "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", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "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" + ], + "codec": "Leopard" +} diff --git a/adapters/celestia/test_data/block_with_rollup_data/etx_rows.json b/adapters/celestia/test_data/block_with_rollup_data/etx_rows.json new file mode 100644 index 000000000..c1dff1796 --- /dev/null +++ b/adapters/celestia/test_data/block_with_rollup_data/etx_rows.json @@ -0,0 +1,19 @@ +[ + { + "shares": [ + "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" + ], + "proof": { + "start": 0, + "end": 3, + "nodes": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAc292LXRlc3QAAAAAAAAAAAAAAAAAAAAAAAAAAABzb3YtdGVzdPbKzgXqabVOZYOHxnqFgTfaSGsMYPl775CAzC/gFopP", + "/////////////////////////////////////////////////////////////////////////////34GEVxg3h/XFav2Yn9hHIGc/d6ZzObX6Z1c+mcHjCsB" + ], + "leaf_hash": "", + "is_max_namespace_ignored": true + } + } +] diff --git a/adapters/celestia/test_data/block_with_rollup_data/header.json b/adapters/celestia/test_data/block_with_rollup_data/header.json new file mode 100644 index 000000000..e769a7944 --- /dev/null +++ b/adapters/celestia/test_data/block_with_rollup_data/header.json @@ -0,0 +1,90 @@ +{ + "header": { + "version": { + "block": "11", + "app": "1" + }, + "chain_id": "private", + "height": "11", + "time": "2023-09-27T16:58:08.620046105Z", + "last_block_id": { + "hash": "7BC39A59FEF49C5C75B1F800A9A8597A2BDB19AD23123AA148C03D28975A2DCF", + "parts": { + "total": 1, + "hash": "F146FF93FB7FC036E0D349C12EE618C2CC0BF174EC91FA58BCBB9E885BB5D063" + } + }, + "last_commit_hash": "392414C92529EC26E9486B54B8E56A56B30B8AD38A05244AB0B8C9DBFDB5786E", + "data_hash": "B1B291D76F10813FC674FC44B41D06CEF72C7F02C000E3089187AD4DF048FF44", + "validators_hash": "D00E6D5C9B8B8FD263FEA6FA8229C5A86BB6D8D03696AC5D7633DF9895F75F40", + "next_validators_hash": "D00E6D5C9B8B8FD263FEA6FA8229C5A86BB6D8D03696AC5D7633DF9895F75F40", + "consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F", + "app_hash": "E5950137BB67524C4A0B9FB4635F22CC552AFD0B554415C31579D2A683FD56C5", + "last_results_hash": "0B1F48181250EB119CB2376E0653517C95244D778CD0AB7BD85296417127E5AE", + "evidence_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", + "proposer_address": "06F9CE527A1CC6EF2678D2F18DDFC06B783597AD" + }, + "commit": { + "height": 11, + "round": 0, + "block_id": { + "hash": "C839E720DA55CC6E43EC7CE00744D6151D79E84C81D7F6995F3B13B7AE532456", + "parts": { + "total": 1, + "hash": "5045BCFCFA3D7BF28F45C02E6E027EF17B1EE30B1C401DF1C8FBA2C005648811" + } + }, + "signatures": [ + { + "block_id_flag": 2, + "validator_address": "06F9CE527A1CC6EF2678D2F18DDFC06B783597AD", + "timestamp": "2023-09-27T16:58:19.63881203Z", + "signature": "NjmOPE3tLPRP+X+fAW1CUdOcunZxVvPwsOEnexZ69slIclwcigOidRg9gdhjpGJqrB0XoepZLkD1Ycu3mM9VAA==" + } + ] + }, + "validator_set": { + "validators": [ + { + "address": "06F9CE527A1CC6EF2678D2F18DDFC06B783597AD", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "u01WraTbmnwJk0b1NytVDe0JFphZNhqbfd2MIpfsF7Q=" + }, + "voting_power": "5000", + "proposer_priority": "0" + } + ], + "proposer": { + "address": "06F9CE527A1CC6EF2678D2F18DDFC06B783597AD", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "u01WraTbmnwJk0b1NytVDe0JFphZNhqbfd2MIpfsF7Q=" + }, + "voting_power": "5000", + "proposer_priority": "0" + } + }, + "dah": { + "row_roots": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAABzb3YtdGVzdODFuRX5SFkwrJgDZlNsBKTHg6oiQIy+mKqZNlE8FbVx", + "AAAAAAAAAAAAAAAAAAAAAAAAACUTM2P1SjCGoXcAAAAAAAAAAAAAAAAAAAAAAAAAuWRzCHGkuecdEU2poPhzydIqKgh0TwBWq5wC3j2lcIxdkiVLqoevNjwi", + "AAAAAAAAAAAAAAAAAAAAAAAAALlkcwhxpLnnHRH//////////////////////////////////////vJ9dv7Uz1bj3ur7mpnO3E2SBLKkffloglJj/0H3oK7U", + "//////////////////////////////////////7//////////////////////////////////////uLSam+eudl+PFq2saaqVHCQkAeVAjSjiuRc4PslqTaj", + "//////////////////////////////////////////////////////////////////////////////SBbMrJ3ZKjBvbYkMxURJTk0VmWyAh4Q6yaAdKTIzyP", + "/////////////////////////////////////////////////////////////////////////////x5DQsKFQmQWDS0DtQn/RsqRlLVVr+A2in6j+CLouflX", + "/////////////////////////////////////////////////////////////////////////////xX8O1QVOeyj3MpKpUOLVAiXCZyA9ePfoO5FstiPESfZ", + "/////////////////////////////////////////////////////////////////////////////zNwkSWhAnXxEqrgbNi9qw0IJDlMnakzcLE20dW7ohRo" + ], + "column_roots": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT//////////////////////////////////////s2/cZvtE/zeAzO4bs8jv5W2OskQYY5uLAdrdEZVOktj", + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT//////////////////////////////////////oJXkXWtRaD2XRqi0184Vlsd1OQc1IeQaJF9Z9bf+x0W", + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT//////////////////////////////////////g8+fkXWz1VvozT2tNvoqIw9caloxkDAqRJBbnjZg6i4", + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAc292LXRlc3T//////////////////////////////////////pulXvW/TCqO9S7SjmD7Gzv4mOgWPAC0QvI7uCRM8xqu", + "/////////////////////////////////////////////////////////////////////////////2oIH0D8MW33fIqQTIDGMcqw5QRlZh+BRxOZ5Tspf9Lo", + "/////////////////////////////////////////////////////////////////////////////2GHintd7+WjGUDgkr6WLftpVYDDTrLF1HlmIHhPN+Ci", + "/////////////////////////////////////////////////////////////////////////////8bNdAw0NmSlGbnFvWk4pwhBI6hxFsDIkzU2cEMTQiGZ", + "/////////////////////////////////////////////////////////////////////////////792SWBgoS1tEDIzu23t3z0KWOjQ3EJriRL5ILIH4wKy" + ] + } +} diff --git a/adapters/celestia/test_data/block_with_rollup_data/rollup_rows.json b/adapters/celestia/test_data/block_with_rollup_data/rollup_rows.json new file mode 100644 index 000000000..3e9e8c27b --- /dev/null +++ b/adapters/celestia/test_data/block_with_rollup_data/rollup_rows.json @@ -0,0 +1,18 @@ +[ + { + "shares": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAc292LXRlc3QBAAAA/AEAAAD0AAAA7kkIPARqpfS7VPCI8ISXp3+yE5juMYVGTjPumbIaH/HlvtOy96FMy6R8i1FmHXXywofV/UfxkMb5iYWe/2juD/itJDeieeHIkywHNYyR3E/jSGSpjGwl8pjioBmcFQn/iAAAAAAACwAAAAAAAAAOAAAAc292LXRlc3QtdG9rZW7oAwAAAAAAAKMgGVT3CtYiMNw9hApb92dwLASGnoWrPu4LlihXunWYAgAAAP6mrFuHURIPti//Z7VNLqxmrvMHx93h05TeoeCeQ91EoyAZVPcK1iIw3D2EClv3Z3AsBIaehas+7guWKFe6dZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + ], + "proof": { + "start": 3, + "end": 4, + "nodes": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNr0OaVlRhcnQ7QibuMWIApgFx1vLeoFCjVZ1VBoaCw7", + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA1Bip57svJ/YAJbqu2eATzaSBu0IxogcH0uczSggxXb", + "/////////////////////////////////////////////////////////////////////////////34GEVxg3h/XFav2Yn9hHIGc/d6ZzObX6Z1c+mcHjCsB" + ], + "leaf_hash": "", + "is_max_namespace_ignored": true + } + } +] diff --git a/adapters/celestia/test_data/block_without_rollup_data/eds.json b/adapters/celestia/test_data/block_without_rollup_data/eds.json new file mode 100644 index 000000000..70e17cac3 --- /dev/null +++ b/adapters/celestia/test_data/block_without_rollup_data/eds.json @@ -0,0 +1,69 @@ +{ + "data_square": [ + "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", + "AAAAAAAAAAAAAAAAAAAAAAAAAPxtQ2jlIaiqMi4AqSBwxi0FaQBRPj2VG/AkmFuGHLPplSrH1F42EID/cBEoiHrbkkZIYxwVRdlpWmudiUYTUHJ7A/zrbllXZQT7okM8/CYaHyNANl0pD7ZOf6wOiq2zauGaLGNHJKcPGpwaFIkHGlslDzk2zDyYa8SsA220qi+4gvuFN5Q5IxBfE3k7WK2Wdn9XGdom/j5du4rvjdOuE31fFF8M1bkUlJ8opPrrz1rFnr92Wll/8EoEWnuy2APdVxBLr9Fj4ZP4507Q2YvBBc7AgPtTCgZvVvy6b47vrDq0S2bDl6qUpCWRUvaj+EwE1b7M8N7gHsl9nbEeTE45VrXj4qgSVPDgv8ay0koEW6Knubr0x2tFcnHI8Vj0cF20kVaKjGO0j81Y/uyFseNjCVWxTwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "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", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "//////////////////////////////////////4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "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" + ], + "codec": "Leopard" +} diff --git a/adapters/celestia/test_data/block_without_rollup_data/etx_rows.json b/adapters/celestia/test_data/block_without_rollup_data/etx_rows.json new file mode 100644 index 000000000..df97b6db5 --- /dev/null +++ b/adapters/celestia/test_data/block_without_rollup_data/etx_rows.json @@ -0,0 +1,18 @@ +[ + { + "shares": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBAAACtgAAACbZAgrNAgqgAQqdAQogL2NlbGVzdGlhLmJsb2IudjEuTXNnUGF5Rm9yQmxvYnMSeQovY2VsZXN0aWExbWVuc3NkejJ3NnU4Y3R6djAyZHpucGNmcnNrODhsMGx1azdyNmESHQAAAAAAAAAAAAAAAAAAAAAAAAD8bUNo5SGoqjIuGgKFBiIg2yJCa5mhWpSCCf4D6JIhl2H1sfYPw8f5FVQ+EyeJil1CAQASZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAtFCZJHMIUdbiMkVX4fhj6xX5Wfk8UyUW5e3quISZM3iEgQKAggBGAUSEgoMCgR1dGlhEgQ4MzkwELSPBRpALyuJS7y5LcvdJ7DzACRef9gw1lGFLWsUOA6evLuG9MIADCpFGibFDfAZuyskWLyIvxZeZ+clXT4p/90VgACdlhIBAxoESU5EWNkCCs0CCqABCp0BCiAvY2VsZXN0aWEuYmxvYi52MS5Nc2dQYXlGb3JCbG9icxJ5Ci9jZWxlc3RpYTFoc3RqeXdtZmFoemR1dTM4bHBnam1qNG43ZGgyOHJjcHlzYzY1bRIdAAAAAAAAAAAAAAAAAAAAAAAAAIvtbFh24m+LNpU=", + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAABoClQMiIHeXFiz0fgUA3efD421ue7STZBsgKxNbnrJh/CNRSPVmQgEAEmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOarfGcZdUWXAZdieeSCsOhJ9Hpj6epcoTsUBEELJ9A5xIECgIIARgFEhIKDAoEdXRpYRIENzk4MBC07wQaQElPonH7neWN3zpBnOOm/8OoTRJ6aZJCgouqKGDrtI+FQeidUMdaT665JZXu2VT2rkiTG7lArYVahXp9XG82qCkSAQIaBElORFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + ], + "proof": { + "start": 0, + "end": 2, + "nodes": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAIvtbFh24m+LNpUAAAAAAAAAAAAAAAAAAAAAAAAA/G1DaOUhqKoyLvsCiA3MWph1KIiwfLUAlUW7hI4RFOCcOyianErJ4KhI", + "/////////////////////////////////////////////////////////////////////////////wJmko3hg439gImAypgButRTOMwq1+SIIPGWqFuWJssZ" + ], + "leaf_hash": "", + "is_max_namespace_ignored": true + } + } +] diff --git a/adapters/celestia/test_data/block_without_rollup_data/header.json b/adapters/celestia/test_data/block_without_rollup_data/header.json new file mode 100644 index 000000000..582e98a5e --- /dev/null +++ b/adapters/celestia/test_data/block_without_rollup_data/header.json @@ -0,0 +1,90 @@ +{ + "header": { + "version": { + "block": "11", + "app": "1" + }, + "chain_id": "private", + "height": "12", + "time": "2023-09-27T16:58:19.63881203Z", + "last_block_id": { + "hash": "C839E720DA55CC6E43EC7CE00744D6151D79E84C81D7F6995F3B13B7AE532456", + "parts": { + "total": 1, + "hash": "5045BCFCFA3D7BF28F45C02E6E027EF17B1EE30B1C401DF1C8FBA2C005648811" + } + }, + "last_commit_hash": "C3EA49B60980AF1E022FA17A6F7F27FDFB957CFB837C589659245A035770CE51", + "data_hash": "A67040F5629A4D26C87D8D7EBAAF5F0DB67ACE2990333601F3AE6C6BAF243EB5", + "validators_hash": "D00E6D5C9B8B8FD263FEA6FA8229C5A86BB6D8D03696AC5D7633DF9895F75F40", + "next_validators_hash": "D00E6D5C9B8B8FD263FEA6FA8229C5A86BB6D8D03696AC5D7633DF9895F75F40", + "consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F", + "app_hash": "BB8BBA6880B1609A960E7650DD4D66B985044711D7CF800AAD74811FEAD9CF17", + "last_results_hash": "6F4AFD25D6AFBFE21CFF2DE494EFEB436CF93B33FB29942E6B3281ADF3CEAD27", + "evidence_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", + "proposer_address": "06F9CE527A1CC6EF2678D2F18DDFC06B783597AD" + }, + "commit": { + "height": 12, + "round": 0, + "block_id": { + "hash": "F769490DC768E7678160384070727533B7AE809477EA5D191CF7AF5C917A7973", + "parts": { + "total": 1, + "hash": "F4385AC3F7D61AF85C556F7082D0EF4846EB82A703396C60D7C9FFB8F4E41C1C" + } + }, + "signatures": [ + { + "block_id_flag": 2, + "validator_address": "06F9CE527A1CC6EF2678D2F18DDFC06B783597AD", + "timestamp": "2023-09-27T16:58:30.665234031Z", + "signature": "5+MOvaRrSzm24wPxb3y/xS7FJzMwZOG2Qu1uta3G79cy803qr+4y7Y0eSQgJkIxUfWoxbyeT6EvmuAGqNf/MCg==" + } + ] + }, + "validator_set": { + "validators": [ + { + "address": "06F9CE527A1CC6EF2678D2F18DDFC06B783597AD", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "u01WraTbmnwJk0b1NytVDe0JFphZNhqbfd2MIpfsF7Q=" + }, + "voting_power": "5000", + "proposer_priority": "0" + } + ], + "proposer": { + "address": "06F9CE527A1CC6EF2678D2F18DDFC06B783597AD", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "u01WraTbmnwJk0b1NytVDe0JFphZNhqbfd2MIpfsF7Q=" + }, + "voting_power": "5000", + "proposer_priority": "0" + } + }, + "dah": { + "row_roots": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAA/G1DaOUhqKoyLrFn9xm/BUXtpRR45zfagBYnv8+3S4n5D/hRtFgtI16u", + "AAAAAAAAAAAAAAAAAAAAAAAAAPxtQ2jlIaiqMi7//////////////////////////////////////p6G4NfV2HgHRAIU2/ymDxvcjx39Z/0xzbTpErIhCHjN", + "//////////////////////////////////////7//////////////////////////////////////uLSam+eudl+PFq2saaqVHCQkAeVAjSjiuRc4PslqTaj", + "//////////////////////////////////////7//////////////////////////////////////uLSam+eudl+PFq2saaqVHCQkAeVAjSjiuRc4PslqTaj", + "/////////////////////////////////////////////////////////////////////////////4l5qoj44rZjDunkjxrjCmVH6th+oYEK0Ya01jz71Xzs", + "/////////////////////////////////////////////////////////////////////////////zA5Tqpc96ztvx0uyBt5N3JJ3HuTCbsuMhbPgzRmntuW", + "/////////////////////////////////////////////////////////////////////////////2fflzKmne+H12fvmxUVSQdi1O6054vc9AQf30jziJ8o", + "/////////////////////////////////////////////////////////////////////////////2UfkxS59vGLbiaZTF87CWi7ZdhAKBfcwkuwfAolkOXA" + ], + "column_roots": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT//////////////////////////////////////opIjb03gu6+1ITQgCx/NE/icMApJxwlLpTHHLCxhuA3", + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT//////////////////////////////////////hz+vAyRFxVMkHLkgeT+OKaNvnatTQ9qn06BCRmlSQde", + "AAAAAAAAAAAAAAAAAAAAAAAAAIvtbFh24m+LNpX//////////////////////////////////////rO1cyfXnI08K4GNifaKNQwS/pPbUFeQ3fkWphPnRIUi", + "AAAAAAAAAAAAAAAAAAAAAAAAAPxtQ2jlIaiqMi7//////////////////////////////////////j4/j5p8NepzsLERA/Von5xRm2HIVdZrR3MAMsNwHl5U", + "/////////////////////////////////////////////////////////////////////////////wz9+SSX4FF6SY4kI8hBW/Iwvzru2SyofLi3kYrTC+r0", + "//////////////////////////////////////////////////////////////////////////////O9VJszUXQ2Bjny+0CgcVz6yGLXZ8TPON6AazxSS5y9", + "/////////////////////////////////////////////////////////////////////////////59bDI193kV2WiJ1XlfvofvIGYefXtrJ9rVeCDwiL+rr", + "/////////////////////////////////////////////////////////////////////////////3KF4bQcDV86P69NjHa2A/AebFl7yutNH7+VWgi4JrjD" + ] + } +} diff --git a/adapters/celestia/test_data/block_without_rollup_data/rollup_rows.json b/adapters/celestia/test_data/block_without_rollup_data/rollup_rows.json new file mode 100644 index 000000000..145d8489c --- /dev/null +++ b/adapters/celestia/test_data/block_without_rollup_data/rollup_rows.json @@ -0,0 +1,16 @@ +[ + { + "shares": [], + "proof": { + "start": 2, + "end": 3, + "nodes": [ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEOx/5Dw9P45TBiPbQSRp0Iozd/Q26b8E5fFjcpQVsvQ", + "AAAAAAAAAAAAAAAAAAAAAAAAAPxtQ2jlIaiqMi4AAAAAAAAAAAAAAAAAAAAAAAAA/G1DaOUhqKoyLhUz+K4Z5TJ0i0ZXCWTtEKRFJm9J0zsDmngfLJIShWCn", + "/////////////////////////////////////////////////////////////////////////////wJmko3hg439gImAypgButRTOMwq1+SIIPGWqFuWJssZ" + ], + "leaf_hash": "AAAAAAAAAAAAAAAAAAAAAAAAAIvtbFh24m+LNpUAAAAAAAAAAAAAAAAAAAAAAAAAi+1sWHbib4s2lcmeFRLinSslQkf29kfq2rR0sNxA1zwiyfM5JNMbxRXy", + "is_max_namespace_ignored": true + } + } +] diff --git a/deny.toml b/deny.toml index 2cdf60ec8..ca525000c 100644 --- a/deny.toml +++ b/deny.toml @@ -2,17 +2,18 @@ # Deny crates that do not have a license. unlicensed = "deny" allow = [ - "Apache-2.0", - "MIT", - "Unlicense", - "Unicode-DFS-2016", - "MPL-2.0", - "ISC", - "CC0-1.0", - "BSD-2-Clause", - "BSD-3-Clause", - "OpenSSL", - "Apache-2.0 WITH LLVM-exception", + "Apache-2.0", + "MIT", + "Unlicense", + "Unicode-DFS-2016", + "MPL-2.0", + "ISC", + "CC0-1.0", + "BSD-2-Clause", + "BSD-3-Clause", + "OpenSSL", + "Apache-2.0 WITH LLVM-exception", + "Zlib", ] [[licenses.clarify]] @@ -20,6 +21,4 @@ name = "ring" # ring is derived from BoringSSL and has a bit of a special licensing situation, # but we can effectively treat is as OpenSSL-like licensing. expression = "OpenSSL" -license-files = [ - { path = "LICENSE", hash = 0xbd0eed23 } -] +license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] diff --git a/docker/Dockerfile.bridge b/docker/Dockerfile.bridge new file mode 100644 index 000000000..864240597 --- /dev/null +++ b/docker/Dockerfile.bridge @@ -0,0 +1,18 @@ +# A dockerfile for the celestia bridge node in DA layer +# Based on: +# https://github.com/celestiaorg/celestia-node/blob/main/Dockerfile +FROM docker.io/alpine:3.18.3 + +ENV CELESTIA_HOME=/root + +RUN apk update && apk add --no-cache bash jq + +# Copy in the binary +COPY --from=ghcr.io/celestiaorg/celestia-node:v0.11.0-rc14 /bin/celestia /bin/celestia +COPY --from=ghcr.io/celestiaorg/celestia-node:v0.11.0-rc14 /bin/cel-key /bin/cel-key + +COPY ./run-bridge.sh /opt/entrypoint.sh + +EXPOSE 2121 26658 + +CMD /opt/entrypoint.sh diff --git a/docker/Dockerfile.validator b/docker/Dockerfile.validator new file mode 100644 index 000000000..6b7d667ce --- /dev/null +++ b/docker/Dockerfile.validator @@ -0,0 +1,18 @@ +# A dockerfile for the celestia validator in consensus layer +# Based on: +# https://github.com/celestiaorg/celestia-app/blob/main/Dockerfile +FROM docker.io/alpine:3.18.3 + +ENV CELESTIA_HOME=/root + +RUN apk update && apk add --no-cache bash jq + +# Copy in the binary +COPY --from=ghcr.io/celestiaorg/celestia-app:v1.0.0-rc17 /bin/celestia-appd /bin/celestia-appd + +COPY ./run-validator.sh /opt/entrypoint.sh + +# p2p, rpc and prometheus port +EXPOSE 26656 26657 1317 9090 + +CMD /opt/entrypoint.sh diff --git a/docker/Makefile b/docker/Makefile index 10a4d5eab..48dd59754 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,21 +1,31 @@ +PROJECT_ROOT := $(shell git rev-parse --show-toplevel) +DOCKER_COMPOSE_DIR := $(PROJECT_ROOT)/docker +DOCKER_COMPOSE_CFG := $(DOCKER_COMPOSE_DIR)/docker-compose.yml +CREDENTIALS_DIR := $(PROJECT_ROOT)/examples/celestia-docker/credentials -COMPOSE_FILE=docker-compose.celestia.yaml -MOUNT_FOLDER=keyring-test -NODE_1_KEY_FILE=bridge_1_key.txt +docker_compose := docker compose -f $(DOCKER_COMPOSE_CFG) up: - docker-compose --file $(COMPOSE_FILE) up -d - + @echo "Starting services" + @$(docker_compose) up -d --build --force-recreate + @echo "Waiting for services to finish setup" + @$(docker_compose) logs -f | awk '/Provisioning finished./ {print;exit}' # exit when encounter this log entry down: - docker-compose --file "$(COMPOSE_FILE)" down - rm -rf $(MOUNT_FOLDER)/*.txt - rm -rf config_*.toml + @echo "Shutting down services" + @$(docker_compose) down + @echo "Removing generated configs" + @rm rollup_config_*.toml + +# wait for the celestia network to perform setup and coins transfers +wait-compose-ready: + @echo "Waiting for services to finish setup" + @$(compose_logs) | awk '/Provisioning finished./ {print;exit}' # exit when encounter this log entry restart: down up generate_configs -generate_configs: - bash ./generate_configs.sh +generate_configs: wait-compose-ready + @$(DOCKER_COMPOSE_DIR)/generate_configs.sh logs: - docker-compose --file "$(COMPOSE_FILE)" logs --follow \ No newline at end of file + @$(docker_compose) logs -f diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..0f10c7418 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,51 @@ +# Local celestia setup + +It consists of one validator (block maker) and arbitrary number of bridge nodes +for sequencers (1 by default). + +## Example + +```sh +# start the celestia network +docker compose -f examples/celestia-docker/docker-compose.yml up --build --force-recreate -d + +# grab the jwt +CELESTIA_NODE_AUTH_TOKEN="$(cat examples/celestia-docker/credentials/bridge-0.jwt)" + +# check the celestia rpc +curl -X POST \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${CELESTIA_NODE_AUTH_TOKEN}" \ + -d '{ + "id": 1, + "jsonrpc": "2.0", + "method": "header.GetByHeight", + "params": [2] + }' \ + localhost:26658 + +# stop the celestia network +docker compose -f examples/celestia-docker/docker-compose.yml down +``` + +## Multiple sequencers + +To have multiple sequencers, a few conditions needs to be met: +- validator must know the number of sequencers to provision them with accounts and coins +- each sequencer must have a unique id and each id has to be a consecutive natural number + starting from 0. (eg. 0, 1, 2) +- each sequencer other than the first one has to have the ports remapped so they don't conflict + with other sequencers + +The `docker-compose.yml` has a commented out example setup for the second sequencer. It can +be copy-pasted and adjusted for an arbitrary number of sequencers. The amount of sequencers +needs to be provided by uncommenting and aligning the `services.validator.command` field. + +## Credentials + +Credentials for each new sequencer are created by validator on the first startup. The validator writes +the keys and address of each sequencer to the `examples/celestia-docker/credentials` volume. Each consecutive +run will use the same credentials until the directory is manually cleaned up. + +In addition, each sequencer on startup will write it's `JWT` token to the same directory. The token is +updated during consecutive runs. diff --git a/docker/credentials/bridge-0.addr b/docker/credentials/bridge-0.addr new file mode 100644 index 000000000..251e6a8cf --- /dev/null +++ b/docker/credentials/bridge-0.addr @@ -0,0 +1 @@ +celestia1a68m2l85zn5xh0l07clk4rfvnezhywc53g8x7s diff --git a/docker/credentials/bridge-0.key b/docker/credentials/bridge-0.key new file mode 100644 index 000000000..fda19e624 --- /dev/null +++ b/docker/credentials/bridge-0.key @@ -0,0 +1,9 @@ +-----BEGIN TENDERMINT PRIVATE KEY----- +kdf: bcrypt +salt: 68B0092F4FC5386C20DA96ECEE1BFE09 +type: secp256k1 + +JEOTdvPjs/4G+Jhz0hyNgdN5CgOCCcf5zvECY6zp+AN6IT6rTW0xbGqgFqbX6Yyi +NUV8e1fo9zhoytjrHjcCgHRnGiBGFQf1Ld4sBzE= +=TmEg +-----END TENDERMINT PRIVATE KEY----- diff --git a/docker/docker-compose.celestia.yaml b/docker/docker-compose.celestia.yaml deleted file mode 100644 index 704e2a16b..000000000 --- a/docker/docker-compose.celestia.yaml +++ /dev/null @@ -1,142 +0,0 @@ -version: '3' - -services: - validator: - container_name: sov-celestia-validator - image: ghcr.io/celestiaorg/celestia-app:v0.13.2 - healthcheck: - test: [ "CMD", "curl", "-f", "http://127.0.0.1:26657/block?height=1" ] - interval: 30s - timeout: 10s - retries: 5 - environment: - - VALIDATOR_NAME=validator - - KEY_NAME_1=validator - - KEY_NAME_2=node1 - - KEY_NAME_3=node2 - - CHAIN_ID=test -# - CHAIN_ID=sov-testnet - - CELES_AMOUNT=12300000000000000000000000utia - - STAKING_AMOUNT=1000000000utia - - DEBIAN_FRONTEND=noninteractive - ports: - - "9090:9090" - - "36656:26656" - - "36657:26657" - - "36658:26658" - entrypoint: - - /bin/sh - - -c - - | - apk update -qq && apk add -qq curl && \ - /bin/celestia-appd init $$VALIDATOR_NAME --chain-id $$CHAIN_ID && \ - if [ ! -f "/root/keyring-test/$$KEY_NAME_1.info" ]; then \ - echo "CREATING NEW KEYS" && \ - /bin/celestia-appd keys add $$KEY_NAME_1 --keyring-backend test && \ - /bin/celestia-appd keys add $$KEY_NAME_2 --keyring-backend test && \ - /bin/celestia-appd keys add $$KEY_NAME_3 --keyring-backend test && \ - mkdir -p /root/keyring-test && cp /root/.celestia-app/keyring-test/* /root/keyring-test/; \ - else \ - echo "USING EXISTING KEYS" && \ - mkdir -p /root/.celestia-app/keyring-test/ && \ - cp /root/keyring-test/* /root/.celestia-app/keyring-test/; \ - fi; \ - echo "AVAILABLE KEYS:" && \ - /bin/celestia-appd keys list --keyring-backend test && \ - /bin/celestia-appd add-genesis-account $$KEY_NAME_1 $$CELES_AMOUNT --keyring-backend test && \ - /bin/celestia-appd add-genesis-account $$KEY_NAME_2 $$CELES_AMOUNT --keyring-backend test && \ - /bin/celestia-appd add-genesis-account $$KEY_NAME_3 $$CELES_AMOUNT --keyring-backend test && \ - /bin/celestia-appd gentx $$KEY_NAME_1 $$STAKING_AMOUNT --chain-id $$CHAIN_ID --keyring-backend test --evm-address 0x966e6f22781EF6a6A82BBB4DB3df8E225DfD9488 && \ - /bin/celestia-appd collect-gentxs && \ - echo "NODE_ID:" && \ - /bin/celestia-appd tendermint show-node-id && \ - - /bin/celestia-appd start --rpc.laddr tcp://0.0.0.0:26657 --proxy_app tcp://0.0.0.0:26658 - volumes: - - ./keyring-test:/root/keyring-test/ - - - # cat /root/.celestia-app/config/genesis.json && \ - - bridge: - image: ghcr.io/celestiaorg/celestia-node:v0.7.1 - container_name: sov-celestia-bridge - environment: - - KEY_NAME=validator - - CHAIN_ID=test - - STAKING_AMOUNT=1000000000utia - - DEBIAN_FRONTEND=noninteractive - depends_on: - - validator - ports: - - "26656:26656" - - "26657:26657" - - "26658:26658" - entrypoint: - - /bin/sh - - -c - - | - apt-get -qq update -qq && apt -qq install -y curl jq && rm -rf /var/lib/apt/lists/* && \ - sleep 20 && \ - until curl http://validator:26657/block?height=1; do echo "Waiting for validator..."; sleep 5; done && \ - mkdir -p /bridge/keys/keyring-test/ && cp -r /root/keyring-test/* /bridge/keys/keyring-test/ && \ - /celestia bridge init --node.store /bridge && \ - until curl http://validator:26657/block?height=1; do echo "Waiting for validator..."; sleep 5; done && \ - export GENESIS=$(curl http://validator:26657/block?height=1 | jq '.result.block_id.hash' | tr -d '"') && \ - export CELESTIA_CUSTOM="$$CHAIN_ID:$$GENESIS" && \ - echo "Starting bridge with option=$$CELESTIA_CUSTOM and key name $$KEY_NAME. API KEY:" && \ - /celestia bridge auth admin --node.store /bridge --log.level=ERROR && echo " " && \ - echo "$(/celestia bridge auth admin --node.store /bridge --log.level=ERROR)" > /root/keyring-test/bridge_1_key.txt && \ - /celestia bridge start --node.store /bridge --gateway --gateway.addr 0.0.0.0 --rpc.addr 0.0.0.0 --core.ip validator --keyring.accname $$KEY_NAME - volumes: - - ./keyring-test:/root/keyring-test - - bridge-2: - image: ghcr.io/celestiaorg/celestia-node:v0.7.1 - container_name: sov-celestia-bridge-2 - environment: - - KEY_NAME=node1 - - CHAIN_ID=test - - STAKING_AMOUNT=1000000000utia - - DEBIAN_FRONTEND=noninteractive - depends_on: - - validator - ports: - - "46656:26656" - - "46657:26657" - - "46658:26658" - entrypoint: - - /bin/sh - - -c - - | - apt-get -qq update -qq && apt -qq install -y curl jq && rm -rf /var/lib/apt/lists/* && \ - sleep 20 && \ - until curl http://validator:26657/block?height=1; do echo "Waiting for validator..."; sleep 5; done && \ - mkdir -p /bridge/keys/keyring-test/ && cp -r /root/keyring-test/* /bridge/keys/keyring-test/ && \ - /celestia bridge init --node.store /bridge && \ - until curl http://validator:26657/block?height=1; do echo "Waiting for validator..."; sleep 5; done && \ - export GENESIS=$(curl http://validator:26657/block?height=1 | jq '.result.block_id.hash' | tr -d '"') && \ - export CELESTIA_CUSTOM="$$CHAIN_ID:$$GENESIS" && \ - echo "Starting bridge with option=$$CELESTIA_CUSTOM and key name $$KEY_NAME. API KEY:" && \ - /celestia bridge auth admin --node.store /bridge --log.level=ERROR && echo " " && \ - echo "$(/celestia bridge auth admin --node.store /bridge --log.level=ERROR)" > /root/keyring-test/bridge_2_key.txt && \ - /celestia bridge start --node.store /bridge --gateway --gateway.addr 0.0.0.0 --rpc.addr 0.0.0.0 --core.ip validator --keyring.accname $$KEY_NAME - volumes: - - ./keyring-test:/root/keyring-test - - - -# TODO: -# * +Create keys only if keyring-test does not exist -# * ~Export validator API key to file. Leftovers - generate rollup_config.toml -# * +Multiple bridges -# * Bridge health-check -# * Timeoutable wait for block_height -# * Quite install script -# * Print genesis.json - - - - -# GENESIS=""; CNT=0; MAX=30; while [ "${#GENESIS}" -le 4 -a $CNT -ne $MAX ]; do GENESIS=$(curl -s http://127.0.0.1:26657/block?height=1 | jq '.result.block_id.hash' | tr -d '"'); ((CNT++)); sleep 1; done -# GENESIS=""; CNT=0; MAX=30; while [ "${#GENESIS}" -le 4 -a $CNT -ne $MAX ]; do GENESIS=$(curl -s http://127.0.0.1:26657/block?height=1 | jq '.result.block_id.hash' | tr -d '"'); ((CNT++)); sleep 1; done && \ \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 000000000..b03da1809 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,54 @@ +services: + validator: + image: validator + build: + context: . + dockerfile: Dockerfile.validator + # uncomment to provide amount of sequencers to provision (default: 1) + # command: ["/opt/entrypoint.sh", "2"] + volumes: + - credentials:/credentials + - genesis:/genesis + + sequencer-0: + image: bridge + build: + context: . + dockerfile: Dockerfile.bridge + # uncomment to provide the id of the sequencer (default: 0) + # command: ["/opt/entrypoint.sh", "0"] + ports: + - 26658:26658 + volumes: + - credentials:/credentials + - genesis:/genesis + + # Uncomment for another sequencer + # remember to adjust services.validator.command + # sequencer-1: + # image: bridge + # build: + # context: . + # dockerfile: Dockerfile.bridge + # # uncomment to provide the id of the sequencer (default: 0) + # command: ["/opt/entrypoint.sh", "1"] + # ports: + # # remap the default port as it's already used + # - 36658:26658 + # volumes: + # - credentials:/credentials + # - genesis:/genesis + +volumes: + # local volume where sequencer's credentials can persist + credentials: + driver: local + driver_opts: + type: 'none' + o: 'bind' + device: './credentials' + # a temporary fs where the genesis hash is announced + genesis: + driver_opts: + type: tmpfs + device: tmpfs diff --git a/docker/generate_configs.sh b/docker/generate_configs.sh index 8d3f4bc29..1f4944653 100755 --- a/docker/generate_configs.sh +++ b/docker/generate_configs.sh @@ -1,19 +1,67 @@ -MOUNT_FOLDER=keyring-test -NODE_1_KEY_FILE=bridge_1_key.txt -NODE_2_KEY_FILE=bridge_2_key.txt +#!/bin/bash -count=0; while [[ ! -f "$MOUNT_FOLDER/$NODE_1_KEY_FILE" && $count -lt 300 ]]; do sleep 1; ((count++)); done +# be strict +set -euo pipefail -NODE_1_KEY="$(cat "$MOUNT_FOLDER/$NODE_1_KEY_FILE" | egrep -v '^$|^WARNING|^\*\*DO NOT')"; -sed "s/^celestia_rpc_auth_token = .*/celestia_rpc_auth_token = \"$NODE_1_KEY\"/g" template.toml | \ - sed "s/^path = .*/path = \"demo_data_1\"/g" \ - > config_1.toml; +PROJECT_DIR="$(git rev-parse --show-toplevel)" +DOCKER_DIR="$PROJECT_DIR/docker" +CREDENTIALS_DIR="$DOCKER_DIR/credentials" +DOCKER_COMPOSE_CFG="$DOCKER_DIR/docker-compose.yml" +CONFIG_TEMPLATE="$DOCKER_DIR/template.toml" +CELESTIA_RPC_PORT=26658 -count=0; while [[ ! -f "$MOUNT_FOLDER/$NODE_2_KEY_FILE" && $count -lt 300 ]]; do sleep 1; ((count++)); done +# get amount of running sequencers +sequencers_running() { + docker compose -f "$DOCKER_COMPOSE_CFG" config --services | grep -c sequencer +} -NODE_1_KEY="$(cat "$MOUNT_FOLDER/$NODE_2_KEY_FILE" | egrep -v '^$|^WARNING|^\*\*DO NOT')"; -sed "s/^celestia_rpc_auth_token = .*/celestia_rpc_auth_token = \"$NODE_1_KEY\"/g" template.toml | \ - sed "s/^path = .*/path = \"demo_data_2\"/g" | \ - sed "s/^celestia_rpc_address = .*/celestia_rpc_address = \"http:\/\/127.0.0.1:46658\"/g" | \ - sed "s/^bind_port = .*/bind_port = 12346/g" \ - > config_2.toml; \ No newline at end of file +# get the jwt for given sequencer +sequencer_jwt() { + local sequencer_id="${1}" + + cat "$CREDENTIALS_DIR/bridge-${sequencer_id}.jwt" +} + +# get the rpc port the sequencer's celestia node listens on +sequencer_rpc_port() { + local sequencer_id="${1}" + + docker compose -f "$DOCKER_COMPOSE_CFG" port "sequencer-${sequencer_id}" "$CELESTIA_RPC_PORT" +} + +# create a new rollup config with given id +create_rollup_config() { + local id="${1}" + local address + local jwt + + jwt="$(sequencer_jwt "$id")" + address="http://127.0.0.1:$(sequencer_rpc_port "$id")" + storage_path="demo_data_$id" + bind_port="1234${id}" + target_file="rollup_config_${id}.toml" + + # use '|' in sed as the url has '/' + sed \ + -e "s||$jwt|" \ + -e "s|
|$address|" \ + -e "s||$storage_path|" \ + -e "s|12345|$bind_port|" \ + "$CONFIG_TEMPLATE" > "$target_file" +} + +main() { + local amount + local last_idx + + # get amount of running sequencers + amount="$(sequencers_running)" + last_idx=$((amount - 1)) + + # create the config for each rollup + for id in $(seq 0 $last_idx); do + create_rollup_config "$id" + done +} + +main diff --git a/docker/keyring-test/00958f6e5478febcc9119b15f5dc492868cbdc8d.address b/docker/keyring-test/00958f6e5478febcc9119b15f5dc492868cbdc8d.address deleted file mode 100644 index e7e0293e7..000000000 --- a/docker/keyring-test/00958f6e5478febcc9119b15f5dc492868cbdc8d.address +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMy0wOC0xOCAxNToxNDozOC4wNDYxNzcwMDcgKzAwMDAgVVRDIG09KzAuMDY5MjIxOTE3IiwiZW5jIjoiQTI1NkdDTSIsInAyYyI6ODE5MiwicDJzIjoiTW5CSTAxLWp3MlMxd1U5bCJ9.yFz0NVVhsX379F1XQQC69JLmMT5JOFlRUey6_L4XzqUk6qPrdkmy6g.5jY9L2FkpGJ70RU7.Q8hsXaRmXDLw0crcz4lUX1U00Y4Mt52tzXC3_hz6GeXsNJc8zSfZTA9RtCWpbEl4I0-BqYdfS4yjAy39Q86EhxhsmQn7HX5mhJdfXwFTh69-3JNpmNEbWRenSFB1zWuwnfJM0rz-TbA72J0tKu6lsWW9YZNxpUL-Ii6_rTTe-_SVhFbwYqy88z_XnYybnaEFJhPos6wk7gHYz6zsXUYLk0V8TCb08oSstistoXz4KbR7EeePLlw.4xpT0Rk6Bc4LzZrDkq_Q1A \ No newline at end of file diff --git a/docker/keyring-test/6973fe78bba1baa7768cb366102d2fbb5c14ed2e.address b/docker/keyring-test/6973fe78bba1baa7768cb366102d2fbb5c14ed2e.address deleted file mode 100644 index 5f07bf4a9..000000000 --- a/docker/keyring-test/6973fe78bba1baa7768cb366102d2fbb5c14ed2e.address +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMy0wOC0xOCAxNToxNDozNy45NzA5NzQ5NjUgKzAwMDAgVVRDIG09KzAuMDY4MTc4MDg1IiwiZW5jIjoiQTI1NkdDTSIsInAyYyI6ODE5MiwicDJzIjoiWGh6OTQ1UGhad29xU0JreSJ9.vzf7oKrTeEfCcw5gzPVL2D7aTLkzbZfBcB0W3haILTp5r2MBrV2-iQ.48VwpB_BvhYUFhs4.FwpwIIDrQb6capMXtJYtUNcFMX6jpGwILxweWaaIScQkRT7S3wkQnIg5lrYnh9Dnt_jbkQ3QgdqrdyA5YNMWGYQ2FQbpV1M7YVQJ9VEZXbws6R61SDQQMyfDrWUHkmdrtd-s1F9G4g5wThN3YOUOdgugMxpal5m-X2nUuRqMNzKN3leeYS_xuh5KPzOJ-ohdIISu6LfCg5Kab36ncJoYxfmurDID2B6Yy6NXAv5stQSlirzvWrk._EBC1TTZUhKwFCodNdWCkQ \ No newline at end of file diff --git a/docker/keyring-test/779d8e06d4441d25530fb5e76c928976ff369b4b.address b/docker/keyring-test/779d8e06d4441d25530fb5e76c928976ff369b4b.address deleted file mode 100644 index fa91aec5e..000000000 --- a/docker/keyring-test/779d8e06d4441d25530fb5e76c928976ff369b4b.address +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMy0wNi0wNSAwOToxMTo1My40OTQ4NDQ4MzYgKzAwMDAgVVRDIG09KzExLjAwMzk0NDI1NSIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjgxOTIsInAycyI6Im1RNi03eFpYYlI5REIzMVIifQ.r8PyyoLcrQqziDGTX3aysFEtR57UyfzmIAMAsuGDz53SgbjMepmqaQ.OqrZtJXHdYE3xp9n.5TMEqd_eyHFTWzcJDzDgcEajOfkN2nHSzJfhDHfGHvk7uu61sZRM1iaqO4-F2nIumILtX1L8j5q31AvtegfZnYCkgqkTi7i1Has-IvFMo6bA1Jj5MYofLWLwoW1A7TCmCN4IEFnoE743Z3xfDidqsXfyp30YTUpnWTxignxUyvcRGO_DFeAFYMOq6rTJMm2nGYDLbxpOO5DtsYNrx13kIDQHw-kU4JMPpPsqFGeb4DapqMCSM2Hu1Atw.2eYgHAYuNe3KMgDWPCItkA \ No newline at end of file diff --git a/docker/keyring-test/a73f3d1c9988502b80e6082cf70a0f352baec2cd.address b/docker/keyring-test/a73f3d1c9988502b80e6082cf70a0f352baec2cd.address deleted file mode 100644 index 567c584ab..000000000 --- a/docker/keyring-test/a73f3d1c9988502b80e6082cf70a0f352baec2cd.address +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMy0wOC0xOCAxNToxNjo0NC44NzIxOTk2MzUgKzAwMDAgVVRDIG09KzAuMTAwMzM5NDYwIiwiZW5jIjoiQTI1NkdDTSIsInAyYyI6ODE5MiwicDJzIjoiWVp2V3E1VEp1UnlCTFVraiJ9.zPIhQ_XLX81N7xjbmnjHAhuiG9Mm6szNmJ-e_sdklGlMPCia_kyi7w.mKDtRZg7cAcqKM9A.tcHEUzMh4UvyZphj1vwnQfLVX-xCzh0NtpEzVF3qF-kduy7uPrkLAYalgoWvqRF9b8l1ku7EmVuMoPEYglWRKCauiMAoBmSvljJJ1hdA3VRkcEtauk56FiFXURgAdb7Noeq56FDJHyGH2QQfUXBaB9hCBB60sDYvb3YWONyvQR8X6vGZ119qSekP05ol9p8VwZNfNujnesv6O_ER3lhDg4ZPmyvgC7Tt2Hh-M628oITCdSCjtoo_scIGCcIVSg.XySLfGathevNdEtK1nzoeg \ No newline at end of file diff --git a/docker/keyring-test/my_celes_key.info b/docker/keyring-test/my_celes_key.info deleted file mode 100644 index 21e24cb7e..000000000 --- a/docker/keyring-test/my_celes_key.info +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMy0wOC0xOCAxNToxNjo0NC44NzA4NzQzMDIgKzAwMDAgVVRDIG09KzAuMDk5MDE0MTI2IiwiZW5jIjoiQTI1NkdDTSIsInAyYyI6ODE5MiwicDJzIjoic1l2UnRGQjVPS21mOHBtWiJ9.eJ1kkpI4FjAAJKcKiJAFHrBH1TKOaIHFiPQv_ScKJFHE7xU02LaZ1w.u5eKGvbYWkgINQkg.H4oRpMcC6qi3DimQOfVt2-8gpaN6ru-6SUSMqtOls8HhopQ0M-hpcdJbzK3OAJyLZ_z5WOLVsFXQLTCf6sdjbkAXdlGn2Hps660cxyDnuy3bcoAvYJAyagxrSSZVib0sfdrYYzVVl-VS9wrCe5hvka4LBzEXsFIEoxuQhRqC228W71hniKX1NgKXL87HURjUX42pzNk7SyrvTI30ngeuIQGQ3LecDsW2OKQ2uh3a799RIz1howGmtAIQGnV_xNI2-_RqX1UsM-KUwikNPZXBnLigjtPLLntj6eBTcxyvFznSx75OtxPhRMnNqm0ARYN3Zuqt7wrwVCkrRw_PpwGK331LuTzg02zVjTsM1YMgEn196MFWXM8z25qaPFoZFUFwmhUnCm8SZDjz8nAGybNTw0ZV3TwjyE8e8V4xrM4zNsdXqQgQCcDdPW460XNET2csW4a99VSBXYME2H8Cwv9g.4tmPQ3IF0FxwvRytzqH31g \ No newline at end of file diff --git a/docker/keyring-test/node1.info b/docker/keyring-test/node1.info deleted file mode 100644 index 2562e47e2..000000000 --- a/docker/keyring-test/node1.info +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMy0wOC0xOCAxNToxNDozNy45Njk0MDY2MzIgKzAwMDAgVVRDIG09KzAuMDY2NjA5NzUxIiwiZW5jIjoiQTI1NkdDTSIsInAyYyI6ODE5MiwicDJzIjoiVk1leDU4TEhGN3dDbVJMQyJ9.Kz4IkUEWfsCYc6C27WYHt-jCbiLad1gbPUQIihV8s5Gyl_WLFeWBug.swUUp4n6eURByky1.E3zebuaNE-1M6SNPe0ZDz_zaKpIlMIfU2OtAczY7HTrOh6G4D5cJmS6679ZnhctpmRKKgShFz_3ZxeCgrADEM_qgWw8uhnEsVCQDglw1W2s5NdF8-5E0F8I2Cp22SR9jpJ2Ef3XTt4jHZihW_cX8E3Ra4oSHov4De6v9GD-ThbgTuTSmpSHzeFHI7YKyKzzyqSXuxRyh5TeHg1vcuXjce3wjlwftg6w8E0l9HRSBiIGp6sE4Zi0uKOirkRP2E1_IREIab455zNp7ME0zn1JkzsRP-Y3Wpyyezoj5ua_mGcFKIHy6r9NYNfwjX4Bhd4vXZZB21sZQny1TllS8qAaxCj2pkGFts7aA1lIwzZNAQBdbfvWCFlFygisKkoz6DxG_shpO_oY6YpvMebKQvZsBKitcllz3nsQ7gleTjCNmyZfyP1bnnrTwPLKt8q0.IeMg-RFNJ13NHZIJSMuwxw \ No newline at end of file diff --git a/docker/keyring-test/node2.info b/docker/keyring-test/node2.info deleted file mode 100644 index a92b571a3..000000000 --- a/docker/keyring-test/node2.info +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMy0wOC0xOCAxNToxNDozOC4wNDQ2ODM3MTUgKzAwMDAgVVRDIG09KzAuMDY3NzI4NTg0IiwiZW5jIjoiQTI1NkdDTSIsInAyYyI6ODE5MiwicDJzIjoiS1JvakRIUW5YRmdfM19pQyJ9.dj-sZHrMz0a5H1mf8j_VDV1GRQXjOa_HTKlGnkuKrDB4SmS16DSxSw.ODdL_jac1zwFhV1G.fzzpZhyeDEJno_iupe5xOux8thDW_t_4VOKynsEMpgf6GZnQLFaykRtpoHyLv8wwBY4GBVYJKQa7JSm9BTihEMwjUcVhZEh0seRv9hhK6lKJJQ5VV5n4NvzXdgt_scou9hn99z34wZRlEGT0bM-vK577rhX-orr6sGZpFTB-uy2KBSIN60zDOJHBzSnMRj9Euyh_5cVzAqt-PL_OBSNlThZwypTOHqB_4tKcgYTRTVBgYc0XjeAGqmEeqQ6I4dhqyewGjKcsFxaQe7kz1UgQyK5t-b69JmY49wRKB_xYL7Ld8A5fRFRVqE_wKwZ9xwabLzqKaxobm8LZGum7VVdMoIZyJ7DnNVLVrNbwfS5Pt0Fy7d_rKEHH0pbjCz1TeY-BGP1OW1JwIYHh2iR8qr4kQTn0RYnoQSrM_sxNLZG6P8UnTK3LBZKwu0nD6ZY.ZRNFW5_65Qv8L-_dGHllmA \ No newline at end of file diff --git a/docker/keyring-test/validator.info b/docker/keyring-test/validator.info deleted file mode 100644 index 8490e5dbc..000000000 --- a/docker/keyring-test/validator.info +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMy0wNi0wNSAwOToxMTo1My40NTIyNjg3NTIgKzAwMDAgVVRDIG09KzEwLjk2MTM2ODUwNSIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjgxOTIsInAycyI6InpjZDlSZ1NvNHFWZWNoa0MifQ.qGafV2C7i9Zqa3TW_ti874R4FjevWRxqCspsRcOPyM3ZrUwOuSwX2w.6vFFL7LFJgP2LJIC.K9rAuw1qq9wU91AipIb_11LZr7RpQX2s3JJspKMC3hi5ou9aZqCdY2z9AOcSiA7Ynnc-bnoG1dBuysUILGZXas9OGiq7KUgAEEAGENeA3jF3kIQ7VCmrL-OgmsgBj17WT2f9Fr4WskX_5HoQQxxuCmHk70eGdBiZSFYAnII5XMG9skeGyn5I4cwV1NCoU3ZRN5EufuLFGsS4wSk2SgHp2nnegwG-EIDo1W5Ci-GFE2hoNW2vxGVyoc9OCPQllU1f_2WUe7MhW74TfHqqbE_xvvoIdl1vWZYEONmku46O2iu00GuWWLleJGy9XaBrvkOZ-gqB2fXWZAX7fqXwMiKiFidjI5RINeUGBaQjyi0LoGDHQsGXsIZ0xRF1G1Z-NrqwcSTZX63naRMpLq13K0NHA9DRYeFrP_Vi2qO47fNU9Vv-Cmpsr8FmET7FQEN8cq6XG07ZbtNYLlA.m4DGni1Ma4vlfuwXUvEnUw \ No newline at end of file diff --git a/docker/rollup_config_0.toml b/docker/rollup_config_0.toml new file mode 100644 index 000000000..33d79a284 --- /dev/null +++ b/docker/rollup_config_0.toml @@ -0,0 +1,15 @@ +[da] +celestia_rpc_auth_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBbGxvdyI6WyJwdWJsaWMiLCJyZWFkIiwid3JpdGUiLCJhZG1pbiJdfQ.Ie5-ulwuVPjzs6ahngRXunEsU4oLV46-AYfbJLz94Pw" +celestia_rpc_address = "http://127.0.0.1:0.0.0.0:26658" +max_celestia_response_body_size = 104_857_600 +celestia_rpc_timeout_seconds = 60 + +[storage] +path = "demo_data_0" + +[runner] +start_height = 1 + +[runner.rpc_config] +bind_host = "127.0.0.1" +bind_port = 12340 diff --git a/docker/run-bridge.sh b/docker/run-bridge.sh new file mode 100755 index 000000000..e493fe42c --- /dev/null +++ b/docker/run-bridge.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# be strict +set -euo pipefail + +# Name for this node, with suffix taken from the first argument +# or `bridge-0` if not provided +NODE_NAME="bridge-${1:-0}" +# a private local network +P2P_NETWORK="private" +# a bridge node configuration directory +CONFIG_DIR="$CELESTIA_HOME/.celestia-bridge-$P2P_NETWORK" +# directory and the files shared with the validator node +CREDENTIALS_DIR="/credentials" +# node credentials +NODE_KEY_FILE="$CREDENTIALS_DIR/$NODE_NAME.key" +NODE_JWT_FILE="$CREDENTIALS_DIR/$NODE_NAME.jwt" +# directory where validator will write the genesis hash +GENESIS_DIR="/genesis" +GENESIS_HASH_FILE="$GENESIS_DIR/genesis_hash" + +# Wait for the validator to set up and provision us via shared dirs +wait_for_provision() { + echo "Waiting for the validator node to start" + while [[ ! ( -e "$GENESIS_HASH_FILE" && -e "$NODE_KEY_FILE" ) ]]; do + sleep 0.5 + done + + sleep 1 # let the validator finish setup + echo "Validator is ready" +} + +# Import the test account key shared by the validator +import_shared_key() { + echo "password" | cel-key import "$NODE_NAME" "$NODE_KEY_FILE" \ + --keyring-backend="test" \ + --p2p.network "$P2P_NETWORK" \ + --node.type bridge +} + +add_trusted_genesis() { + local genesis_hash + + # Read the hash of the genesis block + genesis_hash="$(cat "$GENESIS_HASH_FILE")" + # and make it trusted in the node's config + echo "Trusting a genesis: $genesis_hash" + sed -i'.bak' "s/TrustedHash = .*/TrustedHash = $genesis_hash/" "$CONFIG_DIR/config.toml" +} + +write_jwt_token() { + echo "Saving jwt token to $NODE_JWT_FILE" + celestia bridge auth admin --p2p.network "$P2P_NETWORK" > "$NODE_JWT_FILE" +} + +main() { + # Wait for a validator + wait_for_provision + # Import the key with the coins + import_shared_key + # Initialize the bridge node + celestia bridge init --p2p.network "$P2P_NETWORK" + # Trust the private blockchain + add_trusted_genesis + # Update the JWT token + write_jwt_token + # Start the bridge node + echo "Configuration finished. Running a bridge node..." + celestia bridge start \ + --core.ip validator \ + --keyring.accname "$NODE_NAME" \ + --p2p.network "$P2P_NETWORK" +} + +main diff --git a/docker/run-validator.sh b/docker/run-validator.sh new file mode 100755 index 000000000..68984a25a --- /dev/null +++ b/docker/run-validator.sh @@ -0,0 +1,163 @@ +#!/bin/bash + +# be strict +set -euo pipefail + +# Amount of bridge nodes to setup, taken from the first argument +# or 1 if not provided +BRIDGE_COUNT="${1:-1}" +# a private local network +P2P_NETWORK="private" +# a validator node configuration directory +CONFIG_DIR="$CELESTIA_HOME/.celestia-app" +# the names of the keys +NODE_NAME=validator-0 +# amounts of the coins for the keys +BRIDGE_COINS="200000000000000utia" +VALIDATOR_COINS="1000000000000000utia" +# a directory and the files shared with the bridge nodes +CREDENTIALS_DIR="/credentials" +# directory where validator will write the genesis hash +GENESIS_DIR="/genesis" +GENESIS_HASH_FILE="$GENESIS_DIR/genesis_hash" + +# Get the address of the node of given name +node_address() { + local node_name="$1" + local node_address + + node_address=$(celestia-appd keys show "$node_name" -a --keyring-backend="test") + echo "$node_address" +} + +# Waits for the given block to be created and returns it's hash +wait_for_block() { + local block_num="$1" + local block_hash="" + + # Wait for the block to be created + while [[ -z "$block_hash" ]]; do + # `|| echo` fallbacks to an empty string in case it's not ready + block_hash="$(celestia-appd query block "$block_num" 2>/dev/null | jq '.block_id.hash' || echo)" + sleep 0.5 + done + + echo "$block_hash" +} + +# Saves the hash of the genesis node and the keys funded with the coins +# to the directory shared with the bridge node +provision_bridge_nodes() { + local genesis_hash + local last_node_idx=$((BRIDGE_COUNT - 1)) + + # Save the genesis hash for the bridge + genesis_hash=$(wait_for_block 1) + echo "Saving a genesis hash to $GENESIS_HASH_FILE" + echo "$genesis_hash" > "$GENESIS_HASH_FILE" + + # Get or create the keys for bridge nodes + for node_idx in $(seq 0 "$last_node_idx"); do + local bridge_name="bridge-$node_idx" + local key_file="$CREDENTIALS_DIR/$bridge_name.key" + local addr_file="$CREDENTIALS_DIR/$bridge_name.addr" + + if [ ! -e "$key_file" ]; then + # if key don't exist yet, then create and export it + # create a new key + echo "Creating a new keys for the $bridge_name" + celestia-appd keys add "$bridge_name" --keyring-backend "test" + # export it + echo "password" | celestia-appd keys export "$bridge_name" 2> "$key_file" + # export associated address + node_address "$bridge_name" > "$addr_file" + else + # otherwise, just import it + echo "password" | celestia-appd keys import "$bridge_name" "$key_file" \ + --keyring-backend="test" + fi + done + + # Transfer the coins to bridge nodes addresses + # Coins transfer need to be after validator registers EVM address, which happens in block 2. + # see `setup_private_validator` + local start_block=2 + + for node_idx in $(seq 0 "$last_node_idx"); do + # TODO: create an issue in celestia-app and link it here + # we need to transfer the coins for each node in separate + # block, or the signing of all but the first one will fail + wait_for_block $((start_block + node_idx)) + + local bridge_name="bridge-$node_idx" + local bridge_address + + bridge_address=$(node_address "$bridge_name") + + echo "Transfering $BRIDGE_COINS coins to the $bridge_name" + echo "y" | celestia-appd tx bank send \ + "$NODE_NAME" \ + "$bridge_address" \ + "$BRIDGE_COINS" \ + --fees 21000utia + done + + # !! This is the last log entry that indicates the setup has finished for all the nodes + echo "Provisioning finished." +} + +# Set up the validator for a private alone network. +# Based on +# https://github.com/celestiaorg/celestia-app/blob/main/scripts/single-node.sh +setup_private_validator() { + local validator_addr + + # Initialize the validator + celestia-appd init "$P2P_NETWORK" --chain-id "$P2P_NETWORK" + # Derive a new private key for the validator + celestia-appd keys add "$NODE_NAME" --keyring-backend="test" + validator_addr=$(node_address "$NODE_NAME") + # Create a validator's genesis account for the genesis.json with an initial bag of coins + celestia-appd add-genesis-account "$validator_addr" "$VALIDATOR_COINS" + # Generate a genesis transaction that creates a validator with a self-delegation + celestia-appd gentx "$NODE_NAME" 5000000000utia \ + --keyring-backend="test" \ + --chain-id "$P2P_NETWORK" + # Collect the genesis transactions and form a genesis.json + celestia-appd collect-gentxs + + # Set proper defaults and change ports + # If you encounter: `sed: -I or -i may not be used with stdin` on MacOS you can mitigate by installing gnu-sed + # https://gist.github.com/andre3k1/e3a1a7133fded5de5a9ee99c87c6fa0d?permalink_comment_id=3082272#gistcomment-3082272 + sed -i'.bak' 's|"tcp://127.0.0.1:26657"|"tcp://0.0.0.0:26657"|g' "$CONFIG_DIR/config/config.toml" + sed -i'.bak' 's|"null"|"kv"|g' "$CONFIG_DIR/config/config.toml" + + # Register the validator EVM address in background + { + # wait for the genesis + wait_for_block 1 + + # private key: da6ed55cb2894ac2c9c10209c09de8e8b9d109b910338d5bf3d747a7e1fc9eb9 + celestia-appd tx qgb register \ + "$(celestia-appd keys show "$NODE_NAME" --bech val -a)" \ + 0x966e6f22781EF6a6A82BBB4DB3df8E225DfD9488 \ + --from "$NODE_NAME" \ + --fees 30000utia \ + -b block \ + -y + + echo "Registered validator's EVM address" + } & +} + +main() { + # Configure stuff + setup_private_validator + # Spawn a job to provision a bridge node later + provision_bridge_nodes & + # Start the celestia-app + echo "Configuration finished. Running a validator node..." + celestia-appd start --api.enable +} + +main diff --git a/docker/template.toml b/docker/template.toml index 57280b6a0..ed2b70ca9 100644 --- a/docker/template.toml +++ b/docker/template.toml @@ -1,14 +1,14 @@ [da] -celestia_rpc_auth_token = "" -celestia_rpc_address = "http://127.0.0.1:26658" +celestia_rpc_auth_token = "" +celestia_rpc_address = "
" max_celestia_response_body_size = 104_857_600 celestia_rpc_timeout_seconds = 60 [storage] -path = "demo_data" +path = "" [runner] -start_height = 1 +start_height = 3 [runner.rpc_config] bind_host = "127.0.0.1" diff --git a/examples/const-rollup-config/src/lib.rs b/examples/const-rollup-config/src/lib.rs index 8c1ecd54f..a220febcf 100644 --- a/examples/const-rollup-config/src/lib.rs +++ b/examples/const-rollup-config/src/lib.rs @@ -1,8 +1,8 @@ /// The namespace used by the rollup to store its data. This is a raw slice of 8 bytes. /// The rollup stores its data in the namespace b"sov-test" on Celestia. Which in this case is encoded using the /// ascii representation of each character. -pub const ROLLUP_NAMESPACE_RAW: [u8; 8] = [115, 111, 118, 45, 116, 101, 115, 116]; +pub const ROLLUP_NAMESPACE_RAW: [u8; 10] = [0, 0, 115, 111, 118, 45, 116, 101, 115, 116]; /// The DA address of the sequencer (for now we use a centralized sequencer) in the tests. /// Here this is the address of the sequencer on the celestia blockchain. -pub const SEQUENCER_DA_ADDRESS: &str = "celestia1w7wcupk5gswj25c0khnkey5fwmlndx6t5aarmk"; +pub const SEQUENCER_DA_ADDRESS: &str = "celestia1a68m2l85zn5xh0l07clk4rfvnezhywc53g8x7s"; diff --git a/examples/demo-rollup/Makefile b/examples/demo-rollup/Makefile index a49036df0..87d1164d1 100644 --- a/examples/demo-rollup/Makefile +++ b/examples/demo-rollup/Makefile @@ -1,64 +1,68 @@ -.PHONY: start start-existing start-new config submit-txn - -CONTAINER_NAME=sov-celestia-local -VALIDATOR_ADDRESS=celestia1w7wcupk5gswj25c0khnkey5fwmlndx6t5aarmk -IMAGE_NAME=dubbelosix/sov-celestia-local:genesis-v0.7.1 -RPC_PORT=26658 -KEY_NAME=validator -AMOUNT=10000000utia -START_HEIGHT=1 -BLOB_TXN_FEE=300utia -TEST_PRIVATE_KEY_PATH=../test-data/keys/minter_private_key.json -SOV_CLI_REL_PATH=../../target/debug/sov-cli - -ifndef SERIALIZED_BLOB_PATH -CONTENT := -else -CONTENT := $(shell cat $(SERIALIZED_BLOB_PATH)) -endif - -get_address = $(shell docker exec $(CONTAINER_NAME) celestia-appd keys show $(KEY_NAME) | sed -n 's/- address: \(.*\)/\1/p') -get_auth = $(shell docker exec $(CONTAINER_NAME) /celestia bridge auth admin --node.store /bridge) -get_namespace = $(shell $(SOV_CLI_REL_PATH) util print-namespace) +PROJECT_ROOT := $(shell git rev-parse --show-toplevel) +DOCKER_COMPOSE_DIR := $(PROJECT_ROOT)/docker +DOCKER_COMPOSE_CFG := $(DOCKER_COMPOSE_DIR)/docker-compose.yml +CREDENTIALS_DIR := $(DOCKER_COMPOSE_DIR)/credentials +TEST_PRIVATE_KEY_PATH := $(PROJECT_ROOT)/examples/test-data/keys/minter_private_key.json +SOV_CLI_REL_PATH := $(PROJECT_ROOT)/target/debug/sov-cli + +# at height 3 the credits will already belong to the keys +START_HEIGHT := 3 +KEY_NAME := bridge-0 +RPC_PORT := 26658 + +# get the jwt token +get_auth = $(shell cat $(CREDENTIALS_DIR)/$(KEY_NAME).jwt) + +# Docker compose commands +docker_compose := docker compose -f $(DOCKER_COMPOSE_CFG) +# start the service +compose_up := $(docker_compose) up --build --force-recreate -d +# stop the service +compose_down := $(docker_compose) down +# follow the logs +compose_logs := $(docker_compose) logs -f +# check if service is running +# we need a bridge and validator so naive check is to have at least 2 services +compose_is_running = test 2 -le "$(shell $(docker_compose) ps --filter status=running --format json | jq 'length')" + +# start the celestia network and generate a new config +start: check-docker start-network update-config + @$(compose_logs) + +# start the celestia network or attach to existing +start-network: check-docker + @echo "Starting local celestia network setup" + @if $(compose_is_running); then $(compose_logs); else $(compose_up); fi + +# stop the celestia network +stop: check-docker + @echo "Stopping local celestia network setup" + @$(compose_down) -key-exists: check-container-running - @docker exec $(CONTAINER_NAME) celestia-appd keys show $(KEY_NAME) || make create-new-key +# clean up rollup data and stop the celestia network +clean: stop clean-rollup-db -create-new-key: check-container-running - @echo "Creating new key..." - @output=$$(docker exec $(CONTAINER_NAME) celestia-appd keys add $(KEY_NAME)) - -fund-address: check-container-running - @docker exec $(CONTAINER_NAME) celestia-appd query bank balances $(get_address) | grep amount || docker exec $(CONTAINER_NAME) celestia-appd tx bank send validator $(get_address) $(AMOUNT) --fees=300utia -y +# listen to celestia network logs +compose-logs: check-docker + @echo "Showing celestia logs" + @$(compose_logs) +# check if docker is installed check-docker: @command -v docker > /dev/null 2>&1 || { echo "Docker is not installed"; exit 1; } + @# TODO: check docker compose too -check-container-running: - @echo "Container $(CONTAINER_NAME) running" - @docker ps --format '{{.Names}}' | grep -w $(CONTAINER_NAME) > /dev/null 2>&1 - -check-container-exists: - @echo "Container $(CONTAINER_NAME) exists" - @docker ps -a --format '{{.Names}}' | grep -w $(CONTAINER_NAME) > /dev/null 2>&1 - -start-existing: - @echo "Resuming existing container: $(CONTAINER_NAME)" - docker start $(CONTAINER_NAME) - -start-new: - @echo "Starting new container: $(CONTAINER_NAME)" - docker run -d --name $(CONTAINER_NAME) --platform linux/amd64 -p 26657:26657 -p 26659:26659 -p $(RPC_PORT):$(RPC_PORT) $(IMAGE_NAME) +# check if celestia network is running +check-compose-running: check-docker + @$(compose_is_running) -start-container: - @$(MAKE) check-container-running || { $(MAKE) check-container-exists && $(MAKE) start-existing || $(MAKE) start-new; } +# wait for the celestia network to perform setup and coins transfers +wait-compose-ready: + @echo "Waiting for services to finish setup" + @$(compose_logs) | awk '/Provisioning finished./ {print;exit}' # exit when encounter this log entry -validator-funded: - @until docker exec $(CONTAINER_NAME) celestia-appd query bank balances $(VALIDATOR_ADDRESS) | grep amount ; do \ - sleep 5; \ - done - -config: check-container-running +# update the rollup configuration with local network params +update-config: wait-compose-ready ifeq ($(shell uname -s),Darwin) @sed -i '' 's/^\(celestia_rpc_auth_token = \)"[^"]*"/\1"$(get_auth)"/' rollup_config.toml @sed -i '' 's#^\(celestia_rpc_address = \)"[^"]*"#\1"http://127.0.0.1:$(RPC_PORT)"#' rollup_config.toml @@ -69,31 +73,10 @@ else @sed -i 's#^\(start_height = \)[0-9]*#\1$(START_HEIGHT)#' rollup_config.toml endif -start: check-docker start-container validator-funded key-exists fund-address config - -stop: check-docker - docker stop $(CONTAINER_NAME) -clean: check-docker - -docker stop $(CONTAINER_NAME) - echo 1 - -docker rm $(CONTAINER_NAME) - echo 2 - $(MAKE) clean-rollup-db - -submit-txn : check-container-running build-sov-cli -ifndef SERIALIZED_BLOB_PATH - $(error SERIALIZED_BLOB_PATH is not defined) -else ifeq ($(wildcard $(SERIALIZED_BLOB_PATH)),) - $(error The file $(SERIALIZED_BLOB_PATH) does not exist) -else - @echo CONTENT:$(CONTENT) - @docker exec $(CONTAINER_NAME) celestia-appd tx blob PayForBlobs $(get_namespace) $(CONTENT) --from $(KEY_NAME) --chain-id=test --fees=$(BLOB_TXN_FEE) -y -endif - build-sov-cli: cargo build --bin sov-cli -test-generate-create-token-tx: check-container-running build-sov-cli +test-generate-create-token-tx: wait-compose-ready build-sov-cli $(SOV_CLI_REL_PATH) transactions import from-file bank --path ../test-data/requests/create_token.json set-rpc-url: build-sov-cli @@ -111,8 +94,8 @@ remove-insecure-keys: build-sov-cli clean-rollup-db: $(eval path := ./$(shell awk -F'=' '/^path/ {print $$2}' rollup_config.toml | tr -d '[:space:]"\n')) @if [ -z "${path}" ] || [ "${path}" = "./" ]; then \ - echo "Path is empty or too short, not safe to remove"; \ - exit 1; \ - fi + echo "Path is empty or too short, not safe to remove"; \ + exit 1; \ + fi @echo removing rollup database "${path}" rm -rf "${path}" diff --git a/examples/demo-rollup/README.md b/examples/demo-rollup/README.md index 776d604c3..90a2455a3 100644 --- a/examples/demo-rollup/README.md +++ b/examples/demo-rollup/README.md @@ -63,17 +63,26 @@ understand how to build your own state transition function, check out at the doc 1. Install Docker: . -2. Switch to the `examples/demo-rollup` directory (which is where this `README.md` is located!). +2. Follow [this guide](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-with-a-personal-access-token-classic) +to authorize yourself in github's container registry. (we use original celestia images which they publish in ghcr) + +```shell +# this has to be ran only once, unless your token expires +$ echo $MY_PERSONAL_GITHUB_TOKEN | docker login ghcr.io -u $MY_GITHUB_USERNAME --password-stdin +``` + +3. Switch to the `examples/demo-rollup` directory (which is where this `README.md` is located!). ```shell,test-ci $ cd examples/demo-rollup/ ``` -3. Spin up a local Celestia instance as your DA layer. We've built a small Makefile to simplify that process: +4. Spin up a local Celestia instance as your DA layer. We've built a small Makefile to simplify that process: -```sh,test-ci +```sh,test-ci,bashtestmd:long-running $ make clean -$ make start # Make sure to run `make stop` when you're done with this demo! +# Make sure to run `make stop` or `make clean` when you're done with this demo! +$ make start ``` If interested, you can check out what the Makefile does [here](#Makefile). @@ -298,32 +307,23 @@ $ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method" `demo-rollup/Makefile` automates a number of things for convenience: -- Pull a docker container that runs a single instance of a Celestia full node for a local setup -- The docker container is built with Celestia 0.7.1 at present and is compatible with Sovereign's Celestia adapter) -- `make clean`: - - Stops any running containers with the name `sov-celestia-local` and also removes them - - Removes `demo-data` (or the configured path of the rollup database from rollup_config.toml) +- Starts docker compose with a Celestia network for a local setup - `make start`: - - Pulls the `sov-celestia-local:genesis-v0.7.1` docker image - - Performs a number of checks to ensure container is not already running - - Starts the container with the name `sov-celestia-local` - - Exposes the RPC port `26658` (as configured in the Makefile) + - Performs a number of checks to ensure services are not already running + - Starts the docker compose setup + - Exposes the RPC port `26658` - Waits until the container is started - - It polls the running service inside the container for a specific RPC call, so there will be some errors printed while the container is starting up. This is ok - - Creates a key inside the docker container using `celestia-appd` that is bundled inside the container - the key is named `sequencer-da-address` - - The `sequencer-da-address` key is then funded with `10000000utia` configured by the `AMOUNT` variable in the Makefile - - The validator itself runs with the key name `validator` and is also accessible inside the container but this shouldn't be necessary - Sets up the config - - `examples/const-rollup-config/src/lib.rs` is modified by the `make` command so that `pub const SEQUENCER_DA_ADDRESS` is set to the address of the key ``sov-celestia-local` that was created and funded in the previous steps - `examples/demo-rollup/rollup_config.toml` is modified - - - `start_height` is set to `1` since this is a fresh start - - `celestia_rpc_auth_token` is set to the auth token retrieved by running the container bundled `celestia-appd` - - `/celestia bridge auth admin --node.store /bridge` is the command that is run inside the container to get the token - - `celestia_rpc_address` is set to point to `127.0.0.1` and the `RPC_PORT` configured in the Makefile (default 26658) - - The config is stashed and the changes are visible once you do a `git status` after running `make start` + - `start_height` is set to `3`, which is the block in which sequencers are funded with credits + - `celestia_rpc_auth_token` is set to the auth token exposed by sequencer (in /docker/credentials directory) + - `celestia_rpc_address` is set to point to `127.0.0.1` and the `RPC_PORT` - `make stop`: - - Stops the Celestia Docker image, if running. + - Shuts down the Celestia docker compose setup, if running. - Deletes all contents of the demo-rollup database. +- `make clean`: + - Stops any running containers with the name `sov-celestia-local` and also removes them + - Removes `demo-data` (or the configured path of the rollup database from rollup_config.toml) ### Remote setup diff --git a/examples/demo-rollup/benches/prover/prover_bench.rs b/examples/demo-rollup/benches/prover/prover_bench.rs index d2ffaafdb..83293f783 100644 --- a/examples/demo-rollup/benches/prover/prover_bench.rs +++ b/examples/demo-rollup/benches/prover/prover_bench.rs @@ -13,7 +13,7 @@ use demo_stf::App; use log4rs::config::{Appender, Config, Root}; use regex::Regex; use risc0::ROLLUP_ELF; -use sov_celestia_adapter::types::{FilteredCelestiaBlock, NamespaceId}; +use sov_celestia_adapter::types::{FilteredCelestiaBlock, Namespace}; use sov_celestia_adapter::verifier::address::CelestiaAddress; use sov_celestia_adapter::verifier::{CelestiaSpec, RollupParams}; use sov_celestia_adapter::CelestiaService; @@ -103,7 +103,7 @@ fn get_config(rollup_trace: &str) -> Config { use sov_risc0_adapter::metrics::GLOBAL_HASHMAP; // The rollup stores its data in the namespace b"sov-test" on Celestia -const ROLLUP_NAMESPACE: NamespaceId = NamespaceId(ROLLUP_NAMESPACE_RAW); +const ROLLUP_NAMESPACE: Namespace = Namespace::const_v0(ROLLUP_NAMESPACE_RAW); #[macro_use] extern crate prettytable; diff --git a/examples/demo-rollup/provers/risc0/guest-celestia/Cargo.lock b/examples/demo-rollup/provers/risc0/guest-celestia/Cargo.lock index 2dbb29519..b43aa6f23 100644 --- a/examples/demo-rollup/provers/risc0/guest-celestia/Cargo.lock +++ b/examples/demo-rollup/provers/risc0/guest-celestia/Cargo.lock @@ -15,19 +15,166 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2135563fb5c609d2b2b87c1e8ce7bc41b0b45430fa9661f457981503dd5bf0" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" dependencies = [ "memchr", ] +[[package]] +name = "alloy-rlp" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" +dependencies = [ + "arrayvec", + "bytes", + "smol_str", +] + [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[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 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.0", + "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 = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-trait" version = "0.1.73" @@ -36,7 +183,19 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", +] + +[[package]] +name = "auto_impl" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -45,6 +204,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base64" version = "0.13.1" @@ -100,6 +265,18 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[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 = "blake2" version = "0.10.6" @@ -146,7 +323,7 @@ checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", - "proc-macro-crate", + "proc-macro-crate 0.1.5", "proc-macro2", "syn 1.0.109", ] @@ -173,6 +350,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + [[package]] name = "bytemuck" version = "1.14.0" @@ -190,14 +382,14 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -210,11 +402,44 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +version = "1.0.79" +source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" + +[[package]] +name = "celestia-proto" +version = "0.1.0" +source = "git+https://github.com/eigerco/celestia-node-rs.git?rev=dfe8f2f#dfe8f2f20737d9a6556e43e4dfefd63c4824f936" dependencies = [ - "libc", + "anyhow", + "prost 0.12.1", + "prost-build", + "prost-types", + "serde", + "tendermint-proto", +] + +[[package]] +name = "celestia-types" +version = "0.1.0" +source = "git+https://github.com/eigerco/celestia-node-rs.git?rev=dfe8f2f#dfe8f2f20737d9a6556e43e4dfefd63c4824f936" +dependencies = [ + "base64 0.21.4", + "bech32", + "bytes", + "celestia-proto", + "cid", + "const_format", + "enum_dispatch", + "libp2p-identity", + "multiaddr", + "nmt-rs", + "ruint", + "serde", + "serde_repr", + "sha2 0.10.8", + "tendermint", + "tendermint-proto", + "thiserror", ] [[package]] @@ -223,6 +448,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cid" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3" +dependencies = [ + "core2", + "multibase", + "multihash 0.18.1", + "serde", + "unsigned-varint", +] + [[package]] name = "const-oid" version = "0.9.5" @@ -233,12 +471,41 @@ checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" name = "const-rollup-config" version = "0.2.0" +[[package]] +name = "const_format" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.9" @@ -248,6 +515,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -260,9 +533,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" dependencies = [ "cfg-if", "cpufeatures", @@ -270,7 +543,7 @@ dependencies = [ "digest 0.10.7", "fiat-crypto", "platforms", - "rustc_version", + "rustc_version 0.4.0", "subtle", ] @@ -282,7 +555,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -298,6 +571,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "data-encoding-macro" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + [[package]] name = "demo-stf" version = "0.2.0" @@ -338,6 +637,17 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +[[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_more" version = "0.99.17" @@ -347,7 +657,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.0", "syn 1.0.109", ] @@ -374,9 +684,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" +checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" [[package]] name = "ed25519" @@ -411,7 +721,7 @@ dependencies = [ "curve25519-dalek", "ed25519", "serde", - "sha2 0.10.6", + "sha2 0.10.8", ] [[package]] @@ -426,6 +736,18 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2b183d6ce6ca4cf30e3db37abf5b52568b5f9015c97d9fbdd7026aa5dcdd758" +[[package]] +name = "enum_dispatch" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -434,9 +756,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" dependencies = [ "errno-dragonfly", "libc", @@ -465,9 +787,20 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fastrlp" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] [[package]] name = "fiat-crypto" @@ -475,6 +808,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" +[[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 = "fixedbitset" version = "0.4.2" @@ -491,6 +836,21 @@ dependencies = [ "paste", ] +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.28" @@ -584,9 +944,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" [[package]] name = "heck" @@ -604,10 +964,22 @@ dependencies = [ ] [[package]] -name = "hex-literal" -version = "0.4.1" +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] [[package]] name = "home" @@ -631,10 +1003,40 @@ dependencies = [ "prost 0.11.9", "ripemd", "serde", - "sha2 0.10.6", + "sha2 0.10.8", "sha3", ] +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "indenter" version = "0.3.3" @@ -643,12 +1045,12 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.1", ] [[package]] @@ -670,6 +1072,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -687,12 +1098,12 @@ dependencies = [ "hashbrown 0.13.2", "hex", "ics23", - "itertools", + "itertools 0.10.5", "mirai-annotations", "num-derive 0.3.3", "num-traits", "serde", - "sha2 0.10.6", + "sha2 0.10.8", "thiserror", "tracing", ] @@ -706,6 +1117,12 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.148" @@ -714,15 +1131,31 @@ checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libp2p-identity" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "f37304f29c82ede408db06aaba60cd2f783a111f46414d3fc4beedac19e0c67b" +dependencies = [ + "bs58", + "hkdf", + "log", + "multihash 0.19.1", + "quick-protobuf", + "rand", + "sha2 0.10.8", + "thiserror", +] [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" [[package]] name = "log" @@ -732,9 +1165,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mirai-annotations" @@ -742,6 +1175,71 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" +[[package]] +name = "multiaddr" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92a651988b3ed3ad1bc8c87d016bb92f6f395b84ed1db9b926b32b1fc5a2c8b5" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash 0.19.1", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd8a792c1694c6da4f68db0a9d707c72bd260994da179e6030a5dcee00bb815" +dependencies = [ + "core2", + "multihash-derive", + "unsigned-varint", +] + +[[package]] +name = "multihash" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" +dependencies = [ + "core2", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + [[package]] name = "multimap" version = "0.8.3" @@ -751,12 +1249,23 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "nmt-rs" version = "0.1.0" -source = "git+https://github.com/Sovereign-Labs/nmt-rs.git?rev=dd37588444fca72825d11fe4a46838f66525c49f#dd37588444fca72825d11fe4a46838f66525c49f" +source = "git+https://github.com/Sovereign-Labs/nmt-rs.git?rev=d821332#d821332baa03aea625d23060dc239af57b9121f5" dependencies = [ "borsh", "bytes", "serde", - "sha2 0.10.6", + "sha2 0.10.8", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", ] [[package]] @@ -778,7 +1287,17 @@ checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", ] [[package]] @@ -788,6 +1307,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -802,12 +1322,55 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "parity-scale-codec" +version = "3.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pest" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + [[package]] name = "petgraph" version = "0.6.4" @@ -846,6 +1409,12 @@ version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "prettyplease" version = "0.2.15" @@ -853,7 +1422,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.37", + "syn 2.0.38", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", ] [[package]] @@ -865,15 +1445,65 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" +dependencies = [ + "bitflags 2.4.0", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "unarray", +] + [[package]] name = "prost" version = "0.11.9" @@ -902,16 +1532,16 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac" dependencies = [ "bytes", "heck", - "itertools", + "itertools 0.11.0", "log", "multimap", "once_cell", "petgraph", "prettyplease", "prost 0.12.1", - "prost-types 0.12.1", + "prost-types", "regex", - "syn 2.0.37", + "syn 2.0.38", "tempfile", "which", ] @@ -923,7 +1553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", @@ -936,28 +1566,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" dependencies = [ "anyhow", - "itertools", + "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "prost-types" -version = "0.11.9" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" dependencies = [ - "prost 0.11.9", + "prost 0.12.1", ] [[package]] -name = "prost-types" -version = "0.12.1" +name = "quick-protobuf" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" dependencies = [ - "prost 0.12.1", + "byteorder", ] [[package]] @@ -969,11 +1599,50 @@ 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", +] + +[[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 = "redox_syscall" @@ -986,9 +1655,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.5" +version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", @@ -998,9 +1667,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", @@ -1077,7 +1746,7 @@ dependencies = [ "risc0-core", "risc0-zkvm-platform", "serde", - "sha2 0.10.6", + "sha2 0.10.8", "tracing", ] @@ -1112,20 +1781,74 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8524b46783b58b00e9b2a4712e837093c975b23cf25bfaf99e1cf69e9011bf6b" +[[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.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.19", ] [[package]] name = "rustix" -version = "0.38.13" +version = "0.38.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" dependencies = [ "bitflags 2.4.0", "errno", @@ -1166,9 +1889,27 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" + +[[package]] +name = "semver-parser" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] [[package]] name = "serde" @@ -1196,7 +1937,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1229,7 +1970,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1247,8 +1988,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" -source = "git+https://github.com/risc0/RustCrypto-hashes?tag=sha2/v0.10.6-risc0#e75cafd9f55da196061f6fadf8bc8a86778192b7" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -1282,6 +2024,15 @@ dependencies = [ "sov-state", ] +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] + [[package]] name = "sov-accounts" version = "0.2.0" @@ -1331,16 +2082,15 @@ dependencies = [ "base64 0.21.4", "bech32", "borsh", + "celestia-proto", + "celestia-types", "hex", - "hex-literal", "nmt-rs", - "prost 0.11.9", - "prost-build", - "prost-types 0.11.9", + "prost 0.12.1", "risc0-zkvm", "risc0-zkvm-platform", "serde", - "sha2 0.10.6", + "sha2 0.10.8", "sov-rollup-interface", "sov-zk-cycle-macros", "tendermint", @@ -1393,7 +2143,7 @@ dependencies = [ "hex", "jmt", "serde", - "sha2 0.10.6", + "sha2 0.10.8", "sov-first-read-last-write-cache", "sov-modules-macros", "sov-rollup-interface", @@ -1494,7 +2244,7 @@ dependencies = [ "jmt", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.8", "sov-first-read-last-write-cache", "sov-rollup-interface", "thiserror", @@ -1552,6 +2302,12 @@ dependencies = [ "der", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1586,15 +2342,33 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.8.0" @@ -1610,9 +2384,8 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" +version = "0.32.0" +source = "git+https://github.com/eigerco/celestia-tendermint-rs.git?rev=1f8b574#1f8b574809a43b892f4beb59b887919b484fe232" dependencies = [ "bytes", "digest 0.10.7", @@ -1622,13 +2395,13 @@ dependencies = [ "futures", "num-traits", "once_cell", - "prost 0.11.9", - "prost-types 0.11.9", + "prost 0.12.1", + "prost-types", "serde", "serde_bytes", "serde_json", "serde_repr", - "sha2 0.10.6", + "sha2 0.10.8", "signature", "subtle", "subtle-encoding", @@ -1639,16 +2412,15 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" +version = "0.32.0" +source = "git+https://github.com/eigerco/celestia-tendermint-rs.git?rev=1f8b574#1f8b574809a43b892f4beb59b887919b484fe232" dependencies = [ "bytes", "flex-error", "num-derive 0.3.3", "num-traits", - "prost 0.11.9", - "prost-types 0.11.9", + "prost 0.12.1", + "prost-types", "serde", "serde_bytes", "subtle-encoding", @@ -1657,29 +2429,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "time" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ "deranged", "serde", @@ -1689,19 +2461,34 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml" version = "0.5.11" @@ -1731,7 +2518,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1749,12 +2536,80 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[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-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[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.4" @@ -1845,6 +2700,15 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -1862,10 +2726,10 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[patch.unused]] -name = "cc" -version = "1.0.79" -source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" +name = "sha2" +version = "0.10.6" +source = "git+https://github.com/risc0/RustCrypto-hashes?tag=sha2/v0.10.6-risc0#e75cafd9f55da196061f6fadf8bc8a86778192b7" diff --git a/examples/demo-rollup/provers/risc0/guest-celestia/src/bin/rollup.rs b/examples/demo-rollup/provers/risc0/guest-celestia/src/bin/rollup.rs index 742584634..712f5a450 100644 --- a/examples/demo-rollup/provers/risc0/guest-celestia/src/bin/rollup.rs +++ b/examples/demo-rollup/provers/risc0/guest-celestia/src/bin/rollup.rs @@ -4,12 +4,12 @@ use const_rollup_config::ROLLUP_NAMESPACE_RAW; use demo_stf::{create_zk_app_template, AppVerifier}; -use sov_celestia_adapter::types::NamespaceId; +use sov_celestia_adapter::types::Namespace; use sov_celestia_adapter::verifier::CelestiaVerifier; use sov_risc0_adapter::guest::Risc0Guest; // The rollup stores its data in the namespace b"sov-test" on Celestia -const ROLLUP_NAMESPACE: NamespaceId = NamespaceId(ROLLUP_NAMESPACE_RAW); +const ROLLUP_NAMESPACE: Namespace = Namespace::const_v0(ROLLUP_NAMESPACE_RAW); risc0_zkvm::guest::entry!(main); diff --git a/examples/demo-rollup/rollup_config.toml b/examples/demo-rollup/rollup_config.toml index 1aaf8160f..23f9be3ff 100644 --- a/examples/demo-rollup/rollup_config.toml +++ b/examples/demo-rollup/rollup_config.toml @@ -15,7 +15,7 @@ path = "demo_data" # We define the rollup's genesis to occur at block number `start_height`. The rollup will ignore # any blocks before this height [runner] -start_height = 1 +start_height = 3 [runner.rpc_config] # the host and port to bind the rpc server for diff --git a/examples/demo-rollup/src/lib.rs b/examples/demo-rollup/src/lib.rs index e25d878f5..48973dd44 100644 --- a/examples/demo-rollup/src/lib.rs +++ b/examples/demo-rollup/src/lib.rs @@ -12,13 +12,13 @@ pub use rollup::{ new_rollup_with_celestia_da, new_rollup_with_mock_da, new_rollup_with_mock_da_from_config, DemoProverConfig, Rollup, }; -use sov_celestia_adapter::types::NamespaceId; +use sov_celestia_adapter::types::Namespace; #[cfg(feature = "native")] use sov_db::ledger_db::LedgerDB; /// The rollup stores its data in the namespace b"sov-test" on Celestia /// You can change this constant to point your rollup at a different namespace -pub const ROLLUP_NAMESPACE: NamespaceId = NamespaceId(ROLLUP_NAMESPACE_RAW); +pub const ROLLUP_NAMESPACE: Namespace = Namespace::const_v0(ROLLUP_NAMESPACE_RAW); #[cfg(feature = "native")] /// Initializes a [`LedgerDB`] using the provided `path`. diff --git a/examples/demo-rollup/src/main.rs b/examples/demo-rollup/src/main.rs index e97578262..45e7b5630 100644 --- a/examples/demo-rollup/src/main.rs +++ b/examples/demo-rollup/src/main.rs @@ -54,7 +54,7 @@ async fn main() -> Result<(), anyhow::Error> { // Initializing logging tracing_subscriber::registry() .with(fmt::layer()) - .with(EnvFilter::from_str("info,sov_sequencer=warn").unwrap()) + .with(EnvFilter::from_str("debug,hyper=info").unwrap()) .init(); let args = Args::parse(); diff --git a/examples/demo-rollup/src/test_rpc.rs b/examples/demo-rollup/src/test_rpc.rs index 3b5021b3b..cd3f093b9 100644 --- a/examples/demo-rollup/src/test_rpc.rs +++ b/examples/demo-rollup/src/test_rpc.rs @@ -80,7 +80,7 @@ fn test_helper(test_queries: Vec, slots: Vec().await; }); } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 2f4e494e6..82ae4375d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -24,12 +24,6 @@ sov-state = { path = "../module-system/sov-state" } [workspace] members = ["."] -[[bin]] -name = "namespace_group_from_b64" -path = "fuzz_targets/namespace_group_from_b64.rs" -test = false -doc = false - [[bin]] name = "parse_address" path = "fuzz_targets/parse_address.rs" diff --git a/fuzz/fuzz_targets/namespace_group_from_b64.rs b/fuzz/fuzz_targets/namespace_group_from_b64.rs deleted file mode 100644 index 1ce6b9f65..000000000 --- a/fuzz/fuzz_targets/namespace_group_from_b64.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![no_main] -#[macro_use] -extern crate libfuzzer_sys; - -use sov_celestia_adapter::shares::NamespaceGroup; - -fuzz_target!(|data: &[u8]| { - if let Ok(s) = std::str::from_utf8(data) { - let _ = NamespaceGroup::from_b64(s).ok(); - } -});