From 5191370360fb42f0f125ad778b6fa9cb53ea9d74 Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Mon, 9 Mar 2026 12:31:49 +0000 Subject: [PATCH 01/14] feat(sequencer): use logos-blockchain zone-sdk --- Cargo.lock | 492 +++++++++++++----- Cargo.toml | 1 + Justfile | 2 + indexer/core/Cargo.toml | 3 +- indexer/core/src/lib.rs | 289 ++-------- integration_tests/src/config.rs | 116 ++--- sequencer_core/Cargo.toml | 2 +- sequencer_core/src/block_publisher.rs | 95 ++++ sequencer_core/src/block_settlement_client.rs | 127 ----- sequencer_core/src/config.rs | 4 - sequencer_core/src/lib.rs | 177 +++---- sequencer_core/src/mock.rs | 81 +-- sequencer_rpc/Cargo.toml | 1 - sequencer_rpc/src/lib.rs | 10 +- sequencer_rpc/src/process.rs | 22 +- sequencer_runner/src/lib.rs | 103 +--- 16 files changed, 675 insertions(+), 850 deletions(-) create mode 100644 sequencer_core/src/block_publisher.rs delete mode 100644 sequencer_core/src/block_settlement_client.rs diff --git a/Cargo.lock b/Cargo.lock index 6bb8255c..06d71bcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -726,7 +726,7 @@ dependencies = [ "ark-ff 0.4.2", "ark-std 0.4.0", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] @@ -738,7 +738,7 @@ dependencies = [ "ark-ff 0.5.0", "ark-std 0.5.0", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] @@ -1198,7 +1198,7 @@ dependencies = [ "bitflags 2.11.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.11.0", "proc-macro2", "quote", "regex", @@ -1349,7 +1349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85a885520bf6249ab931a764ffdb87b0ceef48e6e7d807cfdb21b751e086e1ad" dependencies = [ "prost 0.14.3", - "prost-types", + "prost-types 0.14.3", "tonic", "tonic-prost", "ureq", @@ -2344,7 +2344,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2645,7 +2645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3309,6 +3309,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "html-escape" version = "0.2.13" @@ -3523,7 +3534,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -3709,6 +3720,7 @@ dependencies = [ "humantime-serde", "log", "logos-blockchain-core", + "logos-blockchain-zone-sdk", "nssa", "nssa_core", "serde", @@ -4644,7 +4656,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "logos-blockchain-blend-crypto" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "blake2", "logos-blockchain-groth16", @@ -4658,7 +4670,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-message" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "blake2", "derivative", @@ -4680,7 +4692,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-proofs" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "ed25519-dalek", "generic-array 1.3.5", @@ -4698,7 +4710,7 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-broadcast-service" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "derivative", @@ -4714,7 +4726,7 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-service" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "bytes", @@ -4729,6 +4741,7 @@ dependencies = [ "logos-blockchain-services-utils", "logos-blockchain-storage-service", "logos-blockchain-time-service", + "logos-blockchain-tracing", "logos-blockchain-utils", "num-bigint 0.4.6", "overwatch", @@ -4744,7 +4757,7 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-prover" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "logos-blockchain-circuits-utils", "tempfile", @@ -4753,7 +4766,7 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-utils" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "dirs", ] @@ -4761,7 +4774,7 @@ dependencies = [ [[package]] name = "logos-blockchain-common-http-client" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "futures", "hex", @@ -4781,7 +4794,7 @@ dependencies = [ [[package]] name = "logos-blockchain-core" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "ark-ff 0.4.2", "bincode", @@ -4811,7 +4824,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-engine" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "cfg_eval", "logos-blockchain-pol", @@ -4827,7 +4840,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-sync" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "bytes", "futures", @@ -4844,7 +4857,7 @@ dependencies = [ [[package]] name = "logos-blockchain-groth16" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -4862,11 +4875,12 @@ dependencies = [ [[package]] name = "logos-blockchain-http-api-common" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "axum 0.7.9", "logos-blockchain-core", "logos-blockchain-key-management-system-keys", + "logos-blockchain-tracing", "serde", "serde_json", "serde_with", @@ -4876,7 +4890,7 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-keys" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "bytes", @@ -4902,7 +4916,7 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-macros" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "proc-macro2", "quote", @@ -4912,7 +4926,7 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-operators" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "logos-blockchain-blend-proofs", @@ -4928,12 +4942,13 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-service" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "log", "logos-blockchain-key-management-system-keys", "logos-blockchain-key-management-system-operators", + "logos-blockchain-tracing", "overwatch", "serde", "thiserror 2.0.18", @@ -4944,7 +4959,7 @@ dependencies = [ [[package]] name = "logos-blockchain-ledger" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "derivative", "logos-blockchain-blend-crypto", @@ -4968,12 +4983,13 @@ dependencies = [ [[package]] name = "logos-blockchain-network-service" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "futures", "logos-blockchain-core", "logos-blockchain-cryptarchia-sync", + "logos-blockchain-tracing", "overwatch", "serde", "tokio", @@ -4984,7 +5000,7 @@ dependencies = [ [[package]] name = "logos-blockchain-poc" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", @@ -5000,7 +5016,7 @@ dependencies = [ [[package]] name = "logos-blockchain-pol" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "astro-float", "logos-blockchain-circuits-prover", @@ -5019,7 +5035,7 @@ dependencies = [ [[package]] name = "logos-blockchain-poq" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", @@ -5036,7 +5052,7 @@ dependencies = [ [[package]] name = "logos-blockchain-poseidon2" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "ark-bn254 0.4.0", "ark-ff 0.4.2", @@ -5047,7 +5063,7 @@ dependencies = [ [[package]] name = "logos-blockchain-services-utils" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "futures", @@ -5062,13 +5078,14 @@ dependencies = [ [[package]] name = "logos-blockchain-storage-service" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "bytes", "futures", "logos-blockchain-core", "logos-blockchain-cryptarchia-engine", + "logos-blockchain-tracing", "overwatch", "serde", "thiserror 1.0.69", @@ -5079,12 +5096,13 @@ dependencies = [ [[package]] name = "logos-blockchain-time-service" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "futures", "log", "logos-blockchain-cryptarchia-engine", + "logos-blockchain-tracing", "overwatch", "sntpc", "thiserror 2.0.18", @@ -5094,10 +5112,33 @@ dependencies = [ "tracing", ] +[[package]] +name = "logos-blockchain-tracing" +version = "0.2.1" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" +dependencies = [ + "opentelemetry", + "opentelemetry-appender-tracing", + "opentelemetry-http", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "rand 0.8.5", + "serde", + "tokio", + "tracing", + "tracing-appender", + "tracing-gelf", + "tracing-loki", + "tracing-opentelemetry", + "tracing-subscriber 0.3.23", + "url", +] + [[package]] name = "logos-blockchain-utils" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "async-trait", "blake2", @@ -5114,7 +5155,7 @@ dependencies = [ [[package]] name = "logos-blockchain-utxotree" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "ark-ff 0.4.2", "logos-blockchain-groth16", @@ -5128,7 +5169,7 @@ dependencies = [ [[package]] name = "logos-blockchain-witness-generator" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "tempfile", ] @@ -5136,7 +5177,7 @@ dependencies = [ [[package]] name = "logos-blockchain-zksign" version = "0.2.1" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81dbb4517aa466358ed425d92fad7d45a0c419fd" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", @@ -5150,6 +5191,33 @@ dependencies = [ "tracing", ] +[[package]] +name = "logos-blockchain-zone-sdk" +version = "0.2.1" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#c854beb0973a12d5f12fee658e63e48d1911270c" +dependencies = [ + "futures", + "logos-blockchain-common-http-client", + "logos-blockchain-core", + "logos-blockchain-key-management-system-service", + "reqwest", + "rpds", + "serde", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "loki-api" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc38a304f59a03e6efa3876766a48c70a766a93f88341c3fff4212834b8e327" +dependencies = [ + "prost 0.13.5", + "prost-types 0.13.5", +] + [[package]] name = "lru-slab" version = "0.1.2" @@ -5263,6 +5331,21 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "matchit" version = "0.7.3" @@ -5569,6 +5652,15 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "num" version = "0.4.3" @@ -5809,6 +5901,98 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.18", +] + +[[package]] +name = "opentelemetry-appender-tracing" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6a1ac5ca3accf562b8c306fa8483c85f4390f768185ab775f242f7fe8fdcc2" +dependencies = [ + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "opentelemetry-http" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" +dependencies = [ + "async-trait", + "bytes", + "http 1.4.0", + "opentelemetry", + "reqwest", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" +dependencies = [ + "http 1.4.0", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost 0.14.3", + "reqwest", + "thiserror 2.0.18", + "tokio", + "tonic", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost 0.14.3", + "tonic", + "tonic-prost", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e62e29dfe041afb8ed2a6c9737ab57db4907285d999ef8ad3a59092a36bdc846" + +[[package]] +name = "opentelemetry_sdk" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "opentelemetry", + "percent-encoding", + "rand 0.9.2", + "thiserror 2.0.18", + "tokio", + "tokio-stream", +] + [[package]] name = "optfield" version = "0.4.0" @@ -6215,7 +6399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6228,12 +6412,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.117", ] +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost 0.13.5", +] + [[package]] name = "prost-types" version = "0.14.3" @@ -6270,7 +6463,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.3", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -6307,9 +6500,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -7152,7 +7345,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -7429,7 +7622,6 @@ version = "0.1.0" dependencies = [ "anyhow", "base58", - "bedrock_client", "borsh", "bytesize", "chrono", @@ -7440,6 +7632,7 @@ dependencies = [ "log", "logos-blockchain-core", "logos-blockchain-key-management-system-service", + "logos-blockchain-zone-sdk", "mempool", "nssa", "nssa_core", @@ -7461,7 +7654,6 @@ dependencies = [ "anyhow", "base58", "base64 0.22.1", - "bedrock_client", "borsh", "bytesize", "common", @@ -7763,6 +7955,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -7816,6 +8017,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + [[package]] name = "sntpc" version = "0.5.2" @@ -8094,7 +8301,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -8218,6 +8425,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "throw_error" version = "0.3.1" @@ -8618,6 +8834,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +dependencies = [ + "crossbeam-channel", + "thiserror 2.0.18", + "time", + "tracing-subscriber 0.3.23", +] + [[package]] name = "tracing-attributes" version = "0.1.31" @@ -8649,6 +8877,82 @@ dependencies = [ "tracing", ] +[[package]] +name = "tracing-gelf" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c0170f1bf67b749d4377c2da1d99d6e722600051ee53870cfb6f618611e29e" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "hostname", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-util", + "tracing-core", + "tracing-futures", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-loki" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3beec919fbdf99d719de8eda6adae3281f8a5b71ae40431f44dc7423053d34" +dependencies = [ + "loki-api", + "reqwest", + "serde", + "serde_json", + "snap", + "tokio", + "tokio-stream", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", + "tracing-subscriber 0.3.23", + "url", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc" +dependencies = [ + "js-sys", + "opentelemetry", + "smallvec", + "tracing", + "tracing-core", + "tracing-subscriber 0.3.23", + "web-time", +] + +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.2.25" @@ -8658,6 +8962,24 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "triomphe" version = "0.1.15" @@ -9246,7 +9568,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -9352,15 +9674,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -9394,30 +9707,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -9430,12 +9726,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -9448,12 +9738,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -9466,24 +9750,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -9496,12 +9768,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -9514,12 +9780,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -9532,12 +9792,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -9550,12 +9804,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.7.15" diff --git a/Cargo.toml b/Cargo.toml index bcd11651..4626f4a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,6 +115,7 @@ logos-blockchain-key-management-system-service = { git = "https://github.com/log logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } +logos-blockchain-zone-sdk = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", diff --git a/Justfile b/Justfile index b4ec3a98..8dcf3f11 100644 --- a/Justfile +++ b/Justfile @@ -64,7 +64,9 @@ clean: @echo "๐Ÿงน Cleaning run artifacts" rm -rf sequencer_runner/bedrock_signing_key rm -rf sequencer_runner/rocksdb + rm -rf sequencer_runner/zone_sdk_checkpoint.json rm -rf indexer/service/rocksdb + rm -rf indexer/service/zone_sdk_indexer_cursor.json rm -rf wallet/configs/debug/storage.json rm -rf rocksdb cd bedrock && docker compose down -v diff --git a/indexer/core/Cargo.toml b/indexer/core/Cargo.toml index 8129c1ea..be5ac178 100644 --- a/indexer/core/Cargo.toml +++ b/indexer/core/Cargo.toml @@ -10,6 +10,7 @@ workspace = true [dependencies] common.workspace = true bedrock_client.workspace = true +logos-blockchain-zone-sdk.workspace = true nssa.workspace = true nssa_core.workspace = true storage.workspace = true @@ -18,13 +19,13 @@ anyhow.workspace = true log.workspace = true serde.workspace = true humantime-serde.workspace = true -tokio.workspace = true borsh.workspace = true futures.workspace = true url.workspace = true logos-blockchain-core.workspace = true serde_json.workspace = true async-stream.workspace = true +tokio.workspace = true [dev-dependencies] tempfile.workspace = true diff --git a/indexer/core/src/lib.rs b/indexer/core/src/lib.rs index 6c96821e..92842fae 100644 --- a/indexer/core/src/lib.rs +++ b/indexer/core/src/lib.rs @@ -1,15 +1,13 @@ -use std::collections::VecDeque; +use std::sync::Arc; use anyhow::Result; -use bedrock_client::{BedrockClient, HeaderId}; use common::block::{Block, HashableBlockData}; // ToDo: Remove after testnet use common::{HashType, PINATA_BASE58}; -use log::{debug, error, info}; -use logos_blockchain_core::mantle::{ - Op, SignedMantleTx, - ops::channel::{ChannelId, inscribe::InscriptionOp}, -}; +use futures::StreamExt as _; +use log::{error, info, warn}; +use logos_blockchain_core::header::HeaderId; +use logos_blockchain_zone_sdk::indexer::ZoneIndexer; use crate::{block_store::IndexerStore, config::IndexerConfig}; @@ -18,25 +16,11 @@ pub mod config; #[derive(Clone)] pub struct IndexerCore { - pub bedrock_client: BedrockClient, + pub zone_indexer: Arc, pub config: IndexerConfig, pub store: IndexerStore, } -#[derive(Clone)] -/// This struct represents one L1 block data fetched from backfilling. -pub struct BackfillBlockData { - l2_blocks: Vec, - l1_header: HeaderId, -} - -#[derive(Clone)] -/// This struct represents data fetched fom backfilling in one iteration. -pub struct BackfillData { - block_data: VecDeque, - curr_fin_l1_lib_header: HeaderId, -} - impl IndexerCore { pub fn new(config: IndexerConfig) -> Result { let hashable_data = HashableBlockData { @@ -87,12 +71,15 @@ impl IndexerCore { let home = config.home.join("rocksdb"); + let auth = config.bedrock_client_config.auth.clone().map(Into::into); + let zone_indexer = ZoneIndexer::new( + config.channel_id, + config.bedrock_client_config.addr.clone(), + auth, + ); + Ok(Self { - bedrock_client: BedrockClient::new( - config.bedrock_client_config.backoff, - config.bedrock_client_config.addr.clone(), - config.bedrock_client_config.auth.clone(), - )?, + zone_indexer: Arc::new(zone_indexer), config, store: IndexerStore::open_db_with_genesis(&home, &genesis_block, &state)?, }) @@ -100,247 +87,41 @@ impl IndexerCore { pub fn subscribe_parse_block_stream(&self) -> impl futures::Stream> { async_stream::stream! { - info!("Searching for initial header"); + info!("Starting zone-sdk indexer using follow()"); - let last_stored_l1_lib_header = self.store.last_observed_l1_lib_header()?; - - let mut prev_last_l1_lib_header = if let Some(last_l1_lib_header) = last_stored_l1_lib_header { - info!("Last l1 lib header found: {last_l1_lib_header}"); - last_l1_lib_header - } else { - info!("Last l1 lib header not found in DB"); - info!("Searching for the start of a channel"); - - let BackfillData { - block_data: start_buff, - curr_fin_l1_lib_header: last_l1_lib_header, - } = self.search_for_channel_start().await?; - - for BackfillBlockData { - l2_blocks: l2_block_vec, - l1_header, - } in start_buff { - let mut l2_blocks_parsed_ids: Vec<_> = l2_block_vec.iter().map(|block| block.header.block_id).collect(); - l2_blocks_parsed_ids.sort_unstable(); - info!("Parsed {} L2 blocks with ids {:?}", l2_block_vec.len(), l2_blocks_parsed_ids); - - for l2_block in l2_block_vec { - self.store.put_block(l2_block.clone(), l1_header).await?; - - yield Ok(l2_block); - } + let follow_stream = match self.zone_indexer.follow().await { + Ok(s) => s, + Err(e) => { + error!("Failed to start zone-sdk follow stream: {e}"); + return; } - - last_l1_lib_header }; - info!("Searching for initial header finished"); + let mut follow_stream = std::pin::pin!(follow_stream); - info!("Starting backfilling from {prev_last_l1_lib_header}"); - - loop { - let BackfillData { - block_data: buff, - curr_fin_l1_lib_header, - } = self - .backfill_to_last_l1_lib_header_id(prev_last_l1_lib_header, &self.config.channel_id) - .await - .inspect_err(|err| error!("Failed to backfill to last l1 lib header id with err {err:#?}"))?; - - prev_last_l1_lib_header = curr_fin_l1_lib_header; - - for BackfillBlockData { - l2_blocks: l2_block_vec, - l1_header: header, - } in buff { - let mut l2_blocks_parsed_ids: Vec<_> = l2_block_vec.iter().map(|block| block.header.block_id).collect(); - l2_blocks_parsed_ids.sort_unstable(); - info!("Parsed {} L2 blocks with ids {:?}", l2_block_vec.len(), l2_blocks_parsed_ids); - - for l2_block in l2_block_vec { - self.store.put_block(l2_block.clone(), header).await?; - - yield Ok(l2_block); + while let Some(zone_block) = follow_stream.next().await { + let block: Block = match borsh::from_slice(&zone_block.data) { + Ok(b) => b, + Err(e) => { + error!("Failed to deserialize L2 block from zone-sdk: {e}"); + continue; } - } - } - } - } - - async fn get_lib(&self) -> Result { - Ok(self.bedrock_client.get_consensus_info().await?.lib) - } - - async fn get_next_lib(&self, prev_lib: HeaderId) -> Result { - loop { - let next_lib = self.get_lib().await?; - if next_lib == prev_lib { - info!( - "Wait {:?} to not spam the node", - self.config.consensus_info_polling_interval - ); - tokio::time::sleep(self.config.consensus_info_polling_interval).await; - } else { - break Ok(next_lib); - } - } - } - - /// WARNING: depending on channel state, - /// may take indefinite amount of time. - pub async fn search_for_channel_start(&self) -> Result { - let mut curr_last_l1_lib_header = self.get_lib().await?; - let mut backfill_start = curr_last_l1_lib_header; - // ToDo: How to get root? - let mut backfill_limit = HeaderId::from([0; 32]); - // ToDo: Not scalable, initial buffer should be stored in DB to not run out of memory - // Don't want to complicate DB even more right now. - let mut block_buffer = VecDeque::new(); - - 'outer: loop { - let mut cycle_header = curr_last_l1_lib_header; - - loop { - let Some(cycle_block) = self.bedrock_client.get_block_by_id(cycle_header).await? - else { - // First run can reach root easily - // so here we are optimistic about L1 - // failing to get parent. - break; }; - // It would be better to have id, but block does not have it, so slot will do. - info!( - "INITIAL SEARCH: Observed L1 block at slot {}", - cycle_block.header().slot().into_inner() - ); - debug!( - "INITIAL SEARCH: This block header is {}", - cycle_block.header().id() - ); - debug!( - "INITIAL SEARCH: This block parent is {}", - cycle_block.header().parent() - ); + info!("Indexed L2 block {}", block.header.block_id); - let (l2_block_vec, l1_header) = - parse_block_owned(&cycle_block, &self.config.channel_id); + // TODO: Remove l1_header placeholder once storage layer + // no longer requires it. Zone-sdk handles L1 tracking internally. + let placeholder_l1_header = HeaderId::from([0_u8; 32]); - info!("Parsed {} L2 blocks", l2_block_vec.len()); - - if !l2_block_vec.is_empty() { - block_buffer.push_front(BackfillBlockData { - l2_blocks: l2_block_vec.clone(), - l1_header, - }); + if let Err(err) = self.store.put_block(block.clone(), placeholder_l1_header).await { + error!("Failed to store block {}: {err:#}", block.header.block_id); } - if let Some(first_l2_block) = l2_block_vec.first() - && first_l2_block.header.block_id == 1 - { - info!("INITIAL_SEARCH: Found channel start"); - break 'outer; - } - - // Step back to parent - let parent = cycle_block.header().parent(); - - if parent == backfill_limit { - break; - } - - cycle_header = parent; + yield Ok(block); } - info!("INITIAL_SEARCH: Reached backfill limit, refetching last l1 lib header"); - - block_buffer.clear(); - backfill_limit = backfill_start; - curr_last_l1_lib_header = self.get_next_lib(curr_last_l1_lib_header).await?; - backfill_start = curr_last_l1_lib_header; + warn!("zone-sdk follow stream ended"); } - - Ok(BackfillData { - block_data: block_buffer, - curr_fin_l1_lib_header: curr_last_l1_lib_header, - }) - } - - pub async fn backfill_to_last_l1_lib_header_id( - &self, - last_fin_l1_lib_header: HeaderId, - channel_id: &ChannelId, - ) -> Result { - let curr_fin_l1_lib_header = self.get_next_lib(last_fin_l1_lib_header).await?; - // ToDo: Not scalable, buffer should be stored in DB to not run out of memory - // Don't want to complicate DB even more right now. - let mut block_buffer = VecDeque::new(); - - let mut cycle_header = curr_fin_l1_lib_header; - loop { - let Some(cycle_block) = self.bedrock_client.get_block_by_id(cycle_header).await? else { - return Err(anyhow::anyhow!("Parent not found")); - }; - - if cycle_block.header().id() == last_fin_l1_lib_header { - break; - } - // Step back to parent - cycle_header = cycle_block.header().parent(); - - // It would be better to have id, but block does not have it, so slot will do. - info!( - "Observed L1 block at slot {}", - cycle_block.header().slot().into_inner() - ); - - let (l2_block_vec, l1_header) = parse_block_owned(&cycle_block, channel_id); - - info!("Parsed {} L2 blocks", l2_block_vec.len()); - - if !l2_block_vec.is_empty() { - block_buffer.push_front(BackfillBlockData { - l2_blocks: l2_block_vec, - l1_header, - }); - } - } - - Ok(BackfillData { - block_data: block_buffer, - curr_fin_l1_lib_header, - }) } } - -fn parse_block_owned( - l1_block: &bedrock_client::Block, - decoded_channel_id: &ChannelId, -) -> (Vec, HeaderId) { - ( - #[expect( - clippy::wildcard_enum_match_arm, - reason = "We are only interested in channel inscription ops, so it's fine to ignore the rest" - )] - l1_block - .transactions() - .flat_map(|tx| { - tx.mantle_tx.ops.iter().filter_map(|op| match op { - Op::ChannelInscribe(InscriptionOp { - channel_id, - inscription, - .. - }) if channel_id == decoded_channel_id => { - borsh::from_slice::(inscription) - .inspect_err(|err| { - error!("Failed to deserialize our inscription with err: {err:#?}"); - }) - .ok() - } - _ => None, - }) - }) - .collect(), - l1_block.header().id(), - ) -} diff --git a/integration_tests/src/config.rs b/integration_tests/src/config.rs index 0ce55177..465d106e 100644 --- a/integration_tests/src/config.rs +++ b/integration_tests/src/config.rs @@ -33,6 +33,62 @@ impl Default for SequencerPartialConfig { } } +pub fn sequencer_config( + partial: SequencerPartialConfig, + home: PathBuf, + bedrock_addr: SocketAddr, + indexer_addr: SocketAddr, + initial_data: &InitialData, +) -> Result { + let SequencerPartialConfig { + max_num_tx_in_block, + max_block_size, + mempool_max_size, + block_create_timeout, + } = partial; + + Ok(SequencerConfig { + home, + override_rust_log: None, + genesis_id: 1, + is_genesis_random: true, + max_num_tx_in_block, + max_block_size, + mempool_max_size, + block_create_timeout, + retry_pending_blocks_timeout: Duration::from_secs(120), + port: 0, + initial_accounts: initial_data.sequencer_initial_accounts(), + initial_commitments: initial_data.sequencer_initial_commitments(), + signing_key: [37; 32], + bedrock_config: BedrockConfig { + channel_id: bedrock_channel_id(), + node_url: addr_to_url(UrlProtocol::Http, bedrock_addr) + .context("Failed to convert bedrock addr to URL")?, + auth: None, + }, + indexer_rpc_url: addr_to_url(UrlProtocol::Ws, indexer_addr) + .context("Failed to convert indexer addr to URL")?, + }) +} + +pub fn wallet_config( + sequencer_addr: SocketAddr, + initial_data: &InitialData, +) -> Result { + Ok(WalletConfig { + override_rust_log: None, + sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr) + .context("Failed to convert sequencer addr to URL")?, + seq_poll_timeout: Duration::from_secs(30), + seq_tx_poll_max_blocks: 15, + seq_poll_max_retries: 10, + seq_block_poll_max_amount: 100, + initial_accounts: initial_data.wallet_initial_accounts(), + basic_auth: None, + }) +} + pub struct InitialData { pub public_accounts: Vec<(PrivateKey, u128)>, pub private_accounts: Vec<(KeyChain, Account)>, @@ -188,66 +244,6 @@ pub fn indexer_config( }) } -pub fn sequencer_config( - partial: SequencerPartialConfig, - home: PathBuf, - bedrock_addr: SocketAddr, - indexer_addr: SocketAddr, - initial_data: &InitialData, -) -> Result { - let SequencerPartialConfig { - max_num_tx_in_block, - max_block_size, - mempool_max_size, - block_create_timeout, - } = partial; - - Ok(SequencerConfig { - home, - override_rust_log: None, - genesis_id: 1, - is_genesis_random: true, - max_num_tx_in_block, - max_block_size, - mempool_max_size, - block_create_timeout, - retry_pending_blocks_timeout: Duration::from_secs(120), - port: 0, - initial_accounts: initial_data.sequencer_initial_accounts(), - initial_commitments: initial_data.sequencer_initial_commitments(), - signing_key: [37; 32], - bedrock_config: BedrockConfig { - backoff: BackoffConfig { - start_delay: Duration::from_millis(100), - max_retries: 5, - }, - channel_id: bedrock_channel_id(), - node_url: addr_to_url(UrlProtocol::Http, bedrock_addr) - .context("Failed to convert bedrock addr to URL")?, - auth: None, - }, - indexer_rpc_url: addr_to_url(UrlProtocol::Ws, indexer_addr) - .context("Failed to convert indexer addr to URL")?, - }) -} - -pub fn wallet_config( - sequencer_addr: SocketAddr, - initial_data: &InitialData, -) -> Result { - Ok(WalletConfig { - override_rust_log: None, - sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr) - .context("Failed to convert sequencer addr to URL")?, - seq_poll_timeout: Duration::from_secs(30), - seq_tx_poll_max_blocks: 15, - seq_poll_max_retries: 10, - seq_block_poll_max_amount: 100, - initial_accounts: initial_data.wallet_initial_accounts(), - basic_auth: None, - }) -} - pub fn addr_to_url(protocol: UrlProtocol, addr: SocketAddr) -> Result { // Convert 0.0.0.0 to 127.0.0.1 for client connections // When binding to port 0, the server binds to 0.0.0.0: diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index 334f093c..8bf723dc 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -13,7 +13,7 @@ nssa_core.workspace = true common.workspace = true storage.workspace = true mempool.workspace = true -bedrock_client.workspace = true +logos-blockchain-zone-sdk.workspace = true base58.workspace = true anyhow.workspace = true diff --git a/sequencer_core/src/block_publisher.rs b/sequencer_core/src/block_publisher.rs new file mode 100644 index 00000000..94147a1c --- /dev/null +++ b/sequencer_core/src/block_publisher.rs @@ -0,0 +1,95 @@ +use std::{path::Path, sync::Arc, time::Duration}; + +use anyhow::{Context as _, Result}; +use common::block::Block; +use log::info; +pub use logos_blockchain_core::mantle::ops::channel::MsgId; +pub use logos_blockchain_key_management_system_service::keys::Ed25519Key; +use logos_blockchain_zone_sdk::sequencer::{ + PublishResult, SequencerCheckpoint, SequencerConfig as ZoneSdkSequencerConfig, ZoneSequencer, +}; + +use crate::config::BedrockConfig; + +const CHECKPOINT_FILE_NAME: &str = "zone_sdk_checkpoint.json"; + +/// Trait for publishing L2 blocks to the L1 chain. +#[expect(async_fn_in_trait, reason = "We don't care about Send/Sync here")] +pub trait BlockPublisherTrait: Clone { + /// Initialize the publisher. + async fn new( + config: &BedrockConfig, + bedrock_signing_key: Ed25519Key, + checkpoint: Option, + resubmit_interval: Duration, + ) -> Result; + + /// Publish a block. Returns the checkpoint to persist. + async fn publish_block(&self, block: &Block) -> Result; +} + +/// Real block publisher backed by zone-sdk's `ZoneSequencer`. +#[derive(Clone)] +pub struct ZoneSdkPublisher { + sequencer: Arc, +} + +impl BlockPublisherTrait for ZoneSdkPublisher { + async fn new( + config: &BedrockConfig, + bedrock_signing_key: Ed25519Key, + checkpoint: Option, + resubmit_interval: Duration, + ) -> Result { + let auth = config.auth.clone().map(Into::into); + + let sequencer = ZoneSequencer::init_with_config( + config.channel_id, + bedrock_signing_key, + config.node_url.clone(), + auth, + ZoneSdkSequencerConfig { + resubmit_interval, + ..ZoneSdkSequencerConfig::default() + }, + checkpoint, + ); + + Ok(Self { + sequencer: Arc::new(sequencer), + }) + } + + async fn publish_block(&self, block: &Block) -> Result { + let data = borsh::to_vec(block).context("Failed to serialize block")?; + let PublishResult { checkpoint, .. } = self + .sequencer + .publish(data) + .await + .map_err(|e| anyhow::anyhow!("{e}"))?; + + Ok(checkpoint) + } +} + +/// Load a persisted checkpoint from the sequencer home directory. +pub fn load_checkpoint(home: &Path) -> Result> { + let path = home.join(CHECKPOINT_FILE_NAME); + if path.exists() { + let data = std::fs::read(&path).context("Failed to read checkpoint file")?; + let checkpoint: SequencerCheckpoint = + serde_json::from_slice(&data).context("Failed to deserialize checkpoint")?; + info!("Loaded zone-sdk checkpoint from {}", path.display()); + Ok(Some(checkpoint)) + } else { + Ok(None) + } +} + +/// Persist a checkpoint to the sequencer home directory. +pub fn save_checkpoint(home: &Path, checkpoint: &SequencerCheckpoint) -> Result<()> { + let path = home.join(CHECKPOINT_FILE_NAME); + let data = serde_json::to_vec(checkpoint).context("Failed to serialize checkpoint")?; + std::fs::write(&path, data).context("Failed to write checkpoint file")?; + Ok(()) +} diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs deleted file mode 100644 index 2f036b98..00000000 --- a/sequencer_core/src/block_settlement_client.rs +++ /dev/null @@ -1,127 +0,0 @@ -use anyhow::{Context as _, Result}; -use bedrock_client::BedrockClient; -pub use common::block::Block; -pub use logos_blockchain_core::mantle::{MantleTx, SignedMantleTx, ops::channel::MsgId}; -use logos_blockchain_core::mantle::{ - Op, OpProof, Transaction as _, TxHash, ledger, - ops::channel::{ChannelId, inscribe::InscriptionOp}, -}; -pub use logos_blockchain_key_management_system_service::keys::Ed25519Key; -use logos_blockchain_key_management_system_service::keys::Ed25519PublicKey; - -use crate::config::BedrockConfig; - -#[expect(async_fn_in_trait, reason = "We don't care about Send/Sync here")] -pub trait BlockSettlementClientTrait: Clone { - //// Create a new client. - fn new(config: &BedrockConfig, signing_key: Ed25519Key) -> Result; - - /// Get the bedrock channel ID used by this client. - fn bedrock_channel_id(&self) -> ChannelId; - - /// Get the bedrock signing key used by this client. - fn bedrock_signing_key(&self) -> &Ed25519Key; - - /// Post a transaction to the node. - async fn submit_inscribe_tx_to_bedrock(&self, tx: SignedMantleTx) -> Result<()>; - - /// Create and sign a transaction for inscribing data. - fn create_inscribe_tx(&self, block: &Block) -> Result<(SignedMantleTx, MsgId)> { - let inscription_data = borsh::to_vec(block)?; - log::debug!( - "The size of the block {} is {} bytes", - block.header.block_id, - inscription_data.len() - ); - let verifying_key_bytes = self.bedrock_signing_key().public_key().to_bytes(); - let verifying_key = - Ed25519PublicKey::from_bytes(&verifying_key_bytes).expect("valid ed25519 public key"); - - let inscribe_op = InscriptionOp { - channel_id: self.bedrock_channel_id(), - inscription: inscription_data, - parent: block.bedrock_parent_id.into(), - signer: verifying_key, - }; - let inscribe_op_id = inscribe_op.id(); - - let ledger_tx = ledger::Tx::new(vec![], vec![]); - - let inscribe_tx = MantleTx { - ops: vec![Op::ChannelInscribe(inscribe_op)], - ledger_tx, - // Altruistic test config - storage_gas_price: 0, - execution_gas_price: 0, - }; - - let tx_hash = inscribe_tx.hash(); - let signature_bytes = self - .bedrock_signing_key() - .sign_payload(tx_hash.as_signing_bytes().as_ref()) - .to_bytes(); - let signature = - logos_blockchain_key_management_system_service::keys::Ed25519Signature::from_bytes( - &signature_bytes, - ); - - let signed_mantle_tx = SignedMantleTx { - ops_proofs: vec![OpProof::Ed25519Sig(signature)], - ledger_tx_proof: empty_ledger_signature(&tx_hash), - mantle_tx: inscribe_tx, - }; - Ok((signed_mantle_tx, inscribe_op_id)) - } -} - -/// A component that posts block data to logos blockchain. -#[derive(Clone)] -pub struct BlockSettlementClient { - client: BedrockClient, - signing_key: Ed25519Key, - channel_id: ChannelId, -} - -impl BlockSettlementClientTrait for BlockSettlementClient { - fn new(config: &BedrockConfig, signing_key: Ed25519Key) -> Result { - let client = - BedrockClient::new(config.backoff, config.node_url.clone(), config.auth.clone()) - .context("Failed to initialize bedrock client")?; - Ok(Self { - client, - signing_key, - channel_id: config.channel_id, - }) - } - - async fn submit_inscribe_tx_to_bedrock(&self, tx: SignedMantleTx) -> Result<()> { - let (parent_id, msg_id) = match tx.mantle_tx.ops.first() { - Some(Op::ChannelInscribe(inscribe)) => (inscribe.parent, inscribe.id()), - _ => panic!("Expected ChannelInscribe op"), - }; - self.client - .post_transaction(tx) - .await - .context("Failed to post transaction to Bedrock after retries")? - .context("Failed to post transaction to Bedrock with non-retryable error")?; - - log::debug!("Posted block to Bedrock with parent id {parent_id:?} and msg id: {msg_id:?}"); - - Ok(()) - } - - fn bedrock_channel_id(&self) -> ChannelId { - self.channel_id - } - - fn bedrock_signing_key(&self) -> &Ed25519Key { - &self.signing_key - } -} - -fn empty_ledger_signature( - tx_hash: &TxHash, -) -> logos_blockchain_key_management_system_service::keys::ZkSignature { - logos_blockchain_key_management_system_service::keys::ZkKey::multi_sign(&[], tx_hash.as_ref()) - .expect("multi-sign with empty key set works") -} diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index 097d1391..47929132 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -6,7 +6,6 @@ use std::{ }; use anyhow::Result; -use bedrock_client::BackoffConfig; use bytesize::ByteSize; use common::{ block::{AccountInitialData, CommitmentsInitialData}, @@ -57,9 +56,6 @@ pub struct SequencerConfig { #[derive(Clone, Serialize, Deserialize)] pub struct BedrockConfig { - /// Fibonacci backoff retry strategy configuration. - #[serde(default)] - pub backoff: BackoffConfig, /// Bedrock channel ID. pub channel_id: ChannelId, /// Bedrock Url. diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index c844c193..a5b2c778 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -1,7 +1,6 @@ use std::{path::Path, time::Instant}; use anyhow::{Context as _, Result, anyhow}; -use bedrock_client::SignedMantleTx; #[cfg(feature = "testnet")] use common::PINATA_BASE58; use common::{ @@ -17,12 +16,12 @@ use mempool::{MemPool, MemPoolHandle}; pub use mock::SequencerCoreWithMockClients; use crate::{ - block_settlement_client::{BlockSettlementClient, BlockSettlementClientTrait, MsgId}, + block_publisher::{BlockPublisherTrait, ZoneSdkPublisher, load_checkpoint, save_checkpoint}, block_store::SequencerStore, indexer_client::{IndexerClient, IndexerClientTrait}, }; -pub mod block_settlement_client; +pub mod block_publisher; pub mod block_store; pub mod config; pub mod indexer_client; @@ -31,7 +30,7 @@ pub mod indexer_client; pub mod mock; pub struct SequencerCore< - BC: BlockSettlementClientTrait = BlockSettlementClient, + BP: BlockPublisherTrait = ZoneSdkPublisher, IC: IndexerClientTrait = IndexerClient, > { state: nssa::V02State, @@ -39,11 +38,11 @@ pub struct SequencerCore< mempool: MemPool, sequencer_config: SequencerConfig, chain_height: u64, - block_settlement_client: BC, + block_publisher: BP, indexer_client: IC, } -impl SequencerCore { +impl SequencerCore { /// Starts the sequencer using the provided configuration. /// If an existing database is found, the sequencer state is loaded from it and /// assumed to represent the correct latest state consistent with Bedrock-finalized data. @@ -67,23 +66,20 @@ impl SequencerCore SequencerCore SequencerCore SequencerCore Result { - let (tx, _msg_id) = self - .produce_new_block_with_mempool_transactions() - .context("Failed to produce new block with mempool transactions")?; - match self - .block_settlement_client - .submit_inscribe_tx_to_bedrock(tx) - .await - { - Ok(()) => {} + let block = self + .build_block_from_mempool() + .context("Failed to build block from mempool transactions")?; + + // TODO: Remove msg_id from store.update โ€” it is no longer needed now that + // zone-sdk manages L1 settlement state via its own checkpoint. + let placeholder_msg_id = [0_u8; 32]; + + match self.block_publisher.publish_block(&block).await { + Ok(checkpoint) => { + self.store.update(&block, placeholder_msg_id, &self.state)?; + save_checkpoint(&self.sequencer_config.home, &checkpoint)?; + } Err(err) => { - error!("Failed to post block data to Bedrock with error: {err:#}"); + error!("Failed to publish block to Bedrock with error: {err:#}"); + self.store.update(&block, placeholder_msg_id, &self.state)?; } } Ok(self.chain_height) } - /// Produces new block from transactions in mempool and packs it into a `SignedMantleTx`. - pub fn produce_new_block_with_mempool_transactions( - &mut self, - ) -> Result<(SignedMantleTx, MsgId)> { + /// Builds a new block from transactions in the mempool. + /// Does NOT publish or store the block โ€” the caller is responsible for that. + pub fn build_block_from_mempool(&mut self) -> Result { let now = Instant::now(); let new_block_height = self @@ -255,21 +276,12 @@ impl SequencerCore SequencerCore &nssa::V02State { @@ -326,8 +338,8 @@ impl SequencerCore BC { - self.block_settlement_client.clone() + pub fn block_publisher(&self) -> BP { + self.block_publisher.clone() } pub fn indexer_client(&self) -> IC { @@ -365,7 +377,6 @@ mod tests { use std::{pin::pin, str::FromStr as _, time::Duration}; use base58::ToBase58 as _; - use bedrock_client::BackoffConfig; use common::{ block::AccountInitialData, test_utils::sequencer_sign_key_for_testing, transaction::NSSATransaction, @@ -399,10 +410,6 @@ mod tests { initial_commitments: vec![], signing_key: *sequencer_sign_key_for_testing().value(), bedrock_config: BedrockConfig { - backoff: BackoffConfig { - start_delay: Duration::from_millis(100), - max_retries: 5, - }, channel_id: ChannelId::from([0; 32]), node_url: "http://not-used-in-unit-tests".parse().unwrap(), auth: None, @@ -460,9 +467,7 @@ mod tests { let tx = common::test_utils::produce_dummy_empty_transaction(); mempool_handle.push(tx).await.unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); (sequencer, mempool_handle) } @@ -646,23 +651,21 @@ mod tests { assert!(poll.is_pending()); // Empty the mempool by producing a block - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); // Resolve the pending push assert!(push_fut.await.is_ok()); } #[tokio::test] - async fn produce_new_block_with_mempool_transactions() { + async fn test_build_block_from_mempool() { let (mut sequencer, mempool_handle) = common_setup().await; let genesis_height = sequencer.chain_height; let tx = common::test_utils::produce_dummy_empty_transaction(); mempool_handle.push(tx).await.unwrap(); - let result = sequencer.produce_new_block_with_mempool_transactions(); + let result = sequencer.build_block_from_mempool(); assert!(result.is_ok()); assert_eq!(sequencer.chain_height, genesis_height + 1); } @@ -687,9 +690,7 @@ mod tests { mempool_handle.push(tx_replay).await.unwrap(); // Create block - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); let block = sequencer .store .get_block_at_id(sequencer.chain_height) @@ -714,9 +715,7 @@ mod tests { // The transaction should be included the first time mempool_handle.push(tx.clone()).await.unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); let block = sequencer .store .get_block_at_id(sequencer.chain_height) @@ -725,9 +724,7 @@ mod tests { // Add same transaction should fail mempool_handle.push(tx.clone()).await.unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); let block = sequencer .store .get_block_at_id(sequencer.chain_height) @@ -759,9 +756,7 @@ mod tests { ); mempool_handle.push(tx.clone()).await.unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); let block = sequencer .store .get_block_at_id(sequencer.chain_height) @@ -792,15 +787,9 @@ mod tests { let config = setup_sequencer_config(); let (mut sequencer, _mempool_handle) = SequencerCoreWithMockClients::start_from_config(config).await; - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); + sequencer.produce_new_block().await.unwrap(); + sequencer.produce_new_block().await.unwrap(); assert_eq!(sequencer.get_pending_blocks().unwrap().len(), 4); } @@ -809,15 +798,9 @@ mod tests { let config = setup_sequencer_config(); let (mut sequencer, _mempool_handle) = SequencerCoreWithMockClients::start_from_config(config).await; - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); + sequencer.produce_new_block().await.unwrap(); + sequencer.produce_new_block().await.unwrap(); let last_finalized_block = 3; sequencer @@ -850,9 +833,7 @@ mod tests { ); mempool_handle.push(tx).await.unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); // Get the metadata of the last block produced sequencer.store.latest_block_meta().unwrap() @@ -875,9 +856,7 @@ mod tests { mempool_handle.push(tx.clone()).await.unwrap(); // Step 4: Produce new block - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); // Step 5: Verify the new block has correct previous block metadata let new_block = sequencer @@ -889,10 +868,6 @@ mod tests { new_block.header.prev_block_hash, expected_prev_meta.hash, "New block's prev_block_hash should match the stored metadata hash" ); - assert_eq!( - new_block.bedrock_parent_id, expected_prev_meta.msg_id, - "New block's bedrock_parent_id should match the stored metadata msg_id" - ); assert_eq!( new_block.body.transactions, vec![tx], @@ -916,15 +891,11 @@ mod tests { // Produce multiple blocks to advance chain height let tx = common::test_utils::produce_dummy_empty_transaction(); mempool_handle.push(tx).await.unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); let tx = common::test_utils::produce_dummy_empty_transaction(); mempool_handle.push(tx).await.unwrap(); - sequencer - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer.produce_new_block().await.unwrap(); // Return the current chain height (should be genesis_id + 2) sequencer.chain_height diff --git a/sequencer_core/src/mock.rs b/sequencer_core/src/mock.rs index 45a682e2..8ceaa93c 100644 --- a/sequencer_core/src/mock.rs +++ b/sequencer_core/src/mock.rs @@ -1,69 +1,40 @@ -use anyhow::{Result, anyhow}; -use bedrock_client::SignedMantleTx; -use logos_blockchain_core::mantle::ops::channel::ChannelId; +use std::time::Duration; + +use anyhow::Result; +use common::block::Block; use logos_blockchain_key_management_system_service::keys::Ed25519Key; +use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint; use url::Url; use crate::{ - block_settlement_client::BlockSettlementClientTrait, config::BedrockConfig, - indexer_client::IndexerClientTrait, + block_publisher::BlockPublisherTrait, config::BedrockConfig, indexer_client::IndexerClientTrait, }; -pub type SequencerCoreWithMockClients = - crate::SequencerCore; +pub type SequencerCoreWithMockClients = crate::SequencerCore; #[derive(Clone)] -pub struct MockBlockSettlementClient { - bedrock_channel_id: ChannelId, - bedrock_signing_key: Ed25519Key, -} +pub struct MockBlockPublisher; -impl BlockSettlementClientTrait for MockBlockSettlementClient { - fn new(config: &BedrockConfig, signing_key: Ed25519Key) -> Result { - Ok(Self { - bedrock_channel_id: config.channel_id, - bedrock_signing_key: signing_key, +impl BlockPublisherTrait for MockBlockPublisher { + async fn new( + _config: &BedrockConfig, + _bedrock_signing_key: Ed25519Key, + _checkpoint: Option, + _resubmit_interval: Duration, + ) -> Result { + Ok(Self) + } + + async fn publish_block(&self, _block: &Block) -> Result { + use logos_blockchain_core::{header::HeaderId, mantle::ops::channel::MsgId}; + + Ok(SequencerCheckpoint { + last_msg_id: MsgId::from([0; 32]), + pending_txs: vec![], + lib: HeaderId::from([0; 32]), + lib_slot: 0.into(), }) } - - fn bedrock_channel_id(&self) -> ChannelId { - self.bedrock_channel_id - } - - fn bedrock_signing_key(&self) -> &Ed25519Key { - &self.bedrock_signing_key - } - - async fn submit_inscribe_tx_to_bedrock(&self, _tx: SignedMantleTx) -> Result<()> { - Ok(()) - } -} - -#[derive(Clone)] -pub struct MockBlockSettlementClientWithError { - bedrock_channel_id: ChannelId, - bedrock_signing_key: Ed25519Key, -} - -impl BlockSettlementClientTrait for MockBlockSettlementClientWithError { - fn new(config: &BedrockConfig, signing_key: Ed25519Key) -> Result { - Ok(Self { - bedrock_channel_id: config.channel_id, - bedrock_signing_key: signing_key, - }) - } - - fn bedrock_channel_id(&self) -> ChannelId { - self.bedrock_channel_id - } - - fn bedrock_signing_key(&self) -> &Ed25519Key { - &self.bedrock_signing_key - } - - async fn submit_inscribe_tx_to_bedrock(&self, _tx: SignedMantleTx) -> Result<()> { - Err(anyhow!("Mock error")) - } } #[derive(Copy, Clone)] diff --git a/sequencer_rpc/Cargo.toml b/sequencer_rpc/Cargo.toml index 5c76ba34..38805330 100644 --- a/sequencer_rpc/Cargo.toml +++ b/sequencer_rpc/Cargo.toml @@ -12,7 +12,6 @@ nssa.workspace = true common.workspace = true mempool.workspace = true sequencer_core = { workspace = true } -bedrock_client.workspace = true anyhow.workspace = true serde_json.workspace = true diff --git a/sequencer_rpc/src/lib.rs b/sequencer_rpc/src/lib.rs index 47e4fa75..91f3db15 100644 --- a/sequencer_rpc/src/lib.rs +++ b/sequencer_rpc/src/lib.rs @@ -7,10 +7,10 @@ use common::{ use mempool::MemPoolHandle; pub use net_utils::*; #[cfg(feature = "standalone")] -use sequencer_core::mock::{MockBlockSettlementClient, MockIndexerClient}; +use sequencer_core::mock::{MockBlockPublisher, MockIndexerClient}; use sequencer_core::{ SequencerCore, - block_settlement_client::{BlockSettlementClient, BlockSettlementClientTrait}, + block_publisher::{BlockPublisherTrait, ZoneSdkPublisher}, indexer_client::{IndexerClient, IndexerClientTrait}, }; use serde::Serialize; @@ -24,14 +24,14 @@ pub mod process; pub mod types; #[cfg(feature = "standalone")] -pub type JsonHandlerWithMockClients = JsonHandler; +pub type JsonHandlerWithMockClients = JsonHandler; // ToDo: Add necessary fields pub struct JsonHandler< - BC: BlockSettlementClientTrait = BlockSettlementClient, + BP: BlockPublisherTrait = ZoneSdkPublisher, IC: IndexerClientTrait = IndexerClient, > { - sequencer_state: Arc>>, + sequencer_state: Arc>>, mempool_handle: MemPoolHandle, max_block_size: usize, } diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 3a6a4d60..0c323365 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -25,9 +25,7 @@ use common::{ use itertools::Itertools as _; use log::warn; use nssa::{self, program::Program}; -use sequencer_core::{ - block_settlement_client::BlockSettlementClientTrait, indexer_client::IndexerClientTrait, -}; +use sequencer_core::{block_publisher::BlockPublisherTrait, indexer_client::IndexerClientTrait}; use serde_json::Value; use super::{JsonHandler, respond, types::err_rpc::RpcErr}; @@ -56,7 +54,7 @@ pub trait Process: Send + Sync + 'static { } impl< - BC: BlockSettlementClientTrait + Send + Sync + 'static, + BC: BlockPublisherTrait + Send + Sync + 'static, IC: IndexerClientTrait + Send + Sync + 'static, > Process for JsonHandler { @@ -76,7 +74,7 @@ impl< } } -impl JsonHandler { +impl JsonHandler { /// Example of request processing. fn process_temp_hello(request: Request) -> Result { let _hello_request = HelloRequest::parse(Some(request.params))?; @@ -343,7 +341,6 @@ mod tests { use base58::ToBase58 as _; use base64::{Engine as _, engine::general_purpose}; - use bedrock_client::BackoffConfig; use common::{ block::AccountInitialData, config::BasicAuth, test_utils::sequencer_sign_key_for_testing, transaction::NSSATransaction, @@ -351,7 +348,7 @@ mod tests { use nssa::AccountId; use sequencer_core::{ config::{BedrockConfig, SequencerConfig}, - mock::{MockBlockSettlementClient, MockIndexerClient, SequencerCoreWithMockClients}, + mock::{MockBlockPublisher, MockIndexerClient, SequencerCoreWithMockClients}, }; use serde_json::Value; use tempfile::tempdir; @@ -359,8 +356,7 @@ mod tests { use crate::rpc_handler; - type JsonHandlerWithMockClients = - crate::JsonHandler; + type JsonHandlerWithMockClients = crate::JsonHandler; fn sequencer_config_for_tests() -> SequencerConfig { let tempdir = tempdir().unwrap(); @@ -402,10 +398,6 @@ mod tests { signing_key: *sequencer_sign_key_for_testing().value(), retry_pending_blocks_timeout: Duration::from_secs(60 * 4), bedrock_config: BedrockConfig { - backoff: BackoffConfig { - start_delay: Duration::from_millis(100), - max_retries: 5, - }, channel_id: [42; 32].into(), node_url: "http://localhost:8080".parse().unwrap(), auth: Some(BasicAuth { @@ -450,9 +442,7 @@ mod tests { .await .expect("Mempool is closed, this is a bug"); - sequencer_core - .produce_new_block_with_mempool_transactions() - .unwrap(); + sequencer_core.produce_new_block().await.unwrap(); let max_block_size = usize::try_from(sequencer_core.sequencer_config().max_block_size.as_u64()) diff --git a/sequencer_runner/src/lib.rs b/sequencer_runner/src/lib.rs index a17ecbf9..edf814f9 100644 --- a/sequencer_runner/src/lib.rs +++ b/sequencer_runner/src/lib.rs @@ -8,11 +8,11 @@ use futures::{FutureExt as _, never::Never}; #[cfg(not(feature = "standalone"))] use log::warn; use log::{error, info}; +#[cfg(not(feature = "standalone"))] +use sequencer_core::SequencerCore; #[cfg(feature = "standalone")] use sequencer_core::SequencerCoreWithMockClients as SequencerCore; use sequencer_core::config::SequencerConfig; -#[cfg(not(feature = "standalone"))] -use sequencer_core::{SequencerCore, block_settlement_client::BlockSettlementClientTrait as _}; use sequencer_rpc::new_http_server; use tokio::{sync::Mutex, task::JoinHandle}; @@ -32,7 +32,6 @@ pub struct SequencerHandle { addr: SocketAddr, http_server_handle: ServerHandle, main_loop_handle: JoinHandle>, - retry_pending_blocks_loop_handle: JoinHandle>, listen_for_bedrock_blocks_loop_handle: JoinHandle>, } @@ -49,7 +48,6 @@ impl SequencerHandle { addr: _, http_server_handle: _, main_loop_handle, - retry_pending_blocks_loop_handle, listen_for_bedrock_blocks_loop_handle, } = self; @@ -59,11 +57,6 @@ impl SequencerHandle { .context("Main loop task panicked")? .context("Main loop exited unexpectedly") } - res = retry_pending_blocks_loop_handle => { - res - .context("Retry pending blocks loop task panicked")? - .context("Retry pending blocks loop exited unexpectedly") - } res = listen_for_bedrock_blocks_loop_handle => { res .context("Listen for bedrock blocks loop task panicked")? @@ -75,7 +68,6 @@ impl SequencerHandle { #[must_use] pub fn is_finished(&self) -> bool { self.main_loop_handle.is_finished() - || self.retry_pending_blocks_loop_handle.is_finished() || self.listen_for_bedrock_blocks_loop_handle.is_finished() } @@ -91,12 +83,10 @@ impl Drop for SequencerHandle { addr: _, http_server_handle, main_loop_handle, - retry_pending_blocks_loop_handle, listen_for_bedrock_blocks_loop_handle, } = self; main_loop_handle.abort(); - retry_pending_blocks_loop_handle.abort(); listen_for_bedrock_blocks_loop_handle.abort(); // Can't wait here as Drop can't be async, but anyway stop signal should be sent @@ -106,7 +96,6 @@ impl Drop for SequencerHandle { pub async fn startup_sequencer(app_config: SequencerConfig) -> Result { let block_timeout = app_config.block_create_timeout; - let retry_pending_blocks_timeout = app_config.retry_pending_blocks_timeout; let port = app_config.port; let (sequencer_core, mempool_handle) = SequencerCore::start_from_config(app_config).await; @@ -125,23 +114,9 @@ pub async fn startup_sequencer(app_config: SequencerConfig) -> Result Result>, block_timeout: Duration) } } -#[cfg(not(feature = "standalone"))] -async fn retry_pending_blocks(seq_core: &Arc>) -> Result<()> { - use std::time::Instant; - - use log::debug; - - let (mut pending_blocks, block_settlement_client) = { - let sequencer_core = seq_core.lock().await; - let client = sequencer_core.block_settlement_client(); - let pending_blocks = sequencer_core - .get_pending_blocks() - .expect("Sequencer should be able to retrieve pending blocks"); - (pending_blocks, client) - }; - - pending_blocks.sort_by(|block1, block2| block1.header.block_id.cmp(&block2.header.block_id)); - - if !pending_blocks.is_empty() { - info!( - "Resubmitting blocks from {} to {}", - pending_blocks.first().unwrap().header.block_id, - pending_blocks.last().unwrap().header.block_id - ); - } - - for block in &pending_blocks { - debug!( - "Resubmitting pending block with id {}", - block.header.block_id - ); - // TODO: We could cache the inscribe tx for each pending block to avoid re-creating it - // on every retry. - let now = Instant::now(); - let (tx, _msg_id) = block_settlement_client - .create_inscribe_tx(block) - .context("Failed to create inscribe tx for pending block")?; - - debug!(">>>> Create inscribe: {:?}", now.elapsed()); - - let now = Instant::now(); - if let Err(e) = block_settlement_client - .submit_inscribe_tx_to_bedrock(tx) - .await - { - warn!( - "Failed to resubmit block with id {} with error {e:#}", - block.header.block_id - ); - } - debug!(">>>> Post: {:?}", now.elapsed()); - } - Ok(()) -} - -#[cfg(not(feature = "standalone"))] -async fn retry_pending_blocks_loop( - seq_core: Arc>, - retry_pending_blocks_timeout: Duration, -) -> Result { - loop { - tokio::time::sleep(retry_pending_blocks_timeout).await; - retry_pending_blocks(&seq_core).await?; - } -} - #[cfg(not(feature = "standalone"))] async fn listen_for_bedrock_blocks_loop(seq_core: Arc>) -> Result { use indexer_service_rpc::RpcClient as _; @@ -280,14 +189,6 @@ async fn listen_for_bedrock_blocks_loop(_seq_core: Arc>) -> std::future::pending::>().await } -#[cfg(feature = "standalone")] -async fn retry_pending_blocks_loop( - _seq_core: Arc>, - _retry_pending_blocks_timeout: Duration, -) -> Result { - std::future::pending::>().await -} - pub async fn main_runner() -> Result<()> { env_logger::init(); From fee8d1623f21fc24837784a97c2866648b6e400f Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Wed, 18 Mar 2026 16:57:13 +0100 Subject: [PATCH 02/14] lints --- integration_tests/src/config.rs | 112 ++++++++++++++++---------------- sequencer_core/src/lib.rs | 2 +- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/integration_tests/src/config.rs b/integration_tests/src/config.rs index 465d106e..745f5473 100644 --- a/integration_tests/src/config.rs +++ b/integration_tests/src/config.rs @@ -33,62 +33,6 @@ impl Default for SequencerPartialConfig { } } -pub fn sequencer_config( - partial: SequencerPartialConfig, - home: PathBuf, - bedrock_addr: SocketAddr, - indexer_addr: SocketAddr, - initial_data: &InitialData, -) -> Result { - let SequencerPartialConfig { - max_num_tx_in_block, - max_block_size, - mempool_max_size, - block_create_timeout, - } = partial; - - Ok(SequencerConfig { - home, - override_rust_log: None, - genesis_id: 1, - is_genesis_random: true, - max_num_tx_in_block, - max_block_size, - mempool_max_size, - block_create_timeout, - retry_pending_blocks_timeout: Duration::from_secs(120), - port: 0, - initial_accounts: initial_data.sequencer_initial_accounts(), - initial_commitments: initial_data.sequencer_initial_commitments(), - signing_key: [37; 32], - bedrock_config: BedrockConfig { - channel_id: bedrock_channel_id(), - node_url: addr_to_url(UrlProtocol::Http, bedrock_addr) - .context("Failed to convert bedrock addr to URL")?, - auth: None, - }, - indexer_rpc_url: addr_to_url(UrlProtocol::Ws, indexer_addr) - .context("Failed to convert indexer addr to URL")?, - }) -} - -pub fn wallet_config( - sequencer_addr: SocketAddr, - initial_data: &InitialData, -) -> Result { - Ok(WalletConfig { - override_rust_log: None, - sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr) - .context("Failed to convert sequencer addr to URL")?, - seq_poll_timeout: Duration::from_secs(30), - seq_tx_poll_max_blocks: 15, - seq_poll_max_retries: 10, - seq_block_poll_max_amount: 100, - initial_accounts: initial_data.wallet_initial_accounts(), - basic_auth: None, - }) -} - pub struct InitialData { pub public_accounts: Vec<(PrivateKey, u128)>, pub private_accounts: Vec<(KeyChain, Account)>, @@ -220,6 +164,62 @@ impl std::fmt::Display for UrlProtocol { } } +pub fn sequencer_config( + partial: SequencerPartialConfig, + home: PathBuf, + bedrock_addr: SocketAddr, + indexer_addr: SocketAddr, + initial_data: &InitialData, +) -> Result { + let SequencerPartialConfig { + max_num_tx_in_block, + max_block_size, + mempool_max_size, + block_create_timeout, + } = partial; + + Ok(SequencerConfig { + home, + override_rust_log: None, + genesis_id: 1, + is_genesis_random: true, + max_num_tx_in_block, + max_block_size, + mempool_max_size, + block_create_timeout, + retry_pending_blocks_timeout: Duration::from_secs(120), + port: 0, + initial_accounts: initial_data.sequencer_initial_accounts(), + initial_commitments: initial_data.sequencer_initial_commitments(), + signing_key: [37; 32], + bedrock_config: BedrockConfig { + channel_id: bedrock_channel_id(), + node_url: addr_to_url(UrlProtocol::Http, bedrock_addr) + .context("Failed to convert bedrock addr to URL")?, + auth: None, + }, + indexer_rpc_url: addr_to_url(UrlProtocol::Ws, indexer_addr) + .context("Failed to convert indexer addr to URL")?, + }) +} + +pub fn wallet_config( + sequencer_addr: SocketAddr, + initial_data: &InitialData, +) -> Result { + Ok(WalletConfig { + override_rust_log: None, + sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr) + .context("Failed to convert sequencer addr to URL")?, + seq_poll_timeout: Duration::from_secs(30), + seq_tx_poll_max_blocks: 15, + seq_poll_max_retries: 10, + seq_block_poll_max_amount: 100, + initial_accounts: initial_data.wallet_initial_accounts(), + basic_auth: None, + }) +} + pub fn indexer_config( bedrock_addr: SocketAddr, home: PathBuf, diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index a5b2c778..78a49ba0 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -658,7 +658,7 @@ mod tests { } #[tokio::test] - async fn test_build_block_from_mempool() { + async fn build_block_from_mempool() { let (mut sequencer, mempool_handle) = common_setup().await; let genesis_height = sequencer.chain_height; From 81a9ec401c687bbfb6311603348dc3fb6d32124b Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Wed, 29 Apr 2026 11:06:05 +0200 Subject: [PATCH 03/14] indexer stream --- indexer/core/src/lib.rs | 76 +++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/indexer/core/src/lib.rs b/indexer/core/src/lib.rs index 96f4b673..936343a5 100644 --- a/indexer/core/src/lib.rs +++ b/indexer/core/src/lib.rs @@ -5,7 +5,7 @@ use common::block::{Block, HashableBlockData}; // ToDo: Remove after testnet use common::{HashType, PINATA_BASE58}; use futures::StreamExt as _; -use log::{error, info, warn}; +use log::{error, info}; use logos_blockchain_core::header::HeaderId; use logos_blockchain_zone_sdk::{ CommonHttpClient, ZoneMessage, adapter::NodeHttpClient, indexer::ZoneIndexer, @@ -15,6 +15,10 @@ use testnet_initial_state::initial_state_testnet; use crate::{block_store::IndexerStore, config::IndexerConfig}; +// TODO: persist & restore cursor (e.g. in rocksdb) so restarts don't have to +// re-process the channel from the beginning. Mirrors the sequencer checkpoint +// TODO in `block_publisher.rs`. + pub mod block_store; pub mod config; @@ -105,47 +109,59 @@ impl IndexerCore { }) } - pub fn subscribe_parse_block_stream(&self) -> impl futures::Stream> { + pub fn subscribe_parse_block_stream(&self) -> impl futures::Stream> + '_ { + let poll_interval = self.config.consensus_info_polling_interval; + async_stream::stream! { - info!("Starting zone-sdk indexer using follow()"); + // In-memory only; not persisted across restarts (see top-of-file TODO). + let mut cursor = None; - let follow_stream = match self.zone_indexer.follow().await { - Ok(s) => s, - Err(e) => { - error!("Failed to start zone-sdk follow stream: {e}"); - return; - } - }; + info!("Starting indexer from beginning of channel"); - let mut follow_stream = std::pin::pin!(follow_stream); - - while let Some(zone_message) = follow_stream.next().await { - let zone_block = match zone_message { - ZoneMessage::Block(b) => b, - ZoneMessage::Deposit(_) | ZoneMessage::Withdraw(_) => continue, - }; - let block: Block = match borsh::from_slice(&zone_block.data) { - Ok(b) => b, - Err(e) => { - error!("Failed to deserialize L2 block from zone-sdk: {e}"); + loop { + let stream = match self.zone_indexer.next_messages(cursor).await { + Ok(s) => s, + Err(err) => { + error!("Failed to start zone-sdk next_messages stream: {err}"); + tokio::time::sleep(poll_interval).await; continue; } }; + let mut stream = std::pin::pin!(stream); - info!("Indexed L2 block {}", block.header.block_id); + while let Some((msg, slot)) = stream.next().await { + let zone_block = match msg { + ZoneMessage::Block(b) => b, + // Non-block messages don't carry a cursor position; the + // next ZoneBlock advances past them implicitly. + ZoneMessage::Deposit(_) | ZoneMessage::Withdraw(_) => continue, + }; - // TODO: Remove l1_header placeholder once storage layer - // no longer requires it. Zone-sdk handles L1 tracking internally. - let placeholder_l1_header = HeaderId::from([0_u8; 32]); + let block: Block = match borsh::from_slice(&zone_block.data) { + Ok(b) => b, + Err(e) => { + error!("Failed to deserialize L2 block from zone-sdk: {e}"); + cursor = Some((zone_block.id, slot)); + continue; + } + }; - if let Err(err) = self.store.put_block(block.clone(), placeholder_l1_header).await { - error!("Failed to store block {}: {err:#}", block.header.block_id); + info!("Indexed L2 block {}", block.header.block_id); + + // TODO: Remove l1_header placeholder once storage layer + // no longer requires it. Zone-sdk handles L1 tracking internally. + let placeholder_l1_header = HeaderId::from([0_u8; 32]); + if let Err(err) = self.store.put_block(block.clone(), placeholder_l1_header).await { + error!("Failed to store block {}: {err:#}", block.header.block_id); + } + + cursor = Some((zone_block.id, slot)); + yield Ok(block); } - yield Ok(block); + // Stream ended (caught up to LIB). Sleep then poll again. + tokio::time::sleep(poll_interval).await; } - - warn!("zone-sdk follow stream ended"); } } } From 7944a772517a598fcfaf9c58a962dfb957f741e3 Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Wed, 29 Apr 2026 12:50:17 +0200 Subject: [PATCH 04/14] use new docker image --- Cargo.lock | 68 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 12 +++++----- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2f04311..d911b127 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5110,7 +5110,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "logos-blockchain-blend-crypto" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "blake2", "logos-blockchain-groth16", @@ -5124,7 +5124,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-message" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "blake2", "derivative", @@ -5147,7 +5147,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-proofs" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "ed25519-dalek", "generic-array 1.3.5", @@ -5166,7 +5166,7 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-broadcast-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "derivative", @@ -5182,7 +5182,7 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "bytes", @@ -5213,7 +5213,7 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-prover" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "logos-blockchain-circuits-utils", "tempfile", @@ -5222,7 +5222,7 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "dirs", ] @@ -5230,7 +5230,7 @@ dependencies = [ [[package]] name = "logos-blockchain-common-http-client" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "futures", "hex", @@ -5250,7 +5250,7 @@ dependencies = [ [[package]] name = "logos-blockchain-core" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "ark-ff 0.4.2", "bincode", @@ -5282,7 +5282,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-engine" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "logos-blockchain-pol", "logos-blockchain-utils", @@ -5297,7 +5297,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-sync" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "bytes", "futures", @@ -5316,7 +5316,7 @@ dependencies = [ [[package]] name = "logos-blockchain-groth16" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -5334,7 +5334,7 @@ dependencies = [ [[package]] name = "logos-blockchain-http-api-common" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "axum 0.7.9", "logos-blockchain-core", @@ -5349,7 +5349,7 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-keys" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "bytes", @@ -5375,7 +5375,7 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-macros" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "proc-macro2", "quote", @@ -5385,7 +5385,7 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-operators" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "logos-blockchain-blend-proofs", @@ -5401,7 +5401,7 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "log", @@ -5418,7 +5418,7 @@ dependencies = [ [[package]] name = "logos-blockchain-ledger" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "derivative", "logos-blockchain-blend-crypto", @@ -5444,7 +5444,7 @@ dependencies = [ [[package]] name = "logos-blockchain-libp2p" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "backon", @@ -5472,7 +5472,7 @@ dependencies = [ [[package]] name = "logos-blockchain-mmr" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "ark-ff 0.4.2", "logos-blockchain-groth16", @@ -5485,7 +5485,7 @@ dependencies = [ [[package]] name = "logos-blockchain-network-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "futures", @@ -5505,7 +5505,7 @@ dependencies = [ [[package]] name = "logos-blockchain-poc" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", @@ -5521,7 +5521,7 @@ dependencies = [ [[package]] name = "logos-blockchain-pol" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "astro-float", "logos-blockchain-circuits-prover", @@ -5540,7 +5540,7 @@ dependencies = [ [[package]] name = "logos-blockchain-poq" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", @@ -5557,7 +5557,7 @@ dependencies = [ [[package]] name = "logos-blockchain-poseidon2" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "ark-bn254 0.4.0", "ark-ff 0.4.2", @@ -5568,7 +5568,7 @@ dependencies = [ [[package]] name = "logos-blockchain-services-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "futures", @@ -5583,7 +5583,7 @@ dependencies = [ [[package]] name = "logos-blockchain-storage-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "bytes", @@ -5601,7 +5601,7 @@ dependencies = [ [[package]] name = "logos-blockchain-time-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "futures", @@ -5623,7 +5623,7 @@ dependencies = [ [[package]] name = "logos-blockchain-tracing" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "opentelemetry", "opentelemetry-appender-tracing", @@ -5647,7 +5647,7 @@ dependencies = [ [[package]] name = "logos-blockchain-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "blake2", @@ -5664,7 +5664,7 @@ dependencies = [ [[package]] name = "logos-blockchain-utxotree" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "ark-ff 0.4.2", "logos-blockchain-groth16", @@ -5678,7 +5678,7 @@ dependencies = [ [[package]] name = "logos-blockchain-witness-generator" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "tempfile", ] @@ -5686,7 +5686,7 @@ dependencies = [ [[package]] name = "logos-blockchain-zksign" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", @@ -5703,7 +5703,7 @@ dependencies = [ [[package]] name = "logos-blockchain-zone-sdk" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=f1b2a8c8e1dde0151a663795656ad3d855056d71#f1b2a8c8e1dde0151a663795656ad3d855056d71" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "async-trait", "futures", diff --git a/Cargo.toml b/Cargo.toml index d138fb56..4fcaf9f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,12 +120,12 @@ tokio-retry = "0.3.0" schemars = "1.2" async-stream = "0.3.6" -logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "f1b2a8c8e1dde0151a663795656ad3d855056d71" } -logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "f1b2a8c8e1dde0151a663795656ad3d855056d71" } -logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "f1b2a8c8e1dde0151a663795656ad3d855056d71" } -logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "f1b2a8c8e1dde0151a663795656ad3d855056d71" } -logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "f1b2a8c8e1dde0151a663795656ad3d855056d71" } -logos-blockchain-zone-sdk = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "f1b2a8c8e1dde0151a663795656ad3d855056d71" } +logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" } +logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" } +logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" } +logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" } +logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" } +logos-blockchain-zone-sdk = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", From d24313d9994ecc7512b9175700e20fc42ddfb21f Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Wed, 29 Apr 2026 12:52:38 +0200 Subject: [PATCH 05/14] new image --- bedrock/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bedrock/docker-compose.yml b/bedrock/docker-compose.yml index 73795666..e16e505b 100644 --- a/bedrock/docker-compose.yml +++ b/bedrock/docker-compose.yml @@ -1,7 +1,7 @@ services: logos-blockchain-node-0: - image: ghcr.io/logos-blockchain/logos-blockchain@sha256:c5243681b353278cabb562a176f0a5cfbefc2056f18cebc47fe0e3720c29fb12 + image: ghcr.io/logos-blockchain/logos-blockchain@sha256:9f1829dea335c56f6ff68ae37ea872ed5313b96b69e8ffe143c02b7217de85fc ports: - "${PORT:-8080}:18080/tcp" volumes: From c27e30fe5745a5d45a632573b6b970e5c9acc92d Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Wed, 29 Apr 2026 13:20:29 +0200 Subject: [PATCH 06/14] tests pass --- bedrock/deployment-settings.yaml | 62 ++++++++++++++++---------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/bedrock/deployment-settings.yaml b/bedrock/deployment-settings.yaml index d0c05e24..7ef63f03 100644 --- a/bedrock/deployment-settings.yaml +++ b/bedrock/deployment-settings.yaml @@ -39,42 +39,42 @@ cryptarchia: threshold: 1 timestamp: 0 gossipsub_protocol: /integration/logos-blockchain/cryptarchia/proto/1.0.0 - genesis_state: - mantle_tx: - ops: + genesis_block: + header: + version: Bedrock + parent_block: '0000000000000000000000000000000000000000000000000000000000000000' + slot: 0 + block_root: b5f8787ac23674822414c70eea15d842da38f2e806ede1a73cf7b5cf0277da07 + proof_of_leadership: + proof: '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + entropy_contribution: '0000000000000000000000000000000000000000000000000000000000000000' + leader_key: '0000000000000000000000000000000000000000000000000000000000000000' + voucher_cm: '0000000000000000000000000000000000000000000000000000000000000000' + signature: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + transactions: + - mantle_tx: + ops: - opcode: 0 payload: - inputs: [ ] + inputs: [] outputs: - - value: 1 - pk: d204000000000000000000000000000000000000000000000000000000000000 - - value: 100 - pk: 2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26 + - value: 1 + pk: d204000000000000000000000000000000000000000000000000000000000000 + - value: 100 + pk: '2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26' + - value: 1 + pk: ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717 - opcode: 17 payload: - channel_id: "0000000000000000000000000000000000000000000000000000000000000000" - inscription: [ 103, 101, 110, 101, 115, 105, 115 ] # "genesis" in bytes - parent: "0000000000000000000000000000000000000000000000000000000000000000" - signer: "0000000000000000000000000000000000000000000000000000000000000000" - execution_gas_price: 0 - storage_gas_price: 0 - ops_proofs: - - !ZkSig - pi_a: [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] - pi_b: [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] - pi_c: [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] - - NoProof + channel_id: '0000000000000000000000000000000000000000000000000000000000000000' + inscription: '67656e65736973' + parent: '0000000000000000000000000000000000000000000000000000000000000000' + signer: '0000000000000000000000000000000000000000000000000000000000000000' + execution_gas_price: 0 + storage_gas_price: 0 + ops_proofs: + - !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + - !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' time: slot_duration: '1.0' chain_start_time: PLACEHOLDER_CHAIN_START_TIME From 7aecc4fabaa88ec5dce682ff9c269368f96d91a4 Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Wed, 29 Apr 2026 13:23:13 +0200 Subject: [PATCH 07/14] cargo deny --- Cargo.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d911b127..c318ec33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,9 +674,9 @@ checksum = "4858a9d740c5007a9069007c3b4e91152d0506f13c1b31dd49051fd537656156" [[package]] name = "astral-tokio-tar" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c23f3af104b40a3430ccb90ed5f7bd877a8dc5c26fc92fde51a22b40890dcf9" +checksum = "4ce73b17c62717c4b6a9af10b43e87c578b0cac27e00666d48304d3b7d2c0693" dependencies = [ "filetime", "futures-core", @@ -2063,7 +2063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" dependencies = [ "data-encoding", - "syn 2.0.117", + "syn 1.0.109", ] [[package]] @@ -2226,7 +2226,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2557,7 +2557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3527,7 +3527,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -6331,7 +6331,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -7182,7 +7182,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.3", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -7219,7 +7219,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -8099,7 +8099,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -9070,7 +9070,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -10356,7 +10356,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] From 798b89839cb73c568718e5f16e7c8a47f4c090f5 Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Wed, 29 Apr 2026 14:05:23 +0200 Subject: [PATCH 08/14] checkpoints --- Justfile | 2 - configs/docker-all-in-one/indexer_config.json | 8 +--- indexer/core/src/block_store.rs | 21 +++++++++- indexer/core/src/config.rs | 2 +- indexer/core/src/lib.rs | 31 ++++++++++----- indexer/service/configs/indexer_config.json | 8 +--- integration_tests/src/config.rs | 2 +- sequencer/core/src/block_publisher.rs | 19 +++++++--- sequencer/core/src/block_store.rs | 37 ++++++++++++++++-- sequencer/core/src/lib.rs | 34 +++++++++++++---- sequencer/core/src/mock.rs | 6 ++- storage/src/indexer/indexer_cells.rs | 38 ++++++++++++++++++- storage/src/indexer/mod.rs | 2 + storage/src/indexer/read_once.rs | 8 +++- storage/src/indexer/write_non_atomic.rs | 5 +++ storage/src/sequencer/mod.rs | 14 ++++++- storage/src/sequencer/sequencer_cells.rs | 38 ++++++++++++++++++- 17 files changed, 225 insertions(+), 50 deletions(-) diff --git a/Justfile b/Justfile index ad943941..ac003a15 100644 --- a/Justfile +++ b/Justfile @@ -64,9 +64,7 @@ clean: @echo "๐Ÿงน Cleaning run artifacts" rm -rf sequencer/service/bedrock_signing_key rm -rf sequencer/service/rocksdb - rm -rf sequencer/service/zone_sdk_checkpoint.json rm -rf indexer/service/rocksdb - rm -rf indexer/service/zone_sdk_indexer_cursor.json rm -rf wallet/configs/debug/storage.json rm -rf rocksdb cd bedrock && docker compose down -v diff --git a/configs/docker-all-in-one/indexer_config.json b/configs/docker-all-in-one/indexer_config.json index c2b07e3e..ca99a90c 100644 --- a/configs/docker-all-in-one/indexer_config.json +++ b/configs/docker-all-in-one/indexer_config.json @@ -1,12 +1,8 @@ { "home": "./indexer/service", "consensus_info_polling_interval": "1s", - "bedrock_client_config": { - "addr": "http://logos-blockchain-node-0:18080", - "backoff": { - "start_delay": "100ms", - "max_retries": 5 - } + "bedrock_config": { + "addr": "http://logos-blockchain-node-0:18080" }, "channel_id": "0101010101010101010101010101010101010101010101010101010101010101", "initial_accounts": [ diff --git a/indexer/core/src/block_store.rs b/indexer/core/src/block_store.rs index 326a1579..71ddfd82 100644 --- a/indexer/core/src/block_store.rs +++ b/indexer/core/src/block_store.rs @@ -1,11 +1,12 @@ use std::{path::Path, sync::Arc}; -use anyhow::Result; +use anyhow::{Context as _, Result}; use common::{ block::{BedrockStatus, Block}, transaction::{NSSATransaction, clock_invocation}, }; -use logos_blockchain_core::header::HeaderId; +use logos_blockchain_core::{header::HeaderId, mantle::ops::channel::MsgId}; +use logos_blockchain_zone_sdk::Slot; use nssa::{Account, AccountId, V03State}; use nssa_core::BlockId; use storage::indexer::RocksDBIO; @@ -103,6 +104,22 @@ impl IndexerStore { Ok(self.dbio.calculate_state_for_id(block_id)?) } + pub fn get_zone_cursor(&self) -> Result> { + let Some(bytes) = self.dbio.get_zone_sdk_indexer_cursor_bytes()? else { + return Ok(None); + }; + let cursor: (MsgId, Slot) = serde_json::from_slice(&bytes) + .context("Failed to deserialize stored zone-sdk indexer cursor")?; + Ok(Some(cursor)) + } + + pub fn set_zone_cursor(&self, cursor: &(MsgId, Slot)) -> Result<()> { + let bytes = + serde_json::to_vec(cursor).context("Failed to serialize zone-sdk indexer cursor")?; + self.dbio.put_zone_sdk_indexer_cursor_bytes(&bytes)?; + Ok(()) + } + /// Recalculation of final state directly from DB. /// /// Used for indexer healthcheck. diff --git a/indexer/core/src/config.rs b/indexer/core/src/config.rs index d7f6d01e..40ac0870 100644 --- a/indexer/core/src/config.rs +++ b/indexer/core/src/config.rs @@ -28,7 +28,7 @@ pub struct IndexerConfig { pub signing_key: [u8; 32], #[serde(with = "humantime_serde")] pub consensus_info_polling_interval: Duration, - pub bedrock_client_config: ClientConfig, + pub bedrock_config: ClientConfig, pub channel_id: ChannelId, #[serde(skip_serializing_if = "Option::is_none")] pub initial_public_accounts: Option>, diff --git a/indexer/core/src/lib.rs b/indexer/core/src/lib.rs index 936343a5..63a59e91 100644 --- a/indexer/core/src/lib.rs +++ b/indexer/core/src/lib.rs @@ -5,7 +5,7 @@ use common::block::{Block, HashableBlockData}; // ToDo: Remove after testnet use common::{HashType, PINATA_BASE58}; use futures::StreamExt as _; -use log::{error, info}; +use log::{error, info, warn}; use logos_blockchain_core::header::HeaderId; use logos_blockchain_zone_sdk::{ CommonHttpClient, ZoneMessage, adapter::NodeHttpClient, indexer::ZoneIndexer, @@ -15,10 +15,6 @@ use testnet_initial_state::initial_state_testnet; use crate::{block_store::IndexerStore, config::IndexerConfig}; -// TODO: persist & restore cursor (e.g. in rocksdb) so restarts don't have to -// re-process the channel from the beginning. Mirrors the sequencer checkpoint -// TODO in `block_publisher.rs`. - pub mod block_store; pub mod config; @@ -95,10 +91,10 @@ impl IndexerCore { let home = config.home.join("rocksdb"); - let basic_auth = config.bedrock_client_config.auth.clone().map(Into::into); + let basic_auth = config.bedrock_config.auth.clone().map(Into::into); let node = NodeHttpClient::new( CommonHttpClient::new(basic_auth), - config.bedrock_client_config.addr.clone(), + config.bedrock_config.addr.clone(), ); let zone_indexer = ZoneIndexer::new(config.channel_id, node); @@ -111,12 +107,19 @@ impl IndexerCore { pub fn subscribe_parse_block_stream(&self) -> impl futures::Stream> + '_ { let poll_interval = self.config.consensus_info_polling_interval; + let initial_cursor = self + .store + .get_zone_cursor() + .expect("Failed to load zone-sdk indexer cursor"); async_stream::stream! { - // In-memory only; not persisted across restarts (see top-of-file TODO). - let mut cursor = None; + let mut cursor = initial_cursor; - info!("Starting indexer from beginning of channel"); + if cursor.is_some() { + info!("Resuming indexer from cursor {cursor:?}"); + } else { + info!("Starting indexer from beginning of channel"); + } loop { let stream = match self.zone_indexer.next_messages(cursor).await { @@ -141,7 +144,12 @@ impl IndexerCore { Ok(b) => b, Err(e) => { error!("Failed to deserialize L2 block from zone-sdk: {e}"); + // Advance past the broken inscription so we don't + // re-process it on restart. cursor = Some((zone_block.id, slot)); + if let Err(err) = self.store.set_zone_cursor(&(zone_block.id, slot)) { + warn!("Failed to persist indexer cursor: {err:#}"); + } continue; } }; @@ -156,6 +164,9 @@ impl IndexerCore { } cursor = Some((zone_block.id, slot)); + if let Err(err) = self.store.set_zone_cursor(&(zone_block.id, slot)) { + warn!("Failed to persist indexer cursor: {err:#}"); + } yield Ok(block); } diff --git a/indexer/service/configs/indexer_config.json b/indexer/service/configs/indexer_config.json index e4dd8f93..558a3bfe 100644 --- a/indexer/service/configs/indexer_config.json +++ b/indexer/service/configs/indexer_config.json @@ -1,12 +1,8 @@ { "home": ".", "consensus_info_polling_interval": "1s", - "bedrock_client_config": { - "addr": "http://localhost:8080", - "backoff": { - "start_delay": "100ms", - "max_retries": 5 - } + "bedrock_config": { + "addr": "http://localhost:8080" }, "channel_id": "0101010101010101010101010101010101010101010101010101010101010101", "initial_accounts": [ diff --git a/integration_tests/src/config.rs b/integration_tests/src/config.rs index a187ac3e..f2fbd5b3 100644 --- a/integration_tests/src/config.rs +++ b/integration_tests/src/config.rs @@ -226,7 +226,7 @@ pub fn indexer_config( Ok(IndexerConfig { home, consensus_info_polling_interval: Duration::from_secs(1), - bedrock_client_config: ClientConfig { + bedrock_config: ClientConfig { addr: addr_to_url(UrlProtocol::Http, bedrock_addr) .context("Failed to convert bedrock addr to URL")?, auth: None, diff --git a/sequencer/core/src/block_publisher.rs b/sequencer/core/src/block_publisher.rs index 766c3722..238c9553 100644 --- a/sequencer/core/src/block_publisher.rs +++ b/sequencer/core/src/block_publisher.rs @@ -4,21 +4,28 @@ use anyhow::{Context as _, Result, anyhow}; use common::block::Block; pub use logos_blockchain_core::mantle::ops::channel::MsgId; pub use logos_blockchain_key_management_system_service::keys::Ed25519Key; +pub use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint; use logos_blockchain_zone_sdk::{ CommonHttpClient, adapter::NodeHttpClient, - sequencer::{SequencerConfig as ZoneSdkSequencerConfig, SequencerHandle, ZoneSequencer}, + sequencer::{Event, SequencerConfig as ZoneSdkSequencerConfig, SequencerHandle, ZoneSequencer}, }; use tokio::task::JoinHandle; use crate::config::BedrockConfig; +/// Sink for `Event::Published` checkpoints emitted by the drive task. +/// Caller is responsible for persistence (e.g. writing to rocksdb). +pub type CheckpointSink = Box; + #[expect(async_fn_in_trait, reason = "We don't care about Send/Sync here")] pub trait BlockPublisherTrait: Clone { async fn new( config: &BedrockConfig, bedrock_signing_key: Ed25519Key, resubmit_interval: Duration, + initial_checkpoint: Option, + on_checkpoint: CheckpointSink, ) -> Result; /// Fire-and-forget publish. Zone-sdk drives the actual submission and @@ -47,6 +54,8 @@ impl BlockPublisherTrait for ZoneSdkPublisher { config: &BedrockConfig, bedrock_signing_key: Ed25519Key, resubmit_interval: Duration, + initial_checkpoint: Option, + on_checkpoint: CheckpointSink, ) -> Result { let basic_auth = config.auth.clone().map(Into::into); let node = NodeHttpClient::new(CommonHttpClient::new(basic_auth), config.node_url.clone()); @@ -56,19 +65,19 @@ impl BlockPublisherTrait for ZoneSdkPublisher { ..ZoneSdkSequencerConfig::default() }; - // TODO: persist & restore SequencerCheckpoint via Event::Published listener - // for crash recovery. Always-fresh-start for now. let (mut sequencer, mut handle) = ZoneSequencer::init_with_config( config.channel_id, bedrock_signing_key, node, zone_sdk_config, - None, + initial_checkpoint, ); let drive_task = tokio::spawn(async move { loop { - sequencer.next_event().await; + if let Some(Event::Published { checkpoint, .. }) = sequencer.next_event().await { + on_checkpoint(checkpoint); + } } }); diff --git a/sequencer/core/src/block_store.rs b/sequencer/core/src/block_store.rs index 7e47005d..38f79cf2 100644 --- a/sequencer/core/src/block_store.rs +++ b/sequencer/core/src/block_store.rs @@ -1,16 +1,17 @@ -use std::{collections::HashMap, path::Path}; +use std::{collections::HashMap, path::Path, sync::Arc}; -use anyhow::Result; +use anyhow::{Context as _, Result}; use common::{ HashType, block::{Block, BlockMeta, MantleMsgId}, transaction::NSSATransaction, }; +use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint; use nssa::V03State; use storage::{error::DbError, sequencer::RocksDBIO}; pub struct SequencerStore { - dbio: RocksDBIO, + dbio: Arc, // TODO: Consider adding the hashmap to the database for faster recovery. tx_hash_to_block_map: HashMap, genesis_id: u64, @@ -30,7 +31,11 @@ impl SequencerStore { ) -> Result { let tx_hash_to_block_map = block_to_transactions_map(genesis_block); - let dbio = RocksDBIO::open_or_create(location, genesis_block, genesis_msg_id)?; + let dbio = Arc::new(RocksDBIO::open_or_create( + location, + genesis_block, + genesis_msg_id, + )?); let genesis_id = dbio.get_meta_first_block_in_db()?; @@ -42,6 +47,14 @@ impl SequencerStore { }) } + /// Shared handle to the underlying rocksdb. Used to persist the zone-sdk + /// checkpoint from the sequencer's drive task without needing &mut to the + /// store. + #[must_use] + pub fn dbio(&self) -> Arc { + Arc::clone(&self.dbio) + } + pub fn get_block_at_id(&self, id: u64) -> Result, DbError> { self.dbio.get_block(id) } @@ -103,6 +116,22 @@ impl SequencerStore { pub fn get_nssa_state(&self) -> Option { self.dbio.get_nssa_state().ok() } + + pub fn get_zone_checkpoint(&self) -> Result> { + let Some(bytes) = self.dbio.get_zone_sdk_checkpoint_bytes()? else { + return Ok(None); + }; + let checkpoint: SequencerCheckpoint = serde_json::from_slice(&bytes) + .context("Failed to deserialize stored zone-sdk checkpoint")?; + Ok(Some(checkpoint)) + } + + pub fn set_zone_checkpoint(&self, checkpoint: &SequencerCheckpoint) -> Result<()> { + let bytes = + serde_json::to_vec(checkpoint).context("Failed to serialize zone-sdk checkpoint")?; + self.dbio.put_zone_sdk_checkpoint_bytes(&bytes)?; + Ok(()) + } } pub(crate) fn block_to_transactions_map(block: &Block) -> HashMap { diff --git a/sequencer/core/src/lib.rs b/sequencer/core/src/lib.rs index a54d9184..5d1d00a3 100644 --- a/sequencer/core/src/lib.rs +++ b/sequencer/core/src/lib.rs @@ -90,22 +90,40 @@ impl SequencerCore { .latest_block_meta() .expect("Failed to read latest block meta from store"); + let initial_checkpoint = store + .get_zone_checkpoint() + .expect("Failed to load zone-sdk checkpoint"); + let is_fresh_start = initial_checkpoint.is_none(); + + let dbio_for_checkpoint = store.dbio(); + let on_checkpoint: block_publisher::CheckpointSink = Box::new(move |cp| { + let bytes = match serde_json::to_vec(&cp) { + Ok(b) => b, + Err(err) => { + error!("Failed to serialize zone-sdk checkpoint: {err:#}"); + return; + } + }; + if let Err(err) = dbio_for_checkpoint.put_zone_sdk_checkpoint_bytes(&bytes) { + error!("Failed to persist zone-sdk checkpoint: {err:#}"); + } + }); + let block_publisher = BP::new( &config.bedrock_config, bedrock_signing_key, config.retry_pending_blocks_timeout, + initial_checkpoint, + on_checkpoint, ) .await .expect("Failed to initialize Block Publisher"); - // Publish the genesis block on every startup so the indexer can find the - // channel start. Zone-sdk dedups by msg_id, so re-publishing on restart - // is a no-op once it's already on-chain. - // TODO: persist & restore SequencerCheckpoint so restarts don't have to - // republish anything. - if latest_block_meta.id == config.genesis_id - && let Err(err) = block_publisher.publish_block(&genesis_block).await - { + // On a truly fresh start (no checkpoint persisted yet), publish the + // genesis block so the indexer can find the channel start. After the + // first publish, zone-sdk's checkpoint persistence covers further + // restarts. + if is_fresh_start && let Err(err) = block_publisher.publish_block(&genesis_block).await { error!("Failed to publish genesis block: {err:#}"); } diff --git a/sequencer/core/src/mock.rs b/sequencer/core/src/mock.rs index 9b950478..df560938 100644 --- a/sequencer/core/src/mock.rs +++ b/sequencer/core/src/mock.rs @@ -6,7 +6,9 @@ use logos_blockchain_key_management_system_service::keys::Ed25519Key; use url::Url; use crate::{ - block_publisher::BlockPublisherTrait, config::BedrockConfig, indexer_client::IndexerClientTrait, + block_publisher::{BlockPublisherTrait, CheckpointSink, SequencerCheckpoint}, + config::BedrockConfig, + indexer_client::IndexerClientTrait, }; pub type SequencerCoreWithMockClients = crate::SequencerCore; @@ -19,6 +21,8 @@ impl BlockPublisherTrait for MockBlockPublisher { _config: &BedrockConfig, _bedrock_signing_key: Ed25519Key, _resubmit_interval: Duration, + _initial_checkpoint: Option, + _on_checkpoint: CheckpointSink, ) -> Result { Ok(Self) } diff --git a/storage/src/indexer/indexer_cells.rs b/storage/src/indexer/indexer_cells.rs index 76a2c035..744117cb 100644 --- a/storage/src/indexer/indexer_cells.rs +++ b/storage/src/indexer/indexer_cells.rs @@ -8,7 +8,8 @@ use crate::{ indexer::{ ACC_NUM_CELL_NAME, BLOCK_HASH_CELL_NAME, BREAKPOINT_CELL_NAME, CF_ACC_META, CF_BREAKPOINT_NAME, CF_HASH_TO_ID, CF_TX_TO_ID, DB_META_LAST_BREAKPOINT_ID, - DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY, TX_HASH_CELL_NAME, + DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY, DB_META_ZONE_SDK_INDEXER_CURSOR_KEY, + TX_HASH_CELL_NAME, }, }; @@ -211,6 +212,41 @@ impl SimpleWritableCell for AccNumTxCell { } } +/// Opaque bytes for the zone-sdk indexer cursor `Option<(MsgId, Slot)>`. +/// The caller serializes via serde_json (neither type derives borsh). +#[derive(BorshDeserialize)] +pub struct ZoneSdkIndexerCursorCellOwned(pub Vec); + +impl SimpleStorableCell for ZoneSdkIndexerCursorCellOwned { + type KeyParams = (); + + const CELL_NAME: &'static str = DB_META_ZONE_SDK_INDEXER_CURSOR_KEY; + const CF_NAME: &'static str = CF_META_NAME; +} + +impl SimpleReadableCell for ZoneSdkIndexerCursorCellOwned {} + +#[derive(BorshSerialize)] +pub struct ZoneSdkIndexerCursorCellRef<'bytes>(pub &'bytes [u8]); + +impl SimpleStorableCell for ZoneSdkIndexerCursorCellRef<'_> { + type KeyParams = (); + + const CELL_NAME: &'static str = DB_META_ZONE_SDK_INDEXER_CURSOR_KEY; + const CF_NAME: &'static str = CF_META_NAME; +} + +impl SimpleWritableCell for ZoneSdkIndexerCursorCellRef<'_> { + fn value_constructor(&self) -> DbResult> { + borsh::to_vec(&self).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize zone-sdk indexer cursor cell".to_owned()), + ) + }) + } +} + #[cfg(test)] mod uniform_tests { use crate::{ diff --git a/storage/src/indexer/mod.rs b/storage/src/indexer/mod.rs index 7ef21258..75538835 100644 --- a/storage/src/indexer/mod.rs +++ b/storage/src/indexer/mod.rs @@ -22,6 +22,8 @@ pub const DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY: &str = "last_observed_l1_lib_header_in_db"; /// Key base for storing metainformation about the last breakpoint. pub const DB_META_LAST_BREAKPOINT_ID: &str = "last_breakpoint_id"; +/// Key base for storing the zone-sdk indexer cursor (opaque bytes). +pub const DB_META_ZONE_SDK_INDEXER_CURSOR_KEY: &str = "zone_sdk_indexer_cursor"; /// Cell name for a breakpoint. pub const BREAKPOINT_CELL_NAME: &str = "breakpoint"; diff --git a/storage/src/indexer/read_once.rs b/storage/src/indexer/read_once.rs index b1ae0ada..8ab7fd23 100644 --- a/storage/src/indexer/read_once.rs +++ b/storage/src/indexer/read_once.rs @@ -4,7 +4,7 @@ use crate::{ cells::shared_cells::{BlockCell, FirstBlockCell, FirstBlockSetCell, LastBlockCell}, indexer::indexer_cells::{ AccNumTxCell, BlockHashToBlockIdMapCell, BreakpointCellOwned, LastBreakpointIdCell, - LastObservedL1LibHeaderCell, TxHashToBlockIdMapCell, + LastObservedL1LibHeaderCell, TxHashToBlockIdMapCell, ZoneSdkIndexerCursorCellOwned, }, }; @@ -64,4 +64,10 @@ impl RocksDBIO { self.get_opt::(acc_id) .map(|opt| opt.map(|cell| cell.0)) } + + pub fn get_zone_sdk_indexer_cursor_bytes(&self) -> DbResult>> { + Ok(self + .get_opt::(())? + .map(|cell| cell.0)) + } } diff --git a/storage/src/indexer/write_non_atomic.rs b/storage/src/indexer/write_non_atomic.rs index 62b466a2..505360fa 100644 --- a/storage/src/indexer/write_non_atomic.rs +++ b/storage/src/indexer/write_non_atomic.rs @@ -4,6 +4,7 @@ use crate::{ cells::shared_cells::{FirstBlockSetCell, LastBlockCell}, indexer::indexer_cells::{ BreakpointCellRef, LastBreakpointIdCell, LastObservedL1LibHeaderCell, + ZoneSdkIndexerCursorCellRef, }, }; @@ -30,6 +31,10 @@ impl RocksDBIO { self.put(&FirstBlockSetCell(true), ()) } + pub fn put_zone_sdk_indexer_cursor_bytes(&self, bytes: &[u8]) -> DbResult<()> { + self.put(&ZoneSdkIndexerCursorCellRef(bytes), ()) + } + // State pub fn put_breakpoint(&self, br_id: u64, breakpoint: &V03State) -> DbResult<()> { diff --git a/storage/src/sequencer/mod.rs b/storage/src/sequencer/mod.rs index 508f6c29..71a80796 100644 --- a/storage/src/sequencer/mod.rs +++ b/storage/src/sequencer/mod.rs @@ -12,7 +12,7 @@ use crate::{ error::DbError, sequencer::sequencer_cells::{ LastFinalizedBlockIdCell, LatestBlockMetaCellOwned, LatestBlockMetaCellRef, - NSSAStateCellOwned, NSSAStateCellRef, + NSSAStateCellOwned, NSSAStateCellRef, ZoneSdkCheckpointCellOwned, ZoneSdkCheckpointCellRef, }, }; @@ -22,6 +22,8 @@ pub mod sequencer_cells; pub const DB_META_LAST_FINALIZED_BLOCK_ID: &str = "last_finalized_block_id"; /// Key base for storing metainformation about the latest block meta. pub const DB_META_LATEST_BLOCK_META_KEY: &str = "latest_block_meta"; +/// Key base for storing the zone-sdk sequencer checkpoint (opaque bytes). +pub const DB_META_ZONE_SDK_CHECKPOINT_KEY: &str = "zone_sdk_checkpoint"; /// Key base for storing the NSSA state. pub const DB_NSSA_STATE_KEY: &str = "nssa_state"; @@ -205,6 +207,16 @@ impl RocksDBIO { self.get::(()).map(|val| val.0) } + pub fn get_zone_sdk_checkpoint_bytes(&self) -> DbResult>> { + Ok(self + .get_opt::(())? + .map(|cell| cell.0)) + } + + pub fn put_zone_sdk_checkpoint_bytes(&self, bytes: &[u8]) -> DbResult<()> { + self.put(&ZoneSdkCheckpointCellRef(bytes), ()) + } + pub fn put_block( &self, block: &Block, diff --git a/storage/src/sequencer/sequencer_cells.rs b/storage/src/sequencer/sequencer_cells.rs index 0ad092d7..57f487ce 100644 --- a/storage/src/sequencer/sequencer_cells.rs +++ b/storage/src/sequencer/sequencer_cells.rs @@ -8,7 +8,7 @@ use crate::{ error::DbError, sequencer::{ CF_NSSA_STATE_NAME, DB_META_LAST_FINALIZED_BLOCK_ID, DB_META_LATEST_BLOCK_META_KEY, - DB_NSSA_STATE_KEY, + DB_META_ZONE_SDK_CHECKPOINT_KEY, DB_NSSA_STATE_KEY, }, }; @@ -95,6 +95,42 @@ impl SimpleWritableCell for LatestBlockMetaCellRef<'_> { } } +/// Opaque bytes for the zone-sdk sequencer checkpoint. The caller is +/// responsible for the actual encoding (we use serde_json since +/// `SequencerCheckpoint` only derives serde, not borsh). +#[derive(BorshDeserialize)] +pub struct ZoneSdkCheckpointCellOwned(pub Vec); + +impl SimpleStorableCell for ZoneSdkCheckpointCellOwned { + type KeyParams = (); + + const CELL_NAME: &'static str = DB_META_ZONE_SDK_CHECKPOINT_KEY; + const CF_NAME: &'static str = CF_META_NAME; +} + +impl SimpleReadableCell for ZoneSdkCheckpointCellOwned {} + +#[derive(BorshSerialize)] +pub struct ZoneSdkCheckpointCellRef<'bytes>(pub &'bytes [u8]); + +impl SimpleStorableCell for ZoneSdkCheckpointCellRef<'_> { + type KeyParams = (); + + const CELL_NAME: &'static str = DB_META_ZONE_SDK_CHECKPOINT_KEY; + const CF_NAME: &'static str = CF_META_NAME; +} + +impl SimpleWritableCell for ZoneSdkCheckpointCellRef<'_> { + fn value_constructor(&self) -> DbResult> { + borsh::to_vec(&self).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize zone-sdk checkpoint cell".to_owned()), + ) + }) + } +} + #[cfg(test)] mod uniform_tests { use crate::{ From fede4977b791e7c4668029b67c84eb3766247ed9 Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Wed, 29 Apr 2026 14:38:20 +0200 Subject: [PATCH 09/14] clippy --- storage/src/indexer/indexer_cells.rs | 2 +- storage/src/sequencer/sequencer_cells.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/src/indexer/indexer_cells.rs b/storage/src/indexer/indexer_cells.rs index 744117cb..615902bd 100644 --- a/storage/src/indexer/indexer_cells.rs +++ b/storage/src/indexer/indexer_cells.rs @@ -213,7 +213,7 @@ impl SimpleWritableCell for AccNumTxCell { } /// Opaque bytes for the zone-sdk indexer cursor `Option<(MsgId, Slot)>`. -/// The caller serializes via serde_json (neither type derives borsh). +/// The caller serializes via `serde_json` (neither type derives borsh). #[derive(BorshDeserialize)] pub struct ZoneSdkIndexerCursorCellOwned(pub Vec); diff --git a/storage/src/sequencer/sequencer_cells.rs b/storage/src/sequencer/sequencer_cells.rs index 57f487ce..2bf65367 100644 --- a/storage/src/sequencer/sequencer_cells.rs +++ b/storage/src/sequencer/sequencer_cells.rs @@ -96,7 +96,7 @@ impl SimpleWritableCell for LatestBlockMetaCellRef<'_> { } /// Opaque bytes for the zone-sdk sequencer checkpoint. The caller is -/// responsible for the actual encoding (we use serde_json since +/// responsible for the actual encoding (we use `serde_json` since /// `SequencerCheckpoint` only derives serde, not borsh). #[derive(BorshDeserialize)] pub struct ZoneSdkCheckpointCellOwned(pub Vec); From 9f6468682a1d8d0af23bc5dd92a32ce9c28dc748 Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Wed, 29 Apr 2026 15:26:11 +0200 Subject: [PATCH 10/14] clippy --- sequencer/core/src/block_store.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sequencer/core/src/block_store.rs b/sequencer/core/src/block_store.rs index 38f79cf2..e85b5d33 100644 --- a/sequencer/core/src/block_store.rs +++ b/sequencer/core/src/block_store.rs @@ -68,6 +68,7 @@ impl SequencerStore { } /// Returns the transaction corresponding to the given hash, if it exists in the blockchain. + #[must_use] pub fn get_transaction_by_hash(&self, hash: HashType) -> Option { let block_id = *self.tx_hash_to_block_map.get(&hash)?; let block = self @@ -89,10 +90,12 @@ impl SequencerStore { Ok(self.dbio.latest_block_meta()?) } + #[must_use] pub const fn genesis_id(&self) -> u64 { self.genesis_id } + #[must_use] pub const fn signing_key(&self) -> &nssa::PrivateKey { &self.signing_key } @@ -113,6 +116,7 @@ impl SequencerStore { Ok(()) } + #[must_use] pub fn get_nssa_state(&self) -> Option { self.dbio.get_nssa_state().ok() } From 265a269da058929c25ae36289955136eed12f12c Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Thu, 30 Apr 2026 11:13:50 +0200 Subject: [PATCH 11/14] remove indexer dep from sequencer: --- integration_tests/src/config.rs | 3 - integration_tests/src/lib.rs | 12 ++-- integration_tests/src/setup.rs | 2 - integration_tests/src/test_context_ffi.rs | 2 - sequencer/core/src/block_publisher.rs | 40 ++++++++++++- sequencer/core/src/config.rs | 2 - sequencer/core/src/lib.rs | 59 ++++++++----------- sequencer/core/src/mock.rs | 18 ++---- sequencer/service/src/lib.rs | 72 +---------------------- sequencer/service/src/service.rs | 17 +++--- storage/src/sequencer/mod.rs | 16 +++++ 11 files changed, 99 insertions(+), 144 deletions(-) diff --git a/integration_tests/src/config.rs b/integration_tests/src/config.rs index f2fbd5b3..733a9169 100644 --- a/integration_tests/src/config.rs +++ b/integration_tests/src/config.rs @@ -169,7 +169,6 @@ pub fn sequencer_config( partial: SequencerPartialConfig, home: PathBuf, bedrock_addr: SocketAddr, - indexer_addr: SocketAddr, initial_data: &InitialData, ) -> Result { let SequencerPartialConfig { @@ -197,8 +196,6 @@ pub fn sequencer_config( .context("Failed to convert bedrock addr to URL")?, auth: None, }, - indexer_rpc_url: addr_to_url(UrlProtocol::Ws, indexer_addr) - .context("Failed to convert indexer addr to URL")?, }) } diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index fcae2c71..47324e53 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -77,14 +77,10 @@ impl TestContext { .await .context("Failed to setup Indexer")?; - let (sequencer_handle, temp_sequencer_dir) = setup_sequencer( - sequencer_partial_config, - bedrock_addr, - indexer_handle.addr(), - &initial_data, - ) - .await - .context("Failed to setup Sequencer")?; + let (sequencer_handle, temp_sequencer_dir) = + setup_sequencer(sequencer_partial_config, bedrock_addr, &initial_data) + .await + .context("Failed to setup Sequencer")?; let (wallet, temp_wallet_dir, wallet_password) = setup_wallet(sequencer_handle.addr(), &initial_data) diff --git a/integration_tests/src/setup.rs b/integration_tests/src/setup.rs index 58b33c60..774c67e3 100644 --- a/integration_tests/src/setup.rs +++ b/integration_tests/src/setup.rs @@ -119,7 +119,6 @@ pub(crate) async fn setup_indexer( pub(crate) async fn setup_sequencer( partial: config::SequencerPartialConfig, bedrock_addr: SocketAddr, - indexer_addr: SocketAddr, initial_data: &config::InitialData, ) -> Result<(SequencerHandle, TempDir)> { let temp_sequencer_dir = @@ -134,7 +133,6 @@ pub(crate) async fn setup_sequencer( partial, temp_sequencer_dir.path().to_owned(), bedrock_addr, - indexer_addr, initial_data, ) .context("Failed to create Sequencer config")?; diff --git a/integration_tests/src/test_context_ffi.rs b/integration_tests/src/test_context_ffi.rs index 7d21aa28..77e5f0f2 100644 --- a/integration_tests/src/test_context_ffi.rs +++ b/integration_tests/src/test_context_ffi.rs @@ -85,8 +85,6 @@ impl TestContextFFI { .block_on(setup_sequencer( sequencer_partial_config, bedrock_addr, - // SAFETY: addr is valid if indexer_ffi is valid. - unsafe { indexer_ffi.addr() }, initial_data, )) .context("Failed to setup Sequencer")?; diff --git a/sequencer/core/src/block_publisher.rs b/sequencer/core/src/block_publisher.rs index 238c9553..9f4c8235 100644 --- a/sequencer/core/src/block_publisher.rs +++ b/sequencer/core/src/block_publisher.rs @@ -2,6 +2,7 @@ use std::{sync::Arc, time::Duration}; use anyhow::{Context as _, Result, anyhow}; use common::block::Block; +use log::warn; pub use logos_blockchain_core::mantle::ops::channel::MsgId; pub use logos_blockchain_key_management_system_service::keys::Ed25519Key; pub use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint; @@ -9,6 +10,7 @@ use logos_blockchain_zone_sdk::{ CommonHttpClient, adapter::NodeHttpClient, sequencer::{Event, SequencerConfig as ZoneSdkSequencerConfig, SequencerHandle, ZoneSequencer}, + state::InscriptionInfo, }; use tokio::task::JoinHandle; @@ -18,6 +20,11 @@ use crate::config::BedrockConfig; /// Caller is responsible for persistence (e.g. writing to rocksdb). pub type CheckpointSink = Box; +/// Sink for finalized L2 block ids derived from `Event::TxsFinalized` and +/// `Event::FinalizedInscriptions`. Caller is responsible for cleanup +/// (e.g. marking pending blocks as finalized in storage). +pub type FinalizedBlockSink = Box; + #[expect(async_fn_in_trait, reason = "We don't care about Send/Sync here")] pub trait BlockPublisherTrait: Clone { async fn new( @@ -26,6 +33,7 @@ pub trait BlockPublisherTrait: Clone { resubmit_interval: Duration, initial_checkpoint: Option, on_checkpoint: CheckpointSink, + on_finalized_block: FinalizedBlockSink, ) -> Result; /// Fire-and-forget publish. Zone-sdk drives the actual submission and @@ -56,6 +64,7 @@ impl BlockPublisherTrait for ZoneSdkPublisher { resubmit_interval: Duration, initial_checkpoint: Option, on_checkpoint: CheckpointSink, + on_finalized_block: FinalizedBlockSink, ) -> Result { let basic_auth = config.auth.clone().map(Into::into); let node = NodeHttpClient::new(CommonHttpClient::new(basic_auth), config.node_url.clone()); @@ -75,8 +84,18 @@ impl BlockPublisherTrait for ZoneSdkPublisher { let drive_task = tokio::spawn(async move { loop { - if let Some(Event::Published { checkpoint, .. }) = sequencer.next_event().await { - on_checkpoint(checkpoint); + let Some(event) = sequencer.next_event().await else { + continue; + }; + match event { + Event::Published { checkpoint, .. } => on_checkpoint(checkpoint), + Event::TxsFinalized { inscriptions, .. } + | Event::FinalizedInscriptions { inscriptions } => { + if let Some(max_block_id) = max_block_id_from_inscriptions(&inscriptions) { + on_finalized_block(max_block_id); + } + } + Event::ChannelUpdate { .. } | Event::Ready => {} } } }); @@ -98,3 +117,20 @@ impl BlockPublisherTrait for ZoneSdkPublisher { Ok(()) } } + +/// Deserialize each inscription payload as a `Block` and return the highest +/// `block_id`. Bad payloads are logged and skipped. +fn max_block_id_from_inscriptions(inscriptions: &[InscriptionInfo]) -> Option { + inscriptions + .iter() + .filter_map( + |inscription| match borsh::from_slice::(&inscription.payload) { + Ok(block) => Some(block.header.block_id), + Err(err) => { + warn!("Failed to deserialize finalized inscription as Block: {err:#}"); + None + } + }, + ) + .max() +} diff --git a/sequencer/core/src/config.rs b/sequencer/core/src/config.rs index fad2fef3..b33dd694 100644 --- a/sequencer/core/src/config.rs +++ b/sequencer/core/src/config.rs @@ -41,8 +41,6 @@ pub struct SequencerConfig { pub signing_key: [u8; 32], /// Bedrock configuration options. pub bedrock_config: BedrockConfig, - /// Indexer RPC URL. - pub indexer_rpc_url: Url, #[serde(skip_serializing_if = "Option::is_none")] pub initial_public_accounts: Option>, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/sequencer/core/src/lib.rs b/sequencer/core/src/lib.rs index 5d1d00a3..e95bd139 100644 --- a/sequencer/core/src/lib.rs +++ b/sequencer/core/src/lib.rs @@ -21,31 +21,29 @@ use testnet_initial_state::initial_state; use crate::{ block_publisher::{BlockPublisherTrait, ZoneSdkPublisher}, block_store::SequencerStore, - indexer_client::{IndexerClient, IndexerClientTrait}, }; pub mod block_publisher; pub mod block_store; pub mod config; +// Kept as a thin client lib for callers that want to query the indexer +// directly (e.g. integration tests). The sequencer no longer depends on the +// indexer at runtime โ€” finalization comes from zone-sdk events. pub mod indexer_client; #[cfg(feature = "mock")] pub mod mock; -pub struct SequencerCore< - BP: BlockPublisherTrait = ZoneSdkPublisher, - IC: IndexerClientTrait = IndexerClient, -> { +pub struct SequencerCore { state: nssa::V03State, store: SequencerStore, mempool: MemPool, sequencer_config: SequencerConfig, chain_height: u64, block_publisher: BP, - indexer_client: IC, } -impl SequencerCore { +impl SequencerCore { /// Starts the sequencer using the provided configuration. /// If an existing database is found, the sequencer state is loaded from it and /// assumed to represent the correct latest state consistent with Bedrock-finalized data. @@ -69,10 +67,6 @@ impl SequencerCore { load_or_create_signing_key(&config.home.join("bedrock_signing_key")) .expect("Failed to load or create bedrock signing key"); - let indexer_client = IC::new(&config.indexer_rpc_url) - .await - .expect("Failed to create Indexer Client"); - // TODO: Remove msg_id from BlockMeta โ€” it is no longer needed now that // zone-sdk manages L1 settlement state via its own checkpoint. let genesis_msg_id = [0_u8; 32]; @@ -109,12 +103,20 @@ impl SequencerCore { } }); + let dbio_for_finalized = store.dbio(); + let on_finalized_block: block_publisher::FinalizedBlockSink = Box::new(move |block_id| { + if let Err(err) = dbio_for_finalized.clean_pending_blocks_up_to(block_id) { + error!("Failed to mark pending blocks finalized up to {block_id}: {err:#}"); + } + }); + let block_publisher = BP::new( &config.bedrock_config, bedrock_signing_key, config.retry_pending_blocks_timeout, initial_checkpoint, on_checkpoint, + on_finalized_block, ) .await .expect("Failed to initialize Block Publisher"); @@ -192,7 +194,6 @@ impl SequencerCore { chain_height: latest_block_meta.id, sequencer_config: config, block_publisher, - indexer_client, }; (sequencer_core, mempool_handle) @@ -341,22 +342,19 @@ impl SequencerCore { &self.sequencer_config } - /// Deletes finalized blocks from the sequencer's pending block list. - /// This method must be called when new blocks are finalized on Bedrock. - /// All pending blocks with an ID less than or equal to `last_finalized_block_id` - /// are removed from the database. - pub fn clean_finalized_blocks_from_db(&mut self, last_finalized_block_id: u64) -> Result<()> { - self.get_pending_blocks()? - .iter() - .map(|block| block.header.block_id) - .min() - .map_or(Ok(()), |first_pending_block_id| { - info!("Clearing pending blocks up to id: {last_finalized_block_id}"); - // TODO: Delete blocks instead of marking them as finalized. - // Current approach is used because we still have `GetBlockDataRequest`. - (first_pending_block_id..=last_finalized_block_id) - .try_for_each(|id| self.store.mark_block_as_finalized(id)) - }) + /// Marks all pending blocks with `block_id <= last_finalized_block_id` as + /// finalized. Idempotent. Production callers don't invoke this directly โ€” + /// it's wired up in `start_from_config` to the publisher's + /// `on_finalized_block` sink, which fires on `Event::TxsFinalized` / + /// `Event::FinalizedInscriptions`. Kept on the type for tests. + // TODO: Delete blocks instead of marking them as finalized. Current + // approach is used because we still have `GetBlockDataRequest`. + pub fn clean_finalized_blocks_from_db(&self, last_finalized_block_id: u64) -> Result<()> { + info!("Clearing pending blocks up to id: {last_finalized_block_id}"); + self.store + .dbio() + .clean_pending_blocks_up_to(last_finalized_block_id)?; + Ok(()) } /// Returns the list of stored pending blocks. @@ -374,10 +372,6 @@ impl SequencerCore { self.block_publisher.clone() } - pub fn indexer_client(&self) -> IC { - self.indexer_client.clone() - } - fn next_block_id(&self) -> u64 { self.chain_height .checked_add(1) @@ -446,7 +440,6 @@ mod tests { auth: None, }, retry_pending_blocks_timeout: Duration::from_mins(4), - indexer_rpc_url: "ws://localhost:8779".parse().unwrap(), initial_public_accounts: None, initial_private_accounts: None, } diff --git a/sequencer/core/src/mock.rs b/sequencer/core/src/mock.rs index df560938..ebe6ea5d 100644 --- a/sequencer/core/src/mock.rs +++ b/sequencer/core/src/mock.rs @@ -3,15 +3,15 @@ use std::time::Duration; use anyhow::Result; use common::block::Block; use logos_blockchain_key_management_system_service::keys::Ed25519Key; -use url::Url; use crate::{ - block_publisher::{BlockPublisherTrait, CheckpointSink, SequencerCheckpoint}, + block_publisher::{ + BlockPublisherTrait, CheckpointSink, FinalizedBlockSink, SequencerCheckpoint, + }, config::BedrockConfig, - indexer_client::IndexerClientTrait, }; -pub type SequencerCoreWithMockClients = crate::SequencerCore; +pub type SequencerCoreWithMockClients = crate::SequencerCore; #[derive(Clone)] pub struct MockBlockPublisher; @@ -23,6 +23,7 @@ impl BlockPublisherTrait for MockBlockPublisher { _resubmit_interval: Duration, _initial_checkpoint: Option, _on_checkpoint: CheckpointSink, + _on_finalized_block: FinalizedBlockSink, ) -> Result { Ok(Self) } @@ -31,12 +32,3 @@ impl BlockPublisherTrait for MockBlockPublisher { Ok(()) } } - -#[derive(Copy, Clone)] -pub struct MockIndexerClient; - -impl IndexerClientTrait for MockIndexerClient { - async fn new(_indexer_url: &Url) -> Result { - Ok(Self) - } -} diff --git a/sequencer/service/src/lib.rs b/sequencer/service/src/lib.rs index 7c6de793..319b75ad 100644 --- a/sequencer/service/src/lib.rs +++ b/sequencer/service/src/lib.rs @@ -5,8 +5,6 @@ use bytesize::ByteSize; use common::transaction::NSSATransaction; use futures::never::Never; use jsonrpsee::server::ServerHandle; -#[cfg(not(feature = "standalone"))] -use log::warn; use log::{error, info}; use mempool::MemPoolHandle; #[cfg(not(feature = "standalone"))] @@ -29,7 +27,6 @@ pub struct SequencerHandle { /// Option because of `Drop` which forbids to simply move out of `self` in `stopped()`. server_handle: Option, main_loop_handle: JoinHandle>, - listen_for_bedrock_blocks_loop_handle: JoinHandle>, } impl SequencerHandle { @@ -37,13 +34,11 @@ impl SequencerHandle { addr: SocketAddr, server_handle: ServerHandle, main_loop_handle: JoinHandle>, - listen_for_bedrock_blocks_loop_handle: JoinHandle>, ) -> Self { Self { addr, server_handle: Some(server_handle), main_loop_handle, - listen_for_bedrock_blocks_loop_handle, } } @@ -57,7 +52,6 @@ impl SequencerHandle { addr: _, server_handle, main_loop_handle, - listen_for_bedrock_blocks_loop_handle, } = &mut self; let server_handle = server_handle.take().expect("Server handle is set"); @@ -71,11 +65,6 @@ impl SequencerHandle { .context("Main loop task panicked")? .context("Main loop exited unexpectedly") } - res = listen_for_bedrock_blocks_loop_handle => { - res - .context("Listen for bedrock blocks loop task panicked")? - .context("Listen for bedrock blocks loop exited unexpectedly") - } } } @@ -89,12 +78,10 @@ impl SequencerHandle { addr: _, server_handle, main_loop_handle, - listen_for_bedrock_blocks_loop_handle, } = self; let stopped = server_handle.as_ref().is_none_or(ServerHandle::is_stopped) - || main_loop_handle.is_finished() - || listen_for_bedrock_blocks_loop_handle.is_finished(); + || main_loop_handle.is_finished(); !stopped } @@ -110,11 +97,9 @@ impl Drop for SequencerHandle { addr: _, server_handle, main_loop_handle, - listen_for_bedrock_blocks_loop_handle, } = self; main_loop_handle.abort(); - listen_for_bedrock_blocks_loop_handle.abort(); let Some(handle) = server_handle else { return; @@ -146,18 +131,9 @@ pub async fn run(config: SequencerConfig, port: u16) -> Result info!("RPC server started"); info!("Starting main sequencer loop"); - let main_loop_handle = tokio::spawn(main_loop(Arc::clone(&seq_core_wrapped), block_timeout)); + let main_loop_handle = tokio::spawn(main_loop(seq_core_wrapped, block_timeout)); - info!("Starting bedrock block listening loop"); - let listen_for_bedrock_blocks_loop_handle = - tokio::spawn(listen_for_bedrock_blocks_loop(seq_core_wrapped)); - - Ok(SequencerHandle::new( - addr, - server_handle, - main_loop_handle, - listen_for_bedrock_blocks_loop_handle, - )) + Ok(SequencerHandle::new(addr, server_handle, main_loop_handle)) } async fn run_server( @@ -206,45 +182,3 @@ async fn main_loop(seq_core: Arc>, block_timeout: Duration) info!("Waiting for new transactions"); } } - -#[cfg(not(feature = "standalone"))] -async fn listen_for_bedrock_blocks_loop(seq_core: Arc>) -> Result { - use indexer_service_rpc::RpcClient as _; - - let indexer_client = seq_core.lock().await.indexer_client(); - - let retry_delay = Duration::from_secs(5); - - loop { - // TODO: Subscribe from the first pending block ID? - let mut subscription = indexer_client - .subscribe_to_finalized_blocks() - .await - .context("Failed to subscribe to finalized blocks")?; - - while let Some(block_id) = subscription.next().await { - let block_id = block_id.context("Failed to get next block from subscription")?; - - info!("Received new L2 block with ID {block_id}"); - - seq_core - .lock() - .await - .clean_finalized_blocks_from_db(block_id) - .with_context(|| { - format!("Failed to clean finalized blocks from DB for block ID {block_id}") - })?; - } - - warn!( - "Block subscription closed unexpectedly, reason: {:?}, retrying after {retry_delay:?}", - subscription.close_reason() - ); - tokio::time::sleep(retry_delay).await; - } -} - -#[cfg(feature = "standalone")] -async fn listen_for_bedrock_blocks_loop(_seq_core: Arc>) -> Result { - std::future::pending::>().await -} diff --git a/sequencer/service/src/service.rs b/sequencer/service/src/service.rs index de70c72c..0bb8e1dd 100644 --- a/sequencer/service/src/service.rs +++ b/sequencer/service/src/service.rs @@ -8,10 +8,7 @@ use jsonrpsee::{ use log::warn; use mempool::MemPoolHandle; use nssa::{self, program::Program}; -use sequencer_core::{ - DbError, SequencerCore, block_publisher::BlockPublisherTrait, - indexer_client::IndexerClientTrait, -}; +use sequencer_core::{DbError, SequencerCore, block_publisher::BlockPublisherTrait}; use sequencer_service_protocol::{ Account, AccountId, Block, BlockId, Commitment, HashType, MembershipProof, Nonce, ProgramId, }; @@ -19,15 +16,15 @@ use tokio::sync::Mutex; const NOT_FOUND_ERROR_CODE: i32 = -31999; -pub struct SequencerService { - sequencer: Arc>>, +pub struct SequencerService { + sequencer: Arc>>, mempool_handle: MemPoolHandle, max_block_size: u64, } -impl SequencerService { +impl SequencerService { pub const fn new( - sequencer: Arc>>, + sequencer: Arc>>, mempool_handle: MemPoolHandle, max_block_size: u64, ) -> Self { @@ -40,8 +37,8 @@ impl SequencerService { } #[async_trait] -impl - sequencer_service_rpc::RpcServer for SequencerService +impl sequencer_service_rpc::RpcServer + for SequencerService { async fn send_transaction(&self, tx: NSSATransaction) -> Result { // Reserve ~200 bytes for block header overhead diff --git a/storage/src/sequencer/mod.rs b/storage/src/sequencer/mod.rs index 71a80796..537d198d 100644 --- a/storage/src/sequencer/mod.rs +++ b/storage/src/sequencer/mod.rs @@ -287,6 +287,22 @@ impl RocksDBIO { Ok(()) } + /// Mark every pending block with `block_id <= last_finalized` as finalized. + /// Idempotent โ€” already-finalized blocks are skipped. + pub fn clean_pending_blocks_up_to(&self, last_finalized: u64) -> DbResult<()> { + let pending_ids: Vec = self + .get_all_blocks() + .filter_map(Result::ok) + .filter(|b| matches!(b.bedrock_status, BedrockStatus::Pending)) + .map(|b| b.header.block_id) + .filter(|id| *id <= last_finalized) + .collect(); + for id in pending_ids { + self.mark_block_as_finalized(id)?; + } + Ok(()) + } + pub fn mark_block_as_finalized(&self, block_id: u64) -> DbResult<()> { let mut block = self.get_block(block_id)?.ok_or_else(|| { DbError::db_interaction_error(format!("Block with id {block_id} not found")) From af3a31509e432cbda5a4c08fd035de8bf8fda5a7 Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Thu, 30 Apr 2026 11:29:49 +0200 Subject: [PATCH 12/14] machete --- Cargo.lock | 1 - integration_tests/Cargo.toml | 2 +- sequencer/service/Cargo.toml | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46b4e531..57a98511 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8402,7 +8402,6 @@ dependencies = [ "common", "env_logger", "futures", - "indexer_service_rpc", "jsonrpsee", "log", "mempool", diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index 53f0ee98..02b8e865 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -19,7 +19,7 @@ indexer_service.workspace = true serde_json.workspace = true token_core.workspace = true ata_core.workspace = true -indexer_service_rpc.workspace = true +indexer_service_rpc = { workspace = true, features = ["client"] } sequencer_service_rpc = { workspace = true, features = ["client"] } wallet-ffi.workspace = true indexer_ffi.workspace = true diff --git a/sequencer/service/Cargo.toml b/sequencer/service/Cargo.toml index 6fee808c..beed6be2 100644 --- a/sequencer/service/Cargo.toml +++ b/sequencer/service/Cargo.toml @@ -14,7 +14,6 @@ mempool.workspace = true sequencer_core = { workspace = true, features = ["testnet"] } sequencer_service_protocol.workspace = true sequencer_service_rpc = { workspace = true, features = ["server"] } -indexer_service_rpc = { workspace = true, features = ["client"] } clap = { workspace = true, features = ["derive", "env"] } anyhow.workspace = true From c263a98231c62333d69438bbfe1791fd4af4bdf6 Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Fri, 1 May 2026 09:12:24 +0200 Subject: [PATCH 13/14] address comments --- Cargo.lock | 2 +- integration_tests/Cargo.toml | 1 + integration_tests/src/lib.rs | 7 +++-- integration_tests/src/test_context_ffi.rs | 2 +- sequencer/core/Cargo.toml | 1 - sequencer/core/src/indexer_client.rs | 34 ----------------------- sequencer/core/src/lib.rs | 4 --- 7 files changed, 8 insertions(+), 43 deletions(-) delete mode 100644 sequencer/core/src/indexer_client.rs diff --git a/Cargo.lock b/Cargo.lock index 57a98511..78cd05ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3915,6 +3915,7 @@ dependencies = [ "indexer_ffi", "indexer_service", "indexer_service_rpc", + "jsonrpsee", "key_protocol", "log", "nssa", @@ -8372,7 +8373,6 @@ dependencies = [ "common", "futures", "humantime-serde", - "jsonrpsee", "log", "logos-blockchain-core", "logos-blockchain-key-management-system-service", diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index 02b8e865..a99a82aa 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -21,6 +21,7 @@ token_core.workspace = true ata_core.workspace = true indexer_service_rpc = { workspace = true, features = ["client"] } sequencer_service_rpc = { workspace = true, features = ["client"] } +jsonrpsee = { workspace = true, features = ["ws-client"] } wallet-ffi.workspace = true indexer_ffi.workspace = true testnet_initial_state.workspace = true diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 47324e53..2a9e7c67 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -9,16 +9,19 @@ use indexer_service::IndexerHandle; use log::{debug, error}; use nssa::{AccountId, PrivacyPreservingTransaction}; use nssa_core::Commitment; -use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait as _}; use sequencer_service::SequencerHandle; use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder}; use tempfile::TempDir; use testcontainers::compose::DockerCompose; use wallet::WalletCore; -use crate::setup::{setup_bedrock_node, setup_indexer, setup_sequencer, setup_wallet}; +use crate::{ + indexer_client::IndexerClient, + setup::{setup_bedrock_node, setup_indexer, setup_sequencer, setup_wallet}, +}; pub mod config; +pub mod indexer_client; pub mod setup; pub mod test_context_ffi; diff --git a/integration_tests/src/test_context_ffi.rs b/integration_tests/src/test_context_ffi.rs index 77e5f0f2..b29ace7c 100644 --- a/integration_tests/src/test_context_ffi.rs +++ b/integration_tests/src/test_context_ffi.rs @@ -6,7 +6,6 @@ use indexer_ffi::IndexerServiceFFI; use indexer_service_rpc::RpcClient as _; use log::{debug, error}; use nssa::AccountId; -use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait as _}; use sequencer_service::SequencerHandle; use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder}; use tempfile::TempDir; @@ -15,6 +14,7 @@ use wallet::WalletCore; use crate::{ BEDROCK_SERVICE_WITH_OPEN_PORT, LOGGER, TestContextBuilder, config, + indexer_client::IndexerClient, setup::{setup_bedrock_node, setup_indexer_ffi, setup_sequencer, setup_wallet}, }; diff --git a/sequencer/core/Cargo.toml b/sequencer/core/Cargo.toml index 93be6df3..827c8b2e 100644 --- a/sequencer/core/Cargo.toml +++ b/sequencer/core/Cargo.toml @@ -30,7 +30,6 @@ rand.workspace = true borsh.workspace = true bytesize.workspace = true url.workspace = true -jsonrpsee = { workspace = true, features = ["ws-client"] } [features] default = [] diff --git a/sequencer/core/src/indexer_client.rs b/sequencer/core/src/indexer_client.rs deleted file mode 100644 index 960b77a4..00000000 --- a/sequencer/core/src/indexer_client.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -use anyhow::{Context as _, Result}; -use log::info; -pub use url::Url; - -#[expect(async_fn_in_trait, reason = "We don't care about Send/Sync here")] -pub trait IndexerClientTrait: Clone { - async fn new(indexer_url: &Url) -> Result; -} - -#[derive(Clone)] -pub struct IndexerClient(Arc); - -impl IndexerClientTrait for IndexerClient { - async fn new(indexer_url: &Url) -> Result { - info!("Connecting to Indexer at {indexer_url}"); - - let client = jsonrpsee::ws_client::WsClientBuilder::default() - .build(indexer_url) - .await - .context("Failed to create websocket client")?; - - Ok(Self(Arc::new(client))) - } -} - -impl Deref for IndexerClient { - type Target = jsonrpsee::ws_client::WsClient; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/sequencer/core/src/lib.rs b/sequencer/core/src/lib.rs index 03eec509..00450e06 100644 --- a/sequencer/core/src/lib.rs +++ b/sequencer/core/src/lib.rs @@ -26,10 +26,6 @@ use crate::{ pub mod block_publisher; pub mod block_store; pub mod config; -// Kept as a thin client lib for callers that want to query the indexer -// directly (e.g. integration tests). The sequencer no longer depends on the -// indexer at runtime โ€” finalization comes from zone-sdk events. -pub mod indexer_client; #[cfg(feature = "mock")] pub mod mock; From 5bcb1d468b4cab708872edd4cb11a59e2f638355 Mon Sep 17 00:00:00 2001 From: Petar Radovic Date: Fri, 1 May 2026 09:25:37 +0200 Subject: [PATCH 14/14] unstaged file --- integration_tests/src/indexer_client.rs | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 integration_tests/src/indexer_client.rs diff --git a/integration_tests/src/indexer_client.rs b/integration_tests/src/indexer_client.rs new file mode 100644 index 00000000..5641d824 --- /dev/null +++ b/integration_tests/src/indexer_client.rs @@ -0,0 +1,34 @@ +//! Thin client wrapper for querying the indexer's JSON-RPC API in tests. +//! +//! The sequencer doesn't depend on the indexer at runtime โ€” finalization comes +//! from zone-sdk events. This wrapper exists purely for test ergonomics so +//! integration tests can construct a single connection and call +//! `indexer_service_rpc::RpcClient` methods directly via `Deref`. + +use std::ops::Deref; + +use anyhow::{Context as _, Result}; +use jsonrpsee::ws_client::{WsClient, WsClientBuilder}; +use log::info; +use url::Url; + +pub struct IndexerClient(WsClient); + +impl IndexerClient { + pub async fn new(indexer_url: &Url) -> Result { + info!("Connecting to Indexer at {indexer_url}"); + let client = WsClientBuilder::default() + .build(indexer_url) + .await + .context("Failed to create websocket client")?; + Ok(Self(client)) + } +} + +impl Deref for IndexerClient { + type Target = WsClient; + + fn deref(&self) -> &Self::Target { + &self.0 + } +}