diff --git a/.deny.toml b/.deny.toml index 94b820f5..5f3a9161 100644 --- a/.deny.toml +++ b/.deny.toml @@ -59,6 +59,8 @@ allow-git = [ "https://github.com/EspressoSystems/jellyfish.git", "https://github.com/logos-blockchain/logos-blockchain.git", "https://github.com/logos-blockchain/logos-blockchain-circuits.git", + "https://github.com/logos-blockchain/logos-blockchain-rust-rapidsnark.git", + "https://github.com/arkworks-rs/spongefish.git" ] unknown-git = "deny" unknown-registry = "deny" diff --git a/.github/actions/install-logos-blockchain-circuits/action.yaml b/.github/actions/install-logos-blockchain-circuits/action.yaml index 9356ac5c..361ace65 100644 --- a/.github/actions/install-logos-blockchain-circuits/action.yaml +++ b/.github/actions/install-logos-blockchain-circuits/action.yaml @@ -16,4 +16,4 @@ runs: env: GITHUB_TOKEN: ${{ inputs.github-token }} run: | - curl -sSL https://raw.githubusercontent.com/logos-blockchain/logos-blockchain/6ac348bea4160ca708b70a86b3964e9f1ce82fff/scripts/setup-logos-blockchain-circuits.sh | bash + curl -sSL https://raw.githubusercontent.com/logos-blockchain/logos-blockchain/6ac348bea4160ca708b70a86b3964e9f1ce82fff/scripts/setup-logos-blockchain-circuits.sh | bash \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49ceaab9..40a0a84c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,10 +87,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-logos-blockchain-circuits - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install active toolchain run: rustup install @@ -122,10 +118,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-logos-blockchain-circuits - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install active toolchain run: rustup install @@ -157,10 +149,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-logos-blockchain-circuits - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install active toolchain run: rustup install @@ -221,10 +209,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-logos-blockchain-circuits - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install active toolchain run: rustup install @@ -254,10 +238,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-logos-blockchain-circuits - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install active toolchain run: rustup install diff --git a/Cargo.lock b/Cargo.lock index e0c8ea2c..d3898e49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -236,45 +236,16 @@ dependencies = [ "triomphe", ] -[[package]] -name = "ark-bn254" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" -dependencies = [ - "ark-ec 0.4.2", - "ark-ff 0.4.2", - "ark-std 0.4.0", -] - [[package]] name = "ark-bn254" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" dependencies = [ - "ark-ec 0.5.0", - "ark-ff 0.5.0", + "ark-ec", + "ark-ff", "ark-r1cs-std", - "ark-std 0.5.0", -] - -[[package]] -name = "ark-crypto-primitives" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" -dependencies = [ - "ark-ec 0.4.2", - "ark-ff 0.4.2", - "ark-relations 0.4.0", - "ark-serialize 0.4.2", - "ark-snark 0.4.0", - "ark-std 0.4.0", - "blake2", - "derivative", - "digest 0.10.7", - "sha2", + "ark-std", ] [[package]] @@ -285,12 +256,12 @@ checksum = "1e0c292754729c8a190e50414fd1a37093c786c709899f29c9f7daccecfa855e" dependencies = [ "ahash", "ark-crypto-primitives-macros", - "ark-ec 0.5.0", - "ark-ff 0.5.0", - "ark-relations 0.5.1", - "ark-serialize 0.5.0", - "ark-snark 0.5.1", - "ark-std 0.5.0", + "ark-ec", + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-snark", + "ark-std", "blake2", "derivative", "digest 0.10.7", @@ -310,23 +281,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "ark-ec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" -dependencies = [ - "ark-ff 0.4.2", - "ark-poly 0.4.2", - "ark-serialize 0.4.2", - "ark-std 0.4.0", - "derivative", - "hashbrown 0.13.2", - "itertools 0.10.5", - "num-traits", - "zeroize", -] - [[package]] name = "ark-ec" version = "0.5.0" @@ -334,10 +288,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ "ahash", - "ark-ff 0.5.0", - "ark-poly 0.5.0", - "ark-serialize 0.5.0", - "ark-std 0.5.0", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", "educe", "fnv", "hashbrown 0.15.5", @@ -348,36 +302,16 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ark-ff" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" -dependencies = [ - "ark-ff-asm 0.4.2", - "ark-ff-macros 0.4.2", - "ark-serialize 0.4.2", - "ark-std 0.4.0", - "derivative", - "digest 0.10.7", - "itertools 0.10.5", - "num-bigint 0.4.6", - "num-traits", - "paste", - "rustc_version", - "zeroize", -] - [[package]] name = "ark-ff" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" dependencies = [ - "ark-ff-asm 0.5.0", - "ark-ff-macros 0.5.0", - "ark-serialize 0.5.0", - "ark-std 0.5.0", + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", "arrayvec", "digest 0.10.7", "educe", @@ -388,16 +322,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ark-ff-asm" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "ark-ff-asm" version = "0.5.0" @@ -408,19 +332,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "ark-ff-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" -dependencies = [ - "num-bigint 0.4.6", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "ark-ff-macros" version = "0.5.0" @@ -434,47 +345,19 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "ark-groth16" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" -dependencies = [ - "ark-crypto-primitives 0.4.0", - "ark-ec 0.4.2", - "ark-ff 0.4.2", - "ark-poly 0.4.2", - "ark-relations 0.4.0", - "ark-serialize 0.4.2", - "ark-std 0.4.0", -] - [[package]] name = "ark-groth16" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88f1d0f3a534bb54188b8dcc104307db6c56cdae574ddc3212aec0625740fc7e" dependencies = [ - "ark-crypto-primitives 0.5.0", - "ark-ec 0.5.0", - "ark-ff 0.5.0", - "ark-poly 0.5.0", - "ark-relations 0.5.1", - "ark-serialize 0.5.0", - "ark-std 0.5.0", -] - -[[package]] -name = "ark-poly" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" -dependencies = [ - "ark-ff 0.4.2", - "ark-serialize 0.4.2", - "ark-std 0.4.0", - "derivative", - "hashbrown 0.13.2", + "ark-crypto-primitives", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-relations", + "ark-serialize", + "ark-std", ] [[package]] @@ -484,9 +367,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ "ahash", - "ark-ff 0.5.0", - "ark-serialize 0.5.0", - "ark-std 0.5.0", + "ark-ff", + "ark-serialize", + "ark-std", "educe", "fnv", "hashbrown 0.15.5", @@ -498,10 +381,10 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" dependencies = [ - "ark-ec 0.5.0", - "ark-ff 0.5.0", - "ark-relations 0.5.1", - "ark-std 0.5.0", + "ark-ec", + "ark-ff", + "ark-relations", + "ark-std", "educe", "num-bigint 0.4.6", "num-integer", @@ -509,66 +392,31 @@ dependencies = [ "tracing", ] -[[package]] -name = "ark-relations" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" -dependencies = [ - "ark-ff 0.4.2", - "ark-std 0.4.0", - "tracing", - "tracing-subscriber 0.2.25", -] - [[package]] name = "ark-relations" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" dependencies = [ - "ark-ff 0.5.0", - "ark-std 0.5.0", + "ark-ff", + "ark-std", "tracing", "tracing-subscriber 0.2.25", ] -[[package]] -name = "ark-serialize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" -dependencies = [ - "ark-serialize-derive 0.4.2", - "ark-std 0.4.0", - "digest 0.10.7", - "num-bigint 0.4.6", -] - [[package]] name = "ark-serialize" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ - "ark-serialize-derive 0.5.0", - "ark-std 0.5.0", + "ark-serialize-derive", + "ark-std", "arrayvec", "digest 0.10.7", "num-bigint 0.4.6", ] -[[package]] -name = "ark-serialize-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "ark-serialize-derive" version = "0.5.0" @@ -580,38 +428,16 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "ark-snark" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" -dependencies = [ - "ark-ff 0.4.2", - "ark-relations 0.4.0", - "ark-serialize 0.4.2", - "ark-std 0.4.0", -] - [[package]] name = "ark-snark" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d368e2848c2d4c129ce7679a7d0d2d612b6a274d3ea6a13bad4445d61b381b88" dependencies = [ - "ark-ff 0.5.0", - "ark-relations 0.5.1", - "ark-serialize 0.5.0", - "ark-std 0.5.0", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "num-traits", - "rand 0.8.6", + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-std", ] [[package]] @@ -1496,6 +1322,15 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "chkstk_stub" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "047f6ab2f3b9bcaf23b593d1580898e4244d27eadf1a1fae99212ee5735d3d1c" +dependencies = [ + "cc", +] + [[package]] name = "chrono" version = "0.4.44" @@ -2203,7 +2038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] @@ -2707,7 +2542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3309,15 +3144,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -4091,15 +3917,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - [[package]] name = "inout" version = "0.1.4" @@ -4149,6 +3966,7 @@ dependencies = [ "bytesize", "common", "faucet_core", + "futures", "hex", "indexer_ffi", "indexer_service_protocol", @@ -4159,6 +3977,9 @@ dependencies = [ "log", "logos-blockchain-core", "logos-blockchain-http-api-common", + "logos-blockchain-key-management-system-service", + "logos-blockchain-zone-sdk", + "num-bigint 0.4.6", "reqwest", "sequencer_core", "sequencer_service_rpc", @@ -4256,26 +4077,26 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jf-crhf" -version = "0.1.1" -source = "git+https://github.com/EspressoSystems/jellyfish?tag=jf-crhf-v0.1.1#8f3dce0bc2bd161b4648f6ac029dcc1a23aaf4c5" +version = "0.2.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=jf-crhf-v0.2.0#f1538793f7f0e391495cb17bbb0c8703ec5f689d" dependencies = [ - "ark-serialize 0.4.2", - "ark-std 0.4.0", + "ark-serialize", + "ark-std", ] [[package]] name = "jf-poseidon2" -version = "0.1.0" -source = "git+https://github.com/EspressoSystems/jellyfish.git?rev=dc166cf0f803c3e5067f9dfcc21e3dade986a447#dc166cf0f803c3e5067f9dfcc21e3dade986a447" +version = "0.2.0" +source = "git+https://github.com/EspressoSystems/jellyfish.git?rev=8d80230358e900f8d63765a937f63f4978ca1daa#8d80230358e900f8d63765a937f63f4978ca1daa" dependencies = [ - "ark-bn254 0.4.0", - "ark-ff 0.4.2", - "ark-std 0.4.0", + "ark-bn254", + "ark-ff", + "ark-std", "displaydoc", "hex", "jf-crhf", "lazy_static", - "nimue", + "spongefish", "zeroize", ] @@ -5427,8 +5248,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47a1ccadd0bb5a32c196da536fd72c59183de24a055f6bf0513bf845fefab862" dependencies = [ - "ark-bn254 0.5.0", - "ark-ff 0.5.0", + "ark-bn254", + "ark-ff", "num-bigint 0.4.6", "thiserror 1.0.69", ] @@ -5463,7 +5284,7 @@ checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f" [[package]] name = "logos-blockchain-blend-crypto" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "blake2", "logos-blockchain-groth16", @@ -5477,7 +5298,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-message" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "blake2", "derivative", @@ -5486,12 +5307,12 @@ dependencies = [ "logos-blockchain-blend-crypto", "logos-blockchain-blend-proofs", "logos-blockchain-core", + "logos-blockchain-cryptarchia-engine", "logos-blockchain-groth16", "logos-blockchain-key-management-system-keys", "logos-blockchain-log-targets", "logos-blockchain-utils", "serde", - "serde-big-array", "serde_with", "thiserror 2.0.18", "tracing", @@ -5501,7 +5322,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-proofs" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "ed25519-dalek", "generic-array 1.4.3", @@ -5521,23 +5342,21 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-broadcast-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "derivative", - "futures", "logos-blockchain-core", "overwatch", "serde", "tokio", - "tokio-stream", "tracing", ] [[package]] name = "logos-blockchain-chain-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "bytes", @@ -5558,7 +5377,6 @@ dependencies = [ "overwatch", "serde", "serde_with", - "strum", "thiserror 2.0.18", "time", "tokio", @@ -5568,8 +5386,8 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-build" -version = "0.5.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +version = "0.5.3" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" dependencies = [ "dirs", "fd-lock", @@ -5580,16 +5398,16 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-common" -version = "0.5.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +version = "0.5.3" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" dependencies = [ "logos-blockchain-circuits-types", ] [[package]] name = "logos-blockchain-circuits-poc-sys" -version = "0.5.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +version = "0.5.3" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" dependencies = [ "logos-blockchain-circuits-build", "logos-blockchain-circuits-common", @@ -5598,8 +5416,8 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-pol-sys" -version = "0.5.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +version = "0.5.3" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" dependencies = [ "logos-blockchain-circuits-build", "logos-blockchain-circuits-common", @@ -5608,8 +5426,8 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-poq-sys" -version = "0.5.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +version = "0.5.3" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" dependencies = [ "logos-blockchain-circuits-build", "logos-blockchain-circuits-common", @@ -5619,16 +5437,15 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-prover" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ - "logos-blockchain-circuits-utils", - "tempfile", + "rust-rapidsnark", ] [[package]] name = "logos-blockchain-circuits-signature-sys" -version = "0.5.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +version = "0.5.3" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" dependencies = [ "logos-blockchain-circuits-build", "logos-blockchain-circuits-common", @@ -5637,25 +5454,17 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-types" -version = "0.5.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?rev=2e79ac30831d89e6a349720c08d5b8b9978970e0#2e79ac30831d89e6a349720c08d5b8b9978970e0" +version = "0.5.3" +source = "git+https://github.com/logos-blockchain/logos-blockchain-circuits.git?tag=v0.5.3#127626881faa975aa8e9868422cf6bbb08fcb512" dependencies = [ "bytes", "libc", ] -[[package]] -name = "logos-blockchain-circuits-utils" -version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" -dependencies = [ - "dirs", -] - [[package]] name = "logos-blockchain-common-http-client" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "futures", "hex", @@ -5666,6 +5475,7 @@ dependencies = [ "logos-blockchain-groth16", "logos-blockchain-http-api-common", "logos-blockchain-key-management-system-keys", + "logos-blockchain-log-targets", "reqwest", "serde", "serde_json", @@ -5677,9 +5487,9 @@ dependencies = [ [[package]] name = "logos-blockchain-core" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ - "ark-ff 0.4.2", + "ark-ff", "bincode", "blake2", "bytes", @@ -5690,6 +5500,7 @@ dependencies = [ "logos-blockchain-cryptarchia-engine", "logos-blockchain-groth16", "logos-blockchain-key-management-system-keys", + "logos-blockchain-log-targets", "logos-blockchain-mmr", "logos-blockchain-poc", "logos-blockchain-pol", @@ -5710,7 +5521,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-engine" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "logos-blockchain-pol", "logos-blockchain-utils", @@ -5725,7 +5536,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-sync" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "bytes", "futures", @@ -5744,13 +5555,13 @@ dependencies = [ [[package]] name = "logos-blockchain-groth16" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ - "ark-bn254 0.4.0", - "ark-ec 0.4.2", - "ark-ff 0.4.2", - "ark-groth16 0.4.0", - "ark-serialize 0.4.2", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-groth16", + "ark-serialize", "generic-array 1.4.3", "hex", "num-bigint 0.4.6", @@ -5762,11 +5573,12 @@ dependencies = [ [[package]] name = "logos-blockchain-http-api-common" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "axum 0.7.9", "logos-blockchain-core", "logos-blockchain-key-management-system-keys", + "logos-blockchain-log-targets", "logos-blockchain-tracing", "serde", "serde_json", @@ -5782,7 +5594,7 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-keys" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "bytes", @@ -5791,6 +5603,7 @@ dependencies = [ "hex", "logos-blockchain-groth16", "logos-blockchain-key-management-system-macros", + "logos-blockchain-log-targets", "logos-blockchain-poseidon2", "logos-blockchain-utils", "logos-blockchain-zksign", @@ -5808,7 +5621,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=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "proc-macro2", "quote", @@ -5818,13 +5631,14 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-operators" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "logos-blockchain-blend-proofs", "logos-blockchain-core", "logos-blockchain-groth16", "logos-blockchain-key-management-system-keys", + "logos-blockchain-log-targets", "logos-blockchain-poseidon2", "logos-blockchain-utxotree", "tokio", @@ -5834,12 +5648,12 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", - "log", "logos-blockchain-key-management-system-keys", "logos-blockchain-key-management-system-operators", + "logos-blockchain-log-targets", "logos-blockchain-tracing", "overwatch", "serde", @@ -5851,7 +5665,7 @@ dependencies = [ [[package]] name = "logos-blockchain-ledger" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "derivative", "logos-blockchain-blend-crypto", @@ -5877,7 +5691,7 @@ dependencies = [ [[package]] name = "logos-blockchain-libp2p" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "backon", @@ -5906,7 +5720,7 @@ dependencies = [ [[package]] name = "logos-blockchain-log-targets" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "logos-blockchain-log-targets-macros", ] @@ -5914,7 +5728,7 @@ dependencies = [ [[package]] name = "logos-blockchain-log-targets-macros" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "proc-macro2", "quote", @@ -5924,9 +5738,9 @@ dependencies = [ [[package]] name = "logos-blockchain-mmr" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ - "ark-ff 0.4.2", + "ark-ff", "logos-blockchain-groth16", "logos-blockchain-poseidon2", "rpds", @@ -5937,7 +5751,7 @@ dependencies = [ [[package]] name = "logos-blockchain-network-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "futures", @@ -5958,13 +5772,13 @@ dependencies = [ [[package]] name = "logos-blockchain-poc" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "logos-blockchain-circuits-poc-sys", "logos-blockchain-circuits-prover", "logos-blockchain-circuits-types", - "logos-blockchain-circuits-utils", "logos-blockchain-groth16", + "logos-blockchain-log-targets", "logos-blockchain-proofs-error", "num-bigint 0.4.6", "serde", @@ -5975,14 +5789,14 @@ dependencies = [ [[package]] name = "logos-blockchain-pol" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "astro-float", "logos-blockchain-circuits-pol-sys", "logos-blockchain-circuits-prover", "logos-blockchain-circuits-types", - "logos-blockchain-circuits-utils", "logos-blockchain-groth16", + "logos-blockchain-log-targets", "logos-blockchain-proofs-error", "logos-blockchain-utils", "num-bigint 0.4.6", @@ -5995,13 +5809,13 @@ dependencies = [ [[package]] name = "logos-blockchain-poq" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "logos-blockchain-circuits-poq-sys", "logos-blockchain-circuits-prover", "logos-blockchain-circuits-types", - "logos-blockchain-circuits-utils", "logos-blockchain-groth16", + "logos-blockchain-log-targets", "logos-blockchain-pol", "logos-blockchain-proofs-error", "num-bigint 0.4.6", @@ -6014,10 +5828,10 @@ dependencies = [ [[package]] name = "logos-blockchain-poseidon2" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ - "ark-bn254 0.4.0", - "ark-ff 0.4.2", + "ark-bn254", + "ark-ff", "jf-poseidon2", "num-bigint 0.4.6", ] @@ -6025,7 +5839,7 @@ dependencies = [ [[package]] name = "logos-blockchain-proofs-error" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "logos-blockchain-circuits-types", "logos-blockchain-groth16", @@ -6036,11 +5850,12 @@ dependencies = [ [[package]] name = "logos-blockchain-services-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "futures", "log", + "logos-blockchain-log-targets", "overwatch", "serde", "serde_json", @@ -6051,7 +5866,7 @@ dependencies = [ [[package]] name = "logos-blockchain-storage-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "bytes", @@ -6070,7 +5885,7 @@ dependencies = [ [[package]] name = "logos-blockchain-time-service" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "futures", @@ -6093,7 +5908,7 @@ dependencies = [ [[package]] name = "logos-blockchain-tracing" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "flate2", "logos-blockchain-log-targets", @@ -6119,28 +5934,31 @@ dependencies = [ [[package]] name = "logos-blockchain-utils" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "blake2", "cipher 0.4.4", "const-hex", "humantime", + "logos-blockchain-log-targets", "overwatch", "rand 0.8.6", "serde", + "serde_ignored", "serde_with", "serde_yaml", "thiserror 2.0.18", "time", + "tracing", ] [[package]] name = "logos-blockchain-utxotree" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ - "ark-ff 0.4.2", + "ark-ff", "logos-blockchain-groth16", "logos-blockchain-poseidon2", "num-bigint 0.4.6", @@ -6152,13 +5970,13 @@ dependencies = [ [[package]] name = "logos-blockchain-zksign" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-signature-sys", "logos-blockchain-circuits-types", - "logos-blockchain-circuits-utils", "logos-blockchain-groth16", + "logos-blockchain-log-targets", "logos-blockchain-poseidon2", "logos-blockchain-proofs-error", "num-bigint 0.4.6", @@ -6171,7 +5989,7 @@ dependencies = [ [[package]] name = "logos-blockchain-zone-sdk" version = "0.1.2" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=db9a8d821c1b20f29b03d02072817150cf969b8e#db9a8d821c1b20f29b03d02072817150cf969b8e" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "async-trait", "futures", @@ -6181,6 +5999,7 @@ dependencies = [ "logos-blockchain-groth16", "logos-blockchain-http-api-common", "logos-blockchain-key-management-system-service", + "logos-blockchain-log-targets", "rand 0.8.6", "reqwest", "rpds", @@ -6374,15 +6193,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "mempool" version = "0.1.0" @@ -6471,7 +6281,7 @@ dependencies = [ "module-lattice", "pkcs8 0.11.0", "rand_core 0.10.1", - "sha3", + "sha3 0.11.0", ] [[package]] @@ -6727,24 +6537,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60993920e071b0c9b66f14e2b32740a4e27ffc82854dcd72035887f336a09a28" -[[package]] -name = "nimue" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0dc7d3b2b7bd112c0cecf7d6f4f16a174ee7a98e27615b1d08256d0176588f2" -dependencies = [ - "ark-ec 0.4.2", - "ark-ff 0.4.2", - "ark-serialize 0.4.2", - "digest 0.10.7", - "generic-array 0.14.7", - "hex", - "keccak 0.1.6", - "log", - "rand 0.8.6", - "zeroize", -] - [[package]] name = "nix" version = "0.30.1" @@ -7718,37 +7510,32 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.24.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219" +checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c" dependencies = [ - "cfg-if", - "indoc", "libc", - "memoffset", "once_cell", "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", - "unindent", ] [[package]] name = "pyo3-build-config" -version = "0.24.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999" +checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078" dependencies = [ - "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.24.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33" +checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b" dependencies = [ "libc", "pyo3-build-config", @@ -7756,9 +7543,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.24.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9" +checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -7768,13 +7555,12 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.24.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a" +checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362" dependencies = [ "heck", "proc-macro2", - "pyo3-build-config", "quote", "syn 2.0.117", ] @@ -8436,11 +8222,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc57e76bb87193d154ac5ee6ee352fbd7edabddab36f02a81f40a048e5ca14f9" dependencies = [ "anyhow", - "ark-bn254 0.5.0", - "ark-ec 0.5.0", - "ark-ff 0.5.0", - "ark-groth16 0.5.0", - "ark-serialize 0.5.0", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-groth16", + "ark-serialize", "bytemuck", "cfg-if", "hex", @@ -8627,8 +8413,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7a3ef170810c387d31b64c0b59734abb0839dac2a8d137909e271bfdec9b1e0" dependencies = [ - "ark-bn254 0.5.0", - "ark-ff 0.5.0", + "ark-bn254", + "ark-ff", "byteorder", "futures", "light-poseidon", @@ -8725,6 +8511,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rust-rapidsnark" +version = "0.1.3" +source = "git+https://github.com/logos-blockchain/logos-blockchain-rust-rapidsnark.git?rev=e91187f8ccb5bbfc7bb00dac88169112428da78f#e91187f8ccb5bbfc7bb00dac88169112428da78f" +dependencies = [ + "anyhow", + "cc", + "chkstk_stub", + "num-bigint 0.4.6", + "num-traits", +] + [[package]] name = "rustc-demangle" version = "0.1.27" @@ -8765,7 +8563,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9035,6 +8833,7 @@ dependencies = [ "futures", "hex", "humantime-serde", + "key_protocol", "lee", "lee_core", "log", @@ -9042,6 +8841,7 @@ dependencies = [ "logos-blockchain-key-management-system-service", "logos-blockchain-zone-sdk", "mempool", + "num-bigint 0.4.6", "rand 0.8.6", "risc0-zkvm", "serde", @@ -9104,15 +8904,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-big-array" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" -dependencies = [ - "serde", -] - [[package]] name = "serde_arrays" version = "0.2.0" @@ -9153,6 +8944,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "serde_ignored" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_json" version = "1.0.150" @@ -9371,6 +9172,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest 0.10.7", + "keccak 0.1.6", +] + [[package]] name = "sha3" version = "0.11.0" @@ -9530,6 +9341,24 @@ dependencies = [ "der 0.8.0", ] +[[package]] +name = "spongefish" +version = "0.2.0" +source = "git+https://github.com/arkworks-rs/spongefish.git?rev=3ded547f7f56d7f8a1fc4c9a5c0ce965310bba5f#3ded547f7f56d7f8a1fc4c9a5c0ce965310bba5f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "digest 0.10.7", + "hex", + "keccak 0.1.6", + "rand 0.8.6", + "sha3 0.10.9", + "thiserror 2.0.18", + "zerocopy", + "zeroize", +] + [[package]] name = "stability" version = "0.2.1" @@ -9783,7 +9612,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9843,7 +9672,6 @@ dependencies = [ "testcontainers", "tokio", "url", - "vault_core", "wallet", ] @@ -10673,12 +10501,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "unindent" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" - [[package]] name = "unit-prefix" version = "0.5.2" @@ -10905,6 +10727,7 @@ dependencies = [ "base58", "bincode", "bip39", + "bridge_core", "clap", "common", "derive_more", @@ -10934,6 +10757,7 @@ dependencies = [ "token_core", "tokio", "url", + "vault_core", "zeroize", ] @@ -10942,9 +10766,11 @@ name = "wallet-ffi" version = "0.1.0" dependencies = [ "cbindgen", + "common", "key_protocol", "lee", "lee_core", + "risc0-zkvm", "sequencer_service_rpc", "serde_json", "tempfile", @@ -11207,7 +11033,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 290c3540..25f03774 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,18 +133,19 @@ chrono = "0.4.41" borsh = "1.5.7" base58 = "0.2.0" itertools = "0.14.0" +num-bigint = "0.4.6" url = { version = "2.5.4", features = ["serde"] } tokio-retry = "0.3.0" schemars = "1.2" async-stream = "0.3.6" -logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } -logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } -logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } -logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } -logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } -logos-blockchain-zone-sdk = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } -logos-blockchain-http-api-common = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "db9a8d821c1b20f29b03d02072817150cf969b8e" } +logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "d8711bbc3d43d3ef9755ef9b73af32fd0f703160" } +logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "d8711bbc3d43d3ef9755ef9b73af32fd0f703160" } +logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "d8711bbc3d43d3ef9755ef9b73af32fd0f703160" } +logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "d8711bbc3d43d3ef9755ef9b73af32fd0f703160" } +logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "d8711bbc3d43d3ef9755ef9b73af32fd0f703160" } +logos-blockchain-zone-sdk = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "d8711bbc3d43d3ef9755ef9b73af32fd0f703160" } +logos-blockchain-http-api-common = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "d8711bbc3d43d3ef9755ef9b73af32fd0f703160" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", @@ -157,6 +158,7 @@ k256 = { version = "0.13.3", features = [ "expose-field", "serde", "pem", + "schnorr", ] } ml-kem = { version = "0.3", features = ["hazmat"] } elliptic-curve = { version = "0.13.8", features = ["arithmetic"] } @@ -165,7 +167,7 @@ actix-web = { version = "4.13.0", default-features = false, features = [ ] } clap = { version = "4.5.42", features = ["derive", "env"] } reqwest = { version = "0.12", features = ["json", "rustls-tls", "stream"] } -pyo3 = { version = "0.24", features = ["auto-initialize"] } +pyo3 = { version = "0.29", features = ["auto-initialize"] } zeroize = "1" criterion = { version = "0.8", features = ["html_reports"] } diff --git a/Justfile b/Justfile index 83c9dd0c..346dc84b 100644 --- a/Justfile +++ b/Justfile @@ -8,7 +8,7 @@ METHODS_PATH := "program_methods" TEST_METHODS_PATH := "test_program_methods" ARTIFACTS := "artifacts" -# Build risc0 program artifacts +# Build risc0 program artifacts. build-artifacts: @echo "๐Ÿ”จ Building artifacts" @for methods_path in {{METHODS_PATH}} {{TEST_METHODS_PATH}}; do \ @@ -18,7 +18,13 @@ build-artifacts: cp target/$methods_path/riscv32im-risc0-zkvm-elf/docker/*.bin {{ARTIFACTS}}/$methods_path; \ done -# Run tests +# Format codebase. +fmt: + @echo "๐ŸŽจ Formatting codebase" + cargo +nightly fmt + taplo fmt + +# Run tests. test: @echo "๐Ÿงช Running tests" RISC0_DEV_MODE=1 cargo nextest run --no-fail-fast @@ -29,42 +35,59 @@ bench: cargo bench -p crypto_primitives_bench --bench primitives cargo bench -p cycle_bench --features ppe --bench verify -# Run Bedrock node in docker +# Run Bedrock node in docker. [working-directory: 'bedrock'] run-bedrock: @echo "โ›“๏ธ Running bedrock" docker compose up -# Run Sequencer +# Run Sequencer. Run with RISC0_DEV_MODE=1 to disable proof verification for faster iteration. [working-directory: 'lez/sequencer/service'] -run-sequencer: +run-sequencer standalone="": @echo "๐Ÿง  Running sequencer" - RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release -p sequencer_service configs/debug/sequencer_config.json + @if [ "{{standalone}}" = "standalone" ]; then \ + echo "๐Ÿงช Running in standalone mode"; \ + RUST_LOG=info cargo run --features standalone --release -p sequencer_service configs/debug/sequencer_config.json; \ + else \ + echo "๐Ÿš€ Running in normal mode"; \ + RUST_LOG=info cargo run --release -p sequencer_service configs/debug/sequencer_config.json; \ + fi -# Run Indexer +# Run Indexer. Run with RISC0_DEV_MODE=1 to disable proof verification for faster iteration. [working-directory: 'lez/indexer/service'] run-indexer mock="": @echo "๐Ÿ” Running indexer" @if [ "{{mock}}" = "mock" ]; then \ echo "๐Ÿงช Using mock data"; \ - RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release --features mock-responses -p indexer_service configs/indexer_config.json; \ + RUST_LOG=info cargo run --release --features mock-responses -p indexer_service configs/indexer_config.json; \ else \ echo "๐Ÿš€ Using real data"; \ - RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release -p indexer_service configs/indexer_config.json; \ + RUST_LOG=info cargo run --release -p indexer_service configs/indexer_config.json; \ fi -# Run Explorer +# Run Explorer. [working-directory: 'lez/explorer_service'] run-explorer: @echo "๐ŸŒ Running explorer" RUST_LOG=info cargo leptos serve -# Run Wallet +# Run Wallet. [working-directory: 'lez/wallet'] run-wallet +args: @echo "๐Ÿ”‘ Running wallet" LEE_WALLET_HOME_DIR=$(pwd)/configs/debug cargo run --release -p wallet -- {{args}} +# Import test accounts supplied in sequencer configuration. +wallet-import-test-accounts: + @echo "โš™๏ธ Initializing accounts" + just run-wallet account import public --private-key 7f273098f25b71e6c005a9519f2678da8d1c7f01f6a27778e2d9948abdf901fb + just run-wallet vault claim --account-id Public/CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r --amount 10000 + + just run-wallet account import public --private-key f434f8741720014586ae43356d2aec6257da086222f604ddb75d69733b86fc4c + just run-wallet vault claim --account-id Public/2RHZhw9h534Zr3eq2RGhQete2Hh667foECzXPmSkGni2 --amount 20000 + + just run-wallet account list + # Clean runtime data clean: @echo "๐Ÿงน Cleaning run artifacts" diff --git a/README.md b/README.md index f1ae82dc..e0de266f 100644 --- a/README.md +++ b/README.md @@ -147,8 +147,6 @@ The sequencer and logos blockchain node can be run locally: 1. On one terminal go to the `logos-blockchain/logos-blockchain` repo and run a local logos blockchain node: - `git checkout master; git pull` - `cargo clean` - - `rm -r ~/.logos-blockchain-circuits` - - `./scripts/setup-logos-blockchain-circuits.sh` - `cargo build --all-features` - `./target/debug/logos-blockchain-node --deployment nodes/node/standalone-deployment-config.yaml nodes/node/standalone-node-config.yaml` diff --git a/artifacts/program_methods/amm.bin b/artifacts/program_methods/amm.bin index 046f21bb..e8eab0e7 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 cae6ed4e..ed23efb4 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 cebe1042..481baa83 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 9810c6ac..c55b6f94 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 1124913f..151a72d7 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 ca45b686..1385c2e1 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 118f19d3..7ae7c005 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 f3ecb0e9..a2581702 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 66f6d5b6..ebd4c38f 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 a36fbbc8..430122b1 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 7628b459..57bba45e 100644 Binary files a/artifacts/program_methods/vault.bin and b/artifacts/program_methods/vault.bin differ diff --git a/artifacts/test_program_methods/auth_asserting_noop.bin b/artifacts/test_program_methods/auth_asserting_noop.bin index 3884195d..2507eacb 100644 Binary files a/artifacts/test_program_methods/auth_asserting_noop.bin and b/artifacts/test_program_methods/auth_asserting_noop.bin differ diff --git a/artifacts/test_program_methods/auth_transfer_proxy.bin b/artifacts/test_program_methods/auth_transfer_proxy.bin index 6afda3a5..2672cd29 100644 Binary files a/artifacts/test_program_methods/auth_transfer_proxy.bin and b/artifacts/test_program_methods/auth_transfer_proxy.bin differ diff --git a/artifacts/test_program_methods/burner.bin b/artifacts/test_program_methods/burner.bin index dd2db03f..d0d9893a 100644 Binary files a/artifacts/test_program_methods/burner.bin and b/artifacts/test_program_methods/burner.bin differ diff --git a/artifacts/test_program_methods/chain_caller.bin b/artifacts/test_program_methods/chain_caller.bin index 58291896..4511b516 100644 Binary files a/artifacts/test_program_methods/chain_caller.bin and b/artifacts/test_program_methods/chain_caller.bin differ diff --git a/artifacts/test_program_methods/changer_claimer.bin b/artifacts/test_program_methods/changer_claimer.bin index bb2feabb..c657a86d 100644 Binary files a/artifacts/test_program_methods/changer_claimer.bin and b/artifacts/test_program_methods/changer_claimer.bin differ diff --git a/artifacts/test_program_methods/claimer.bin b/artifacts/test_program_methods/claimer.bin index b0f3a67c..47b451ec 100644 Binary files a/artifacts/test_program_methods/claimer.bin and b/artifacts/test_program_methods/claimer.bin differ diff --git a/artifacts/test_program_methods/clock_chain_caller.bin b/artifacts/test_program_methods/clock_chain_caller.bin index 5cda6717..b6e5b7f6 100644 Binary files a/artifacts/test_program_methods/clock_chain_caller.bin and b/artifacts/test_program_methods/clock_chain_caller.bin differ diff --git a/artifacts/test_program_methods/data_changer.bin b/artifacts/test_program_methods/data_changer.bin index 898cea24..4cae9b77 100644 Binary files a/artifacts/test_program_methods/data_changer.bin and b/artifacts/test_program_methods/data_changer.bin differ diff --git a/artifacts/test_program_methods/extra_output.bin b/artifacts/test_program_methods/extra_output.bin index a74d931d..d93baede 100644 Binary files a/artifacts/test_program_methods/extra_output.bin and b/artifacts/test_program_methods/extra_output.bin differ diff --git a/artifacts/test_program_methods/faucet_chain_caller.bin b/artifacts/test_program_methods/faucet_chain_caller.bin index 8effbf85..01e32aa1 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/flash_swap_callback.bin b/artifacts/test_program_methods/flash_swap_callback.bin index e4aa09dc..028b64df 100644 Binary files a/artifacts/test_program_methods/flash_swap_callback.bin and b/artifacts/test_program_methods/flash_swap_callback.bin differ diff --git a/artifacts/test_program_methods/flash_swap_initiator.bin b/artifacts/test_program_methods/flash_swap_initiator.bin index f0d031b8..f18afc5f 100644 Binary files a/artifacts/test_program_methods/flash_swap_initiator.bin and b/artifacts/test_program_methods/flash_swap_initiator.bin differ diff --git a/artifacts/test_program_methods/malicious_authorization_changer.bin b/artifacts/test_program_methods/malicious_authorization_changer.bin index 589bc620..99359e22 100644 Binary files a/artifacts/test_program_methods/malicious_authorization_changer.bin and b/artifacts/test_program_methods/malicious_authorization_changer.bin differ diff --git a/artifacts/test_program_methods/malicious_caller_program_id.bin b/artifacts/test_program_methods/malicious_caller_program_id.bin index 8c0d1350..fe8f5511 100644 Binary files a/artifacts/test_program_methods/malicious_caller_program_id.bin and b/artifacts/test_program_methods/malicious_caller_program_id.bin differ diff --git a/artifacts/test_program_methods/malicious_injector.bin b/artifacts/test_program_methods/malicious_injector.bin index 8bcccb4e..9cadd3b3 100644 Binary files a/artifacts/test_program_methods/malicious_injector.bin and b/artifacts/test_program_methods/malicious_injector.bin differ diff --git a/artifacts/test_program_methods/malicious_launderer.bin b/artifacts/test_program_methods/malicious_launderer.bin index 70392d9c..9686a8f4 100644 Binary files a/artifacts/test_program_methods/malicious_launderer.bin and b/artifacts/test_program_methods/malicious_launderer.bin differ diff --git a/artifacts/test_program_methods/malicious_self_program_id.bin b/artifacts/test_program_methods/malicious_self_program_id.bin index 62b8af74..853740ba 100644 Binary files a/artifacts/test_program_methods/malicious_self_program_id.bin and b/artifacts/test_program_methods/malicious_self_program_id.bin differ diff --git a/artifacts/test_program_methods/minter.bin b/artifacts/test_program_methods/minter.bin index c4b115ab..a012dd94 100644 Binary files a/artifacts/test_program_methods/minter.bin and b/artifacts/test_program_methods/minter.bin differ diff --git a/artifacts/test_program_methods/missing_output.bin b/artifacts/test_program_methods/missing_output.bin index ae599f60..4a056f6d 100644 Binary files a/artifacts/test_program_methods/missing_output.bin and b/artifacts/test_program_methods/missing_output.bin differ diff --git a/artifacts/test_program_methods/modified_transfer.bin b/artifacts/test_program_methods/modified_transfer.bin index 1435dea9..65f77b77 100644 Binary files a/artifacts/test_program_methods/modified_transfer.bin and b/artifacts/test_program_methods/modified_transfer.bin differ diff --git a/artifacts/test_program_methods/nonce_changer.bin b/artifacts/test_program_methods/nonce_changer.bin index bc479a80..e1cb3175 100644 Binary files a/artifacts/test_program_methods/nonce_changer.bin and b/artifacts/test_program_methods/nonce_changer.bin differ diff --git a/artifacts/test_program_methods/noop.bin b/artifacts/test_program_methods/noop.bin index e0d52639..7575a2c3 100644 Binary files a/artifacts/test_program_methods/noop.bin and b/artifacts/test_program_methods/noop.bin differ diff --git a/artifacts/test_program_methods/pda_claimer.bin b/artifacts/test_program_methods/pda_claimer.bin index f34373ff..55a934b1 100644 Binary files a/artifacts/test_program_methods/pda_claimer.bin and b/artifacts/test_program_methods/pda_claimer.bin differ diff --git a/artifacts/test_program_methods/pda_spend_proxy.bin b/artifacts/test_program_methods/pda_spend_proxy.bin index 33e38f00..8f7e8977 100644 Binary files a/artifacts/test_program_methods/pda_spend_proxy.bin and b/artifacts/test_program_methods/pda_spend_proxy.bin differ diff --git a/artifacts/test_program_methods/pinata_cooldown.bin b/artifacts/test_program_methods/pinata_cooldown.bin index 65879893..5655e958 100644 Binary files a/artifacts/test_program_methods/pinata_cooldown.bin and b/artifacts/test_program_methods/pinata_cooldown.bin differ diff --git a/artifacts/test_program_methods/private_pda_delegator.bin b/artifacts/test_program_methods/private_pda_delegator.bin index 7652eaa7..163135b6 100644 Binary files a/artifacts/test_program_methods/private_pda_delegator.bin and b/artifacts/test_program_methods/private_pda_delegator.bin differ diff --git a/artifacts/test_program_methods/program_owner_changer.bin b/artifacts/test_program_methods/program_owner_changer.bin index 81d9a7e1..185e521e 100644 Binary files a/artifacts/test_program_methods/program_owner_changer.bin and b/artifacts/test_program_methods/program_owner_changer.bin differ diff --git a/artifacts/test_program_methods/simple_balance_transfer.bin b/artifacts/test_program_methods/simple_balance_transfer.bin index ef9b3006..57510322 100644 Binary files a/artifacts/test_program_methods/simple_balance_transfer.bin and b/artifacts/test_program_methods/simple_balance_transfer.bin differ diff --git a/artifacts/test_program_methods/time_locked_transfer.bin b/artifacts/test_program_methods/time_locked_transfer.bin index ba50eebd..f762398b 100644 Binary files a/artifacts/test_program_methods/time_locked_transfer.bin and b/artifacts/test_program_methods/time_locked_transfer.bin differ diff --git a/artifacts/test_program_methods/two_pda_claimer.bin b/artifacts/test_program_methods/two_pda_claimer.bin index 32a8b581..33995cb7 100644 Binary files a/artifacts/test_program_methods/two_pda_claimer.bin and b/artifacts/test_program_methods/two_pda_claimer.bin differ diff --git a/artifacts/test_program_methods/validity_window.bin b/artifacts/test_program_methods/validity_window.bin index 85a38041..88cbf9c3 100644 Binary files a/artifacts/test_program_methods/validity_window.bin and b/artifacts/test_program_methods/validity_window.bin differ diff --git a/artifacts/test_program_methods/validity_window_chain_caller.bin b/artifacts/test_program_methods/validity_window_chain_caller.bin index 99f379c5..a8cb5663 100644 Binary files a/artifacts/test_program_methods/validity_window_chain_caller.bin and b/artifacts/test_program_methods/validity_window_chain_caller.bin differ diff --git a/bedrock/deployment-settings.yaml b/bedrock/deployment-settings.yaml index de58f12a..9c21ee28 100644 --- a/bedrock/deployment-settings.yaml +++ b/bedrock/deployment-settings.yaml @@ -8,7 +8,6 @@ blend: scheduler: cover: message_frequency_per_round: 1.0 - intervals_for_safety_buffer: 100 delayer: maximum_release_delay_in_rounds: 3 minimum_messages_coefficient: 1 @@ -31,10 +30,9 @@ cryptarchia: sdp_config: service_params: BN: - lock_period: 10 inactivity_period: 1 retention_period: 1 - timestamp: 0 + epoch: 0 min_stake: threshold: 1 timestamp: 0 @@ -72,13 +70,10 @@ cryptarchia: inscription: '0c000000000000006c6f676f732d6465766e65742c046269000000000000000000000000000000000000000000000000000000000000000000000000' parent: '0000000000000000000000000000000000000000000000000000000000000000' signer: '0000000000000000000000000000000000000000000000000000000000000000' - execution_gas_price: 0 - storage_gas_price: 0 ops_proofs: - !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' - !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' time: slot_duration: '1.0' - chain_start_time: PLACEHOLDER_CHAIN_START_TIME mempool: pubsub_topic: mantle_e2e_tests diff --git a/bedrock/docker-compose.yml b/bedrock/docker-compose.yml index 356c6df8..a7a0d7f6 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:f160cfbf898a06554451cc066d84cfd0f8ab62d59bd3e62d9cde3bd5582c12ab + image: ghcr.io/logos-blockchain/logos-blockchain@sha256:91d6c5bf07e07fcfba5e7cf07d21ee686a6bc4b9f6210f2d28bffbcad9a3729f ports: - "${PORT:-8080}:18080/tcp" volumes: diff --git a/completions/bash/wallet b/completions/bash/wallet index a4d390f6..4820d985 100644 --- a/completions/bash/wallet +++ b/completions/bash/wallet @@ -46,7 +46,7 @@ _wallet() { cword=$COMP_CWORD } - local commands="auth-transfer chain-info account pinata token amm ata check-health config restore-keys deploy-program help" + local commands="auth-transfer chain-info account pinata token amm ata vault bridge check-health config restore-keys deploy-program help" # Find the main command and subcommand by scanning words before the cursor. # Global options that take a value are skipped along with their argument. @@ -535,6 +535,56 @@ _wallet() { esac ;; + vault) + case "$subcmd" in + "") + COMPREPLY=($(compgen -W "transfer claim help" -- "$cur")) + ;; + transfer) + case "$prev" in + --from | --to) + _wallet_complete_account_id "$cur" + ;; + --amount) + ;; # no specific completion + *) + COMPREPLY=($(compgen -W "--from --to --amount" -- "$cur")) + ;; + esac + ;; + claim) + case "$prev" in + --account-id) + _wallet_complete_account_id "$cur" + ;; + --amount) + ;; # no specific completion + *) + COMPREPLY=($(compgen -W "--account-id --amount" -- "$cur")) + esac + ;; + esac + ;; + bridge) + case "$subcmd" in + "") + COMPREPLY=($(compgen -W "withdraw help" -- "$cur")) + ;; + withdraw) + case "$prev" in + --from) + _wallet_complete_account_id "$cur" + ;; + --amount | --bedrock-account-pk) + ;; # no specific completion + *) + COMPREPLY=($(compgen -W "--from --amount --bedrock-account-pk" -- "$cur")) + ;; + esac + ;; + esac + ;; + config) case "$subcmd" in "") diff --git a/completions/zsh/_wallet b/completions/zsh/_wallet index 8f573ab0..c16b9ada 100644 --- a/completions/zsh/_wallet +++ b/completions/zsh/_wallet @@ -25,6 +25,8 @@ _wallet() { 'token:Token program interaction subcommand' 'amm:AMM program interaction subcommand' 'ata:Associated Token Account program interaction subcommand' + 'vault:Vault program interaction subcommand' + 'bridge:Bridge program interaction subcommand' 'check-health:Check the wallet can connect to the node and builtin local programs match the remote versions' 'config:Command to setup config, get and set config fields' 'restore-keys:Restoring keys from given password at given depth' @@ -56,6 +58,11 @@ _wallet() { ata) _wallet_ata ;; + vault) + _wallet_vault + bridge) + _wallet_bridge + ;; config) _wallet_config ;; @@ -442,6 +449,70 @@ _wallet_ata() { esac } +# vault subcommand +_wallet_vault() { + local -a subcommands + + _arguments -C \ + '1: :->subcommand' \ + '*:: :->args' + + case $state in + subcommand) + subcommands=( + 'transfer:Transfer native tokens from sender to recipient vault account' + 'claim:Claim native tokens from account vault account' + 'help:Print this message or the help of the given subcommand(s)' + ) + _describe -t subcommands 'vault subcommands' subcommands + ;; + args) + case $line[1] in + transfer) + _arguments \ + '--from[Source account with privacy prefix or label]:from:_wallet_account_ids' \ + '--to[Recipient account with privacy prefix or label]:to:_wallet_account_ids' \ + '--amount[Amount of native tokens to transfer]:amount:' + ;; + claim) + _arguments \ + '--account-id[Account with privacy prefix or label; vault id is derived automatically]:account:_wallet_account_ids' \ + '--amount[Amount of native tokens to claim]:amount:' + ;; + esac + ;; + esac +} + +# bridge subcommand +_wallet_bridge() { + local -a subcommands + + _arguments -C \ + '1: :->subcommand' \ + '*:: :->args' + + case $state in + subcommand) + subcommands=( + 'withdraw:Withdraw native tokens through the bridge' + 'help:Print this message or the help of the given subcommand(s)' + ) + _describe -t subcommands 'bridge subcommands' subcommands + ;; + args) + case $line[1] in + withdraw) + _arguments \ + '--from[Sender account with privacy prefix]:from:_wallet_account_ids' \ + '--amount[Amount of native tokens to withdraw]:amount:' \ + '--bedrock-account-pk[Bedrock account public key (32-byte hex)]:bedrock_pk:' + ;; + esac + ;; + esac +} + # config subcommand _wallet_config() { local -a subcommands @@ -515,6 +586,8 @@ _wallet_help() { 'token:Token program interaction subcommand' 'amm:AMM program interaction subcommand' 'ata:Associated Token Account program interaction subcommand' + 'vault:Vault program interaction subcommand' + 'bridge:Bridge program interaction subcommand' 'check-health:Check the wallet can connect to the node' 'config:Command to setup config, get and set config fields' 'restore-keys:Restoring keys from given password at given depth' diff --git a/docs/benchmarks/cycle_bench.md b/docs/benchmarks/cycle_bench.md index 0e880070..2a8785f0 100644 --- a/docs/benchmarks/cycle_bench.md +++ b/docs/benchmarks/cycle_bench.md @@ -14,21 +14,37 @@ Per-program Risc0 cycle counts, prover wall time, PPE composition cost, and veri | Profile | release | | GPU acceleration | none | -## Executor cycles +## Executor cycles and public-execution ms -`SessionInfo::cycles()` per instruction. Deterministic across runs. Wall time is `best / mean ยฑ stdev` over 5 timed iterations (1 warmup discarded). +`SessionInfo::cycles()` per instruction. Deterministic across runs. Wall time is `best / mean ยฑ stdev` over the timed iterations (1 warmup discarded; `--exec-iters` sets the count, 50 below). `calib_ms` and `net_ms` are the public-execution time in milliseconds, on the same axis as the private `G_verify` so the fee model has one common unit for both paths. See the calibration block below for how they are derived. -| Program | Instruction | user_cycles | segments | exec_ms (best / mean ยฑ stdev) | -|---|---|---:|---:|---| -| authenticated_transfer | Initialize | 43,642 | 1 | 18.86 / 19.41 ยฑ 0.48 | -| authenticated_transfer | Transfer | 77,095 | 1 | 19.67 / 20.84 ยฑ 1.16 | -| token | Burn | 116,546 | 1 | 24.86 / 25.46 ยฑ 0.63 | -| token | Mint | 116,862 | 1 | 24.47 / 25.08 ยฑ 0.42 | -| token | Transfer | 127,726 | 1 | 25.00 / 25.40 ยฑ 0.29 | -| clock | Tick (no rollups) | 137,022 | 1 | 21.18 / 21.57 ยฑ 0.41 | -| ata | Create | 175,056 | 1 | 23.64 / 24.94 ยฑ 1.09 | -| amm | SwapExactInput | 508,634 | 1 | 34.21 / 34.77 ยฑ 0.55 | -| amm | AddLiquidity | 642,774 | 1 | 37.59 / 37.87 ยฑ 0.28 | +| Program | Instruction | user_cycles | segments | exec_ms (best / mean ยฑ stdev) | calib_ms | net_ms | +|---|---|---:|---:|---|---:|---:| +| authenticated_transfer | Initialize | 43,818 | 1 | 30.69 / 31.93 ยฑ 1.03 | 1.31 | 0.29 | +| authenticated_transfer | Transfer | 79,958 | 1 | 31.02 / 32.35 ยฑ 0.59 | 2.38 | 0.61 | +| token | Burn | 116,546 | 1 | 36.08 / 37.18 ยฑ 0.60 | 3.47 | 5.67 | +| token | Mint | 116,862 | 1 | 35.67 / 37.73 ยฑ 2.54 | 3.48 | 5.26 | +| token | Transfer | 127,726 | 1 | 35.49 / 36.86 ยฑ 0.90 | 3.81 | 5.08 | +| clock | Tick (no rollups) | 137,022 | 1 | 32.12 / 33.16 ยฑ 0.89 | 4.08 | 1.72 | +| ata | Create | 174,515 | 1 | 35.41 / 36.49 ยฑ 0.65 | 5.20 | 5.00 | +| amm | SwapExactInput | 508,904 | 1 | 46.71 / 48.06 ยฑ 0.86 | 15.17 | 16.30 | +| amm | AddLiquidity | 643,464 | 1 | 48.57 / 50.28 ยฑ 0.98 | 19.18 | 18.16 | + +### Public-execution ms calibration + +The binary fits `best_ms = intercept + slope ยท user_cycles` by ordinary least squares across the nine cases (best-of-N, not mean, so one OS scheduling spike cannot tilt the slope). On the machine above: + +| Field | Value | +|---|---| +| throughput (1 / slope) | 33,546 cycles/ms | +| fixed overhead (intercept) | 30.41 ms per call | +| Rยฒ | 0.935 | + +- `calib_ms = user_cycles / throughput` is the compute-only time, a pure function of the deterministic cycle count and the one pinned-hardware constant, so it reproduces run to run where raw wall-time does not. This is the number to put on the common public/private ms axis. +- `net_ms = best exec_ms โˆ’ fixed overhead` is the measured compute with the host-side overhead stripped; it agrees with `calib_ms` to within the per-program overhead scatter (the intercept is an ELF-size-averaged constant, so this decomposition is first-order, not mechanistic). +- The `fixed overhead` is host-side per-call setup (ELF parse into a `MemoryImage`, `ExecutorEnv` build) that is outside the cycle count and does not scale with the instruction's work. + +The fixed overhead is paid per transaction in the current node, not amortized. The public-execution path at `lee/state_machine/src/program.rs:56-87` builds a fresh `ExecutorEnv` and calls `default_executor().execute(env, self.elf())` per call with the raw ELF bytes; no parsed image is cached across transactions. So today the real per-public-tx sequencer cost is the raw `exec_ms` (โ‰ˆ 31 ms for the cheapest program), overhead-dominated. Caching the parsed `MemoryImage` per `ProgramId` would drop the per-tx cost to `calib_ms` (1โ€“19 ms). Public execution is also cycle-capped at `MAX_NUM_CYCLES_PUBLIC_EXECUTION` (`program.rs:64`), which bounds the worst-case public-tx cost. ## Real proving (`--prove`) @@ -85,7 +101,8 @@ The corresponding `proof_bytes` (S_agg) for the bench receipt is captured by `-- ## Reproduce ```sh -cargo run --release -p cycle_bench +# Executor cycles + public-execution ms calibration (no proving). --exec-iters sets the sample count. +cargo run --release -p cycle_bench -- --exec-iters 50 cargo run --release -p cycle_bench --features prove -- --prove cargo run --release -p cycle_bench --features ppe -- --prove --ppe diff --git a/flake.lock b/flake.lock index cd5fbc8a..21fb4220 100644 --- a/flake.lock +++ b/flake.lock @@ -30,6 +30,6323 @@ "original": { "owner": "logos-blockchain", "repo": "logos-blockchain-circuits", + "rev": "2846ee7a4cfa24458bb8063412ab2e753b344d2f", + "type": "github" + } + }, + "logos-capability-module": { + "inputs": { + "logos-module-builder": "logos-module-builder" + }, + "locked": { + "lastModified": 1781210397, + "narHash": "sha256-LaQ5EuAVZeW6H2JUaBXtXfke6ZZvsqgE9qfeFueKAHI=", + "owner": "logos-co", + "repo": "logos-capability-module", + "rev": "13441afc8e8af8cfd22d6e007c6f46874eac97d9", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-capability-module", + "type": "github" + } + }, + "logos-capability-module_2": { + "inputs": { + "logos-module-builder": "logos-module-builder_2" + }, + "locked": { + "lastModified": 1780946924, + "narHash": "sha256-8wFILW1oMS1L77Ff4bcC/5W9dytNe6I1rhf5AHhQW2U=", + "owner": "logos-co", + "repo": "logos-capability-module", + "rev": "0013e7653eb1b23d5495e39f149259bd60dc1db4", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-capability-module", + "type": "github" + } + }, + "logos-capability-module_3": { + "inputs": { + "logos-cpp-sdk": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk" + ], + "logos-module": "logos-module_7", + "logos-nix": "logos-nix_16", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455138, + "narHash": "sha256-szx2dnnY9MP1NpdBnR8E2DRSz9CtQlo/6698zgJcAEM=", + "owner": "logos-co", + "repo": "logos-capability-module", + "rev": "0655be68e0078bede0682bb6a5b53330dac37a72", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-capability-module", + "type": "github" + } + }, + "logos-capability-module_4": { + "inputs": { + "logos-cpp-sdk": "logos-cpp-sdk_4", + "logos-module": "logos-module_8", + "logos-nix": "logos-nix_21", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455138, + "narHash": "sha256-szx2dnnY9MP1NpdBnR8E2DRSz9CtQlo/6698zgJcAEM=", + "owner": "logos-co", + "repo": "logos-capability-module", + "rev": "0655be68e0078bede0682bb6a5b53330dac37a72", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-capability-module", + "type": "github" + } + }, + "logos-capability-module_5": { + "inputs": { + "logos-module-builder": "logos-module-builder_3" + }, + "locked": { + "lastModified": 1778182598, + "narHash": "sha256-QoyirL/blmVM2cmXQwbR6rmHufQJ6vaJSCkNORu4qaA=", + "owner": "logos-co", + "repo": "logos-capability-module", + "rev": "e675e9e3a98ee69bb303365c2c626f9237bc1ab5", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-capability-module", + "type": "github" + } + }, + "logos-capability-module_6": { + "inputs": { + "logos-cpp-sdk": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk" + ], + "logos-module": "logos-module_13", + "logos-nix": "logos-nix_60", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455138, + "narHash": "sha256-szx2dnnY9MP1NpdBnR8E2DRSz9CtQlo/6698zgJcAEM=", + "owner": "logos-co", + "repo": "logos-capability-module", + "rev": "0655be68e0078bede0682bb6a5b53330dac37a72", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-capability-module", + "type": "github" + } + }, + "logos-capability-module_7": { + "inputs": { + "logos-cpp-sdk": "logos-cpp-sdk_9", + "logos-module": "logos-module_14", + "logos-nix": "logos-nix_65", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455138, + "narHash": "sha256-szx2dnnY9MP1NpdBnR8E2DRSz9CtQlo/6698zgJcAEM=", + "owner": "logos-co", + "repo": "logos-capability-module", + "rev": "0655be68e0078bede0682bb6a5b53330dac37a72", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-capability-module", + "type": "github" + } + }, + "logos-cpp-sdk": { + "inputs": { + "logos-nix": "logos-nix", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781207077, + "narHash": "sha256-ZamVbPJxW364RFOa0WnYTJV7AiEjnUcOgfxIbfNgTO0=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "f5a127dd11589c8fc6db0a1bd4332d09ac080bb6", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_10": { + "inputs": { + "logos-nix": "logos-nix_66", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778167634, + "narHash": "sha256-gbBYvyEDxBHF8iACAE/dFcljBkIUTWr1rP3gBLj62JI=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "25c88f4d48fa95ea4437194bcf60bd8d0cf84a74", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_11": { + "inputs": { + "logos-nix": "logos-nix_94", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1779716125, + "narHash": "sha256-PfpmvkXyDnQRW0dcnIthGKE17rm5bwm8KLbEqJcm9kM=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "d77c3dd616384addfcc8c5607860466850fdfcf2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_12": { + "inputs": { + "logos-nix": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "logos-nix" + ], + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781027175, + "narHash": "sha256-NjODUctwKSIi3vMUSMgk6bxfW4fJM9vX5jndk8CfZJ4=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "bb6d87b6ec7a64814df48fbdb10cf5290ce4b724", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_13": { + "inputs": { + "logos-nix": "logos-nix_122", + "nixpkgs": [ + "logos-liblogos", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781207077, + "narHash": "sha256-ZamVbPJxW364RFOa0WnYTJV7AiEjnUcOgfxIbfNgTO0=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "f5a127dd11589c8fc6db0a1bd4332d09ac080bb6", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_2": { + "inputs": { + "logos-nix": "logos-nix_8", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778167634, + "narHash": "sha256-gbBYvyEDxBHF8iACAE/dFcljBkIUTWr1rP3gBLj62JI=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "25c88f4d48fa95ea4437194bcf60bd8d0cf84a74", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_3": { + "inputs": { + "logos-nix": "logos-nix_17", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778167634, + "narHash": "sha256-gbBYvyEDxBHF8iACAE/dFcljBkIUTWr1rP3gBLj62JI=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "25c88f4d48fa95ea4437194bcf60bd8d0cf84a74", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_4": { + "inputs": { + "logos-nix": "logos-nix_19", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773956385, + "narHash": "sha256-CV0Lo1FrosBt/MSP+GWQGWXnYobxRGXGOREylNuwZ58=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "4b66dac015e4b977d33cfae80a4c8e1d518679f3", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_5": { + "inputs": { + "logos-nix": "logos-nix_22", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778167634, + "narHash": "sha256-gbBYvyEDxBHF8iACAE/dFcljBkIUTWr1rP3gBLj62JI=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "25c88f4d48fa95ea4437194bcf60bd8d0cf84a74", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_6": { + "inputs": { + "logos-nix": "logos-nix_50", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781027175, + "narHash": "sha256-NjODUctwKSIi3vMUSMgk6bxfW4fJM9vX5jndk8CfZJ4=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "bb6d87b6ec7a64814df48fbdb10cf5290ce4b724", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_7": { + "inputs": { + "logos-nix": "logos-nix_52", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778167634, + "narHash": "sha256-gbBYvyEDxBHF8iACAE/dFcljBkIUTWr1rP3gBLj62JI=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "25c88f4d48fa95ea4437194bcf60bd8d0cf84a74", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_8": { + "inputs": { + "logos-nix": "logos-nix_61", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778167634, + "narHash": "sha256-gbBYvyEDxBHF8iACAE/dFcljBkIUTWr1rP3gBLj62JI=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "25c88f4d48fa95ea4437194bcf60bd8d0cf84a74", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_9": { + "inputs": { + "logos-nix": "logos-nix_63", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773956385, + "narHash": "sha256-CV0Lo1FrosBt/MSP+GWQGWXnYobxRGXGOREylNuwZ58=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "4b66dac015e4b977d33cfae80a4c8e1d518679f3", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-design-system": { + "inputs": { + "logos-nix": "logos-nix_18", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-design-system", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777999983, + "narHash": "sha256-D6W7oC80cT7pg95kSpxO0jtoVj1hG9A151CciCSZaBc=", + "owner": "logos-co", + "repo": "logos-design-system", + "rev": "f6e95ae24ede3871c380f8250feffd17d173f5a4", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-design-system", + "type": "github" + } + }, + "logos-design-system_2": { + "inputs": { + "logos-nix": "logos-nix_51", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-design-system", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780947586, + "narHash": "sha256-0l5Xjbx9bLRc6LF0FU3LZ15RmEYK9oOPy/EaOxQYHkc=", + "owner": "logos-co", + "repo": "logos-design-system", + "rev": "4a37b34446ef4e55b16a5aeb011ffc5e8cbcbbb8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-design-system", + "type": "github" + } + }, + "logos-design-system_3": { + "inputs": { + "logos-nix": "logos-nix_62", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-design-system", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777999983, + "narHash": "sha256-D6W7oC80cT7pg95kSpxO0jtoVj1hG9A151CciCSZaBc=", + "owner": "logos-co", + "repo": "logos-design-system", + "rev": "f6e95ae24ede3871c380f8250feffd17d173f5a4", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-design-system", + "type": "github" + } + }, + "logos-liblogos": { + "inputs": { + "logos-capability-module": "logos-capability-module", + "logos-cpp-sdk": "logos-cpp-sdk_13", + "logos-module": "logos-module_17", + "logos-nix": "logos-nix_124", + "logos-package-manager": "logos-package-manager_7", + "nixpkgs": [ + "logos-liblogos", + "logos-nix", + "nixpkgs" + ], + "process-stats": "process-stats_4" + }, + "locked": { + "lastModified": 1781211239, + "narHash": "sha256-qRWrWyxpry5cO03rG1cXYPtujQON6A/0Z0Z/R1NmUV8=", + "owner": "logos-co", + "repo": "logos-liblogos", + "rev": "66256b54efb995adf977689ea63f6bfb6ccfdd81", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-liblogos", + "type": "github" + } + }, + "logos-liblogos_2": { + "inputs": { + "logos-capability-module": "logos-capability-module_4", + "logos-cpp-sdk": "logos-cpp-sdk_5", + "logos-module": "logos-module_9", + "logos-nix": "logos-nix_24", + "logos-package-manager": "logos-package-manager", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ], + "process-stats": "process-stats" + }, + "locked": { + "lastModified": 1778168472, + "narHash": "sha256-Td7Um8HOx4hG6k8ky3+bTsmgA9bgspI3eDs9LzVWv2s=", + "owner": "logos-co", + "repo": "logos-liblogos", + "rev": "94af58c819038e0eb5c2003f69d3260d964aa8f3", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-liblogos", + "type": "github" + } + }, + "logos-liblogos_3": { + "inputs": { + "logos-capability-module": "logos-capability-module_5", + "logos-cpp-sdk": "logos-cpp-sdk_11", + "logos-module": "logos-module_16", + "logos-nix": "logos-nix_96", + "logos-package-manager": "logos-package-manager_5", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-nix", + "nixpkgs" + ], + "process-stats": "process-stats_3" + }, + "locked": { + "lastModified": 1781104104, + "narHash": "sha256-RzYek00R1a+nTOejMKXER3WZLfO30A+3mCqvM6m7PfQ=", + "owner": "logos-co", + "repo": "logos-liblogos", + "rev": "68e408a5de5a21948684485582a5d03d7bfc5d78", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-liblogos", + "type": "github" + } + }, + "logos-liblogos_4": { + "inputs": { + "logos-capability-module": "logos-capability-module_7", + "logos-cpp-sdk": "logos-cpp-sdk_10", + "logos-module": "logos-module_15", + "logos-nix": "logos-nix_68", + "logos-package-manager": "logos-package-manager_3", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ], + "process-stats": "process-stats_2" + }, + "locked": { + "lastModified": 1778168472, + "narHash": "sha256-Td7Um8HOx4hG6k8ky3+bTsmgA9bgspI3eDs9LzVWv2s=", + "owner": "logos-co", + "repo": "logos-liblogos", + "rev": "94af58c819038e0eb5c2003f69d3260d964aa8f3", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-liblogos", + "type": "github" + } + }, + "logos-module": { + "inputs": { + "logos-nix": "logos-nix_2", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780603603, + "narHash": "sha256-CV7R+lwNIP0baXZtE9uNjqu7qDLiANwyGfMkHfTjcl0=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "780894dd40ebc8eded0fa97b1729286f31571cfb", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module-builder": { + "inputs": { + "logos-cpp-sdk": "logos-cpp-sdk", + "logos-module": "logos-module", + "logos-nix": "logos-nix_3", + "logos-plugin-core": "logos-plugin-core", + "logos-plugin-qt": "logos-plugin-qt", + "logos-standalone-app": "logos-standalone-app", + "logos-test-framework": "logos-test-framework_3", + "nix-bundle-lgx": "nix-bundle-lgx_8", + "nix-bundle-logos-module-install": "nix-bundle-logos-module-install_3", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781208169, + "narHash": "sha256-u+GUBX0Evswa2tRwuUybsiUU+7O0wnNh33GJDBwk9ps=", + "owner": "logos-co", + "repo": "logos-module-builder", + "rev": "fd07679ecfa1b2d8cfdd06799f3e03ed385b57f6", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module-builder", + "type": "github" + } + }, + "logos-module-builder_2": { + "inputs": { + "logos-cpp-sdk": "logos-cpp-sdk_2", + "logos-module": "logos-module_4", + "logos-nix": "logos-nix_10", + "logos-plugin-core": "logos-plugin-core_2", + "logos-plugin-qt": "logos-plugin-qt_2", + "logos-standalone-app": "logos-standalone-app_2", + "logos-test-framework": "logos-test-framework", + "nix-bundle-lgx": "nix-bundle-lgx_2", + "nix-bundle-logos-module-install": "nix-bundle-logos-module-install", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778177522, + "narHash": "sha256-sUGiatEU51cyZeATc3P/8b0myrAkCKavePpFwtkKxAI=", + "owner": "logos-co", + "repo": "logos-module-builder", + "rev": "b0e41abf3e14c0534b41933c5f8e3fc697319037", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module-builder", + "type": "github" + } + }, + "logos-module-builder_3": { + "inputs": { + "logos-cpp-sdk": "logos-cpp-sdk_7", + "logos-module": "logos-module_10", + "logos-nix": "logos-nix_54", + "logos-plugin-core": "logos-plugin-core_3", + "logos-plugin-qt": "logos-plugin-qt_3", + "logos-standalone-app": "logos-standalone-app_3", + "logos-test-framework": "logos-test-framework_2", + "nix-bundle-lgx": "nix-bundle-lgx_5", + "nix-bundle-logos-module-install": "nix-bundle-logos-module-install_2", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778177522, + "narHash": "sha256-sUGiatEU51cyZeATc3P/8b0myrAkCKavePpFwtkKxAI=", + "owner": "logos-co", + "repo": "logos-module-builder", + "rev": "b0e41abf3e14c0534b41933c5f8e3fc697319037", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module-builder", + "type": "github" + } + }, + "logos-module_10": { + "inputs": { + "logos-nix": "logos-nix_53", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_11": { + "inputs": { + "logos-nix": "logos-nix_55", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-core", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_12": { + "inputs": { + "logos-nix": "logos-nix_57", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-qt", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_13": { + "inputs": { + "logos-nix": "logos-nix_59", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773963329, + "narHash": "sha256-zdvDHoYWQDse0eJ/UCKIJcfuYJ8NMgl6QfxRcyDEovI=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "ac5a4f06ea94b01dd9c5fbb9ed4f20620beab88d", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_14": { + "inputs": { + "logos-nix": "logos-nix_64", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773963329, + "narHash": "sha256-zdvDHoYWQDse0eJ/UCKIJcfuYJ8NMgl6QfxRcyDEovI=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "ac5a4f06ea94b01dd9c5fbb9ed4f20620beab88d", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_15": { + "inputs": { + "logos-nix": "logos-nix_67", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_16": { + "inputs": { + "logos-nix": "logos-nix_95", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_17": { + "inputs": { + "logos-nix": "logos-nix_123", + "nixpkgs": [ + "logos-liblogos", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_2": { + "inputs": { + "logos-nix": "logos-nix_4", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-core", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_3": { + "inputs": { + "logos-nix": "logos-nix_6", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-qt", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_4": { + "inputs": { + "logos-nix": "logos-nix_9", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_5": { + "inputs": { + "logos-nix": "logos-nix_11", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-core", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_6": { + "inputs": { + "logos-nix": "logos-nix_13", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-qt", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_7": { + "inputs": { + "logos-nix": "logos-nix_15", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773963329, + "narHash": "sha256-zdvDHoYWQDse0eJ/UCKIJcfuYJ8NMgl6QfxRcyDEovI=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "ac5a4f06ea94b01dd9c5fbb9ed4f20620beab88d", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_8": { + "inputs": { + "logos-nix": "logos-nix_20", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773963329, + "narHash": "sha256-zdvDHoYWQDse0eJ/UCKIJcfuYJ8NMgl6QfxRcyDEovI=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "ac5a4f06ea94b01dd9c5fbb9ed4f20620beab88d", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-module_9": { + "inputs": { + "logos-nix": "logos-nix_23", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-module", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776369033, + "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, + "logos-nix": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_10": { + "inputs": { + "nixpkgs": "nixpkgs_11" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_100": { + "inputs": { + "nixpkgs": "nixpkgs_101" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_101": { + "inputs": { + "nixpkgs": "nixpkgs_102" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_102": { + "inputs": { + "nixpkgs": "nixpkgs_103" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_103": { + "inputs": { + "nixpkgs": "nixpkgs_104" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_104": { + "inputs": { + "nixpkgs": "nixpkgs_105" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_105": { + "inputs": { + "nixpkgs": "nixpkgs_106" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_106": { + "inputs": { + "nixpkgs": "nixpkgs_107" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_107": { + "inputs": { + "nixpkgs": "nixpkgs_108" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_108": { + "inputs": { + "nixpkgs": "nixpkgs_109" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_109": { + "inputs": { + "nixpkgs": "nixpkgs_110" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_11": { + "inputs": { + "nixpkgs": "nixpkgs_12" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_110": { + "inputs": { + "nixpkgs": "nixpkgs_111" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_111": { + "inputs": { + "nixpkgs": "nixpkgs_112" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_112": { + "inputs": { + "nixpkgs": "nixpkgs_113" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_113": { + "inputs": { + "nixpkgs": "nixpkgs_114" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_114": { + "inputs": { + "nixpkgs": "nixpkgs_115" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_115": { + "inputs": { + "nixpkgs": "nixpkgs_116" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_116": { + "inputs": { + "nixpkgs": "nixpkgs_117" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_117": { + "inputs": { + "nixpkgs": "nixpkgs_118" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_118": { + "inputs": { + "nixpkgs": "nixpkgs_119" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_119": { + "inputs": { + "nixpkgs": "nixpkgs_120" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_12": { + "inputs": { + "nixpkgs": "nixpkgs_13" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_120": { + "inputs": { + "nixpkgs": "nixpkgs_121" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_121": { + "inputs": { + "nixpkgs": "nixpkgs_122" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_122": { + "inputs": { + "nixpkgs": "nixpkgs_123" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_123": { + "inputs": { + "nixpkgs": "nixpkgs_124" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_124": { + "inputs": { + "nixpkgs": "nixpkgs_125" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_125": { + "inputs": { + "nixpkgs": "nixpkgs_126" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_126": { + "inputs": { + "nixpkgs": "nixpkgs_127" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_127": { + "inputs": { + "nixpkgs": "nixpkgs_128" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_128": { + "inputs": { + "nixpkgs": "nixpkgs_129" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_129": { + "inputs": { + "nixpkgs": "nixpkgs_130" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_13": { + "inputs": { + "nixpkgs": "nixpkgs_14" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_130": { + "inputs": { + "nixpkgs": "nixpkgs_131" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_14": { + "inputs": { + "nixpkgs": "nixpkgs_15" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_15": { + "inputs": { + "nixpkgs": "nixpkgs_16" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_16": { + "inputs": { + "nixpkgs": "nixpkgs_17" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_17": { + "inputs": { + "nixpkgs": "nixpkgs_18" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_18": { + "inputs": { + "nixpkgs": "nixpkgs_19" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_19": { + "inputs": { + "nixpkgs": "nixpkgs_20" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_2": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_20": { + "inputs": { + "nixpkgs": "nixpkgs_21" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_21": { + "inputs": { + "nixpkgs": "nixpkgs_22" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_22": { + "inputs": { + "nixpkgs": "nixpkgs_23" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_23": { + "inputs": { + "nixpkgs": "nixpkgs_24" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_24": { + "inputs": { + "nixpkgs": "nixpkgs_25" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_25": { + "inputs": { + "nixpkgs": "nixpkgs_26" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_26": { + "inputs": { + "nixpkgs": "nixpkgs_27" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_27": { + "inputs": { + "nixpkgs": "nixpkgs_28" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_28": { + "inputs": { + "nixpkgs": "nixpkgs_29" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_29": { + "inputs": { + "nixpkgs": "nixpkgs_30" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_3": { + "inputs": { + "nixpkgs": "nixpkgs_4" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_30": { + "inputs": { + "nixpkgs": "nixpkgs_31" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_31": { + "inputs": { + "nixpkgs": "nixpkgs_32" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_32": { + "inputs": { + "nixpkgs": "nixpkgs_33" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_33": { + "inputs": { + "nixpkgs": "nixpkgs_34" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_34": { + "inputs": { + "nixpkgs": "nixpkgs_35" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_35": { + "inputs": { + "nixpkgs": "nixpkgs_36" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_36": { + "inputs": { + "nixpkgs": "nixpkgs_37" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_37": { + "inputs": { + "nixpkgs": "nixpkgs_38" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_38": { + "inputs": { + "nixpkgs": "nixpkgs_39" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_39": { + "inputs": { + "nixpkgs": "nixpkgs_40" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_4": { + "inputs": { + "nixpkgs": "nixpkgs_5" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_40": { + "inputs": { + "nixpkgs": "nixpkgs_41" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_41": { + "inputs": { + "nixpkgs": "nixpkgs_42" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_42": { + "inputs": { + "nixpkgs": "nixpkgs_43" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_43": { + "inputs": { + "nixpkgs": "nixpkgs_44" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_44": { + "inputs": { + "nixpkgs": "nixpkgs_45" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_45": { + "inputs": { + "nixpkgs": "nixpkgs_46" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_46": { + "inputs": { + "nixpkgs": "nixpkgs_47" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_47": { + "inputs": { + "nixpkgs": "nixpkgs_48" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_48": { + "inputs": { + "nixpkgs": "nixpkgs_49" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_49": { + "inputs": { + "nixpkgs": "nixpkgs_50" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_5": { + "inputs": { + "nixpkgs": "nixpkgs_6" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_50": { + "inputs": { + "nixpkgs": "nixpkgs_51" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_51": { + "inputs": { + "nixpkgs": "nixpkgs_52" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_52": { + "inputs": { + "nixpkgs": "nixpkgs_53" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_53": { + "inputs": { + "nixpkgs": "nixpkgs_54" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_54": { + "inputs": { + "nixpkgs": "nixpkgs_55" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_55": { + "inputs": { + "nixpkgs": "nixpkgs_56" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_56": { + "inputs": { + "nixpkgs": "nixpkgs_57" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_57": { + "inputs": { + "nixpkgs": "nixpkgs_58" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_58": { + "inputs": { + "nixpkgs": "nixpkgs_59" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_59": { + "inputs": { + "nixpkgs": "nixpkgs_60" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_6": { + "inputs": { + "nixpkgs": "nixpkgs_7" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_60": { + "inputs": { + "nixpkgs": "nixpkgs_61" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_61": { + "inputs": { + "nixpkgs": "nixpkgs_62" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_62": { + "inputs": { + "nixpkgs": "nixpkgs_63" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_63": { + "inputs": { + "nixpkgs": "nixpkgs_64" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_64": { + "inputs": { + "nixpkgs": "nixpkgs_65" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_65": { + "inputs": { + "nixpkgs": "nixpkgs_66" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_66": { + "inputs": { + "nixpkgs": "nixpkgs_67" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_67": { + "inputs": { + "nixpkgs": "nixpkgs_68" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_68": { + "inputs": { + "nixpkgs": "nixpkgs_69" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_69": { + "inputs": { + "nixpkgs": "nixpkgs_70" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_7": { + "inputs": { + "nixpkgs": "nixpkgs_8" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_70": { + "inputs": { + "nixpkgs": "nixpkgs_71" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_71": { + "inputs": { + "nixpkgs": "nixpkgs_72" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_72": { + "inputs": { + "nixpkgs": "nixpkgs_73" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_73": { + "inputs": { + "nixpkgs": "nixpkgs_74" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_74": { + "inputs": { + "nixpkgs": "nixpkgs_75" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_75": { + "inputs": { + "nixpkgs": "nixpkgs_76" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_76": { + "inputs": { + "nixpkgs": "nixpkgs_77" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_77": { + "inputs": { + "nixpkgs": "nixpkgs_78" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_78": { + "inputs": { + "nixpkgs": "nixpkgs_79" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_79": { + "inputs": { + "nixpkgs": "nixpkgs_80" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_8": { + "inputs": { + "nixpkgs": "nixpkgs_9" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_80": { + "inputs": { + "nixpkgs": "nixpkgs_81" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_81": { + "inputs": { + "nixpkgs": "nixpkgs_82" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_82": { + "inputs": { + "nixpkgs": "nixpkgs_83" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_83": { + "inputs": { + "nixpkgs": "nixpkgs_84" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_84": { + "inputs": { + "nixpkgs": "nixpkgs_85" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_85": { + "inputs": { + "nixpkgs": "nixpkgs_86" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_86": { + "inputs": { + "nixpkgs": "nixpkgs_87" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_87": { + "inputs": { + "nixpkgs": "nixpkgs_88" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_88": { + "inputs": { + "nixpkgs": "nixpkgs_89" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_89": { + "inputs": { + "nixpkgs": "nixpkgs_90" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_9": { + "inputs": { + "nixpkgs": "nixpkgs_10" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_90": { + "inputs": { + "nixpkgs": "nixpkgs_91" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_91": { + "inputs": { + "nixpkgs": "nixpkgs_92" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_92": { + "inputs": { + "nixpkgs": "nixpkgs_93" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_93": { + "inputs": { + "nixpkgs": "nixpkgs_94" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_94": { + "inputs": { + "nixpkgs": "nixpkgs_95" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_95": { + "inputs": { + "nixpkgs": "nixpkgs_96" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_96": { + "inputs": { + "nixpkgs": "nixpkgs_97" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_97": { + "inputs": { + "nixpkgs": "nixpkgs_98" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_98": { + "inputs": { + "nixpkgs": "nixpkgs_99" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_99": { + "inputs": { + "nixpkgs": "nixpkgs_100" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-package": { + "inputs": { + "logos-nix": "logos-nix_26", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package-manager": { + "inputs": { + "logos-nix": "logos-nix_25", + "logos-package": "logos-package", + "nix-bundle-appimage": "nix-bundle-appimage", + "nix-bundle-dir": "nix-bundle-dir_2", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776374462, + "narHash": "sha256-HMkuqSLdScAWTwXEWjhqx9Yk82GiPzPIfRaHTvjG730=", + "owner": "logos-co", + "repo": "logos-package-manager", + "rev": "9101875bc103214855bc6217834e22e66802ed86", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package-manager", + "type": "github" + } + }, + "logos-package-manager_2": { + "inputs": { + "logos-nix": "logos-nix_42", + "logos-package": "logos-package_4", + "nix-bundle-appimage": "nix-bundle-appimage_2", + "nix-bundle-dir": "nix-bundle-dir_6", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836847, + "narHash": "sha256-pU7GShEizE8HkDGvR9NWZPqksiGyvfcWodtFyc318TM=", + "owner": "logos-co", + "repo": "logos-package-manager", + "rev": "39118d6c52226e88a77c6ff7d1196229f56b757e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package-manager", + "type": "github" + } + }, + "logos-package-manager_3": { + "inputs": { + "logos-nix": "logos-nix_69", + "logos-package": "logos-package_6", + "nix-bundle-appimage": "nix-bundle-appimage_3", + "nix-bundle-dir": "nix-bundle-dir_9", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776374462, + "narHash": "sha256-HMkuqSLdScAWTwXEWjhqx9Yk82GiPzPIfRaHTvjG730=", + "owner": "logos-co", + "repo": "logos-package-manager", + "rev": "9101875bc103214855bc6217834e22e66802ed86", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package-manager", + "type": "github" + } + }, + "logos-package-manager_4": { + "inputs": { + "logos-nix": "logos-nix_86", + "logos-package": "logos-package_9", + "nix-bundle-appimage": "nix-bundle-appimage_4", + "nix-bundle-dir": "nix-bundle-dir_13", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836847, + "narHash": "sha256-pU7GShEizE8HkDGvR9NWZPqksiGyvfcWodtFyc318TM=", + "owner": "logos-co", + "repo": "logos-package-manager", + "rev": "39118d6c52226e88a77c6ff7d1196229f56b757e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package-manager", + "type": "github" + } + }, + "logos-package-manager_5": { + "inputs": { + "logos-nix": "logos-nix_97", + "logos-package": "logos-package_11", + "nix-bundle-appimage": "nix-bundle-appimage_5", + "nix-bundle-dir": "nix-bundle-dir_16", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776374462, + "narHash": "sha256-HMkuqSLdScAWTwXEWjhqx9Yk82GiPzPIfRaHTvjG730=", + "owner": "logos-co", + "repo": "logos-package-manager", + "rev": "9101875bc103214855bc6217834e22e66802ed86", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package-manager", + "type": "github" + } + }, + "logos-package-manager_6": { + "inputs": { + "logos-nix": "logos-nix_114", + "logos-package": "logos-package_14", + "nix-bundle-appimage": "nix-bundle-appimage_6", + "nix-bundle-dir": "nix-bundle-dir_20", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836847, + "narHash": "sha256-pU7GShEizE8HkDGvR9NWZPqksiGyvfcWodtFyc318TM=", + "owner": "logos-co", + "repo": "logos-package-manager", + "rev": "39118d6c52226e88a77c6ff7d1196229f56b757e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package-manager", + "type": "github" + } + }, + "logos-package-manager_7": { + "inputs": { + "logos-nix": "logos-nix_125", + "logos-package": "logos-package_16", + "nix-bundle-appimage": "nix-bundle-appimage_7", + "nix-bundle-dir": "nix-bundle-dir_23", + "nixpkgs": [ + "logos-liblogos", + "logos-package-manager", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776374462, + "narHash": "sha256-HMkuqSLdScAWTwXEWjhqx9Yk82GiPzPIfRaHTvjG730=", + "owner": "logos-co", + "repo": "logos-package-manager", + "rev": "9101875bc103214855bc6217834e22e66802ed86", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package-manager", + "type": "github" + } + }, + "logos-package_10": { + "inputs": { + "logos-nix": "logos-nix_92", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_11": { + "inputs": { + "logos-nix": "logos-nix_98", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_12": { + "inputs": { + "logos-nix": "logos-nix_107", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575342, + "narHash": "sha256-l/tL39ZVNz3nWpE9zWpC/kg11QHSQJ2HHp3W3yv1pNI=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "118ccc4efd04eb0c4d541be8db7f1075e014ac39", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_13": { + "inputs": { + "logos-nix": "logos-nix_111", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575342, + "narHash": "sha256-l/tL39ZVNz3nWpE9zWpC/kg11QHSQJ2HHp3W3yv1pNI=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "118ccc4efd04eb0c4d541be8db7f1075e014ac39", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_14": { + "inputs": { + "logos-nix": "logos-nix_115", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_15": { + "inputs": { + "logos-nix": "logos-nix_120", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_16": { + "inputs": { + "logos-nix": "logos-nix_126", + "nixpkgs": [ + "logos-liblogos", + "logos-package-manager", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_2": { + "inputs": { + "logos-nix": "logos-nix_35", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575342, + "narHash": "sha256-l/tL39ZVNz3nWpE9zWpC/kg11QHSQJ2HHp3W3yv1pNI=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "118ccc4efd04eb0c4d541be8db7f1075e014ac39", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_3": { + "inputs": { + "logos-nix": "logos-nix_39", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575342, + "narHash": "sha256-l/tL39ZVNz3nWpE9zWpC/kg11QHSQJ2HHp3W3yv1pNI=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "118ccc4efd04eb0c4d541be8db7f1075e014ac39", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_4": { + "inputs": { + "logos-nix": "logos-nix_43", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_5": { + "inputs": { + "logos-nix": "logos-nix_48", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_6": { + "inputs": { + "logos-nix": "logos-nix_70", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_7": { + "inputs": { + "logos-nix": "logos-nix_79", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575342, + "narHash": "sha256-l/tL39ZVNz3nWpE9zWpC/kg11QHSQJ2HHp3W3yv1pNI=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "118ccc4efd04eb0c4d541be8db7f1075e014ac39", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_8": { + "inputs": { + "logos-nix": "logos-nix_83", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575342, + "narHash": "sha256-l/tL39ZVNz3nWpE9zWpC/kg11QHSQJ2HHp3W3yv1pNI=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "118ccc4efd04eb0c4d541be8db7f1075e014ac39", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_9": { + "inputs": { + "logos-nix": "logos-nix_87", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-plugin-core": { + "inputs": { + "logos-module": "logos-module_2", + "logos-nix": "logos-nix_5", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-core", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780929868, + "narHash": "sha256-yDxv9Cv5VEJDVqHz9qYCU3UXv+kCIjRoD90A7uGYuGU=", + "owner": "logos-co", + "repo": "logos-plugin-qt", + "rev": "68090d0a976224ca1c3c606c486a9ac3dd7a4559", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-plugin-qt", + "type": "github" + } + }, + "logos-plugin-core_2": { + "inputs": { + "logos-module": "logos-module_5", + "logos-nix": "logos-nix_12", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-core", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778168218, + "narHash": "sha256-EGHUsyKaA7IjnBKNysFD62Qlqsd0GyxyhbuYNAS0+Tg=", + "owner": "logos-co", + "repo": "logos-plugin-qt", + "rev": "8c45eca86db8fba8689326c35d0ec8c2b3cdd9a0", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-plugin-qt", + "type": "github" + } + }, + "logos-plugin-core_3": { + "inputs": { + "logos-module": "logos-module_11", + "logos-nix": "logos-nix_56", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-core", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778168218, + "narHash": "sha256-EGHUsyKaA7IjnBKNysFD62Qlqsd0GyxyhbuYNAS0+Tg=", + "owner": "logos-co", + "repo": "logos-plugin-qt", + "rev": "8c45eca86db8fba8689326c35d0ec8c2b3cdd9a0", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-plugin-qt", + "type": "github" + } + }, + "logos-plugin-qt": { + "inputs": { + "logos-module": "logos-module_3", + "logos-nix": "logos-nix_7", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-qt", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780929868, + "narHash": "sha256-yDxv9Cv5VEJDVqHz9qYCU3UXv+kCIjRoD90A7uGYuGU=", + "owner": "logos-co", + "repo": "logos-plugin-qt", + "rev": "68090d0a976224ca1c3c606c486a9ac3dd7a4559", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-plugin-qt", + "type": "github" + } + }, + "logos-plugin-qt_2": { + "inputs": { + "logos-module": "logos-module_6", + "logos-nix": "logos-nix_14", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-qt", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778168218, + "narHash": "sha256-EGHUsyKaA7IjnBKNysFD62Qlqsd0GyxyhbuYNAS0+Tg=", + "owner": "logos-co", + "repo": "logos-plugin-qt", + "rev": "8c45eca86db8fba8689326c35d0ec8c2b3cdd9a0", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-plugin-qt", + "type": "github" + } + }, + "logos-plugin-qt_3": { + "inputs": { + "logos-module": "logos-module_12", + "logos-nix": "logos-nix_58", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-plugin-qt", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778168218, + "narHash": "sha256-EGHUsyKaA7IjnBKNysFD62Qlqsd0GyxyhbuYNAS0+Tg=", + "owner": "logos-co", + "repo": "logos-plugin-qt", + "rev": "8c45eca86db8fba8689326c35d0ec8c2b3cdd9a0", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-plugin-qt", + "type": "github" + } + }, + "logos-qt-mcp": { + "inputs": { + "logos-nix": "logos-nix_32", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455349, + "narHash": "sha256-rebrtH1UxC1hDuwQBwyYbGzNCrnuuqiVL7OvzUhk65k=", + "owner": "logos-co", + "repo": "logos-qt-mcp", + "rev": "c5223b4b640add09e461983b8fddbd12c8b31f4f", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-qt-mcp", + "type": "github" + } + }, + "logos-qt-mcp_2": { + "inputs": { + "logos-nix": "logos-nix_76", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455349, + "narHash": "sha256-rebrtH1UxC1hDuwQBwyYbGzNCrnuuqiVL7OvzUhk65k=", + "owner": "logos-co", + "repo": "logos-qt-mcp", + "rev": "c5223b4b640add09e461983b8fddbd12c8b31f4f", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-qt-mcp", + "type": "github" + } + }, + "logos-qt-mcp_3": { + "inputs": { + "logos-nix": "logos-nix_104", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-qt-mcp", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781077555, + "narHash": "sha256-rkqva3DbhhxWrVp6KLyhVxbAK2MLIWfUcPG5HM7wnsQ=", + "owner": "logos-co", + "repo": "logos-qt-mcp", + "rev": "c87f6bd985a07ec2809081c8d0830108aa895819", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-qt-mcp", + "type": "github" + } + }, + "logos-standalone-app": { + "inputs": { + "logos-capability-module": "logos-capability-module_2", + "logos-cpp-sdk": "logos-cpp-sdk_6", + "logos-design-system": "logos-design-system_2", + "logos-liblogos": "logos-liblogos_3", + "logos-nix": "logos-nix_103", + "logos-qt-mcp": "logos-qt-mcp_3", + "logos-view-module-runtime": "logos-view-module-runtime_3", + "nix-bundle-lgx": "nix-bundle-lgx_7", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781127963, + "narHash": "sha256-Z7vO0bm1trVctlqsDc+p6lLYEC1K5qEJctgdSkVG2YI=", + "owner": "logos-co", + "repo": "logos-standalone-app", + "rev": "0206e2ea6c43d78d083ba7e4e002fe34a60b77ac", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-standalone-app", + "type": "github" + } + }, + "logos-standalone-app_2": { + "inputs": { + "logos-capability-module": "logos-capability-module_3", + "logos-cpp-sdk": "logos-cpp-sdk_3", + "logos-design-system": "logos-design-system", + "logos-liblogos": "logos-liblogos_2", + "logos-nix": "logos-nix_31", + "logos-qt-mcp": "logos-qt-mcp", + "logos-view-module-runtime": "logos-view-module-runtime", + "nix-bundle-lgx": "nix-bundle-lgx", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778177364, + "narHash": "sha256-k05qvbRM2fR7SAEKaElXZNtneWJx/IN/yEx4JMnxv1A=", + "owner": "logos-co", + "repo": "logos-standalone-app", + "rev": "a749953e0e9f3d716856d2bd818cd5757aeb065d", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-standalone-app", + "type": "github" + } + }, + "logos-standalone-app_3": { + "inputs": { + "logos-capability-module": "logos-capability-module_6", + "logos-cpp-sdk": "logos-cpp-sdk_8", + "logos-design-system": "logos-design-system_3", + "logos-liblogos": "logos-liblogos_4", + "logos-nix": "logos-nix_75", + "logos-qt-mcp": "logos-qt-mcp_2", + "logos-view-module-runtime": "logos-view-module-runtime_2", + "nix-bundle-lgx": "nix-bundle-lgx_4", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778177364, + "narHash": "sha256-k05qvbRM2fR7SAEKaElXZNtneWJx/IN/yEx4JMnxv1A=", + "owner": "logos-co", + "repo": "logos-standalone-app", + "rev": "a749953e0e9f3d716856d2bd818cd5757aeb065d", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-standalone-app", + "type": "github" + } + }, + "logos-test-framework": { + "inputs": { + "logos-cpp-sdk": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-cpp-sdk" + ], + "logos-nix": "logos-nix_37", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-test-framework", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778168561, + "narHash": "sha256-BINVw7hztdrDkoDSdmnKZes7A15g1SmXKjIHpjZv/3I=", + "owner": "logos-co", + "repo": "logos-test-framework", + "rev": "44816073a437fde4203e140b896e70a387c0678a", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-test-framework", + "type": "github" + } + }, + "logos-test-framework_2": { + "inputs": { + "logos-cpp-sdk": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-cpp-sdk" + ], + "logos-nix": "logos-nix_81", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-test-framework", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778168561, + "narHash": "sha256-BINVw7hztdrDkoDSdmnKZes7A15g1SmXKjIHpjZv/3I=", + "owner": "logos-co", + "repo": "logos-test-framework", + "rev": "44816073a437fde4203e140b896e70a387c0678a", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-test-framework", + "type": "github" + } + }, + "logos-test-framework_3": { + "inputs": { + "logos-cpp-sdk": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-cpp-sdk" + ], + "logos-nix": "logos-nix_109", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-test-framework", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780432542, + "narHash": "sha256-LcZnXuNTCyFQS7rhOHF3sy+oF6PNq/H1MLPHch08XrU=", + "owner": "logos-co", + "repo": "logos-test-framework", + "rev": "ee081954096f602b47308c6dc7d00fb71d5dcdc7", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-test-framework", + "type": "github" + } + }, + "logos-view-module-runtime": { + "inputs": { + "logos-cpp-sdk": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk" + ], + "logos-nix": "logos-nix_33", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778168591, + "narHash": "sha256-gHapkm3yh3YNuM8EaFvThrJ2qVjcKKr5KA6wFfE0M7Q=", + "owner": "logos-co", + "repo": "logos-view-module-runtime", + "rev": "3c3735c25c8d66cc713623420dc74e445442592a", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-view-module-runtime", + "type": "github" + } + }, + "logos-view-module-runtime_2": { + "inputs": { + "logos-cpp-sdk": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk" + ], + "logos-nix": "logos-nix_77", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778168591, + "narHash": "sha256-gHapkm3yh3YNuM8EaFvThrJ2qVjcKKr5KA6wFfE0M7Q=", + "owner": "logos-co", + "repo": "logos-view-module-runtime", + "rev": "3c3735c25c8d66cc713623420dc74e445442592a", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-view-module-runtime", + "type": "github" + } + }, + "logos-view-module-runtime_3": { + "inputs": { + "logos-cpp-sdk": "logos-cpp-sdk_12", + "logos-nix": "logos-nix_105", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781120903, + "narHash": "sha256-dPi2gaFPB0mfDLyFRO2xVo88GODYgs/akT5CNy1J2o0=", + "owner": "logos-co", + "repo": "logos-view-module-runtime", + "rev": "bec7d3e6041c33109d6696276e634a5258ba788c", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-view-module-runtime", + "type": "github" + } + }, + "nix-bundle-appimage": { + "inputs": { + "logos-nix": "logos-nix_27", + "nix-bundle-dir": "nix-bundle-dir", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455478, + "narHash": "sha256-S8IMfdDc+2Wwri0krLDsIUwSqmwanmvHAJWHOFo8ykk=", + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "rev": "2428125a4a1b34ad9119efa97edb98676283e3da", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "type": "github" + } + }, + "nix-bundle-appimage_2": { + "inputs": { + "logos-nix": "logos-nix_44", + "nix-bundle-dir": "nix-bundle-dir_5", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-appimage", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455478, + "narHash": "sha256-S8IMfdDc+2Wwri0krLDsIUwSqmwanmvHAJWHOFo8ykk=", + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "rev": "2428125a4a1b34ad9119efa97edb98676283e3da", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "type": "github" + } + }, + "nix-bundle-appimage_3": { + "inputs": { + "logos-nix": "logos-nix_71", + "nix-bundle-dir": "nix-bundle-dir_8", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455478, + "narHash": "sha256-S8IMfdDc+2Wwri0krLDsIUwSqmwanmvHAJWHOFo8ykk=", + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "rev": "2428125a4a1b34ad9119efa97edb98676283e3da", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "type": "github" + } + }, + "nix-bundle-appimage_4": { + "inputs": { + "logos-nix": "logos-nix_88", + "nix-bundle-dir": "nix-bundle-dir_12", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-appimage", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455478, + "narHash": "sha256-S8IMfdDc+2Wwri0krLDsIUwSqmwanmvHAJWHOFo8ykk=", + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "rev": "2428125a4a1b34ad9119efa97edb98676283e3da", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "type": "github" + } + }, + "nix-bundle-appimage_5": { + "inputs": { + "logos-nix": "logos-nix_99", + "nix-bundle-dir": "nix-bundle-dir_15", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455478, + "narHash": "sha256-S8IMfdDc+2Wwri0krLDsIUwSqmwanmvHAJWHOFo8ykk=", + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "rev": "2428125a4a1b34ad9119efa97edb98676283e3da", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "type": "github" + } + }, + "nix-bundle-appimage_6": { + "inputs": { + "logos-nix": "logos-nix_116", + "nix-bundle-dir": "nix-bundle-dir_19", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-appimage", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455478, + "narHash": "sha256-S8IMfdDc+2Wwri0krLDsIUwSqmwanmvHAJWHOFo8ykk=", + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "rev": "2428125a4a1b34ad9119efa97edb98676283e3da", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "type": "github" + } + }, + "nix-bundle-appimage_7": { + "inputs": { + "logos-nix": "logos-nix_127", + "nix-bundle-dir": "nix-bundle-dir_22", + "nixpkgs": [ + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455478, + "narHash": "sha256-S8IMfdDc+2Wwri0krLDsIUwSqmwanmvHAJWHOFo8ykk=", + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "rev": "2428125a4a1b34ad9119efa97edb98676283e3da", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "type": "github" + } + }, + "nix-bundle-dir": { + "inputs": { + "logos-nix": "logos-nix_28", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773961179, + "narHash": "sha256-bpaTvz//R8WFP5xnnDLv3a9l7unDmBwJjCewx3wCjzM=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "cd214dbf15487d80967389847ae2210468be6ebf", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_10": { + "inputs": { + "logos-nix": "logos-nix_80", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_11": { + "inputs": { + "logos-nix": "logos-nix_84", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_12": { + "inputs": { + "logos-nix": "logos-nix_89", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-appimage", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773961179, + "narHash": "sha256-bpaTvz//R8WFP5xnnDLv3a9l7unDmBwJjCewx3wCjzM=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "cd214dbf15487d80967389847ae2210468be6ebf", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_13": { + "inputs": { + "logos-nix": "logos-nix_90", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_14": { + "inputs": { + "logos-nix": "logos-nix_93", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_15": { + "inputs": { + "logos-nix": "logos-nix_100", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773961179, + "narHash": "sha256-bpaTvz//R8WFP5xnnDLv3a9l7unDmBwJjCewx3wCjzM=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "cd214dbf15487d80967389847ae2210468be6ebf", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_16": { + "inputs": { + "logos-nix": "logos-nix_101", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_17": { + "inputs": { + "logos-nix": "logos-nix_108", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_18": { + "inputs": { + "logos-nix": "logos-nix_112", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_19": { + "inputs": { + "logos-nix": "logos-nix_117", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-appimage", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773961179, + "narHash": "sha256-bpaTvz//R8WFP5xnnDLv3a9l7unDmBwJjCewx3wCjzM=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "cd214dbf15487d80967389847ae2210468be6ebf", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_2": { + "inputs": { + "logos-nix": "logos-nix_29", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_20": { + "inputs": { + "logos-nix": "logos-nix_118", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_21": { + "inputs": { + "logos-nix": "logos-nix_121", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_22": { + "inputs": { + "logos-nix": "logos-nix_128", + "nixpkgs": [ + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773961179, + "narHash": "sha256-bpaTvz//R8WFP5xnnDLv3a9l7unDmBwJjCewx3wCjzM=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "cd214dbf15487d80967389847ae2210468be6ebf", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_23": { + "inputs": { + "logos-nix": "logos-nix_129", + "nixpkgs": [ + "logos-liblogos", + "logos-package-manager", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_3": { + "inputs": { + "logos-nix": "logos-nix_36", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_4": { + "inputs": { + "logos-nix": "logos-nix_40", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_5": { + "inputs": { + "logos-nix": "logos-nix_45", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-appimage", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773961179, + "narHash": "sha256-bpaTvz//R8WFP5xnnDLv3a9l7unDmBwJjCewx3wCjzM=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "cd214dbf15487d80967389847ae2210468be6ebf", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_6": { + "inputs": { + "logos-nix": "logos-nix_46", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_7": { + "inputs": { + "logos-nix": "logos-nix_49", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_8": { + "inputs": { + "logos-nix": "logos-nix_72", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773961179, + "narHash": "sha256-bpaTvz//R8WFP5xnnDLv3a9l7unDmBwJjCewx3wCjzM=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "cd214dbf15487d80967389847ae2210468be6ebf", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_9": { + "inputs": { + "logos-nix": "logos-nix_73", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-lgx": { + "inputs": { + "logos-nix": "logos-nix_34", + "logos-package": "logos-package_2", + "nix-bundle-dir": "nix-bundle-dir_3", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575520, + "narHash": "sha256-HOwg1N4NfGYq579IppVPpVjPDZfYQGndXGlcl1VRXXo=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "3c44d99b9d8dbd8a135b44b5b328e6175650305e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_2": { + "inputs": { + "logos-nix": "logos-nix_38", + "logos-package": "logos-package_3", + "nix-bundle-dir": "nix-bundle-dir_4", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-lgx", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575520, + "narHash": "sha256-HOwg1N4NfGYq579IppVPpVjPDZfYQGndXGlcl1VRXXo=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "3c44d99b9d8dbd8a135b44b5b328e6175650305e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_3": { + "inputs": { + "logos-nix": "logos-nix_47", + "logos-package": "logos-package_5", + "nix-bundle-dir": "nix-bundle-dir_7", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836380, + "narHash": "sha256-XbBPcMuDFA/SxYVw9TIRQbhie5Vj5MqwdU+Gh1iR1LA=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "9d8f8602b1574ec9ac4c9b31ae0c92570221c268", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_4": { + "inputs": { + "logos-nix": "logos-nix_78", + "logos-package": "logos-package_7", + "nix-bundle-dir": "nix-bundle-dir_10", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575520, + "narHash": "sha256-HOwg1N4NfGYq579IppVPpVjPDZfYQGndXGlcl1VRXXo=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "3c44d99b9d8dbd8a135b44b5b328e6175650305e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_5": { + "inputs": { + "logos-nix": "logos-nix_82", + "logos-package": "logos-package_8", + "nix-bundle-dir": "nix-bundle-dir_11", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-lgx", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575520, + "narHash": "sha256-HOwg1N4NfGYq579IppVPpVjPDZfYQGndXGlcl1VRXXo=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "3c44d99b9d8dbd8a135b44b5b328e6175650305e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_6": { + "inputs": { + "logos-nix": "logos-nix_91", + "logos-package": "logos-package_10", + "nix-bundle-dir": "nix-bundle-dir_14", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836380, + "narHash": "sha256-XbBPcMuDFA/SxYVw9TIRQbhie5Vj5MqwdU+Gh1iR1LA=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "9d8f8602b1574ec9ac4c9b31ae0c92570221c268", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_7": { + "inputs": { + "logos-nix": "logos-nix_106", + "logos-package": "logos-package_12", + "nix-bundle-dir": "nix-bundle-dir_17", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "nix-bundle-lgx", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575520, + "narHash": "sha256-HOwg1N4NfGYq579IppVPpVjPDZfYQGndXGlcl1VRXXo=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "3c44d99b9d8dbd8a135b44b5b328e6175650305e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_8": { + "inputs": { + "logos-nix": "logos-nix_110", + "logos-package": "logos-package_13", + "nix-bundle-dir": "nix-bundle-dir_18", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-lgx", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777575520, + "narHash": "sha256-HOwg1N4NfGYq579IppVPpVjPDZfYQGndXGlcl1VRXXo=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "3c44d99b9d8dbd8a135b44b5b328e6175650305e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_9": { + "inputs": { + "logos-nix": "logos-nix_119", + "logos-package": "logos-package_15", + "nix-bundle-dir": "nix-bundle-dir_21", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836380, + "narHash": "sha256-XbBPcMuDFA/SxYVw9TIRQbhie5Vj5MqwdU+Gh1iR1LA=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "9d8f8602b1574ec9ac4c9b31ae0c92570221c268", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-logos-module-install": { + "inputs": { + "logos-nix": "logos-nix_41", + "logos-package-manager": "logos-package-manager_2", + "nix-bundle-lgx": "nix-bundle-lgx_3", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775839388, + "narHash": "sha256-0QH146bzL2kKBYJq2yA35iPwug55j2xjEyKCS7tjhvg=", + "owner": "logos-co", + "repo": "nix-bundle-logos-module-install", + "rev": "89cc9ea91275396d589c767d76926459ac77ef20", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-logos-module-install", + "type": "github" + } + }, + "nix-bundle-logos-module-install_2": { + "inputs": { + "logos-nix": "logos-nix_85", + "logos-package-manager": "logos-package-manager_4", + "nix-bundle-lgx": "nix-bundle-lgx_6", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775839388, + "narHash": "sha256-0QH146bzL2kKBYJq2yA35iPwug55j2xjEyKCS7tjhvg=", + "owner": "logos-co", + "repo": "nix-bundle-logos-module-install", + "rev": "89cc9ea91275396d589c767d76926459ac77ef20", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-logos-module-install", + "type": "github" + } + }, + "nix-bundle-logos-module-install_3": { + "inputs": { + "logos-nix": "logos-nix_113", + "logos-package-manager": "logos-package-manager_6", + "nix-bundle-lgx": "nix-bundle-lgx_9", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775839388, + "narHash": "sha256-0QH146bzL2kKBYJq2yA35iPwug55j2xjEyKCS7tjhvg=", + "owner": "logos-co", + "repo": "nix-bundle-logos-module-install", + "rev": "89cc9ea91275396d589c767d76926459ac77ef20", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-logos-module-install", "type": "github" } }, @@ -2380,6 +8697,166 @@ "type": "github" } }, + "nixpkgs_100": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_101": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_102": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_103": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_104": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_105": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_106": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_107": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_108": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_109": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_11": { "locked": { "lastModified": 1759036355, @@ -2396,6 +8873,166 @@ "type": "github" } }, + "nixpkgs_110": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_111": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_112": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_113": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_114": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_115": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_116": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_117": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_118": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_119": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_12": { "locked": { "lastModified": 1759036355, @@ -2412,6 +9049,166 @@ "type": "github" } }, + "nixpkgs_120": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_121": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_122": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_123": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_124": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_125": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_126": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_127": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_128": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_129": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_13": { "locked": { "lastModified": 1759036355, @@ -2428,6 +9225,54 @@ "type": "github" } }, + "nixpkgs_130": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_131": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_132": { + "locked": { + "lastModified": 1767313136, + "narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.05", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_14": { "locked": { "lastModified": 1759036355, @@ -3116,6 +9961,118 @@ "type": "github" } }, + "nixpkgs_53": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_54": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_55": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_56": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_57": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_58": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_59": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_6": { "locked": { "lastModified": 1759036355, @@ -3132,6 +10089,166 @@ "type": "github" } }, + "nixpkgs_60": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_61": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_62": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_63": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_64": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_65": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_66": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_67": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_68": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_69": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_7": { "locked": { "lastModified": 1759036355, @@ -3148,6 +10265,166 @@ "type": "github" } }, + "nixpkgs_70": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_71": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_72": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_73": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_74": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_75": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_76": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_77": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_78": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_79": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_8": { "locked": { "lastModified": 1759036355, @@ -3164,6 +10441,166 @@ "type": "github" } }, + "nixpkgs_80": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_81": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_82": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_83": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_84": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_85": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_86": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_87": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_88": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_89": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_9": { "locked": { "lastModified": 1759036355, @@ -3180,14 +10617,177 @@ "type": "github" } }, + "nixpkgs_90": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_91": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_92": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_93": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_94": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_95": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_96": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_97": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_98": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_99": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "process-stats": { "inputs": { - "logos-nix": "logos-nix_23", + "logos-nix": "logos-nix_30", "nixpkgs": [ "logos-liblogos", "logos-capability-module", "logos-module-builder", "logos-standalone-app", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", "logos-liblogos", "process-stats", "logos-nix", @@ -3210,7 +10810,67 @@ }, "process-stats_2": { "inputs": { - "logos-nix": "logos-nix_51", + "logos-nix": "logos-nix_74", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "process-stats", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775744159, + "narHash": "sha256-hTVAnDREBQOVHML6KU3K7Ge0CRBqnFIg7uYL7qDnD8o=", + "owner": "logos-co", + "repo": "process-stats", + "rev": "33ace1270f90c89b3565e803139c0970fcd1ce8f", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "process-stats", + "type": "github" + } + }, + "process-stats_3": { + "inputs": { + "logos-nix": "logos-nix_102", + "nixpkgs": [ + "logos-liblogos", + "logos-capability-module", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "process-stats", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775744159, + "narHash": "sha256-hTVAnDREBQOVHML6KU3K7Ge0CRBqnFIg7uYL7qDnD8o=", + "owner": "logos-co", + "repo": "process-stats", + "rev": "33ace1270f90c89b3565e803139c0970fcd1ce8f", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "process-stats", + "type": "github" + } + }, + "process-stats_4": { + "inputs": { + "logos-nix": "logos-nix_130", "nixpkgs": [ "logos-liblogos", "process-stats", @@ -3241,7 +10901,8 @@ "logos-liblogos", "nixpkgs" ], - "rust-overlay": "rust-overlay" + "rust-overlay": "rust-overlay", + "rust-rapidsnark": "rust-rapidsnark" } }, "rust-overlay": { @@ -3251,11 +10912,11 @@ ] }, "locked": { - "lastModified": 1780975129, - "narHash": "sha256-428T1pLXnbVeUgZx2wWEkbvl3ZORjras34ANZ3ACp5A=", + "lastModified": 1781234414, + "narHash": "sha256-HdA+P4fKRGOomkewnI/Tww5Wz4xK1O7+hDO90YAsPB4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "fe64e6b409dc513274d2941f8da13bbd0fdcf44e", + "rev": "1d18bfe3de6244c641ca4e8011186d0981b81d76", "type": "github" }, "original": { @@ -3263,6 +10924,25 @@ "repo": "rust-overlay", "type": "github" } + }, + "rust-rapidsnark": { + "inputs": { + "nixpkgs": "nixpkgs_132" + }, + "locked": { + "lastModified": 1781090841, + "narHash": "sha256-A1wVkHRw3/xpV30JUgWxvfW5PgcyrxQxk7b4So5vXNs=", + "owner": "logos-blockchain", + "repo": "logos-blockchain-rust-rapidsnark", + "rev": "e91187f8ccb5bbfc7bb00dac88169112428da78f", + "type": "github" + }, + "original": { + "owner": "logos-blockchain", + "repo": "logos-blockchain-rust-rapidsnark", + "rev": "e91187f8ccb5bbfc7bb00dac88169112428da78f", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index f8823a6c..3babf94c 100644 --- a/flake.nix +++ b/flake.nix @@ -13,8 +13,14 @@ crane.url = "github:ipetkov/crane"; + # Must stay in sync with the lbc-* tags in logos-blockchain/Cargo.lock. logos-blockchain-circuits = { - url = "github:logos-blockchain/logos-blockchain-circuits"; + url = "github:logos-blockchain/logos-blockchain-circuits/2846ee7a4cfa24458bb8063412ab2e753b344d2f"; + }; + + # Must stay in sync with the rust-rapidsnark rev in Cargo.lock. + rust-rapidsnark = { + url = "github:logos-blockchain/logos-blockchain-rust-rapidsnark/e91187f8ccb5bbfc7bb00dac88169112428da78f"; }; }; @@ -25,6 +31,7 @@ rust-overlay, crane, logos-blockchain-circuits, + rust-rapidsnark, ... }: let @@ -53,6 +60,7 @@ craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; src = ./.; cargoLock = builtins.fromTOML (builtins.readFile ./Cargo.lock); + lbc_dir = logos-blockchain-circuits.packages.${system}.default; # Parse Cargo.lock at eval time to find the locked risc0-circuit-recursion # version and its crates.io checksum โ€” no hardcoding required. @@ -102,6 +110,9 @@ pkgs.python3 # Required for correct builds now, as python is sandboxed in nix builds ]; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + # Logos blockchain related env vars + LBC_ROOT_DIR = logos-blockchain-circuits.packages.${system}.default; + RAPIDSNARK_LIB_DIR = rust-rapidsnark.packages.${system}.rapidsnark; # Point the risc0-circuit-recursion build script to the pre-fetched zip # so it doesn't try to download it inside the sandbox. RECURSION_SRC_PATH = "${recursionZkr}"; @@ -115,7 +126,6 @@ '' + pkgs.lib.optionalString pkgs.stdenv.isDarwin '' export PATH="$PATH:/usr/bin" ''; - LOGOS_BLOCKCHAIN_CIRCUITS = logos-blockchain-circuits.packages.${system}.default; }; walletFfiPackage = craneLib.buildPackage ( @@ -142,7 +152,7 @@ cargoExtraArgs = "-p indexer_ffi"; postInstall = '' mkdir -p $out/include - cp indexer/ffi/indexer_ffi.h $out/include/ + cp lez/indexer/ffi/indexer_ffi.h $out/include/ '' + pkgs.lib.optionalString pkgs.stdenv.isDarwin '' install_name_tool -id @rpath/libindexer_ffi.dylib $out/lib/libindexer_ffi.dylib diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index b35f9deb..3b1731d8 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -29,13 +29,17 @@ wallet-ffi.workspace = true indexer_ffi.workspace = true indexer_service_protocol.workspace = true +logos-blockchain-http-api-common.workspace = true +logos-blockchain-core.workspace = true +logos-blockchain-zone-sdk.workspace = true +logos-blockchain-key-management-system-service.workspace = true anyhow.workspace = true log.workspace = true tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } +futures.workspace = true 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 +num-bigint.workspace = true diff --git a/integration_tests/tests/auth_transfer/private.rs b/integration_tests/tests/auth_transfer/private.rs index 9eea9b04..45a1b085 100644 --- a/integration_tests/tests/auth_transfer/private.rs +++ b/integration_tests/tests/auth_transfer/private.rs @@ -11,7 +11,7 @@ use lee::{ privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, }; use lee_core::{ - InputAccountIdentity, NullifierPublicKey, + EncryptedAccountData, InputAccountIdentity, NullifierPublicKey, account::AccountWithMetadata, encryption::{EphemeralPublicKey, ViewingPublicKey}, }; @@ -665,9 +665,9 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> { let auth_transfer_program_id = Program::authenticated_transfer_program().id(); let nsk: lee_core::NullifierSecretKey = [3; 32]; let npk = NullifierPublicKey::from(&nsk); - let _vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap(); + let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap(); let ssk = SharedSecretKey([55_u8; 32]); - let _epk = EphemeralPublicKey(vec![55_u8; 1088]); + let epk = EphemeralPublicKey(vec![55_u8; 1088]); let attacker_vault_id = { let seed = vault_core::compute_vault_seed(attacker_id); AccountId::for_private_pda(&vault_program_id, &seed, &npk, 1337) @@ -712,6 +712,8 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivatePdaInit { + epk, + view_tag: EncryptedAccountData::compute_view_tag(&npk, &vpk), npk, ssk, identifier: 1337, diff --git a/integration_tests/tests/bridge.rs b/integration_tests/tests/bridge.rs index 81f62f2b..41781a68 100644 --- a/integration_tests/tests/bridge.rs +++ b/integration_tests/tests/bridge.rs @@ -4,19 +4,22 @@ reason = "We don't care about these in tests" )] -use std::time::Duration; +use std::{ops::Deref as _, time::Duration}; use anyhow::Context as _; use borsh::BorshSerialize; use common::transaction::LeeTransaction; -use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext}; +use futures::StreamExt as _; +use integration_tests::{ + TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, wait_for_indexer_to_catch_up, +}; use lee::{ AccountId, execute_and_prove, privacy_preserving_transaction, program::Program, public_transaction, }; use lee_core::{InputAccountIdentity, account::AccountWithMetadata}; use log::info; -use logos_blockchain_core::mantle::{Value, ledger::Inputs, ops::channel::deposit::DepositOp}; +use logos_blockchain_core::mantle::{ledger::Inputs, ops::channel::deposit::DepositOp}; use logos_blockchain_http_api_common::bodies::{ channel::ChannelDepositRequestBody, wallet::{ @@ -24,8 +27,14 @@ use logos_blockchain_http_api_common::bodies::{ transfer_funds::{WalletTransferFundsRequestBody, WalletTransferFundsResponseBody}, }, }; +use logos_blockchain_zone_sdk::{ + CommonHttpClient, ZoneMessage, adapter::NodeHttpClient, indexer::ZoneIndexer, +}; +use num_bigint::BigUint; use sequencer_service_rpc::RpcClient as _; +use test_fixtures::public_mention; use tokio::test; +use wallet::cli::{Command, execute_subcommand, programs::bridge::BridgeSubcommand}; const TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK: Duration = Duration::from_mins(2); @@ -150,7 +159,6 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> { 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")?; @@ -196,8 +204,9 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> { async fn submit_bedrock_deposit( bedrock_addr: std::net::SocketAddr, + bedrock_account_pk: &str, recipient_id: AccountId, - amount: u128, + amount: u64, ) -> anyhow::Result<()> { #[derive(BorshSerialize)] struct DepositMetadata { @@ -210,18 +219,13 @@ async fn submit_bedrock_deposit( .try_into() .context("Encoded metadata is too big")?; - 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" + "http://{bedrock_addr}/wallet/{bedrock_account_pk}/balance" )) .send() .await @@ -238,13 +242,13 @@ async fn submit_bedrock_deposit( let mut balance = query_balance().await?; info!( - "Queried Bedrock balance for key {funding_key}: {:?}", + "Queried Bedrock balance for key {bedrock_account_pk}: {:?}", balance.balance ); if balance.balance < amount { anyhow::bail!( - "Bedrock wallet with key {funding_key} has insufficient balance {:?} for deposit amount {:?}", + "Bedrock wallet with key {bedrock_account_pk} has insufficient balance {:?} for deposit amount {:?}", balance.balance, amount ); @@ -370,11 +374,18 @@ async fn wait_for_vault_balance( })? } +/// Test deposit and withdraw round trip. +/// +/// Implemented as one test instead of two separate tests for deposit and withdraw, because the +/// withdraw test depends on the deposit to set up the necessary state (funds in vault) for testing +/// withdraw functionality. #[test] -async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result<()> { - let ctx = TestContext::new().await?; +async fn bedrock_deposit_claim_and_withdraw_round_trip_succeeds() -> anyhow::Result<()> { + let mut ctx = TestContext::new().await?; + let bedrock_account_pk = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26"; let recipient_id = ctx.existing_public_accounts()[0]; + let amount = 1_u64; let vault_program_id = Program::vault().id(); let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id); @@ -388,10 +399,17 @@ async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result< .await?; // Submit deposit to Bedrock - submit_bedrock_deposit(ctx.bedrock_addr(), recipient_id, 1).await?; + submit_bedrock_deposit(ctx.bedrock_addr(), bedrock_account_pk, recipient_id, amount) + .await + .context("Failed to submit Bedrock deposit for round-trip setup")?; // 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?; + wait_for_vault_balance( + &ctx, + recipient_vault_id, + vault_balance_before + u128::from(amount), + ) + .await?; // Now claim funds from vault back to recipient let nonces = ctx @@ -411,7 +429,9 @@ async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result< vault_program_id, vec![recipient_id, recipient_vault_id], nonces, - vault_core::Instruction::Claim { amount: 1 }, + vault_core::Instruction::Claim { + amount: u128::from(amount), + }, ) .context("Failed to build vault claim message")?; @@ -446,9 +466,125 @@ async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result< ); assert_eq!( recipient_balance_after_claim, - recipient_balance_before + 1, + recipient_balance_before + u128::from(amount), "Recipient balance should increase by claimed amount" ); + // The indexer must replay the deposit and claim blocks and reach the same + // state as the sequencer โ€” including the bridge system account the deposit + // modifies, which is the case the hot fix unblocks. + wait_for_indexer_to_catch_up(&ctx).await?; + let bridge_account_id = lee::system_bridge_account_id(); + for account_id in [recipient_id, recipient_vault_id, bridge_account_id] { + let indexer_account = indexer_service_rpc::RpcClient::get_account( + // `deref` is needed for correct trait resolution + // of the async `get_account` method on `RpcClient` + ctx.indexer_client().deref(), + account_id.into(), + ) + .await?; + let sequencer_account = ctx.sequencer_client().get_account(account_id).await?; + assert_eq!( + indexer_account, + sequencer_account.into(), + "Indexer and sequencer diverged for account {account_id} after deposit" + ); + } + + // Withdraw back to Bedrock and wait for finalized withdraw event. + let sender_id = recipient_id; + + let observer = create_zone_indexer_observer(ctx.bedrock_addr())?; + let observe_fut = wait_for_finalized_withdraw_op(&observer, amount, bedrock_account_pk); + + let withdraw_fut = execute_subcommand( + ctx.wallet_mut(), + Command::Bridge(BridgeSubcommand::Withdraw { + from: public_mention(sender_id), + amount, + bedrock_account_pk: bedrock_account_pk.to_owned(), + }), + ); + + let (observe_result, withdraw_result) = tokio::join!(observe_fut, withdraw_fut); + + withdraw_result.context("Failed to execute wallet bridge withdraw command")?; + + observe_result + .context("Failed while waiting for finalized withdraw event from zone indexer")?; + + // Sleep to observe sequencer log about validated withdraw event + tokio::time::sleep(Duration::from_secs(1)).await; + Ok(()) } + +fn create_zone_indexer_observer( + bedrock_addr: std::net::SocketAddr, +) -> anyhow::Result> { + let bedrock_url = integration_tests::config::addr_to_url( + integration_tests::config::UrlProtocol::Http, + bedrock_addr, + ) + .context("Failed to convert Bedrock addr to URL for zone indexer observer")?; + + let node = NodeHttpClient::new(CommonHttpClient::new(None), bedrock_url); + + Ok(ZoneIndexer::new( + integration_tests::config::bedrock_channel_id(), + node, + )) +} + +async fn wait_for_finalized_withdraw_op( + observer: &ZoneIndexer, + expected_amount: u64, + receiver_pk: &str, +) -> anyhow::Result<()> { + let timeout = TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK + + Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS); + + let bedrock_account_pk_bytes = hex::decode(receiver_pk) + .context("Failed to decode expected receiver public key from hex")?; + let expected_receiver_pk = + logos_blockchain_key_management_system_service::keys::ZkPublicKey::from( + BigUint::from_bytes_le(&bedrock_account_pk_bytes), + ); + + tokio::time::timeout(timeout, async { + loop { + let stream = observer + .follow() + .await + .context("Failed to read zone indexer message batch")?; + let mut stream = std::pin::pin!(stream); + + while let Some(message) = stream.next().await { + info!("Observed zone message {message:?}"); + + let ZoneMessage::Withdraw(withdraw) = message else { + continue; + }; + + let mut iter = withdraw.outputs.iter(); + let Some(note) = iter.next() else { + continue; + }; + if iter.next().is_some() { + // Withdraw op should only have one output + continue; + } + + if note.value == expected_amount && note.pk == expected_receiver_pk { + return Ok(()); + } + } + + tokio::time::sleep(Duration::from_millis(500)).await; + } + }) + .await + .with_context(|| { + format!("Timed out waiting for finalized withdraw message with amount {expected_amount}") + })? +} diff --git a/integration_tests/tests/private_pda.rs b/integration_tests/tests/private_pda.rs index ea7cafab..f96faa52 100644 --- a/integration_tests/tests/private_pda.rs +++ b/integration_tests/tests/private_pda.rs @@ -23,7 +23,7 @@ use lee::{ program::Program, }; use lee_core::{ - InputAccountIdentity, NullifierPublicKey, + EncryptedAccountData, InputAccountIdentity, NullifierPublicKey, account::{Account, AccountWithMetadata}, encryption::ViewingPublicKey, program::PdaSeed, @@ -74,6 +74,8 @@ async fn fund_private_pda( let account_identities = vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivatePdaInit { + epk, + view_tag: EncryptedAccountData::compute_view_tag(&npk, &vpk), npk, ssk, identifier, @@ -89,13 +91,9 @@ async fn fund_private_pda( ) .map_err(|e| anyhow::anyhow!("circuit proving failed: {e}"))?; - let message = Message::try_from_circuit_output( - vec![sender], - vec![sender_account.nonce], - vec![(npk, vpk, epk)], - output, - ) - .map_err(|e| anyhow::anyhow!("message build failed: {e}"))?; + let message = + Message::try_from_circuit_output(vec![sender], vec![sender_account.nonce], output) + .map_err(|e| anyhow::anyhow!("message build failed: {e}"))?; let witness_set = WitnessSet::for_message(&message, proof, &[sender_sk]); let tx = PrivacyPreservingTransaction::new(message, witness_set); diff --git a/integration_tests/tests/tps.rs b/integration_tests/tests/tps.rs index daf52609..459f3d61 100644 --- a/integration_tests/tests/tps.rs +++ b/integration_tests/tests/tps.rs @@ -23,7 +23,7 @@ use lee::{ public_transaction as putx, }; use lee_core::{ - InputAccountIdentity, MembershipProof, NullifierPublicKey, + EncryptedAccountData, InputAccountIdentity, MembershipProof, NullifierPublicKey, account::{AccountWithMetadata, Nonce, data::Data}, encryption::ViewingPublicKey, }; @@ -301,12 +301,16 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction { .unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: sender_epk, + view_tag: EncryptedAccountData::compute_view_tag(&sender_npk, &sender_vpk), ssk: sender_ss, nsk: sender_nsk, membership_proof: proof, identifier: 0, }, InputAccountIdentity::PrivateUnauthorized { + epk: recipient_epk, + view_tag: EncryptedAccountData::compute_view_tag(&recipient_npk, &recipient_vpk), npk: recipient_npk, ssk: recipient_ss, identifier: 0, @@ -315,16 +319,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction { &program.into(), ) .unwrap(); - let message = pptx::message::Message::try_from_circuit_output( - vec![], - vec![], - vec![ - (sender_npk, sender_vpk, sender_epk), - (recipient_npk, recipient_vpk, recipient_epk), - ], - output, - ) - .unwrap(); + let message = pptx::message::Message::try_from_circuit_output(vec![], vec![], output).unwrap(); let witness_set = pptx::witness_set::WitnessSet::for_message(&message, proof, &[]); pptx::PrivacyPreservingTransaction::new(message, witness_set) } diff --git a/integration_tests/tests/vault.rs b/integration_tests/tests/vault.rs new file mode 100644 index 00000000..33d2e9a9 --- /dev/null +++ b/integration_tests/tests/vault.rs @@ -0,0 +1,208 @@ +#![expect( + clippy::tests_outside_test_module, + reason = "We don't care about these in tests" +)] + +use anyhow::{Context as _, Result}; +use integration_tests::{TestContext, private_mention, public_mention}; +use lee::program::Program; +use sequencer_service_rpc::RpcClient as _; +use tokio::test; +use wallet::cli::{Command, SubcommandReturnValue, programs::vault::VaultSubcommand}; + +#[test] +async fn public_transfer_and_public_claim() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let amount: u128 = 100; + let sender = ctx.existing_public_accounts()[0]; + let recipient = ctx.existing_public_accounts()[1]; + + let vault_program_id = Program::vault().id(); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient); + + let sender_balance_before = ctx.sequencer_client().get_account_balance(sender).await?; + let recipient_balance_before = ctx + .sequencer_client() + .get_account_balance(recipient) + .await?; + let recipient_vault_balance_before = ctx + .sequencer_client() + .get_account_balance(recipient_vault_id) + .await?; + + let transfer_result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Vault(VaultSubcommand::Transfer { + from: public_mention(sender), + to: public_mention(recipient), + amount, + }), + ) + .await?; + assert!( + matches!(transfer_result, SubcommandReturnValue::Empty), + "Expected Empty return value for public vault transfer" + ); + + let sender_balance_after_transfer = ctx.sequencer_client().get_account_balance(sender).await?; + let recipient_balance_after_transfer = ctx + .sequencer_client() + .get_account_balance(recipient) + .await?; + let recipient_vault_balance_after_transfer = ctx + .sequencer_client() + .get_account_balance(recipient_vault_id) + .await?; + + assert_eq!( + sender_balance_after_transfer, + sender_balance_before - amount + ); + assert_eq!(recipient_balance_after_transfer, recipient_balance_before); + assert_eq!( + recipient_vault_balance_after_transfer, + recipient_vault_balance_before + amount + ); + + let claim_result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Vault(VaultSubcommand::Claim { + account_id: public_mention(recipient), + amount, + }), + ) + .await?; + assert!( + matches!(claim_result, SubcommandReturnValue::Empty), + "Expected Empty return value for public vault claim" + ); + + let sender_balance_after_claim = ctx.sequencer_client().get_account_balance(sender).await?; + let recipient_balance_after_claim = ctx + .sequencer_client() + .get_account_balance(recipient) + .await?; + let recipient_vault_balance_after_claim = ctx + .sequencer_client() + .get_account_balance(recipient_vault_id) + .await?; + + assert_eq!(sender_balance_after_claim, sender_balance_before - amount); + assert_eq!( + recipient_balance_after_claim, + recipient_balance_before + amount + ); + assert_eq!( + recipient_vault_balance_after_claim, + recipient_vault_balance_before + ); + + Ok(()) +} + +#[test] +async fn private_transfer_and_private_claim() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let amount: u128 = 100; + let sender = ctx.existing_private_accounts()[0]; + let owner = ctx.existing_private_accounts()[1]; + + let vault_program_id = Program::vault().id(); + let owner_vault_id = vault_core::compute_vault_account_id(vault_program_id, owner); + + let sender_balance_before = ctx + .wallet() + .get_account_private(sender) + .context("Failed to load sender private account")? + .balance; + let owner_balance_before = ctx + .wallet() + .get_account_private(owner) + .context("Failed to load owner private account")? + .balance; + let owner_vault_balance_before = ctx + .sequencer_client() + .get_account_balance(owner_vault_id) + .await?; + + let transfer_result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Vault(VaultSubcommand::Transfer { + from: private_mention(sender), + to: private_mention(owner), + amount, + }), + ) + .await?; + assert!( + matches!( + transfer_result, + SubcommandReturnValue::PrivacyPreservingTransfer { .. } + ), + "Expected PrivacyPreservingTransfer return value for private vault transfer" + ); + + let sender_balance_after_transfer = ctx + .wallet() + .get_account_private(sender) + .context("Failed to load sender private account after transfer")? + .balance; + let owner_balance_after_transfer = ctx + .wallet() + .get_account_private(owner) + .context("Failed to load owner private account after transfer")? + .balance; + let owner_vault_balance_after_transfer = ctx + .sequencer_client() + .get_account_balance(owner_vault_id) + .await?; + + assert_eq!( + sender_balance_after_transfer, + sender_balance_before - amount + ); + assert_eq!(owner_balance_after_transfer, owner_balance_before); + assert_eq!( + owner_vault_balance_after_transfer, + owner_vault_balance_before + amount + ); + + let claim_result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Vault(VaultSubcommand::Claim { + account_id: private_mention(owner), + amount, + }), + ) + .await?; + assert!( + matches!( + claim_result, + SubcommandReturnValue::PrivacyPreservingTransfer { .. } + ), + "Expected PrivacyPreservingTransfer return value for private vault claim" + ); + + let sender_balance_after_claim = ctx + .wallet() + .get_account_private(sender) + .context("Failed to load sender private account after claim")? + .balance; + let owner_balance_after_claim = ctx + .wallet() + .get_account_private(owner) + .context("Failed to load owner private account after claim")? + .balance; + let owner_vault_balance_after_claim = ctx + .sequencer_client() + .get_account_balance(owner_vault_id) + .await?; + + assert_eq!(sender_balance_after_claim, sender_balance_before - amount); + assert_eq!(owner_balance_after_claim, owner_balance_before + amount); + assert_eq!(owner_vault_balance_after_claim, owner_vault_balance_before); + + Ok(()) +} diff --git a/integration_tests/tests/wallet_ffi.rs b/integration_tests/tests/wallet_ffi.rs index f47435bd..5ce5ddbe 100644 --- a/integration_tests/tests/wallet_ffi.rs +++ b/integration_tests/tests/wallet_ffi.rs @@ -7,6 +7,7 @@ clippy::undocumented_unsafe_blocks, clippy::multiple_unsafe_ops_per_block, clippy::shadow_unrelated, + clippy::as_conversions, reason = "We don't care about these in tests" )] @@ -20,14 +21,19 @@ use std::{ use anyhow::Result; use integration_tests::{BlockingTestContext, TIME_TO_WAIT_FOR_BLOCK_SECONDS}; -use lee::{Account, AccountId, PrivateKey, PublicKey, program::Program}; +use lee::{ + Account, AccountId, PrivateKey, PublicKey, + privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, +}; use lee_core::program::DEFAULT_PROGRAM_ID; use log::info; use tempfile::tempdir; use wallet::account::HumanReadableAccount; use wallet_ffi::{ - FfiAccount, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys, FfiPublicAccountKey, - FfiTransferResult, FfiU128, WalletHandle, error, wallet::FfiCreateWalletResult, + FfiAccount, FfiAccountIdentity, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys, + FfiPublicAccountKey, FfiTransferResult, FfiU128, WalletHandle, error, + generic_transaction::{FfiProgramWithDependencies, FfiTransactionResult}, + wallet::FfiCreateWalletResult, }; unsafe extern "C" { @@ -186,6 +192,42 @@ unsafe extern "C" { mnemonic: *const c_char, password: *const c_char, ) -> error::WalletFfiError; + + fn wallet_ffi_resolve_public_account( + account_id: FfiBytes32, + needs_sign: bool, + out_account_identity: *mut FfiAccountIdentity, + ) -> error::WalletFfiError; + + fn wallet_ffi_send_generic_public_transaction( + handle: *mut WalletHandle, + account_identities: *const FfiAccountIdentity, + account_identities_size: usize, + instruction_words: *const u32, + instruction_words_size: usize, + program_with_dependencies: *const FfiProgramWithDependencies, + out_result: *mut FfiTransactionResult, + ) -> error::WalletFfiError; + + fn wallet_ffi_resolve_private_account( + handle: *mut WalletHandle, + account_id: FfiBytes32, + out_account_identity: *mut FfiAccountIdentity, + ) -> error::WalletFfiError; + + fn wallet_ffi_send_generic_private_transaction( + handle: *mut WalletHandle, + account_identities: *const FfiAccountIdentity, + account_identities_size: usize, + instruction_words: *const u32, + instruction_words_size: usize, + program_with_dependencies: *const FfiProgramWithDependencies, + out_result: *mut FfiTransactionResult, + ) -> error::WalletFfiError; + + fn wallet_ffi_free_transaction_result(result: *mut FfiTransactionResult); + + fn wallet_ffi_free_account_identity(account_identity: *mut FfiAccountIdentity); } fn new_wallet_ffi_with_test_context_config( @@ -1400,34 +1442,6 @@ fn restore_keys_from_seed_ffi() -> Result<()> { u128::from_le_bytes(out_balance) }; - // Get the account list with FFI method - let wallet_ffi_account_list = unsafe { - let mut out_list = FfiAccountList::default(); - wallet_ffi_list_accounts(wallet_ffi_handle, &raw mut out_list).unwrap(); - out_list - }; - - let wallet_ffi_account_list_slice = unsafe { - core::slice::from_raw_parts( - wallet_ffi_account_list.entries, - wallet_ffi_account_list.count, - ) - }; - - // All created accounts must appear in the list - let listed_public_ids: HashSet<_> = wallet_ffi_account_list_slice - .iter() - .filter(|e| e.is_public) - .map(|e| hex::encode(e.account_id.data)) - .collect(); - - info!("Current list of accounts: {listed_public_ids:?}"); - - info!("Private acc to be restored 1 {:?}", hex::encode(private_account_id_1.data)); - info!("Private acc to be restored 2 {:?}", hex::encode(private_account_id_2.data)); - info!("Pub acc to be restored 1 {:?}", hex::encode(public_account_id_1.data)); - info!("Pub acc to be restored 2 {:?}", hex::encode(public_account_id_2.data)); - assert_eq!(private_account_id_1_balance, 100); assert_eq!(private_account_id_2_balance, 101); assert_eq!(public_account_id_1_balance, 102); @@ -1437,3 +1451,207 @@ fn restore_keys_from_seed_ffi() -> Result<()> { Ok(()) } + +#[test] +fn test_wallet_ffi_transfer_generic_public() -> Result<()> { + let ctx = BlockingTestContext::new()?; + let home = tempfile::tempdir()?; + let FfiCreateWalletResult { + wallet: wallet_ffi_handle, + mnemonic: _, + } = new_wallet_ffi_with_test_context_config(&ctx, home.path())?; + let from: FfiBytes32 = ctx.ctx().existing_public_accounts()[0].into(); + let to: FfiBytes32 = ctx.ctx().existing_public_accounts()[1].into(); + let amount = 100_u128; + + let mut transaction_result = FfiTransactionResult::default(); + + let mut from_account_identity = FfiAccountIdentity::default(); + let mut to_account_identity = FfiAccountIdentity::default(); + + unsafe { + wallet_ffi_resolve_public_account(from, true, &raw mut from_account_identity).unwrap(); + } + + unsafe { + wallet_ffi_resolve_public_account(to, true, &raw mut to_account_identity).unwrap(); + } + + let ffi_accs = vec![from_account_identity, to_account_identity]; + let account_identities_size = ffi_accs.len(); + let account_identities = + Box::into_raw(ffi_accs.into_boxed_slice()) as *const FfiAccountIdentity; + + let instruction_data = + Program::serialize_instruction(authenticated_transfer_core::Instruction::Transfer { + amount, + }) + .unwrap(); + let instruction_words_size = instruction_data.len(); + let instruction_words = Box::into_raw(instruction_data.into_boxed_slice()) as *const u32; + + let program: ProgramWithDependencies = Program::authenticated_transfer_program().into(); + let program_with_dependencies: FfiProgramWithDependencies = program.into(); + + unsafe { + wallet_ffi_send_generic_public_transaction( + wallet_ffi_handle, + account_identities, + account_identities_size, + instruction_words, + instruction_words_size, + &raw const program_with_dependencies, + &raw mut transaction_result, + ) + .unwrap(); + } + + info!("Waiting for next block creation"); + std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)); + + let from_balance = unsafe { + let mut out_balance: [u8; 16] = [0; 16]; + wallet_ffi_get_balance( + wallet_ffi_handle, + &raw const from, + true, + &raw mut out_balance, + ) + .unwrap(); + u128::from_le_bytes(out_balance) + }; + + let to_balance = unsafe { + let mut out_balance: [u8; 16] = [0; 16]; + wallet_ffi_get_balance(wallet_ffi_handle, &raw const to, true, &raw mut out_balance) + .unwrap(); + u128::from_le_bytes(out_balance) + }; + + assert_eq!(from_balance, 9900); + assert_eq!(to_balance, 20100); + + unsafe { + let account_identities_mut = account_identities.cast_mut(); + wallet_ffi_free_account_identity(account_identities_mut); + wallet_ffi_free_account_identity(account_identities_mut.add(1)); + + let instruction_data = + std::slice::from_raw_parts_mut(instruction_words.cast_mut(), instruction_words_size); + drop(Box::from_raw(std::ptr::from_mut(instruction_data))); + + wallet_ffi_free_transaction_result(&raw mut transaction_result); + wallet_ffi_destroy(wallet_ffi_handle); + } + + Ok(()) +} + +#[test] +fn test_wallet_ffi_transfer_generic_private() -> Result<()> { + let ctx = BlockingTestContext::new()?; + let home = tempfile::tempdir()?; + let FfiCreateWalletResult { + wallet: wallet_ffi_handle, + mnemonic: _, + } = new_wallet_ffi_with_test_context_config(&ctx, home.path())?; + let from: FfiBytes32 = ctx.ctx().existing_private_accounts()[0].into(); + let to: FfiBytes32 = ctx.ctx().existing_private_accounts()[1].into(); + let amount = 100_u128; + + let mut transaction_result = FfiTransactionResult::default(); + + let mut from_account_identity = FfiAccountIdentity::default(); + let mut to_account_identity = FfiAccountIdentity::default(); + + unsafe { + wallet_ffi_resolve_private_account(wallet_ffi_handle, from, &raw mut from_account_identity) + .unwrap(); + } + + unsafe { + wallet_ffi_resolve_private_account(wallet_ffi_handle, to, &raw mut to_account_identity) + .unwrap(); + } + + let ffi_accs = vec![from_account_identity, to_account_identity]; + let account_identities_size = ffi_accs.len(); + let account_identities = + Box::into_raw(ffi_accs.into_boxed_slice()) as *const FfiAccountIdentity; + + let instruction_data = + Program::serialize_instruction(authenticated_transfer_core::Instruction::Transfer { + amount, + }) + .unwrap(); + let instruction_words_size = instruction_data.len(); + let instruction_words = Box::into_raw(instruction_data.into_boxed_slice()) as *const u32; + + let program: ProgramWithDependencies = Program::authenticated_transfer_program().into(); + let program_with_dependencies: FfiProgramWithDependencies = program.into(); + + unsafe { + wallet_ffi_send_generic_private_transaction( + wallet_ffi_handle, + account_identities, + account_identities_size, + instruction_words, + instruction_words_size, + &raw const program_with_dependencies, + &raw mut transaction_result, + ) + .unwrap(); + } + + assert_eq!(transaction_result.secrets_size, 2); + + info!("Waiting for next block creation"); + std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)); + + // Sync private account local storage with onchain encrypted state + unsafe { + let mut current_height = 0; + wallet_ffi_get_current_block_height(wallet_ffi_handle, &raw mut current_height).unwrap(); + wallet_ffi_sync_to_block(wallet_ffi_handle, current_height).unwrap(); + }; + + let from_balance = unsafe { + let mut out_balance: [u8; 16] = [0; 16]; + let _result = wallet_ffi_get_balance( + wallet_ffi_handle, + &raw const from, + false, + &raw mut out_balance, + ); + u128::from_le_bytes(out_balance) + }; + + let to_balance = unsafe { + let mut out_balance: [u8; 16] = [0; 16]; + let _result = wallet_ffi_get_balance( + wallet_ffi_handle, + &raw const to, + false, + &raw mut out_balance, + ); + u128::from_le_bytes(out_balance) + }; + + assert_eq!(from_balance, 9900); + assert_eq!(to_balance, 20100); + + unsafe { + let account_identities_mut = account_identities.cast_mut(); + wallet_ffi_free_account_identity(account_identities_mut); + wallet_ffi_free_account_identity(account_identities_mut.add(1)); + + let instruction_data = + std::slice::from_raw_parts_mut(instruction_words.cast_mut(), instruction_words_size); + drop(Box::from_raw(std::ptr::from_mut(instruction_data))); + + wallet_ffi_free_transaction_result(&raw mut transaction_result); + wallet_ffi_destroy(wallet_ffi_handle); + } + + Ok(()) +} diff --git a/lee/key_protocol/src/key_management/key_tree/keys_public.rs b/lee/key_protocol/src/key_management/key_tree/keys_public.rs index a8f97070..947fb83c 100644 --- a/lee/key_protocol/src/key_management/key_tree/keys_public.rs +++ b/lee/key_protocol/src/key_management/key_tree/keys_public.rs @@ -1,4 +1,4 @@ -use k256::elliptic_curve::{PrimeField as _, sec1::ToEncodedPoint as _}; +use k256::elliptic_curve::PrimeField as _; use serde::{Deserialize, Serialize}; use crate::key_management::key_tree::traits::KeyTreeNode; @@ -6,9 +6,13 @@ use crate::key_management::key_tree::traits::KeyTreeNode; #[derive(Debug, Serialize, Deserialize, Clone)] #[cfg_attr(any(test, feature = "test_utils"), derive(PartialEq, Eq))] pub struct ChildKeysPublic { - pub csk: lee::PrivateKey, - pub cpk: lee::PublicKey, - pub ccc: [u8; 32], + /// Secret key for public account. + pub sk: lee::PrivateKey, + /// Schnorr secret key. + pub ssk: lee::PrivateKey, + /// Schnorr public key. + pub pk: lee::PublicKey, + pub cc: [u8; 32], /// Can be [`None`] if root. pub cci: Option, } @@ -18,19 +22,24 @@ impl ChildKeysPublic { pub fn root(seed: [u8; 64]) -> Self { let hash_value = hmac_sha512::HMAC::mac(seed, "LEE_master_pub"); - let csk = lee::PrivateKey::try_new( + let sk = lee::PrivateKey::try_new( *hash_value .first_chunk::<32>() .expect("hash_value is 64 bytes, must be safe to get first 32"), ) .expect("Expect a valid Private Key"); - let ccc = *hash_value.last_chunk::<32>().unwrap(); - let cpk = lee::PublicKey::new_from_private_key(&csk); + let ssk = lee::PrivateKey::tweak(sk.value()).expect("`key_protocol::key_management::keys_public::root()`: Invalid private key produced from `tweak`"); + + let cc = *hash_value + .last_chunk::<32>() + .expect("hash_value is 64 bytes, must be safe to get last 32"); + let pk = lee::PublicKey::new_from_private_key(&ssk); Self { - csk, - cpk, - ccc, + sk, + ssk, + pk, + cc, cci: None, } } @@ -39,61 +48,53 @@ impl ChildKeysPublic { pub fn nth_child(&self, cci: u32) -> Self { let hash_value = self.compute_hash_value(cci); - let csk = lee::PrivateKey::try_new({ - let hash_value = hash_value + let lhs = k256::Scalar::from_repr( + (*hash_value .first_chunk::<32>() - .expect("hash_value is 64 bytes, must be safe to get first 32"); + .expect("hash_value is 64 bytes, must be safe to get first 32")) + .into(), + ) + .expect("Expect a valid k256 scalar"); + let rhs = + k256::Scalar::from_repr((*self.sk.value()).into()).expect("Expect a valid k256 scalar"); - let value_1 = - k256::Scalar::from_repr((*hash_value).into()).expect("Expect a valid k256 scalar"); - let value_2 = k256::Scalar::from_repr((*self.csk.value()).into()) - .expect("Expect a valid k256 scalar"); + let sk = lee::PrivateKey::try_new(lhs.add(&rhs).to_bytes().into()) + .expect("Expect a valid private key"); - let sum = value_1.add(&value_2); - sum.to_bytes().into() - }) - .expect("Expect a valid private key"); + let ssk = lee::PrivateKey::tweak(sk.value()).expect("`key_protocol::key_management::keys_public::nth_child()`: Invalid private key produced from `tweak`"); - let ccc = *hash_value + let cc = *hash_value .last_chunk::<32>() .expect("hash_value is 64 bytes, must be safe to get last 32"); - let cpk = lee::PublicKey::new_from_private_key(&csk); + let pk = lee::PublicKey::new_from_private_key(&ssk); Self { - csk, - cpk, - ccc, + sk, + ssk, + pk, + cc, cci: Some(cci), } } #[must_use] pub fn account_id(&self) -> lee::AccountId { - lee::AccountId::from(&self.cpk) + lee::AccountId::from(&self.pk) } fn compute_hash_value(&self, cci: u32) -> [u8; 64] { let mut hash_input = vec![]; - - if ((2_u32).pow(31)).cmp(&cci) == std::cmp::Ordering::Greater { - // Non-harden. - // BIP-032 compatibility requires 1-byte header from the public_key; - // Not stored in `self.cpk.value()`. - let sk = k256::SecretKey::from_bytes(self.csk.value().into()) - .expect("32 bytes, within curve order"); - let pk = sk.public_key(); - hash_input.extend_from_slice(pk.to_encoded_point(true).as_bytes()); - } else { - // Harden. - hash_input.extend_from_slice(&[0_u8]); - hash_input.extend_from_slice(self.csk.value()); - } + // Simplified key logic by only supporting harden keys. + // Non-harden keys would require access to untweaked public keys associated to `sk`s. + // Thus, not PQ secure. + hash_input.extend_from_slice(&[0_u8]); + hash_input.extend_from_slice(self.sk.value()); #[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")] hash_input.extend_from_slice(&cci.to_be_bytes()); - hmac_sha512::HMAC::mac(hash_input, self.ccc) + hmac_sha512::HMAC::mac(hash_input, self.cc) } } @@ -103,7 +104,7 @@ impl ChildKeysPublic { )] impl<'a> From<&'a ChildKeysPublic> for &'a lee::PrivateKey { fn from(value: &'a ChildKeysPublic) -> Self { - &value.csk + &value.ssk } } @@ -137,30 +138,37 @@ mod tests { ]; let keys = ChildKeysPublic::root(seed); - let expected_ccc = [ + let expected_cc = [ 238, 94, 84, 154, 56, 224, 80, 218, 133, 249, 179, 222, 9, 24, 17, 252, 120, 127, 222, 13, 146, 126, 232, 239, 113, 9, 194, 219, 190, 48, 187, 155, ]; - let expected_csk: PrivateKey = PrivateKey::try_new([ + let expected_sk: PrivateKey = PrivateKey::try_new([ 40, 35, 239, 19, 53, 178, 250, 55, 115, 12, 34, 3, 153, 153, 72, 170, 190, 36, 172, 36, 202, 148, 181, 228, 35, 222, 58, 84, 156, 24, 146, 86, ]) .unwrap(); - let expected_cpk: PublicKey = PublicKey::try_new([ - 219, 141, 130, 105, 11, 203, 187, 124, 112, 75, 223, 22, 11, 164, 153, 127, 59, 247, - 244, 166, 75, 66, 242, 224, 35, 156, 161, 75, 41, 51, 76, 245, + let expected_ssk: PrivateKey = PrivateKey::try_new([ + 207, 4, 246, 223, 104, 72, 19, 85, 14, 122, 194, 82, 32, 163, 60, 57, 8, 25, 209, 91, + 254, 107, 76, 238, 31, 68, 236, 192, 154, 78, 105, 118, ]) .unwrap(); - assert!(expected_ccc == keys.ccc); - assert!(expected_csk == keys.csk); - assert!(expected_cpk == keys.cpk); + let expected_pk: PublicKey = PublicKey::try_new([ + 188, 163, 203, 45, 151, 154, 230, 254, 123, 114, 158, 130, 19, 182, 164, 143, 150, 131, + 176, 7, 27, 58, 204, 116, 5, 247, 0, 255, 111, 160, 52, 201, + ]) + .unwrap(); + + assert!(expected_cc == keys.cc); + assert!(expected_ssk == keys.ssk); + assert!(expected_sk == keys.sk); + assert!(expected_pk == keys.pk); } #[test] - fn harden_child_keys_generation() { + fn child_keys_generation() { let seed = [ 88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173, 134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87, @@ -171,93 +179,32 @@ mod tests { let cci = (2_u32).pow(31) + 13; let child_keys = ChildKeysPublic::nth_child(&root_keys, cci); - let expected_ccc = [ + let expected_cc = [ 149, 226, 13, 4, 194, 12, 69, 29, 9, 234, 209, 119, 98, 4, 128, 91, 37, 103, 192, 31, 130, 126, 123, 20, 90, 34, 173, 209, 101, 248, 155, 36, ]; - let expected_csk: PrivateKey = PrivateKey::try_new([ + let expected_sk: PrivateKey = PrivateKey::try_new([ 9, 65, 33, 228, 25, 82, 219, 117, 91, 217, 11, 223, 144, 85, 246, 26, 123, 216, 107, 213, 33, 52, 188, 22, 198, 246, 71, 46, 245, 174, 16, 47, ]) .unwrap(); - let expected_cpk: PublicKey = PublicKey::try_new([ - 142, 143, 238, 159, 105, 165, 224, 252, 108, 62, 53, 209, 176, 219, 249, 38, 90, 241, - 201, 81, 194, 146, 236, 5, 83, 152, 238, 243, 138, 16, 229, 15, + let expected_ssk: PrivateKey = PrivateKey::try_new([ + 100, 37, 212, 81, 40, 233, 72, 156, 177, 139, 50, 114, 136, 157, 202, 132, 203, 246, + 252, 242, 13, 81, 42, 100, 159, 240, 187, 252, 202, 108, 25, 105, ]) .unwrap(); - assert!(expected_ccc == child_keys.ccc); - assert!(expected_csk == child_keys.csk); - assert!(expected_cpk == child_keys.cpk); - } - - #[test] - fn nonharden_child_keys_generation() { - let seed = [ - 88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173, - 134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87, - 22, 227, 38, 71, 17, 144, 251, 118, 217, 115, 33, 222, 201, 61, 203, 246, 121, 214, 6, - 187, 148, 92, 44, 253, 210, 37, - ]; - let root_keys = ChildKeysPublic::root(seed); - let cci = 13; - let child_keys = ChildKeysPublic::nth_child(&root_keys, cci); - - let expected_ccc = [ - 79, 228, 242, 119, 211, 203, 198, 175, 95, 36, 4, 234, 139, 45, 137, 138, 54, 211, 187, - 16, 28, 79, 80, 232, 216, 101, 145, 19, 101, 220, 217, 141, - ]; - - let expected_csk: PrivateKey = PrivateKey::try_new([ - 185, 147, 32, 242, 145, 91, 123, 77, 42, 33, 134, 84, 12, 165, 117, 70, 158, 201, 95, - 153, 14, 12, 92, 235, 128, 156, 194, 169, 68, 35, 165, 127, + let expected_pk: PublicKey = PublicKey::try_new([ + 210, 59, 119, 137, 21, 153, 82, 22, 195, 82, 12, 16, 80, 156, 125, 199, 19, 173, 46, + 224, 213, 144, 165, 126, 70, 129, 171, 141, 77, 212, 108, 233, ]) .unwrap(); - let expected_cpk: PublicKey = PublicKey::try_new([ - 119, 16, 145, 121, 97, 244, 186, 35, 136, 34, 140, 171, 206, 139, 11, 208, 207, 121, - 158, 45, 28, 22, 140, 98, 161, 179, 212, 173, 238, 220, 2, 34, - ]) - .unwrap(); - - assert!(expected_ccc == child_keys.ccc); - assert!(expected_csk == child_keys.csk); - assert!(expected_cpk == child_keys.cpk); - } - - #[test] - fn edge_case_child_keys_generation_2_power_31() { - let seed = [ - 88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173, - 134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87, - 22, 227, 38, 71, 17, 144, 251, 118, 217, 115, 33, 222, 201, 61, 203, 246, 121, 214, 6, - 187, 148, 92, 44, 253, 210, 37, - ]; - let root_keys = ChildKeysPublic::root(seed); - let cci = (2_u32).pow(31); //equivant to 0, thus non-harden. - let child_keys = ChildKeysPublic::nth_child(&root_keys, cci); - - let expected_ccc = [ - 221, 208, 47, 189, 174, 152, 33, 25, 151, 114, 233, 191, 57, 15, 40, 140, 46, 87, 126, - 58, 215, 40, 246, 111, 166, 113, 183, 145, 173, 11, 27, 182, - ]; - - let expected_csk: PrivateKey = PrivateKey::try_new([ - 223, 29, 87, 189, 126, 24, 117, 225, 190, 57, 0, 143, 207, 168, 231, 139, 170, 192, 81, - 254, 126, 10, 115, 42, 141, 157, 70, 171, 199, 231, 198, 132, - ]) - .unwrap(); - - let expected_cpk: PublicKey = PublicKey::try_new([ - 96, 123, 245, 51, 214, 216, 215, 205, 70, 145, 105, 221, 166, 169, 122, 27, 94, 112, - 228, 110, 249, 177, 85, 173, 180, 248, 185, 199, 112, 246, 83, 33, - ]) - .unwrap(); - - assert!(expected_ccc == child_keys.ccc); - assert!(expected_csk == child_keys.csk); - assert!(expected_cpk == child_keys.cpk); + assert!(expected_cc == child_keys.cc); + assert!(expected_ssk == child_keys.ssk); + assert!(expected_sk == child_keys.sk); + assert!(expected_pk == child_keys.pk); } } diff --git a/lee/key_protocol/src/key_management/key_tree/mod.rs b/lee/key_protocol/src/key_management/key_tree/mod.rs index 3d93427b..c15c09a5 100644 --- a/lee/key_protocol/src/key_management/key_tree/mod.rs +++ b/lee/key_protocol/src/key_management/key_tree/mod.rs @@ -347,8 +347,8 @@ mod tests { assert!(tree.key_map.contains_key(&ChainIndex::root())); assert!(tree.account_id_map.contains_key(&AccountId::new([ - 172, 82, 222, 249, 164, 16, 148, 184, 219, 56, 92, 145, 203, 220, 251, 89, 214, 178, - 38, 30, 108, 202, 251, 241, 148, 200, 125, 185, 93, 227, 189, 247 + 10, 231, 159, 65, 236, 46, 205, 5, 172, 89, 250, 29, 123, 195, 212, 137, 155, 111, 40, + 120, 53, 28, 124, 54, 224, 170, 119, 208, 2, 72, 75, 50 ]))); } diff --git a/lee/state_machine/core/src/circuit_io.rs b/lee/state_machine/core/src/circuit_io.rs index b1c2e44f..78bfa24f 100644 --- a/lee/state_machine/core/src/circuit_io.rs +++ b/lee/state_machine/core/src/circuit_io.rs @@ -4,7 +4,7 @@ use crate::{ Commitment, CommitmentSetDigest, Identifier, MembershipProof, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey, account::{Account, AccountWithMetadata}, - encryption::Ciphertext, + encryption::{EncryptedAccountData, EphemeralPublicKey, ViewTag}, program::{BlockValidityWindow, PdaSeed, ProgramId, ProgramOutput, TimestampValidityWindow}, }; @@ -33,6 +33,8 @@ pub enum InputAccountIdentity { /// `AccountId::for_regular_private_account(&NullifierPublicKey::from(nsk), identifier)` and /// matched against `pre_state.account_id`. PrivateAuthorizedInit { + epk: EphemeralPublicKey, + view_tag: ViewTag, ssk: SharedSecretKey, nsk: NullifierSecretKey, identifier: Identifier, @@ -40,6 +42,8 @@ pub enum InputAccountIdentity { /// Update of an authorized standalone private account: existing on-chain commitment, with /// membership proof. PrivateAuthorizedUpdate { + epk: EphemeralPublicKey, + view_tag: ViewTag, ssk: SharedSecretKey, nsk: NullifierSecretKey, membership_proof: MembershipProof, @@ -48,6 +52,8 @@ pub enum InputAccountIdentity { /// Init of a standalone private account the caller does not own (e.g. a recipient who /// doesn't yet exist on chain). No `nsk`, no membership proof. PrivateUnauthorized { + epk: EphemeralPublicKey, + view_tag: ViewTag, npk: NullifierPublicKey, ssk: SharedSecretKey, identifier: Identifier, @@ -57,6 +63,8 @@ pub enum InputAccountIdentity { /// PDA within the `(program_id, seed, npk)` family: `AccountId::for_private_pda` uses it /// as the 4th input. PrivatePdaInit { + epk: EphemeralPublicKey, + view_tag: ViewTag, npk: NullifierPublicKey, ssk: SharedSecretKey, identifier: Identifier, @@ -72,6 +80,8 @@ pub enum InputAccountIdentity { /// from `nsk`. Authorization may be established upstream by a caller `pda_seeds` match or a /// previously-seen authorization in a chained call. PrivatePdaUpdate { + epk: EphemeralPublicKey, + view_tag: ViewTag, ssk: SharedSecretKey, nsk: NullifierSecretKey, membership_proof: MembershipProof, @@ -123,7 +133,7 @@ impl InputAccountIdentity { pub struct PrivacyPreservingCircuitOutput { pub public_pre_states: Vec, pub public_post_states: Vec, - pub ciphertexts: Vec, + pub encrypted_private_post_states: Vec, pub new_commitments: Vec, pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>, pub block_validity_window: BlockValidityWindow, @@ -148,6 +158,7 @@ mod tests { use crate::{ Commitment, Nullifier, account::{Account, AccountId, AccountWithMetadata, Nonce}, + encryption::Ciphertext, }; #[test] @@ -181,7 +192,11 @@ mod tests { data: b"post state data".to_vec().try_into().unwrap(), nonce: Nonce(0xFFFF_FFFF_FFFF_FFFF), }], - ciphertexts: vec![Ciphertext(vec![255, 255, 1, 1, 2, 2])], + encrypted_private_post_states: vec![EncryptedAccountData { + ciphertext: Ciphertext(vec![255, 255, 1, 1, 2, 2]), + epk: EphemeralPublicKey(vec![9, 9, 9]), + view_tag: 42, + }], new_commitments: vec![Commitment::new( &AccountId::new([1; 32]), &Account::default(), diff --git a/lee/state_machine/core/src/commitment.rs b/lee/state_machine/core/src/commitment.rs index 7c81c12c..92085d7d 100644 --- a/lee/state_machine/core/src/commitment.rs +++ b/lee/state_machine/core/src/commitment.rs @@ -52,6 +52,7 @@ impl std::fmt::Debug for Commitment { impl Commitment { /// Generates the commitment to a private account owned by user for `account_id`: /// SHA256( `Comm_DS` || `account_id` || `program_owner` || balance || nonce || SHA256(data)). + // TODO: Accept account_id by value as it's Copy #[must_use] pub fn new(account_id: &AccountId, account: &Account) -> Self { const COMMITMENT_PREFIX: &[u8; 32] = diff --git a/lee/state_machine/core/src/encryption/mod.rs b/lee/state_machine/core/src/encryption/mod.rs index 37745d4f..5fa80b60 100644 --- a/lee/state_machine/core/src/encryption/mod.rs +++ b/lee/state_machine/core/src/encryption/mod.rs @@ -6,7 +6,7 @@ use chacha20::{ use risc0_zkvm::sha::{Impl, Sha256 as _}; use serde::{Deserialize, Serialize}; #[cfg(feature = "host")] -pub use shared_key_derivation::{EphemeralPublicKey, MlKem768EncapsulationKey, ViewingPublicKey}; +pub use shared_key_derivation::{MlKem768EncapsulationKey, ViewingPublicKey}; use crate::{Commitment, account::Account, program::PrivateAccountKind}; #[cfg(feature = "host")] @@ -17,6 +17,11 @@ pub type Scalar = [u8; 32]; #[derive(Serialize, Deserialize, Clone, Copy)] pub struct SharedSecretKey(pub [u8; 32]); +/// The ML-KEM-768 ciphertext produced during encapsulation; transmitted on-wire in place of the +/// former ECDH ephemeral public key. Always 1088 bytes for ML-KEM-768. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct EphemeralPublicKey(pub Vec); + pub struct EncryptionScheme; #[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)] @@ -36,6 +41,45 @@ impl std::fmt::Debug for Ciphertext { } } +pub type ViewTag = u8; + +/// Encrypted private-account note for one output. +#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq))] +pub struct EncryptedAccountData { + pub ciphertext: Ciphertext, + pub epk: EphemeralPublicKey, + pub view_tag: ViewTag, +} + +#[cfg(feature = "host")] +impl EncryptedAccountData { + #[must_use] + pub fn new( + ciphertext: Ciphertext, + npk: &crate::NullifierPublicKey, + vpk: &ViewingPublicKey, + epk: EphemeralPublicKey, + ) -> Self { + let view_tag = Self::compute_view_tag(npk, vpk); + Self { + ciphertext, + epk, + view_tag, + } + } + + /// Computes the tag as the first byte of SHA256("/LEE/v0.3/ViewTag/" || npk || vpk). + #[must_use] + pub fn compute_view_tag(npk: &crate::NullifierPublicKey, vpk: &ViewingPublicKey) -> ViewTag { + let mut bytes = Vec::new(); + bytes.extend_from_slice(b"/LEE/v0.3/ViewTag/"); + bytes.extend_from_slice(&npk.to_byte_array()); + bytes.extend_from_slice(vpk.to_bytes()); + Impl::hash_bytes(&bytes).as_bytes()[0] + } +} + impl EncryptionScheme { #[must_use] pub fn encrypt( diff --git a/lee/state_machine/core/src/encryption/shared_key_derivation.rs b/lee/state_machine/core/src/encryption/shared_key_derivation.rs index a3b2fd32..5c982c6f 100644 --- a/lee/state_machine/core/src/encryption/shared_key_derivation.rs +++ b/lee/state_machine/core/src/encryption/shared_key_derivation.rs @@ -2,12 +2,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use ml_kem::{Decapsulate as _, Encapsulate as _, KeyExport as _, Seed}; use serde::{Deserialize, Serialize}; -use crate::SharedSecretKey; - -/// The ML-KEM-768 ciphertext produced during encapsulation; transmitted on-wire in place of the -/// former ECDH ephemeral public key. Always 1088 bytes for ML-KEM-768. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -pub struct EphemeralPublicKey(pub Vec); +use crate::{EphemeralPublicKey, SharedSecretKey}; /// ML-KEM-768 encapsulation key bytes (1184 bytes, opaque to this crate). #[derive( diff --git a/lee/state_machine/core/src/lib.rs b/lee/state_machine/core/src/lib.rs index 466e1f5d..9ad2858e 100644 --- a/lee/state_machine/core/src/lib.rs +++ b/lee/state_machine/core/src/lib.rs @@ -10,7 +10,9 @@ pub use commitment::{ Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, MembershipProof, compute_digest_for_path, }; -pub use encryption::{EncryptionScheme, SharedSecretKey}; +pub use encryption::{ + EncryptedAccountData, EncryptionScheme, EphemeralPublicKey, SharedSecretKey, ViewTag, +}; pub use nullifier::{Identifier, Nullifier, NullifierPublicKey, NullifierSecretKey}; pub use program::PrivateAccountKind; diff --git a/lee/state_machine/core/src/nullifier.rs b/lee/state_machine/core/src/nullifier.rs index d1fbae42..0490ac00 100644 --- a/lee/state_machine/core/src/nullifier.rs +++ b/lee/state_machine/core/src/nullifier.rs @@ -97,6 +97,7 @@ impl Nullifier { } /// Computes a nullifier for an account initialization. + // TODO: Accept account_id by value as it's Copy #[must_use] pub fn for_account_initialization(account_id: &AccountId) -> Self { const INIT_PREFIX: &[u8; 32] = b"/LEE/v0.3/Nullifier/Initialize/\x00"; diff --git a/lee/state_machine/src/privacy_preserving_transaction/circuit.rs b/lee/state_machine/src/privacy_preserving_transaction/circuit.rs index cebef4cf..87c7c5bb 100644 --- a/lee/state_machine/src/privacy_preserving_transaction/circuit.rs +++ b/lee/state_machine/src/privacy_preserving_transaction/circuit.rs @@ -178,8 +178,8 @@ mod tests { #![expect(clippy::shadow_unrelated, reason = "We don't care about it in tests")] use lee_core::{ - Commitment, DUMMY_COMMITMENT_HASH, EncryptionScheme, Nullifier, - PrivacyPreservingCircuitOutput, SharedSecretKey, + Commitment, DUMMY_COMMITMENT_HASH, EncryptedAccountData, EncryptionScheme, + EphemeralPublicKey, Nullifier, PrivacyPreservingCircuitOutput, SharedSecretKey, account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data}, program::{PdaSeed, PrivateAccountKind}, }; @@ -201,7 +201,7 @@ mod tests { idx: usize, ) -> PrivateAccountKind { let (kind, _) = EncryptionScheme::decrypt( - &output.ciphertexts[idx], + &output.encrypted_private_post_states[idx].ciphertext, ssk, &output.new_commitments[idx], u32::try_from(idx).expect("idx fits in u32"), @@ -210,6 +210,17 @@ mod tests { kind } + #[test] + fn proof_inner_roundtrip() { + // `Proof::from_inner(b).into_inner()` must return exactly `b`. Catches + // mutations of `into_inner` returning `vec![]`, `vec![0]`, or `vec![1]`, + // and of `from_inner` discarding its argument. + let bytes = vec![0xDE_u8, 0xAD, 0xBE, 0xEF]; + assert_eq!(Proof::from_inner(bytes.clone()).into_inner(), bytes); + assert!(Proof::from_inner(vec![]).into_inner().is_empty()); + assert_eq!(Proof::from_inner(vec![0xFF]).into_inner(), vec![0xFF_u8]); + } + #[test] fn prove_privacy_preserving_execution_circuit_public_and_private_pre_accounts() { let recipient_keys = test_private_account_keys_1(); @@ -257,6 +268,11 @@ mod tests { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: shared_secret, identifier: 0, @@ -274,10 +290,10 @@ mod tests { assert_eq!(sender_post, expected_sender_post); assert_eq!(output.new_commitments.len(), 1); assert_eq!(output.new_nullifiers.len(), 1); - assert_eq!(output.ciphertexts.len(), 1); + assert_eq!(output.encrypted_private_post_states.len(), 1); let (_identifier, recipient_post) = EncryptionScheme::decrypt( - &output.ciphertexts[0], + &output.encrypted_private_post_states[0].ciphertext, &shared_secret, &output.new_commitments[0], 0, @@ -356,6 +372,11 @@ mod tests { .unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: shared_secret_1, nsk: sender_keys.nsk, membership_proof: commitment_set @@ -364,6 +385,11 @@ mod tests { identifier: 0, }, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: shared_secret_2, identifier: 0, @@ -378,10 +404,10 @@ mod tests { assert!(output.public_post_states.is_empty()); assert_eq!(output.new_commitments, expected_new_commitments); assert_eq!(output.new_nullifiers, expected_new_nullifiers); - assert_eq!(output.ciphertexts.len(), 2); + assert_eq!(output.encrypted_private_post_states.len(), 2); let (_identifier, sender_post) = EncryptionScheme::decrypt( - &output.ciphertexts[0], + &output.encrypted_private_post_states[0].ciphertext, &shared_secret_1, &expected_new_commitments[0], 0, @@ -390,7 +416,7 @@ mod tests { assert_eq!(sender_post, expected_private_account_1); let (_identifier, recipient_post) = EncryptionScheme::decrypt( - &output.ciphertexts[1], + &output.encrypted_private_post_states[1].ciphertext, &shared_secret_2, &expected_new_commitments[1], 1, @@ -432,6 +458,11 @@ mod tests { vec![pre], instruction, vec![InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &account_keys.npk(), + &account_keys.vpk(), + ), npk: account_keys.npk(), ssk: shared_secret, identifier: 0, @@ -461,6 +492,8 @@ mod tests { vec![pre_state], Program::serialize_instruction(seed).unwrap(), vec![InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), npk, ssk: shared_secret, identifier, @@ -508,6 +541,8 @@ mod tests { vec![pda_pre], instruction, vec![InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), npk, ssk: shared_secret_pda, identifier: 0, @@ -561,6 +596,8 @@ mod tests { instruction, vec![ InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), npk, ssk: shared_secret_pda, identifier: 0, @@ -618,6 +655,11 @@ mod tests { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &shared_npk, + &shared_keys.vpk(), + ), npk: shared_npk, ssk: shared_secret, identifier: shared_identifier, @@ -647,6 +689,8 @@ mod tests { Program::serialize_instruction(authenticated_transfer_core::Instruction::Initialize) .unwrap(), vec![InputAccountIdentity::PrivateAuthorizedInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&keys.npk(), &keys.vpk()), ssk, nsk: keys.nsk, identifier, @@ -691,6 +735,8 @@ mod tests { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&keys.npk(), &keys.vpk()), npk: keys.npk(), ssk, identifier, @@ -735,6 +781,8 @@ mod tests { .unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&keys.npk(), &keys.vpk()), ssk, nsk: keys.nsk, membership_proof: commitment_set.get_proof_for(&commitment).unwrap(), @@ -789,6 +837,8 @@ mod tests { Program::serialize_instruction((seed, 1_u128, auth_transfer_id, false)).unwrap(), vec![ InputAccountIdentity::PrivatePdaUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), ssk, nsk: keys.nsk, membership_proof: commitment_set.get_proof_for(&pda_commitment).unwrap(), @@ -827,6 +877,8 @@ mod tests { vec![pre_state], Program::serialize_instruction(seed).unwrap(), vec![InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), npk, ssk: shared_secret, identifier: 99, @@ -870,6 +922,8 @@ mod tests { Program::serialize_instruction((seed, 1_u128, auth_transfer_id, false)).unwrap(), vec![ InputAccountIdentity::PrivatePdaUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), ssk, nsk: keys.nsk, membership_proof: commitment_set.get_proof_for(&pda_commitment).unwrap(), diff --git a/lee/state_machine/src/privacy_preserving_transaction/message.rs b/lee/state_machine/src/privacy_preserving_transaction/message.rs index 6a289a0a..b2594912 100644 --- a/lee/state_machine/src/privacy_preserving_transaction/message.rs +++ b/lee/state_machine/src/privacy_preserving_transaction/message.rs @@ -1,52 +1,16 @@ use borsh::{BorshDeserialize, BorshSerialize}; use lee_core::{ - Commitment, CommitmentSetDigest, Nullifier, NullifierPublicKey, PrivacyPreservingCircuitOutput, + Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput, account::{Account, Nonce}, - encryption::{Ciphertext, EphemeralPublicKey, ViewingPublicKey}, program::{BlockValidityWindow, TimestampValidityWindow}, }; +pub use lee_core::{EncryptedAccountData, ViewTag}; use sha2::{Digest as _, Sha256}; use crate::{AccountId, error::LeeError}; const PREFIX: &[u8; 32] = b"/LEE/v0.3/Message/Privacy/\x00\x00\x00\x00\x00\x00"; -pub type ViewTag = u8; - -#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -pub struct EncryptedAccountData { - pub ciphertext: Ciphertext, - pub epk: EphemeralPublicKey, - pub view_tag: ViewTag, -} - -impl EncryptedAccountData { - fn new( - ciphertext: Ciphertext, - npk: &NullifierPublicKey, - vpk: &ViewingPublicKey, - epk: EphemeralPublicKey, - ) -> Self { - let view_tag = Self::compute_view_tag(npk, vpk); - Self { - ciphertext, - epk, - view_tag, - } - } - - /// Computes the tag as the first byte of SHA256("/LEE/v0.3/ViewTag/" || Npk || vpk). - #[must_use] - pub fn compute_view_tag(npk: &NullifierPublicKey, vpk: &ViewingPublicKey) -> ViewTag { - let mut hasher = Sha256::new(); - hasher.update(b"/LEE/v0.3/ViewTag/"); - hasher.update(npk.to_byte_array()); - hasher.update(vpk.to_bytes()); - let digest: [u8; 32] = hasher.finalize().into(); - digest[0] - } -} - #[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct Message { pub public_account_ids: Vec, @@ -92,28 +56,13 @@ impl Message { pub fn try_from_circuit_output( public_account_ids: Vec, nonces: Vec, - public_keys: Vec<(NullifierPublicKey, ViewingPublicKey, EphemeralPublicKey)>, output: PrivacyPreservingCircuitOutput, ) -> Result { - if public_keys.len() != output.ciphertexts.len() { - return Err(LeeError::InvalidInput( - "Ephemeral public keys and ciphertexts length mismatch".into(), - )); - } - - let encrypted_private_post_states = output - .ciphertexts - .into_iter() - .zip(public_keys) - .map(|(ciphertext, (npk, vpk, epk))| { - EncryptedAccountData::new(ciphertext, &npk, &vpk, epk) - }) - .collect(); Ok(Self { public_account_ids, nonces, public_post_states: output.public_post_states, - encrypted_private_post_states, + encrypted_private_post_states: output.encrypted_private_post_states, new_commitments: output.new_commitments, new_nullifiers: output.new_nullifiers, block_validity_window: output.block_validity_window, diff --git a/lee/state_machine/src/program.rs b/lee/state_machine/src/program.rs index 9692a5ee..c4223810 100644 --- a/lee/state_machine/src/program.rs +++ b/lee/state_machine/src/program.rs @@ -498,6 +498,20 @@ mod tests { } } + #[test] + fn elf_returns_the_program_bytecode_constant() { + // `Program::elf` must return exactly the compile-time ELF, never an empty + // or placeholder slice. Catches mutations returning `Vec::leak(Vec::new())`, + // `Vec::leak(vec![0])`, or `Vec::leak(vec![1])`. + let at = Program::authenticated_transfer_program(); + assert!(!at.elf().is_empty()); + assert_eq!(at.elf(), AUTHENTICATED_TRANSFER_ELF); + + let token = Program::token(); + assert!(!token.elf().is_empty()); + assert_eq!(token.elf(), TOKEN_ELF); + } + #[test] fn program_execution() { let program = Program::simple_balance_transfer(); diff --git a/lee/state_machine/src/program_deployment_transaction/message.rs b/lee/state_machine/src/program_deployment_transaction/message.rs index a51e4149..866399e8 100644 --- a/lee/state_machine/src/program_deployment_transaction/message.rs +++ b/lee/state_machine/src/program_deployment_transaction/message.rs @@ -16,3 +16,18 @@ impl Message { self.bytecode } } + +#[cfg(test)] +mod tests { + use super::Message; + + #[test] + fn bytecode_roundtrip() { + // `Message::new(b).into_bytecode()` must return exactly `b`. Catches + // mutations of `into_bytecode` returning `vec![]`, `vec![0]`, or `vec![1]`. + let bytecode = vec![0x7F_u8, 0x45, 0x4C, 0x46]; // ELF magic + assert_eq!(Message::new(bytecode.clone()).into_bytecode(), bytecode); + assert!(Message::new(vec![]).into_bytecode().is_empty()); + assert_eq!(Message::new(vec![0xAB]).into_bytecode(), vec![0xAB_u8]); + } +} diff --git a/lee/state_machine/src/signature/mod.rs b/lee/state_machine/src/signature/mod.rs index a46b1ff5..ba049419 100644 --- a/lee/state_machine/src/signature/mod.rs +++ b/lee/state_machine/src/signature/mod.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; +use k256::ecdsa::signature::hazmat::PrehashVerifier as _; pub use private_key::PrivateKey; pub use public_key::PublicKey; use rand::{RngCore as _, rngs::OsRng}; @@ -72,7 +73,7 @@ impl Signature { return false; }; - pk.verify_raw(bytes, &sig).is_ok() + pk.verify_prehash(bytes, &sig).is_ok() } } diff --git a/lee/state_machine/src/signature/private_key.rs b/lee/state_machine/src/signature/private_key.rs index 29f3cd3c..c13be154 100644 --- a/lee/state_machine/src/signature/private_key.rs +++ b/lee/state_machine/src/signature/private_key.rs @@ -1,7 +1,9 @@ use std::str::FromStr; +use k256::elliptic_curve::{PrimeField as _, sec1::ToEncodedPoint as _}; use rand::{Rng as _, rngs::OsRng}; use serde_with::{DeserializeFromStr, SerializeDisplay}; +use sha2::{Digest as _, Sha256}; use crate::error::LeeError; @@ -60,6 +62,29 @@ impl PrivateKey { pub const fn value(&self) -> &[u8; 32] { &self.0 } + + /// `tweak` produces the "tweaked secret key" (`sk`) given a public account's `ssk`. + /// We use "tweaked keys" to shield the public accounts' `ssk` against quantum threats. + /// The "tweaked keys" are used for Schnorr Signatures (BIP-340). + /// The usage of these keys will be greatly reduced once LEE is upgraded to use a PQ signatures. + pub fn tweak(value: &[u8; 32]) -> Result { + if !Self::is_valid_key(*value) { + return Err(LeeError::InvalidPrivateKey); + } + + let sk = k256::SecretKey::from_slice(value).map_err(|_e| LeeError::InvalidPrivateKey)?; + + let hashed: [u8; 32] = + Sha256::digest(sk.public_key().to_encoded_point(true).as_bytes()).into(); + + let sk = sk.to_nonzero_scalar(); + + let scalar = k256::Scalar::from_repr(hashed.into()) + .into_option() + .ok_or(LeeError::InvalidPrivateKey)?; + + Self::try_new(sk.add(&scalar).to_bytes().into()) + } } #[cfg(test)] @@ -75,4 +100,33 @@ mod tests { fn produce_key() { let _key = PrivateKey::new_os_random(); } + + #[test] + fn tweak_rejects_zero_key() { + assert!(matches!( + PrivateKey::tweak(&[0_u8; 32]), + Err(LeeError::InvalidPrivateKey) + )); + } + + // tweak: 0xFFโ€ฆFF exceeds the secp256k1 curve order + #[test] + fn tweak_rejects_out_of_range_key() { + assert!(matches!( + PrivateKey::tweak(&[0xFF; 32]), + Err(LeeError::InvalidPrivateKey) + )); + } + + #[test] + fn tweak_deterministic() { + let tweaked = PrivateKey::tweak(&[1_u8; 32]).unwrap(); + assert_eq!( + tweaked.value(), + &[ + 242, 210, 33, 19, 65, 108, 136, 176, 179, 128, 110, 210, 107, 193, 168, 112, 206, + 171, 86, 238, 131, 10, 39, 36, 44, 39, 246, 20, 46, 193, 204, 66 + ] + ); + } } diff --git a/lee/state_machine/src/state.rs b/lee/state_machine/src/state.rs index 4b74cf55..c7152917 100644 --- a/lee/state_machine/src/state.rs +++ b/lee/state_machine/src/state.rs @@ -418,8 +418,8 @@ pub mod tests { use authenticated_transfer_core::Instruction as AuthTransferInstruction; use lee_core::{ - BlockId, Commitment, InputAccountIdentity, Nullifier, NullifierPublicKey, - NullifierSecretKey, SharedSecretKey, Timestamp, + BlockId, Commitment, EncryptedAccountData, InputAccountIdentity, Nullifier, + NullifierPublicKey, NullifierSecretKey, SharedSecretKey, Timestamp, account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data}, encryption::{EphemeralPublicKey, ViewingPublicKey}, program::{ @@ -613,6 +613,48 @@ pub mod tests { PublicTransaction::new(message, witness_set) } + #[test] + fn genesis_system_accounts_have_expected_contents() { + // System-account IDs must be distinct and non-default, and the genesis + // faucet/bridge accounts must carry their expected field values. Catches + // mutations that replace `system_faucet_account`/`system_bridge_account` + // with `Default::default()`, delete their `balance`/`program_owner` + // fields, or replace `system_bridge_account_id` with `Default::default()`. + let faucet_id = system_faucet_account_id(); + let bridge_id = system_bridge_account_id(); + assert_ne!(bridge_id, AccountId::default()); + assert_ne!(faucet_id, bridge_id); + + let state = V03State::new_with_genesis_accounts(&[], vec![], 0); + let default_owner = Account::default().program_owner; + + let faucet = state.get_account_by_id(faucet_id); + assert_eq!(faucet.balance, u128::MAX, "faucet must hold u128::MAX"); + assert_ne!( + faucet.program_owner, default_owner, + "faucet must have a non-default program_owner" + ); + + let bridge = state.get_account_by_id(bridge_id); + assert_ne!( + bridge.program_owner, default_owner, + "bridge must have a non-default program_owner" + ); + } + + #[test] + fn genesis_commitment_set_digest_differs_from_empty_state() { + // The genesis state inserts DUMMY_COMMITMENT, so its commitment-set digest + // must differ from a freshly-created empty state's all-zero root. Catches + // the mutation that replaces `commitment_set_digest` with `Default::default()`. + let genesis = V03State::new_with_genesis_accounts(&[], vec![], 0); + let empty = V03State::new(); + assert_ne!( + genesis.commitment_set_digest(), + empty.commitment_set_digest() + ); + } + #[test] fn new_with_genesis() { let key1 = PrivateKey::try_new([1; 32]).unwrap(); @@ -1376,6 +1418,11 @@ pub mod tests { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivateUnauthorized { + epk, + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: shared_secret, identifier: 0, @@ -1388,7 +1435,6 @@ pub mod tests { let message = Message::try_from_circuit_output( vec![sender_keys.account_id()], vec![sender_nonce], - vec![(recipient_keys.npk(), recipient_keys.vpk(), epk)], output, ) .unwrap(); @@ -1429,6 +1475,11 @@ pub mod tests { .unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: epk_1, + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: shared_secret_1, nsk: sender_keys.nsk, membership_proof: state @@ -1437,6 +1488,11 @@ pub mod tests { identifier: 0, }, InputAccountIdentity::PrivateUnauthorized { + epk: epk_2, + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: shared_secret_2, identifier: 0, @@ -1446,16 +1502,7 @@ pub mod tests { ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![ - (sender_keys.npk(), sender_keys.vpk(), epk_1), - (recipient_keys.npk(), recipient_keys.vpk(), epk_2), - ], - output, - ) - .unwrap(); + let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); @@ -1494,6 +1541,11 @@ pub mod tests { .unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk, + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: shared_secret, nsk: sender_keys.nsk, membership_proof: state @@ -1507,13 +1559,8 @@ pub mod tests { ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![*recipient_account_id], - vec![], - vec![(sender_keys.npk(), sender_keys.vpk(), epk)], - output, - ) - .unwrap(); + let message = + Message::try_from_circuit_output(vec![*recipient_account_id], vec![], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); @@ -1630,6 +1677,79 @@ pub mod tests { assert!(state.private_state.1.contains(&expected_new_nullifier)); } + fn valid_private_transfer_tx_and_state() -> (V03State, PrivacyPreservingTransaction) { + let sender_keys = test_private_account_keys_1(); + let sender_private_account = Account { + program_owner: Program::authenticated_transfer_program().id(), + balance: 100, + nonce: Nonce(0xdead_beef), + ..Account::default() + }; + let recipient_keys = test_private_account_keys_2(); + let state = V03State::new_with_genesis_accounts(&[], vec![], 0) + .with_private_account(&sender_keys, &sender_private_account); + let tx = private_balance_transfer_for_tests( + &sender_keys, + &sender_private_account, + &recipient_keys, + 37, + &state, + ); + (state, tx) + } + + /// After a valid fully-private tx is proven, tampering with a note's epk should + /// make the shielding proof invalid. + #[test] + fn privacy_tampered_epk_is_rejected() { + use crate::validated_state_diff::ValidatedStateDiff; + + let (state, mut tx) = valid_private_transfer_tx_and_state(); + + // Baseline: the untampered tx verifies + assert!( + ValidatedStateDiff::from_privacy_preserving_transaction(&tx, &state, 1, 0).is_ok(), + "the unmodified private transfer must verify" + ); + + // Flip a byte of the first note's epk + tx.message.encrypted_private_post_states[0].epk.0[0] ^= 0xFF; + + assert!( + matches!( + ValidatedStateDiff::from_privacy_preserving_transaction(&tx, &state, 1, 0), + Err(LeeError::InvalidPrivacyPreservingProof) + ), + "a tampered epk must be rejected by proof verification" + ); + } + + /// After a valid fully-private tx is proven, tampering with a note's view tag should + /// make the shielding proof invalid. + #[test] + fn privacy_tampered_view_tag_is_rejected() { + use crate::validated_state_diff::ValidatedStateDiff; + + let (state, mut tx) = valid_private_transfer_tx_and_state(); + + // Baseline: the untampered tx verifies. + assert!( + ValidatedStateDiff::from_privacy_preserving_transaction(&tx, &state, 1, 0).is_ok(), + "the unmodified private transfer must verify" + ); + + // Flip the first note's view_tag + tx.message.encrypted_private_post_states[0].view_tag ^= 0xFF; + + assert!( + matches!( + ValidatedStateDiff::from_privacy_preserving_transaction(&tx, &state, 1, 0), + Err(LeeError::InvalidPrivacyPreservingProof) + ), + "a tampered view_tag must be rejected by proof verification" + ); + } + #[test] fn transition_from_privacy_preserving_transaction_deshielded() { let sender_keys = test_private_account_keys_1(); @@ -1992,6 +2112,11 @@ pub mod tests { Program::serialize_instruction(10_u128).unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: SharedSecretKey::encapsulate_deterministic( &sender_keys.vpk(), &[0_u8; 32], @@ -2003,6 +2128,11 @@ pub mod tests { identifier: 0, }, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: SharedSecretKey::encapsulate_deterministic( &recipient_keys.vpk(), @@ -2048,6 +2178,11 @@ pub mod tests { Program::serialize_instruction(10_u128).unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: SharedSecretKey::encapsulate_deterministic( &sender_keys.vpk(), &[0_u8; 32], @@ -2059,6 +2194,11 @@ pub mod tests { identifier: 0, }, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: SharedSecretKey::encapsulate_deterministic( &recipient_keys.vpk(), @@ -2104,6 +2244,11 @@ pub mod tests { Program::serialize_instruction(10_u128).unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: SharedSecretKey::encapsulate_deterministic( &sender_keys.vpk(), &[0_u8; 32], @@ -2115,6 +2260,11 @@ pub mod tests { identifier: 0, }, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: SharedSecretKey::encapsulate_deterministic( &recipient_keys.vpk(), @@ -2160,6 +2310,11 @@ pub mod tests { Program::serialize_instruction(10_u128).unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: SharedSecretKey::encapsulate_deterministic( &sender_keys.vpk(), &[0_u8; 32], @@ -2171,6 +2326,11 @@ pub mod tests { identifier: 0, }, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: SharedSecretKey::encapsulate_deterministic( &recipient_keys.vpk(), @@ -2216,6 +2376,11 @@ pub mod tests { Program::serialize_instruction(10_u128).unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: SharedSecretKey::encapsulate_deterministic( &sender_keys.vpk(), &[0_u8; 32], @@ -2227,6 +2392,11 @@ pub mod tests { identifier: 0, }, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: SharedSecretKey::encapsulate_deterministic( &recipient_keys.vpk(), @@ -2270,6 +2440,11 @@ pub mod tests { Program::serialize_instruction(10_u128).unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: SharedSecretKey::encapsulate_deterministic( &sender_keys.vpk(), &[0_u8; 32], @@ -2281,6 +2456,11 @@ pub mod tests { identifier: 0, }, InputAccountIdentity::PrivateUnauthorized { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), npk: recipient_keys.npk(), ssk: SharedSecretKey::encapsulate_deterministic( &recipient_keys.vpk(), @@ -2326,6 +2506,8 @@ pub mod tests { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), npk, ssk: shared_secret, identifier: u128::MAX, @@ -2359,6 +2541,8 @@ pub mod tests { vec![pre_state], Program::serialize_instruction(seed).unwrap(), vec![InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), npk, ssk: shared_secret, identifier: u128::MAX, @@ -2370,7 +2554,7 @@ pub mod tests { let (output, _proof) = result.expect("private PDA claim should succeed"); assert_eq!(output.new_nullifiers.len(), 1); assert_eq!(output.new_commitments.len(), 1); - assert_eq!(output.ciphertexts.len(), 1); + assert_eq!(output.encrypted_private_post_states.len(), 1); assert!(output.public_pre_states.is_empty()); assert!(output.public_post_states.is_empty()); } @@ -2400,6 +2584,8 @@ pub mod tests { vec![pre_state], Program::serialize_instruction(seed).unwrap(), vec![InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk_b, &keys_b.vpk()), npk: npk_b, ssk: shared_secret, identifier: u128::MAX, @@ -2437,6 +2623,8 @@ pub mod tests { vec![pre_state], Program::serialize_instruction((seed, seed, callee_id)).unwrap(), vec![InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), npk, ssk: shared_secret, identifier: u128::MAX, @@ -2477,6 +2665,8 @@ pub mod tests { vec![pre_state], Program::serialize_instruction((claim_seed, wrong_delegated_seed, callee_id)).unwrap(), vec![InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), npk, ssk: shared_secret, identifier: u128::MAX, @@ -2516,12 +2706,16 @@ pub mod tests { Program::serialize_instruction(seed).unwrap(), vec![ InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&keys_a.npk(), &keys_a.vpk()), npk: keys_a.npk(), ssk: shared_a, identifier: u128::MAX, seed: None, }, InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&keys_b.npk(), &keys_b.vpk()), npk: keys_b.npk(), ssk: shared_b, identifier: u128::MAX, @@ -2564,6 +2758,8 @@ pub mod tests { vec![owned_pre_state], Program::serialize_instruction(()).unwrap(), vec![InputAccountIdentity::PrivatePdaInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()), npk, ssk: shared_secret, identifier: u128::MAX, @@ -2652,12 +2848,22 @@ pub mod tests { Program::serialize_instruction(100_u128).unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: shared_secret, nsk: sender_keys.nsk, membership_proof: (1, vec![]), identifier: 0, }, InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: shared_secret, nsk: sender_keys.nsk, membership_proof: (1, vec![]), @@ -3003,6 +3209,11 @@ pub mod tests { .unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk, + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: shared_secret, nsk: sender_keys.nsk, membership_proof: state @@ -3016,13 +3227,9 @@ pub mod tests { ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![recipient_account_id], - vec![Nonce(0)], - vec![(sender_keys.npk(), sender_keys.vpk(), epk)], - output, - ) - .unwrap(); + let message = + Message::try_from_circuit_output(vec![recipient_account_id], vec![Nonce(0)], output) + .unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_private_key]); let tx = PrivacyPreservingTransaction::new(message, witness_set); @@ -3129,6 +3336,11 @@ pub mod tests { Program::serialize_instruction(instruction).unwrap(), vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: to_epk, + view_tag: EncryptedAccountData::compute_view_tag( + &to_keys.npk(), + &to_keys.vpk(), + ), ssk: to_ss, nsk: from_keys.nsk, membership_proof: state @@ -3137,6 +3349,11 @@ pub mod tests { identifier: 0, }, InputAccountIdentity::PrivateAuthorizedUpdate { + epk: from_epk, + view_tag: EncryptedAccountData::compute_view_tag( + &from_keys.npk(), + &from_keys.vpk(), + ), ssk: from_ss, nsk: to_keys.nsk, membership_proof: state @@ -3149,16 +3366,7 @@ pub mod tests { ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![ - (to_keys.npk(), to_keys.vpk(), to_epk), - (from_keys.npk(), from_keys.vpk(), from_epk), - ], - output, - ) - .unwrap(); + let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); let transaction = PrivacyPreservingTransaction::new(message, witness_set); @@ -3406,6 +3614,11 @@ pub mod tests { vec![authorized_account], Program::serialize_instruction(instruction).unwrap(), vec![InputAccountIdentity::PrivateAuthorizedInit { + epk, + view_tag: EncryptedAccountData::compute_view_tag( + &private_keys.npk(), + &private_keys.vpk(), + ), ssk: shared_secret, nsk: private_keys.nsk, identifier: 0, @@ -3415,13 +3628,7 @@ pub mod tests { .unwrap(); // Create message from circuit output - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![(private_keys.npk(), private_keys.vpk(), epk)], - output, - ) - .unwrap(); + let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); @@ -3454,6 +3661,11 @@ pub mod tests { vec![unauthorized_account], Program::serialize_instruction(0_u128).unwrap(), vec![InputAccountIdentity::PrivateUnauthorized { + epk, + view_tag: EncryptedAccountData::compute_view_tag( + &private_keys.npk(), + &private_keys.vpk(), + ), npk: private_keys.npk(), ssk: shared_secret, identifier: 0, @@ -3462,13 +3674,7 @@ pub mod tests { ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![(private_keys.npk(), private_keys.vpk(), epk)], - output, - ) - .unwrap(); + let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); let tx = PrivacyPreservingTransaction::new(message, witness_set); @@ -3506,6 +3712,11 @@ pub mod tests { vec![authorized_account.clone()], Program::serialize_instruction(instruction).unwrap(), vec![InputAccountIdentity::PrivateAuthorizedInit { + epk, + view_tag: EncryptedAccountData::compute_view_tag( + &private_keys.npk(), + &private_keys.vpk(), + ), ssk: shared_secret, nsk: private_keys.nsk, identifier: 0, @@ -3514,13 +3725,7 @@ pub mod tests { ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![(private_keys.npk(), private_keys.vpk(), epk)], - output, - ) - .unwrap(); + let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); let tx = PrivacyPreservingTransaction::new(message, witness_set); @@ -3553,6 +3758,11 @@ pub mod tests { vec![account_metadata], Program::serialize_instruction(()).unwrap(), vec![InputAccountIdentity::PrivateAuthorizedInit { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &private_keys.npk(), + &private_keys.vpk(), + ), ssk: shared_secret2, nsk: private_keys.nsk, identifier: 0, @@ -3630,6 +3840,11 @@ pub mod tests { vec![private_account], Program::serialize_instruction(instruction).unwrap(), vec![InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0) .0, nsk: sender_keys.nsk, @@ -3657,6 +3872,11 @@ pub mod tests { vec![private_account], Program::serialize_instruction(instruction).unwrap(), vec![InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.npk(), + &sender_keys.vpk(), + ), ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0) .0, nsk: sender_keys.nsk, @@ -3718,6 +3938,11 @@ pub mod tests { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(Vec::new()), + view_tag: EncryptedAccountData::compute_view_tag( + &recipient_keys.npk(), + &recipient_keys.vpk(), + ), ssk: recipient, nsk: recipient_keys.nsk, membership_proof: state @@ -3872,6 +4097,11 @@ pub mod tests { vec![pre], Program::serialize_instruction(instruction).unwrap(), vec![InputAccountIdentity::PrivateUnauthorized { + epk, + view_tag: EncryptedAccountData::compute_view_tag( + &account_keys.npk(), + &account_keys.vpk(), + ), npk: account_keys.npk(), ssk: shared_secret, identifier: 0, @@ -3880,13 +4110,7 @@ pub mod tests { ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![(account_keys.npk(), account_keys.vpk(), epk)], - output, - ) - .unwrap(); + let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); PrivacyPreservingTransaction::new(message, witness_set) @@ -3941,6 +4165,11 @@ pub mod tests { vec![pre], Program::serialize_instruction(instruction).unwrap(), vec![InputAccountIdentity::PrivateUnauthorized { + epk, + view_tag: EncryptedAccountData::compute_view_tag( + &account_keys.npk(), + &account_keys.vpk(), + ), npk: account_keys.npk(), ssk: shared_secret, identifier: 0, @@ -3949,13 +4178,7 @@ pub mod tests { ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![], - vec![], - vec![(account_keys.npk(), account_keys.vpk(), epk)], - output, - ) - .unwrap(); + let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); PrivacyPreservingTransaction::new(message, witness_set) @@ -4504,6 +4727,11 @@ pub mod tests { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivatePdaInit { + epk: alice_epk_0.clone(), + view_tag: EncryptedAccountData::compute_view_tag( + &alice_npk, + &alice_keys.vpk(), + ), npk: alice_npk, ssk: alice_shared_0, identifier: 0, @@ -4513,13 +4741,9 @@ pub mod tests { &auth_transfer.clone().into(), ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![funder_id], - vec![funder_nonce], - vec![(alice_npk, alice_keys.vpk(), alice_epk_0.clone())], - output, - ) - .unwrap(); + let message = + Message::try_from_circuit_output(vec![funder_id], vec![funder_nonce], output) + .unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[&funder_keys.signing_key]); state .transition_from_privacy_preserving_transaction( @@ -4544,6 +4768,11 @@ pub mod tests { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivatePdaInit { + epk: alice_epk_1.clone(), + view_tag: EncryptedAccountData::compute_view_tag( + &alice_npk, + &alice_keys.vpk(), + ), npk: alice_npk, ssk: alice_shared_1, identifier: 1, @@ -4553,13 +4782,9 @@ pub mod tests { &auth_transfer.into(), ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![funder_id], - vec![funder_nonce], - vec![(alice_npk, alice_keys.vpk(), alice_epk_1.clone())], - output, - ) - .unwrap(); + let message = + Message::try_from_circuit_output(vec![funder_id], vec![funder_nonce], output) + .unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[&funder_keys.signing_key]); state .transition_from_privacy_preserving_transaction( @@ -4587,6 +4812,11 @@ pub mod tests { Program::serialize_instruction((seed, amount, auth_transfer_id)).unwrap(), vec![ InputAccountIdentity::PrivatePdaUpdate { + epk: alice_epk_0, + view_tag: EncryptedAccountData::compute_view_tag( + &alice_npk, + &alice_keys.vpk(), + ), ssk: alice_shared_0, nsk: alice_keys.nsk, membership_proof: state @@ -4600,13 +4830,9 @@ pub mod tests { &spend_with_deps, ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![recipient_id], - vec![Nonce(0)], - vec![(alice_npk, alice_keys.vpk(), alice_epk_0)], - output, - ) - .unwrap(); + let message = + Message::try_from_circuit_output(vec![recipient_id], vec![Nonce(0)], output) + .unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_signing_key]); state .transition_from_privacy_preserving_transaction( @@ -4628,6 +4854,11 @@ pub mod tests { Program::serialize_instruction((seed, amount, auth_transfer_id)).unwrap(), vec![ InputAccountIdentity::PrivatePdaUpdate { + epk: alice_epk_1, + view_tag: EncryptedAccountData::compute_view_tag( + &alice_npk, + &alice_keys.vpk(), + ), ssk: alice_shared_1, nsk: alice_keys.nsk, membership_proof: state @@ -4641,13 +4872,8 @@ pub mod tests { &spend_with_deps, ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![recipient_id], - vec![], - vec![(alice_npk, alice_keys.vpk(), alice_epk_1)], - output, - ) - .unwrap(); + let message = + Message::try_from_circuit_output(vec![recipient_id], vec![], output).unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[]); state .transition_from_privacy_preserving_transaction( @@ -4690,6 +4916,11 @@ pub mod tests { vec![ InputAccountIdentity::Public, InputAccountIdentity::PrivatePdaUpdate { + epk: EphemeralPublicKey(vec![12_u8; 1088]), + view_tag: EncryptedAccountData::compute_view_tag( + &alice_npk, + &alice_keys.vpk(), + ), nsk: alice_keys.nsk, ssk: alice_shared_1_refund, membership_proof: state @@ -4702,17 +4933,9 @@ pub mod tests { &Program::authenticated_transfer_program().into(), ) .unwrap(); - let message = Message::try_from_circuit_output( - vec![recipient_id], - vec![recipient_nonce], - vec![( - alice_npk, - alice_keys.vpk(), - EphemeralPublicKey(vec![12_u8; 1088]), - )], - output, - ) - .unwrap(); + let message = + Message::try_from_circuit_output(vec![recipient_id], vec![recipient_nonce], output) + .unwrap(); let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_signing_key]); state .transition_from_privacy_preserving_transaction( diff --git a/lee/state_machine/src/validated_state_diff.rs b/lee/state_machine/src/validated_state_diff.rs index cfbd1703..0d71d485 100644 --- a/lee/state_machine/src/validated_state_diff.rs +++ b/lee/state_machine/src/validated_state_diff.rs @@ -492,12 +492,7 @@ fn check_privacy_preserving_circuit_proof_is_valid( let output = PrivacyPreservingCircuitOutput { public_pre_states: public_pre_states.to_vec(), public_post_states: message.public_post_states.clone(), - ciphertexts: message - .encrypted_private_post_states - .iter() - .cloned() - .map(|value| value.ciphertext) - .collect(), + encrypted_private_post_states: message.encrypted_private_post_states.clone(), new_commitments: message.new_commitments.clone(), new_nullifiers: message.new_nullifiers.clone(), block_validity_window: message.block_validity_window, @@ -526,6 +521,44 @@ mod tests { validated_state_diff::ValidatedStateDiff, }; + #[test] + fn public_diff_reflects_a_successful_transfer() { + // A successful native transfer must record the debited sender in + // `public_diff()`. Catches the mutation that replaces `public_diff` with + // `HashMap::new()` (which would hide every account change). + use authenticated_transfer_core::Instruction as AtInstruction; + + let from_key = PrivateKey::try_new([1_u8; 32]).unwrap(); + let from = AccountId::from(&PublicKey::new_from_private_key(&from_key)); + let to_key = PrivateKey::try_new([2_u8; 32]).unwrap(); + let to = AccountId::from(&PublicKey::new_from_private_key(&to_key)); + + let state = V03State::new_with_genesis_accounts(&[(from, 100)], vec![], 0); + let program_id = Program::authenticated_transfer_program().id(); + let message = Message::try_new( + program_id, + vec![from, to], + vec![Nonce(0), Nonce(0)], + AtInstruction::Transfer { amount: 5 }, + ) + .unwrap(); + let witness_set = WitnessSet::for_message(&message, &[&from_key, &to_key]); + let tx = crate::PublicTransaction::new(message, witness_set); + + let diff = ValidatedStateDiff::from_public_transaction(&tx, &state, 1, 0) + .expect("a valid native transfer must validate"); + let public_diff = diff.public_diff(); + + assert!( + public_diff.contains_key(&from), + "public_diff must contain the debited sender", + ); + assert_eq!( + public_diff[&from].balance, 95, + "sender balance in the diff must reflect the debit", + ); + } + /// Privacy-path version of the authorization-injection attack. The test passes when the /// attack is rejected and the victim's balance is left untouched. /// @@ -542,7 +575,7 @@ mod tests { #[test] fn privacy_malicious_programs_cannot_drain_public_victim() { use lee_core::{ - Commitment, InputAccountIdentity, SharedSecretKey, + Commitment, EncryptedAccountData, InputAccountIdentity, SharedSecretKey, account::{Account, AccountWithMetadata}, }; @@ -626,6 +659,11 @@ mod tests { // [2] recipient โ€” first seen in authenticated_transfer's program_output.pre_states let account_identities = vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: attacker_epk, + view_tag: EncryptedAccountData::compute_view_tag( + &attacker_keys.npk(), + &attacker_keys.vpk(), + ), ssk: attacker_ssk, nsk: attacker_keys.nsk, membership_proof, @@ -650,7 +688,6 @@ mod tests { let message = Message::try_from_circuit_output( vec![victim_id, recipient_id], vec![], // no public signers, no nonces - vec![(attacker_keys.npk(), attacker_keys.vpk(), attacker_epk)], circuit_output, ) .unwrap(); @@ -690,7 +727,7 @@ mod tests { #[test] fn privacy_malicious_programs_cannot_drain_private_victim() { use lee_core::{ - Commitment, InputAccountIdentity, SharedSecretKey, + Commitment, EncryptedAccountData, InputAccountIdentity, SharedSecretKey, account::{Account, AccountWithMetadata}, }; @@ -782,6 +819,11 @@ mod tests { // so PrivateAuthorizedUpdate is not an option. let account_identities = vec![ InputAccountIdentity::PrivateAuthorizedUpdate { + epk: attacker_epk, + view_tag: EncryptedAccountData::compute_view_tag( + &attacker_keys.npk(), + &attacker_keys.vpk(), + ), ssk: attacker_ssk, nsk: attacker_keys.nsk, membership_proof, @@ -807,7 +849,6 @@ mod tests { let message = Message::try_from_circuit_output( vec![victim_id, recipient_id], vec![], // no public signers, no nonces - vec![(attacker_keys.npk(), attacker_keys.vpk(), attacker_epk)], circuit_output, ) .unwrap(); diff --git a/lez/common/src/block.rs b/lez/common/src/block.rs index 8f82c56a..6e956f9f 100644 --- a/lez/common/src/block.rs +++ b/lez/common/src/block.rs @@ -5,14 +5,12 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest as _, Sha256, digest::FixedOutput as _}; use crate::{HashType, transaction::LeeTransaction}; -pub type MantleMsgId = [u8; 32]; pub type BlockHash = HashType; #[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] pub struct BlockMeta { pub id: BlockId, pub hash: BlockHash, - pub msg_id: MantleMsgId, } #[derive(Debug, Clone)] @@ -55,7 +53,6 @@ pub struct Block { pub header: BlockHeader, pub body: BlockBody, pub bedrock_status: BedrockStatus, - pub bedrock_parent_id: MantleMsgId, } impl Serialize for Block { @@ -80,11 +77,7 @@ pub struct HashableBlockData { impl HashableBlockData { #[must_use] - pub fn into_pending_block( - self, - signing_key: &lee::PrivateKey, - bedrock_parent_id: MantleMsgId, - ) -> Block { + pub fn into_pending_block(self, signing_key: &lee::PrivateKey) -> Block { const PREFIX: &[u8; 32] = b"/LEE/v0.3/Message/Block/\x00\x00\x00\x00\x00\x00\x00\x00"; let data_bytes = borsh::to_vec(&self).unwrap(); @@ -111,7 +104,6 @@ impl HashableBlockData { transactions: self.transactions, }, bedrock_status: BedrockStatus::Pending, - bedrock_parent_id, } } } diff --git a/lez/common/src/config.rs b/lez/common/src/config.rs index c076f699..ec7ed6b3 100644 --- a/lez/common/src/config.rs +++ b/lez/common/src/config.rs @@ -53,3 +53,33 @@ impl From for BasicAuthCredentials { Self::new(value.username, value.password) } } + +#[cfg(test)] +mod tests { + use std::str::FromStr as _; + + use super::BasicAuth; + + #[test] + fn parse_preserves_non_empty_password() { + let auth = BasicAuth::from_str("user:secret").expect("must parse"); + assert_eq!(auth.username, "user"); + assert_eq!(auth.password.as_deref(), Some("secret")); + } + + #[test] + fn parse_empty_password_is_none() { + // A trailing colon means an empty password, which must become `None`. + // Catches deletion of `!` in `.filter(|p| !p.is_empty())`, which would + // instead yield `Some("")`. + let auth = BasicAuth::from_str("user:").expect("must parse"); + assert_eq!(auth.password, None); + } + + #[test] + fn parse_username_only_has_no_password() { + let auth = BasicAuth::from_str("alice").expect("must parse"); + assert_eq!(auth.username, "alice"); + assert_eq!(auth.password, None); + } +} diff --git a/lez/common/src/lib.rs b/lez/common/src/lib.rs index a7744d63..cfbbbd9b 100644 --- a/lez/common/src/lib.rs +++ b/lez/common/src/lib.rs @@ -93,4 +93,16 @@ mod tests { let deserialized = HashType::from_str(&serialized).unwrap(); assert_eq!(original, deserialized); } + + #[test] + fn as_ref_returns_exact_inner_bytes() { + // `HashType::as_ref` must return exactly the inner `[u8; 32]` โ€” not an + // empty slice or a placeholder. Catches mutations of `as_ref` that return + // `Vec::leak(Vec::new())`, `vec![0]`, or `vec![1]`. + let known = [0x42_u8; 32]; + let hash = HashType(known); + assert_eq!(hash.as_ref(), &known); + assert_eq!(hash.as_ref().len(), 32); + assert_eq!(HashType([0_u8; 32]).as_ref().len(), 32); + } } diff --git a/lez/common/src/test_utils.rs b/lez/common/src/test_utils.rs index f4e185ea..179e9601 100644 --- a/lez/common/src/test_utils.rs +++ b/lez/common/src/test_utils.rs @@ -39,7 +39,7 @@ pub fn produce_dummy_block( transactions, }; - block_data.into_pending_block(&sequencer_sign_key_for_testing(), [0; 32]) + block_data.into_pending_block(&sequencer_sign_key_for_testing()) } #[must_use] diff --git a/lez/common/src/transaction.rs b/lez/common/src/transaction.rs index 42a7b761..7fb32e39 100644 --- a/lez/common/src/transaction.rs +++ b/lez/common/src/transaction.rs @@ -78,7 +78,31 @@ impl LeeTransaction { block_id: BlockId, timestamp: Timestamp, ) -> Result { - let diff = match self { + let diff = self.compute_state_diff(state, block_id, timestamp)?; + + let restricted_modification_accounts = lee::CLOCK_PROGRAM_ACCOUNT_IDS + .iter() + .copied() + .chain(std::iter::once(lee::system_faucet_account_id())); + for account_id in restricted_modification_accounts { + validate_doesnt_modify_account(state, &diff, account_id)?; + } + + self.validate_bridge_account_modification(state, &diff)?; + + Ok(diff) + } + + /// Computes the validated state diff without enforcing the system-account + /// restriction. Shared by [`Self::validate_on_state`] and + /// [`Self::execute_without_system_accounts_check_on_state`]. + fn compute_state_diff( + &self, + state: &V03State, + block_id: BlockId, + timestamp: Timestamp, + ) -> Result { + match self { Self::Public(tx) => { ValidatedStateDiff::from_public_transaction(tx, state, block_id, timestamp) } @@ -88,17 +112,7 @@ impl LeeTransaction { Self::ProgramDeployment(tx) => { ValidatedStateDiff::from_program_deployment_transaction(tx, state) } - }?; - - let system_accounts = lee::CLOCK_PROGRAM_ACCOUNT_IDS.iter().copied().chain([ - lee::system_faucet_account_id(), - lee::system_bridge_account_id(), - ]); - for account_id in system_accounts { - validate_doesnt_modify_account(state, &diff, account_id)?; } - - Ok(diff) } /// Validates the transaction against the current state, rejects modifications to clock @@ -115,6 +129,62 @@ impl LeeTransaction { state.apply_state_diff(diff); Ok(self) } + + /// Similar to [`Self::execute_check_on_state`], but skips the system-account guard. + /// + /// FIXME: HOT FIX (testnet v0.2): the indexer replays blocks the sequencer already + /// accepted, including sequencer-generated deposit transactions that + /// legitimately modify the bridge account. The `TransactionOrigin::Sequencer` + /// tag that lets the sequencer bypass the guard is not carried in the block, + /// so the indexer cannot yet distinguish deposit txs from user txs. + /// + /// REMOVE ME when the indexer can authenticate deposit transactions. + pub fn execute_without_system_accounts_check_on_state( + self, + state: &mut V03State, + block_id: BlockId, + timestamp: Timestamp, + ) -> Result { + let diff = self + .compute_state_diff(state, block_id, timestamp) + .inspect_err(|err| warn!("Error at transition {err:#?}"))?; + state.apply_state_diff(diff); + Ok(self) + } + + fn validate_bridge_account_modification( + &self, + state: &V03State, + diff: &ValidatedStateDiff, + ) -> Result<(), lee::error::LeeError> { + let bridge_account_id = lee::system_bridge_account_id(); + let pre = state.get_account_by_id(bridge_account_id); + let Some(post) = diff.public_diff().get(&bridge_account_id).cloned() else { + return Ok(()); + }; + + let Self::Public(_) = self else { + return Err(lee::error::LeeError::InvalidInput(format!( + "Non-public transaction cannot modify system bridge account {bridge_account_id}" + ))); + }; + + let only_balance_increased = { + let expected_pre = lee::Account { + balance: pre.balance, + ..post.clone() + }; + (expected_pre == pre) && (pre.balance <= post.balance) + }; + + if only_balance_increased { + Ok(()) + } else { + Err(lee::error::LeeError::InvalidInput(format!( + "Transaction modifies restricted system bridge account {bridge_account_id}" + ))) + } + } } impl From for LeeTransaction { @@ -188,3 +258,47 @@ fn validate_doesnt_modify_account( Ok(()) } } + +#[cfg(test)] +mod tests { + use lee::{ + AccountId, CLOCK_01_PROGRAM_ACCOUNT_ID, PrivateKey, PublicKey, V03State, + system_bridge_account_id, system_faucet_account_id, + }; + + use crate::test_utils::create_transaction_native_token_transfer; + + #[test] + fn system_account_ids_are_distinct_and_non_default() { + let faucet = system_faucet_account_id(); + let bridge = system_bridge_account_id(); + assert_ne!(faucet, AccountId::default()); + assert_ne!(bridge, AccountId::default()); + assert_ne!(faucet, bridge); + } + + #[test] + fn validate_on_state_rejects_modifying_a_system_account() { + // A native transfer that credits a clock system account *changes* that + // account, so `validate_doesnt_modify_account` must reject it. Catches + // the `!=` โ†’ `==` inversion at `validate_doesnt_modify_account` (a changed + // account would no longer be flagged) and `public_diff โ†’ HashMap::new()` + // (an empty diff hides the modification). + let sender_key = PrivateKey::try_new([5_u8; 32]).expect("valid key"); + let sender_id = AccountId::from(&PublicKey::new_from_private_key(&sender_key)); + let state = V03State::new_with_genesis_accounts(&[(sender_id, 10_000)], vec![], 0); + + let tx = create_transaction_native_token_transfer( + sender_id, + 0, + CLOCK_01_PROGRAM_ACCOUNT_ID, + 100, + &sender_key, + ); + + assert!( + tx.validate_on_state(&state, 1, 0).is_err(), + "validate_on_state must reject a transfer that credits a clock system account", + ); + } +} diff --git a/lez/explorer_service/src/components/block_preview.rs b/lez/explorer_service/src/components/block_preview.rs index 8fe48f9f..0d3b9e14 100644 --- a/lez/explorer_service/src/components/block_preview.rs +++ b/lez/explorer_service/src/components/block_preview.rs @@ -27,7 +27,6 @@ pub fn BlockPreview(block: Block) -> impl IntoView { }, body: BlockBody { transactions }, bedrock_status, - bedrock_parent_id: _, } = block; let tx_count = transactions.len(); diff --git a/lez/explorer_service/src/pages/block_page.rs b/lez/explorer_service/src/pages/block_page.rs index 8f54fe18..5df3b4dc 100644 --- a/lez/explorer_service/src/pages/block_page.rs +++ b/lez/explorer_service/src/pages/block_page.rs @@ -64,7 +64,6 @@ pub fn BlockPage() -> impl IntoView { transactions, }, bedrock_status, - bedrock_parent_id: _, } = blk; let hash_str = hash.to_string(); diff --git a/lez/indexer/core/src/block_store.rs b/lez/indexer/core/src/block_store.rs index d293637c..f00c94c5 100644 --- a/lez/indexer/core/src/block_store.rs +++ b/lez/indexer/core/src/block_store.rs @@ -171,7 +171,10 @@ impl IndexerStore { transaction .clone() .transaction_stateless_check()? - .execute_check_on_state( + // FIXME: HOT FIX (testnet v0.2): does not check for system account updates due to + // sequencer-generated deposit tx'es; + // CHANGE ME back to `execute_check_on_state` when the indexer can authenticate deposit transactions + .execute_without_system_accounts_check_on_state( &mut state_guard, block.header.block_id, block.header.timestamp, @@ -238,10 +241,8 @@ mod tests { timestamp: 0, transactions: vec![clock_tx], }; - let genesis_block = genesis_block_data.into_pending_block( - &common::test_utils::sequencer_sign_key_for_testing(), - [0; 32], - ); + let genesis_block = genesis_block_data + .into_pending_block(&common::test_utils::sequencer_sign_key_for_testing()); let mut prev_hash = Some(genesis_block.header.hash); storage .put_block(genesis_block, HeaderId::from([0_u8; 32])) diff --git a/lez/indexer/ffi/indexer_ffi.h b/lez/indexer/ffi/indexer_ffi.h index 1562eb15..84aaeae7 100644 --- a/lez/indexer/ffi/indexer_ffi.h +++ b/lez/indexer/ffi/indexer_ffi.h @@ -320,13 +320,10 @@ typedef struct FfiVec_FfiTransaction { typedef struct FfiVec_FfiTransaction FfiBlockBody; -typedef struct FfiBytes32 FfiMsgId; - typedef struct FfiBlock { struct FfiBlockHeader header; FfiBlockBody body; enum FfiBedrockStatus bedrock_status; - FfiMsgId bedrock_parent_id; } FfiBlock; typedef struct FfiOption_FfiBlock { diff --git a/lez/indexer/ffi/src/api/types/block.rs b/lez/indexer/ffi/src/api/types/block.rs index bca2fdb5..e7ae0760 100644 --- a/lez/indexer/ffi/src/api/types/block.rs +++ b/lez/indexer/ffi/src/api/types/block.rs @@ -1,9 +1,7 @@ -use indexer_service_protocol::{ - BedrockStatus, Block, BlockHeader, HashType, MantleMsgId, Signature, -}; +use indexer_service_protocol::{BedrockStatus, Block, BlockHeader, HashType, Signature}; use crate::api::types::{ - FfiBlockId, FfiHashType, FfiMsgId, FfiOption, FfiSignature, FfiTimestamp, FfiVec, + FfiBlockId, FfiHashType, FfiOption, FfiSignature, FfiTimestamp, FfiVec, transaction::free_ffi_transaction_vec, vectors::FfiBlockBody, }; @@ -12,7 +10,6 @@ pub struct FfiBlock { pub header: FfiBlockHeader, pub body: FfiBlockBody, pub bedrock_status: FfiBedrockStatus, - pub bedrock_parent_id: FfiMsgId, } impl From for FfiBlock { @@ -21,7 +18,6 @@ impl From for FfiBlock { header, body, bedrock_status, - bedrock_parent_id, } = value; Self { @@ -33,7 +29,6 @@ impl From for FfiBlock { .collect::>() .into(), bedrock_status: bedrock_status.into(), - bedrock_parent_id: bedrock_parent_id.into(), } } } @@ -126,8 +121,6 @@ pub unsafe extern "C" fn free_ffi_block(val: FfiBlock) { #[expect(clippy::let_underscore_must_use, reason = "No use for this Copy type")] let _: BedrockStatus = val.bedrock_status.into(); - let _ = MantleMsgId(val.bedrock_parent_id.data); - unsafe { free_ffi_transaction_vec(ffi_tx_ffi_vec); }; @@ -166,8 +159,6 @@ pub unsafe extern "C" fn free_ffi_block_opt(val: FfiBlockOpt) { #[expect(clippy::let_underscore_must_use, reason = "No use for this Copy type")] let _: BedrockStatus = value.bedrock_status.into(); - let _ = MantleMsgId(value.bedrock_parent_id.data); - unsafe { free_ffi_transaction_vec(ffi_tx_ffi_vec); }; diff --git a/lez/indexer/ffi/src/api/types/mod.rs b/lez/indexer/ffi/src/api/types/mod.rs index b42b014c..0b3574e6 100644 --- a/lez/indexer/ffi/src/api/types/mod.rs +++ b/lez/indexer/ffi/src/api/types/mod.rs @@ -1,4 +1,4 @@ -use indexer_service_protocol::{AccountId, HashType, MantleMsgId, ProgramId, PublicKey, Signature}; +use indexer_service_protocol::{AccountId, HashType, ProgramId, PublicKey, Signature}; pub mod account; pub mod block; @@ -68,7 +68,6 @@ impl From for u128 { } pub type FfiHashType = FfiBytes32; -pub type FfiMsgId = FfiBytes32; pub type FfiBlockId = u64; pub type FfiTimestamp = u64; pub type FfiSignature = FfiBytes64; @@ -82,12 +81,6 @@ impl From for FfiHashType { } } -impl From for FfiMsgId { - fn from(value: MantleMsgId) -> Self { - Self { data: value.0 } - } -} - impl From for FfiSignature { fn from(value: Signature) -> Self { Self { data: value.0 } diff --git a/lez/indexer/service/Dockerfile b/lez/indexer/service/Dockerfile index cc7087bb..3da1eb7f 100644 --- a/lez/indexer/service/Dockerfile +++ b/lez/indexer/service/Dockerfile @@ -37,9 +37,6 @@ RUN cp "$(which r0vm)" /usr/local/bin/r0vm RUN test -x /usr/local/bin/r0vm RUN r0vm --version -# Install logos blockchain circuits -RUN curl -sSL https://raw.githubusercontent.com/logos-blockchain/logos-blockchain/main/scripts/setup-logos-blockchain-circuits.sh | bash - WORKDIR /indexer_service # Planner stage - generates dependency recipe @@ -83,9 +80,6 @@ COPY --from=builder --chown=indexer_service_user:indexer_service_user /usr/local # Copy r0vm binary from builder COPY --from=builder --chown=indexer_service_user:indexer_service_user /usr/local/bin/r0vm /usr/local/bin/r0vm -# Copy logos blockchain circuits from builder -COPY --from=builder --chown=indexer_service_user:indexer_service_user /root/.logos-blockchain-circuits /home/indexer_service_user/.logos-blockchain-circuits - VOLUME /var/lib/indexer_service # Expose default port diff --git a/lez/indexer/service/protocol/src/convert.rs b/lez/indexer/service/protocol/src/convert.rs index cec8b7bb..cd0bff7e 100644 --- a/lez/indexer/service/protocol/src/convert.rs +++ b/lez/indexer/service/protocol/src/convert.rs @@ -4,8 +4,8 @@ use lee_core::account::Nonce; use crate::{ Account, AccountId, BedrockStatus, Block, BlockBody, BlockHeader, Ciphertext, Commitment, - CommitmentSetDigest, Data, EncryptedAccountData, EphemeralPublicKey, HashType, MantleMsgId, - Nullifier, PrivacyPreservingMessage, PrivacyPreservingTransaction, ProgramDeploymentMessage, + CommitmentSetDigest, Data, EncryptedAccountData, EphemeralPublicKey, HashType, Nullifier, + PrivacyPreservingMessage, PrivacyPreservingTransaction, ProgramDeploymentMessage, ProgramDeploymentTransaction, ProgramId, Proof, PublicKey, PublicMessage, PublicTransaction, Signature, Transaction, ValidityWindow, WitnessSet, }; @@ -630,14 +630,12 @@ impl From for Block { header, body, bedrock_status, - bedrock_parent_id, } = value; Self { header: header.into(), body: body.into(), bedrock_status: bedrock_status.into(), - bedrock_parent_id: MantleMsgId(bedrock_parent_id), } } } @@ -650,14 +648,12 @@ impl TryFrom for common::block::Block { header, body, bedrock_status, - bedrock_parent_id, } = value; Ok(Self { header: header.try_into()?, body: body.try_into()?, bedrock_status: bedrock_status.into(), - bedrock_parent_id: bedrock_parent_id.0, }) } } diff --git a/lez/indexer/service/protocol/src/lib.rs b/lez/indexer/service/protocol/src/lib.rs index 0d94167d..a670dee6 100644 --- a/lez/indexer/service/protocol/src/lib.rs +++ b/lez/indexer/service/protocol/src/lib.rs @@ -145,7 +145,6 @@ pub struct Block { pub header: BlockHeader, pub body: BlockBody, pub bedrock_status: BedrockStatus, - pub bedrock_parent_id: MantleMsgId, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] @@ -358,13 +357,6 @@ impl FromStr for HashType { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub struct MantleMsgId( - #[serde(with = "base64::arr")] - #[schemars(with = "String", description = "base64-encoded Bedrock message id")] - pub [u8; 32], -); - #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] pub enum BedrockStatus { Pending, diff --git a/lez/indexer/service/src/mock_service.rs b/lez/indexer/service/src/mock_service.rs index a83e9ccc..d9ab9484 100644 --- a/lez/indexer/service/src/mock_service.rs +++ b/lez/indexer/service/src/mock_service.rs @@ -10,10 +10,10 @@ use std::{collections::HashMap, sync::Arc, time::Duration}; use indexer_service_protocol::{ Account, AccountId, BedrockStatus, Block, BlockBody, BlockHeader, BlockId, Commitment, - CommitmentSetDigest, Data, EncryptedAccountData, HashType, MantleMsgId, - PrivacyPreservingMessage, PrivacyPreservingTransaction, ProgramDeploymentMessage, - ProgramDeploymentTransaction, ProgramId, PublicMessage, PublicTransaction, Signature, - Transaction, ValidityWindow, WitnessSet, + CommitmentSetDigest, Data, EncryptedAccountData, HashType, PrivacyPreservingMessage, + PrivacyPreservingTransaction, ProgramDeploymentMessage, ProgramDeploymentTransaction, + ProgramId, PublicMessage, PublicTransaction, Signature, Transaction, ValidityWindow, + WitnessSet, }; use jsonrpsee::{ core::{SubscriptionResult, async_trait}, @@ -432,7 +432,6 @@ fn build_mock_block( transactions: block_transactions, }, bedrock_status, - bedrock_parent_id: MantleMsgId([0; 32]), } } diff --git a/lez/keycard_wallet/keycard_applets/LEE_keycard.cap b/lez/keycard_wallet/keycard_applets/LEE_keycard.cap index b44835c4..b2e71d56 100644 Binary files a/lez/keycard_wallet/keycard_applets/LEE_keycard.cap and b/lez/keycard_wallet/keycard_applets/LEE_keycard.cap differ diff --git a/lez/keycard_wallet/src/lib.rs b/lez/keycard_wallet/src/lib.rs index 1f009900..73486392 100644 --- a/lez/keycard_wallet/src/lib.rs +++ b/lez/keycard_wallet/src/lib.rs @@ -7,8 +7,9 @@ use zeroize::Zeroizing; pub mod python_path; -/// NSK and VSK as fixed-length zeroizing byte arrays. -type PrivateKeyPair = (Zeroizing<[u8; 32]>, Zeroizing<[u8; 32]>); +/// NSK (32 bytes) and VSK (64 bytes, the ML-KEM-768 seed `d || z`) as fixed-length zeroizing byte +/// arrays. +type PrivateKeyPair = (Zeroizing<[u8; 32]>, Zeroizing<[u8; 64]>); // TODO: encrypt at rest alongside broader wallet storage encryption work. #[derive(Serialize, Deserialize)] @@ -123,7 +124,7 @@ impl KeycardWallet { } pub fn get_public_key_for_path_with_connect(pin: &str, path: &str) -> PyResult { - Python::with_gil(|py| { + Python::attach(|py| { python_path::add_python_path(py)?; let wallet = Self::new(py)?; wallet.connect(py, pin)?; @@ -190,7 +191,7 @@ impl KeycardWallet { path: &str, message: &[u8; 32], ) -> PyResult<(Signature, PublicKey)> { - Python::with_gil(|py| { + Python::attach(|py| { python_path::add_python_path(py)?; let wallet = Self::new(py)?; wallet.connect(py, pin)?; @@ -239,13 +240,13 @@ impl KeycardWallet { }; let vsk = { - if raw_vsk.len() != 32 { + if raw_vsk.len() != 64 { return Err(PyErr::new::(format!( - "expected 32-byte VSK from keycard, got {} bytes", + "expected 64-byte VSK from keycard, got {} bytes", raw_vsk.len() ))); } - let mut arr = Zeroizing::new([0_u8; 32]); + let mut arr = Zeroizing::new([0_u8; 64]); arr.copy_from_slice(&raw_vsk); arr }; @@ -257,7 +258,7 @@ impl KeycardWallet { pin: &str, path: &str, ) -> PyResult { - Python::with_gil(|py| { + Python::attach(|py| { python_path::add_python_path(py)?; let wallet = Self::new(py)?; wallet.connect(py, pin)?; diff --git a/lez/keycard_wallet/src/python_path.rs b/lez/keycard_wallet/src/python_path.rs index cee051ca..99ed936e 100644 --- a/lez/keycard_wallet/src/python_path.rs +++ b/lez/keycard_wallet/src/python_path.rs @@ -48,7 +48,7 @@ pub fn add_python_path(py: Python<'_>) -> PyResult<()> { let sys = PyModule::import(py, "sys")?; let binding = sys.getattr("path")?; - let sys_path = binding.downcast::()?; + let sys_path = binding.cast::()?; for path in &paths_to_add { let path_str = path.to_str().expect("Invalid path"); diff --git a/lez/sequencer/core/Cargo.toml b/lez/sequencer/core/Cargo.toml index f7296f42..9201d8b3 100644 --- a/lez/sequencer/core/Cargo.toml +++ b/lez/sequencer/core/Cargo.toml @@ -19,6 +19,8 @@ faucet_core.workspace = true bridge_core.workspace = true vault_core.workspace = true +logos-blockchain-key-management-system-service.workspace = true +logos-blockchain-core.workspace = true anyhow.workspace = true serde.workspace = true serde_json.workspace = true @@ -27,14 +29,14 @@ tempfile.workspace = true chrono.workspace = true log.workspace = true tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } -logos-blockchain-key-management-system-service.workspace = true -logos-blockchain-core.workspace = true rand.workspace = true borsh.workspace = true bytesize.workspace = true hex.workspace = true url.workspace = true +num-bigint.workspace = true risc0-zkvm.workspace = true +futures.workspace = true [features] default = [] @@ -46,3 +48,4 @@ mock = [] futures.workspace = true test_program_methods.workspace = true lee = { workspace = true, features = ["test-utils"] } +key_protocol.workspace = true diff --git a/lez/sequencer/core/src/block_publisher.rs b/lez/sequencer/core/src/block_publisher.rs index f07a47c6..ab2cbf86 100644 --- a/lez/sequencer/core/src/block_publisher.rs +++ b/lez/sequencer/core/src/block_publisher.rs @@ -1,21 +1,29 @@ use std::{pin::Pin, sync::Arc, time::Duration}; -use anyhow::{Context as _, Result}; +use anyhow::{Context as _, Result, anyhow}; use common::block::Block; -use log::warn; +use log::{info, warn}; pub use logos_blockchain_core::mantle::ops::channel::MsgId; -pub use logos_blockchain_key_management_system_service::keys::Ed25519Key; +use logos_blockchain_core::mantle::ops::channel::inscribe::Inscription; +pub use logos_blockchain_key_management_system_service::keys::{Ed25519Key, ZkKey}; pub use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint; use logos_blockchain_zone_sdk::{ CommonHttpClient, adapter::NodeHttpClient, - sequencer::{Event, SequencerConfig as ZoneSdkSequencerConfig, SequencerHandle, ZoneSequencer}, - state::{DepositInfo, FinalizedOp, InscriptionInfo}, + sequencer::{ + DepositInfo, Event, FinalizedOp, InscriptionInfo, + SequencerConfig as ZoneSdkSequencerConfig, WithdrawArg, WithdrawInfo, ZoneSequencer, + }, }; -use tokio::task::JoinHandle; +use tokio::{sync::mpsc, task::JoinHandle}; use crate::config::BedrockConfig; +/// Channel capacity for the publish inbox. One publish per produced block, drained +/// in microseconds by the drive task โ€” 32 is huge headroom and just provides +/// backpressure if the drive task stalls (reconnect, long backfill). +const PUBLISH_INBOX_CAPACITY: usize = 32; + /// Sink for `Event::Published` checkpoints emitted by the drive task. /// Caller is responsible for persistence (e.g. writing to rocksdb). pub type CheckpointSink = Box; @@ -29,8 +37,16 @@ pub type FinalizedBlockSink = Box; pub type OnDepositEventSink = Box Pin + Send>> + Send + 'static>; +/// Sink for finalized Bedrock withdraw events. +pub type OnWithdrawEventSink = + Box Pin + Send>> + Send + 'static>; + #[expect(async_fn_in_trait, reason = "We don't care about Send/Sync here")] pub trait BlockPublisherTrait: Clone { + #[expect( + clippy::too_many_arguments, + reason = "Looks better than bundling all those callbacks into a struct" + )] async fn new( config: &BedrockConfig, bedrock_signing_key: Ed25519Key, @@ -39,17 +55,18 @@ pub trait BlockPublisherTrait: Clone { on_checkpoint: CheckpointSink, on_finalized_block: FinalizedBlockSink, on_deposit_event: OnDepositEventSink, + on_withdraw_event: OnWithdrawEventSink, ) -> Result; /// Fire-and-forget publish. Zone-sdk drives the actual submission and /// retries internally; this just hands the payload off. - async fn publish_block(&self, block: &Block) -> Result<()>; + async fn publish_block(&self, block: &Block, withdrawals: Vec) -> Result<()>; } /// Real block publisher backed by zone-sdk's `ZoneSequencer`. #[derive(Clone)] pub struct ZoneSdkPublisher { - handle: SequencerHandle, + publish_tx: mpsc::Sender<(Inscription, Vec)>, // Aborts the drive task when the last clone is dropped. _drive_task: Arc, } @@ -71,6 +88,7 @@ impl BlockPublisherTrait for ZoneSdkPublisher { on_checkpoint: CheckpointSink, on_finalized_block: FinalizedBlockSink, on_deposit_event: OnDepositEventSink, + on_withdraw_event: OnWithdrawEventSink, ) -> Result { let basic_auth = config.auth.clone().map(Into::into); let node = NodeHttpClient::new(CommonHttpClient::new(basic_auth), config.node_url.clone()); @@ -80,7 +98,7 @@ impl BlockPublisherTrait for ZoneSdkPublisher { ..ZoneSdkSequencerConfig::default() }; - let (mut sequencer, mut handle) = ZoneSequencer::init_with_config( + let mut sequencer = ZoneSequencer::init_with_config( config.channel_id, bedrock_signing_key, node, @@ -88,55 +106,107 @@ impl BlockPublisherTrait for ZoneSdkPublisher { initial_checkpoint, ); + // Grab readiness receiver before moving the sequencer into the drive + // task so we can await cold-start completion below. + let mut ready_rx = sequencer.subscribe_ready(); + + let (publish_tx, mut publish_rx) = + mpsc::channel::<(Inscription, Vec)>(PUBLISH_INBOX_CAPACITY); + let drive_task = tokio::spawn(async move { loop { - let Some(event) = sequencer.next_event().await else { - continue; - }; - match event { - Event::Checkpoint { checkpoint } => on_checkpoint(checkpoint), - 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); + #[expect( + clippy::integer_division_remainder_used, + reason = "tokio::select! expansion uses `%` for random branch selection" + )] + { + tokio::select! { + // Drain external publish requests by calling the + // borrowing handle โ€” `&mut sequencer` is only + // available here. + Some((data_bounded, withdrawals)) = publish_rx.recv() => { + let data_byte_size = data_bounded.len(); + if withdrawals.is_empty() { + if let Err(e) = sequencer.handle() + .publish(data_bounded) + .context("Failed to publish block") { + warn!("zone-sdk publish failed: {e:?}"); + } + + info!("Published block with the size of {data_byte_size} bytes"); + } else { + let withdraw_count = withdrawals.len(); + if let Err(e) = sequencer.handle() + .publish_atomic_withdraw(data_bounded, withdrawals) + .context("Failed to publish block with withdrawals") { + warn!("zone-sdk publish failed: {e:?}"); + } + + info!( + "Published block with the size of {data_byte_size} bytes and {withdraw_count} bridge withdrawals", + ); + } + } + event = sequencer.next_event() => { + let Some(event) = event else { + continue; + }; + match event { + Event::BlocksProcessed { + checkpoint, + channel_update: _, + finalized, + } => { + on_checkpoint(checkpoint); + for op in finalized.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(withdraw) => { + on_withdraw_event(withdraw).await; + } + } } } - FinalizedOp::Deposit(deposit) => { - on_deposit_event(deposit).await; - } - FinalizedOp::Withdraw(_) => {} + Event::Ready | Event::TurnNotification { .. } => {} } } } - Event::ChannelUpdate { .. } - | Event::Published { .. } - | Event::Readiness { .. } - | Event::TurnNotification { .. } => {} } } }); - handle.wait_ready().await; + // Wait for cold-start backfill to complete before returning so callers + // can publish immediately (e.g. genesis block) without racing readiness. + ready_rx + .wait_for(|v| *v) + .await + .context("Zone-sdk readiness channel closed before becoming ready")?; Ok(Self { - handle, + publish_tx, _drive_task: Arc::new(DriveTaskGuard(drive_task)), }) } - async fn publish_block(&self, block: &Block) -> Result<()> { + async fn publish_block(&self, block: &Block, withdrawals: Vec) -> Result<()> { let data = borsh::to_vec(block).context("Failed to serialize block")?; - let data_bounded = data + let data_bounded: Inscription = data .try_into() .context("Block data exceeds maximum allowed size")?; - self.handle - .publish_message(data_bounded) + self.publish_tx + .send((data_bounded, withdrawals)) .await - .context("Failed to publish block")?; + .map_err(|_closed| anyhow!("Drive task is no longer running"))?; Ok(()) } diff --git a/lez/sequencer/core/src/block_store.rs b/lez/sequencer/core/src/block_store.rs index 97a23848..4e5489ff 100644 --- a/lez/sequencer/core/src/block_store.rs +++ b/lez/sequencer/core/src/block_store.rs @@ -3,14 +3,17 @@ use std::{collections::HashMap, path::Path, sync::Arc}; use anyhow::{Context as _, Result}; use common::{ HashType, - block::{Block, BlockMeta, MantleMsgId}, + block::{Block, BlockMeta}, transaction::LeeTransaction, }; use lee::V03State; use log::info; use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint; pub use storage::DbResult; -use storage::sequencer::{RocksDBIO, sequencer_cells::PendingDepositEventRecord}; +use storage::sequencer::{ + RocksDBIO, + sequencer_cells::{PendingDepositEventRecord, WithdrawalReconciliationKey}, +}; pub struct SequencerStore { dbio: Arc, @@ -56,16 +59,10 @@ impl SequencerStore { pub fn create_db_with_genesis( location: &Path, genesis_block: &Block, - genesis_msg_id: MantleMsgId, genesis_state: &V03State, signing_key: lee::PrivateKey, ) -> DbResult { - let dbio = Arc::new(RocksDBIO::create( - location, - genesis_block, - genesis_msg_id, - genesis_state, - )?); + let dbio = Arc::new(RocksDBIO::create(location, genesis_block, genesis_state)?); let genesis_id = dbio.get_meta_first_block_in_db()?; let tx_hash_to_block_map = block_to_transactions_map(genesis_block); @@ -137,11 +134,13 @@ impl SequencerStore { pub(crate) fn update( &mut self, block: &Block, - msg_id: MantleMsgId, + deposit_event_ids: &[HashType], + withdrawals: Vec, state: &V03State, ) -> DbResult<()> { let new_transactions_map = block_to_transactions_map(block); - self.dbio.atomic_update(block, msg_id, state)?; + self.dbio + .atomic_update(block, deposit_event_ids, withdrawals, state)?; self.tx_hash_to_block_map.extend(new_transactions_map); Ok(()) } @@ -169,23 +168,6 @@ impl SequencerStore { pub fn get_unfulfilled_deposit_events(&self) -> DbResult> { self.dbio.get_pending_deposit_events() } - - pub fn mark_unfulfilled_deposit_events_submitted( - &self, - deposit_op_ids: &[HashType], - submitted_block_id: u64, - ) -> DbResult { - self.dbio - .mark_pending_deposit_events_submitted(deposit_op_ids, submitted_block_id) - } - - pub fn remove_fulfilled_unfulfilled_deposit_events_up_to_block( - &self, - finalized_block_id: u64, - ) -> DbResult { - self.dbio - .remove_fulfilled_pending_deposit_events_up_to_block(finalized_block_id) - } } pub(crate) fn block_to_transactions_map(block: &Block) -> HashMap { @@ -220,12 +202,11 @@ mod tests { transactions: vec![], }; - let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]); + let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key); // Start an empty node store let mut node_store = SequencerStore::create_db_with_genesis( path, &genesis_block, - [0; 32], &testnet_initial_state::initial_state(), signing_key, ) @@ -239,7 +220,9 @@ mod tests { assert_eq!(None, retrieved_tx); // Add the block with the transaction let dummy_state = V03State::new_with_genesis_accounts(&[], vec![], 0); - node_store.update(&block, [1; 32], &dummy_state).unwrap(); + node_store + .update(&block, &[], vec![], &dummy_state) + .unwrap(); // Try again let retrieved_tx = node_store.get_transaction_by_hash(tx.hash()); assert_eq!(Some(tx), retrieved_tx); @@ -259,13 +242,12 @@ mod tests { transactions: vec![], }; - let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]); + let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key); let genesis_hash = genesis_block.header.hash; let node_store = SequencerStore::create_db_with_genesis( path, &genesis_block, - [0; 32], &testnet_initial_state::initial_state(), signing_key, ) @@ -274,7 +256,6 @@ mod tests { // Verify that initially the latest block hash equals genesis hash let latest_meta = node_store.latest_block_meta().unwrap(); assert_eq!(latest_meta.hash, genesis_hash); - assert_eq!(latest_meta.msg_id, [0; 32]); } #[test] @@ -291,11 +272,10 @@ mod tests { transactions: vec![], }; - let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]); + let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key); let mut node_store = SequencerStore::create_db_with_genesis( path, &genesis_block, - [0; 32], &testnet_initial_state::initial_state(), signing_key, ) @@ -305,17 +285,15 @@ mod tests { let tx = common::test_utils::produce_dummy_empty_transaction(); let block = common::test_utils::produce_dummy_block(1, None, vec![tx]); let block_hash = block.header.hash; - let block_msg_id = [1; 32]; let dummy_state = V03State::new_with_genesis_accounts(&[], vec![], 0); node_store - .update(&block, block_msg_id, &dummy_state) + .update(&block, &[], vec![], &dummy_state) .unwrap(); - // Verify that the latest block meta now equals the new block's hash and msg_id + // Verify that the latest block meta now equals the new block's hash let latest_meta = node_store.latest_block_meta().unwrap(); assert_eq!(latest_meta.hash, block_hash); - assert_eq!(latest_meta.msg_id, block_msg_id); } #[test] @@ -332,11 +310,10 @@ mod tests { transactions: vec![], }; - let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]); + let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key); let mut node_store = SequencerStore::create_db_with_genesis( path, &genesis_block, - [0; 32], &testnet_initial_state::initial_state(), signing_key, ) @@ -348,7 +325,9 @@ mod tests { let block_id = block.header.block_id; let dummy_state = V03State::new_with_genesis_accounts(&[], vec![], 0); - node_store.update(&block, [1; 32], &dummy_state).unwrap(); + node_store + .update(&block, &[], vec![], &dummy_state) + .unwrap(); // Verify initial status is Pending let retrieved_block = node_store.get_block_at_id(block_id).unwrap().unwrap(); @@ -382,14 +361,13 @@ mod tests { transactions: vec![], }; - let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]); + let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key); let tx = common::test_utils::produce_dummy_empty_transaction(); { // Create a scope to drop the first store after creating the db let mut node_store = SequencerStore::create_db_with_genesis( path, &genesis_block, - [0; 32], &testnet_initial_state::initial_state(), signing_key.clone(), ) @@ -400,7 +378,8 @@ mod tests { node_store .update( &block, - [1; 32], + &[], + vec![], &V03State::new_with_genesis_accounts(&[], vec![], 0), ) .unwrap(); diff --git a/lez/sequencer/core/src/lib.rs b/lez/sequencer/core/src/lib.rs index 0f836c00..651951d2 100644 --- a/lez/sequencer/core/src/lib.rs +++ b/lez/sequencer/core/src/lib.rs @@ -12,11 +12,16 @@ use lee::{AccountId, PublicTransaction, program::Program, public_transaction::Me use lee_core::GENESIS_BLOCK_ID; use log::{error, info, warn}; use logos_blockchain_key_management_system_service::keys::{ED25519_SECRET_KEY_SIZE, Ed25519Key}; +use logos_blockchain_zone_sdk::sequencer::{DepositInfo, WithdrawArg}; use mempool::{MemPool, MemPoolHandle}; #[cfg(feature = "mock")] pub use mock::SequencerCoreWithMockClients; +use num_bigint::BigUint; pub use storage::error::DbError; -use storage::sequencer::sequencer_cells::PendingDepositEventRecord; +use storage::sequencer::{ + RocksDBIO, + sequencer_cells::{PendingDepositEventRecord, WithdrawalReconciliationKey}, +}; use crate::{ block_publisher::{BlockPublisherTrait, ZoneSdkPublisher}, @@ -96,10 +101,6 @@ impl SequencerCore { db_path.display() ); - // TODO: Remove msg_id from BlockMeta โ€” it is no longer needed now that - // zone-sdk manages L1 settlement state via its own checkpoint. - let genesis_msg_id = [0; 32]; - let genesis_parent_msg_id = [0; 32]; let (genesis_state, genesis_txs) = build_genesis_state(&config); let hashable_data = HashableBlockData { @@ -108,13 +109,11 @@ impl SequencerCore { prev_block_hash: HashType([0; 32]), timestamp: 0, }; - let genesis_block = - hashable_data.into_pending_block(&signing_key, genesis_parent_msg_id); + let genesis_block = hashable_data.into_pending_block(&signing_key); let store = SequencerStore::create_db_with_genesis( &db_path, &genesis_block, - genesis_msg_id, &genesis_state, signing_key, ) @@ -132,8 +131,47 @@ impl SequencerCore { .expect("Failed to load zone-sdk checkpoint"); let is_fresh_start = initial_checkpoint.is_none(); - let dbio_for_checkpoint = store.dbio(); - let on_checkpoint: block_publisher::CheckpointSink = Box::new(move |cp| { + let (mempool, mempool_handle) = MemPool::new(config.mempool_max_size); + replay_unfulfilled_deposit_events(&store, mempool_handle.clone()); + + let block_publisher = BP::new( + &config.bedrock_config, + bedrock_signing_key, + config.retry_pending_blocks_timeout, + initial_checkpoint, + Self::on_checkpoint(store.dbio()), + Self::on_finalized_block(store.dbio()), + Self::on_deposit_event(store.dbio(), mempool_handle.clone()), + Self::on_withdraw_event(store.dbio()), + ) + .await + .expect("Failed to initialize Block Publisher"); + + // On a truly fresh start (no checkpoint persisted yet), publish the + // genesis block so the indexer can find the channel start. After the + // first publish, zone-sdk's checkpoint persistence covers further + // restarts. + if is_fresh_start { + block_publisher + .publish_block(&genesis_block, vec![]) + .await + .expect("Failed to publish genesis block"); + } + + let sequencer_core = Self { + state, + store, + mempool, + chain_height: latest_block_meta.id, + sequencer_config: config, + block_publisher, + }; + + (sequencer_core, mempool_handle) + } + + fn on_checkpoint(dbio: Arc) -> block_publisher::CheckpointSink { + Box::new(move |cp| { let bytes = match serde_json::to_vec(&cp) { Ok(b) => b, Err(err) => { @@ -141,24 +179,25 @@ impl SequencerCore { return; } }; - if let Err(err) = dbio_for_checkpoint.put_zone_sdk_checkpoint_bytes(&bytes) { + if let Err(err) = dbio.put_zone_sdk_checkpoint_bytes(&bytes) { error!("Failed to persist zone-sdk checkpoint: {err:#}"); } - }); + }) + } - let dbio_for_finalized = store.dbio(); - let on_finalized_block: block_publisher::FinalizedBlockSink = Box::new(move |block_id| { + fn on_finalized_block(dbio: Arc) -> block_publisher::FinalizedBlockSink { + Box::new(move |block_id| { // NOTE: Theoretically Zone SDK may report finalization happening multiple times for the // same block. In practice this is very unlikely to happen. For that to // happen Sequencer should crash between receiving Finalized and Checkpoint events while // these events happen very fast (because Checkpoints are generated by Zone SDK // locally). - if let Err(err) = dbio_for_finalized.clean_pending_blocks_up_to(block_id) { + if let Err(err) = dbio.clean_pending_blocks_up_to(block_id) { error!("Failed to mark pending blocks finalized up to {block_id}: {err:#}"); } - match dbio_for_finalized.remove_fulfilled_pending_deposit_events_up_to_block(block_id) { + match dbio.remove_fulfilled_pending_deposit_events_up_to_block(block_id) { Ok(0) => {} Ok(removed) => { info!( @@ -171,29 +210,29 @@ impl SequencerCore { ); } } - }); + }) + } - let (mempool, mempool_handle) = MemPool::new(config.mempool_max_size); - - replay_unfulfilled_deposit_events(&store, mempool_handle.clone()); - - let mempool_handle_for_deposit = mempool_handle.clone(); - let dbio_for_deposit = store.dbio(); - let on_deposit_event: block_publisher::OnDepositEventSink = Box::new(move |deposit| { + fn on_deposit_event( + dbio: Arc, + mempool_handle: MemPoolHandle<(TransactionOrigin, LeeTransaction)>, + ) -> block_publisher::OnDepositEventSink { + Box::new(move |deposit| { // NOTE: Theoretically Zone SDK may report multiple identical deposits. In practice this // is very unlikely to happen. For that to happen Sequencer should crash // between receiving Deposit and Checkpoint events while these events happen // very fast (because Checkpoints are generated by Zone SDK locally). - let dbio_for_deposit = Arc::clone(&dbio_for_deposit); - let mempool_handle_for_deposit = mempool_handle_for_deposit.clone(); + let dbio = Arc::clone(&dbio); + let mempool_handle = mempool_handle.clone(); + Box::pin(async move { let id_hex = hex::encode(deposit.op_id); info!("Observed Bedrock Deposit event with id: {id_hex}"); let event_record = pending_deposit_event_record(&deposit); - match dbio_for_deposit.add_pending_deposit_event(event_record.clone()) { + match dbio.add_pending_deposit_event(event_record.clone()) { Ok(true) => {} Ok(false) => { info!( @@ -219,7 +258,7 @@ impl SequencerCore { } }; - if let Err(err) = mempool_handle_for_deposit + if let Err(err) = mempool_handle .push((TransactionOrigin::Sequencer, tx)) .await { @@ -228,73 +267,66 @@ impl SequencerCore { ); } }) - }); + }) + } - let block_publisher = BP::new( - &config.bedrock_config, - bedrock_signing_key, - config.retry_pending_blocks_timeout, - initial_checkpoint, - on_checkpoint, - on_finalized_block, - on_deposit_event, - ) - .await - .expect("Failed to initialize Block Publisher"); + fn on_withdraw_event(dbio: Arc) -> block_publisher::OnWithdrawEventSink { + Box::new(move |withdraw| { + let dbio = Arc::clone(&dbio); + Box::pin(async move { + let hash_encoded = hex::encode(withdraw.tx_hash.as_ref()); + let withdraw_key = match withdraw_event_reconciliation_key(&withdraw.op.outputs) { + Ok(key) => key, + Err(err) => { + error!( + "Failed to build reconciliation key for Bedrock Withdraw event with tx_hash {hash_encoded}: {err:#}" + ); + return; + } + }; - // On a truly fresh start (no checkpoint persisted yet), publish the - // genesis block so the indexer can find the channel start. After the - // first publish, zone-sdk's checkpoint persistence covers further - // restarts. - if is_fresh_start { - block_publisher - .publish_block(&genesis_block) - .await - .expect("Failed to publish genesis block"); - } - - let sequencer_core = Self { - state, - store, - mempool, - chain_height: latest_block_meta.id, - sequencer_config: config, - block_publisher, - }; - - (sequencer_core, mempool_handle) + match dbio.consume_unseen_withdraw_count(withdraw_key) { + Ok(true) => { + info!("Validated Bedrock Withdraw event with tx_hash: {hash_encoded}"); + } + Ok(false) => warn!( + "Unexpected Bedrock Withdraw event with tx_hash {hash_encoded}: no matching unseen withdraw found" + ), + Err(err) => error!( + "Failed to reconcile Bedrock Withdraw event with tx_hash {hash_encoded}: {err:#}" + ), + } + }) + }) } /// Produces a new block from mempool transactions and publishes it via zone-sdk. pub async fn produce_new_block(&mut self) -> Result { - let block_with_meta = self - .build_block_from_mempool() - .context("Failed to build block from mempool transactions")?; let BlockWithMeta { block, deposit_event_ids, - } = block_with_meta; + withdrawals, + } = self + .build_block_from_mempool() + .context("Failed to build block from mempool transactions")?; - // TODO: Remove msg_id from store.update โ€” it is no longer needed now that - // zone-sdk manages L1 settlement state via its own checkpoint. - let placeholder_msg_id = [0_u8; 32]; + let withdrawal_reconciliation_keys = withdrawals + .iter() + .map(|withdraw| withdraw_event_reconciliation_key(&withdraw.outputs)) + .collect::>() + .context("Failed to build reconciliation keys for block withdrawals")?; self.block_publisher - .publish_block(&block) + .publish_block(&block, withdrawals) .await .context("Failed to publish block to Bedrock")?; - self.store.update(&block, placeholder_msg_id, &self.state)?; - - let updated_deposits = self - .store - .mark_unfulfilled_deposit_events_submitted(&deposit_event_ids, block.header.block_id)?; - if updated_deposits > 0 { - info!( - "Marked {updated_deposits} pending deposit events as submitted in block {}", - block.header.block_id - ); - } + self.store.update( + &block, + &deposit_event_ids, + withdrawal_reconciliation_keys, + &self.state, + )?; Ok(self.chain_height) } @@ -308,6 +340,7 @@ impl SequencerCore { let mut valid_transactions = Vec::new(); let mut deposit_event_ids = Vec::new(); + let mut withdrawals = Vec::new(); let max_block_size = usize::try_from(self.sequencer_config.max_block_size.as_u64()) .expect("`max_block_size` should fit into usize"); @@ -372,6 +405,10 @@ impl SequencerCore { } }; + if let Some(withdraw_data) = extract_bridge_withdraw_data(&tx) { + withdrawals.push(withdraw_data); + } + self.state.apply_state_diff(validated_diff); } TransactionOrigin::Sequencer => { @@ -379,17 +416,8 @@ impl SequencerCore { panic!("Sequencer may only generate Public transactions, found {tx:#?}"); }; - if public_tx.message.program_id == Program::bridge().id() { - let instruction: bridge_core::Instruction = - risc0_zkvm::serde::from_slice(&public_tx.message.instruction_data) - .context("Failed to deserialize bridge instruction")?; - match instruction { - bridge_core::Instruction::Deposit { - l1_deposit_op_id, .. - } => { - deposit_event_ids.push(HashType(l1_deposit_op_id)); - } - } + if let Some(deposit_op_id) = extract_bridge_deposit_id(&tx) { + deposit_event_ids.push(deposit_op_id); } self.state @@ -403,6 +431,7 @@ impl SequencerCore { } valid_transactions.push(tx); + info!("Validated transaction with hash {tx_hash}, including it in block"); if valid_transactions.len() >= self.sequencer_config.max_num_tx_in_block { break; @@ -422,12 +451,9 @@ impl SequencerCore { timestamp: new_block_timestamp, }; - // TODO: Remove bedrock_parent_id from Block โ€” it is no longer needed now - // that zone-sdk manages the inscription parent chain internally. - let placeholder_parent_id = [0_u8; 32]; let block = hashable_data .clone() - .into_pending_block(self.store.signing_key(), placeholder_parent_id); + .into_pending_block(self.store.signing_key()); self.chain_height = new_block_height; @@ -440,6 +466,7 @@ impl SequencerCore { Ok(BlockWithMeta { block, deposit_event_ids, + withdrawals, }) } @@ -499,6 +526,7 @@ impl SequencerCore { struct BlockWithMeta { block: Block, deposit_event_ids: Vec, + withdrawals: Vec, } /// Checks the database for any pending deposit events that have not yet been marked as submitted in @@ -624,9 +652,7 @@ fn build_supply_bridge_account_genesis_transaction(balance: u128) -> PublicTrans PublicTransaction::new(message, witness_set) } -fn pending_deposit_event_record( - deposit: &logos_blockchain_zone_sdk::state::DepositInfo, -) -> PendingDepositEventRecord { +fn pending_deposit_event_record(deposit: &DepositInfo) -> PendingDepositEventRecord { PendingDepositEventRecord { deposit_op_id: HashType(deposit.op_id), source_tx_hash: HashType(deposit.tx_hash.0), @@ -653,7 +679,7 @@ fn build_bridge_deposit_tx_from_event(event: &PendingDepositEventRecord) -> Resu l1_deposit_op_id: event.deposit_op_id.0, vault_program_id, recipient_id: metadata.recipient_id, - amount: u128::from(event.amount), + amount: event.amount, }, ) .context("Failed to build bridge deposit message")?; @@ -665,6 +691,97 @@ fn build_bridge_deposit_tx_from_event(event: &PendingDepositEventRecord) -> Resu ))) } +#[must_use] +fn extract_bridge_deposit_id(tx: &LeeTransaction) -> Option { + let LeeTransaction::Public(tx) = tx else { + return None; + }; + + let message = tx.message(); + if message.program_id != lee::program::Program::bridge().id() { + return None; + } + + let instruction = + risc0_zkvm::serde::from_slice::(&message.instruction_data) + .ok()?; + + match instruction { + bridge_core::Instruction::Deposit { + l1_deposit_op_id, .. + } => Some(HashType(l1_deposit_op_id)), + bridge_core::Instruction::Withdraw { .. } => None, + } +} + +#[must_use] +fn extract_bridge_withdraw_data(tx: &LeeTransaction) -> Option { + let LeeTransaction::Public(tx) = tx else { + return None; + }; + + let message = tx.message(); + if message.program_id != lee::program::Program::bridge().id() { + return None; + } + + let instruction = + risc0_zkvm::serde::from_slice::(&message.instruction_data) + .ok()?; + + match instruction { + bridge_core::Instruction::Withdraw { + amount, + bedrock_account_pk, + } => { + let recipient_pk = + logos_blockchain_key_management_system_service::keys::ZkPublicKey::from( + BigUint::from_bytes_le(&bedrock_account_pk), + ); + + Some(WithdrawArg { + outputs: logos_blockchain_core::mantle::ledger::Outputs::new( + logos_blockchain_core::mantle::Note::new(amount, recipient_pk), + ), + }) + } + bridge_core::Instruction::Deposit { .. } => unreachable!( + "Deposit instructions from users should never pass validation, and thus should never be seen here" + ), + } +} + +fn withdraw_event_reconciliation_key( + outputs: &logos_blockchain_core::mantle::ledger::Outputs, +) -> Result { + let [note] = outputs.as_ref().as_slice() else { + return Err(anyhow!( + "Unsupported withdraw output count for reconciliation: {}", + outputs.len() + )); + }; + + // `extract_bridge_withdraw_data` maps [u8;32] LE -> BigUint -> ZkPublicKey. + // Reconcile by reversing that direction here. + let mut bedrock_account_pk = BigUint::from(note.pk.into_inner()).to_bytes_le(); + if bedrock_account_pk.len() > 32 { + return Err(anyhow!( + "Withdraw recipient public key is too large: {} bytes", + bedrock_account_pk.len() + )); + } + bedrock_account_pk.resize(32, 0); + + let bedrock_account_pk: [u8; 32] = bedrock_account_pk + .try_into() + .expect("Public key bytes were padded/truncated to 32 bytes"); + + Ok(WithdrawalReconciliationKey { + amount: note.value, + bedrock_account_pk, + }) +} + /// 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() { @@ -700,6 +817,20 @@ mod tests { test_utils::sequencer_sign_key_for_testing, transaction::{LeeTransaction, clock_invocation}, }; + use key_protocol::key_management::KeyChain; + use lee::{ + Account, AccountId, Data, EphemeralPublicKey, PrivacyPreservingTransaction, + SharedSecretKey, V03State, + error::LeeError, + execute_and_prove, + privacy_preserving_transaction::{Message, circuit::ProgramWithDependencies}, + program::Program, + system_bridge_account_id, + }; + use lee_core::{ + Commitment, EncryptedAccountData, InputAccountIdentity, Nullifier, + account::{AccountWithMetadata, Nonce}, + }; use logos_blockchain_core::mantle::ops::channel::ChannelId; use mempool::MemPoolHandle; use storage::sequencer::sequencer_cells::PendingDepositEventRecord; @@ -801,7 +932,7 @@ mod tests { l1_deposit_op_id, amount, .. - } if l1_deposit_op_id == deposit_op_id && amount == u128::from(expected_amount) + } if l1_deposit_op_id == deposit_op_id && amount == expected_amount ) } @@ -839,13 +970,11 @@ mod tests { prev_block_hash: HashType([0; 32]), timestamp: 0, }; - let genesis_block = genesis_hashable_data.into_pending_block(&signing_key, [0; 32]); + let genesis_block = genesis_hashable_data.into_pending_block(&signing_key); - let expected_msg_id = [7; 32]; SequencerStore::create_db_with_genesis( &config.home.join("rocksdb"), &genesis_block, - expected_msg_id, &genesis_state, signing_key, ) @@ -853,10 +982,8 @@ mod tests { let (sequencer, _mempool_handle) = SequencerCoreWithMockClients::start_from_config(config).await; - let latest_meta = sequencer.store.latest_block_meta().unwrap(); - - assert_eq!(latest_meta.msg_id, expected_msg_id); assert_eq!(sequencer.chain_height, 1); + assert!(sequencer.store.latest_block_meta().is_ok()); } #[should_panic(expected = "Failed to open database")] @@ -1474,4 +1601,95 @@ mod tests { "Block production should abort when clock account data is corrupted" ); } + + #[test] + fn private_bridge_withdraw_invocation_is_dropped() { + let sender_keys = KeyChain::new_os_random(); + let sender_account_id = + AccountId::for_regular_private_account(&sender_keys.nullifier_public_key, 0); + let sender_private_account = Account { + program_owner: Program::authenticated_transfer_program().id(), + balance: 100, + nonce: Nonce(0xdead_beef), + data: Data::default(), + }; + + let mut state = V03State::new_with_genesis_accounts( + &[], + vec![( + Commitment::new(&sender_account_id, &sender_private_account), + Nullifier::for_account_initialization(&sender_account_id), + )], + 0, + ); + + let sender_commitment = Commitment::new(&sender_account_id, &sender_private_account); + let bridge_account_id = system_bridge_account_id(); + + let sender_pre = AccountWithMetadata::new( + sender_private_account, + true, + (&sender_keys.nullifier_public_key, 0), + ); + let bridge_pre = AccountWithMetadata::new( + state.get_account_by_id(bridge_account_id), + false, + bridge_account_id, + ); + + let shared_secret = SharedSecretKey::encapsulate(&sender_keys.viewing_public_key).0; + + let instruction = Program::serialize_instruction(bridge_core::Instruction::Withdraw { + amount: 1, + bedrock_account_pk: [0; 32], + }) + .unwrap(); + + let program_with_deps = ProgramWithDependencies::new( + Program::bridge(), + [( + Program::authenticated_transfer_program().id(), + Program::authenticated_transfer_program(), + )] + .into(), + ); + + let (output, proof) = execute_and_prove( + vec![sender_pre, bridge_pre], + instruction, + vec![ + InputAccountIdentity::PrivateAuthorizedUpdate { + epk: EphemeralPublicKey(vec![12_u8; 1088]), + view_tag: EncryptedAccountData::compute_view_tag( + &sender_keys.nullifier_public_key, + &sender_keys.viewing_public_key, + ), + ssk: shared_secret, + nsk: sender_keys.private_key_holder.nullifier_secret_key, + membership_proof: state + .get_proof_for_commitment(&sender_commitment) + .expect("sender commitment must be in state"), + identifier: 0, + }, + InputAccountIdentity::Public, + ], + &program_with_deps, + ) + .expect("Execution should succeed"); + + let message = Message::try_from_circuit_output(vec![bridge_account_id], vec![], output) + .expect("Message construction should succeed"); + let witness_set = + lee::privacy_preserving_transaction::WitnessSet::for_message(&message, proof, &[]); + let tx = LeeTransaction::PrivacyPreserving(PrivacyPreservingTransaction::new( + message, + witness_set, + )); + let res = tx.execute_check_on_state(&mut state, 1, 0); + + assert!( + matches!(res, Err(LeeError::InvalidInput(_))), + "Bridge withdraw invocation should be rejected in private execution" + ); + } } diff --git a/lez/sequencer/core/src/mock.rs b/lez/sequencer/core/src/mock.rs index e041269a..5f2ab8cf 100644 --- a/lez/sequencer/core/src/mock.rs +++ b/lez/sequencer/core/src/mock.rs @@ -3,11 +3,12 @@ use std::time::Duration; use anyhow::Result; use common::block::Block; use logos_blockchain_key_management_system_service::keys::Ed25519Key; +use logos_blockchain_zone_sdk::sequencer::WithdrawArg; use crate::{ block_publisher::{ BlockPublisherTrait, CheckpointSink, FinalizedBlockSink, OnDepositEventSink, - SequencerCheckpoint, + OnWithdrawEventSink, SequencerCheckpoint, }, config::BedrockConfig, }; @@ -26,11 +27,16 @@ impl BlockPublisherTrait for MockBlockPublisher { _on_checkpoint: CheckpointSink, _on_finalized_block: FinalizedBlockSink, _on_deposit_event: OnDepositEventSink, + _on_withdraw_event: OnWithdrawEventSink, ) -> Result { Ok(Self) } - async fn publish_block(&self, _block: &Block) -> Result<()> { + async fn publish_block( + &self, + _block: &Block, + _bridge_withdrawals: Vec, + ) -> Result<()> { Ok(()) } } diff --git a/lez/sequencer/service/Dockerfile b/lez/sequencer/service/Dockerfile index 10641e9a..5b5d3686 100644 --- a/lez/sequencer/service/Dockerfile +++ b/lez/sequencer/service/Dockerfile @@ -37,9 +37,6 @@ RUN cp "$(which r0vm)" /usr/local/bin/r0vm RUN test -x /usr/local/bin/r0vm RUN r0vm --version -# Install logos blockchain circuits -RUN curl -sSL https://raw.githubusercontent.com/logos-blockchain/logos-blockchain/main/scripts/setup-logos-blockchain-circuits.sh | bash - WORKDIR /sequencer_service # Build argument to enable standalone feature (defaults to false) @@ -95,9 +92,6 @@ COPY --from=builder --chown=sequencer_user:sequencer_user /usr/local/bin/sequenc # Copy r0vm binary from builder COPY --from=builder --chown=sequencer_user:sequencer_user /usr/local/bin/r0vm /usr/local/bin/r0vm -# Copy logos blockchain circuits from builder -COPY --from=builder --chown=sequencer_user:sequencer_user /root/.logos-blockchain-circuits /home/sequencer_user/.logos-blockchain-circuits - VOLUME /var/lib/sequencer_service # Expose default port diff --git a/lez/storage/src/indexer/mod.rs b/lez/storage/src/indexer/mod.rs index 51df8042..b682a4d7 100644 --- a/lez/storage/src/indexer/mod.rs +++ b/lez/storage/src/indexer/mod.rs @@ -208,7 +208,10 @@ impl RocksDBIO { "transaction pre check failed with err {err:?}" )) })? - .execute_check_on_state( + // FIXME: HOT FIX (testnet v0.2): does not check for system account updates due to + // sequencer-generated deposit tx'es; + // CHANGE ME back to `execute_check_on_state` when the indexer can authenticate deposit transactions + .execute_without_system_accounts_check_on_state( &mut breakpoint, block.header.block_id, block.header.timestamp, diff --git a/lez/storage/src/sequencer/mod.rs b/lez/storage/src/sequencer/mod.rs index 851dc4ff..44068517 100644 --- a/lez/storage/src/sequencer/mod.rs +++ b/lez/storage/src/sequencer/mod.rs @@ -2,7 +2,7 @@ use std::{path::Path, sync::Arc}; use common::{ HashType, - block::{BedrockStatus, Block, BlockMeta, MantleMsgId}, + block::{BedrockStatus, Block, BlockMeta}, }; use lee::V03State; use rocksdb::{ @@ -11,12 +11,16 @@ use rocksdb::{ use crate::{ CF_BLOCK_NAME, CF_META_NAME, DB_META_FIRST_BLOCK_IN_DB_KEY, DBIO, DbResult, - cells::shared_cells::{BlockCell, FirstBlockCell, FirstBlockSetCell, LastBlockCell}, + cells::{ + SimpleStorableCell, + shared_cells::{BlockCell, FirstBlockCell, FirstBlockSetCell, LastBlockCell}, + }, error::DbError, sequencer::sequencer_cells::{ LEEStateCellOwned, LEEStateCellRef, LastFinalizedBlockIdCell, LatestBlockMetaCellOwned, LatestBlockMetaCellRef, PendingDepositEventRecord, PendingDepositEventsCellOwned, - PendingDepositEventsCellRef, ZoneSdkCheckpointCellOwned, ZoneSdkCheckpointCellRef, + PendingDepositEventsCellRef, UnseenWithdrawCountCell, WithdrawalReconciliationKey, + ZoneSdkCheckpointCellOwned, ZoneSdkCheckpointCellRef, }, }; @@ -31,6 +35,8 @@ pub const DB_META_ZONE_SDK_CHECKPOINT_KEY: &str = "zone_sdk_checkpoint"; /// Key base for storing queued deposit events that were not yet /// fulfilled on L2. pub const DB_META_PENDING_DEPOSIT_EVENTS_KEY: &str = "pending_deposit_events"; +/// Key base for counting unseen L2 withdraw intents. +pub const DB_META_UNSEEN_WITHDRAW_COUNT_KEY: &str = "unseen_withdraw_count"; /// Key base for storing the LEE state. pub const DB_LEE_STATE_KEY: &str = "lee_state"; @@ -54,12 +60,7 @@ impl RocksDBIO { Self::open_inner(path, &db_opts) } - pub fn create( - path: &Path, - genesis_block: &Block, - genesis_msg_id: MantleMsgId, - genesis_state: &V03State, - ) -> DbResult { + pub fn create(path: &Path, genesis_block: &Block, genesis_state: &V03State) -> DbResult { let mut db_opts = Options::default(); db_opts.create_missing_column_families(true); db_opts.create_if_missing(true); @@ -69,14 +70,13 @@ impl RocksDBIO { if !is_start_set { let block_id = genesis_block.header.block_id; // TODO: Shouldn't this be atomic (batched)? - dbio.put_meta_first_block_in_db(genesis_block, genesis_msg_id)?; + dbio.put_meta_first_block_in_db(genesis_block)?; dbio.put_meta_is_first_block_set()?; dbio.put_meta_last_block_in_db(block_id)?; dbio.put_meta_last_finalized_block_id(None)?; dbio.put_meta_latest_block_meta(&BlockMeta { id: genesis_block.header.block_id, hash: genesis_block.header.hash, - msg_id: genesis_msg_id, })?; dbio.put_lee_state_in_db(genesis_state)?; } @@ -168,7 +168,7 @@ impl RocksDBIO { self.put_batch(&LEEStateCellRef(state), (), batch) } - pub fn put_meta_first_block_in_db(&self, block: &Block, msg_id: MantleMsgId) -> DbResult<()> { + pub fn put_meta_first_block_in_db(&self, block: &Block) -> DbResult<()> { let cf_meta = self.meta_column(); self.db .put_cf( @@ -189,7 +189,7 @@ impl RocksDBIO { .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; let mut batch = WriteBatch::default(); - self.put_block(block, msg_id, true, &mut batch)?; + self.put_block(block, true, &mut batch)?; self.db.write(batch).map_err(|rerr| { DbError::rocksdb_cast_message( rerr, @@ -256,6 +256,14 @@ impl RocksDBIO { self.put(&PendingDepositEventsCellRef(records), ()) } + fn put_pending_deposit_events_batch( + &self, + records: &[PendingDepositEventRecord], + batch: &mut WriteBatch, + ) -> DbResult<()> { + self.put_batch(&PendingDepositEventsCellRef(records), (), batch) + } + pub fn add_pending_deposit_event(&self, event: PendingDepositEventRecord) -> DbResult { let mut records = self.get_pending_deposit_events()?; if records @@ -269,10 +277,11 @@ impl RocksDBIO { Ok(true) } - pub fn mark_pending_deposit_events_submitted( + fn mark_pending_deposit_events_submitted( &self, deposit_op_ids: &[HashType], submitted_block_id: u64, + batch: &mut WriteBatch, ) -> DbResult { let mut records = self.get_pending_deposit_events()?; let mut updated: usize = 0; @@ -286,7 +295,7 @@ impl RocksDBIO { } if updated > 0 { - self.put_pending_deposit_events(&records)?; + self.put_pending_deposit_events_batch(&records, batch)?; } Ok(updated) @@ -312,13 +321,54 @@ impl RocksDBIO { Ok(removed) } - pub fn put_block( + fn increment_unseen_withdraw_count( &self, - block: &Block, - msg_id: MantleMsgId, - first: bool, + withdrawal: WithdrawalReconciliationKey, batch: &mut WriteBatch, - ) -> DbResult<()> { + ) -> DbResult { + let current = self + .get_opt::(withdrawal)? + .map_or(0, |cell| cell.0); + + let next = current.checked_add(1).ok_or_else(|| { + DbError::db_interaction_error("Unseen withdraw counter overflow".to_owned()) + })?; + + self.put_batch(&UnseenWithdrawCountCell(next), withdrawal, batch)?; + + Ok(next) + } + + pub fn consume_unseen_withdraw_count( + &self, + withdrawal: WithdrawalReconciliationKey, + ) -> DbResult { + let Some(current) = self + .get_opt::(withdrawal)? + .map(|cell| cell.0) + else { + return Ok(false); + }; + + if let Some(next) = current.checked_sub(1) { + self.put(&UnseenWithdrawCountCell(next), withdrawal)?; + } else { + let cf_meta = self.meta_column(); + let db_key = + ::key_constructor(withdrawal)?; + + self.db.delete_cf(&cf_meta, db_key).map_err(|rerr| { + DbError::rocksdb_cast_message( + rerr, + Some("Failed to delete unseen withdraw count".to_owned()), + ) + })?; + } + + Ok(true) + } + + pub fn put_block(&self, block: &Block, first: bool, batch: &mut WriteBatch) -> DbResult<()> { let cf_block = self.block_column(); if !first { @@ -330,7 +380,6 @@ impl RocksDBIO { &BlockMeta { id: block.header.block_id, hash: block.header.hash, - msg_id, }, batch, )?; @@ -455,13 +504,23 @@ impl RocksDBIO { pub fn atomic_update( &self, block: &Block, - msg_id: MantleMsgId, + deposit_op_ids: &[HashType], + withdrawals: Vec, state: &V03State, ) -> DbResult<()> { let block_id = block.header.block_id; let mut batch = WriteBatch::default(); - self.put_block(block, msg_id, false, &mut batch)?; + + self.put_block(block, false, &mut batch)?; + + self.mark_pending_deposit_events_submitted(deposit_op_ids, block_id, &mut batch)?; + + for withdrawal in withdrawals { + self.increment_unseen_withdraw_count(withdrawal, &mut batch)?; + } + self.put_lee_state_in_db_batch(state, &mut batch)?; + self.db.write(batch).map_err(|rerr| { DbError::rocksdb_cast_message( rerr, diff --git a/lez/storage/src/sequencer/sequencer_cells.rs b/lez/storage/src/sequencer/sequencer_cells.rs index 39b6a406..7672e271 100644 --- a/lez/storage/src/sequencer/sequencer_cells.rs +++ b/lez/storage/src/sequencer/sequencer_cells.rs @@ -9,7 +9,7 @@ use crate::{ sequencer::{ CF_LEE_STATE_NAME, DB_LEE_STATE_KEY, DB_META_LAST_FINALIZED_BLOCK_ID, DB_META_LATEST_BLOCK_META_KEY, DB_META_PENDING_DEPOSIT_EVENTS_KEY, - DB_META_ZONE_SDK_CHECKPOINT_KEY, + DB_META_UNSEEN_WITHDRAW_COUNT_KEY, DB_META_ZONE_SDK_CHECKPOINT_KEY, }, }; @@ -175,6 +175,52 @@ impl SimpleWritableCell for PendingDepositEventsCellRef<'_> { } } +#[derive(Debug, Clone, Copy)] +pub struct WithdrawalReconciliationKey { + pub amount: u64, + pub bedrock_account_pk: [u8; 32], +} + +#[derive(Debug, BorshSerialize, BorshDeserialize)] +pub struct UnseenWithdrawCountCell(pub u64); + +impl SimpleStorableCell for UnseenWithdrawCountCell { + type KeyParams = WithdrawalReconciliationKey; + + const CELL_NAME: &'static str = DB_META_UNSEEN_WITHDRAW_COUNT_KEY; + const CF_NAME: &'static str = CF_META_NAME; + + fn key_constructor(key_params: Self::KeyParams) -> DbResult> { + let WithdrawalReconciliationKey { + amount, + bedrock_account_pk, + } = key_params; + + borsh::to_vec(&(Self::CELL_NAME, amount, bedrock_account_pk)).map_err(|err| { + DbError::borsh_cast_message( + err, + Some(format!( + "Failed to serialize {:?} key params", + Self::CELL_NAME + )), + ) + }) + } +} + +impl SimpleReadableCell for UnseenWithdrawCountCell {} + +impl SimpleWritableCell for UnseenWithdrawCountCell { + fn value_constructor(&self) -> DbResult> { + borsh::to_vec(&self).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize unseen withdraw count".to_owned()), + ) + }) + } +} + #[cfg(test)] mod uniform_tests { use crate::{ diff --git a/lez/wallet-ffi/Cargo.toml b/lez/wallet-ffi/Cargo.toml index 567ad27c..1e8b6395 100644 --- a/lez/wallet-ffi/Cargo.toml +++ b/lez/wallet-ffi/Cargo.toml @@ -15,10 +15,12 @@ wallet.workspace = true lee.workspace = true lee_core.workspace = true sequencer_service_rpc = { workspace = true, features = ["client"] } +common.workspace = true tokio.workspace = true key_protocol.workspace = true serde_json.workspace = true +risc0-zkvm.workspace = true [build-dependencies] cbindgen = "0.29" diff --git a/lez/wallet-ffi/src/error.rs b/lez/wallet-ffi/src/error.rs index 206f24d8..c701cb6f 100644 --- a/lez/wallet-ffi/src/error.rs +++ b/lez/wallet-ffi/src/error.rs @@ -2,6 +2,8 @@ //! //! Uses numeric error codes with error messages printed to stderr. +use std::str::Utf8Error; + /// Error codes returned by FFI functions. #[repr(C)] #[must_use] @@ -41,10 +43,18 @@ pub enum WalletFfiError { InvalidTypeConversion = 15, /// Invalid Key value. InvalidKeyValue = 16, + /// Invalid program bytecode. + InvalidBytecode = 17, /// Internal error (catch-all). InternalError = 99, } +impl From for WalletFfiError { + fn from(_value: Utf8Error) -> Self { + Self::InvalidUtf8 + } +} + impl WalletFfiError { /// Check if it's [`WalletFfiError::Success`] or panic. pub fn unwrap(self) { diff --git a/lez/wallet-ffi/src/generic_transaction.rs b/lez/wallet-ffi/src/generic_transaction.rs new file mode 100644 index 00000000..f8f344ad --- /dev/null +++ b/lez/wallet-ffi/src/generic_transaction.rs @@ -0,0 +1,469 @@ +use std::{ + collections::HashMap, + ffi::{c_char, CString}, +}; + +use lee::{privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program}; + +use crate::{ + block_on, + error::{print_error, WalletFfiError}, + map_execution_error, + wallet::get_wallet, + FfiAccountIdentity, FfiBytes32, WalletHandle, +}; + +#[repr(C)] +pub struct FfiInstructionWords { + pub instruction_words: *mut u32, + pub instruction_words_size: usize, + pub error: WalletFfiError, +} + +impl FfiInstructionWords { + const fn from_err(error: WalletFfiError) -> Self { + Self { + instruction_words: std::ptr::null_mut(), + instruction_words_size: 0, + error, + } + } +} + +#[repr(C)] +/// Intended to be created manually. +pub struct FfiProgram { + pub elf_data: *const u8, + pub elf_size: usize, +} + +impl TryFrom<&FfiProgram> for Program { + type Error = WalletFfiError; + + fn try_from(value: &FfiProgram) -> Result { + let mut elf = Vec::with_capacity(value.elf_size); + + // Alignment will be different, we need to read elements one-by-one + for i in 0..value.elf_size { + elf.push(unsafe { *value.elf_data.add(i) }); + } + + Self::new(elf).map_err(|err| { + print_error(format!("Invalid program bytecode, err: {err}")); + WalletFfiError::InvalidBytecode + }) + } +} + +impl From for FfiProgram { + fn from(value: Program) -> Self { + let elf_clone = value.elf().to_vec(); + let elf_size = elf_clone.len(); + let elf_data = Box::into_raw(elf_clone.into_boxed_slice()) as *const u8; + + Self { elf_data, elf_size } + } +} + +#[repr(C)] +/// Intended to be created manually. +pub struct FfiProgramWithDependencies { + pub program: FfiProgram, + pub deps: *const FfiProgram, + pub deps_size: usize, +} + +impl TryFrom<&FfiProgramWithDependencies> for ProgramWithDependencies { + type Error = WalletFfiError; + + fn try_from(value: &FfiProgramWithDependencies) -> Result { + let mut program_map = HashMap::new(); + + let orig_program = (&value.program).try_into()?; + + // Alignment will be different, we need to read elements one-by-one + for i in 0..value.deps_size { + let program_dep: Program = unsafe { value.deps.add(i).as_ref() } + .ok_or(WalletFfiError::NullPointer)? + .try_into()?; + + program_map.insert(program_dep.id(), program_dep); + } + + Ok(Self { + program: orig_program, + dependencies: program_map, + }) + } +} + +impl From for FfiProgramWithDependencies { + fn from(value: ProgramWithDependencies) -> Self { + let ffi_program = value.program.into(); + + let ffi_deps: Vec = value + .dependencies + .into_values() + .map(Into::into) + .collect::>(); + + let deps_size = ffi_deps.len(); + let deps = Box::into_raw(ffi_deps.into_boxed_slice()) as *const FfiProgram; + + Self { + program: ffi_program, + deps, + deps_size, + } + } +} + +/// Result of a generic transaction operation. +#[repr(C)] +pub struct FfiTransactionResult { + // TODO: Replace with HashType FFI representation + /// Transaction hash (null-terminated string, or null on failure). + pub tx_hash: *mut c_char, + /// Whether the transaction succeeded. + pub success: bool, + pub secrets_data: *const FfiBytes32, + /// Public transactions have 0 secrets. + pub secrets_size: usize, +} + +impl Default for FfiTransactionResult { + fn default() -> Self { + Self { + tx_hash: std::ptr::null_mut(), + success: false, + secrets_data: std::ptr::null(), + secrets_size: 0, + } + } +} + +/// Serialize sequence of bytes into RISC0 readable words. +/// +/// # Parameters +/// - `input_instruction_data`: Valid pointer to a sequence of bytes +/// - `input_instruction_data_size`: Size of `input_instruction_data` +/// +/// # Returns +/// - `Success` on successful creation +/// - Error code on failure +/// +/// # Safety +/// - `input_instruction_data` must be a valid pointer +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_serialization_helper( + input_instruction_data: *const u8, + input_instruction_data_size: usize, +) -> FfiInstructionWords { + if input_instruction_data.is_null() { + print_error("Null input pointer for instruction_data"); + return FfiInstructionWords::from_err(WalletFfiError::NullPointer); + } + + let input_slice = + unsafe { std::slice::from_raw_parts(input_instruction_data, input_instruction_data_size) }; + let res_vec_u32_with_prefix = match risc0_zkvm::serde::to_vec(input_slice).map_err(|err| { + print_error(format!( + "Failed to serialize input into words with err {err}" + )); + WalletFfiError::SerializationError + }) { + Ok(res) => res, + Err(err) => return FfiInstructionWords::from_err(err), + }; + + // The resulting vec contains len as prefix + let res_vec_u32 = res_vec_u32_with_prefix[1..].to_vec(); + + let res_len = res_vec_u32.len(); + let res_boxed = res_vec_u32.into_boxed_slice(); + let res_ptr = Box::into_raw(res_boxed).cast::(); + + FfiInstructionWords { + instruction_words: res_ptr, + instruction_words_size: res_len, + error: WalletFfiError::Success, + } +} + +/// Send generic public transaction. +/// +/// # Parameters +/// - `handle`: Valid pointer to wallet handle +/// - `account_identities`: Valid pointer to list of `FfiAccountIdentity` +/// - `instruction_words`: Valid pointer to instruction words +/// - `out_result`: Valid pointer to `FfiTransactionResult` +/// +/// # Returns +/// - `Success` on successful creation +/// - Error code on failure +/// +/// # Safety +/// - `handle` must be a valid pointer +/// - `account_identities` must be a valid pointer +/// - `instruction_words` must be a valid pointer +/// - `out_result` must be a valid pointer +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_send_generic_public_transaction( + handle: *mut WalletHandle, + account_identities: *const FfiAccountIdentity, + account_identities_size: usize, + instruction_words: *const u32, + instruction_words_size: usize, + program_with_dependencies: *const FfiProgramWithDependencies, + out_result: *mut FfiTransactionResult, +) -> WalletFfiError { + let wrapper = match get_wallet(handle) { + Ok(w) => w, + Err(e) => return e, + }; + + if account_identities.is_null() { + print_error("Null input pointer for account identities list"); + return WalletFfiError::NullPointer; + } + + if instruction_words.is_null() { + print_error("Null input pointer for instruction data"); + return WalletFfiError::NullPointer; + } + + if out_result.is_null() { + print_error("Null output pointer return hash"); + return WalletFfiError::NullPointer; + } + + let wallet = match wrapper.core.lock() { + Ok(w) => w, + Err(e) => { + print_error(format!("Failed to lock wallet: {e}")); + return WalletFfiError::InternalError; + } + }; + + let accounts_ffi = std::slice::from_raw_parts(account_identities, account_identities_size); + let instruction_data = std::slice::from_raw_parts(instruction_words, instruction_words_size); + + let mut accounts = Vec::with_capacity(account_identities_size); + + for ffi_acc in accounts_ffi { + match ffi_acc.try_into() { + Ok(v) => accounts.push(v), + Err(err) => { + print_error("Failed to convert FfiAccountIdentity into AccountIdentity"); + return err; + } + } + } + + let program = match unsafe { &*program_with_dependencies }.try_into() { + Ok(v) => v, + Err(err) => return err, + }; + + match block_on(wallet.send_pub_tx(accounts, instruction_data.to_vec(), &program)) { + Ok(tx_hash) => { + let tx_hash = CString::new(tx_hash.to_string()) + .map_or(std::ptr::null_mut(), std::ffi::CString::into_raw); + + unsafe { + (*out_result).tx_hash = tx_hash; + (*out_result).success = true; + } + WalletFfiError::Success + } + Err(e) => { + print_error(format!("Public send failed: {e:?}")); + unsafe { + (*out_result).tx_hash = std::ptr::null_mut(); + (*out_result).success = false; + } + map_execution_error(e) + } + } +} + +/// Send generic private transaction. +/// +/// # Parameters +/// - `handle`: Valid pointer to wallet handle +/// - `account_identities`: Valid pointer to list of `FfiAccountIdentity` +/// - `instruction_words`: Valid pointer to instruction words +/// - `out_result`: Valid pointer to `FfiTransactionResult` +/// +/// # Returns +/// - `Success` on successful creation +/// - Error code on failure +/// +/// # Safety +/// - `handle` must be a valid pointer +/// - `account_identities` must be a valid pointer +/// - `instruction_words` must be a valid pointer +/// - `out_result` must be a valid pointer +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_send_generic_private_transaction( + handle: *mut WalletHandle, + account_identities: *const FfiAccountIdentity, + account_identities_size: usize, + instruction_words: *const u32, + instruction_words_size: usize, + program_with_dependencies: *const FfiProgramWithDependencies, + out_result: *mut FfiTransactionResult, +) -> WalletFfiError { + let wrapper = match get_wallet(handle) { + Ok(w) => w, + Err(e) => return e, + }; + + if account_identities.is_null() { + print_error("Null input pointer for account identities list"); + return WalletFfiError::NullPointer; + } + + if instruction_words.is_null() { + print_error("Null input pointer for instruction data"); + return WalletFfiError::NullPointer; + } + + if out_result.is_null() { + print_error("Null output pointer return hash"); + return WalletFfiError::NullPointer; + } + + let wallet = match wrapper.core.lock() { + Ok(w) => w, + Err(e) => { + print_error(format!("Failed to lock wallet: {e}")); + return WalletFfiError::InternalError; + } + }; + + let accounts_ffi = std::slice::from_raw_parts(account_identities, account_identities_size); + let instruction_data = std::slice::from_raw_parts(instruction_words, instruction_words_size); + + let mut accounts = Vec::with_capacity(account_identities_size); + + for ffi_acc in accounts_ffi { + match ffi_acc.try_into() { + Ok(v) => accounts.push(v), + Err(err) => { + print_error("Failed to convert FfiAccountIdentity into AccountIdentity"); + return err; + } + } + } + + let program = match unsafe { &*program_with_dependencies }.try_into() { + Ok(v) => v, + Err(err) => return err, + }; + + match block_on(wallet.send_privacy_preserving_tx(accounts, instruction_data.to_vec(), &program)) + { + Ok((tx_hash, secrets)) => { + let tx_hash = CString::new(tx_hash.to_string()) + .map_or(std::ptr::null_mut(), std::ffi::CString::into_raw); + + unsafe { + (*out_result).tx_hash = tx_hash; + (*out_result).success = true; + + let secrets_size = secrets.len(); + let boxed_slice = secrets + .into_iter() + .map(Into::into) + .collect::>() + .into_boxed_slice(); + let secrets_data = Box::into_raw(boxed_slice) as *const FfiBytes32; + + (*out_result).secrets_size = secrets_size; + (*out_result).secrets_data = secrets_data; + } + WalletFfiError::Success + } + Err(e) => { + print_error(format!("Private send failed: {e:?}")); + unsafe { + *out_result = FfiTransactionResult::default(); + } + map_execution_error(e) + } + } +} + +/// Free a transaction result returned by `wallet_ffi_send_generic_public_transaction` or +/// `wallet_ffi_send_generic_private_transaction`. +/// +/// # Safety +/// The result must be either null or a valid result from a transaction function. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_free_transaction_result(result: *mut FfiTransactionResult) { + if result.is_null() { + return; + } + + unsafe { + let result = &*result; + if !result.tx_hash.is_null() { + drop(CString::from_raw(result.tx_hash)); + } + + if !result.secrets_data.is_null() { + let secrets = + std::slice::from_raw_parts_mut(result.secrets_data.cast_mut(), result.secrets_size); + drop(Box::from_raw(std::ptr::from_mut::<[FfiBytes32]>(secrets))); + } + } +} + +/// Free a instruction words returned by `wallet_ffi_serialization_helper`. +/// +/// # Safety +/// The result must be either null or a valid result from a serialization helper function. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_free_instruction_words(words: *mut FfiInstructionWords) { + if words.is_null() { + return; + } + + unsafe { + let words = &*words; + + if !words.instruction_words.is_null() { + let words = std::slice::from_raw_parts_mut( + words.instruction_words, + words.instruction_words_size, + ); + drop(Box::from_raw(std::ptr::from_mut::<[u32]>(words))); + } + } +} + +#[cfg(test)] +mod tests { + use lee::program::Program; + + use crate::generic_transaction::FfiProgram; + + #[test] + fn program_cast_consistency() { + let prog = Program::amm(); + + let first_5_bytes = prog.elf()[..5].to_vec(); + + let ffi_prog: FfiProgram = prog.into(); + + assert!(!ffi_prog.elf_data.is_null()); + + let mut ffi_first_5_bytes = vec![]; + for i in 0..5 { + ffi_first_5_bytes.push(unsafe { *ffi_prog.elf_data.add(i) }); + } + + assert_eq!(ffi_first_5_bytes, first_5_bytes); + } +} diff --git a/lez/wallet-ffi/src/keys.rs b/lez/wallet-ffi/src/keys.rs index 1eae1723..6a2c4d0b 100644 --- a/lez/wallet-ffi/src/keys.rs +++ b/lez/wallet-ffi/src/keys.rs @@ -1,13 +1,15 @@ //! Key retrieval functions. -use std::ptr; +use std::{ffi::CString, ptr}; use lee::{AccountId, PublicKey}; +use wallet::AccountIdentity; use crate::{ error::{print_error, WalletFfiError}, types::{FfiBytes32, FfiPrivateAccountKeys, FfiPublicAccountKey, WalletHandle}, wallet::get_wallet, + FfiAccountIdentity, }; /// Get the public key for a public account. @@ -250,3 +252,153 @@ pub unsafe extern "C" fn wallet_ffi_account_id_from_base58( WalletFfiError::Success } + +/// Resolve public account. +/// +/// # Parameters +/// - `account_id`: 32 bytes of the public account ID +/// - `needs_sign`: whether the account needs signing +/// - `out_account_identity`: valid pointer, where output will be written +/// +/// # Returns +/// - `Success` on successful retrieval +/// +/// # Safety +/// - `out_account_identity` must be a valid pointer to a `FfiAccountIdentity` struct +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_resolve_public_account( + account_id: FfiBytes32, + needs_sign: bool, + out_account_identity: *mut FfiAccountIdentity, +) -> WalletFfiError { + if out_account_identity.is_null() { + print_error("Null pointer argument"); + return WalletFfiError::NullPointer; + } + + let resolved_account = if needs_sign { + AccountIdentity::Public(account_id.into()) + } else { + AccountIdentity::PublicNoSign(account_id.into()) + }; + + unsafe { + *out_account_identity = resolved_account.into(); + } + + WalletFfiError::Success +} + +/// Resolve private account. +/// +/// # Parameters +/// - `handle`: Valid wallet handle +/// - `account_id`: 32 bytes of the public account ID +/// - `out_account_identity`: valid pointer, where output will be written +/// +/// # Returns +/// - `Success` on successful retrieval +/// - `InternalError` if failed to lock wallet +/// - `AccountNotFound` if the account is not found +/// +/// # Safety +/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open` +/// - `out_account_identity` must be a valid pointer to a `FfiAccountIdentity` struct +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_resolve_private_account( + handle: *mut WalletHandle, + account_id: FfiBytes32, + out_account_identity: *mut FfiAccountIdentity, +) -> WalletFfiError { + if out_account_identity.is_null() { + print_error("Null pointer argument"); + return WalletFfiError::NullPointer; + } + + let wrapper = match get_wallet(handle) { + Ok(w) => w, + Err(e) => return e, + }; + + let wallet = match wrapper.core.lock() { + Ok(w) => w, + Err(e) => { + print_error(format!("Failed to lock wallet: {e}")); + return WalletFfiError::InternalError; + } + }; + + let account_id = account_id.into(); + + let Some(resolved_account) = wallet.resolve_private_account(account_id) else { + print_error("Account not found"); + return WalletFfiError::AccountNotFound; + }; + + unsafe { + *out_account_identity = resolved_account.into(); + } + + WalletFfiError::Success +} + +/// Free account identity returned by `wallet_ffi_resolve_private_account` or +/// `wallet_ffi_resolve_public_account`. +/// +/// # Safety +/// The account must be either null or a valid account returned by +/// `wallet_ffi_resolve_private_account` or `wallet_ffi_resolve_public_account`. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_free_account_identity( + account_identity: *mut FfiAccountIdentity, +) { + if account_identity.is_null() { + return; + } + + unsafe { + let FfiAccountIdentity { + kind: _, + account_id: _, + key_path, + nullifier_secret_key: _, + nullifier_public_key: _, + viewing_public_key, + viewing_public_key_len, + identifier: _, + } = *account_identity; + + if !viewing_public_key.is_null() { + let slice = std::slice::from_raw_parts_mut( + viewing_public_key.cast_mut(), + viewing_public_key_len, + ); + drop(Box::from_raw(std::ptr::from_mut::<[u8]>(slice))); + } + + if !key_path.is_null() { + let key_path_cstring = CString::from_raw(key_path); + drop(key_path_cstring); + } + } +} + +#[cfg(test)] +mod tests { + use lee::AccountId; + use wallet::AccountIdentity; + + use crate::{keys::wallet_ffi_free_account_identity, FfiAccountIdentity}; + + #[test] + fn acc_identity_correct_free() { + let acc_identity = AccountIdentity::Public(AccountId::new([42; 32])); + let mut ffi_acc_identity: FfiAccountIdentity = acc_identity.into(); + + unsafe { + wallet_ffi_free_account_identity(&raw mut ffi_acc_identity); + } + + assert!(ffi_acc_identity.viewing_public_key.is_null()); + } +} diff --git a/lez/wallet-ffi/src/lib.rs b/lez/wallet-ffi/src/lib.rs index b28a548a..6f4c1808 100644 --- a/lez/wallet-ffi/src/lib.rs +++ b/lez/wallet-ffi/src/lib.rs @@ -23,6 +23,7 @@ #![expect( clippy::undocumented_unsafe_blocks, clippy::multiple_unsafe_ops_per_block, + clippy::as_conversions, reason = "TODO: fix later" )] @@ -42,8 +43,10 @@ use crate::error::print_error; pub mod account; pub mod error; +pub mod generic_transaction; pub mod keys; pub mod pinata; +pub mod program_deployment; pub mod sync; pub mod transfer; pub mod types; diff --git a/lez/wallet-ffi/src/program_deployment.rs b/lez/wallet-ffi/src/program_deployment.rs new file mode 100644 index 00000000..8820a935 --- /dev/null +++ b/lez/wallet-ffi/src/program_deployment.rs @@ -0,0 +1,253 @@ +use std::{ffi::CString, ptr, slice}; + +use common::transaction::LeeTransaction; +use lee::{program::Program, ProgramDeploymentTransaction}; +use sequencer_service_rpc::RpcClient as _; + +use crate::{ + block_on, + error::{print_error, WalletFfiError}, + generic_transaction::{FfiProgram, FfiTransactionResult}, + wallet::get_wallet, + WalletHandle, +}; + +/// Send a program deployment transaction. +/// +/// Publishes program for future use. +/// +/// # Parameters +/// - `handle`: Valid wallet handle +/// - `elf_data`: Valid pointer to elf data in bytes +/// - `elf_size`: Size of elf data +/// - `out_result`: Output pointer for transfer result +/// +/// # Returns +/// - `Success` if deployment was submitted successfully +/// - Error code on other failures +/// +/// # Memory +/// The result must be freed with `wallet_ffi_free_transaction_result()`. +/// +/// # Safety +/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open` +/// - `elf_data` must be a valid pointer to elf data +/// - `out_result` must be a valid pointer to a `FfiTransferResult` struct +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_program_deployment( + handle: *mut WalletHandle, + elf_data: *const u8, + elf_size: usize, + out_result: *mut FfiTransactionResult, +) -> WalletFfiError { + let wrapper = match get_wallet(handle) { + Ok(w) => w, + Err(e) => return e, + }; + + if elf_data.is_null() || out_result.is_null() { + print_error("Null pointer argument"); + return WalletFfiError::NullPointer; + } + + let wallet = match wrapper.core.lock() { + Ok(w) => w, + Err(e) => { + print_error(format!("Failed to lock wallet: {e}")); + return WalletFfiError::InternalError; + } + }; + + let elf = unsafe { slice::from_raw_parts(elf_data, elf_size) }.to_vec(); + + let message = lee::program_deployment_transaction::Message::new(elf); + let transaction = ProgramDeploymentTransaction::new(message); + + match block_on( + wallet + .sequencer_client + .send_transaction(LeeTransaction::ProgramDeployment(transaction)), + ) { + Ok(tx_hash) => { + let tx_hash = CString::new(tx_hash.to_string()) + .map_or(ptr::null_mut(), std::ffi::CString::into_raw); + + unsafe { + (*out_result).tx_hash = tx_hash; + (*out_result).success = true; + } + WalletFfiError::Success + } + Err(e) => { + print_error(format!("Deployment failed: {e:?}")); + unsafe { + (*out_result).tx_hash = ptr::null_mut(); + (*out_result).success = false; + } + WalletFfiError::NetworkError + } + } +} + +/// Writes elf data of authenticated transfer program into buffer. +/// +/// WARNING: Result is not consisent and change between versions, use for testing purposes only. +/// +/// # Parameters +/// - `ffi_program`: Valid pointer to `FfiProgram` +/// +/// # Returns +/// - `Success` if deployment was submitted successfully +/// - Error code on other failures +/// +/// # Memory +/// - `FfiProgram` can be freed with corresponding `wallet_ffi_free_ffi_program` function +/// +/// # Safety +/// - `ffi_program` must be a non-null pointer +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_transfer_elf(ffi_program: *mut FfiProgram) -> WalletFfiError { + if ffi_program.is_null() { + print_error("Null pointer argument"); + return WalletFfiError::NullPointer; + } + + let elf = Program::authenticated_transfer_program().elf().to_vec(); + + let (raw_elf_data, raw_elf_size, _) = elf.into_raw_parts(); + + unsafe { + (*ffi_program).elf_data = raw_elf_data; + (*ffi_program).elf_size = raw_elf_size; + }; + + WalletFfiError::Success +} + +/// Writes elf data of authenticated token program into buffer. +/// +/// WARNING: Result is not consisent and change between versions, use for testing purposes only. +/// +/// # Parameters +/// - `ffi_program`: Valid pointer to `FfiProgram` +/// +/// # Returns +/// - `Success` if deployment was submitted successfully +/// - Error code on other failures +/// +/// # Memory +/// - `FfiProgram` can be freed with corresponding `wallet_ffi_free_ffi_program` function +/// +/// # Safety +/// - `ffi_program` must be a non-null pointer +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_token_elf(ffi_program: *mut FfiProgram) -> WalletFfiError { + if ffi_program.is_null() { + print_error("Null pointer argument"); + return WalletFfiError::NullPointer; + } + + let elf = Program::token().elf().to_vec(); + + let (raw_elf_data, raw_elf_size, _) = elf.into_raw_parts(); + + unsafe { + (*ffi_program).elf_data = raw_elf_data; + (*ffi_program).elf_size = raw_elf_size; + }; + + WalletFfiError::Success +} + +/// Writes elf data of amm into buffer. +/// +/// WARNING: Result is not consisent and change between versions, use for testing purposes only. +/// +/// # Parameters +/// - `ffi_program`: Valid pointer to `FfiProgram` +/// +/// # Returns +/// - `Success` if deployment was submitted successfully +/// - Error code on other failures +/// +/// # Memory +/// - `FfiProgram` can be freed with corresponding `wallet_ffi_free_ffi_program` function +/// +/// # Safety +/// - `ffi_program` must be a non-null pointer +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_amm_elf(ffi_program: *mut FfiProgram) -> WalletFfiError { + if ffi_program.is_null() { + print_error("Null pointer argument"); + return WalletFfiError::NullPointer; + } + + let elf = Program::amm().elf().to_vec(); + + let (raw_elf_data, raw_elf_size, _) = elf.into_raw_parts(); + + unsafe { + (*ffi_program).elf_data = raw_elf_data; + (*ffi_program).elf_size = raw_elf_size; + }; + + WalletFfiError::Success +} + +/// Writes elf data of ata into buffer. +/// +/// WARNING: Result is not consisent and change between versions, use for testing purposes only. +/// +/// # Parameters +/// - `ffi_program`: Valid pointer to `FfiProgram` +/// +/// # Returns +/// - `Success` if deployment was submitted successfully +/// - Error code on other failures +/// +/// # Memory +/// - `FfiProgram` can be freed with corresponding `wallet_ffi_free_ffi_program` function +/// +/// # Safety +/// - `ffi_program` must be a non-null pointer +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_ata_elf(ffi_program: *mut FfiProgram) -> WalletFfiError { + if ffi_program.is_null() { + print_error("Null pointer argument"); + return WalletFfiError::NullPointer; + } + + let elf = Program::ata().elf().to_vec(); + + let (raw_elf_data, raw_elf_size, _) = elf.into_raw_parts(); + + unsafe { + (*ffi_program).elf_data = raw_elf_data; + (*ffi_program).elf_size = raw_elf_size; + }; + + WalletFfiError::Success +} + +/// Free a ffi program returned by functions `wallet_ffi_*_elf`. +/// +/// # Safety +/// The result must be either null or a valid result from a elf getter function. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_free_ffi_program(ffi_program: *mut FfiProgram) { + if ffi_program.is_null() { + return; + } + + unsafe { + let ffi_program = &*ffi_program; + + if !ffi_program.elf_data.is_null() { + let elf = std::slice::from_raw_parts_mut( + ffi_program.elf_data.cast_mut(), + ffi_program.elf_size, + ); + drop(Box::from_raw(std::ptr::from_mut::<[u8]>(elf))); + } + } +} diff --git a/lez/wallet-ffi/src/types.rs b/lez/wallet-ffi/src/types.rs index 8c9e105d..ad366b91 100644 --- a/lez/wallet-ffi/src/types.rs +++ b/lez/wallet-ffi/src/types.rs @@ -1,9 +1,15 @@ //! C-compatible type definitions for the FFI layer. use core::slice; -use std::{ffi::c_char, ptr}; +use std::{ + ffi::{c_char, CString}, + ptr, + str::FromStr as _, +}; -use lee::Data; +use lee::{Data, SharedSecretKey}; +use lee_core::{encryption::MlKem768EncapsulationKey, NullifierPublicKey}; +use wallet::AccountIdentity; use crate::error::WalletFfiError; @@ -153,6 +159,12 @@ impl FfiBytes32 { } } +impl From for FfiBytes32 { + fn from(value: SharedSecretKey) -> Self { + Self { data: value.0 } + } +} + impl FfiPrivateAccountKeys { #[must_use] pub const fn npk(&self) -> lee_core::NullifierPublicKey { @@ -174,6 +186,50 @@ impl FfiPrivateAccountKeys { } } +/// Enumeration to represent kinds of `FfiAccountIdentity`. +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FfiAccountIdentityKind { + Public = 0, + PublicNoSign = 1, + PublicKeycard = 2, + PrivateOwned = 3, + PrivateForeign = 4, + PrivatePdaOwned = 5, + PrivatePdaForeign = 6, + PrivateShared = 7, + PrivatePdaShared = 8, +} + +/// Struct representing an account identity, given to `AccountManager` at intialization. +#[repr(C)] +pub struct FfiAccountIdentity { + pub kind: FfiAccountIdentityKind, + pub account_id: FfiBytes32, + /// C-compatible string. + pub key_path: *mut c_char, + pub nullifier_secret_key: FfiBytes32, + pub nullifier_public_key: FfiBytes32, + pub viewing_public_key: *const u8, + pub viewing_public_key_len: usize, + pub identifier: FfiU128, +} + +impl Default for FfiAccountIdentity { + fn default() -> Self { + Self { + kind: FfiAccountIdentityKind::Public, + account_id: FfiBytes32::default(), + key_path: std::ptr::null_mut(), + nullifier_secret_key: FfiBytes32::default(), + nullifier_public_key: FfiBytes32::default(), + viewing_public_key: std::ptr::null(), + viewing_public_key_len: 0, + identifier: FfiU128::default(), + } + } +} + impl From for FfiU128 { fn from(value: u128) -> Self { Self { @@ -194,6 +250,12 @@ impl From for FfiBytes32 { } } +impl From<[u8; 32]> for FfiBytes32 { + fn from(value: [u8; 32]) -> Self { + Self { data: value } + } +} + impl From for lee::AccountId { fn from(bytes: FfiBytes32) -> Self { Self::new(bytes.data) @@ -268,3 +330,384 @@ impl TryFrom<&FfiPublicAccountKey> for lee::PublicKey { Ok(public_key) } } + +impl From for FfiAccountIdentity { + fn from(value: AccountIdentity) -> Self { + match value { + AccountIdentity::Public(account_id) => Self { + kind: FfiAccountIdentityKind::Public, + account_id: account_id.into(), + ..Default::default() + }, + AccountIdentity::PublicNoSign(account_id) => Self { + kind: FfiAccountIdentityKind::PublicNoSign, + account_id: account_id.into(), + ..Default::default() + }, + AccountIdentity::PublicKeycard { + account_id, + key_path, + } => Self { + kind: FfiAccountIdentityKind::PublicKeycard, + account_id: account_id.into(), + key_path: CString::into_raw( + CString::from_str(&key_path).expect("key_path should be a valid string"), + ), + ..Default::default() + }, + AccountIdentity::PrivateOwned(account_id) => Self { + kind: FfiAccountIdentityKind::PrivateOwned, + account_id: account_id.into(), + ..Default::default() + }, + AccountIdentity::PrivateForeign { + npk, + vpk, + identifier, + } => { + let vpk_vec = vpk.to_bytes().to_vec(); + let vpk_len = vpk_vec.len(); + let vpk_data = if vpk_len > 0 { + let vpk_data_boxed = vpk_vec.into_boxed_slice(); + Box::into_raw(vpk_data_boxed) as *const u8 + } else { + ptr::null() + }; + + Self { + kind: FfiAccountIdentityKind::PrivateForeign, + nullifier_public_key: npk.0.into(), + viewing_public_key: vpk_data, + viewing_public_key_len: vpk_len, + identifier: identifier.into(), + ..Default::default() + } + } + AccountIdentity::PrivatePdaOwned(account_id) => Self { + kind: FfiAccountIdentityKind::PrivatePdaOwned, + account_id: account_id.into(), + ..Default::default() + }, + AccountIdentity::PrivatePdaForeign { + account_id, + npk, + vpk, + identifier, + } => { + let vpk_vec = vpk.to_bytes().to_vec(); + let vpk_len = vpk_vec.len(); + let vpk_data = if vpk_len > 0 { + let vpk_data_boxed = vpk_vec.into_boxed_slice(); + Box::into_raw(vpk_data_boxed) as *const u8 + } else { + ptr::null() + }; + + Self { + kind: FfiAccountIdentityKind::PrivatePdaForeign, + account_id: account_id.into(), + nullifier_public_key: npk.0.into(), + viewing_public_key: vpk_data, + viewing_public_key_len: vpk_len, + identifier: identifier.into(), + ..Default::default() + } + } + AccountIdentity::PrivateShared { + nsk, + npk, + vpk, + identifier, + } => { + let vpk_vec = vpk.to_bytes().to_vec(); + let vpk_len = vpk_vec.len(); + let vpk_data = if vpk_len > 0 { + let vpk_data_boxed = vpk_vec.into_boxed_slice(); + Box::into_raw(vpk_data_boxed) as *const u8 + } else { + ptr::null() + }; + + Self { + kind: FfiAccountIdentityKind::PrivateShared, + nullifier_secret_key: nsk.into(), + nullifier_public_key: npk.0.into(), + viewing_public_key: vpk_data, + viewing_public_key_len: vpk_len, + identifier: identifier.into(), + ..Default::default() + } + } + AccountIdentity::PrivatePdaShared { + account_id, + nsk, + npk, + vpk, + identifier, + } => { + let vpk_vec = vpk.to_bytes().to_vec(); + let vpk_len = vpk_vec.len(); + let vpk_data = if vpk_len > 0 { + let vpk_data_boxed = vpk_vec.into_boxed_slice(); + Box::into_raw(vpk_data_boxed) as *const u8 + } else { + ptr::null() + }; + + Self { + kind: FfiAccountIdentityKind::PrivatePdaShared, + account_id: account_id.into(), + nullifier_secret_key: nsk.into(), + nullifier_public_key: npk.0.into(), + viewing_public_key: vpk_data, + viewing_public_key_len: vpk_len, + identifier: identifier.into(), + ..Default::default() + } + } + } + } +} + +impl TryFrom<&FfiAccountIdentity> for AccountIdentity { + type Error = WalletFfiError; + + #[expect( + clippy::map_err_ignore, + reason = "`WalletFfiError` must be a trivial enum for FFI" + )] + fn try_from(value: &FfiAccountIdentity) -> Result { + match value.kind { + FfiAccountIdentityKind::Public => Ok(Self::Public(value.account_id.into())), + FfiAccountIdentityKind::PublicNoSign => Ok(Self::PublicNoSign(value.account_id.into())), + FfiAccountIdentityKind::PublicKeycard => { + let key_path = unsafe { CString::from_raw(value.key_path) } + .to_str()? + .to_owned(); + Ok(Self::PublicKeycard { + account_id: value.account_id.into(), + key_path, + }) + } + FfiAccountIdentityKind::PrivateOwned => Ok(Self::PrivateOwned(value.account_id.into())), + FfiAccountIdentityKind::PrivateForeign => { + let vpk = if value.viewing_public_key_len == 1184 { + let slice = unsafe { + slice::from_raw_parts( + value.viewing_public_key, + value.viewing_public_key_len, + ) + }; + Ok(MlKem768EncapsulationKey::from_bytes(slice.to_vec()) + .map_err(|_| WalletFfiError::InvalidKeyValue)?) + } else { + Err(WalletFfiError::InvalidKeyValue) + }?; + + Ok(Self::PrivateForeign { + npk: NullifierPublicKey(value.nullifier_public_key.data), + vpk, + identifier: value.identifier.into(), + }) + } + FfiAccountIdentityKind::PrivatePdaOwned => { + Ok(Self::PrivatePdaOwned(value.account_id.into())) + } + FfiAccountIdentityKind::PrivatePdaForeign => { + let vpk = if value.viewing_public_key_len == 1184 { + let slice = unsafe { + slice::from_raw_parts( + value.viewing_public_key, + value.viewing_public_key_len, + ) + }; + Ok(MlKem768EncapsulationKey::from_bytes(slice.to_vec()) + .map_err(|_| WalletFfiError::InvalidKeyValue)?) + } else { + Err(WalletFfiError::InvalidKeyValue) + }?; + + Ok(Self::PrivatePdaForeign { + account_id: value.account_id.into(), + npk: NullifierPublicKey(value.nullifier_public_key.data), + vpk, + identifier: value.identifier.into(), + }) + } + FfiAccountIdentityKind::PrivateShared => { + let vpk = if value.viewing_public_key_len == 1184 { + let slice = unsafe { + slice::from_raw_parts( + value.viewing_public_key, + value.viewing_public_key_len, + ) + }; + Ok(MlKem768EncapsulationKey::from_bytes(slice.to_vec()) + .map_err(|_| WalletFfiError::InvalidKeyValue)?) + } else { + Err(WalletFfiError::InvalidKeyValue) + }?; + + Ok(Self::PrivateShared { + nsk: value.nullifier_secret_key.data, + npk: NullifierPublicKey(value.nullifier_public_key.data), + vpk, + identifier: value.identifier.into(), + }) + } + FfiAccountIdentityKind::PrivatePdaShared => { + let vpk = if value.viewing_public_key_len == 1184 { + let slice = unsafe { + slice::from_raw_parts( + value.viewing_public_key, + value.viewing_public_key_len, + ) + }; + Ok(MlKem768EncapsulationKey::from_bytes(slice.to_vec()) + .map_err(|_| WalletFfiError::InvalidKeyValue)?) + } else { + Err(WalletFfiError::InvalidKeyValue) + }?; + + Ok(Self::PrivatePdaShared { + account_id: value.account_id.into(), + nsk: value.nullifier_secret_key.data, + npk: NullifierPublicKey(value.nullifier_public_key.data), + vpk, + identifier: value.identifier.into(), + }) + } + } + } +} + +#[cfg(test)] +mod tests { + use lee::{AccountId, PrivateKey, PublicKey}; + use lee_core::{encryption::ViewingPublicKey, program::PdaSeed, PrivateAccountKind}; + use wallet::AccountIdentity; + + use crate::{FfiAccountIdentity, FfiAccountIdentityKind}; + + #[test] + fn account_identity_roundtrip() { + let private_key = PrivateKey::try_new([42; 32]).unwrap(); + let public_key = PublicKey::new_from_private_key(&private_key); + let pub_acc_id = (&public_key).into(); + + let nsk = [43; 32]; + let vpk = ViewingPublicKey::from_seed(&[44; 32], &[54; 32]); + let npk = (&nsk).into(); + let identifier = u128::from_le_bytes([45; 16]); + + let private_reg_acc_id = + AccountId::for_private_account(&npk, &PrivateAccountKind::Regular(identifier)); + let private_pda_acc_id = AccountId::for_private_account( + &npk, + &PrivateAccountKind::Pda { + program_id: [46; 8], + seed: PdaSeed::new([47; 32]), + identifier, + }, + ); + + let acc_identity_1 = AccountIdentity::Public(pub_acc_id); + let acc_identity_2 = AccountIdentity::PublicNoSign(pub_acc_id); + + let acc_identity_2_5 = AccountIdentity::PublicKeycard { + account_id: pub_acc_id, + key_path: "path/to/key".to_owned(), + }; + + let acc_identity_3 = AccountIdentity::PrivateOwned(private_reg_acc_id); + let acc_identity_4 = AccountIdentity::PrivateForeign { + npk, + vpk: vpk.clone(), + identifier, + }; + let acc_identity_5 = AccountIdentity::PrivatePdaOwned(private_pda_acc_id); + let acc_identity_6 = AccountIdentity::PrivatePdaForeign { + account_id: private_pda_acc_id, + npk, + vpk: vpk.clone(), + identifier, + }; + let acc_identity_7 = AccountIdentity::PrivateShared { + nsk, + npk, + vpk: vpk.clone(), + identifier, + }; + let acc_identity_8 = AccountIdentity::PrivatePdaShared { + account_id: private_pda_acc_id, + nsk, + npk, + vpk, + identifier, + }; + + let ffi_acc_identity_1: FfiAccountIdentity = acc_identity_1.clone().into(); + let ffi_acc_identity_2: FfiAccountIdentity = acc_identity_2.clone().into(); + let ffi_acc_identity_2_5: FfiAccountIdentity = acc_identity_2_5.clone().into(); + let ffi_acc_identity_3: FfiAccountIdentity = acc_identity_3.clone().into(); + let ffi_acc_identity_4: FfiAccountIdentity = acc_identity_4.clone().into(); + let ffi_acc_identity_5: FfiAccountIdentity = acc_identity_5.clone().into(); + let ffi_acc_identity_6: FfiAccountIdentity = acc_identity_6.clone().into(); + let ffi_acc_identity_7: FfiAccountIdentity = acc_identity_7.clone().into(); + let ffi_acc_identity_8: FfiAccountIdentity = acc_identity_8.clone().into(); + + assert_eq!(ffi_acc_identity_1.kind, FfiAccountIdentityKind::Public); + assert_eq!( + ffi_acc_identity_2.kind, + FfiAccountIdentityKind::PublicNoSign + ); + assert_eq!( + ffi_acc_identity_2_5.kind, + FfiAccountIdentityKind::PublicKeycard + ); + assert_eq!( + ffi_acc_identity_3.kind, + FfiAccountIdentityKind::PrivateOwned + ); + assert_eq!( + ffi_acc_identity_4.kind, + FfiAccountIdentityKind::PrivateForeign + ); + assert_eq!( + ffi_acc_identity_5.kind, + FfiAccountIdentityKind::PrivatePdaOwned + ); + assert_eq!( + ffi_acc_identity_6.kind, + FfiAccountIdentityKind::PrivatePdaForeign + ); + assert_eq!( + ffi_acc_identity_7.kind, + FfiAccountIdentityKind::PrivateShared + ); + assert_eq!( + ffi_acc_identity_8.kind, + FfiAccountIdentityKind::PrivatePdaShared + ); + + let acc_identity_res_1: AccountIdentity = (&ffi_acc_identity_1).try_into().unwrap(); + let acc_identity_res_2: AccountIdentity = (&ffi_acc_identity_2).try_into().unwrap(); + let acc_identity_res_2_5: AccountIdentity = (&ffi_acc_identity_2_5).try_into().unwrap(); + let acc_identity_res_3: AccountIdentity = (&ffi_acc_identity_3).try_into().unwrap(); + let acc_identity_res_4: AccountIdentity = (&ffi_acc_identity_4).try_into().unwrap(); + let acc_identity_res_5: AccountIdentity = (&ffi_acc_identity_5).try_into().unwrap(); + let acc_identity_res_6: AccountIdentity = (&ffi_acc_identity_6).try_into().unwrap(); + let acc_identity_res_7: AccountIdentity = (&ffi_acc_identity_7).try_into().unwrap(); + let acc_identity_res_8: AccountIdentity = (&ffi_acc_identity_8).try_into().unwrap(); + + assert_eq!(acc_identity_res_1, acc_identity_1); + assert_eq!(acc_identity_res_2, acc_identity_2); + assert_eq!(acc_identity_res_2_5, acc_identity_2_5); + assert_eq!(acc_identity_res_3, acc_identity_3); + assert_eq!(acc_identity_res_4, acc_identity_4); + assert_eq!(acc_identity_res_5, acc_identity_5); + assert_eq!(acc_identity_res_6, acc_identity_6); + assert_eq!(acc_identity_res_7, acc_identity_7); + assert_eq!(acc_identity_res_8, acc_identity_8); + } +} diff --git a/lez/wallet-ffi/src/wallet.rs b/lez/wallet-ffi/src/wallet.rs index 485e4841..f0f02afd 100644 --- a/lez/wallet-ffi/src/wallet.rs +++ b/lez/wallet-ffi/src/wallet.rs @@ -286,7 +286,7 @@ pub unsafe extern "C" fn wallet_ffi_restore_data( if res == WalletFfiError::Success { match block_on(execute_keys_restoration(&mut wallet, 10)) { - Ok(_) => WalletFfiError::Success, + Ok(()) => WalletFfiError::Success, Err(err) => { print_error(format!("Failed to restore wallet data: {err}")); WalletFfiError::StorageError diff --git a/lez/wallet-ffi/wallet_ffi.h b/lez/wallet-ffi/wallet_ffi.h index b20e81c1..27585a3e 100644 --- a/lez/wallet-ffi/wallet_ffi.h +++ b/lez/wallet-ffi/wallet_ffi.h @@ -103,12 +103,31 @@ typedef enum WalletFfiError { * Invalid Key value. */ INVALID_KEY_VALUE = 16, + /** + * Invalid program bytecode. + */ + INVALID_BYTECODE = 17, /** * Internal error (catch-all). */ INTERNAL_ERROR = 99, } WalletFfiError; +/** + * Enumeration to represent kinds of `FfiAccountIdentity`. + */ +typedef enum FfiAccountIdentityKind { + PUBLIC = 0, + PUBLIC_NO_SIGN = 1, + PUBLIC_KEYCARD = 2, + PRIVATE_OWNED = 3, + PRIVATE_FOREIGN = 4, + PRIVATE_PDA_OWNED = 5, + PRIVATE_PDA_FOREIGN = 6, + PRIVATE_SHARED = 7, + PRIVATE_PDA_SHARED = 8, +} FfiAccountIdentityKind; + /** * Opaque pointer to the Wallet instance. * @@ -200,6 +219,65 @@ typedef struct FfiAccount { struct FfiU128 nonce; } FfiAccount; +typedef struct FfiInstructionWords { + uint32_t *instruction_words; + uintptr_t instruction_words_size; + enum WalletFfiError error; +} FfiInstructionWords; + +/** + * Struct representing an account identity, given to `AccountManager` at intialization. + */ +typedef struct FfiAccountIdentity { + enum FfiAccountIdentityKind kind; + struct FfiBytes32 account_id; + /** + * C-compatible string. + */ + char *key_path; + struct FfiBytes32 nullifier_secret_key; + struct FfiBytes32 nullifier_public_key; + const uint8_t *viewing_public_key; + uintptr_t viewing_public_key_len; + struct FfiU128 identifier; +} FfiAccountIdentity; + +/** + * Intended to be created manually. + */ +typedef struct FfiProgram { + const uint8_t *elf_data; + uintptr_t elf_size; +} FfiProgram; + +/** + * Intended to be created manually. + */ +typedef struct FfiProgramWithDependencies { + struct FfiProgram program; + const struct FfiProgram *deps; + uintptr_t deps_size; +} FfiProgramWithDependencies; + +/** + * Result of a generic transaction operation. + */ +typedef struct FfiTransactionResult { + /** + * Transaction hash (null-terminated string, or null on failure). + */ + char *tx_hash; + /** + * Whether the transaction succeeded. + */ + bool success; + const struct FfiBytes32 *secrets_data; + /** + * Public transactions have 0 secrets. + */ + uintptr_t secrets_size; +} FfiTransactionResult; + /** * Public key info for a public account. */ @@ -462,6 +540,94 @@ enum WalletFfiError wallet_ffi_import_private_account(struct WalletHandle *handl const struct FfiU128 *identifier, const char *account_state_json); +/** + * Serialize sequence of bytes into RISC0 readable words. + * + * # Parameters + * - `input_instruction_data`: Valid pointer to a sequence of bytes + * - `input_instruction_data_size`: Size of `input_instruction_data` + * + * # Returns + * - `Success` on successful creation + * - Error code on failure + * + * # Safety + * - `input_instruction_data` must be a valid pointer + */ +struct FfiInstructionWords wallet_ffi_serialization_helper(const uint8_t *input_instruction_data, + uintptr_t input_instruction_data_size); + +/** + * Send generic public transaction. + * + * # Parameters + * - `handle`: Valid pointer to wallet handle + * - `account_identities`: Valid pointer to list of `FfiAccountIdentity` + * - `instruction_words`: Valid pointer to instruction words + * - `out_result`: Valid pointer to `FfiTransactionResult` + * + * # Returns + * - `Success` on successful creation + * - Error code on failure + * + * # Safety + * - `handle` must be a valid pointer + * - `account_identities` must be a valid pointer + * - `instruction_words` must be a valid pointer + * - `out_result` must be a valid pointer + */ +enum WalletFfiError wallet_ffi_send_generic_public_transaction(struct WalletHandle *handle, + const struct FfiAccountIdentity *account_identities, + uintptr_t account_identities_size, + const uint32_t *instruction_words, + uintptr_t instruction_words_size, + const struct FfiProgramWithDependencies *program_with_dependencies, + struct FfiTransactionResult *out_result); + +/** + * Send generic private transaction. + * + * # Parameters + * - `handle`: Valid pointer to wallet handle + * - `account_identities`: Valid pointer to list of `FfiAccountIdentity` + * - `instruction_words`: Valid pointer to instruction words + * - `out_result`: Valid pointer to `FfiTransactionResult` + * + * # Returns + * - `Success` on successful creation + * - Error code on failure + * + * # Safety + * - `handle` must be a valid pointer + * - `account_identities` must be a valid pointer + * - `instruction_words` must be a valid pointer + * - `out_result` must be a valid pointer + */ +enum WalletFfiError wallet_ffi_send_generic_private_transaction(struct WalletHandle *handle, + const struct FfiAccountIdentity *account_identities, + uintptr_t account_identities_size, + const uint32_t *instruction_words, + uintptr_t instruction_words_size, + const struct FfiProgramWithDependencies *program_with_dependencies, + struct FfiTransactionResult *out_result); + +/** + * Free a transaction result returned by `wallet_ffi_send_generic_public_transaction` or + * `wallet_ffi_send_generic_private_transaction`. + * + * # Safety + * The result must be either null or a valid result from a transaction function. + */ +void wallet_ffi_free_transaction_result(struct FfiTransactionResult *result); + +/** + * Free a instruction words returned by `wallet_ffi_serialization_helper`. + * + * # Safety + * The result must be either null or a valid result from a serialization helper function. + */ +void wallet_ffi_free_instruction_words(struct FfiInstructionWords *words); + /** * Get the public key for a public account. * @@ -560,6 +726,55 @@ char *wallet_ffi_account_id_to_base58(const struct FfiBytes32 *account_id); enum WalletFfiError wallet_ffi_account_id_from_base58(const char *base58_str, struct FfiBytes32 *out_account_id); +/** + * Resolve public account. + * + * # Parameters + * - `account_id`: 32 bytes of the public account ID + * - `needs_sign`: whether the account needs signing + * - `out_account_identity`: valid pointer, where output will be written + * + * # Returns + * - `Success` on successful retrieval + * + * # Safety + * - `out_account_identity` must be a valid pointer to a `FfiAccountIdentity` struct + */ +enum WalletFfiError wallet_ffi_resolve_public_account(struct FfiBytes32 account_id, + bool needs_sign, + struct FfiAccountIdentity *out_account_identity); + +/** + * Resolve private account. + * + * # Parameters + * - `handle`: Valid wallet handle + * - `account_id`: 32 bytes of the public account ID + * - `out_account_identity`: valid pointer, where output will be written + * + * # Returns + * - `Success` on successful retrieval + * - `InternalError` if failed to lock wallet + * - `AccountNotFound` if the account is not found + * + * # Safety + * - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open` + * - `out_account_identity` must be a valid pointer to a `FfiAccountIdentity` struct + */ +enum WalletFfiError wallet_ffi_resolve_private_account(struct WalletHandle *handle, + struct FfiBytes32 account_id, + struct FfiAccountIdentity *out_account_identity); + +/** + * Free account identity returned by `wallet_ffi_resolve_private_account` or + * `wallet_ffi_resolve_public_account`. + * + * # Safety + * The account must be either null or a valid account returned by + * `wallet_ffi_resolve_private_account` or `wallet_ffi_resolve_public_account`. + */ +void wallet_ffi_free_account_identity(struct FfiAccountIdentity *account_identity); + /** * Claim a pinata reward using a public transaction. * @@ -666,6 +881,122 @@ enum WalletFfiError wallet_ffi_claim_pinata_private_owned_not_initialized(struct const uint8_t (*solution)[16], struct FfiTransferResult *out_result); +/** + * Send a program deployment transaction. + * + * Publishes program for future use. + * + * # Parameters + * - `handle`: Valid wallet handle + * - `elf_data`: Valid pointer to elf data in bytes + * - `elf_size`: Size of elf data + * - `out_result`: Output pointer for transfer result + * + * # Returns + * - `Success` if deployment was submitted successfully + * - Error code on other failures + * + * # Memory + * The result must be freed with `wallet_ffi_free_transaction_result()`. + * + * # Safety + * - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open` + * - `elf_data` must be a valid pointer to elf data + * - `out_result` must be a valid pointer to a `FfiTransferResult` struct + */ +enum WalletFfiError wallet_ffi_program_deployment(struct WalletHandle *handle, + const uint8_t *elf_data, + uintptr_t elf_size, + struct FfiTransactionResult *out_result); + +/** + * Writes elf data of authenticated transfer program into buffer. + * + * WARNING: Result is not consisent and change between versions, use for testing purposes only. + * + * # Parameters + * - `ffi_program`: Valid pointer to `FfiProgram` + * + * # Returns + * - `Success` if deployment was submitted successfully + * - Error code on other failures + * + * # Memory + * - `FfiProgram` can be freed with corresponding `wallet_ffi_free_ffi_program` function + * + * # Safety + * - `ffi_program` must be a non-null pointer + */ +enum WalletFfiError wallet_ffi_transfer_elf(struct FfiProgram *ffi_program); + +/** + * Writes elf data of authenticated token program into buffer. + * + * WARNING: Result is not consisent and change between versions, use for testing purposes only. + * + * # Parameters + * - `ffi_program`: Valid pointer to `FfiProgram` + * + * # Returns + * - `Success` if deployment was submitted successfully + * - Error code on other failures + * + * # Memory + * - `FfiProgram` can be freed with corresponding `wallet_ffi_free_ffi_program` function + * + * # Safety + * - `ffi_program` must be a non-null pointer + */ +enum WalletFfiError wallet_ffi_token_elf(struct FfiProgram *ffi_program); + +/** + * Writes elf data of amm into buffer. + * + * WARNING: Result is not consisent and change between versions, use for testing purposes only. + * + * # Parameters + * - `ffi_program`: Valid pointer to `FfiProgram` + * + * # Returns + * - `Success` if deployment was submitted successfully + * - Error code on other failures + * + * # Memory + * - `FfiProgram` can be freed with corresponding `wallet_ffi_free_ffi_program` function + * + * # Safety + * - `ffi_program` must be a non-null pointer + */ +enum WalletFfiError wallet_ffi_amm_elf(struct FfiProgram *ffi_program); + +/** + * Writes elf data of ata into buffer. + * + * WARNING: Result is not consisent and change between versions, use for testing purposes only. + * + * # Parameters + * - `ffi_program`: Valid pointer to `FfiProgram` + * + * # Returns + * - `Success` if deployment was submitted successfully + * - Error code on other failures + * + * # Memory + * - `FfiProgram` can be freed with corresponding `wallet_ffi_free_ffi_program` function + * + * # Safety + * - `ffi_program` must be a non-null pointer + */ +enum WalletFfiError wallet_ffi_ata_elf(struct FfiProgram *ffi_program); + +/** + * Free a ffi program returned by functions `wallet_ffi_*_elf`. + * + * # Safety + * The result must be either null or a valid result from a elf getter function. + */ +void wallet_ffi_free_ffi_program(struct FfiProgram *ffi_program); + /** * Synchronize private accounts to a specific block. * diff --git a/lez/wallet/Cargo.toml b/lez/wallet/Cargo.toml index 40222715..e04b0d91 100644 --- a/lez/wallet/Cargo.toml +++ b/lez/wallet/Cargo.toml @@ -14,15 +14,18 @@ common.workspace = true authenticated_transfer_core.workspace = true key_protocol.workspace = true sequencer_service_rpc = { workspace = true, features = ["client"] } -token_core.workspace = true amm_core.workspace = true testnet_initial_state.workspace = true +token_core.workspace = true ata_core.workspace = true +vault_core.workspace = true +bridge_core.workspace = true +keycard_wallet.workspace = true + bip39.workspace = true pyo3.workspace = true rpassword = "7" zeroize.workspace = true -keycard_wallet.workspace = true anyhow.workspace = true thiserror.workspace = true diff --git a/lez/wallet/src/account_manager.rs b/lez/wallet/src/account_manager.rs index ffff57c6..ce9d1833 100644 --- a/lez/wallet/src/account_manager.rs +++ b/lez/wallet/src/account_manager.rs @@ -1,3 +1,5 @@ +use core::fmt; + use anyhow::Result; use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; use keycard_wallet::{KeycardWallet, python_path}; @@ -6,12 +8,12 @@ use lee_core::{ Identifier, InputAccountIdentity, MembershipProof, NullifierPublicKey, NullifierSecretKey, SharedSecretKey, account::{AccountWithMetadata, Nonce}, - encryption::{EphemeralPublicKey, ViewingPublicKey}, + encryption::{EncryptedAccountData, EphemeralPublicKey, ViewingPublicKey}, }; use crate::{ExecutionFailureKind, WalletCore}; -#[derive(Clone, Debug)] +#[derive(Clone, PartialEq, Eq)] pub enum AccountIdentity { Public(AccountId), /// A public account without signing. Would not try to sign, even if account is owned. @@ -58,6 +60,73 @@ pub enum AccountIdentity { }, } +impl fmt::Debug for AccountIdentity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Public(id) => f.debug_tuple("Public").field(id).finish(), + Self::PublicNoSign(id) => f.debug_tuple("PublicNoSign").field(id).finish(), + Self::PublicKeycard { + account_id, + key_path: _, + } => f + .debug_struct("PublicKeycard") + .field("account_id", account_id) + .field("key_path", &"") + .finish(), + Self::PrivateOwned(id) => f.debug_tuple("PrivateOwned").field(id).finish(), + Self::PrivateForeign { + npk, + vpk, + identifier, + } => f + .debug_struct("PrivateForeign") + .field("npk", npk) + .field("vpk", vpk) + .field("identifier", identifier) + .finish(), + Self::PrivatePdaOwned(id) => f.debug_tuple("PrivatePdaOwned").field(id).finish(), + Self::PrivatePdaForeign { + account_id, + npk, + vpk, + identifier, + } => f + .debug_struct("PrivatePdaForeign") + .field("account_id", account_id) + .field("npk", npk) + .field("vpk", vpk) + .field("identifier", identifier) + .finish(), + Self::PrivateShared { + npk, + vpk, + identifier, + .. + } => f + .debug_struct("PrivateShared") + .field("nsk", &"") + .field("npk", npk) + .field("vpk", vpk) + .field("identifier", identifier) + .finish(), + Self::PrivatePdaShared { + account_id, + npk, + vpk, + identifier, + .. + } => f + .debug_struct("PrivatePdaShared") + .field("account_id", account_id) + .field("nsk", &"") + .field("npk", npk) + .field("vpk", vpk) + .field("identifier", identifier) + .finish(), + } + } +} + impl AccountIdentity { #[must_use] /// Note: `PublicNoSign` still counts as public, the variant just suppresses the signing-key @@ -100,10 +169,7 @@ impl AccountIdentity { } pub struct PrivateAccountKeys { - pub npk: NullifierPublicKey, pub ssk: SharedSecretKey, - pub vpk: ViewingPublicKey, - pub epk: EphemeralPublicKey, } enum State { @@ -307,12 +373,7 @@ impl AccountManager { self.states .iter() .filter_map(|state| match state { - State::Private(pre) => Some(PrivateAccountKeys { - npk: pre.npk, - ssk: pre.ssk, - vpk: pre.vpk.clone(), - epk: pre.epk.clone(), - }), + State::Private(pre) => Some(PrivateAccountKeys { ssk: pre.ssk }), State::Public { .. } | State::PublicKeycard { .. } => None, }) .collect() @@ -329,6 +390,8 @@ impl AccountManager { State::Public { .. } | State::PublicKeycard { .. } => InputAccountIdentity::Public, State::Private(pre) if pre.is_pda => match (pre.nsk, pre.proof.clone()) { (Some(nsk), Some(membership_proof)) => InputAccountIdentity::PrivatePdaUpdate { + epk: pre.epk.clone(), + view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk), ssk: pre.ssk, nsk, membership_proof, @@ -336,6 +399,8 @@ impl AccountManager { seed: None, }, _ => InputAccountIdentity::PrivatePdaInit { + epk: pre.epk.clone(), + view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk), npk: pre.npk, ssk: pre.ssk, identifier: pre.identifier, @@ -345,6 +410,8 @@ impl AccountManager { State::Private(pre) => match (pre.nsk, pre.proof.clone()) { (Some(nsk), Some(membership_proof)) => { InputAccountIdentity::PrivateAuthorizedUpdate { + epk: pre.epk.clone(), + view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk), ssk: pre.ssk, nsk, membership_proof, @@ -352,11 +419,15 @@ impl AccountManager { } } (Some(nsk), None) => InputAccountIdentity::PrivateAuthorizedInit { + epk: pre.epk.clone(), + view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk), ssk: pre.ssk, nsk, identifier: pre.identifier, }, (None, _) => InputAccountIdentity::PrivateUnauthorized { + epk: pre.epk.clone(), + view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk), npk: pre.npk, ssk: pre.ssk, identifier: pre.identifier, @@ -410,7 +481,7 @@ impl AccountManager { .collect(); if let Some(pin) = self.pin.clone() { - pyo3::Python::with_gil(|py| -> pyo3::PyResult<()> { + pyo3::Python::attach(|py| -> pyo3::PyResult<()> { python_path::add_python_path(py)?; let wallet = KeycardWallet::new(py)?; wallet.connect(py, &pin)?; diff --git a/lez/wallet/src/cli/keycard.rs b/lez/wallet/src/cli/keycard.rs index 1dec07d9..a2eae73c 100644 --- a/lez/wallet/src/cli/keycard.rs +++ b/lez/wallet/src/cli/keycard.rs @@ -39,7 +39,7 @@ impl WalletSubcommand for KeycardSubcommand { ) -> Result { match self { Self::Available => { - Python::with_gil(|py| { + Python::attach(|py| { python_path::add_python_path(py) .expect("`wallet::keycard::available`: unable to setup python path"); @@ -61,7 +61,7 @@ impl WalletSubcommand for KeycardSubcommand { Self::Connect => { let pin = read_pin()?; - Python::with_gil(|py| { + Python::attach(|py| { python_path::add_python_path(py) .expect("`wallet::keycard::connect`: unable to setup python path"); @@ -81,7 +81,7 @@ impl WalletSubcommand for KeycardSubcommand { Self::Disconnect => { let pin = read_pin()?; - Python::with_gil(|py| { + Python::attach(|py| { python_path::add_python_path(py) .expect("`wallet::keycard::disconnect`: unable to setup python path"); @@ -105,7 +105,7 @@ impl WalletSubcommand for KeycardSubcommand { Self::Init => { let pin = read_pin()?; - Python::with_gil(|py| { + Python::attach(|py| { python_path::add_python_path(py) .expect("`wallet::keycard::init`: unable to setup python path"); @@ -128,7 +128,7 @@ impl WalletSubcommand for KeycardSubcommand { let pin = read_pin()?; let mnemonic = read_mnemonic()?; - Python::with_gil(|py| { + Python::attach(|py| { python_path::add_python_path(py) .expect("`wallet::keycard::load`: unable to setup python path"); diff --git a/lez/wallet/src/cli/mod.rs b/lez/wallet/src/cli/mod.rs index d5808000..76c69c8c 100644 --- a/lez/wallet/src/cli/mod.rs +++ b/lez/wallet/src/cli/mod.rs @@ -20,9 +20,9 @@ use crate::{ group::GroupSubcommand, keycard::KeycardSubcommand, programs::{ - amm::AmmProgramAgnosticSubcommand, ata::AtaSubcommand, + amm::AmmProgramAgnosticSubcommand, ata::AtaSubcommand, bridge::BridgeSubcommand, native_token_transfer::AuthTransferSubcommand, pinata::PinataProgramAgnosticSubcommand, - token::TokenProgramAgnosticSubcommand, + token::TokenProgramAgnosticSubcommand, vault::VaultSubcommand, }, }, storage::Storage, @@ -65,6 +65,12 @@ pub enum Command { /// Associated Token Account program interaction subcommand. #[command(subcommand)] Ata(AtaSubcommand), + /// Vault program interaction subcommand. + #[command(subcommand)] + Vault(VaultSubcommand), + /// Bridge program interaction subcommand. + #[command(subcommand)] + Bridge(BridgeSubcommand), /// Group key management (create, invite, join, derive keys). #[command(subcommand)] Group(GroupSubcommand), @@ -254,6 +260,10 @@ pub async fn execute_subcommand( Command::Token(token_subcommand) => token_subcommand.handle_subcommand(wallet_core).await?, Command::AMM(amm_subcommand) => amm_subcommand.handle_subcommand(wallet_core).await?, Command::Ata(ata_subcommand) => ata_subcommand.handle_subcommand(wallet_core).await?, + Command::Vault(vault_subcommand) => vault_subcommand.handle_subcommand(wallet_core).await?, + Command::Bridge(bridge_subcommand) => { + bridge_subcommand.handle_subcommand(wallet_core).await? + } Command::Group(group_subcommand) => group_subcommand.handle_subcommand(wallet_core).await?, Command::Keycard(keycard_subcommand) => { keycard_subcommand.handle_subcommand(wallet_core).await? diff --git a/lez/wallet/src/cli/programs/bridge.rs b/lez/wallet/src/cli/programs/bridge.rs new file mode 100644 index 00000000..8d8cf5ee --- /dev/null +++ b/lez/wallet/src/cli/programs/bridge.rs @@ -0,0 +1,64 @@ +use anyhow::{Context as _, Result}; +use clap::Subcommand; + +use crate::{ + WalletCore, + account::AccountIdWithPrivacy, + cli::{CliAccountMention, SubcommandReturnValue, WalletSubcommand}, + program_facades::bridge::Bridge, +}; + +/// Represents generic CLI subcommand for a wallet working with bridge program. +#[derive(Subcommand, Debug, Clone)] +pub enum BridgeSubcommand { + /// Withdraw native tokens from a public account to Bedrock through the bridge. + Withdraw { + /// Sender account mention - account id with privacy prefix or a label. + #[arg(long)] + from: CliAccountMention, + /// Amount of native tokens to withdraw. + #[arg(long)] + amount: u64, + /// Bedrock account public key encoded as a 32-byte hex string. + #[arg(long)] + bedrock_account_pk: String, + }, +} + +impl WalletSubcommand for BridgeSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + Self::Withdraw { + from, + amount, + bedrock_account_pk, + } => { + let from = from.resolve(wallet_core.storage())?; + let AccountIdWithPrivacy::Public(sender_account_id) = from else { + anyhow::bail!("Bridge withdraw supports only public sender accounts"); + }; + + let bedrock_account_pk = parse_bedrock_account_pk(&bedrock_account_pk)?; + + let tx_hash = Bridge(wallet_core) + .send_withdraw(sender_account_id, amount, bedrock_account_pk) + .await?; + + println!("Transaction hash is {tx_hash}"); + + Ok(SubcommandReturnValue::Empty) + } + } + } +} + +fn parse_bedrock_account_pk(raw: &str) -> Result<[u8; 32]> { + let raw = raw.strip_prefix("0x").unwrap_or(raw); + let mut bedrock_account_pk = [0_u8; 32]; + hex::decode_to_slice(raw, &mut bedrock_account_pk) + .context("Invalid `bedrock-account-pk`: expected hex string of 32 bytes")?; + Ok(bedrock_account_pk) +} diff --git a/lez/wallet/src/cli/programs/mod.rs b/lez/wallet/src/cli/programs/mod.rs index f6e4b5dc..bc8abaf2 100644 --- a/lez/wallet/src/cli/programs/mod.rs +++ b/lez/wallet/src/cli/programs/mod.rs @@ -1,5 +1,7 @@ pub mod amm; pub mod ata; +pub mod bridge; pub mod native_token_transfer; pub mod pinata; pub mod token; +pub mod vault; diff --git a/lez/wallet/src/cli/programs/vault.rs b/lez/wallet/src/cli/programs/vault.rs new file mode 100644 index 00000000..777cc25b --- /dev/null +++ b/lez/wallet/src/cli/programs/vault.rs @@ -0,0 +1,140 @@ +use anyhow::Result; +use clap::Subcommand; +use common::transaction::LeeTransaction; +use lee::AccountId; + +use crate::{ + AccDecodeData::Decode, + WalletCore, + account::AccountIdWithPrivacy, + cli::{CliAccountMention, SubcommandReturnValue, WalletSubcommand}, + program_facades::vault::Vault, +}; + +/// Represents generic CLI subcommand for a wallet working with vault program. +#[derive(Subcommand, Debug, Clone)] +pub enum VaultSubcommand { + /// Transfer native tokens from sender to recipient's vault account. + Transfer { + /// Either 32 byte base58 account id string with privacy prefix or a label. + #[arg(long)] + from: CliAccountMention, + /// Either 32 byte base58 account id string with privacy prefix or a label. + #[arg(long)] + to: CliAccountMention, + /// Amount of native tokens to transfer. + #[arg(long)] + amount: u128, + }, + /// Claim native tokens from account's vault account. + Claim { + /// Either 32 byte base58 account id string with privacy prefix or a label. + /// + /// The owner's vault account id is computed from this account id. + #[arg(long)] + account_id: CliAccountMention, + /// Amount of native tokens to claim. + #[arg(long)] + amount: u128, + }, +} + +impl WalletSubcommand for VaultSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + Self::Transfer { from, to, amount } => { + let from = from.resolve(wallet_core.storage())?; + let recipient = to.resolve(wallet_core.storage())?; + let recipient_id = account_id_without_privacy(recipient); + + match from { + AccountIdWithPrivacy::Public(sender_id) => { + let tx_hash = Vault(wallet_core) + .send_transfer(sender_id, recipient_id, amount) + .await?; + + println!("Transaction hash is {tx_hash}"); + + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + + println!("Transaction data is {transfer_tx:?}"); + + Ok(SubcommandReturnValue::Empty) + } + AccountIdWithPrivacy::Private(sender_id) => { + let (tx_hash, secret_sender) = Vault(wallet_core) + .send_transfer_private_sender(sender_id, recipient_id, amount) + .await?; + + println!("Transaction hash is {tx_hash}"); + + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + + println!("Transaction data is {transfer_tx:?}"); + + if let LeeTransaction::PrivacyPreserving(tx) = transfer_tx { + wallet_core.decode_insert_privacy_preserving_transaction_results( + &tx, + &[Decode(secret_sender, sender_id)], + )?; + } + + wallet_core.store_persistent_data()?; + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } + } + } + Self::Claim { account_id, amount } => { + let account_id = account_id.resolve(wallet_core.storage())?; + + match account_id { + AccountIdWithPrivacy::Public(owner_id) => { + let tx_hash = Vault(wallet_core).send_claim(owner_id, amount).await?; + + println!("Transaction hash is {tx_hash}"); + + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + + println!("Transaction data is {transfer_tx:?}"); + + Ok(SubcommandReturnValue::Empty) + } + AccountIdWithPrivacy::Private(owner_id) => { + let (tx_hash, secret_owner) = Vault(wallet_core) + .send_claim_private_owner(owner_id, amount) + .await?; + + println!("Transaction hash is {tx_hash}"); + + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + + println!("Transaction data is {transfer_tx:?}"); + + if let LeeTransaction::PrivacyPreserving(tx) = transfer_tx { + wallet_core.decode_insert_privacy_preserving_transaction_results( + &tx, + &[Decode(secret_owner, owner_id)], + )?; + } + + wallet_core.store_persistent_data()?; + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } + } + } + } + } +} + +const fn account_id_without_privacy(account_id: AccountIdWithPrivacy) -> AccountId { + match account_id { + AccountIdWithPrivacy::Public(account_id) | AccountIdWithPrivacy::Private(account_id) => { + account_id + } + } +} diff --git a/lez/wallet/src/lib.rs b/lez/wallet/src/lib.rs index b4e52721..1850c33b 100644 --- a/lez/wallet/src/lib.rs +++ b/lez/wallet/src/lib.rs @@ -587,10 +587,6 @@ impl WalletCore { lee::privacy_preserving_transaction::message::Message::try_from_circuit_output( acc_manager.public_account_ids(), acc_manager.public_account_nonces(), - private_account_keys - .iter() - .map(|keys| (keys.npk, keys.vpk.clone(), keys.epk.clone())) - .collect(), output, )?; @@ -881,7 +877,7 @@ impl WalletCore { mod tests { use std::{ ffi::{CStr, CString}, - str::FromStr, + str::FromStr as _, }; use bip39::Mnemonic; @@ -896,7 +892,10 @@ mod tests { let boxed_mnemonic_string = Box::new(c_mnemonic_string.as_ptr()); let raw_mnemonic_string_pointer = Box::into_raw(boxed_mnemonic_string); - let c_str = unsafe { CStr::from_ptr(*raw_mnemonic_string_pointer) }; + // Safety: Will be safe, pointer is created from CString + let c_str_pointer = unsafe { *raw_mnemonic_string_pointer }; + // Safety: Will be safe, pointer is created from CString + let c_str = unsafe { CStr::from_ptr(c_str_pointer) }; let mn_string = c_str.to_str().unwrap(); let mn_ret = Mnemonic::from_str(mn_string).unwrap(); diff --git a/lez/wallet/src/program_facades/bridge.rs b/lez/wallet/src/program_facades/bridge.rs new file mode 100644 index 00000000..60ad9483 --- /dev/null +++ b/lez/wallet/src/program_facades/bridge.rs @@ -0,0 +1,35 @@ +use common::HashType; +use lee::{AccountId, program::Program}; + +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; + +pub struct Bridge<'wallet>(pub &'wallet WalletCore); + +impl Bridge<'_> { + pub async fn send_withdraw( + &self, + sender_account_id: AccountId, + amount: u64, + bedrock_account_pk: [u8; 32], + ) -> Result { + let program = Program::bridge(); + let bridge_account_id = lee::system_bridge_account_id(); + let instruction = bridge_core::Instruction::Withdraw { + amount, + bedrock_account_pk, + }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); + + self.0 + .send_pub_tx( + vec![ + AccountIdentity::Public(sender_account_id), + AccountIdentity::PublicNoSign(bridge_account_id), + ], + instruction_data, + &program.into(), + ) + .await + } +} diff --git a/lez/wallet/src/program_facades/mod.rs b/lez/wallet/src/program_facades/mod.rs index a0f8189c..a634e4ca 100644 --- a/lez/wallet/src/program_facades/mod.rs +++ b/lez/wallet/src/program_facades/mod.rs @@ -3,6 +3,8 @@ pub mod amm; pub mod ata; +pub mod bridge; pub mod native_token_transfer; pub mod pinata; pub mod token; +pub mod vault; diff --git a/lez/wallet/src/program_facades/vault.rs b/lez/wallet/src/program_facades/vault.rs new file mode 100644 index 00000000..bccee4f2 --- /dev/null +++ b/lez/wallet/src/program_facades/vault.rs @@ -0,0 +1,139 @@ +use std::collections::HashMap; + +use common::HashType; +use lee::{ + AccountId, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, +}; +use lee_core::SharedSecretKey; + +use crate::{AccountIdentity, ExecutionFailureKind, WalletCore}; + +pub struct Vault<'wallet>(pub &'wallet WalletCore); + +impl Vault<'_> { + pub async fn send_transfer( + &self, + sender_id: AccountId, + recipient_id: AccountId, + amount: u128, + ) -> Result { + let program = Program::vault(); + let vault_program_id = program.id(); + let recipient_vault_id = + vault_core::compute_vault_account_id(vault_program_id, recipient_id); + + let instruction = vault_core::Instruction::Transfer { + recipient_id, + amount, + }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); + + self.0 + .send_pub_tx( + vec![ + AccountIdentity::Public(sender_id), + AccountIdentity::PublicNoSign(recipient_vault_id), + ], + instruction_data, + &program.into(), + ) + .await + } + + pub async fn send_transfer_private_sender( + &self, + sender_id: AccountId, + recipient_id: AccountId, + amount: u128, + ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { + let vault_program_id = Program::vault().id(); + let recipient_vault_id = + vault_core::compute_vault_account_id(vault_program_id, recipient_id); + let instruction = vault_core::Instruction::Transfer { + recipient_id, + amount, + }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); + + self.0 + .send_privacy_preserving_tx( + vec![ + self.0 + .resolve_private_account(sender_id) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?, + AccountIdentity::Public(recipient_vault_id), + ], + instruction_data, + &vault_with_auth_dependency(), + ) + .await + .map(|(hash, mut secrets)| { + let secret = secrets.pop().expect("expected sender's secret"); + (hash, secret) + }) + } + + pub async fn send_claim( + &self, + owner_id: AccountId, + amount: u128, + ) -> Result { + let program = Program::vault(); + let vault_program_id = program.id(); + let owner_vault_id = vault_core::compute_vault_account_id(vault_program_id, owner_id); + + let instruction = vault_core::Instruction::Claim { amount }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); + + self.0 + .send_pub_tx( + vec![ + AccountIdentity::Public(owner_id), + AccountIdentity::PublicNoSign(owner_vault_id), + ], + instruction_data, + &program.into(), + ) + .await + } + + pub async fn send_claim_private_owner( + &self, + owner_id: AccountId, + amount: u128, + ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { + let vault_program_id = Program::vault().id(); + let owner_vault_id = vault_core::compute_vault_account_id(vault_program_id, owner_id); + + let instruction = vault_core::Instruction::Claim { amount }; + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); + + self.0 + .send_privacy_preserving_tx( + vec![ + self.0 + .resolve_private_account(owner_id) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?, + AccountIdentity::Public(owner_vault_id), + ], + instruction_data, + &vault_with_auth_dependency(), + ) + .await + .map(|(hash, mut secrets)| { + let secret = secrets.pop().expect("expected owner's secret"); + (hash, secret) + }) + } +} + +fn vault_with_auth_dependency() -> ProgramWithDependencies { + let auth_transfer = Program::authenticated_transfer_program(); + let mut deps = HashMap::new(); + deps.insert(auth_transfer.id(), auth_transfer); + ProgramWithDependencies::new(Program::vault(), deps) +} diff --git a/program_methods/guest/src/bin/bridge.rs b/program_methods/guest/src/bin/bridge.rs index eb082c7c..19d6509c 100644 --- a/program_methods/guest/src/bin/bridge.rs +++ b/program_methods/guest/src/bin/bridge.rs @@ -63,12 +63,40 @@ fn main() { vec![bridge_for_vault, recipient_vault], &vault_core::Instruction::Transfer { recipient_id, - amount, + amount: u128::from(amount), }, ) .with_pda_seeds(vec![bridge_core::compute_bridge_seed()]), ] } + Instruction::Withdraw { + amount, + bedrock_account_pk: _, + } => { + let [sender, bridge] = pre_states + .try_into() + .expect("Withdraw requires exactly 2 accounts"); + + assert_eq!( + bridge.account_id, + bridge_core::compute_bridge_account_id(self_program_id), + "Second account must be bridge PDA" + ); + + let auth_transfer_program_id = bridge.account.program_owner; + assert_eq!( + sender.account.program_owner, auth_transfer_program_id, + "Sender account must be owned by the authenticated transfer program" + ); + + vec![ChainedCall::new( + auth_transfer_program_id, + vec![sender, bridge], + &authenticated_transfer_core::Instruction::Transfer { + amount: u128::from(amount), + }, + )] + } }; ProgramOutput::new( diff --git a/program_methods/guest/src/bin/privacy_preserving_circuit/output.rs b/program_methods/guest/src/bin/privacy_preserving_circuit/output.rs index 621a461c..8c8ec2a4 100644 --- a/program_methods/guest/src/bin/privacy_preserving_circuit/output.rs +++ b/program_methods/guest/src/bin/privacy_preserving_circuit/output.rs @@ -1,7 +1,7 @@ use lee_core::{ - Commitment, CommitmentSetDigest, DUMMY_COMMITMENT_HASH, EncryptionScheme, InputAccountIdentity, - MembershipProof, Nullifier, NullifierPublicKey, NullifierSecretKey, - PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey, + Commitment, CommitmentSetDigest, DUMMY_COMMITMENT_HASH, EncryptedAccountData, EncryptionScheme, + EphemeralPublicKey, InputAccountIdentity, MembershipProof, Nullifier, NullifierPublicKey, + NullifierSecretKey, PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey, account::{Account, AccountId, Nonce}, compute_digest_for_path, }; @@ -17,7 +17,7 @@ pub fn compute_circuit_output( let mut output = PrivacyPreservingCircuitOutput { public_pre_states: Vec::new(), public_post_states: Vec::new(), - ciphertexts: Vec::new(), + encrypted_private_post_states: Vec::new(), new_commitments: Vec::new(), new_nullifiers: Vec::new(), block_validity_window, @@ -40,6 +40,8 @@ pub fn compute_circuit_output( output.public_post_states.push(post_state); } InputAccountIdentity::PrivateAuthorizedInit { + epk, + view_tag, ssk, nsk, identifier, @@ -71,11 +73,15 @@ pub fn compute_circuit_output( &account_id, &PrivateAccountKind::Regular(*identifier), ssk, + epk, + *view_tag, new_nullifier, new_nonce, ); } InputAccountIdentity::PrivateAuthorizedUpdate { + epk, + view_tag, ssk, nsk, membership_proof, @@ -105,11 +111,15 @@ pub fn compute_circuit_output( &account_id, &PrivateAccountKind::Regular(*identifier), ssk, + epk, + *view_tag, new_nullifier, new_nonce, ); } InputAccountIdentity::PrivateUnauthorized { + epk, + view_tag, npk, ssk, identifier, @@ -140,11 +150,15 @@ pub fn compute_circuit_output( &account_id, &PrivateAccountKind::Regular(*identifier), ssk, + epk, + *view_tag, new_nullifier, new_nonce, ); } InputAccountIdentity::PrivatePdaInit { + epk, + view_tag, npk: _, ssk, identifier, @@ -187,11 +201,15 @@ pub fn compute_circuit_output( identifier: *identifier, }, ssk, + epk, + *view_tag, new_nullifier, new_nonce, ); } InputAccountIdentity::PrivatePdaUpdate { + epk, + view_tag, ssk, nsk, membership_proof, @@ -231,6 +249,8 @@ pub fn compute_circuit_output( identifier: *identifier, }, ssk, + epk, + *view_tag, new_nullifier, new_nonce, ); @@ -243,7 +263,7 @@ pub fn compute_circuit_output( #[expect( clippy::too_many_arguments, - reason = "All seven inputs are distinct concerns from the variant arms; bundling would be artificial" + reason = "Inputs are distinct concerns from the variant arms; bundling would be artificial" )] fn emit_private_output( output: &mut PrivacyPreservingCircuitOutput, @@ -252,6 +272,8 @@ fn emit_private_output( account_id: &AccountId, kind: &PrivateAccountKind, shared_secret: &SharedSecretKey, + epk: &EphemeralPublicKey, + view_tag: u8, new_nullifier: (Nullifier, CommitmentSetDigest), new_nonce: Nonce, ) { @@ -270,7 +292,13 @@ fn emit_private_output( ); output.new_commitments.push(commitment_post); - output.ciphertexts.push(encrypted_account); + output + .encrypted_private_post_states + .push(EncryptedAccountData { + ciphertext: encrypted_account, + epk: epk.clone(), + view_tag, + }); *output_index = output_index .checked_add(1) .unwrap_or_else(|| panic!("Too many private accounts, output index overflow")); diff --git a/programs/bridge/core/src/lib.rs b/programs/bridge/core/src/lib.rs index 1e1bff4f..c9666f27 100644 --- a/programs/bridge/core/src/lib.rs +++ b/programs/bridge/core/src/lib.rs @@ -17,7 +17,20 @@ pub enum Instruction { l1_deposit_op_id: [u8; 32], vault_program_id: ProgramId, recipient_id: AccountId, - amount: u128, + amount: u64, + }, + + /// Transfers native tokens from a user account to the bridge PDA account. + /// + /// Required accounts (2): + /// - Sender account + /// - Bridge PDA account + /// + /// `bedrock_account_pk` is consumed by the Sequencer and is not used by the Bridge program + /// logic. + Withdraw { + amount: u64, + bedrock_account_pk: [u8; 32], }, } diff --git a/test_fixtures/Cargo.toml b/test_fixtures/Cargo.toml index 1585cd99..54cb6733 100644 --- a/test_fixtures/Cargo.toml +++ b/test_fixtures/Cargo.toml @@ -17,7 +17,6 @@ lee_core = { workspace = true, features = ["host"] } sequencer_core = { workspace = true, features = ["default", "testnet"] } sequencer_service.workspace = true sequencer_service_rpc = { workspace = true, features = ["client"] } -vault_core.workspace = true wallet.workspace = true anyhow.workspace = true diff --git a/test_fixtures/src/lib.rs b/test_fixtures/src/lib.rs index c632ba94..75394662 100644 --- a/test_fixtures/src/lib.rs +++ b/test_fixtures/src/lib.rs @@ -353,7 +353,7 @@ impl TestContextBuilder { ) .context("Failed to setup wallet")?; - setup_public_accounts_with_initial_supply(&wallet, &initial_public_accounts) + setup_public_accounts_with_initial_supply(&mut wallet, &initial_public_accounts) .await .context("Failed to initialize public accounts in wallet")?; diff --git a/test_fixtures/src/setup.rs b/test_fixtures/src/setup.rs index 597b0714..7eb0e1fd 100644 --- a/test_fixtures/src/setup.rs +++ b/test_fixtures/src/setup.rs @@ -1,19 +1,22 @@ -use std::{collections::HashMap, net::SocketAddr, path::PathBuf}; +use std::{net::SocketAddr, path::PathBuf}; use anyhow::{Context as _, Result, bail}; -use common::transaction::LeeTransaction; use indexer_service::IndexerHandle; -use lee::{AccountId, PrivateKey, PublicKey, PublicTransaction, program::Program}; +use lee::{AccountId, PrivateKey, PublicKey}; use log::{debug, warn}; use sequencer_service::{GenesisAction, SequencerHandle}; -use sequencer_service_rpc::RpcClient as _; use tempfile::TempDir; use testcontainers::compose::DockerCompose; -use wallet::{AccDecodeData::Decode, AccountIdentity, WalletCore, config::WalletConfigOverrides}; +use wallet::{ + WalletCore, + cli::{Command, SubcommandReturnValue, programs::vault::VaultSubcommand}, + config::WalletConfigOverrides, +}; use crate::{ BEDROCK_SERVICE_PORT, BEDROCK_SERVICE_WITH_OPEN_PORT, config::{self, InitialPrivateAccountForWallet}, + private_mention, public_mention, }; pub async fn setup_bedrock_node() -> Result<(DockerCompose, SocketAddr)> { @@ -185,7 +188,7 @@ pub fn setup_wallet( } pub async fn setup_public_accounts_with_initial_supply( - wallet: &WalletCore, + wallet: &mut WalletCore, initial_public_accounts: &[(PrivateKey, u128)], ) -> Result<()> { for (private_key, amount) in initial_public_accounts { @@ -219,45 +222,23 @@ pub async fn setup_private_accounts_with_initial_supply( } async fn claim_funds_from_vault( - wallet: &WalletCore, + wallet: &mut WalletCore, owner_id: AccountId, amount: u128, ) -> Result<()> { - let vault_program_id = Program::vault().id(); - let owner_vault_id = vault_core::compute_vault_account_id(vault_program_id, owner_id); - - let nonces = wallet - .get_accounts_nonces(vec![owner_id]) - .await - .context("Failed to fetch owner nonce")?; - - let signing_key = wallet - .storage() - .key_chain() - .pub_account_signing_key(owner_id) - .with_context(|| format!("Missing signing key for public account {owner_id}"))?; - - let message = lee::public_transaction::Message::try_new( - vault_program_id, - vec![owner_id, owner_vault_id], - nonces, - vault_core::Instruction::Claim { amount }, + let result = wallet::cli::execute_subcommand( + wallet, + Command::Vault(VaultSubcommand::Claim { + account_id: public_mention(owner_id), + amount, + }), ) - .context("Failed to build vault claim message")?; + .await + .context("Failed to execute public vault claim command")?; - let witness_set = lee::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - let tx = PublicTransaction::new(message, witness_set); - - let tx_hash = wallet - .sequencer_client - .send_transaction(LeeTransaction::Public(tx)) - .await - .context("Failed to submit vault claim transaction")?; - - wallet - .poll_native_token_transfer(tx_hash) - .await - .context("Failed to confirm vault claim transaction")?; + let SubcommandReturnValue::Empty = result else { + bail!("Expected Empty return value for public vault claim"); + }; Ok(()) } @@ -271,55 +252,19 @@ async fn claim_funds_from_vault_to_private( bail!("Missing private account in wallet key chain for account {owner_id}"); }; - let vault_program = Program::vault(); - let vault_program_id = vault_program.id(); - let owner_vault_id = vault_core::compute_vault_account_id(vault_program_id, owner_id); + let result = wallet::cli::execute_subcommand( + wallet, + Command::Vault(VaultSubcommand::Claim { + account_id: private_mention(owner_id), + amount, + }), + ) + .await + .context("Failed to execute private vault claim command")?; - let instruction_data = - Program::serialize_instruction(vault_core::Instruction::Claim { amount }) - .context("Failed to serialize vault private claim instruction")?; - - let program_with_dependencies = - lee::privacy_preserving_transaction::circuit::ProgramWithDependencies::new( - vault_program, - HashMap::from([( - Program::authenticated_transfer_program().id(), - Program::authenticated_transfer_program(), - )]), - ); - - let (tx_hash, mut secrets) = wallet - .send_privacy_preserving_tx( - vec![ - AccountIdentity::PrivateOwned(owner_id), - AccountIdentity::Public(owner_vault_id), - ], - instruction_data, - &program_with_dependencies, - ) - .await - .context("Failed to submit private vault claim transaction")?; - - let secret = secrets - .pop() - .context("Expected one private output secret for vault claim")?; - - let transfer_tx = wallet - .poll_native_token_transfer(tx_hash) - .await - .context("Failed to confirm private vault claim transaction")?; - - let LeeTransaction::PrivacyPreserving(tx) = transfer_tx else { - bail!("Expected privacy preserving transaction result for private vault claim"); + let SubcommandReturnValue::PrivacyPreservingTransfer { .. } = result else { + bail!("Expected PrivacyPreservingTransfer return value for private vault claim"); }; - wallet - .decode_insert_privacy_preserving_transaction_results(&tx, &[Decode(secret, owner_id)]) - .context("Failed to decode private vault claim transaction")?; - - wallet - .store_persistent_data() - .context("Failed to store wallet data after private vault claim")?; - Ok(()) } diff --git a/tools/cycle_bench/src/main.rs b/tools/cycle_bench/src/main.rs index 914d68c5..bed61f92 100644 --- a/tools/cycle_bench/src/main.rs +++ b/tools/cycle_bench/src/main.rs @@ -9,11 +9,14 @@ #![expect( clippy::arithmetic_side_effects, + clippy::as_conversions, + clippy::cast_precision_loss, clippy::float_arithmetic, clippy::missing_const_for_fn, clippy::non_ascii_literal, clippy::print_stderr, clippy::print_stdout, + clippy::suboptimal_flops, reason = "Bench tool: matches test-style fixture code" )] @@ -68,6 +71,13 @@ struct BenchResult { user_cycles: u64, segments: usize, exec_stats: Stats, + /// Compute-only execution time (ms): best-of-N executor wall-time minus the calibrated + /// host-side fixed per-call overhead. Filled after the calibration fit over all cases. + net_compute_ms: Option, + /// Deterministic model prediction of compute time (ms): `user_cycles * slope` from the + /// calibration fit. Pure function of the deterministic cycle count and the pinned-hardware + /// throughput, so it reproduces across re-runs where raw wall-time does not. + calibrated_ms: Option, /// Stats over prover.prove(env, elf) wall-clock samples. Only populated when --prove is set. /// Single-sample (n=1) when --prove is on without explicit repetition, since proving is slow. prove_stats: Option, @@ -81,6 +91,89 @@ struct BenchResult { prove_segments: Option, } +/// Linear calibration of executor wall-time against deterministic user cycles, +/// fitted across all standalone cases as `best_ms = intercept_ms + slope_ms_per_cycle * +/// user_cycles`. +/// +/// The intercept is the host-side fixed per-call cost (ELF parse, `ExecutorEnv` build) that is +/// outside the cycle count and does not scale with the instruction's work. The slope is the +/// per-cycle execution rate on the pinned box; its reciprocal is the throughput the tokenomics +/// fee model denominates public execution in, and is the public-side counterpart to the flat +/// `G_verify` verify cost. The intercept is an ELF-size-averaged constant, so `net_compute_ms` +/// is a first-order decomposition, not a mechanistic per-program overhead. +#[derive(Debug, Serialize, Clone, Copy)] +struct Calibration { + /// Cases the fit was computed over. + n: usize, + /// Slope: milliseconds of executor wall-time per user cycle. + slope_ms_per_cycle: f64, + /// Intercept: host-side fixed per-call overhead in milliseconds. + intercept_ms: f64, + /// Reciprocal of the slope: cycles executed per millisecond on the pinned box. + throughput_cycles_per_ms: f64, + /// Coefficient of determination of the fit (1.0 = perfect linear fit). + r2: f64, +} + +impl Calibration { + /// Ordinary least squares of `best_ms` (y) on `user_cycles` (x) across `results`. + /// The fit uses best-of-N rather than the mean so a single OS scheduling spike in one + /// case cannot tilt the slope; best-of-N is the per-case noise floor and reproduces + /// run-to-run, which is what a pinned-hardware throughput constant needs. + /// Returns `None` when there are fewer than two distinct cycle counts to fit a line. + fn fit(results: &[BenchResult]) -> Option { + let n = results.len(); + if n < 2 { + return None; + } + let xs: Vec = results.iter().map(|r| r.user_cycles as f64).collect(); + let ys: Vec = results.iter().map(|r| r.exec_stats.best_ms).collect(); + let nf = n as f64; + let sum_x: f64 = xs.iter().sum(); + let sum_y: f64 = ys.iter().sum(); + let sum_xy: f64 = xs.iter().zip(&ys).map(|(x, y)| x * y).sum(); + let sum_xx: f64 = xs.iter().map(|x| x * x).sum(); + let denom = nf * sum_xx - sum_x.powi(2); + if denom.abs() < f64::EPSILON { + return None; + } + let slope = (nf * sum_xy - sum_x * sum_y) / denom; + let intercept = (sum_y - slope * sum_x) / nf; + let mean_y = sum_y / nf; + let ss_tot: f64 = ys.iter().map(|y| (y - mean_y).powi(2)).sum(); + let ss_res: f64 = xs + .iter() + .zip(&ys) + .map(|(x, y)| (y - (intercept + slope * x)).powi(2)) + .sum(); + // ss_tot โ‰ˆ 0 means every best_ms is identical; the ratio is 0/0. We report 1.0 (a flat + // line fits a flat cloud exactly). This is a degenerate guard, not a real-data path: the + // bench cases span a wide cycle range, so ss_tot is large in practice. + let r2 = if ss_tot.abs() < f64::EPSILON { + 1.0 + } else { + 1.0 - ss_res / ss_tot + }; + let throughput_cycles_per_ms = if slope.abs() < f64::EPSILON { + 0.0 + } else { + 1.0 / slope + }; + Some(Self { + n, + slope_ms_per_cycle: slope, + intercept_ms: intercept, + throughput_cycles_per_ms, + r2, + }) + } + + /// Compute-time prediction for a cycle count: `slope * user_cycles` (overhead excluded). + fn calibrated_ms(&self, user_cycles: u64) -> f64 { + self.slope_ms_per_cycle * user_cycles as f64 + } +} + struct Case { program: &'static str, instruction_label: &'static str, @@ -185,6 +278,8 @@ impl Case { user_cycles: info.cycles(), segments: info.segments.len(), exec_stats, + net_compute_ms: None, + calibrated_ms: None, prove_stats, prove_total_cycles, prove_user_cycles, @@ -495,12 +590,23 @@ fn main() -> Result<()> { )?, ]; - let results: Vec = cases + let mut results: Vec = cases .into_iter() .map(|c| c.run(prove, exec_iters)) .collect::>>()?; + let calibration = Calibration::fit(&results); + if let Some(cal) = calibration { + for r in &mut results { + r.calibrated_ms = Some(cal.calibrated_ms(r.user_cycles)); + r.net_compute_ms = Some(r.exec_stats.best_ms - cal.intercept_ms); + } + } + print_table(&results, prove); + if let Some(cal) = calibration { + print_calibration(&cal); + } #[cfg(feature = "ppe")] let ppe_results = if cli.ppe { ppe::run_all() } else { Vec::new() }; @@ -525,6 +631,7 @@ fn main() -> Result<()> { } let combined = serde_json::json!({ "standalone": results, + "calibration": calibration, "ppe": ppe_results, }); std::fs::write(&out_path, serde_json::to_string_pretty(&combined)?)?; @@ -533,6 +640,24 @@ fn main() -> Result<()> { Ok(()) } +fn print_calibration(cal: &Calibration) { + println!("\npublic-execution ms calibration (pinned hardware):"); + println!( + " fit: best_ms = {:.4} + {:.3e} * user_cycles (n={}, Rยฒ={:.4})", + cal.intercept_ms, cal.slope_ms_per_cycle, cal.n, cal.r2, + ); + println!( + " throughput: {:.0} cycles/ms", + cal.throughput_cycles_per_ms, + ); + println!( + " fixed overhead: {:.3} ms host-side per call (ELF parse + env build, off-cycle)", + cal.intercept_ms, + ); + println!(" calib_ms = user_cycles / throughput (compute only, overhead excluded)"); + println!(" net_ms = best exec_ms - fixed overhead (measured compute, overhead stripped)"); +} + fn print_table(results: &[BenchResult], prove: bool) { let pw = results .iter() @@ -555,15 +680,28 @@ fn print_table(results: &[BenchResult], prove: bool) { .unwrap_or(0) .max("exec_ms (best / mean ยฑ stdev)".len()); + let dw = 10_usize; println!( - "{:cw$} {:>sw$} {:cw$} {:>sw$} {:dw$} {:>dw$}", + "program", + "instruction", + "user_cycles", + "segments", + "exec_ms (best / mean ยฑ stdev)", + "calib_ms", + "net_ms", ); - println!("{}", "-".repeat(pw + iw + cw + sw + exec_w + 8)); + println!("{}", "-".repeat(pw + iw + cw + sw + exec_w + 2 * dw + 12)); for r in results { + let calib = r + .calibrated_ms + .map_or_else(|| "-".to_owned(), |v| format!("{v:.2}")); + let net = r + .net_compute_ms + .map_or_else(|| "-".to_owned(), |v| format!("{v:.2}")); println!( - "{:cw$} {:>sw$} {:cw$} {:>sw$} {:dw$} {:>dw$}", + r.program, r.instruction, r.user_cycles, r.segments, r.exec_stats, calib, net, ); } @@ -595,3 +733,78 @@ fn print_table(results: &[BenchResult], prove: bool) { } } } + +#[cfg(test)] +mod tests { + use cycle_bench::stats::Stats; + + use super::{BenchResult, Calibration}; + + /// Minimal `BenchResult` carrying only the fields the calibration fit reads: + /// `user_cycles` (x) and `exec_stats.best_ms` (y). + fn point(user_cycles: u64, best_ms: f64) -> BenchResult { + BenchResult { + program: "test", + instruction: "test", + user_cycles, + segments: 1, + exec_stats: Stats::from_samples(&[best_ms]), + net_compute_ms: None, + calibrated_ms: None, + prove_stats: None, + prove_total_cycles: None, + prove_user_cycles: None, + prove_paging_cycles: None, + prove_segments: None, + } + } + + fn close(a: f64, b: f64) -> bool { + (a - b).abs() < 1e-9 + } + + #[test] + fn fit_recovers_a_known_line() { + // best_ms = 10 + 0.001 * user_cycles -> slope 1e-3, intercept 10, throughput 1000. + let results = [point(1000, 11.0), point(2000, 12.0), point(3000, 13.0)]; + let cal = Calibration::fit(&results).expect("fit over three points"); + + assert!( + close(cal.slope_ms_per_cycle, 0.001), + "slope {}", + cal.slope_ms_per_cycle + ); + assert!( + close(cal.intercept_ms, 10.0), + "intercept {}", + cal.intercept_ms + ); + assert!( + close(cal.throughput_cycles_per_ms, 1000.0), + "throughput {}", + cal.throughput_cycles_per_ms, + ); + assert!(close(cal.r2, 1.0), "r2 {}", cal.r2); + assert_eq!(cal.n, 3); + // calibrated_ms is the overhead-excluded compute prediction: slope * cycles. + assert!( + close(cal.calibrated_ms(2000), 2.0), + "calib {}", + cal.calibrated_ms(2000) + ); + } + + #[test] + fn fit_needs_at_least_two_points() { + assert!(Calibration::fit(&[]).is_none()); + assert!(Calibration::fit(&[point(1000, 11.0)]).is_none()); + } + + #[test] + fn fit_with_identical_cycle_counts_returns_none() { + // Zero spread in x leaves the slope undetermined; the fit must decline rather than divide + // by zero. + let results = [point(1000, 11.0), point(1000, 12.0)]; + assert!(Calibration::fit(&results).is_none()); + } +}