From 1e4e82a8ec039c209366a13b961cfce1855ad30b Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Fri, 29 May 2026 21:07:46 +0300 Subject: [PATCH 1/2] feat(sequencer): update to a new zone sdk --- .deny.toml | 1 + Cargo.lock | 214 +++++++++++++++++++------- Cargo.toml | 14 +- integration_tests/tests/bridge.rs | 6 +- sequencer/core/src/block_publisher.rs | 7 +- 5 files changed, 172 insertions(+), 70 deletions(-) diff --git a/.deny.toml b/.deny.toml index 6981f303..d48c60d0 100644 --- a/.deny.toml +++ b/.deny.toml @@ -57,6 +57,7 @@ unused-allowed-license = "deny" allow-git = [ "https://github.com/EspressoSystems/jellyfish.git", "https://github.com/logos-blockchain/logos-blockchain.git", + "https://github.com/logos-blockchain/logos-blockchain-circuits.git", ] unknown-git = "deny" unknown-registry = "deny" diff --git a/Cargo.lock b/Cargo.lock index 0bad6271..9b047e78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2670,7 +2670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2768,6 +2768,17 @@ dependencies = [ "serde", ] +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "ferroid" version = "2.0.0" @@ -5314,7 +5325,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "logos-blockchain-blend-crypto" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "blake2", "logos-blockchain-groth16", @@ -5328,7 +5339,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-message" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "blake2", "derivative", @@ -5352,7 +5363,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-proofs" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "ed25519-dalek", "generic-array 1.3.5", @@ -5372,7 +5383,7 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-broadcast-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "derivative", @@ -5388,7 +5399,7 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "bytes", @@ -5417,19 +5428,88 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "logos-blockchain-circuits-build" +version = "0.5.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +dependencies = [ + "dirs", + "fd-lock", + "flate2", + "tar", + "ureq", +] + +[[package]] +name = "logos-blockchain-circuits-common" +version = "0.5.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +dependencies = [ + "logos-blockchain-circuits-types", +] + +[[package]] +name = "logos-blockchain-circuits-poc-sys" +version = "0.5.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +dependencies = [ + "logos-blockchain-circuits-build", + "logos-blockchain-circuits-common", + "logos-blockchain-circuits-types", +] + +[[package]] +name = "logos-blockchain-circuits-pol-sys" +version = "0.5.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +dependencies = [ + "logos-blockchain-circuits-build", + "logos-blockchain-circuits-common", + "logos-blockchain-circuits-types", +] + +[[package]] +name = "logos-blockchain-circuits-poq-sys" +version = "0.5.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +dependencies = [ + "logos-blockchain-circuits-build", + "logos-blockchain-circuits-common", + "logos-blockchain-circuits-types", +] + [[package]] name = "logos-blockchain-circuits-prover" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "logos-blockchain-circuits-utils", "tempfile", ] +[[package]] +name = "logos-blockchain-circuits-signature-sys" +version = "0.5.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +dependencies = [ + "logos-blockchain-circuits-build", + "logos-blockchain-circuits-common", + "logos-blockchain-circuits-types", +] + +[[package]] +name = "logos-blockchain-circuits-types" +version = "0.5.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +dependencies = [ + "bytes", + "libc", +] + [[package]] name = "logos-blockchain-circuits-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "dirs", ] @@ -5437,7 +5517,7 @@ dependencies = [ [[package]] name = "logos-blockchain-common-http-client" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "futures", "hex", @@ -5459,7 +5539,7 @@ dependencies = [ [[package]] name = "logos-blockchain-core" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "ark-ff 0.4.2", "bincode", @@ -5492,7 +5572,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-engine" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "logos-blockchain-pol", "logos-blockchain-utils", @@ -5507,7 +5587,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-sync" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "bytes", "futures", @@ -5526,7 +5606,7 @@ dependencies = [ [[package]] name = "logos-blockchain-groth16" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -5544,7 +5624,7 @@ dependencies = [ [[package]] name = "logos-blockchain-http-api-common" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "axum 0.7.9", "logos-blockchain-core", @@ -5564,7 +5644,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=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "bytes", @@ -5590,7 +5670,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=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "proc-macro2", "quote", @@ -5600,7 +5680,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=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "logos-blockchain-blend-proofs", @@ -5616,7 +5696,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=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "log", @@ -5633,7 +5713,7 @@ dependencies = [ [[package]] name = "logos-blockchain-ledger" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "derivative", "logos-blockchain-blend-crypto", @@ -5659,7 +5739,7 @@ dependencies = [ [[package]] name = "logos-blockchain-libp2p" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "backon", @@ -5688,7 +5768,7 @@ dependencies = [ [[package]] name = "logos-blockchain-log-targets" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "logos-blockchain-log-targets-macros", ] @@ -5696,7 +5776,7 @@ dependencies = [ [[package]] name = "logos-blockchain-log-targets-macros" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "proc-macro2", "quote", @@ -5706,7 +5786,7 @@ dependencies = [ [[package]] name = "logos-blockchain-mmr" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "ark-ff 0.4.2", "logos-blockchain-groth16", @@ -5719,7 +5799,7 @@ dependencies = [ [[package]] name = "logos-blockchain-network-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "futures", @@ -5740,13 +5820,14 @@ dependencies = [ [[package]] name = "logos-blockchain-poc" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ + "logos-blockchain-circuits-poc-sys", "logos-blockchain-circuits-prover", + "logos-blockchain-circuits-types", "logos-blockchain-circuits-utils", "logos-blockchain-groth16", "logos-blockchain-proofs-error", - "logos-blockchain-witness-generator", "num-bigint 0.4.6", "serde", "serde_json", @@ -5756,15 +5837,16 @@ dependencies = [ [[package]] name = "logos-blockchain-pol" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "astro-float", + "logos-blockchain-circuits-pol-sys", "logos-blockchain-circuits-prover", + "logos-blockchain-circuits-types", "logos-blockchain-circuits-utils", "logos-blockchain-groth16", "logos-blockchain-proofs-error", "logos-blockchain-utils", - "logos-blockchain-witness-generator", "num-bigint 0.4.6", "num-traits", "serde", @@ -5775,14 +5857,15 @@ dependencies = [ [[package]] name = "logos-blockchain-poq" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ + "logos-blockchain-circuits-poq-sys", "logos-blockchain-circuits-prover", + "logos-blockchain-circuits-types", "logos-blockchain-circuits-utils", "logos-blockchain-groth16", "logos-blockchain-pol", "logos-blockchain-proofs-error", - "logos-blockchain-witness-generator", "num-bigint 0.4.6", "serde", "serde_json", @@ -5793,7 +5876,7 @@ dependencies = [ [[package]] name = "logos-blockchain-poseidon2" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "ark-bn254 0.4.0", "ark-ff 0.4.2", @@ -5804,8 +5887,9 @@ dependencies = [ [[package]] name = "logos-blockchain-proofs-error" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ + "logos-blockchain-circuits-types", "logos-blockchain-groth16", "serde_json", "thiserror 2.0.18", @@ -5814,7 +5898,7 @@ dependencies = [ [[package]] name = "logos-blockchain-services-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "futures", @@ -5829,7 +5913,7 @@ dependencies = [ [[package]] name = "logos-blockchain-storage-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "bytes", @@ -5848,7 +5932,7 @@ dependencies = [ [[package]] name = "logos-blockchain-time-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "futures", @@ -5871,7 +5955,7 @@ dependencies = [ [[package]] name = "logos-blockchain-tracing" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "flate2", "logos-blockchain-log-targets", @@ -5897,7 +5981,7 @@ dependencies = [ [[package]] name = "logos-blockchain-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "blake2", @@ -5916,7 +6000,7 @@ dependencies = [ [[package]] name = "logos-blockchain-utxotree" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "ark-ff 0.4.2", "logos-blockchain-groth16", @@ -5927,25 +6011,18 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "logos-blockchain-witness-generator" -version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" -dependencies = [ - "tempfile", -] - [[package]] name = "logos-blockchain-zksign" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "logos-blockchain-circuits-prover", + "logos-blockchain-circuits-signature-sys", + "logos-blockchain-circuits-types", "logos-blockchain-circuits-utils", "logos-blockchain-groth16", "logos-blockchain-poseidon2", "logos-blockchain-proofs-error", - "logos-blockchain-witness-generator", "num-bigint 0.4.6", "serde", "serde_json", @@ -5956,7 +6033,7 @@ dependencies = [ [[package]] name = "logos-blockchain-zone-sdk" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" dependencies = [ "async-trait", "futures", @@ -7666,7 +7743,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8585,7 +8662,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8643,7 +8720,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs 0.26.11", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -9554,6 +9631,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.13.5" @@ -9570,7 +9658,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -10498,24 +10586,26 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ "base64 0.22.1", + "flate2", "log", "percent-encoding", "rustls", "rustls-pki-types", "ureq-proto", - "utf-8", + "utf8-zero", + "webpki-roots", ] [[package]] name = "ureq-proto" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ "base64 0.22.1", "http 1.4.0", @@ -10554,6 +10644,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -10986,7 +11082,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ca9880ab..1f576d7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -137,13 +137,13 @@ schemars = "1.2" async-stream = "0.3.6" criterion = { version = "0.8", features = ["html_reports"] } -logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "dd055cc1ef7c130f710a52a190edd97bc7b0f71b" } -logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "dd055cc1ef7c130f710a52a190edd97bc7b0f71b" } -logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "dd055cc1ef7c130f710a52a190edd97bc7b0f71b" } -logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "dd055cc1ef7c130f710a52a190edd97bc7b0f71b" } -logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "dd055cc1ef7c130f710a52a190edd97bc7b0f71b" } -logos-blockchain-zone-sdk = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "dd055cc1ef7c130f710a52a190edd97bc7b0f71b" } -logos-blockchain-http-api-common = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "dd055cc1ef7c130f710a52a190edd97bc7b0f71b" } +logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } +logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } +logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } +logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } +logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } +logos-blockchain-zone-sdk = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } +logos-blockchain-http-api-common = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", diff --git a/integration_tests/tests/bridge.rs b/integration_tests/tests/bridge.rs index 7cb63b22..7ab90dc7 100644 --- a/integration_tests/tests/bridge.rs +++ b/integration_tests/tests/bridge.rs @@ -204,7 +204,9 @@ async fn submit_bedrock_deposit( // Encode deposit metadata let metadata = borsh::to_vec(&DepositMetadata { recipient_id }) - .context("Failed to encode deposit metadata")?; + .context("Failed to encode deposit metadata")? + .try_into() + .context("Encoded metadata is too big")?; let funding_key = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26"; @@ -307,7 +309,7 @@ async fn submit_bedrock_deposit( tip: None, deposit: DepositOp { channel_id, - inputs: Inputs::new(vec![selected_note_id]), + inputs: Inputs::new(selected_note_id), metadata, }, change_public_key: balance.address, diff --git a/sequencer/core/src/block_publisher.rs b/sequencer/core/src/block_publisher.rs index 62cd7259..f07a47c6 100644 --- a/sequencer/core/src/block_publisher.rs +++ b/sequencer/core/src/block_publisher.rs @@ -94,7 +94,7 @@ impl BlockPublisherTrait for ZoneSdkPublisher { continue; }; match event { - Event::Published { checkpoint, .. } => on_checkpoint(checkpoint), + Event::Checkpoint { checkpoint } => on_checkpoint(checkpoint), Event::TxsFinalized { items } => { for op in items.into_iter().flat_map(|item| item.ops) { match op { @@ -111,7 +111,10 @@ impl BlockPublisherTrait for ZoneSdkPublisher { } } } - Event::ChannelUpdate { .. } | Event::Ready => {} + Event::ChannelUpdate { .. } + | Event::Published { .. } + | Event::Readiness { .. } + | Event::TurnNotification { .. } => {} } } }); From 15521eedc2b3d2bed7c584caf6bc7f1fed6117cd Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Fri, 29 May 2026 22:44:48 +0300 Subject: [PATCH 2/2] feat(sequencer): make bridge deposits fault-tolerant --- Cargo.lock | 1 + artifacts/program_methods/bridge.bin | Bin 419612 -> 425204 bytes integration_tests/tests/bridge.rs | 2 + program_methods/guest/src/bin/bridge.rs | 1 + programs/bridge/core/src/lib.rs | 3 + sequencer/core/Cargo.toml | 1 + sequencer/core/src/block_store.rs | 23 +- sequencer/core/src/lib.rs | 292 +++++++++++++++++++++-- storage/src/sequencer/mod.rs | 78 +++++- storage/src/sequencer/sequencer_cells.rs | 64 ++++- 10 files changed, 439 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b047e78..84caf697 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8946,6 +8946,7 @@ dependencies = [ "nssa", "nssa_core", "rand 0.8.5", + "risc0-zkvm", "serde", "serde_json", "storage", diff --git a/artifacts/program_methods/bridge.bin b/artifacts/program_methods/bridge.bin index 3eacbc9d5eed4ee7abb47df575d7f265125e285f..5dd41e346e8311fdf483b30adebb0ea53ce30646 100644 GIT binary patch delta 116962 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 delta 111541 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

{W!D~$Z54ioue#n2p^~CGdZkKsX zI-KvyUxZiUZqCo)b#HL}*9O*;Q1E82d(;NDO3kZGlFpakcA=6z7$!vY_HOHKSS zxb}Nr{&+kgUglS}Ye=XxegaRWLp@xNKf?2H7w60~oMw0%=YDvV$v+OSGv#BPZ|Z*( z$ID6B-Bqw$0^afswBTy;_i*`rpXDQ%rh?;e4?Mu-$GFVoKZ-{gzmF&V=tp41v&?^+ z@L3SB3fGwe-{8f6_zHf&Jxqr;pU+D*;}TqKd@i1AJbga%Z>C9jg@8)qwYb`N%jei5 z;{)&n|4iWLaFfY@G4M;c=r7-)_*D`X z(qqoCuv#)RGwvMA+Nr>L@1y06OOo7t_ zpMeLP_$gS{iFC_GQ?aZA>gib40rdp;5k{C_41>sk}|F&QlDfEIWFSKysVkac01 zv*y2pNHa1Nee{2#o~cpR?G z_wkqE)y7x7!2Guf*9HNTajvtk;3moEEGS{ja8U z;CJw-O?(5(1Al}kn*5cxsEd#P5)U{2HgNo7knl6EG6jCa6`T4B*W()Fe*$M;Y@6Q! zFE{a>@T#t@^~KR9B=p#TWK%3{$-*~NGMDaNTWmD0&f+#2QD@7 zJ#qW({0{WNIpcnTi_}T`G&qQaB2!=p9%Z}_9&3C69+LA79)gD&mjymL@Nhhi__TdG zaDq#a{g)9Kg=M8nH-6(0_hwFXPjdM*oU`UvD@I2G^L5WDwevROV5nn{jXp(`-wA%29A3j4bX?>8NiTdGAthhH)I@+TM; z2OVB&@}>N0JSV1sQPKcI-ghAhINkW%Z{S^V`<_1j3TKU<82DOTPQ2z<1-=Q#6FDaO z$mDqvW}5;F0x!bzP5x_mm2o}p*2_2e7OtTFt>}SVE!R7z9pb0k7c&2(1DfzJ0d@{# zUUFH6Vi9@7;(Ar#epx6gM=&bL{s1zTxWbe?$*aQP#yTTz|(N4iJyThjN^Mr=wIk7 zd@%5wz>ni$CVn0+H+~M!Gky^-Gk&?P<7jc)glH*lG6k05;=aCv@8SyM4+DQ1_;Wno z#D9&eTg~-<4GAk+6Ee|HfqxDBJC;e5Zp>rHUg^yI&qh5+_}m0+hrclHg;yExfWI^@ z#9tZr!}c^Di2c^svI};nv8?~?qH9>IrN>z$51*G{*&|ls#CZ>7l08r#j%5!t{uV4l zUf|*zu#AxAZ}wm&*&!`I0?Q8b`)6qbGf0p_panj~vWHq=9oGWcL-mA5_)I@eKD>S#_TIfPo@0a!**%U_3O&K1f2%hhVI86sK9j>d%_`dhLqa9+P&M9ao62 zKCYz$ySwsV;B}@$o$9!u`H1WP9xh-v66TnIa$HA+`f&M1yv{W69FBFT&$j`1cR)mH54V{)4#6cn+RLygt5v zAJ2{V>lLlerq}-gukuo9UthtlxVI^AG9HY3(_^`MRpF5)|5?n{DC+6*KgVNC{)Vq{ zh>Qp0xXvV;O2S+^lx{pw3Ve3pO1#p^086RVLw20;U|` z3pC?3oHpr}jau;f4|+udNstlPX>n@J?~moIQSXdpmaBKgGRf7uEsoP!t_ed3kXf$Y z8_O(L?~7%YtM|t;N!6uTCaL-mER$4y7?w$@J|ZST&QnbohGo{OkHs=;)g$m&;}fu) zHJU#P%W0=R6|XZs9m^ci{P+wKWRhvZ7%Y=aeJ++sram9bq*7mi|_3@&wGG*1$tF8?H>{7 zgM#_2h6BM{4$1Wz6U{^5Xn(s!Gr|2B>);~B^T8cpucqRS;7qWZ`^&&1!0l=OG?Tgi z8#QDKUBGWJ9tHLcuq$x4gL51_9vo)+lfcKbZ2SAb{RXk~{|9A()eZOzw}WSbXTd

(97YwX|>K!z8a8qy&95j*+yMw0< zwJUJDgL{MLKwq8QP6G=>)Y{x)I=I83>h&M@d{97rq!bo(HBb zQ}QRlltYS(z*`wF0B_S-!6FotGX4{|lJOF71GGpvG{6lRzX)!`cm=o#<5l41j9&%o z)Mv`!8Wd2UDP9YvK2y9NOkJS(E%5zdbzALo$x40%OkJS(IG9h}U+z-ZpG=L4s#8$l z<36+omtc@|=M$`PJ`GHx!2D(uj+)=cxTlc1VNd4`?l)a#m(VVWGr<%gOcN$Q1*QnW zoymLN#QaYoRSFH)<2DRa$Oco$lzcIG8+K@pW9;DXVCpjSAQaqs1D={;@~vP>x(;%O zXVE~l(ZrOf@5WrsviK?oHv*S2c{A|(VJf8ffHe3V<7*t8?BMIbVz_OucSb>!6?6q> zF%E;qCKI(RU6 z1k)c57I#T|TK`9*;3V6@6X1i4H-ht(0rcN?@D>OE1zgDVw}a;}{x5K`WOM!BgM#Hu z;bZW6#s|U286N^)V0;wZ>25nBUlNb8`HX|Vwb@$#&ZD5{b7g=B62R*j{}a3o+*Wpp zZ;!=5Y;MsJOnq!^ZgB&+9IV!gVUm@6B)DiP>6`1nTZ~4*&IPuEyTM<>fjRE6gFi@C z4xR>65-DB?Zh$Vk3I&v8C6bl=U2sdtub1+#B+K~^-+)9x^@e#{^K*8C>VCkR#*%!JZ|$+Fkd`g0NYzd)p9UjJ2tGv zlhWuSHB~R&h>sj$7<$pCbj9T)*nJG^e>S|(VsZUjF0%s3P#~TJrV89Bc^#NqI9lqT z15*q4Nlw{>rzp|FQ&N8#n3CEjP{_}#iP$*;yUNXqH4Xy)^`og{{mJRkyu;n8ie3ygAf;%z&JRJqy3Ty`x z!7~_70XM)7t<5c_g1az&kn9;h1f~iq{Tbltj2{E@28&=?u3pFl8 zR@1QQ`!Zg=f+iRNV>YT@b)bMG+}Ph^Pz3Fn!AE zN5PZsRXf1fbR0YvJcG&q0A6w`SrlQXXun$=LBVR&@O$il_{NVhPAA(AQ^4hnLtxUk zHn)g?7k_1QI+%8#LwL_Pf z>fi^#*-ZXOHJj_7OUy)p|Fk`ot_E*o25TL>9-P7SKLcN6`sEHj2JUj!ei*$o7L6z| z?VmzE0|m3-(7b~Ieigh5?3MD5z?t{iEj-}hG6x?9Ph$GV9sDJ@5OTBqRE0ArSkDZ; z1#d?MATlf1YCp1ks$GF=zy}#8JNO0%cLp~{i&X;GsJw8IY7$>coA2mm&> zcoIxqWNoGuIY2>BIK{(xj9F9grhp`Z#LLBSGc@Mp5Tpp!1MqWnT`~PeI`(lu*d(9T0+8DO?F|f<8kEnDSJ~N*)DMpDMl; zOj)kD2bi*4aZfOx2Qm++{4+Hws`^5K&jSO%YNL7JA%}w*OiuI2V-EY-OiuP|9ro)F z$o#i=dKOim7hU>=cFCMWwGhy8daC;I|!Pv1Yemno2gB@PD$laqrNxdYg*U~;nG?Xcg= z)LFqn_ibO#$-{GN_&c^WwMrV6$ z?&%E$&i%kAnS2oVJmX>Dq=)Re>ke=?#$&*|f#bmThN-L!z0GU*NIg78f*s^Qiw>+O zFaxS_vBTkFCZ~uk1#^EdFge-pblC6GnF2Z3<8bgHlaqss4hKImIoV%!*t-td?Irsz zU>*^@D^nl`K`?JYDwC6g9EXGPOiuO%4*PqVoa~p>wAY2f6v)Ag4hJikoE+?~=|Jpd za%YE30QB95tK`e79^`nRI$VK7gtN12@Jv>MEV`Z|-7{W^#J1|}!_vcs54xP!w? zfgBulI5@%NG1%Hv3-fV52DN z>8SG{e%cz(t-+!rB2Ck+4;E@8ms#M|%;Qi8j{t9B@;kv@9Qe`51{;4}NZ;)2xQ4eqqhYu<_?{r%t!$m!M;@gZ;}{w0x-r%yodPSj>?*pG(=@qLa zC!G}He*@NU=r$3WE&?c6xXLRieW?N)!1RLjPPfG;z~KkH=2d)>Fa8QAP!D^>L8{uFfRt3uo1PbY)D_)m1guVJ8o6tgr|cf#)3dnvbH={sw5c2``dGh8yFb z3es?#S2V;cRG-ZTS7KN$lsT{s?8)|;7eq-vp+0WP4)coTvO?M5g^-uZ3j7gFmm^g~ zw}a^`LLH_4Q&-|~rK!|~x)rjoP_PU;tS=QZuX2ftc9x1~!1Ur}v8>Qm@R?FO2f8=F zN%v0M{xPuUK}1yQp90f2ar#OBeHx+xTfE{T_HRaD5DMtC0<9z`w8T|;{FHPy7{f$F z!RJ1*lknA6h*)ogQ0h-_g9=r6g;(Z4QG2}GfN$C?lk#_b_zV#~u!3AS_tzpB6~OmV zbXCy3Bl^6Z^>z-0SX_rgjAZN3n>Ylf_aJkmgSWu+ol@4 zPy=g+uXMsI%RcLnh5Y$(*A7XkS7+s76wpUWG(>HDuztp*={1TYUciiMM9~(R< zE6||}D!}~B1Ji4V{bYrAgGaQ(<;7BI(D-H;U|gy`>ISB3(o>{7EyX2vKkF4`k{<)p z^G%~AzXPUsThv4|p(~P*b?F-L_OI=!xosF-pcmPFwiZWq^eSkk%wioUjDF20Cdoc6 z0e53L5KTi;?eL2KkQI3DcE}S@VX40teEwsv`FrLR@*chLTgc!MQhz;IUydxNVbKIL z{C6nWz6mvyAzFf((8n=aNrOXR`b>}-cImy*f}{3KcNyILm{)9*^TO~x=wry$%=sah zN3dW3#$Wwi_OMw$05$%bUBl!o3@cpoR^ztWAlzJOoGdQN3MCK59bOE(O3Cwv)EtH< zMNHl9|F^Rk>rv%c{Ey#9O)=|449ssQ4!75?{#NT4J>cR)Uh^(JwXkb0)&`90Y0_aac+=Um2ZB8s_Rb>S&6TWxjm*NK>V>*EJe zWYUbCOly2Ho%o;&++qWmua0YGAj@;Gy2%}ugU{XUs~$C{A4k%%E^0FyA82Eo1um;* zT@&x=nAtk{#2o1`GPmYLvlPrHn&>E)2Gi3*J4sIK|GQ74&vJcYnlxzn3=ELVldTGQc~dJ~2hw z?*(Vx<`eBCyB6Z1yWT!AUp8{&SM?i0JE z1F;B;QxBggmi#z)Ik-@A!XI5C2^9z{IXEZ7C&tJIZ3S<;-K>DM{+~d>$!wqSN`s!o z$a;j7#x1RW-+>GJ_{2gffB!j``Kx}LWX_CSjOBKsPi%q#h4@|Kp>~$Lmtd5E^QHX& z@M`dA9DkSw&!M0a1#0dt2h-1u=SYJd&tq5)vlpX<;BDaJQvN=eer|k>V#aU@BHK;ZNXYBeC4l51^8RA2(s5m|8NspC{3!cbp+ovU2aAdVfkZZG140nmQqr z=g&(o2u9Mvp#qfTO~_9z2t)(Xl%Rm}uh93$p|A5DMB}bX7vJ0466XfRt-Vb?~PwSD)e7x5c!omNQZd#O^%$KRGBem3tM+h>*%KiOl$nk(RJ;09Le)bxhOR#)95gDv_e?aV4)N4W zj;S3oHoxX+V|;&yC($@M(9^>BXo%N(XRw%edFWtb;5 zcB{hiS2gU9vyS1Eu#CTce@e`%hAIE!i)*+#&0>3{QIf0ELKagul}2oQHAoG{uZnh! zh}UiP1R1-s5o73hPiwL!mMttO7XTbjHNwpgG3r|;jrOrqP0zqH84~C zMw20))+K94dagehd*KN9Q>ILL_`Z~2IGpAWh0;<(sgaN$e+PxxUoq#8K%hjn$!s&rX*q;^&MgtKs?ykXUA>);ST5BU| zh<1fBud9|7yK@5Rzu+>yq{Ok98Kqikl^L-D6|-eQ8K_!(L`oLF@TaEmPmSt2% zw5p!5`&BxtB{9$qk!W={_zZ8MR&VKQ?aGp(sMf|6N6w{M^{mBpoH-Z%1qXdFA;oT; zOtV_5fYt>9fpNx(p`Mn;cQL?bF}IiYWNi1LI!CK9 zcbL|}n2@Qhieq5b6_7aBf{>hPf1Uv30Ea5E$ItJb8p3riN5;HiNWyFSXwCKX++d&} z@8~A${OWC^LJ_AI4XAfD^-tgbP+}GK|M)pPwhO>R+84Q6sn_=uY zs-+pz`)T81=e$}_f1YiYm)`v`*T&AcItON)IT5ZqDR2OEq`#KeE#|R-TDWUD=rLvv z(Ec3TgUqbj4lb0oV( zt9h+TtFpB_YnK$~d2-rV(@w3Hw37aUyrgodw#yYelK&5Zth anyhow::Result<()> { vec![bridge_account_id, recipient_vault_id], vec![], bridge_core::Instruction::Deposit { + l1_deposit_op_id: [0_u8; 32], vault_program_id, recipient_id, amount: 1, @@ -129,6 +130,7 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> { // Serialize the bridge deposit instruction let instruction = Program::serialize_instruction(bridge_core::Instruction::Deposit { + l1_deposit_op_id: [0_u8; 32], vault_program_id, recipient_id, amount: 1, diff --git a/program_methods/guest/src/bin/bridge.rs b/program_methods/guest/src/bin/bridge.rs index 0833d5aa..c9426be7 100644 --- a/program_methods/guest/src/bin/bridge.rs +++ b/program_methods/guest/src/bin/bridge.rs @@ -33,6 +33,7 @@ fn main() { let chained_calls = match instruction { Instruction::Deposit { + l1_deposit_op_id: _, vault_program_id, recipient_id, amount, diff --git a/programs/bridge/core/src/lib.rs b/programs/bridge/core/src/lib.rs index 1da75f97..214a5a47 100644 --- a/programs/bridge/core/src/lib.rs +++ b/programs/bridge/core/src/lib.rs @@ -12,6 +12,9 @@ pub enum Instruction { /// - Bridge PDA account /// - Recipient vault PDA account Deposit { + /// Deposit OP ID from L1, stored here to pin each [`Deposit`](Instruction::Deposit) to a + /// Deposit Event on L1. + l1_deposit_op_id: [u8; 32], vault_program_id: ProgramId, recipient_id: AccountId, amount: u128, diff --git a/sequencer/core/Cargo.toml b/sequencer/core/Cargo.toml index 985fc969..5e8a6ef6 100644 --- a/sequencer/core/Cargo.toml +++ b/sequencer/core/Cargo.toml @@ -34,6 +34,7 @@ borsh.workspace = true bytesize.workspace = true hex.workspace = true url.workspace = true +risc0-zkvm.workspace = true [features] default = [] diff --git a/sequencer/core/src/block_store.rs b/sequencer/core/src/block_store.rs index ada6d306..68145e44 100644 --- a/sequencer/core/src/block_store.rs +++ b/sequencer/core/src/block_store.rs @@ -10,7 +10,7 @@ use log::info; use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint; use nssa::V03State; pub use storage::DbResult; -use storage::sequencer::RocksDBIO; +use storage::sequencer::{RocksDBIO, sequencer_cells::PendingDepositEventRecord}; pub struct SequencerStore { dbio: Arc, @@ -165,6 +165,27 @@ impl SequencerStore { self.dbio.put_zone_sdk_checkpoint_bytes(&bytes)?; Ok(()) } + + pub fn get_unfulfilled_deposit_events(&self) -> DbResult> { + self.dbio.get_pending_deposit_events() + } + + pub fn mark_unfulfilled_deposit_events_submitted( + &self, + deposit_op_ids: &[HashType], + submitted_block_id: u64, + ) -> DbResult { + self.dbio + .mark_pending_deposit_events_submitted(deposit_op_ids, submitted_block_id) + } + + pub fn remove_fulfilled_unfulfilled_deposit_events_up_to_block( + &self, + finalized_block_id: u64, + ) -> DbResult { + self.dbio + .remove_fulfilled_pending_deposit_events_up_to_block(finalized_block_id) + } } 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 13b455df..82f5065c 100644 --- a/sequencer/core/src/lib.rs +++ b/sequencer/core/src/lib.rs @@ -1,4 +1,4 @@ -use std::{path::Path, time::Instant}; +use std::{path::Path, sync::Arc, time::Instant}; use anyhow::{Context as _, Result, anyhow}; use borsh::BorshDeserialize; @@ -16,6 +16,7 @@ pub use mock::SequencerCoreWithMockClients; use nssa::{AccountId, PublicTransaction, program::Program, public_transaction::Message}; use nssa_core::GENESIS_BLOCK_ID; pub use storage::error::DbError; +use storage::sequencer::sequencer_cells::PendingDepositEventRecord; use crate::{ block_publisher::{BlockPublisherTrait, ZoneSdkPublisher}, @@ -147,26 +148,72 @@ impl SequencerCore { let dbio_for_finalized = store.dbio(); let on_finalized_block: block_publisher::FinalizedBlockSink = Box::new(move |block_id| { + // NOTE: Theoretically Zone SDK may report finalization happening multiple times for the + // same block. In practice this is very unlikely to happen. For that to + // happen Sequencer should crash between receiving Finalized and Checkpoint events while + // these events happen very fast (because Checkpoints are generated by Zone SDK + // locally). + 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:#}"); } + + match dbio_for_finalized.remove_fulfilled_pending_deposit_events_up_to_block(block_id) { + Ok(0) => {} + Ok(removed) => { + info!( + "Removed {removed} fulfilled pending deposit events up to finalized block {block_id}" + ); + } + Err(err) => { + error!( + "Failed to remove fulfilled pending deposit events up to block {block_id}: {err:#}" + ); + } + } }); let (mempool, mempool_handle) = MemPool::new(config.mempool_max_size); + replay_unfulfilled_deposit_events(&store, mempool_handle.clone()); + let mempool_handle_for_deposit = mempool_handle.clone(); + let dbio_for_deposit = store.dbio(); let on_deposit_event: block_publisher::OnDepositEventSink = Box::new(move |deposit| { + // NOTE: Theoretically Zone SDK may report multiple identical deposits. In practice this + // is very unlikely to happen. For that to happen Sequencer should crash + // between receiving Deposit and Checkpoint events while these events happen + // very fast (because Checkpoints are generated by Zone SDK locally). + + let dbio_for_deposit = Arc::clone(&dbio_for_deposit); let mempool_handle_for_deposit = mempool_handle_for_deposit.clone(); Box::pin(async move { - info!( - "Observed Bedrock Deposit event with id: {:?}", - hex::encode(deposit.op_id) - ); - let tx = match build_bridge_deposit_tx(&deposit) { + let id_hex = hex::encode(deposit.op_id); + info!("Observed Bedrock Deposit event with id: {id_hex}"); + + let event_record = pending_deposit_event_record(&deposit); + + match dbio_for_deposit.add_pending_deposit_event(event_record.clone()) { + Ok(true) => {} + Ok(false) => { + info!( + "Deposit event {id_hex} already persisted as unfulfilled, skipping duplicate enqueue", + ); + return; + } + Err(err) => { + error!( + "Failed to persist unfulfilled deposit event {id_hex} before enqueue: {err:#}. Deposit will be lost.", + ); + return; + } + } + + let tx = match build_bridge_deposit_tx_from_event(&event_record) { Ok(tx) => tx, Err(err) => { - warn!( - "Skipping finalized Bedrock deposit event due to tx build failure: {err:#}" + error!( + "Failed to build transaction from Bedrock deposit event {id_hex}: {err:#}. Deposit will be lost.", ); return; } @@ -177,7 +224,7 @@ impl SequencerCore { .await { error!( - "Failed to queue sequencer transaction built from finalized Bedrock event: {err:#}" + "Failed to queue sequencer transaction built from finalized Bedrock event: {err:#}. Deposit will be lost." ); } }) @@ -199,8 +246,11 @@ impl SequencerCore { // 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:#}"); + if is_fresh_start { + block_publisher + .publish_block(&genesis_block) + .await + .expect("Failed to publish genesis block"); } let sequencer_core = Self { @@ -217,30 +267,47 @@ impl SequencerCore { /// Produces a new block from mempool transactions and publishes it via zone-sdk. pub async fn produce_new_block(&mut self) -> Result { - let block = self + let block_with_meta = self .build_block_from_mempool() .context("Failed to build block from mempool transactions")?; + let BlockWithMeta { + block, + deposit_event_ids, + } = block_with_meta; // 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]; - if let Err(err) = self.block_publisher.publish_block(&block).await { - error!("Failed to publish block to Bedrock with error: {err:#}"); - } + self.block_publisher + .publish_block(&block) + .await + .context("Failed to publish block to Bedrock")?; + self.store.update(&block, placeholder_msg_id, &self.state)?; + let updated_deposits = self + .store + .mark_unfulfilled_deposit_events_submitted(&deposit_event_ids, block.header.block_id)?; + if updated_deposits > 0 { + info!( + "Marked {updated_deposits} pending deposit events as submitted in block {}", + block.header.block_id + ); + } + Ok(self.chain_height) } /// 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 { + fn build_block_from_mempool(&mut self) -> Result { let now = Instant::now(); let new_block_height = self.next_block_id(); - let mut valid_transactions = vec![]; + let mut valid_transactions = Vec::new(); + let mut deposit_event_ids = Vec::new(); let max_block_size = usize::try_from(self.sequencer_config.max_block_size.as_u64()) .expect("`max_block_size` should fit into usize"); @@ -311,6 +378,20 @@ impl SequencerCore { let NSSATransaction::Public(public_tx) = &tx else { panic!("Sequencer may only generate Public transactions, found {tx:#?}"); }; + + if public_tx.message.program_id == Program::bridge().id() { + let instruction: bridge_core::Instruction = + risc0_zkvm::serde::from_slice(&public_tx.message.instruction_data) + .context("Failed to deserialize bridge instruction")?; + match instruction { + bridge_core::Instruction::Deposit { + l1_deposit_op_id, .. + } => { + deposit_event_ids.push(HashType(l1_deposit_op_id)); + } + } + } + self.state .transition_from_public_transaction( public_tx, @@ -355,7 +436,11 @@ impl SequencerCore { hashable_data.transactions.len(), now.elapsed().as_secs() ); - Ok(block) + + Ok(BlockWithMeta { + block, + deposit_event_ids, + }) } pub const fn state(&self) -> &nssa::V03State { @@ -411,6 +496,60 @@ impl SequencerCore { } } +struct BlockWithMeta { + block: Block, + deposit_event_ids: Vec, +} + +/// Checks the database for any pending deposit events that have not yet been marked as submitted in +/// a block, and re-queues them in the mempool in a separate async task for inclusion in the next +/// block. +fn replay_unfulfilled_deposit_events( + store: &SequencerStore, + mempool_handle: MemPoolHandle<(TransactionOrigin, NSSATransaction)>, +) { + let replay_records: Vec = store + .get_unfulfilled_deposit_events() + .expect("Failed to load unfulfilled deposit events") + .into_iter() + .filter(|record| record.submitted_in_block_id.is_none()) + .collect(); + + if replay_records.is_empty() { + return; + } + + info!( + "Found {} unfulfilled deposit events in DB, re-queueing", + replay_records.len() + ); + tokio::spawn(async move { + for record in replay_records { + let tx = match build_bridge_deposit_tx_from_event(&record) { + Ok(tx) => tx, + Err(err) => { + warn!( + "Skipping replay of pending deposit event {} due to tx build failure: {err:#}", + hex::encode(record.deposit_op_id) + ); + continue; + } + }; + + if let Err(err) = mempool_handle + .push((TransactionOrigin::Sequencer, tx)) + .await + { + error!( + "Failed to re-queue unfulfilled deposit event {} from DB: {err:#}", + hex::encode(record.deposit_op_id) + ); + break; + } + } + }); +} + /// Builds the initial genesis state from `testnet_initial_state` plus configured genesis /// transactions. Returns the final state and the list of [`NSSATransaction`]s that should be /// committed to the genesis block so external observers can replay them. @@ -485,10 +624,22 @@ fn build_supply_bridge_account_genesis_transaction(balance: u128) -> PublicTrans PublicTransaction::new(message, witness_set) } -fn build_bridge_deposit_tx( +fn pending_deposit_event_record( deposit: &logos_blockchain_zone_sdk::state::DepositInfo, +) -> PendingDepositEventRecord { + PendingDepositEventRecord { + deposit_op_id: HashType(deposit.op_id), + source_tx_hash: HashType(deposit.tx_hash.0), + amount: deposit.amount, + metadata: deposit.metadata.clone().into(), + submitted_in_block_id: None, + } +} + +fn build_bridge_deposit_tx_from_event( + event: &PendingDepositEventRecord, ) -> Result { - let metadata = DepositMetadata::decode(&deposit.metadata) + let metadata = DepositMetadata::decode(&event.metadata) .context("Failed to decode finalized Bedrock deposit metadata")?; let bridge_program_id = Program::bridge().id(); @@ -501,9 +652,10 @@ fn build_bridge_deposit_tx( vec![nssa::system_bridge_account_id(), recipient_vault_id], vec![], bridge_core::Instruction::Deposit { + l1_deposit_op_id: event.deposit_op_id.0, vault_program_id, recipient_id: metadata.recipient_id, - amount: u128::from(deposit.amount), + amount: u128::from(event.amount), }, ) .context("Failed to build bridge deposit message")?; @@ -552,6 +704,7 @@ mod tests { }; use logos_blockchain_core::mantle::ops::channel::ChannelId; use mempool::MemPoolHandle; + use storage::sequencer::sequencer_cells::PendingDepositEventRecord; use tempfile::tempdir; use testnet_initial_state::{initial_accounts, initial_pub_accounts_private_keys}; @@ -563,6 +716,11 @@ mod tests { mock::SequencerCoreWithMockClients, }; + #[derive(borsh::BorshSerialize)] + struct DepositMetadataForEncoding { + recipient_id: nssa::AccountId, + } + fn setup_sequencer_config() -> SequencerConfig { let tempdir = tempfile::tempdir().unwrap(); let home = tempdir.path().to_path_buf(); @@ -620,6 +778,35 @@ mod tests { (sequencer, mempool_handle) } + fn tx_is_bridge_deposit( + tx: &NSSATransaction, + deposit_op_id: [u8; 32], + expected_amount: u64, + ) -> bool { + let NSSATransaction::Public(public_tx) = tx else { + return false; + }; + + if public_tx.message.program_id != nssa::program::Program::bridge().id() { + return false; + } + + let instruction: bridge_core::Instruction = + match risc0_zkvm::serde::from_slice(&public_tx.message.instruction_data) { + Ok(instruction) => instruction, + Err(_err) => return false, + }; + + matches!( + instruction, + bridge_core::Instruction::Deposit { + l1_deposit_op_id, + amount, + .. + } if l1_deposit_op_id == deposit_op_id && amount == u128::from(expected_amount) + ) + } + #[tokio::test] async fn start_from_config() { let config = setup_sequencer_config(); @@ -690,6 +877,69 @@ mod tests { let _ = SequencerCoreWithMockClients::start_from_config(config).await; } + #[tokio::test] + async fn start_from_config_replays_unfulfilled_deposit_events_from_db() { + let config = setup_sequencer_config(); + let deposit_op_id = [13_u8; 32]; + let expected_amount = 1_u64; + let recipient_id = initial_accounts()[0].account_id; + + { + let (_sequencer, _mempool_handle) = + SequencerCoreWithMockClients::start_from_config(config.clone()).await; + } + + let pending_event = PendingDepositEventRecord { + deposit_op_id: HashType(deposit_op_id), + source_tx_hash: HashType([7_u8; 32]), + amount: expected_amount, + metadata: borsh::to_vec(&DepositMetadataForEncoding { recipient_id }).unwrap(), + submitted_in_block_id: None, + }; + + { + let signing_key = nssa::PrivateKey::try_new(config.signing_key).unwrap(); + let store = SequencerStore::open_db(&config.home.join("rocksdb"), signing_key).unwrap(); + + let inserted = store + .dbio() + .add_pending_deposit_event(pending_event) + .unwrap(); + assert!(inserted); + } + + let (mut sequencer, _mempool_handle) = + SequencerCoreWithMockClients::start_from_config(config).await; + + let (origin, tx) = tokio::time::timeout(Duration::from_secs(5), async { + loop { + if let Some((origin, tx)) = sequencer.mempool.pop() { + return (origin, tx); + } + + tokio::time::sleep(Duration::from_millis(100)).await; + } + }) + .await + .expect("Timed out waiting for pending deposit event to be replayed into mempool"); + + match origin { + TransactionOrigin::Sequencer => {} + TransactionOrigin::User => { + panic!("Unexpected user transaction in empty mempool replay test") + } + } + + assert!(tx_is_bridge_deposit(&tx, deposit_op_id, expected_amount)); + + let pending_events = sequencer.store.get_unfulfilled_deposit_events().unwrap(); + let replayed_event = pending_events + .into_iter() + .find(|event| event.deposit_op_id == HashType(deposit_op_id)) + .expect("Pending deposit event should remain in DB until included in a block"); + assert!(replayed_event.submitted_in_block_id.is_none()); + } + #[test] fn transaction_pre_check_pass() { let tx = common::test_utils::produce_dummy_empty_transaction(); diff --git a/storage/src/sequencer/mod.rs b/storage/src/sequencer/mod.rs index be5e5cfe..34b7ca2c 100644 --- a/storage/src/sequencer/mod.rs +++ b/storage/src/sequencer/mod.rs @@ -1,6 +1,9 @@ use std::{path::Path, sync::Arc}; -use common::block::{BedrockStatus, Block, BlockMeta, MantleMsgId}; +use common::{ + HashType, + block::{BedrockStatus, Block, BlockMeta, MantleMsgId}, +}; use nssa::V03State; use rocksdb::{ BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, WriteBatch, @@ -12,7 +15,9 @@ use crate::{ error::DbError, sequencer::sequencer_cells::{ LastFinalizedBlockIdCell, LatestBlockMetaCellOwned, LatestBlockMetaCellRef, - NSSAStateCellOwned, NSSAStateCellRef, ZoneSdkCheckpointCellOwned, ZoneSdkCheckpointCellRef, + NSSAStateCellOwned, NSSAStateCellRef, PendingDepositEventRecord, + PendingDepositEventsCellOwned, PendingDepositEventsCellRef, ZoneSdkCheckpointCellOwned, + ZoneSdkCheckpointCellRef, }, }; @@ -24,6 +29,9 @@ pub const DB_META_LAST_FINALIZED_BLOCK_ID: &str = "last_finalized_block_id"; 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 queued deposit events that were not yet +/// fulfilled on L2. +pub const DB_META_PENDING_DEPOSIT_EVENTS_KEY: &str = "pending_deposit_events"; /// Key base for storing the NSSA state. pub const DB_NSSA_STATE_KEY: &str = "nssa_state"; @@ -239,6 +247,72 @@ impl RocksDBIO { self.put(&ZoneSdkCheckpointCellRef(bytes), ()) } + pub fn get_pending_deposit_events(&self) -> DbResult> { + Ok(self + .get_opt::(())? + .map_or_else(Vec::new, |cell| cell.0)) + } + + fn put_pending_deposit_events(&self, records: &[PendingDepositEventRecord]) -> DbResult<()> { + self.put(&PendingDepositEventsCellRef(records), ()) + } + + pub fn add_pending_deposit_event(&self, event: PendingDepositEventRecord) -> DbResult { + let mut records = self.get_pending_deposit_events()?; + if records + .iter() + .any(|record| record.deposit_op_id == event.deposit_op_id) + { + return Ok(false); + } + records.push(event); + self.put_pending_deposit_events(&records)?; + Ok(true) + } + + pub fn mark_pending_deposit_events_submitted( + &self, + deposit_op_ids: &[HashType], + submitted_block_id: u64, + ) -> DbResult { + let mut records = self.get_pending_deposit_events()?; + let mut updated: usize = 0; + + for record in records + .iter_mut() + .filter(|record| deposit_op_ids.contains(&record.deposit_op_id)) + { + record.submitted_in_block_id = Some(submitted_block_id); + updated = updated.saturating_add(1); + } + + if updated > 0 { + self.put_pending_deposit_events(&records)?; + } + + Ok(updated) + } + + pub fn remove_fulfilled_pending_deposit_events_up_to_block( + &self, + finalized_block_id: u64, + ) -> DbResult { + let mut records = self.get_pending_deposit_events()?; + let before = records.len(); + records.retain(|record| { + record + .submitted_in_block_id + .is_none_or(|submitted_id| submitted_id > finalized_block_id) + }); + + let removed = before.saturating_sub(records.len()); + if removed > 0 { + self.put_pending_deposit_events(&records)?; + } + + Ok(removed) + } + pub fn put_block( &self, block: &Block, diff --git a/storage/src/sequencer/sequencer_cells.rs b/storage/src/sequencer/sequencer_cells.rs index 2bf65367..78d09944 100644 --- a/storage/src/sequencer/sequencer_cells.rs +++ b/storage/src/sequencer/sequencer_cells.rs @@ -1,5 +1,5 @@ use borsh::{BorshDeserialize, BorshSerialize}; -use common::block::BlockMeta; +use common::{HashType, block::BlockMeta}; use nssa::V03State; use crate::{ @@ -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_META_ZONE_SDK_CHECKPOINT_KEY, DB_NSSA_STATE_KEY, + DB_META_PENDING_DEPOSIT_EVENTS_KEY, DB_META_ZONE_SDK_CHECKPOINT_KEY, DB_NSSA_STATE_KEY, }, }; @@ -131,12 +131,56 @@ impl SimpleWritableCell for ZoneSdkCheckpointCellRef<'_> { } } +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct PendingDepositEventRecord { + pub deposit_op_id: HashType, + pub source_tx_hash: HashType, + pub amount: u64, + pub metadata: Vec, + /// Set when block containing the deposit event is submitted, but not necessarily finalized. + pub submitted_in_block_id: Option, +} + +#[derive(BorshDeserialize)] +pub struct PendingDepositEventsCellOwned(pub Vec); + +impl SimpleStorableCell for PendingDepositEventsCellOwned { + type KeyParams = (); + + const CELL_NAME: &'static str = DB_META_PENDING_DEPOSIT_EVENTS_KEY; + const CF_NAME: &'static str = CF_META_NAME; +} + +impl SimpleReadableCell for PendingDepositEventsCellOwned {} + +#[derive(BorshSerialize)] +pub struct PendingDepositEventsCellRef<'records>(pub &'records [PendingDepositEventRecord]); + +impl SimpleStorableCell for PendingDepositEventsCellRef<'_> { + type KeyParams = (); + + const CELL_NAME: &'static str = DB_META_PENDING_DEPOSIT_EVENTS_KEY; + const CF_NAME: &'static str = CF_META_NAME; +} + +impl SimpleWritableCell for PendingDepositEventsCellRef<'_> { + fn value_constructor(&self) -> DbResult> { + borsh::to_vec(&self).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize pending deposit events cell".to_owned()), + ) + }) + } +} + #[cfg(test)] mod uniform_tests { use crate::{ cells::SimpleStorableCell as _, sequencer::sequencer_cells::{ LatestBlockMetaCellOwned, LatestBlockMetaCellRef, NSSAStateCellOwned, NSSAStateCellRef, + PendingDepositEventsCellOwned, PendingDepositEventsCellRef, }, }; @@ -165,4 +209,20 @@ mod uniform_tests { LatestBlockMetaCellOwned::key_constructor(()).unwrap() ); } + + #[test] + fn pending_deposit_events_ref_and_owned_is_aligned() { + assert_eq!( + PendingDepositEventsCellRef::CELL_NAME, + PendingDepositEventsCellOwned::CELL_NAME + ); + assert_eq!( + PendingDepositEventsCellRef::CF_NAME, + PendingDepositEventsCellOwned::CF_NAME + ); + assert_eq!( + PendingDepositEventsCellRef::key_constructor(()).unwrap(), + PendingDepositEventsCellOwned::key_constructor(()).unwrap() + ); + } }