diff --git a/.deny.toml b/.deny.toml index fb1ce3cf..6981f303 100644 --- a/.deny.toml +++ b/.deny.toml @@ -16,6 +16,7 @@ ignore = [ { id = "RUSTSEC-2026-0097", reason = "`rand` v0.8.5 is present transitively from logos crates, modification may break integration" }, { id = "RUSTSEC-2026-0118", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" }, { id = "RUSTSEC-2026-0119", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" }, + { id = "RUSTSEC-2024-0370", reason = "transitive dependency of `logos-blockchain-http-api-common`, can't do anything than wait for upstream fix" }, ] yanked = "deny" unused-ignored-advisory = "deny" diff --git a/Cargo.lock b/Cargo.lock index 40d3a348..bcd49668 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -733,8 +733,6 @@ checksum = "86887daca11d02e0b04f37a9cb81888aae881397fb48ff66494e356aea97554a" dependencies = [ "itertools 0.10.5", "lazy_static", - "rand 0.8.5", - "serde", ] [[package]] @@ -1056,7 +1054,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" dependencies = [ "fastrand", - "gloo-timers 0.3.0", "tokio", ] @@ -1319,6 +1316,14 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "bridge_core" +version = "0.1.0" +dependencies = [ + "nssa_core", + "serde", +] + [[package]] name = "bs58" version = "0.5.1" @@ -2181,7 +2186,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]] @@ -2665,7 +2670,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]] @@ -2847,6 +2852,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -2854,7 +2868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -2868,6 +2882,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -2997,7 +3017,7 @@ version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" dependencies = [ - "gloo-timers 0.2.6", + "gloo-timers", "send_wrapper 0.4.0", ] @@ -3167,18 +3187,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "gloo-utils" version = "0.2.0" @@ -3638,6 +3646,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -3655,10 +3679,12 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.3", + "system-configuration 0.7.0", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -4071,6 +4097,8 @@ dependencies = [ "anyhow", "ata_core", "authenticated_transfer_core", + "borsh", + "bridge_core", "bytesize", "common", "faucet_core", @@ -4080,8 +4108,11 @@ dependencies = [ "indexer_service_rpc", "key_protocol", "log", + "logos-blockchain-core", + "logos-blockchain-http-api-common", "nssa", "nssa_core", + "reqwest", "sequencer_core", "sequencer_service_rpc", "serde_json", @@ -5283,7 +5314,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "logos-blockchain-blend-crypto" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "blake2", "logos-blockchain-groth16", @@ -5291,13 +5322,13 @@ dependencies = [ "logos-blockchain-poseidon2", "logos-blockchain-utils", "rs-merkle-tree", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] name = "logos-blockchain-blend-message" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "blake2", "derivative", @@ -5308,11 +5339,12 @@ dependencies = [ "logos-blockchain-core", "logos-blockchain-groth16", "logos-blockchain-key-management-system-keys", + "logos-blockchain-log-targets", "logos-blockchain-utils", "serde", "serde-big-array", "serde_with", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", "zeroize", ] @@ -5320,7 +5352,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-proofs" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "ed25519-dalek", "generic-array 1.3.5", @@ -5329,17 +5361,18 @@ dependencies = [ "logos-blockchain-groth16", "logos-blockchain-pol", "logos-blockchain-poq", + "logos-blockchain-poseidon2", "logos-blockchain-utils", "num-bigint 0.4.6", "serde", - "thiserror 1.0.69", + "thiserror 2.0.18", "zeroize", ] [[package]] name = "logos-blockchain-chain-broadcast-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "derivative", @@ -5355,7 +5388,7 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "bytes", @@ -5377,7 +5410,8 @@ dependencies = [ "serde", "serde_with", "strum", - "thiserror 1.0.69", + "thiserror 2.0.18", + "time", "tokio", "tracing", "tracing-futures", @@ -5386,7 +5420,7 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-prover" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "logos-blockchain-circuits-utils", "tempfile", @@ -5395,7 +5429,7 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "dirs", ] @@ -5403,10 +5437,11 @@ dependencies = [ [[package]] name = "logos-blockchain-common-http-client" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "futures", "hex", + "log", "logos-blockchain-chain-broadcast-service", "logos-blockchain-chain-service", "logos-blockchain-core", @@ -5416,14 +5451,15 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.18", + "tokio-util", "url", ] [[package]] name = "logos-blockchain-core" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "ark-ff 0.4.2", "bincode", @@ -5448,20 +5484,21 @@ dependencies = [ "rpds", "serde", "strum", - "thiserror 1.0.69", + "thiserror 2.0.18", + "time", "tracing", ] [[package]] name = "logos-blockchain-cryptarchia-engine" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "logos-blockchain-pol", "logos-blockchain-utils", "serde", "serde_with", - "thiserror 1.0.69", + "thiserror 2.0.18", "time", "tokio", "tracing", @@ -5470,7 +5507,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-sync" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "bytes", "futures", @@ -5481,7 +5518,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_with", - "thiserror 1.0.69", + "thiserror 2.0.18", "tokio", "tracing", ] @@ -5489,7 +5526,7 @@ dependencies = [ [[package]] name = "logos-blockchain-groth16" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -5507,7 +5544,7 @@ dependencies = [ [[package]] name = "logos-blockchain-http-api-common" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "axum 0.7.9", "logos-blockchain-core", @@ -5515,14 +5552,19 @@ dependencies = [ "logos-blockchain-tracing", "serde", "serde_json", + "serde_urlencoded", "serde_with", + "time", "tracing", + "url", + "utoipa", + "validator", ] [[package]] name = "logos-blockchain-key-management-system-keys" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "bytes", @@ -5548,7 +5590,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=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "proc-macro2", "quote", @@ -5558,7 +5600,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=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "logos-blockchain-blend-proofs", @@ -5574,7 +5616,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=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "log", @@ -5591,7 +5633,7 @@ dependencies = [ [[package]] name = "logos-blockchain-ledger" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "derivative", "logos-blockchain-blend-crypto", @@ -5610,14 +5652,14 @@ dependencies = [ "rpds", "serde", "serde_arrays", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", ] [[package]] name = "logos-blockchain-libp2p" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "backon", @@ -5628,6 +5670,7 @@ dependencies = [ "igd-next 0.16.2", "libp2p", "logos-blockchain-cryptarchia-sync", + "logos-blockchain-log-targets", "logos-blockchain-utils", "multiaddr", "natpmp", @@ -5636,16 +5679,34 @@ dependencies = [ "rand 0.8.5", "serde", "serde_with", - "thiserror 1.0.69", + "thiserror 2.0.18", "tokio", "tracing", "zerocopy", ] +[[package]] +name = "logos-blockchain-log-targets" +version = "0.1.2" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +dependencies = [ + "logos-blockchain-log-targets-macros", +] + +[[package]] +name = "logos-blockchain-log-targets-macros" +version = "0.1.2" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "logos-blockchain-mmr" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "ark-ff 0.4.2", "logos-blockchain-groth16", @@ -5658,13 +5719,14 @@ dependencies = [ [[package]] name = "logos-blockchain-network-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "futures", "logos-blockchain-core", "logos-blockchain-cryptarchia-sync", "logos-blockchain-libp2p", + "logos-blockchain-log-targets", "logos-blockchain-tracing", "overwatch", "rand 0.8.5", @@ -5678,47 +5740,48 @@ dependencies = [ [[package]] name = "logos-blockchain-poc" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", "logos-blockchain-groth16", + "logos-blockchain-proofs-error", "logos-blockchain-witness-generator", "num-bigint 0.4.6", "serde", "serde_json", - "thiserror 2.0.18", "tracing", ] [[package]] name = "logos-blockchain-pol" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "astro-float", "logos-blockchain-circuits-prover", "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", "serde_json", - "thiserror 2.0.18", "tracing", ] [[package]] name = "logos-blockchain-poq" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", "logos-blockchain-groth16", "logos-blockchain-pol", + "logos-blockchain-proofs-error", "logos-blockchain-witness-generator", "num-bigint 0.4.6", "serde", @@ -5730,7 +5793,7 @@ dependencies = [ [[package]] name = "logos-blockchain-poseidon2" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "ark-bn254 0.4.0", "ark-ff 0.4.2", @@ -5738,10 +5801,20 @@ dependencies = [ "num-bigint 0.4.6", ] +[[package]] +name = "logos-blockchain-proofs-error" +version = "0.1.2" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" +dependencies = [ + "logos-blockchain-groth16", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "logos-blockchain-services-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "futures", @@ -5749,24 +5822,25 @@ dependencies = [ "overwatch", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", ] [[package]] name = "logos-blockchain-storage-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "bytes", "futures", "logos-blockchain-core", "logos-blockchain-cryptarchia-engine", + "logos-blockchain-log-targets", "logos-blockchain-tracing", "overwatch", "serde", - "thiserror 1.0.69", + "thiserror 2.0.18", "tokio", "tracing", ] @@ -5774,12 +5848,13 @@ dependencies = [ [[package]] name = "logos-blockchain-time-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "futures", "log", "logos-blockchain-cryptarchia-engine", + "logos-blockchain-log-targets", "logos-blockchain-tracing", "logos-blockchain-utils", "overwatch", @@ -5796,8 +5871,10 @@ dependencies = [ [[package]] name = "logos-blockchain-tracing" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ + "flate2", + "logos-blockchain-log-targets", "opentelemetry", "opentelemetry-appender-tracing", "opentelemetry-http", @@ -5820,7 +5897,7 @@ dependencies = [ [[package]] name = "logos-blockchain-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "blake2", @@ -5831,13 +5908,15 @@ dependencies = [ "rand 0.8.5", "serde", "serde_with", + "serde_yaml", + "thiserror 2.0.18", "time", ] [[package]] name = "logos-blockchain-utxotree" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "ark-ff 0.4.2", "logos-blockchain-groth16", @@ -5845,13 +5924,13 @@ dependencies = [ "num-bigint 0.4.6", "rpds", "serde", - "thiserror 1.0.69", + "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=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "tempfile", ] @@ -5859,12 +5938,13 @@ dependencies = [ [[package]] name = "logos-blockchain-zksign" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", "logos-blockchain-groth16", "logos-blockchain-poseidon2", + "logos-blockchain-proofs-error", "logos-blockchain-witness-generator", "num-bigint 0.4.6", "serde", @@ -5876,13 +5956,15 @@ dependencies = [ [[package]] name = "logos-blockchain-zone-sdk" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b" dependencies = [ "async-trait", "futures", + "hex", "logos-blockchain-common-http-client", "logos-blockchain-core", "logos-blockchain-groth16", + "logos-blockchain-http-api-common", "logos-blockchain-key-management-system-service", "rand 0.8.5", "reqwest", @@ -6114,7 +6196,7 @@ dependencies = [ "bitflags 2.11.0", "block", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "log", "objc", "paste", @@ -6253,6 +6335,23 @@ dependencies = [ "unsigned-varint 0.7.2", ] +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "natpmp" version = "0.5.0" @@ -6460,6 +6559,7 @@ dependencies = [ "anyhow", "authenticated_transfer_core", "borsh", + "bridge_core", "clock_core", "env_logger", "faucet_core", @@ -6718,12 +6818,49 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-sys" +version = "0.9.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "opentelemetry" version = "0.31.0" @@ -7157,6 +7294,30 @@ dependencies = [ "toml_edit 0.25.4+spec-1.1.0", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -7241,6 +7402,7 @@ dependencies = [ "ata_core", "ata_program", "authenticated_transfer_core", + "bridge_core", "clock_core", "faucet_core", "nssa_core", @@ -7820,6 +7982,7 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", + "encoding_rs", "futures-core", "futures-util", "h2", @@ -7828,9 +7991,12 @@ dependencies = [ "http-body-util", "hyper", "hyper-rustls", + "hyper-tls", "hyper-util", "js-sys", "log", + "mime", + "native-tls", "percent-encoding", "pin-project-lite", "quinn", @@ -7841,6 +8007,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-util", "tower", @@ -8418,7 +8585,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8686,11 +8853,13 @@ version = "0.1.0" dependencies = [ "anyhow", "borsh", + "bridge_core", "bytesize", "chrono", "common", "faucet_core", "futures", + "hex", "humantime-serde", "log", "logos-blockchain-core", @@ -9249,6 +9418,12 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + [[package]] name = "syn" version = "1.0.109" @@ -9395,7 +9570,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -9700,6 +9875,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -9962,11 +10147,12 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" dependencies = [ "crossbeam-channel", + "symlink", "thiserror 2.0.18", "time", "tracing-subscriber 0.3.23", @@ -10380,6 +10566,30 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utoipa" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" +dependencies = [ + "indexmap 2.13.0", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c24e8ab68ff9ee746aad22d39b5535601e6416d1b0feeabf78be986a5c4392" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "uuid" version = "1.22.0" @@ -10391,6 +10601,36 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "validator" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa" +dependencies = [ + "idna", + "once_cell", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca" +dependencies = [ + "darling 0.20.11", + "once_cell", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "valuable" version = "0.1.1" @@ -10747,7 +10987,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]] diff --git a/Cargo.toml b/Cargo.toml index f4a981ad..ca9880ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "programs/associated_token_account", "programs/authenticated_transfer/core", "programs/faucet/core", + "programs/bridge/core", "programs/vault/core", "sequencer/core", "sequencer/service", @@ -75,6 +76,7 @@ ata_core = { path = "programs/associated_token_account/core" } ata_program = { path = "programs/associated_token_account" } authenticated_transfer_core = { path = "programs/authenticated_transfer/core" } faucet_core = { path = "programs/faucet/core" } +bridge_core = { path = "programs/bridge/core" } vault_core = { path = "programs/vault/core" } test_program_methods = { path = "test_program_methods" } testnet_initial_state = { path = "testnet_initial_state" } @@ -135,12 +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 = "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" } +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" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", diff --git a/artifacts/program_methods/amm.bin b/artifacts/program_methods/amm.bin index 685e01bb..d8749ada 100644 Binary files a/artifacts/program_methods/amm.bin and b/artifacts/program_methods/amm.bin differ diff --git a/artifacts/program_methods/associated_token_account.bin b/artifacts/program_methods/associated_token_account.bin index d67cd89d..3c2f0622 100644 Binary files a/artifacts/program_methods/associated_token_account.bin and b/artifacts/program_methods/associated_token_account.bin differ diff --git a/artifacts/program_methods/authenticated_transfer.bin b/artifacts/program_methods/authenticated_transfer.bin index 8a335adf..56feb4b9 100644 Binary files a/artifacts/program_methods/authenticated_transfer.bin and b/artifacts/program_methods/authenticated_transfer.bin differ diff --git a/artifacts/program_methods/bridge.bin b/artifacts/program_methods/bridge.bin index 58a6cf32..3eacbc9d 100644 Binary files a/artifacts/program_methods/bridge.bin and b/artifacts/program_methods/bridge.bin differ diff --git a/artifacts/program_methods/clock.bin b/artifacts/program_methods/clock.bin index f24484ee..b755c262 100644 Binary files a/artifacts/program_methods/clock.bin and b/artifacts/program_methods/clock.bin differ diff --git a/artifacts/program_methods/faucet.bin b/artifacts/program_methods/faucet.bin index 8f091358..2399f4f8 100644 Binary files a/artifacts/program_methods/faucet.bin and b/artifacts/program_methods/faucet.bin differ diff --git a/artifacts/program_methods/pinata.bin b/artifacts/program_methods/pinata.bin index 2a2ec7b5..0f305832 100644 Binary files a/artifacts/program_methods/pinata.bin and b/artifacts/program_methods/pinata.bin differ diff --git a/artifacts/program_methods/pinata_token.bin b/artifacts/program_methods/pinata_token.bin index 94f2598d..e3751f5f 100644 Binary files a/artifacts/program_methods/pinata_token.bin and b/artifacts/program_methods/pinata_token.bin differ diff --git a/artifacts/program_methods/privacy_preserving_circuit.bin b/artifacts/program_methods/privacy_preserving_circuit.bin index 5b52b624..99cfd7ee 100644 Binary files a/artifacts/program_methods/privacy_preserving_circuit.bin and b/artifacts/program_methods/privacy_preserving_circuit.bin differ diff --git a/artifacts/program_methods/token.bin b/artifacts/program_methods/token.bin index 6a904824..2a46dd89 100644 Binary files a/artifacts/program_methods/token.bin and b/artifacts/program_methods/token.bin differ diff --git a/artifacts/program_methods/vault.bin b/artifacts/program_methods/vault.bin index 4c5d875b..31629aa3 100644 Binary files a/artifacts/program_methods/vault.bin and b/artifacts/program_methods/vault.bin differ diff --git a/artifacts/test_program_methods/faucet_chain_caller.bin b/artifacts/test_program_methods/faucet_chain_caller.bin index 6df3f51c..1f1e3158 100644 Binary files a/artifacts/test_program_methods/faucet_chain_caller.bin and b/artifacts/test_program_methods/faucet_chain_caller.bin differ diff --git a/artifacts/test_program_methods/pda_fund_spend_proxy.bin b/artifacts/test_program_methods/pda_fund_spend_proxy.bin index c649bb1f..03e85c2e 100644 Binary files a/artifacts/test_program_methods/pda_fund_spend_proxy.bin and b/artifacts/test_program_methods/pda_fund_spend_proxy.bin differ diff --git a/bedrock/deployment-settings.yaml b/bedrock/deployment-settings.yaml index 7ef63f03..de58f12a 100644 --- a/bedrock/deployment-settings.yaml +++ b/bedrock/deployment-settings.yaml @@ -67,7 +67,9 @@ cryptarchia: - opcode: 17 payload: channel_id: '0000000000000000000000000000000000000000000000000000000000000000' - inscription: '67656e65736973' + # chain_id_len=12 (u64_le), chain_id=logos-devnet (utf-8), + # genesis_time=2026-01-10T07:47:56Z (u64_le), epoch_nonce=[0u8; 32] + inscription: '0c000000000000006c6f676f732d6465766e65742c046269000000000000000000000000000000000000000000000000000000000000000000000000' parent: '0000000000000000000000000000000000000000000000000000000000000000' signer: '0000000000000000000000000000000000000000000000000000000000000000' execution_gas_price: 0 diff --git a/bedrock/docker-compose.yml b/bedrock/docker-compose.yml index e16e505b..356c6df8 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:9f1829dea335c56f6ff68ae37ea872ed5313b96b69e8ffe143c02b7217de85fc + image: ghcr.io/logos-blockchain/logos-blockchain@sha256:f160cfbf898a06554451cc066d84cfd0f8ab62d59bd3e62d9cde3bd5582c12ab ports: - "${PORT:-8080}:18080/tcp" volumes: diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 21cbfd75..0015e9a9 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -67,7 +67,7 @@ impl NSSATransaction { } /// Validates the transaction against the current state and returns the resulting diff - /// without applying it. Rejects transactions that modify clock or faucet system accounts, + /// without applying it. Rejects transactions that modify clock, faucet or bridge accounts, /// whether directly or indirectly via chain calls. /// /// This check is required for all user transactions. Only sequencer transactions may bypass @@ -90,26 +90,12 @@ impl NSSATransaction { } }?; - let public_diff = diff.public_diff(); - let touches_clock = nssa::CLOCK_PROGRAM_ACCOUNT_IDS.iter().any(|id| { - public_diff - .get(id) - .is_some_and(|post| *post != state.get_account_by_id(*id)) - }); - if touches_clock { - return Err(nssa::error::NssaError::InvalidInput( - "Transaction modifies system clock accounts".into(), - )); - } - - let faucet_id = nssa::system_faucet_account_id(); - if public_diff - .get(&faucet_id) - .is_some_and(|post| *post != state.get_account_by_id(faucet_id)) - { - return Err(nssa::error::NssaError::InvalidInput( - "Transaction modifies system faucet account".into(), - )); + let system_accounts = nssa::CLOCK_PROGRAM_ACCOUNT_IDS.iter().copied().chain([ + nssa::system_faucet_account_id(), + nssa::system_bridge_account_id(), + ]); + for account_id in system_accounts { + validate_doesnt_modify_account(state, &diff, account_id)?; } Ok(diff) @@ -184,3 +170,21 @@ pub fn clock_invocation(timestamp: clock_core::Instruction) -> nssa::PublicTrans nssa::public_transaction::WitnessSet::from_raw_parts(vec![]), ) } + +fn validate_doesnt_modify_account( + state: &V03State, + diff: &ValidatedStateDiff, + account_id: AccountId, +) -> Result<(), nssa::error::NssaError> { + if diff + .public_diff() + .get(&account_id) + .is_some_and(|post| *post != state.get_account_by_id(account_id)) + { + Err(nssa::error::NssaError::InvalidInput(format!( + "Transaction modifies restricted system account {account_id}" + ))) + } else { + Ok(()) + } +} diff --git a/configs/docker-all-in-one/sequencer_config.json b/configs/docker-all-in-one/sequencer_config.json index 207f2e79..edb0132a 100644 --- a/configs/docker-all-in-one/sequencer_config.json +++ b/configs/docker-all-in-one/sequencer_config.json @@ -15,6 +15,11 @@ }, "indexer_rpc_url": "ws://indexer_service:8779", "genesis": [ + { + "supply_bridge_account": { + "balance": 1000000 + } + }, { "supply_account": { "account_id": "6iArKUXxhUJqS7kCaPNhwMWt3ro71PDyBj7jwAyE2VQV", diff --git a/indexer/core/src/block_store.rs b/indexer/core/src/block_store.rs index b66b778f..323f5ff3 100644 --- a/indexer/core/src/block_store.rs +++ b/indexer/core/src/block_store.rs @@ -6,7 +6,7 @@ use common::{ transaction::{NSSATransaction, clock_invocation}, }; use log::info; -use logos_blockchain_core::{header::HeaderId, mantle::ops::channel::MsgId}; +use logos_blockchain_core::header::HeaderId; use logos_blockchain_zone_sdk::Slot; use nssa::{Account, AccountId, V03State}; use nssa_core::BlockId; @@ -97,16 +97,16 @@ impl IndexerStore { Ok(self.dbio.calculate_state_for_id(block_id)?) } - pub fn get_zone_cursor(&self) -> Result> { + 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) + let cursor: 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<()> { + pub fn set_zone_cursor(&self, cursor: &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)?; diff --git a/indexer/core/src/lib.rs b/indexer/core/src/lib.rs index 400d0a9d..b0416905 100644 --- a/indexer/core/src/lib.rs +++ b/indexer/core/src/lib.rs @@ -81,8 +81,8 @@ impl IndexerCore { 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)) { + cursor = Some(slot); + if let Err(err) = self.store.set_zone_cursor(&slot) { warn!("Failed to persist indexer cursor: {err:#}"); } continue; @@ -98,8 +98,8 @@ impl IndexerCore { error!("Failed to store block {}: {err:#}", block.header.block_id); } - cursor = Some((zone_block.id, slot)); - if let Err(err) = self.store.set_zone_cursor(&(zone_block.id, slot)) { + cursor = Some(slot); + if let Err(err) = self.store.set_zone_cursor(&slot) { warn!("Failed to persist indexer cursor: {err:#}"); } yield Ok(block); diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index 82d8ebd1..0a0048bd 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -22,6 +22,7 @@ token_core.workspace = true ata_core.workspace = true vault_core.workspace = true faucet_core.workspace = true +bridge_core.workspace = true indexer_service_rpc = { workspace = true, features = ["client"] } sequencer_service_rpc = { workspace = true, features = ["client"] } wallet-ffi.workspace = true @@ -34,3 +35,7 @@ tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } hex.workspace = true tempfile.workspace = true bytesize.workspace = true +reqwest.workspace = true +borsh.workspace = true +logos-blockchain-http-api-common.workspace = true +logos-blockchain-core.workspace = true diff --git a/integration_tests/tests/auth_transfer/private.rs b/integration_tests/tests/auth_transfer/private.rs index a77ccf34..6a8eed82 100644 --- a/integration_tests/tests/auth_transfer/private.rs +++ b/integration_tests/tests/auth_transfer/private.rs @@ -7,8 +7,14 @@ use integration_tests::{ public_mention, verify_commitment_is_in_state, }; use log::info; -use nssa::{AccountId, program::Program}; -use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point}; +use nssa::{ + AccountId, SharedSecretKey, execute_and_prove, + privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, +}; +use nssa_core::{ + InputAccountIdentity, NullifierPublicKey, account::AccountWithMetadata, + encryption::shared_key_derivation::Secp256k1Point, +}; use sequencer_service_rpc::RpcClient as _; use tokio::test; use wallet::{ @@ -626,13 +632,7 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> { } #[test] -async fn ppt_that_chain_calls_faucet_is_dropped() -> Result<()> { - use nssa::{ - EphemeralPublicKey, SharedSecretKey, execute_and_prove, - privacy_preserving_transaction::{self, circuit::ProgramWithDependencies}, - }; - use nssa_core::{InputAccountIdentity, account::AccountWithMetadata}; - +async fn ppt_cant_chain_call_faucet() -> Result<()> { let ctx = TestContext::new().await?; let binary = std::fs::read( @@ -656,7 +656,6 @@ async fn ppt_that_chain_calls_faucet_is_dropped() -> Result<()> { let npk = NullifierPublicKey::from(&nsk); let vpk = Secp256k1Point::from_scalar([4; 32]); let ssk = SharedSecretKey::new([55; 32], &vpk); - let epk = EphemeralPublicKey::from_scalar([55; 32]); let attacker_vault_id = { let seed = vault_core::compute_vault_seed(attacker_id); AccountId::for_private_pda(&vault_program_id, &seed, &npk, 1337) @@ -695,7 +694,7 @@ async fn ppt_that_chain_calls_faucet_is_dropped() -> Result<()> { let instruction = Program::serialize_instruction((faucet_program_id, vault_program_id, attacker_id, amount))?; - let (output, proof) = execute_and_prove( + let res = execute_and_prove( vec![faucet_pre, vault_pda_pre], instruction, vec![ @@ -708,47 +707,9 @@ async fn ppt_that_chain_calls_faucet_is_dropped() -> Result<()> { }, ], &program_with_deps, - )?; + ); - let message = privacy_preserving_transaction::Message::try_from_circuit_output( - vec![faucet_account_id], - vec![], - vec![(npk, vpk, epk)], - output, - )?; - let witness_set = privacy_preserving_transaction::WitnessSet::for_message(&message, proof, &[]); - let attack_ppt = NSSATransaction::PrivacyPreserving(nssa::PrivacyPreservingTransaction::new( - message, - witness_set, - )); - - let faucet_balance_before = ctx - .sequencer_client() - .get_account_balance(faucet_account_id) - .await?; - let vault_balance_before = ctx - .sequencer_client() - .get_account_balance(attacker_vault_id) - .await?; - - let tx_hash = ctx.sequencer_client().send_transaction(attack_ppt).await?; - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let faucet_balance_after = ctx - .sequencer_client() - .get_account_balance(faucet_account_id) - .await?; - let vault_balance_after = ctx - .sequencer_client() - .get_account_balance(attacker_vault_id) - .await?; - let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?; - - assert_eq!(faucet_balance_after, faucet_balance_before); - assert_eq!(vault_balance_after, vault_balance_before); - assert!(tx_on_chain.is_none()); + assert!(res.is_err()); Ok(()) } diff --git a/integration_tests/tests/auth_transfer/public.rs b/integration_tests/tests/auth_transfer/public.rs index 72685d0b..69988d54 100644 --- a/integration_tests/tests/auth_transfer/public.rs +++ b/integration_tests/tests/auth_transfer/public.rs @@ -420,7 +420,7 @@ async fn cannot_execute_faucet_program() -> Result<()> { Program::faucet().id(), vec![faucet_account_id, recipient_vault_id], vec![], - faucet_core::Instruction::Transfer { + faucet_core::Instruction::GenesisTransferVault { vault_program_id, recipient_id: recipient, amount, diff --git a/integration_tests/tests/bridge.rs b/integration_tests/tests/bridge.rs new file mode 100644 index 00000000..7cb63b22 --- /dev/null +++ b/integration_tests/tests/bridge.rs @@ -0,0 +1,450 @@ +#![expect( + clippy::tests_outside_test_module, + clippy::arithmetic_side_effects, + reason = "We don't care about these in tests" +)] + +use std::time::Duration; + +use anyhow::Context as _; +use borsh::BorshSerialize; +use common::transaction::NSSATransaction; +use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext}; +use log::info; +use logos_blockchain_core::mantle::{Value, ledger::Inputs, ops::channel::deposit::DepositOp}; +use logos_blockchain_http_api_common::bodies::{ + channel::ChannelDepositRequestBody, + wallet::{ + balance::WalletBalanceResponseBody, + transfer_funds::{WalletTransferFundsRequestBody, WalletTransferFundsResponseBody}, + }, +}; +use nssa::{ + AccountId, execute_and_prove, privacy_preserving_transaction, program::Program, + public_transaction, +}; +use nssa_core::{InputAccountIdentity, account::AccountWithMetadata}; +use sequencer_service_rpc::RpcClient as _; +use tokio::test; + +const TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK: Duration = Duration::from_mins(2); + +#[test] +async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> { + let ctx = TestContext::new().await?; + + let recipient_id = ctx.existing_public_accounts()[0]; + let bridge_account_id = nssa::system_bridge_account_id(); + let vault_program_id = Program::vault().id(); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id); + + let message = public_transaction::Message::try_new( + Program::bridge().id(), + vec![bridge_account_id, recipient_vault_id], + vec![], + bridge_core::Instruction::Deposit { + vault_program_id, + recipient_id, + amount: 1, + }, + ) + .context("Failed to build public bridge deposit transaction")?; + + let attack_tx = NSSATransaction::Public(nssa::PublicTransaction::new( + message, + nssa::public_transaction::WitnessSet::from_raw_parts(vec![]), + )); + + let bridge_balance_before = ctx + .sequencer_client() + .get_account_balance(bridge_account_id) + .await?; + let vault_balance_before = ctx + .sequencer_client() + .get_account_balance(recipient_vault_id) + .await?; + + let tx_hash = ctx.sequencer_client().send_transaction(attack_tx).await?; + + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let bridge_balance_after = ctx + .sequencer_client() + .get_account_balance(bridge_account_id) + .await?; + let vault_balance_after = ctx + .sequencer_client() + .get_account_balance(recipient_vault_id) + .await?; + let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?; + + assert_eq!(bridge_balance_after, bridge_balance_before); + assert_eq!(vault_balance_after, vault_balance_before); + assert!( + tx_on_chain.is_none(), + "Direct public bridge::Deposit invocation should be rejected" + ); + + Ok(()) +} + +#[test] +async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> { + let ctx = TestContext::new().await?; + + let recipient_id = ctx.existing_public_accounts()[0]; + let bridge_account_id = nssa::system_bridge_account_id(); + let vault_program_id = Program::vault().id(); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id); + + // Get pre-state of bridge and vault accounts + let bridge_pre = AccountWithMetadata::new( + ctx.sequencer_client() + .get_account(bridge_account_id) + .await?, + false, + bridge_account_id, + ); + let vault_pre = AccountWithMetadata::new( + ctx.sequencer_client() + .get_account(recipient_vault_id) + .await?, + false, + recipient_vault_id, + ); + + // Create program with dependencies + let program_with_deps = + nssa::privacy_preserving_transaction::circuit::ProgramWithDependencies::new( + Program::bridge(), + [ + (vault_program_id, Program::vault()), + ( + Program::authenticated_transfer_program().id(), + Program::authenticated_transfer_program(), + ), + ] + .into(), + ); + + // Serialize the bridge deposit instruction + let instruction = Program::serialize_instruction(bridge_core::Instruction::Deposit { + vault_program_id, + recipient_id, + amount: 1, + }) + .context("Failed to serialize bridge deposit instruction")?; + + // Execute and prove the bridge deposit + let (output, proof) = execute_and_prove( + vec![bridge_pre.clone(), vault_pre.clone()], + instruction, + vec![InputAccountIdentity::Public, InputAccountIdentity::Public], + &program_with_deps, + ) + .context("Failed to execute/prove bridge deposit")?; + + // Create privacy-preserving transaction from circuit output + let message = privacy_preserving_transaction::Message::try_from_circuit_output( + vec![bridge_account_id, recipient_vault_id], + vec![bridge_pre.account.nonce, vault_pre.account.nonce], + vec![], + output, + ) + .context("Failed to build privacy-preserving bridge deposit message")?; + + let witness_set = privacy_preserving_transaction::WitnessSet::for_message(&message, proof, &[]); + let attack_tx = NSSATransaction::PrivacyPreserving(nssa::PrivacyPreservingTransaction::new( + message, + witness_set, + )); + + let bridge_balance_before = ctx + .sequencer_client() + .get_account_balance(bridge_account_id) + .await?; + let vault_balance_before = ctx + .sequencer_client() + .get_account_balance(recipient_vault_id) + .await?; + + let tx_hash = ctx.sequencer_client().send_transaction(attack_tx).await?; + + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let bridge_balance_after = ctx + .sequencer_client() + .get_account_balance(bridge_account_id) + .await?; + let vault_balance_after = ctx + .sequencer_client() + .get_account_balance(recipient_vault_id) + .await?; + let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?; + + assert_eq!(bridge_balance_after, bridge_balance_before); + assert_eq!(vault_balance_after, vault_balance_before); + assert!( + tx_on_chain.is_none(), + "Privacy-preserving bridge::Deposit invocation should be rejected" + ); + + Ok(()) +} + +async fn submit_bedrock_deposit( + bedrock_addr: std::net::SocketAddr, + recipient_id: AccountId, + amount: u128, +) -> anyhow::Result<()> { + #[derive(BorshSerialize)] + struct DepositMetadata { + recipient_id: AccountId, + } + + // Encode deposit metadata + let metadata = borsh::to_vec(&DepositMetadata { recipient_id }) + .context("Failed to encode deposit metadata")?; + + let funding_key = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26"; + + let amount: Value = amount + .try_into() + .context("Deposit amount does not fit Bedrock Value type")?; + let channel_id = integration_tests::config::bedrock_channel_id(); + let client = reqwest::Client::new(); + + let query_balance = || async { + let balance_response = client + .get(format!( + "http://{bedrock_addr}/wallet/{funding_key}/balance" + )) + .send() + .await + .context("Failed to query Bedrock wallet balance")?; + + let balance_response = check_response_success(balance_response).await?; + + balance_response + .json::() + .await + .context("Failed to decode Bedrock balance response") + }; + + let mut balance = query_balance().await?; + + info!( + "Queried Bedrock balance for key {funding_key}: {:?}", + balance.balance + ); + + if balance.balance < amount { + anyhow::bail!( + "Bedrock wallet with key {funding_key} has insufficient balance {:?} for deposit amount {:?}", + balance.balance, + amount + ); + } + + let mut selected_note_id = balance + .notes + .iter() + .find_map(|(note_id, value)| (*value == amount).then_some(*note_id)); + + if selected_note_id.is_none() { + let transfer_body = WalletTransferFundsRequestBody { + tip: None, + change_public_key: balance.address, + funding_public_keys: vec![balance.address], + recipient_public_key: balance.address, + amount, + }; + + let transfer_response = client + .post(format!( + "http://{bedrock_addr}/wallet/transactions/transfer-funds" + )) + .json(&transfer_body) + .send() + .await + .context("Failed to submit Bedrock transfer-funds request")?; + let transfer_response = check_response_success(transfer_response).await?; + + let transfer: WalletTransferFundsResponseBody = transfer_response + .json() + .await + .context("Failed to decode Bedrock transfer-funds response")?; + + info!( + "Submitted transfer-funds to create exact deposit note, tx hash {:?}", + transfer.hash + ); + + let mut found_note = None; + for _ in 0..20 { + tokio::time::sleep(Duration::from_millis(500)).await; + balance = query_balance().await?; + found_note = balance + .notes + .iter() + .find_map(|(note_id, value)| (*value == amount).then_some(*note_id)); + if found_note.is_some() { + break; + } + } + + selected_note_id = found_note; + } + + let Some(selected_note_id) = selected_note_id else { + anyhow::bail!( + "Failed to locate exact-value note {amount:?} for Bedrock deposit; available notes: {:?}", + balance.notes, + ); + }; + + let body = ChannelDepositRequestBody { + tip: None, + deposit: DepositOp { + channel_id, + inputs: Inputs::new(vec![selected_note_id]), + metadata, + }, + change_public_key: balance.address, + funding_public_keys: vec![balance.address], + max_tx_fee: 1_000_u64.into(), + }; + + let response = client + .post(format!("http://{bedrock_addr}/channel/deposit")) + .json(&body) + .send() + .await + .context("Failed to submit Bedrock deposit request")?; + let response = check_response_success(response).await?; + + let body_text = response + .text() + .await + .unwrap_or_else(|_| "".to_owned()); + info!( + "Successfully submitted Bedrock deposit request for recipient {recipient_id} and amount {amount}, response body: {body_text}", + ); + + Ok(()) +} + +async fn check_response_success(response: reqwest::Response) -> anyhow::Result { + if response.status().is_success() { + Ok(response) + } else { + let status = response.status(); + let body_text = response.text().await.unwrap_or_default(); + anyhow::bail!("Request failed with status {status} and body {body_text}"); + } +} + +async fn wait_for_vault_balance( + ctx: &TestContext, + vault_id: AccountId, + expected_balance: u128, +) -> anyhow::Result<()> { + let timeout = TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK + + Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS); + tokio::time::timeout(timeout, async { + loop { + let balance = ctx.sequencer_client().get_account_balance(vault_id).await?; + if balance == expected_balance { + return Ok(()); + } + tokio::time::sleep(Duration::from_millis(500)).await; + } + }) + .await + .with_context(|| { + format!("Timed out waiting for vault {vault_id} balance to reach {expected_balance}") + })? +} + +#[test] +async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result<()> { + let ctx = TestContext::new().await?; + + let recipient_id = ctx.existing_public_accounts()[0]; + let vault_program_id = Program::vault().id(); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id); + + let vault_balance_before = ctx + .sequencer_client() + .get_account_balance(recipient_vault_id) + .await?; + let recipient_balance_before = ctx + .sequencer_client() + .get_account_balance(recipient_id) + .await?; + + // Submit deposit to Bedrock + submit_bedrock_deposit(ctx.bedrock_addr(), recipient_id, 1).await?; + + // Wait for vault to receive the deposit (minted from bridge to vault) + wait_for_vault_balance(&ctx, recipient_vault_id, vault_balance_before + 1).await?; + + // Now claim funds from vault back to recipient + let nonces = ctx + .wallet() + .get_accounts_nonces(vec![recipient_id]) + .await + .context("Failed to get nonce for vault claim")?; + + let signing_key = ctx + .wallet() + .storage() + .key_chain() + .pub_account_signing_key(recipient_id) + .with_context(|| format!("Missing signing key for account {recipient_id}"))?; + + let claim_message = public_transaction::Message::try_new( + vault_program_id, + vec![recipient_id, recipient_vault_id], + nonces, + vault_core::Instruction::Claim { amount: 1 }, + ) + .context("Failed to build vault claim message")?; + + let claim_witness_set = + public_transaction::WitnessSet::for_message(&claim_message, &[signing_key]); + let claim_tx = NSSATransaction::Public(nssa::PublicTransaction::new( + claim_message, + claim_witness_set, + )); + + let claim_hash = ctx.sequencer_client().send_transaction(claim_tx).await?; + + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let claim_on_chain = ctx.sequencer_client().get_transaction(claim_hash).await?; + let vault_balance_after_claim = ctx + .sequencer_client() + .get_account_balance(recipient_vault_id) + .await?; + let recipient_balance_after_claim = ctx + .sequencer_client() + .get_account_balance(recipient_id) + .await?; + + assert!( + claim_on_chain.is_some(), + "Vault claim transaction must be included on-chain" + ); + assert_eq!( + vault_balance_after_claim, vault_balance_before, + "Vault balance should return to initial state after claim" + ); + assert_eq!( + recipient_balance_after_claim, + recipient_balance_before + 1, + "Recipient balance should increase by claimed amount" + ); + + Ok(()) +} diff --git a/integration_tests/tests/indexer.rs b/integration_tests/tests/indexer.rs index 5cf33cde..e3b3bc7b 100644 --- a/integration_tests/tests/indexer.rs +++ b/integration_tests/tests/indexer.rs @@ -20,13 +20,12 @@ use wallet::{ }; /// Maximum time to wait for the indexer to catch up to the sequencer. -const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000; +const L2_TO_L1_TIMEOUT: Duration = Duration::from_mins(6); /// Poll the indexer until its last finalized block id reaches the sequencer's /// current last block id or until [`L2_TO_L1_TIMEOUT_MILLIS`] elapses. /// Returns the last indexer block id observed. async fn wait_for_indexer_to_catch_up(ctx: &TestContext) -> Result { - let timeout = Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS); let block_id_to_catch_up = sequencer_service_rpc::RpcClient::get_last_block_id(ctx.sequencer_client()).await?; let mut last_ind: u64 = 1; @@ -50,11 +49,11 @@ async fn wait_for_indexer_to_catch_up(ctx: &TestContext) -> Result { tokio::time::sleep(Duration::from_secs(2)).await; } }; - tokio::time::timeout(timeout, inner) + tokio::time::timeout(L2_TO_L1_TIMEOUT, inner) .await .with_context(|| { format!( - "Indexer failed to catch up within {L2_TO_L1_TIMEOUT_MILLIS} milliseconds. Last indexer block id observed: {last_ind}, but needed to catch up to at least {block_id_to_catch_up}" + "Indexer failed to catch up within {L2_TO_L1_TIMEOUT:?}. Last indexer block id observed: {last_ind}, but needed to catch up to at least {block_id_to_catch_up}" ) })? } diff --git a/integration_tests/tests/indexer_ffi.rs b/integration_tests/tests/indexer_ffi.rs index 178b2640..8088b6cb 100644 --- a/integration_tests/tests/indexer_ffi.rs +++ b/integration_tests/tests/indexer_ffi.rs @@ -10,6 +10,7 @@ use std::{ fs::File, io::Write as _, net::SocketAddr, + time::Duration, }; use anyhow::{Context as _, Result}; @@ -34,7 +35,7 @@ use wallet::{ }; /// Maximum time to wait for the indexer to catch up to the sequencer. -const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000; +const L2_TO_L1_TIMEOUT: Duration = Duration::from_mins(6); unsafe extern "C" { unsafe fn query_last_block( @@ -114,7 +115,7 @@ fn indexer_test_run_ffi() -> Result<()> { let (ctx, indexer_ffi, _indexer_dir) = setup()?; // RUN OBSERVATION - std::thread::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)); + std::thread::sleep(L2_TO_L1_TIMEOUT); // Safety: ctx runtime is valid for the lifetime of the returned Runtime let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) }; @@ -138,7 +139,7 @@ fn indexer_ffi_block_batching() -> Result<()> { // WAIT info!("Waiting for indexer to parse blocks"); - std::thread::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)); + std::thread::sleep(L2_TO_L1_TIMEOUT); // Safety: ctx runtime is valid for the lifetime of the returned Runtime let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) }; @@ -265,7 +266,7 @@ fn indexer_ffi_state_consistency() -> Result<()> { // WAIT info!("Waiting for indexer to parse blocks"); - std::thread::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)); + std::thread::sleep(L2_TO_L1_TIMEOUT); // Safety: ctx runtime is valid for the lifetime of the returned Runtime let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) }; @@ -371,7 +372,7 @@ fn indexer_ffi_state_consistency_with_labels() -> Result<()> { assert_eq!(acc_2_balance, 20100); info!("Waiting for indexer to parse blocks"); - std::thread::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)); + std::thread::sleep(L2_TO_L1_TIMEOUT); // Safety: ctx runtime is valid for the lifetime of the returned Runtime let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) }; diff --git a/mempool/src/lib.rs b/mempool/src/lib.rs index 3bf4ac2a..1b36eaf7 100644 --- a/mempool/src/lib.rs +++ b/mempool/src/lib.rs @@ -48,6 +48,14 @@ pub struct MemPoolHandle { sender: Sender, } +impl Clone for MemPoolHandle { + fn clone(&self) -> Self { + Self { + sender: self.sender.clone(), + } + } +} + impl MemPoolHandle { const fn new(sender: Sender) -> Self { Self { sender } diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 80542f16..ee9b8bb6 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -11,6 +11,7 @@ workspace = true nssa_core = { workspace = true, features = ["host"] } clock_core.workspace = true faucet_core.workspace = true +bridge_core.workspace = true anyhow.workspace = true thiserror.workspace = true diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index 5998e803..125bf7ee 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -18,7 +18,7 @@ pub use public_transaction::PublicTransaction; pub use signature::{PrivateKey, PublicKey, Signature}; pub use state::{ CLOCK_01_PROGRAM_ACCOUNT_ID, CLOCK_10_PROGRAM_ACCOUNT_ID, CLOCK_50_PROGRAM_ACCOUNT_ID, - CLOCK_PROGRAM_ACCOUNT_IDS, V03State, system_faucet_account_id, + CLOCK_PROGRAM_ACCOUNT_IDS, V03State, system_bridge_account_id, system_faucet_account_id, }; pub use validated_state_diff::ValidatedStateDiff; diff --git a/nssa/src/program.rs b/nssa/src/program.rs index a3d47563..c624af3b 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -10,8 +10,9 @@ use crate::{ error::NssaError, program_methods::{ AMM_ELF, AMM_ID, ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID, - AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, FAUCET_ELF, - FAUCET_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, VAULT_ID, + AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, BRIDGE_ELF, BRIDGE_ID, CLOCK_ELF, + CLOCK_ID, FAUCET_ELF, FAUCET_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, + VAULT_ID, }, }; @@ -164,6 +165,14 @@ impl Program { elf: FAUCET_ELF.to_vec(), } } + + #[must_use] + pub fn bridge() -> Self { + Self { + id: BRIDGE_ID, + elf: BRIDGE_ELF.to_vec(), + } + } } // TODO: Testnet only. Refactor to prevent compilation on mainnet. @@ -194,9 +203,9 @@ mod tests { program::Program, program_methods::{ AMM_ELF, AMM_ID, ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID, - AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, FAUCET_ELF, - FAUCET_ID, PINATA_ELF, PINATA_ID, PINATA_TOKEN_ELF, PINATA_TOKEN_ID, TOKEN_ELF, - TOKEN_ID, VAULT_ELF, VAULT_ID, + AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, BRIDGE_ELF, BRIDGE_ID, + CLOCK_ELF, CLOCK_ID, FAUCET_ELF, FAUCET_ID, PINATA_ELF, PINATA_ID, PINATA_TOKEN_ELF, + PINATA_TOKEN_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, VAULT_ID, }, }; @@ -529,6 +538,7 @@ mod tests { let token_program = Program::token(); let vault_program = Program::vault(); let faucet_program = Program::faucet(); + let bridge_program = Program::bridge(); let pinata_program = Program::pinata(); assert_eq!(auth_transfer_program.id, AUTHENTICATED_TRANSFER_ID); @@ -539,6 +549,8 @@ mod tests { assert_eq!(vault_program.elf, VAULT_ELF); assert_eq!(faucet_program.id, FAUCET_ID); assert_eq!(faucet_program.elf, FAUCET_ELF); + assert_eq!(bridge_program.id, BRIDGE_ID); + assert_eq!(bridge_program.elf, BRIDGE_ELF); assert_eq!(pinata_program.id, PINATA_ID); assert_eq!(pinata_program.elf, PINATA_ELF); } @@ -551,6 +563,7 @@ mod tests { (ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID), (CLOCK_ELF, CLOCK_ID), (FAUCET_ELF, FAUCET_ID), + (BRIDGE_ELF, BRIDGE_ID), (PINATA_ELF, PINATA_ID), (PINATA_TOKEN_ELF, PINATA_TOKEN_ID), (TOKEN_ELF, TOKEN_ID), diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 0f38f9f3..25297c99 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -126,8 +126,11 @@ impl Default for V03State { fn default() -> Self { let faucet_account_id = system_faucet_account_id(); let faucet_account = system_faucet_account(); + let bridge_account_id = system_bridge_account_id(); + let bridge_account = system_bridge_account(); let mut public_state = HashMap::new(); public_state.insert(faucet_account_id, faucet_account); + public_state.insert(bridge_account_id, bridge_account); Self { public_state, @@ -150,6 +153,7 @@ impl V03State { genesis_timestamp: nssa_core::Timestamp, ) -> Self { let faucet_account_id = system_faucet_account_id(); + let bridge_account_id = system_bridge_account_id(); let authenticated_transfer_program = Program::authenticated_transfer_program(); let mut public_state: HashMap<_, _> = initial_data .iter() @@ -164,7 +168,9 @@ impl V03State { }) .collect(); let faucet_account = system_faucet_account(); + let bridge_account = system_bridge_account(); public_state.insert(faucet_account_id, faucet_account); + public_state.insert(bridge_account_id, bridge_account); let mut commitment_set = CommitmentSet::with_capacity(32); commitment_set.extend(&[DUMMY_COMMITMENT]); @@ -190,6 +196,7 @@ impl V03State { this.insert_program(Program::ata()); this.insert_program(Program::vault()); this.insert_program(Program::faucet()); + this.insert_program(Program::bridge()); this } @@ -384,11 +391,23 @@ fn system_faucet_account() -> Account { } } +fn system_bridge_account() -> Account { + Account { + program_owner: Program::authenticated_transfer_program().id(), + ..Account::default() + } +} + #[must_use] pub fn system_faucet_account_id() -> AccountId { faucet_core::compute_faucet_account_id(Program::faucet().id()) } +#[must_use] +pub fn system_bridge_account_id() -> AccountId { + bridge_core::compute_bridge_account_id(Program::bridge().id()) +} + #[cfg(test)] pub mod tests { #![expect( @@ -426,9 +445,10 @@ pub mod tests { signature::PrivateKey, state::{ CLOCK_01_PROGRAM_ACCOUNT_ID, CLOCK_10_PROGRAM_ACCOUNT_ID, CLOCK_50_PROGRAM_ACCOUNT_ID, - CLOCK_PROGRAM_ACCOUNT_IDS, MAX_NUMBER_CHAINED_CALLS, system_faucet_account, + CLOCK_PROGRAM_ACCOUNT_IDS, MAX_NUMBER_CHAINED_CALLS, system_bridge_account, + system_faucet_account, }, - system_faucet_account_id, + system_bridge_account_id, system_faucet_account_id, }; impl V03State { @@ -622,6 +642,7 @@ pub mod tests { }, ); this.insert(system_faucet_account_id(), system_faucet_account()); + this.insert(system_bridge_account_id(), system_bridge_account()); for account_id in CLOCK_PROGRAM_ACCOUNT_IDS { this.insert( account_id, @@ -646,6 +667,7 @@ pub mod tests { this.insert(Program::ata().id(), Program::ata()); this.insert(Program::vault().id(), Program::vault()); this.insert(Program::faucet().id(), Program::faucet()); + this.insert(Program::bridge().id(), Program::bridge()); this }; diff --git a/program_methods/guest/Cargo.toml b/program_methods/guest/Cargo.toml index 136fb0b8..e60fcc60 100644 --- a/program_methods/guest/Cargo.toml +++ b/program_methods/guest/Cargo.toml @@ -18,6 +18,7 @@ amm_program.workspace = true ata_core.workspace = true ata_program.workspace = true faucet_core.workspace = true +bridge_core.workspace = true vault_core.workspace = true risc0-zkvm.workspace = true serde = { workspace = true, default-features = false } diff --git a/program_methods/guest/src/bin/bridge.rs b/program_methods/guest/src/bin/bridge.rs new file mode 100644 index 00000000..0833d5aa --- /dev/null +++ b/program_methods/guest/src/bin/bridge.rs @@ -0,0 +1,82 @@ +use bridge_core::Instruction; +use nssa_core::program::{ + AccountPostState, ChainedCall, ProgramInput, ProgramOutput, read_nssa_inputs, +}; + +fn unchanged_post_states( + pre_states: &[nssa_core::account::AccountWithMetadata], +) -> Vec { + pre_states + .iter() + .map(|pre_state| AccountPostState::new(pre_state.account.clone())) + .collect() +} + +fn main() { + let ( + ProgramInput { + self_program_id, + caller_program_id, + pre_states, + instruction, + }, + instruction_words, + ) = read_nssa_inputs::(); + + assert!( + caller_program_id.is_none(), + "Bridge cannot be invoked through chain calls" + ); + + let pre_states_clone = pre_states.clone(); + let post_states = unchanged_post_states(&pre_states_clone); + + let chained_calls = match instruction { + Instruction::Deposit { + vault_program_id, + recipient_id, + amount, + } => { + let [bridge, recipient_vault] = pre_states + .try_into() + .expect("Deposit requires exactly 2 accounts"); + + assert_eq!( + bridge.account_id, + bridge_core::compute_bridge_account_id(self_program_id), + "First account must be bridge PDA" + ); + + assert_eq!( + recipient_vault.account_id, + vault_core::compute_vault_account_id(vault_program_id, recipient_id), + "Second account must be recipient vault PDA" + ); + + let mut bridge_for_vault = bridge; + bridge_for_vault.is_authorized = true; + + vec![ + ChainedCall::new( + vault_program_id, + vec![bridge_for_vault, recipient_vault], + &vault_core::Instruction::Transfer { + recipient_id, + amount, + }, + ) + .with_pda_seeds(vec![bridge_core::compute_bridge_seed()]), + ] + } + }; + + ProgramOutput::new( + self_program_id, + caller_program_id, + instruction_words, + pre_states_clone, + post_states, + ) + .with_chained_calls(chained_calls) + .write(); +} diff --git a/program_methods/guest/src/bin/faucet.rs b/program_methods/guest/src/bin/faucet.rs index e56330cd..0026ab0b 100644 --- a/program_methods/guest/src/bin/faucet.rs +++ b/program_methods/guest/src/bin/faucet.rs @@ -23,11 +23,16 @@ fn main() { instruction_words, ) = read_nssa_inputs::(); + assert!( + caller_program_id.is_none(), + "Faucet cannot be invoked through chain calls" + ); + let pre_states_clone = pre_states.clone(); let post_states = unchanged_post_states(&pre_states_clone); let chained_calls = match instruction { - Instruction::Transfer { + Instruction::GenesisTransferVault { vault_program_id, recipient_id, amount, @@ -57,6 +62,29 @@ fn main() { .with_pda_seeds(vec![faucet_core::compute_faucet_seed()]), ] } + Instruction::GenesisTransferDirect { amount } => { + let [faucet, recipient] = pre_states + .try_into() + .expect("TransferDirect requires exactly 2 accounts"); + + assert_eq!( + faucet.account_id, + faucet_core::compute_faucet_account_id(self_program_id), + "First account must be faucet PDA" + ); + + let mut faucet_for_transfer = faucet; + faucet_for_transfer.is_authorized = true; + + vec![ + ChainedCall::new( + faucet_for_transfer.account.program_owner, + vec![faucet_for_transfer, recipient], + &authenticated_transfer_core::Instruction::Transfer { amount }, + ) + .with_pda_seeds(vec![faucet_core::compute_faucet_seed()]), + ] + } }; ProgramOutput::new( diff --git a/programs/bridge/core/Cargo.toml b/programs/bridge/core/Cargo.toml new file mode 100644 index 00000000..683f2115 --- /dev/null +++ b/programs/bridge/core/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "bridge_core" +version = "0.1.0" +edition = "2024" +license = { workspace = true } + +[lints] +workspace = true + +[dependencies] +nssa_core.workspace = true +serde = { workspace = true, default-features = false } diff --git a/programs/bridge/core/src/lib.rs b/programs/bridge/core/src/lib.rs new file mode 100644 index 00000000..1da75f97 --- /dev/null +++ b/programs/bridge/core/src/lib.rs @@ -0,0 +1,29 @@ +pub use nssa_core::program::PdaSeed; +use nssa_core::{account::AccountId, program::ProgramId}; +use serde::{Deserialize, Serialize}; + +const BRIDGE_SEED_DOMAIN_SEPARATOR: [u8; 32] = *b"/LEZ/v0.3/BridgeSeed/0000000000/"; + +#[derive(Serialize, Deserialize)] +pub enum Instruction { + /// Transfers native tokens from the bridge PDA account to a recipient vault. + /// + /// Required accounts (2): + /// - Bridge PDA account + /// - Recipient vault PDA account + Deposit { + vault_program_id: ProgramId, + recipient_id: AccountId, + amount: u128, + }, +} + +#[must_use] +pub const fn compute_bridge_seed() -> PdaSeed { + PdaSeed::new(BRIDGE_SEED_DOMAIN_SEPARATOR) +} + +#[must_use] +pub fn compute_bridge_account_id(bridge_program_id: ProgramId) -> AccountId { + AccountId::for_public_pda(&bridge_program_id, &compute_bridge_seed()) +} diff --git a/programs/faucet/core/src/lib.rs b/programs/faucet/core/src/lib.rs index da9861e6..4d90d5e5 100644 --- a/programs/faucet/core/src/lib.rs +++ b/programs/faucet/core/src/lib.rs @@ -8,14 +8,25 @@ const FAUCET_SEED_DOMAIN_SEPARATOR: [u8; 32] = *b"/LEZ/v0.3/FaucetSeed/000000000 pub enum Instruction { /// Transfers native tokens from system faucet to recipient's vault. /// + /// Executed only in genesis block by sequencer it-self. User transactions will be denied. + /// /// Required accounts (2): /// - Faucet PDA account /// - Recipient vault PDA account - Transfer { + GenesisTransferVault { vault_program_id: ProgramId, recipient_id: AccountId, amount: u128, }, + + /// Transfers native tokens from system faucet directly to a recipient account. + /// + /// Executed only in genesis block by sequencer it-self. User transactions will be denied. + /// + /// Required accounts (2): + /// - Faucet PDA account + /// - Recipient account + GenesisTransferDirect { amount: u128 }, } #[must_use] diff --git a/sequencer/core/Cargo.toml b/sequencer/core/Cargo.toml index 5f74fbde..985fc969 100644 --- a/sequencer/core/Cargo.toml +++ b/sequencer/core/Cargo.toml @@ -16,6 +16,7 @@ mempool.workspace = true logos-blockchain-zone-sdk.workspace = true testnet_initial_state.workspace = true faucet_core.workspace = true +bridge_core.workspace = true vault_core.workspace = true anyhow.workspace = true @@ -31,6 +32,7 @@ logos-blockchain-core.workspace = true rand.workspace = true borsh.workspace = true bytesize.workspace = true +hex.workspace = true url.workspace = true [features] diff --git a/sequencer/core/src/block_publisher.rs b/sequencer/core/src/block_publisher.rs index 9f4c8235..62cd7259 100644 --- a/sequencer/core/src/block_publisher.rs +++ b/sequencer/core/src/block_publisher.rs @@ -1,6 +1,6 @@ -use std::{sync::Arc, time::Duration}; +use std::{pin::Pin, sync::Arc, time::Duration}; -use anyhow::{Context as _, Result, anyhow}; +use anyhow::{Context as _, Result}; use common::block::Block; use log::warn; pub use logos_blockchain_core::mantle::ops::channel::MsgId; @@ -10,7 +10,7 @@ use logos_blockchain_zone_sdk::{ CommonHttpClient, adapter::NodeHttpClient, sequencer::{Event, SequencerConfig as ZoneSdkSequencerConfig, SequencerHandle, ZoneSequencer}, - state::InscriptionInfo, + state::{DepositInfo, FinalizedOp, InscriptionInfo}, }; use tokio::task::JoinHandle; @@ -18,12 +18,16 @@ 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; +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; +pub type FinalizedBlockSink = Box; + +/// Sink for finalized Bedrock deposit events. +pub type OnDepositEventSink = + Box Pin + Send>> + Send + 'static>; #[expect(async_fn_in_trait, reason = "We don't care about Send/Sync here")] pub trait BlockPublisherTrait: Clone { @@ -34,6 +38,7 @@ pub trait BlockPublisherTrait: Clone { initial_checkpoint: Option, on_checkpoint: CheckpointSink, on_finalized_block: FinalizedBlockSink, + on_deposit_event: OnDepositEventSink, ) -> Result; /// Fire-and-forget publish. Zone-sdk drives the actual submission and @@ -65,6 +70,7 @@ impl BlockPublisherTrait for ZoneSdkPublisher { initial_checkpoint: Option, on_checkpoint: CheckpointSink, on_finalized_block: FinalizedBlockSink, + on_deposit_event: OnDepositEventSink, ) -> Result { let basic_auth = config.auth.clone().map(Into::into); let node = NodeHttpClient::new(CommonHttpClient::new(basic_auth), config.node_url.clone()); @@ -89,10 +95,20 @@ impl BlockPublisherTrait for ZoneSdkPublisher { }; 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::TxsFinalized { items } => { + for op in items.into_iter().flat_map(|item| item.ops) { + match op { + FinalizedOp::Inscription(inscription) => { + if let Some(block_id) = block_id_from_inscription(&inscription) + { + on_finalized_block(block_id); + } + } + FinalizedOp::Deposit(deposit) => { + on_deposit_event(deposit).await; + } + FinalizedOp::Withdraw(_) => {} + } } } Event::ChannelUpdate { .. } | Event::Ready => {} @@ -110,27 +126,26 @@ impl BlockPublisherTrait for ZoneSdkPublisher { async fn publish_block(&self, block: &Block) -> Result<()> { let data = borsh::to_vec(block).context("Failed to serialize block")?; + let data_bounded = data + .try_into() + .context("Block data exceeds maximum allowed size")?; + self.handle - .publish_message(data) + .publish_message(data_bounded) .await - .map_err(|e| anyhow!("zone-sdk publish failed: {e}"))?; + .context("Failed to publish block")?; + 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() +/// Deserialize inscription payload as a `Block` and return it's`block_id`. +/// Bad payloads are logged and skipped. +fn block_id_from_inscription(inscription: &InscriptionInfo) -> Option { + borsh::from_slice::(&inscription.payload) + .inspect_err(|err| { + warn!("Failed to deserialize block from inscription: {err:?}"); + }) + .ok() + .map(|block| block.header.block_id) } diff --git a/sequencer/core/src/config.rs b/sequencer/core/src/config.rs index 371ebc89..6609f58c 100644 --- a/sequencer/core/src/config.rs +++ b/sequencer/core/src/config.rs @@ -22,6 +22,9 @@ pub enum GenesisAction { account_id: AccountId, balance: u128, }, + SupplyBridgeAccount { + balance: u128, + }, } // TODO: Provide default values diff --git a/sequencer/core/src/lib.rs b/sequencer/core/src/lib.rs index c6606145..13b455df 100644 --- a/sequencer/core/src/lib.rs +++ b/sequencer/core/src/lib.rs @@ -1,6 +1,7 @@ use std::{path::Path, time::Instant}; use anyhow::{Context as _, Result, anyhow}; +use borsh::BorshDeserialize; use common::{ HashType, block::{BedrockStatus, Block, HashableBlockData}, @@ -28,10 +29,29 @@ pub mod config; #[cfg(feature = "mock")] pub mod mock; +/// The origin of a transaction. +pub enum TransactionOrigin { + /// Basic transactions submitted by users via RPC. + User, + /// Transactions generated by the sequencer itself. + Sequencer, +} + +#[derive(Clone, Debug, BorshDeserialize)] +struct DepositMetadata { + recipient_id: nssa::AccountId, +} + +impl DepositMetadata { + fn decode(bytes: &[u8]) -> Result { + Self::try_from_slice(bytes) + } +} + pub struct SequencerCore { state: nssa::V03State, store: SequencerStore, - mempool: MemPool, + mempool: MemPool<(TransactionOrigin, NSSATransaction)>, sequencer_config: SequencerConfig, chain_height: u64, block_publisher: BP, @@ -45,7 +65,7 @@ impl SequencerCore { /// initializing its state with the accounts defined in the configuration file. pub async fn start_from_config( config: SequencerConfig, - ) -> (Self, MemPoolHandle) { + ) -> (Self, MemPoolHandle<(TransactionOrigin, NSSATransaction)>) { let signing_key = nssa::PrivateKey::try_new(config.signing_key).unwrap(); let bedrock_signing_key = @@ -132,6 +152,37 @@ impl SequencerCore { } }); + let (mempool, mempool_handle) = MemPool::new(config.mempool_max_size); + + let mempool_handle_for_deposit = mempool_handle.clone(); + let on_deposit_event: block_publisher::OnDepositEventSink = Box::new(move |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) { + Ok(tx) => tx, + Err(err) => { + warn!( + "Skipping finalized Bedrock deposit event due to tx build failure: {err:#}" + ); + return; + } + }; + + if let Err(err) = mempool_handle_for_deposit + .push((TransactionOrigin::Sequencer, tx)) + .await + { + error!( + "Failed to queue sequencer transaction built from finalized Bedrock event: {err:#}" + ); + } + }) + }); + let block_publisher = BP::new( &config.bedrock_config, bedrock_signing_key, @@ -139,6 +190,7 @@ impl SequencerCore { initial_checkpoint, on_checkpoint, on_finalized_block, + on_deposit_event, ) .await .expect("Failed to initialize Block Publisher"); @@ -151,8 +203,6 @@ impl SequencerCore { error!("Failed to publish genesis block: {err:#}"); } - let (mempool, mempool_handle) = MemPool::new(config.mempool_max_size); - let sequencer_core = Self { state, store, @@ -207,7 +257,7 @@ impl SequencerCore { let clock_tx = clock_invocation(new_block_timestamp); let clock_nssa_tx = NSSATransaction::Public(clock_tx.clone()); - while let Some(tx) = self.mempool.pop() { + while let Some((origin, tx)) = self.mempool.pop() { let tx_hash = tx.hash(); // Check if block size exceeds limit (including the mandatory clock tx). @@ -235,25 +285,41 @@ impl SequencerCore { block size {block_size} bytes would exceed limit of {max_block_size} bytes", ); - self.mempool.push_front(tx); + self.mempool.push_front((origin, tx)); break; } - let validated_diff = match tx.validate_on_state( - &self.state, - new_block_height, - new_block_timestamp, - ) { - Ok(diff) => diff, - Err(err) => { - error!( - "Transaction with hash {tx_hash} failed execution check with error: {err:#?}, skipping it", - ); - continue; - } - }; + match origin { + TransactionOrigin::User => { + let validated_diff = match tx.validate_on_state( + &self.state, + new_block_height, + new_block_timestamp, + ) { + Ok(diff) => diff, + Err(err) => { + error!( + "Transaction with hash {tx_hash} failed execution check with error: {err:#?}, skipping it", + ); + continue; + } + }; - self.state.apply_state_diff(validated_diff); + self.state.apply_state_diff(validated_diff); + } + TransactionOrigin::Sequencer => { + let NSSATransaction::Public(public_tx) = &tx else { + panic!("Sequencer may only generate Public transactions, found {tx:#?}"); + }; + self.state + .transition_from_public_transaction( + public_tx, + new_block_height, + new_block_timestamp, + ) + .context("Failed to execute sequencer-generated transaction")?; + } + } valid_transactions.push(tx); info!("Validated transaction with hash {tx_hash}, including it in block"); @@ -363,6 +429,9 @@ fn build_genesis_state(config: &SequencerConfig) -> (nssa::V03State, Vec build_supply_account_genesis_transaction(account_id, *balance), + GenesisAction::SupplyBridgeAccount { balance } => { + build_supply_bridge_account_genesis_transaction(*balance) + } }) .chain(std::iter::once(clock_invocation(0))) .inspect(|tx| { @@ -388,7 +457,7 @@ fn build_supply_account_genesis_transaction( faucet_program_id, vec![nssa::system_faucet_account_id(), recipient_vault_id], vec![], - faucet_core::Instruction::Transfer { + faucet_core::Instruction::GenesisTransferVault { vault_program_id, recipient_id: *account_id, amount: balance, @@ -400,6 +469,52 @@ fn build_supply_account_genesis_transaction( PublicTransaction::new(message, witness_set) } +fn build_supply_bridge_account_genesis_transaction(balance: u128) -> PublicTransaction { + let faucet_program_id = Program::faucet().id(); + let bridge_account_id = nssa::system_bridge_account_id(); + + let message = Message::try_new( + faucet_program_id, + vec![nssa::system_faucet_account_id(), bridge_account_id], + vec![], + faucet_core::Instruction::GenesisTransferDirect { amount: balance }, + ) + .expect("Failed to serialize bridge genesis transfer instruction"); + let witness_set = nssa::public_transaction::WitnessSet::from_raw_parts(vec![]); + + PublicTransaction::new(message, witness_set) +} + +fn build_bridge_deposit_tx( + deposit: &logos_blockchain_zone_sdk::state::DepositInfo, +) -> Result { + let metadata = DepositMetadata::decode(&deposit.metadata) + .context("Failed to decode finalized Bedrock deposit metadata")?; + + let bridge_program_id = Program::bridge().id(); + let vault_program_id = Program::vault().id(); + let recipient_vault_id = + vault_core::compute_vault_account_id(vault_program_id, metadata.recipient_id); + + let message = Message::try_new( + bridge_program_id, + vec![nssa::system_bridge_account_id(), recipient_vault_id], + vec![], + bridge_core::Instruction::Deposit { + vault_program_id, + recipient_id: metadata.recipient_id, + amount: u128::from(deposit.amount), + }, + ) + .context("Failed to build bridge deposit message")?; + + let witness_set = nssa::public_transaction::WitnessSet::from_raw_parts(vec![]); + Ok(NSSATransaction::Public(PublicTransaction::new( + message, + witness_set, + ))) +} + /// Load signing key from file or generate a new one if it doesn't exist. fn load_or_create_signing_key(path: &Path) -> Result { if path.exists() { @@ -441,6 +556,7 @@ mod tests { use testnet_initial_state::{initial_accounts, initial_pub_accounts_private_keys}; use crate::{ + TransactionOrigin, block_store::SequencerStore, build_genesis_state, config::{BedrockConfig, SequencerConfig}, @@ -476,19 +592,28 @@ mod tests { initial_pub_accounts_private_keys()[1].pub_sign_key.clone() } - async fn common_setup() -> (SequencerCoreWithMockClients, MemPoolHandle) { + async fn common_setup() -> ( + SequencerCoreWithMockClients, + MemPoolHandle<(TransactionOrigin, NSSATransaction)>, + ) { let config = setup_sequencer_config(); common_setup_with_config(config).await } async fn common_setup_with_config( config: SequencerConfig, - ) -> (SequencerCoreWithMockClients, MemPoolHandle) { + ) -> ( + SequencerCoreWithMockClients, + MemPoolHandle<(TransactionOrigin, NSSATransaction)>, + ) { let (mut sequencer, mempool_handle) = SequencerCoreWithMockClients::start_from_config(config).await; let tx = common::test_utils::produce_dummy_empty_transaction(); - mempool_handle.push(tx).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx)) + .await + .unwrap(); sequencer.produce_new_block().await.unwrap(); @@ -678,10 +803,13 @@ mod tests { let tx = common::test_utils::produce_dummy_empty_transaction(); // Fill the mempool - mempool_handle.push(tx.clone()).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx.clone())) + .await + .unwrap(); // Check that pushing another transaction will block - let mut push_fut = pin!(mempool_handle.push(tx.clone())); + let mut push_fut = pin!(mempool_handle.push((TransactionOrigin::User, tx.clone()))); let poll = futures::poll!(push_fut.as_mut()); assert!(poll.is_pending()); @@ -698,7 +826,10 @@ mod tests { let genesis_height = sequencer.chain_height; let tx = common::test_utils::produce_dummy_empty_transaction(); - mempool_handle.push(tx).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx)) + .await + .unwrap(); let result = sequencer.build_block_from_mempool(); assert!(result.is_ok()); @@ -721,8 +852,14 @@ mod tests { let tx_original = tx.clone(); let tx_replay = tx.clone(); // Pushing two copies of the same tx to the mempool - mempool_handle.push(tx_original).await.unwrap(); - mempool_handle.push(tx_replay).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx_original)) + .await + .unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx_replay)) + .await + .unwrap(); // Create block sequencer.produce_new_block().await.unwrap(); @@ -756,7 +893,10 @@ mod tests { ); // The transaction should be included the first time - mempool_handle.push(tx.clone()).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx.clone())) + .await + .unwrap(); sequencer.produce_new_block().await.unwrap(); let block = sequencer .store @@ -772,7 +912,10 @@ mod tests { ); // Add same transaction should fail - mempool_handle.push(tx.clone()).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx.clone())) + .await + .unwrap(); sequencer.produce_new_block().await.unwrap(); let block = sequencer .store @@ -811,7 +954,10 @@ mod tests { &signing_key, ); - mempool_handle.push(tx.clone()).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx.clone())) + .await + .unwrap(); sequencer.produce_new_block().await.unwrap(); let block = sequencer .store @@ -895,7 +1041,10 @@ mod tests { &signing_key, ); - mempool_handle.push(tx).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx)) + .await + .unwrap(); sequencer.produce_new_block().await.unwrap(); // Get the metadata of the last block produced @@ -916,7 +1065,10 @@ mod tests { &signing_key, ); - mempool_handle.push(tx.clone()).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx.clone())) + .await + .unwrap(); // Step 4: Produce new block sequencer.produce_new_block().await.unwrap(); @@ -962,10 +1114,16 @@ mod tests { )) }; mempool_handle - .push(NSSATransaction::Public(clock_invocation(0))) + .push(( + TransactionOrigin::User, + NSSATransaction::Public(clock_invocation(0)), + )) + .await + .unwrap(); + mempool_handle + .push((TransactionOrigin::User, crafted_clock_tx)) .await .unwrap(); - mempool_handle.push(crafted_clock_tx).await.unwrap(); sequencer.produce_new_block().await.unwrap(); let block = sequencer @@ -994,7 +1152,10 @@ mod tests { test_program_methods::CLOCK_CHAIN_CALLER_ELF.to_vec(), ), )); - mempool_handle.push(deploy_tx).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, deploy_tx)) + .await + .unwrap(); sequencer.produce_new_block().await.unwrap(); // Build a user transaction that invokes clock_chain_caller, which in turn chain-calls the @@ -1019,7 +1180,10 @@ mod tests { nssa::public_transaction::WitnessSet::from_raw_parts(vec![]), )); - mempool_handle.push(user_tx).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, user_tx)) + .await + .unwrap(); sequencer.produce_new_block().await.unwrap(); let block = sequencer @@ -1051,7 +1215,10 @@ mod tests { // Push a dummy transaction so the mempool is non-empty. let tx = common::test_utils::produce_dummy_empty_transaction(); - mempool_handle.push(tx).await.unwrap(); + mempool_handle + .push((TransactionOrigin::User, tx)) + .await + .unwrap(); // Block production must fail because the appended clock tx cannot execute. let result = sequencer.produce_new_block().await; diff --git a/sequencer/core/src/mock.rs b/sequencer/core/src/mock.rs index ebe6ea5d..e041269a 100644 --- a/sequencer/core/src/mock.rs +++ b/sequencer/core/src/mock.rs @@ -6,7 +6,8 @@ use logos_blockchain_key_management_system_service::keys::Ed25519Key; use crate::{ block_publisher::{ - BlockPublisherTrait, CheckpointSink, FinalizedBlockSink, SequencerCheckpoint, + BlockPublisherTrait, CheckpointSink, FinalizedBlockSink, OnDepositEventSink, + SequencerCheckpoint, }, config::BedrockConfig, }; @@ -24,6 +25,7 @@ impl BlockPublisherTrait for MockBlockPublisher { _initial_checkpoint: Option, _on_checkpoint: CheckpointSink, _on_finalized_block: FinalizedBlockSink, + _on_deposit_event: OnDepositEventSink, ) -> Result { Ok(Self) } diff --git a/sequencer/service/configs/debug/sequencer_config.json b/sequencer/service/configs/debug/sequencer_config.json index bfe963ae..359c84f4 100644 --- a/sequencer/service/configs/debug/sequencer_config.json +++ b/sequencer/service/configs/debug/sequencer_config.json @@ -15,6 +15,11 @@ }, "indexer_rpc_url": "ws://localhost:8779", "genesis": [ + { + "supply_bridge_account": { + "balance": 1000000 + } + }, { "supply_account": { "account_id": "CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r", diff --git a/sequencer/service/configs/docker/sequencer_config.json b/sequencer/service/configs/docker/sequencer_config.json index c9d0e6a6..69238fe7 100644 --- a/sequencer/service/configs/docker/sequencer_config.json +++ b/sequencer/service/configs/docker/sequencer_config.json @@ -15,6 +15,11 @@ }, "indexer_rpc_url": "ws://localhost:8779", "genesis": [ + { + "supply_bridge_account": { + "balance": 1000000 + } + }, { "supply_account": { "account_id": "CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r", diff --git a/sequencer/service/src/lib.rs b/sequencer/service/src/lib.rs index 319b75ad..416822ac 100644 --- a/sequencer/service/src/lib.rs +++ b/sequencer/service/src/lib.rs @@ -11,6 +11,7 @@ use mempool::MemPoolHandle; use sequencer_core::SequencerCore; #[cfg(feature = "standalone")] use sequencer_core::SequencerCoreWithMockClients as SequencerCore; +use sequencer_core::TransactionOrigin; pub use sequencer_core::config::*; use sequencer_service_rpc::RpcServer as _; use tokio::{sync::Mutex, task::JoinHandle}; @@ -55,15 +56,14 @@ impl SequencerHandle { } = &mut self; let server_handle = server_handle.take().expect("Server handle is set"); - tokio::select! { () = server_handle.stopped() => { Err(anyhow!("RPC Server stopped")) } res = main_loop_handle => { res - .context("Main loop task panicked")? - .context("Main loop exited unexpectedly") + .context("Main loop task panicked")? + .context("Main loop exited unexpectedly") } } } @@ -120,10 +120,11 @@ pub async fn run(config: SequencerConfig, port: u16) -> Result info!("Sequencer core set up"); let seq_core_wrapped = Arc::new(Mutex::new(sequencer_core)); + let mempool_handle_for_server = mempool_handle.clone(); let (server_handle, addr) = run_server( Arc::clone(&seq_core_wrapped), - mempool_handle, + mempool_handle_for_server, port, max_block_size.as_u64(), ) @@ -133,12 +134,14 @@ pub async fn run(config: SequencerConfig, port: u16) -> Result info!("Starting main sequencer loop"); let main_loop_handle = tokio::spawn(main_loop(seq_core_wrapped, block_timeout)); + let _ = mempool_handle; + Ok(SequencerHandle::new(addr, server_handle, main_loop_handle)) } async fn run_server( sequencer: Arc>, - mempool_handle: MemPoolHandle, + mempool_handle: MemPoolHandle<(TransactionOrigin, NSSATransaction)>, port: u16, max_block_size: u64, ) -> Result<(ServerHandle, SocketAddr)> { diff --git a/sequencer/service/src/service.rs b/sequencer/service/src/service.rs index 0bb8e1dd..4a478d56 100644 --- a/sequencer/service/src/service.rs +++ b/sequencer/service/src/service.rs @@ -8,7 +8,9 @@ use jsonrpsee::{ use log::warn; use mempool::MemPoolHandle; use nssa::{self, program::Program}; -use sequencer_core::{DbError, SequencerCore, block_publisher::BlockPublisherTrait}; +use sequencer_core::{ + DbError, SequencerCore, TransactionOrigin, block_publisher::BlockPublisherTrait, +}; use sequencer_service_protocol::{ Account, AccountId, Block, BlockId, Commitment, HashType, MembershipProof, Nonce, ProgramId, }; @@ -18,14 +20,14 @@ const NOT_FOUND_ERROR_CODE: i32 = -31999; pub struct SequencerService { sequencer: Arc>>, - mempool_handle: MemPoolHandle, + mempool_handle: MemPoolHandle<(TransactionOrigin, NSSATransaction)>, max_block_size: u64, } impl SequencerService { pub const fn new( sequencer: Arc>>, - mempool_handle: MemPoolHandle, + mempool_handle: MemPoolHandle<(TransactionOrigin, NSSATransaction)>, max_block_size: u64, ) -> Self { Self { @@ -72,7 +74,7 @@ impl sequencer_service_rpc::RpcServer })?; self.mempool_handle - .push(authenticated_tx) + .push((TransactionOrigin::User, authenticated_tx)) .await .expect("Mempool is closed, this is a bug"); diff --git a/test_fixtures/src/config.rs b/test_fixtures/src/config.rs index 00bdc74a..3119a195 100644 --- a/test_fixtures/src/config.rs +++ b/test_fixtures/src/config.rs @@ -143,7 +143,12 @@ pub fn genesis_from_accounts( balance: account.balance, }); - public_genesis.chain(private_genesis).collect() + let supply_bridge_account = GenesisAction::SupplyBridgeAccount { balance: 1_000_000 }; + + public_genesis + .chain(private_genesis) + .chain(std::iter::once(supply_bridge_account)) + .collect() } pub fn wallet_config(sequencer_addr: SocketAddr) -> Result { @@ -184,7 +189,8 @@ pub fn addr_to_url(protocol: UrlProtocol, addr: SocketAddr) -> Result { url_string.parse().map_err(Into::into) } -fn bedrock_channel_id() -> ChannelId { +#[must_use] +pub fn bedrock_channel_id() -> ChannelId { let channel_id: [u8; 32] = [0_u8, 1] .repeat(16) .try_into() diff --git a/test_program_methods/guest/src/bin/faucet_chain_caller.rs b/test_program_methods/guest/src/bin/faucet_chain_caller.rs index 2e02982d..965abe1d 100644 --- a/test_program_methods/guest/src/bin/faucet_chain_caller.rs +++ b/test_program_methods/guest/src/bin/faucet_chain_caller.rs @@ -30,7 +30,7 @@ fn main() { let chained_calls = vec![ChainedCall { program_id: faucet_program_id, - instruction_data: to_vec(&faucet_core::Instruction::Transfer { + instruction_data: to_vec(&faucet_core::Instruction::GenesisTransferVault { vault_program_id, recipient_id, amount,