diff --git a/Cargo.lock b/Cargo.lock index 5b27b0ed..ad7d600c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "amm_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "borsh", "lee_core", @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "archery" @@ -197,7 +197,7 @@ checksum = "e7e89fe77d1f0f4fe5b96dfc940923d88d17b6a773808124f21e764dfb063c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -248,7 +248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -261,7 +261,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -344,7 +344,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -366,7 +366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -383,9 +383,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "asn1-rs" @@ -411,7 +411,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "synstructure", ] @@ -423,7 +423,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -435,7 +435,7 @@ checksum = "4858a9d740c5007a9069007c3b4e91152d0506f13c1b31dd49051fd537656156" [[package]] name = "associated_token_account_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "lee_core", "risc0-zkvm", @@ -512,7 +512,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -534,7 +534,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -545,7 +545,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -585,7 +585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" dependencies = [ "base64", - "http 1.4.0", + "http 1.4.2", "log", "url", ] @@ -593,7 +593,7 @@ dependencies = [ [[package]] name = "authenticated_transfer_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "serde", ] @@ -614,7 +614,7 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body", "http-body-util", "hyper", @@ -646,7 +646,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body", "http-body-util", "mime", @@ -689,6 +689,12 @@ dependencies = [ "match-lookup", ] +[[package]] +name = "base45" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240e56f4d3c453c36faacb695c535a4d5f8c7d23dac175014f32eb0a71012a03" + [[package]] name = "base58" version = "0.2.0" @@ -735,9 +741,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitcoin_hashes" -version = "0.14.1" +version = "0.14.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "hex-conservative", ] @@ -750,9 +756,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "blake2" @@ -780,9 +786,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ "hybrid-array", ] @@ -802,9 +808,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" dependencies = [ "borsh-derive", "bytes", @@ -813,21 +819,21 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" +checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "bridge_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "lee_core", "serde", @@ -845,7 +851,7 @@ dependencies = [ [[package]] name = "build_utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "anyhow", "risc0-binfmt", @@ -859,22 +865,22 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -885,27 +891,27 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" dependencies = [ "serde", ] [[package]] name = "bytesize" -version = "2.3.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" +checksum = "3d7c8918969267b2932ffd5655509bbbea0833823058c378876953217f5fc50e" dependencies = [ "serde_core", ] [[package]] name = "camino" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0" dependencies = [ "serde_core", ] @@ -935,9 +941,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.62" +version = "1.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" dependencies = [ "find-msvc-tools", "shlex", @@ -957,19 +963,20 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", "cipher 0.5.2", "cpufeatures 0.3.0", + "rand_core 0.10.1", ] [[package]] @@ -983,9 +990,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "num-traits", @@ -1009,16 +1016,16 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ - "block-buffer 0.12.0", + "block-buffer 0.12.1", "crypto-common 0.2.2", "inout 0.2.2", ] [[package]] name = "clap" -version = "4.6.1" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" dependencies = [ "clap_builder", "clap_derive", @@ -1026,9 +1033,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" dependencies = [ "anstream", "anstyle", @@ -1045,7 +1052,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1057,7 +1064,7 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "clock_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "borsh", "lee_core", @@ -1097,7 +1104,7 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "anyhow", "authenticated_transfer_core", @@ -1128,9 +1135,9 @@ dependencies = [ [[package]] name = "console" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" dependencies = [ "encode_unicode", "libc", @@ -1252,27 +1259,27 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crunchy" @@ -1309,7 +1316,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "hybrid-array", "rand_core 0.10.1", ] @@ -1357,7 +1364,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1391,7 +1398,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1404,7 +1411,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1415,7 +1422,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1426,7 +1433,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1452,7 +1459,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 2.0.117", + "syn 2.0.119", +] + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.18", ] [[package]] @@ -1468,9 +1506,9 @@ dependencies = [ [[package]] name = "der" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" dependencies = [ "const-oid 0.10.2", "zeroize", @@ -1496,7 +1534,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -1529,7 +1566,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1539,7 +1576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1561,7 +1598,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.119", "unicode-xid", ] @@ -1589,7 +1626,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "block-buffer 0.12.0", + "block-buffer 0.12.1", "crypto-common 0.2.2", ] @@ -1616,13 +1653,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1721,7 +1758,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1793,34 +1830,34 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "enum-ordinalize" -version = "4.3.2" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +checksum = "07f808d588c10e464ea6f7d3eaed500049eff30aaac103460f61828c2d65b3eb" dependencies = [ "enum-ordinalize-derive", ] [[package]] name = "enum-ordinalize-derive" -version = "4.3.2" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +checksum = "42e528e2d34ba8a67a1a650b86beae8ef69fc5fdb638016f386b973226590432" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "env_filter" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" dependencies = [ "log", "regex", @@ -1828,9 +1865,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" dependencies = [ "anstream", "anstyle", @@ -1885,7 +1922,7 @@ checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "faucet_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "lee_core", "serde", @@ -1974,7 +2011,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2068,7 +2105,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2134,9 +2171,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "1.4.1" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab9e9188e97a93276e1fe7b56401b851e2b45a46d045ca658100c1303ada649" +checksum = "ab4e5aa225bc56696909483320f0ff9b600f1a971b52e07a17d70f3d9b43254b" dependencies = [ "rustversion", "serde_core", @@ -2163,25 +2200,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", - "wasip2", - "wasip3", + "wasm-bindgen", ] [[package]] @@ -2204,7 +2239,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils", - "http 1.4.0", + "http 1.4.2", "js-sys", "pin-project", "serde", @@ -2253,16 +2288,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.4.0", + "http 1.4.2", "indexmap 2.14.0", "slab", "tokio", @@ -2376,7 +2411,7 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.4", + "rand 0.9.5", "socket2 0.5.10", "thiserror 2.0.18", "tinyvec", @@ -2398,7 +2433,7 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.4", + "rand 0.9.5", "resolv-conf", "smallvec", "thiserror 2.0.18", @@ -2454,9 +2489,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -2464,23 +2499,23 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", - "http 1.4.0", + "http 1.4.2", ] [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.4.2", "http-body", "pin-project-lite", ] @@ -2499,9 +2534,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" [[package]] name = "humantime-serde" @@ -2515,9 +2550,9 @@ dependencies = [ [[package]] name = "hybrid-array" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ "ctutils", "typenum", @@ -2525,16 +2560,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2", - "http 1.4.0", + "http 1.4.2", "http-body", "httparse", "httpdate", @@ -2551,7 +2586,7 @@ version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http 1.4.0", + "http 1.4.2", "hyper", "hyper-util", "log", @@ -2585,14 +2620,14 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body", "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.6.5", "tokio", "tower-service", "tracing", @@ -2704,12 +2739,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -2780,12 +2809,12 @@ dependencies = [ "attohttpc 0.24.1", "bytes", "futures", - "http 1.4.0", + "http 1.4.2", "http-body-util", "hyper", "hyper-util", "log", - "rand 0.8.6", + "rand 0.8.7", "tokio", "url", "xmltree", @@ -2801,12 +2830,12 @@ dependencies = [ "attohttpc 0.30.1", "bytes", "futures", - "http 1.4.0", + "http 1.4.2", "http-body-util", "hyper", "hyper-util", "log", - "rand 0.9.4", + "rand 0.9.5", "tokio", "url", "xmltree", @@ -2843,9 +2872,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.18.4" +version = "0.18.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" +checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c" dependencies = [ "console", "portable-atomic", @@ -2879,7 +2908,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ - "socket2 0.6.3", + "socket2 0.6.5", "widestring", "windows-registry", "windows-result", @@ -2958,10 +2987,11 @@ dependencies = [ [[package]] name = "jiff" -version = "0.2.25" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6835eea34fb6321b9b3aa7b685c2b433948c09447e389dc017fdf687d5d11e65" +checksum = "961d16382652bfdd8c6f68b223b26a8c93e0d475c672f414411db31c6c5c900e" dependencies = [ + "defmt", "jiff-static", "log", "portable-atomic", @@ -2971,13 +3001,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.25" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c22e04db9c58f5136eb1757f3d5c49a7b187f49e52185228cbd2f5acdfcc08c" +checksum = "d0879bd39df99c4c5e2c6615ccc026391a423dde10532c573e6086eb94a802cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -3021,18 +3051,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "js-sys" -version = "0.3.99" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -3062,7 +3091,7 @@ dependencies = [ "futures-channel", "futures-util", "gloo-net", - "http 1.4.0", + "http 1.4.2", "jsonrpsee-core", "pin-project", "rustls", @@ -3087,7 +3116,7 @@ dependencies = [ "bytes", "futures-timer", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body", "http-body-util", "jsonrpsee-types", @@ -3136,7 +3165,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -3145,7 +3174,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5" dependencies = [ - "http 1.4.0", + "http 1.4.2", "serde", "serde_json", "thiserror 2.0.18", @@ -3169,7 +3198,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" dependencies = [ - "http 1.4.0", + "http 1.4.2", "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", @@ -3224,7 +3253,7 @@ dependencies = [ [[package]] name = "key_protocol" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "aes-gcm", "anyhow", @@ -3237,7 +3266,7 @@ dependencies = [ "lee", "lee_core", "ml-kem", - "rand 0.8.6", + "rand 0.8.7", "serde", "sha2", "thiserror 2.0.18", @@ -3246,7 +3275,7 @@ dependencies = [ [[package]] name = "keycard_wallet" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "lee", "log", @@ -3276,7 +3305,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -3288,16 +3317,10 @@ dependencies = [ "spin", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "lee" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "anyhow", "borsh", @@ -3306,7 +3329,7 @@ dependencies = [ "k256", "lee_core", "log", - "rand 0.8.6", + "rand 0.8.7", "risc0-binfmt", "risc0-zkvm", "serde", @@ -3318,7 +3341,7 @@ dependencies = [ [[package]] name = "lee_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "base58", "borsh", @@ -3405,7 +3428,7 @@ dependencies = [ "libp2p-swarm", "quick-protobuf", "quick-protobuf-codec", - "rand 0.8.6", + "rand 0.8.7", "rand_core 0.6.4", "thiserror 2.0.18", "tracing", @@ -3440,7 +3463,7 @@ dependencies = [ "parking_lot", "pin-project", "quick-protobuf", - "rand 0.8.6", + "rand 0.8.7", "rw-stream-sink", "thiserror 2.0.18", "tracing", @@ -3488,7 +3511,7 @@ dependencies = [ "prometheus-client", "quick-protobuf", "quick-protobuf-codec", - "rand 0.8.6", + "rand 0.8.7", "regex", "serde", "sha2", @@ -3519,9 +3542,9 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3" +checksum = "9525f3831544f7ae497bde79adf114ef127b0fbbb97edbbf692a80408636421c" dependencies = [ "asn1_der", "bs58", @@ -3529,8 +3552,8 @@ dependencies = [ "hkdf", "k256", "multihash", - "quick-protobuf", - "rand 0.8.6", + "prost 0.14.4", + "rand 0.8.7", "serde", "sha2", "thiserror 2.0.18", @@ -3556,7 +3579,7 @@ dependencies = [ "libp2p-swarm", "quick-protobuf", "quick-protobuf-codec", - "rand 0.8.6", + "rand 0.8.7", "serde", "sha2", "smallvec", @@ -3578,7 +3601,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.6", + "rand 0.8.7", "smallvec", "socket2 0.5.10", "tokio", @@ -3616,7 +3639,7 @@ dependencies = [ "libp2p-identity", "libp2p-tls", "quinn", - "rand 0.8.6", + "rand 0.8.7", "ring", "rustls", "socket2 0.5.10", @@ -3637,7 +3660,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.6", + "rand 0.8.7", "smallvec", "tracing", ] @@ -3652,7 +3675,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.6", + "rand 0.8.7", "tracing", ] @@ -3672,7 +3695,7 @@ dependencies = [ "lru", "multistream-select", "once_cell", - "rand 0.8.6", + "rand 0.8.7", "smallvec", "tokio", "tracing", @@ -3688,7 +3711,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -3743,9 +3766,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "libc", ] @@ -3785,9 +3808,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.30" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "logos-blockchain-blend-crypto" @@ -3833,7 +3856,7 @@ version = "0.1.2" source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711bbc3d43d3ef9755ef9b73af32fd0f703160#d8711bbc3d43d3ef9755ef9b73af32fd0f703160" dependencies = [ "ed25519-dalek", - "generic-array 1.4.1", + "generic-array 1.4.4", "hex", "logos-blockchain-blend-crypto", "logos-blockchain-groth16", @@ -4052,7 +4075,7 @@ dependencies = [ "libp2p-stream", "logos-blockchain-core", "logos-blockchain-cryptarchia-engine", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_with", "thiserror 2.0.18", @@ -4070,7 +4093,7 @@ dependencies = [ "ark-ff", "ark-groth16", "ark-serialize", - "generic-array 1.4.1", + "generic-array 1.4.4", "hex", "num-bigint", "serde", @@ -4107,7 +4130,7 @@ dependencies = [ "async-trait", "bytes", "ed25519-dalek", - "generic-array 1.4.1", + "generic-array 1.4.4", "hex", "logos-blockchain-groth16", "logos-blockchain-key-management-system-macros", @@ -4133,7 +4156,7 @@ source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711 dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -4154,7 +4177,7 @@ dependencies = [ "logos-blockchain-utils", "logos-blockchain-utxotree", "num-bigint", - "rand 0.8.6", + "rand 0.8.7", "rpds", "serde", "serde_arrays", @@ -4182,7 +4205,7 @@ dependencies = [ "natpmp", "netdev", "num_enum", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_with", "thiserror 2.0.18", @@ -4206,7 +4229,7 @@ source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=d8711 dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -4235,7 +4258,7 @@ dependencies = [ "logos-blockchain-log-targets", "logos-blockchain-tracing", "overwatch", - "rand 0.8.6", + "rand 0.8.7", "rand_chacha 0.3.1", "serde", "tokio", @@ -4392,7 +4415,7 @@ dependencies = [ "opentelemetry-otlp", "opentelemetry-semantic-conventions", "opentelemetry_sdk", - "rand 0.8.6", + "rand 0.8.7", "serde", "tokio", "tonic", @@ -4417,7 +4440,7 @@ dependencies = [ "humantime", "logos-blockchain-log-targets", "overwatch", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_ignored", "serde_with", @@ -4502,7 +4525,7 @@ checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -4534,14 +4557,14 @@ checksum = "746873a384ad60adc5db74471dfaba74bd278afbdcfd81db93fafcdfc8b5ca0c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "merlin" @@ -4561,7 +4584,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", "block", "core-graphics-types", "foreign-types", @@ -4594,9 +4617,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", @@ -4666,12 +4689,13 @@ dependencies = [ [[package]] name = "multibase" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" +checksum = "7e0e4a371cbf1dfd666b658ba137763edb23c45beb43cfe369b5593cd6b437b6" dependencies = [ "base-x", "base256emoji", + "base45", "data-encoding", "data-encoding-macro", ] @@ -4769,7 +4793,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ce3636fa715e988114552619582b530481fd5ef176a1e5c1bf024077c2c9445" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", "libc", "log", "netlink-packet-core 0.8.1", @@ -4820,7 +4844,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", "cfg-if", "cfg_aliases", "libc", @@ -4862,9 +4886,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -4881,7 +4905,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.6", + "rand 0.8.7", "smallvec", "zeroize", ] @@ -4903,11 +4927,10 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -4940,7 +4963,7 @@ checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5018,7 +5041,7 @@ checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" dependencies = [ "async-trait", "bytes", - "http 1.4.0", + "http 1.4.2", "opentelemetry", "reqwest", ] @@ -5029,12 +5052,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" dependencies = [ - "http 1.4.0", + "http 1.4.2", "opentelemetry", "opentelemetry-http", "opentelemetry-proto", "opentelemetry_sdk", - "prost 0.14.3", + "prost 0.14.4", "reqwest", "thiserror 2.0.18", "tokio", @@ -5049,7 +5072,7 @@ checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" dependencies = [ "opentelemetry", "opentelemetry_sdk", - "prost 0.14.3", + "prost 0.14.4", "tonic", "tonic-prost", ] @@ -5071,7 +5094,7 @@ dependencies = [ "futures-util", "opentelemetry", "percent-encoding", - "rand 0.9.4", + "rand 0.9.5", "thiserror 2.0.18", "tokio", "tokio-stream", @@ -5085,7 +5108,7 @@ checksum = "969ccca8ffc4fb105bd131a228107d5c9dd89d9d627edf3295cbe979156f9712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5118,7 +5141,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5198,7 +5221,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5234,7 +5257,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" dependencies = [ - "der 0.8.0", + "der 0.8.1", "spki 0.8.0", ] @@ -5325,23 +5348,13 @@ dependencies = [ "yansi", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - [[package]] name = "proc-macro-crate" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", + "toml_edit 0.25.13+spec-1.1.0", ] [[package]] @@ -5378,6 +5391,16 @@ dependencies = [ "quote", ] +[[package]] +name = "proc-macro-error-attr3" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34e4dd828515431dd6c4a030d26f7eaed7dd4778226e9d2bb968d65ca4ec3d4d" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "proc-macro-error2" version = "2.0.1" @@ -5387,7 +5410,19 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", +] + +[[package]] +name = "proc-macro-error3" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee475e440453418ff1335189eddf7101ba502cd818ab7ae04209bc83aa925aa" +dependencies = [ + "proc-macro-error-attr3", + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -5407,14 +5442,14 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "version_check", ] [[package]] name = "programs" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "build_utils", "lee", @@ -5440,7 +5475,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5449,9 +5484,9 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_chacha 0.9.0", "rand_xorshift", "unarray", @@ -5469,12 +5504,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", - "prost-derive 0.14.3", + "prost-derive 0.14.4", ] [[package]] @@ -5487,20 +5522,20 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "prost-derive" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5554,7 +5589,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5566,7 +5601,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5593,9 +5628,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", "cfg_aliases", @@ -5605,7 +5640,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.3", + "socket2 0.6.5", "thiserror 2.0.18", "tokio", "tracing", @@ -5614,14 +5649,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.4", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash", "rustls", @@ -5635,23 +5671,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2 0.6.5", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -5670,9 +5706,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -5681,14 +5717,25 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -5733,6 +5780,15 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "rand_xorshift" version = "0.4.0" @@ -5761,7 +5817,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", ] [[package]] @@ -5792,14 +5848,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -5809,9 +5865,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -5820,9 +5876,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "reqwest" @@ -5836,7 +5892,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 1.4.0", + "http 1.4.2", "http-body", "http-body-util", "hyper", @@ -5910,7 +5966,7 @@ dependencies = [ "elf", "lazy_static", "postcard", - "rand 0.9.4", + "rand 0.9.5", "risc0-zkp", "risc0-zkvm-platform", "ruint", @@ -6155,7 +6211,7 @@ dependencies = [ "futures", "light-poseidon", "quote", - "rand 0.9.4", + "rand 0.9.5", "syn 1.0.109", "thiserror 2.0.18", "tiny-keccak", @@ -6212,14 +6268,14 @@ dependencies = [ [[package]] name = "ruint" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" +checksum = "45caf26f647c19115bf9c453c70ffe4a4a3a6390dceebd942610584f99b8ddce" dependencies = [ "borsh", "proptest", - "rand 0.8.6", - "rand 0.9.4", + "rand 0.8.7", + "rand 0.9.5", "ruint-macro", "serde_core", "valuable", @@ -6246,9 +6302,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -6274,7 +6330,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys", @@ -6283,9 +6339,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ "log", "once_cell", @@ -6298,9 +6354,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -6310,9 +6366,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "web-time", "zeroize", @@ -6358,9 +6414,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rw-stream-sink" @@ -6467,7 +6523,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -6503,7 +6559,7 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "sequencer_service_protocol" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "common", "hex", @@ -6515,7 +6571,7 @@ dependencies = [ [[package]] name = "sequencer_service_rpc" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "jsonrpsee", "sequencer_service_protocol", @@ -6557,7 +6613,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -6617,9 +6673,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64", "bs58", @@ -6637,14 +6693,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -6672,9 +6728,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -6723,9 +6779,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook-registry" @@ -6749,9 +6805,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "slab" @@ -6761,15 +6817,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "snap" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" +checksum = "199905e6153d6405f9728fe44daace35f8f837bbf830bb6e85fbd5828709a886" [[package]] name = "sntpc" @@ -6793,9 +6849,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -6812,7 +6868,7 @@ dependencies = [ "futures", "httparse", "log", - "rand 0.8.6", + "rand 0.8.7", "sha1", ] @@ -6832,6 +6888,7 @@ dependencies = [ "serde_json", "spel-framework-core", "tempfile", + "thiserror 1.0.69", "tokio", "toml", "wallet", @@ -6845,7 +6902,7 @@ dependencies = [ "serde", "serde_json", "spel-framework-core", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -6892,7 +6949,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.117", + "syn 2.0.119", "thiserror 1.0.69", "toml", ] @@ -6906,14 +6963,14 @@ dependencies = [ "serde_json", "sha2", "spel-framework-core", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" [[package]] name = "spki" @@ -6932,7 +6989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" dependencies = [ "base64ct", - "der 0.8.0", + "der 0.8.1", ] [[package]] @@ -6946,7 +7003,7 @@ dependencies = [ "digest 0.10.7", "hex", "keccak 0.1.6", - "rand 0.8.6", + "rand 0.8.7", "sha3 0.10.9", "thiserror 2.0.18", "zerocopy", @@ -6960,7 +7017,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -6999,7 +7056,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7027,9 +7084,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -7053,7 +7110,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7062,7 +7119,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -7073,7 +7130,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -7091,7 +7148,7 @@ dependencies = [ [[package]] name = "system_accounts" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "bridge_core", "clock_core", @@ -7130,7 +7187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -7139,7 +7196,7 @@ dependencies = [ [[package]] name = "testnet_initial_state" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "key_protocol", "lee", @@ -7147,6 +7204,7 @@ dependencies = [ "programs", "serde", "system_accounts", + "wrapped_token_core", ] [[package]] @@ -7175,7 +7233,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7186,26 +7244,25 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] [[package]] name = "time" -version = "0.3.47" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -7215,15 +7272,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", @@ -7250,9 +7307,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -7266,7 +7323,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "token_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "borsh", "lee_core", @@ -7275,9 +7332,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.3" +version = "1.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "317fafbbe3f02fc663dad00ea6186197de963cd4190e86a26d8d0fae095539af" dependencies = [ "bytes", "libc", @@ -7285,7 +7342,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2 0.6.5", "tokio-macros", "windows-sys 0.61.2", ] @@ -7298,7 +7355,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7383,14 +7440,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -7399,7 +7456,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -7417,7 +7474,7 @@ dependencies = [ "async-trait", "base64", "bytes", - "http 1.4.0", + "http 1.4.2", "http-body", "http-body-util", "hyper", @@ -7441,7 +7498,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" dependencies = [ "bytes", - "prost 0.14.3", + "prost 0.14.4", "tonic", ] @@ -7470,10 +7527,10 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.1", "bytes", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body", "pin-project-lite", "tower", @@ -7527,7 +7584,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7654,9 +7711,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" +checksum = "b40688ea6389c8171614b25491f71d4a27946e0c7ce2da1c6de27e25abf1a0ae" [[package]] name = "try-lock" @@ -7666,9 +7723,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uint" @@ -7705,9 +7762,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-width" @@ -7785,7 +7842,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ "base64", - "http 1.4.0", + "http 1.4.2", "httparse", "log", ] @@ -7842,16 +7899,16 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "uuid" -version = "1.23.4" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "wasm-bindgen", ] @@ -7874,16 +7931,15 @@ dependencies = [ [[package]] name = "validator_derive" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca" +checksum = "240e4b81c20a1d6d50d1d7265c658dfbd204e8b9ac4d80f3c931f39462196335" dependencies = [ - "darling 0.20.11", - "once_cell", - "proc-macro-error2", + "darling 0.23.0", + "proc-macro-error3", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7895,7 +7951,7 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vault_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "lee_core", "risc0-zkvm", @@ -7921,7 +7977,7 @@ dependencies = [ [[package]] name = "wallet" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0#a58fbce2ff48c58b7bb5001b1a27e64b9596ee3a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "amm_core", "anyhow", @@ -7949,7 +8005,7 @@ dependencies = [ "optfield", "programs", "pyo3", - "rand 0.8.6", + "rand 0.8.7", "rpassword", "sequencer_service_rpc", "serde", @@ -7982,27 +8038,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -8013,9 +8060,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.72" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -8023,9 +8070,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8033,48 +8080,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.14.0", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.4.2" @@ -8088,23 +8113,11 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.1", - "hashbrown 0.15.5", - "indexmap 2.14.0", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.99" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -8126,23 +8139,23 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.7", + "webpki-root-certs 1.0.8", ] [[package]] name = "webpki-root-certs" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" dependencies = [ "rustls-pki-types", ] @@ -8237,7 +8250,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -8248,7 +8261,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -8323,15 +8336,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -8365,30 +8369,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows-threading" version = "0.2.1" @@ -8410,12 +8397,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -8428,12 +8409,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -8446,24 +8421,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -8476,12 +8439,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -8494,12 +8451,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -8512,12 +8463,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -8530,12 +8475,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.7.15" @@ -8547,22 +8486,13 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" @@ -8570,82 +8500,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap 2.14.0", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.1", - "indexmap 2.14.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +name = "wrapped_token_core" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.14.0", - "log", - "semver", + "lee_core", + "risc0-zkvm", "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", ] [[package]] @@ -8736,9 +8597,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -8753,28 +8614,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -8794,28 +8655,28 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -8848,11 +8709,11 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/README.md b/README.md index b7353eeb..e33c8dd9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Developer framework for building SPEL programs — inspired by [Anchor](https://www.anchor-lang.com/) for Solana. -Write your program logic with proc macros. Get IDL generation, a full CLI with TX submission, and project scaffolding for free. +Write your program logic with proc macros. Get IDL generation, a full CLI with TX submission, a Wallet-ready transaction-resolution API, and project scaffolding for free. ## Quick Start @@ -158,6 +158,42 @@ This provides: - `--dry-run` mode for testing - `inspect` subcommand to extract ProgramId from binaries +### Transaction Resolution API + +Use `spel::tx` when an application owns `WalletCore` and needs validated inputs +for a direct Wallet build call. The resolvers select one IDL instruction, resolve +accounts and PDAs, and serialize its arguments in IDL declaration order. + +Argument text accepts canonical scalars and JSON containers. It also accepts +the established CLI forms for boolean aliases, options, arrays and vectors, +and program IDs, so applications can use the same values accepted by `spel`. +Canonical JSON remains the preferred representation for structured values. + +```rust +use std::collections::BTreeMap; + +use spel::tx::{resolve_public_instruction, SpelInstructionRequest}; + +// `idl`, `program_id`, and `wallet` are owned by the application. +let request = SpelInstructionRequest { + idl: &idl, + instruction: "initialize", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), +}; +let resolved = resolve_public_instruction(request, program_id)?; +let (program_id, accounts, instruction_data) = resolved.into_parts(); +let _build = wallet + .build_pub_tx(accounts, instruction_data, program_id) + .await?; +``` + +For private programs, use `resolve_private_instruction` with a +`ProgramWithDependencies`, then pass the resolved parts to +`WalletCore::build_privacy_preserving_tx`. Resolvers return `SpelTxError` for +invalid IDL or caller input. They do not initialize a wallet, build, prove, +sign, submit, poll, print, or change CLI behavior; WalletCore owns those steps. + ### Account Types Types that represent on-chain account data can be annotated with `#[account_type]`. This causes them to appear in the generated IDL so `spel inspect` can decode raw account bytes into readable JSON. diff --git a/scripts/ffi-call-test.sh b/scripts/ffi-call-test.sh index 34e1da98..e9a04507 100755 --- a/scripts/ffi-call-test.sh +++ b/scripts/ffi-call-test.sh @@ -73,19 +73,15 @@ for candidate in wallet "$HOME/bin/wallet" "$LSSA_DIR/target/release/wallet"; do done [ -n "$WALLET_BIN" ] || fail "wallet not found" -# LEZ v0.2.0 moved the debug configs under a lez/ subdirectory. Prefer the -# new location, fall back to the pre-rc6 path for older LEZ revisions. -if [ -z "${NSSA_WALLET_HOME_DIR:-}" ]; then +# Prefer current LEZ layout, fall back to older checkout layout. +if [ -z "${LEE_WALLET_HOME_DIR:-}" ]; then if [ -f "${LSSA_DIR}/lez/wallet/configs/debug/wallet_config.json" ]; then - NSSA_WALLET_HOME_DIR="${LSSA_DIR}/lez/wallet/configs/debug" + LEE_WALLET_HOME_DIR="${LSSA_DIR}/lez/wallet/configs/debug" else - NSSA_WALLET_HOME_DIR="${LSSA_DIR}/wallet/configs/debug" + LEE_WALLET_HOME_DIR="${LSSA_DIR}/wallet/configs/debug" fi fi -export NSSA_WALLET_HOME_DIR -# LEZ v0.2.0 renamed the wallet home env var to LEE_WALLET_HOME_DIR. -# Export both so the wallet finds its config on old and new LEZ revisions. -export LEE_WALLET_HOME_DIR="$NSSA_WALLET_HOME_DIR" +export LEE_WALLET_HOME_DIR WALLET_PASSWORD="${WALLET_PASSWORD:-test}" # Determine SPEL ref for testing (PR head or commit SHA) @@ -111,7 +107,7 @@ CLIENT_GEN_BIN="$SPEL_DIR/target/release/spel-client-gen" # ─── Step 1: Scaffold project ────────────────────────────────────────────── log "Step 1: Creating SPEL project (LEZ=${LEZ_TAG})..." -"$SPEL_BIN" init --lez-tag "$LEZ_TAG" --spel-rev "$SPEL_TAG" "$PROJECT_NAME" 2>&1 | tee "$WORK_DIR/init.log" || { echo ''; echo '=== INIT LOG ==='; cat "$WORK_DIR/init.log"; echo '================='; fail "spel init failed"; } +"$SPEL_BIN" init --lez-rev "$LEZ_TAG" --spel-rev "$SPEL_TAG" "$PROJECT_NAME" 2>&1 | tee "$WORK_DIR/init.log" || { echo ''; echo '=== INIT LOG ==='; cat "$WORK_DIR/init.log"; echo '================='; fail "spel init failed"; } cd "$PROJECT_NAME" # Regenerate lockfiles so the patch takes effect @@ -198,7 +194,7 @@ cd "$WORK_DIR/$PROJECT_NAME" log " Waiting for sequencer..." for i in $(seq 1 60); do - if curl -sf -o /dev/null -w '%{http_code}' "$SEQUENCER_URL" 2>/dev/null | grep -qE '200|405'; then + if curl -s -o /dev/null -w '%{http_code}' "$SEQUENCER_URL" 2>/dev/null | grep -qE '200|405'; then log " ✓ Sequencer up"; break fi kill -0 "$SEQ_PID" 2>/dev/null || fail "Sequencer died" @@ -221,7 +217,7 @@ done # ─── Step 5: Update wallet config for correct port ──────────────────────── log "Step 5: Updating wallet config for port ${SEQUENCER_PORT}..." -WALLET_CONFIG="${NSSA_WALLET_HOME_DIR}/wallet_config.json" +WALLET_CONFIG="${LEE_WALLET_HOME_DIR}/wallet_config.json" if [ -f "$WALLET_CONFIG" ]; then python3 -c " import json diff --git a/scripts/init-e2e-test.sh b/scripts/init-e2e-test.sh index 1591edb0..23b9a3a7 100755 --- a/scripts/init-e2e-test.sh +++ b/scripts/init-e2e-test.sh @@ -64,19 +64,15 @@ WALLET_BIN="${LSSA_DIR}/target/release/wallet" SPEL_BIN="/tmp/lssa/target/release/spel" [ -x "$SPEL_BIN" ] || fail "spel binary not found at $SPEL_BIN" -# LEZ v0.2.0 moved the debug configs under a lez/ subdirectory. Prefer the -# new location, fall back to the pre-rc6 path for older LEZ revisions. -if [ -z "${NSSA_WALLET_HOME_DIR:-}" ]; then +# Prefer current LEZ layout, fall back to older checkout layout. +if [ -z "${LEE_WALLET_HOME_DIR:-}" ]; then if [ -f "${LSSA_DIR}/lez/wallet/configs/debug/wallet_config.json" ]; then - NSSA_WALLET_HOME_DIR="${LSSA_DIR}/lez/wallet/configs/debug" + LEE_WALLET_HOME_DIR="${LSSA_DIR}/lez/wallet/configs/debug" else - NSSA_WALLET_HOME_DIR="${LSSA_DIR}/wallet/configs/debug" + LEE_WALLET_HOME_DIR="${LSSA_DIR}/wallet/configs/debug" fi fi -export NSSA_WALLET_HOME_DIR -# LEZ v0.2.0 renamed the wallet home env var to LEE_WALLET_HOME_DIR. -# Export both so the wallet finds its config on old and new LEZ revisions. -export LEE_WALLET_HOME_DIR="$NSSA_WALLET_HOME_DIR" +export LEE_WALLET_HOME_DIR WALLET_PASSWORD="${WALLET_PASSWORD:-test}" # ─── Setup ───────────────────────────────────────────────────────────────── @@ -189,7 +185,7 @@ cd "$WORK_DIR/$PROJECT_NAME" log " Waiting for sequencer..." for i in $(seq 1 90); do - if curl -sf -o /dev/null -w '%{http_code}' "$SEQUENCER_URL" 2>/dev/null | grep -qE '200|405'; then + if curl -s -o /dev/null -w '%{http_code}' "$SEQUENCER_URL" 2>/dev/null | grep -qE '200|405'; then log " ✓ Sequencer up"; break fi kill -0 "$SEQ_PID" 2>/dev/null || fail "Sequencer died" @@ -213,7 +209,7 @@ done # to the default port. We need to update it to match our non-default port. log "Step 5: Updating wallet config for port ${SEQUENCER_PORT}..." -WALLET_CONFIG="${NSSA_WALLET_HOME_DIR}/wallet_config.json" +WALLET_CONFIG="${LEE_WALLET_HOME_DIR}/wallet_config.json" if [ -f "$WALLET_CONFIG" ]; then python3 -c " import json diff --git a/scripts/smoke-test-privacy.sh b/scripts/smoke-test-privacy.sh index 90894b29..351dabf8 100755 --- a/scripts/smoke-test-privacy.sh +++ b/scripts/smoke-test-privacy.sh @@ -6,7 +6,7 @@ # Usage: ./smoke-test-privacy.sh [WORK_DIR] # # Required Environment Variables: -# LEZ_TAG - LEZ revision/tag to test against (e.g., "v0.2.0-rc1" or a commit hash) +# LEZ_TAG - LEZ revision/tag to test against # LSSA_DIR - Path to logos-execution-zone directory with sequencer built # # Optional Environment Variables: @@ -87,19 +87,15 @@ for candidate in wallet "$HOME/bin/wallet" "$LSSA_DIR/target/release/wallet"; do done [ -n "$WALLET_BIN" ] || fail "wallet not found" -# LEZ v0.2.0 moved the debug configs under a lez/ subdirectory. Prefer the -# new location, fall back to the pre-rc6 path for older LEZ revisions. -if [ -z "${NSSA_WALLET_HOME_DIR:-}" ]; then +# Prefer current LEZ layout, fall back to older checkout layout. +if [ -z "${LEE_WALLET_HOME_DIR:-}" ]; then if [ -f "${LSSA_DIR}/lez/wallet/configs/debug/wallet_config.json" ]; then - NSSA_WALLET_HOME_DIR="${LSSA_DIR}/lez/wallet/configs/debug" + LEE_WALLET_HOME_DIR="${LSSA_DIR}/lez/wallet/configs/debug" else - NSSA_WALLET_HOME_DIR="${LSSA_DIR}/wallet/configs/debug" + LEE_WALLET_HOME_DIR="${LSSA_DIR}/wallet/configs/debug" fi fi -export NSSA_WALLET_HOME_DIR -# LEZ v0.2.0 renamed the wallet home env var to LEE_WALLET_HOME_DIR. -# Export both so the wallet finds its config on old and new LEZ revisions. -export LEE_WALLET_HOME_DIR="$NSSA_WALLET_HOME_DIR" +export LEE_WALLET_HOME_DIR WALLET_PASSWORD="${WALLET_PASSWORD:-test}" # ─── Verify LSSA version matches LEZ_TAG ────────────────────────────────── @@ -147,7 +143,7 @@ log " Using local spel: $SPEL_BIN" # ─── Step 1: Scaffold project ────────────────────────────────────────────── log "Step 1: Creating SPEL project (LEZ=${LEZ_TAG})..." -"$SPEL_BIN" init --lez-tag "$LEZ_TAG" --spel-rev "$SPEL_TAG" "$PROJECT_NAME" \ +"$SPEL_BIN" init --lez-rev "$LEZ_TAG" --spel-rev "$SPEL_TAG" "$PROJECT_NAME" \ > "$LOG_DIR/init.log" 2>&1 || fail "spel init failed (see $LOG_DIR/init.log)" cd "$PROJECT_NAME" log " ✓ Project scaffolded" @@ -273,7 +269,8 @@ json.dump(cfg, open(dst, "w")) SEQ_CONFIGS="$SEQ_CONFIG_PATCHED" cd "$SEQ_HOME" -RUST_LOG=info $SEQUENCER_BIN "$SEQ_CONFIGS" > "$LOG_DIR/sequencer.log" 2>&1 & +RUST_LOG=info $SEQUENCER_BIN --port "$SEQUENCER_PORT" "$SEQ_CONFIGS" \ + > "$LOG_DIR/sequencer.log" 2>&1 & SEQ_PID=$! sleep 2 if ! kill -0 $SEQ_PID 2>/dev/null; then @@ -285,7 +282,7 @@ cd "$WORK_DIR/$PROJECT_NAME" log " Waiting for sequencer..." for i in $(seq 1 60); do - if [ $(curl -sf -o /dev/null -w '%{http_code}' "$SEQUENCER_URL" 2>/dev/null | grep -qE '200|405'; echo $?) -eq 0 ]; then + if curl -s -o /dev/null -w '%{http_code}' "$SEQUENCER_URL" 2>/dev/null | grep -qE '200|405'; then log " ✓ Sequencer up"; break fi kill -0 "$SEQ_PID" 2>/dev/null || fail "Sequencer died" @@ -309,6 +306,23 @@ for i in $(seq 1 60); do echo -n "." done +# Keep the wallet CLI on the same sequencer endpoint used by this test. +log " Updating wallet config for port ${SEQUENCER_PORT}..." +WALLET_CONFIG="${LEE_WALLET_HOME_DIR}/wallet_config.json" +if [ -f "$WALLET_CONFIG" ]; then + python3 -c ' +import json, sys +path, sequencer_url = sys.argv[1], sys.argv[2] +with open(path, "r") as wallet_file: + config = json.load(wallet_file) +config["sequencer_addr"] = sequencer_url +with open(path, "w") as wallet_file: + json.dump(config, wallet_file, indent=4) +' "$WALLET_CONFIG" "$SEQUENCER_URL" || fail "Failed to update wallet config" +else + fail "Wallet config not found at $WALLET_CONFIG" +fi + # ─── Step 6: Deploy ─────────────────────────────────────────────────────── log "Step 6: Deploying program..." diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index ca1d09ba..5df8a0e3 100644 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -165,7 +165,7 @@ else exit 0 fi -export NSSA_WALLET_HOME_DIR="${NSSA_WALLET_HOME_DIR:-${LSSA_DIR}/wallet/configs/debug}" +export LEE_WALLET_HOME_DIR="${LEE_WALLET_HOME_DIR:-${LSSA_DIR}/wallet/configs/debug}" WALLET_PASSWORD="${WALLET_PASSWORD:-test}" # Wallet needs password on stdin; first run creates storage diff --git a/spel-cli/Cargo.toml b/spel-cli/Cargo.toml index 9e349758..abaeff60 100644 --- a/spel-cli/Cargo.toml +++ b/spel-cli/Cargo.toml @@ -10,23 +10,24 @@ path = "src/bin/main.rs" [dependencies] spel-framework-core = { path = "../spel-framework-core", features = ["idl-gen"] } -# Pinned to lez-core-v0.2.0, which matches the live testnet: `/LEE/v0.3` public-tx -# signing (spel/co issue #234) and the wallet storage schema written by the wallet -# CLI (#235). nssa_core/nssa were renamed to lee_core/lee upstream — aliased back -# via `package` so spel's source keeps compiling. -nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", package = "lee_core" } -nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", package = "lee" } -common = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0" } -sequencer_service_rpc = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", features = ["client"] } -wallet = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0" } +# Pinned to the Wallet build API commit on top of lez-core-v0.2.0. This retains +# the live testnet's `/LEE/v0.3` signing and wallet storage compatibility. +# nssa_core/nssa were renamed to lee_core/lee upstream — aliased back via +# `package` so spel's source keeps compiling. +nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", package = "lee_core" } +nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", package = "lee" } +common = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63" } +sequencer_service_rpc = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", features = ["client"] } +wallet = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63" } risc0-zkvm = { version = "3.0.3", features = ["std"] } base58 = "0.2" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +serde_json = { version = "1.0", features = ["arbitrary_precision"] } borsh = "1.5" tokio = { version = "1.28.2", features = ["net", "rt-multi-thread", "sync", "macros"] } hex = "0.4" toml = "0.8" +thiserror = "1.0" [dev-dependencies] tempfile = "3" diff --git a/spel-cli/src/generate_idl.rs b/spel-cli/src/generate_idl.rs index 05f0b371..d3df138e 100644 --- a/spel-cli/src/generate_idl.rs +++ b/spel-cli/src/generate_idl.rs @@ -170,7 +170,7 @@ mod tests { impl Drop for TempDir { fn drop(&mut self) { - let _ = fs::remove_dir_all(&self.0); + drop(fs::remove_dir_all(&self.0)); } } diff --git a/spel-cli/src/init.rs b/spel-cli/src/init.rs index 9a4f66ad..11b2b216 100644 --- a/spel-cli/src/init.rs +++ b/spel-cli/src/init.rs @@ -3,6 +3,8 @@ use std::fs; use std::path::Path; +const DEFAULT_LEZ_REV: &str = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63"; + pub fn init_project( name: &str, lez_tag: Option<&str>, @@ -97,7 +99,7 @@ ui/ let lez_ref_ffi = match (lez_tag, lez_rev) { (Some(t), _) => format!("tag = \"{}\"", t), (_, Some(r)) => format!("rev = \"{}\"", r), - _ => "tag = \"v0.2.0\"".to_string(), + _ => format!("rev = \"{DEFAULT_LEZ_REV}\""), }; let spel_ref_ffi = match (spel_tag, spel_rev) { (Some(t), _) => format!("tag = \"{}\"", t), @@ -688,7 +690,7 @@ risc0-zkvm = {{ version = "=3.0.5", features = ["std"] }} let lez_ref = match (lez_tag, lez_rev) { (Some(t), _) => format!("tag = \"{}\"", t), (_, Some(r)) => format!("rev = \"{}\"", r), - _ => "tag = \"v0.2.0\"".to_string(), + _ => format!("rev = \"{DEFAULT_LEZ_REV}\""), }; let spel_ref = match (spel_tag, spel_rev) { (Some(t), _) => format!("tag = \"{}\"", t), diff --git a/spel-cli/src/lib.rs b/spel-cli/src/lib.rs index 89d5995e..3c720204 100644 --- a/spel-cli/src/lib.rs +++ b/spel-cli/src/lib.rs @@ -208,15 +208,15 @@ pub async fn run() { println!("Create a new SPEL project"); println!(); println!("Options:"); - println!(" --lez-tag LEZ version tag (default: v0.1.2)"); - println!(" --spel-rev SPEL revision (default: refs/pull/122/head)"); + println!(" --lez-tag LEZ version tag"); + println!(" --spel-rev SPEL revision"); println!(" --lez-rev LEZ revision (alternative to --lez-tag)"); println!(" --spel-tag SPEL tag (alternative to --spel-rev)"); println!(); println!("Examples:"); println!(" spel init my-project"); println!( - " spel init my-project --lez-tag v0.1.2 --spel-rev refs/pull/122/head" + " spel init my-project --lez-rev 7a40979c7f5b04a46a8665a3d07cd3a300dcae63" ); return; } @@ -545,9 +545,10 @@ fn compute_pda_command( .collect(); // Parse --key value pairs from remaining args, using IDL types when available. - // --npk <64-char-hex> is reserved for private PDA derivation and not treated as a seed arg. + // --npk and --vpk are reserved for private PDA derivation. let mut seed_args: HashMap = HashMap::new(); let mut npk_hex: Option = None; + let mut vpk_hex: Option = None; let mut i = 1; while i < args.len() { if let Some(key) = args[i].strip_prefix("--") { @@ -558,6 +559,11 @@ fn compute_pda_command( i += 2; continue; } + if key == "vpk" { + vpk_hex = Some(raw.clone()); + i += 2; + continue; + } let arg_name = key.replace('-', "_"); let parsed = if let Some(ty) = arg_types.get(arg_name.as_str()) { parse::parse_value(raw, ty).unwrap_or_else(|e| { @@ -621,8 +627,8 @@ fn compute_pda_command( std::process::exit(1); }; - // For private PDAs, parse and require --npk - use nssa_core::NullifierPublicKey; + // For private PDAs, parse and require both public keys. + use nssa_core::{encryption::ViewingPublicKey, NullifierPublicKey}; let npk: Option = if pda_def.private { match npk_hex { Some(ref hex) => { @@ -647,6 +653,34 @@ fn compute_pda_command( } else { None }; + let vpk: Option = if pda_def.private { + match vpk_hex { + Some(ref raw) => { + let hex = raw + .strip_prefix("0x") + .or_else(|| raw.strip_prefix("0X")) + .unwrap_or(raw); + let bytes = crate::hex::hex_decode(hex).unwrap_or_else(|e| { + eprintln!("❌ Invalid --vpk '{}': {}", raw, e); + std::process::exit(1); + }); + Some(ViewingPublicKey::from_bytes(bytes).unwrap_or_else(|e| { + eprintln!("❌ Invalid --vpk '{}': {}", raw, e); + std::process::exit(1); + })) + }, + None => { + eprintln!( + "❌ '{}' is a private PDA — pass --vpk <2368-char-hex>", + account_name + ); + eprintln!(" The ViewingPublicKey is the recipient's vpk from their wallet key."); + std::process::exit(1); + }, + } + } else { + None + }; // Build account_map: parse -- for IdlSeed::Account seeds. let mut account_map: HashMap = HashMap::new(); @@ -682,7 +716,7 @@ fn compute_pda_command( &program_id, &account_map, &seed_args, - npk.as_ref(), + npk.as_ref().zip(vpk.as_ref()), ) { Ok(account_id) => { println!("{}", account_id); diff --git a/spel-cli/src/parse.rs b/spel-cli/src/parse.rs index 37aaa2b1..b6ca091e 100644 --- a/spel-cli/src/parse.rs +++ b/spel-cli/src/parse.rs @@ -1,6 +1,7 @@ //! IDL type-aware value parsing from CLI strings. use crate::hex::{hex_decode, hex_encode}; +use nssa_core::program::ProgramId; use spel_framework_core::idl::IdlType; /// A parsed CLI value with type information preserved. @@ -96,13 +97,32 @@ fn parse_primitive(raw: &str, prim: &str) -> Result { .parse::() .map(ParsedValue::U128) .map_err(|e| format!("Invalid u128 '{}': {}", raw, e)), - "program_id" => parse_program_id(raw), + "program_id" => parse_program_id(raw).map(|value| ParsedValue::U32Array(value.to_vec())), // `AccountId` serializes via `SerializeDisplay` (base58 string), so normalize the // input (base58 or 0x-hex) to canonical base58 and carry it as a string. "account_id" => { let bytes = crate::hex::decode_bytes_32(raw)?; Ok(ParsedValue::Str(nssa::AccountId::new(bytes).to_string())) }, + "nullifier_public_key" => Ok(ParsedValue::ByteArray( + crate::hex::decode_bytes_32(raw)?.to_vec(), + )), + "viewing_public_key" => { + let hex = raw + .strip_prefix("0x") + .or_else(|| raw.strip_prefix("0X")) + .unwrap_or(raw); + let bytes = hex_decode(hex)?; + if bytes.len() != nssa_core::encryption::ViewingPublicKey::LEN { + return Err(format!( + "Invalid ViewingPublicKey '{}': expected {} bytes, got {}", + raw, + nssa_core::encryption::ViewingPublicKey::LEN, + bytes.len() + )); + } + Ok(ParsedValue::ByteArray(bytes)) + }, "bool" => match raw { "true" | "1" | "yes" => Ok(ParsedValue::Bool(true)), "false" | "0" | "no" => Ok(ParsedValue::Bool(false)), @@ -113,29 +133,29 @@ fn parse_primitive(raw: &str, prim: &str) -> Result { } } -fn parse_program_id(raw: &str) -> Result { +/// Parse established ProgramId text into little-endian u32 limbs. +pub(crate) fn parse_program_id(raw: &str) -> Result { if raw.contains(',') { let parts: Vec<&str> = raw.split(',').map(|s| s.trim()).collect(); if parts.len() != 8 { return Err(format!("ProgramId needs 8 u32 values, got {}", parts.len())); } - let mut vals = Vec::with_capacity(8); + let mut program_id = [0; 8]; for (i, p) in parts.iter().enumerate() { let v = if p.starts_with("0x") || p.starts_with("0X") { u32::from_str_radix(&p[2..], 16) } else { p.parse::() }; - vals.push(v.map_err(|e| format!("ProgramId[{}] invalid u32 '{}': {}", i, p, e))?); + program_id[i] = + v.map_err(|e| format!("ProgramId[{}] invalid u32 '{}': {}", i, p, e))?; } - Ok(ParsedValue::U32Array(vals)) + Ok(program_id) } else if raw.len() == 64 && raw.chars().all(|c| c.is_ascii_hexdigit()) { - let bytes = hex_decode(raw)?; - let mut vals = Vec::with_capacity(8); - for chunk in bytes.chunks(4) { - vals.push(u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])); - } - Ok(ParsedValue::U32Array(vals)) + let bytes: [u8; 32] = hex_decode(raw)? + .try_into() + .map_err(|_| format!("Invalid ProgramId '{}': expected 32 bytes", raw))?; + Ok(program_id_from_bytes(bytes)) } else { // base58 (or 0x-prefixed hex) ImageID → little-endian u32 limbs, matching the bare-hex // branch above so all representations of the same ProgramId agree. @@ -178,14 +198,24 @@ fn parse_program_id(raw: &str) -> Result { )); } } - let mut vals = Vec::with_capacity(8); - for chunk in bytes.chunks(4) { - vals.push(u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])); - } - Ok(ParsedValue::U32Array(vals)) + Ok(program_id_from_bytes(bytes)) } } +fn program_id_from_bytes(bytes: [u8; 32]) -> ProgramId { + let mut program_id = [0; 8]; + for (index, word) in program_id.iter_mut().enumerate() { + let offset = index * 4; + *word = u32::from_le_bytes([ + bytes[offset], + bytes[offset + 1], + bytes[offset + 2], + bytes[offset + 3], + ]); + } + program_id +} + fn parse_array(raw: &str, elem_type: &IdlType, size: usize) -> Result { match elem_type { IdlType::Primitive(p) if p == "u8" => { @@ -434,4 +464,30 @@ mod tests { assert!(parse_program_id("not-a-program-id").is_err()); assert!(parse_program_id("deadbeef").is_err()); } + + #[test] + fn shared_program_id_decoder_preserves_cli_forms() { + let bytes = [0x11; 32]; + let expected = [0x1111_1111; 8]; + let csv = expected + .iter() + .map(u32::to_string) + .collect::>() + .join(","); + let inputs = [ + csv, + format!("0x{}", "11".repeat(32)), + nssa::AccountId::new(bytes).to_string(), + ]; + let ty = IdlType::Primitive("program_id".to_string()); + + for input in inputs { + assert_eq!(parse_program_id(&input).unwrap(), expected); + let parsed = parse_value(&input, &ty).unwrap(); + assert!(matches!( + parsed, + ParsedValue::U32Array(values) if values == expected.to_vec() + )); + } + } } diff --git a/spel-cli/src/pda.rs b/spel-cli/src/pda.rs index 8dc29252..f0f9481e 100644 --- a/spel-cli/src/pda.rs +++ b/spel-cli/src/pda.rs @@ -2,6 +2,7 @@ use crate::parse::ParsedValue; use nssa::AccountId; +use nssa_core::encryption::ViewingPublicKey; use nssa_core::program::{PdaSeed, ProgramId}; use nssa_core::NullifierPublicKey; use spel_framework_core::idl::IdlSeed; @@ -99,14 +100,14 @@ fn hash_seeds(seeds: &[[u8; 32]]) -> [u8; 32] { /// /// Supports all seed types: `const`, `account`, and `arg`. /// -/// Pass `npk = Some(key)` for private PDAs; the address will be derived via -/// `AccountId::for_private_pda`. For public PDAs pass `npk = None`. +/// Pass `private_keys = Some((npk, vpk))` for private PDAs; the address will +/// be derived via `AccountId::for_private_pda`. For public PDAs pass `None`. pub fn compute_pda_from_seeds( seeds: &[IdlSeed], program_id: &ProgramId, account_map: &HashMap, parsed_args: &HashMap, - npk: Option<&NullifierPublicKey>, + private_keys: Option<(&NullifierPublicKey, &ViewingPublicKey)>, ) -> Result { if seeds.is_empty() { return Err("PDA requires at least one seed".to_string()); @@ -127,11 +128,13 @@ pub fn compute_pda_from_seeds( }; let pda_seed = PdaSeed::new(combined); - if let Some(npk) = npk { + if let Some((npk, vpk)) = private_keys { // The chain now derives private PDAs with a u128 `identifier`; spel has // no way to specify it yet, so default to 0 (the common case). // TODO: thread a `--identifier` flag through for non-zero identifiers. - Ok(AccountId::for_private_pda(program_id, &pda_seed, npk, 0)) + Ok(AccountId::for_private_pda( + program_id, &pda_seed, npk, vpk, 0, + )) } else { Ok(AccountId::for_public_pda(program_id, &pda_seed)) } @@ -203,7 +206,6 @@ mod tests { #[test] fn test_hash_seeds_not_commutative() { - use risc0_zkvm::sha::{Impl, Sha256}; // SHA-256(A || B) != SHA-256(B || A) for A != B let a = [0x01u8; 32]; let b = [0x02u8; 32]; @@ -227,13 +229,14 @@ mod tests { }]; let program_id: ProgramId = [2u32; 8]; let npk = NullifierPublicKey([0xABu8; 32]); + let vpk = ViewingPublicKey::from_seed(&[0xBC; 32], &[0xCD; 32]); let private = compute_pda_from_seeds( &seeds, &program_id, &HashMap::new(), &HashMap::new(), - Some(&npk), + Some((&npk, &vpk)), ) .unwrap(); let public = @@ -245,7 +248,8 @@ mod tests { // Verify it matches a direct for_private_pda call with the same inputs let mut combined = [0u8; 32]; combined[.."vault".len()].copy_from_slice(b"vault"); - let expected = AccountId::for_private_pda(&program_id, &PdaSeed::new(combined), &npk, 0); + let expected = + AccountId::for_private_pda(&program_id, &PdaSeed::new(combined), &npk, &vpk, 0); assert_eq!(private, expected, "private PDA must match for_private_pda"); } @@ -257,13 +261,14 @@ mod tests { let program_id: ProgramId = [2u32; 8]; let npk1 = NullifierPublicKey([0x01u8; 32]); let npk2 = NullifierPublicKey([0x02u8; 32]); + let vpk = ViewingPublicKey::from_seed(&[0x03; 32], &[0x04; 32]); let addr1 = compute_pda_from_seeds( &seeds, &program_id, &HashMap::new(), &HashMap::new(), - Some(&npk1), + Some((&npk1, &vpk)), ) .unwrap(); let addr2 = compute_pda_from_seeds( @@ -271,7 +276,7 @@ mod tests { &program_id, &HashMap::new(), &HashMap::new(), - Some(&npk2), + Some((&npk2, &vpk)), ) .unwrap(); @@ -281,6 +286,39 @@ mod tests { ); } + #[test] + fn test_private_pda_differs_across_vpks() { + let seeds = vec![IdlSeed::Const { + value: "vault".to_string(), + }]; + let program_id: ProgramId = [2u32; 8]; + let npk = NullifierPublicKey([0x01u8; 32]); + let vpk1 = ViewingPublicKey::from_seed(&[0x02; 32], &[0x03; 32]); + let vpk2 = ViewingPublicKey::from_seed(&[0x04; 32], &[0x05; 32]); + + let addr1 = compute_pda_from_seeds( + &seeds, + &program_id, + &HashMap::new(), + &HashMap::new(), + Some((&npk, &vpk1)), + ) + .unwrap(); + let addr2 = compute_pda_from_seeds( + &seeds, + &program_id, + &HashMap::new(), + &HashMap::new(), + Some((&npk, &vpk2)), + ) + .unwrap(); + + assert_ne!( + addr1, addr2, + "different vpks must yield different private PDAs" + ); + } + #[test] fn test_multi_seed_differs_from_single() { let seeds_multi = vec![ diff --git a/spel-cli/src/serialize.rs b/spel-cli/src/serialize.rs index b4ed4a0c..f809933b 100644 --- a/spel-cli/src/serialize.rs +++ b/spel-cli/src/serialize.rs @@ -132,6 +132,16 @@ fn primitive_to_dynamic(prim: &str, val: &ParsedValue) -> Result Ok(DynamicValue::Str(s.clone())), + ("nullifier_public_key", ParsedValue::ByteArray(bytes)) if bytes.len() == 32 => Ok( + DynamicValue::Tuple(bytes.iter().map(|byte| DynamicValue::U8(*byte)).collect()), + ), + ("viewing_public_key", ParsedValue::ByteArray(bytes)) + if bytes.len() == nssa_core::encryption::ViewingPublicKey::LEN => + { + Ok(DynamicValue::Seq( + bytes.iter().map(|byte| DynamicValue::U8(*byte)).collect(), + )) + }, _ => Err(SerializeError::TypeMismatch { expected: prim.to_string(), got: format!("{:?}", val), diff --git a/spel-cli/src/tx.rs b/spel-cli/src/tx.rs index d47bd991..80e5603f 100644 --- a/spel-cli/src/tx.rs +++ b/spel-cli/src/tx.rs @@ -1,5 +1,14 @@ //! Transaction building and submission. +mod pda_resolution; +mod resolution; +mod value; + +pub use resolution::{ + resolve_private_instruction, resolve_public_instruction, ResolvedPrivateInstruction, + ResolvedPublicInstruction, SpelInstructionRequest, SpelTxError, +}; + use crate::cli::{snake_to_kebab, to_pascal_case}; use crate::hex::{decode_bytes_32, hex_encode, parse_account_id}; use crate::parse::{parse_string_vec, parse_value, ParsedValue}; diff --git a/spel-cli/src/tx/pda_resolution.rs b/spel-cli/src/tx/pda_resolution.rs new file mode 100644 index 00000000..81804379 --- /dev/null +++ b/spel-cli/src/tx/pda_resolution.rs @@ -0,0 +1,302 @@ +use std::collections::BTreeMap; + +use nssa_core::program::ProgramId; +use spel_framework_core::{ + idl::{IdlAccountItem, IdlInstruction, IdlSeed}, + pda::{compute_pda, compute_private_pda_with_identifier}, +}; +use wallet::AccountIdentity; + +use super::{resolution::SpelTxError, value::ArgumentValue}; + +enum PdaState { + Visiting, + Done(AccountIdentity), +} + +pub(crate) fn account_seed_target<'a>( + instruction: &'a IdlInstruction, + path: &str, +) -> Option<&'a IdlAccountItem> { + instruction + .accounts + .iter() + .find(|account| account.name == path) + .or_else(|| { + path.strip_suffix(".id") + .and_then(|name| (!name.is_empty()).then_some(name)) + .and_then(|name| { + instruction + .accounts + .iter() + .find(|account| account.name == name) + }) + }) +} + +pub(crate) fn resolve_pdas( + instruction: &IdlInstruction, + inputs: &BTreeMap>, + arguments: &BTreeMap, + program_id: ProgramId, + private_path: bool, +) -> Result, SpelTxError> { + let pda_names: Vec<_> = instruction + .accounts + .iter() + .filter(|account| account.pda.is_some()) + .map(|account| account.name.clone()) + .collect(); + let mut resolver = PdaResolver { + instruction, + inputs, + arguments, + program_id, + private_path, + states: BTreeMap::new(), + }; + + for name in &pda_names { + resolver.resolve_pda(name)?; + } + + Ok(resolver + .states + .into_iter() + .filter_map(|(name, state)| match state { + PdaState::Done(identity) => Some((name, identity)), + PdaState::Visiting => None, + }) + .collect()) +} + +struct PdaResolver<'a> { + instruction: &'a IdlInstruction, + inputs: &'a BTreeMap>, + arguments: &'a BTreeMap, + program_id: ProgramId, + private_path: bool, + states: BTreeMap, +} + +impl PdaResolver<'_> { + fn resolve_pda(&mut self, name: &str) -> Result { + if let Some(state) = self.states.get(name) { + return match state { + PdaState::Done(identity) => Ok(identity.clone()), + PdaState::Visiting => Err(SpelTxError::PdaResolution { + account: name.to_owned(), + seed_index: None, + reason: "PDA dependency cycle".to_string(), + }), + }; + } + + let account = self + .account(name) + .ok_or_else(|| SpelTxError::PdaResolution { + account: name.to_owned(), + seed_index: None, + reason: "PDA account is not declared".to_string(), + })? + .clone(); + let pda = account + .pda + .clone() + .ok_or_else(|| SpelTxError::PdaResolution { + account: name.to_owned(), + seed_index: None, + reason: "account is not a PDA".to_string(), + })?; + + self.states.insert(name.to_owned(), PdaState::Visiting); + + let mut seeds = Vec::with_capacity(pda.seeds.len()); + for (seed_index, seed) in pda.seeds.iter().enumerate() { + seeds.push(self.resolve_seed(&account.name, seed_index, seed)?); + } + let seed_refs: Vec<_> = seeds.iter().collect(); + + let identity = if pda.private { + self.resolve_private_pda(&account, &seed_refs)? + } else { + AccountIdentity::PublicNoSign(compute_pda(&self.program_id, &seed_refs)) + }; + + self.states + .insert(account.name.clone(), PdaState::Done(identity.clone())); + Ok(identity) + } + + fn resolve_seed( + &mut self, + account: &str, + seed_index: usize, + seed: &IdlSeed, + ) -> Result<[u8; 32], SpelTxError> { + match seed { + IdlSeed::Const { value } => constant_seed(value).ok_or_else(|| { + self.pda_error(account, Some(seed_index), "constant seed exceeds 32 bytes") + }), + IdlSeed::Account { path } => { + let target = account_seed_target(self.instruction, path) + .ok_or_else(|| { + self.pda_error( + account, + Some(seed_index), + "account seed references an undeclared account", + ) + })? + .clone(); + if target.rest { + return Err(self.pda_error( + account, + Some(seed_index), + "account seed cannot reference a rest account", + )); + } + + let identity = if target.pda.is_some() { + if matches!(self.states.get(&target.name), Some(PdaState::Visiting)) { + return Err(self.pda_error( + account, + Some(seed_index), + "PDA dependency cycle", + )); + } + self.resolve_pda(&target.name)? + } else { + self.inputs + .get(&target.name) + .and_then(|identities| identities.first()) + .cloned() + .ok_or_else(|| { + self.pda_error( + account, + Some(seed_index), + "account seed input is unavailable", + ) + })? + }; + Ok(*identity.account_id().value()) + }, + IdlSeed::Arg { path } => { + let argument = self + .instruction + .args + .iter() + .find(|argument| argument.name == *path) + .ok_or_else(|| { + self.pda_error( + account, + Some(seed_index), + "argument seed references an undeclared argument", + ) + })?; + let value = self.arguments.get(path).ok_or_else(|| { + self.pda_error( + account, + Some(seed_index), + "argument seed input is unavailable", + ) + })?; + value + .seed_bytes(&argument.type_) + .map_err(|reason| self.pda_error(account, Some(seed_index), reason)) + }, + } + } + + fn resolve_private_pda( + &self, + account: &IdlAccountItem, + seeds: &[&[u8; 32]], + ) -> Result { + if !self.private_path { + return Err(SpelTxError::InvalidIdl { + instruction: self.instruction.name.clone(), + path: format!("accounts.{}", account.name), + reason: "public resolution does not support private PDAs".to_string(), + }); + } + + let identity = self + .inputs + .get(&account.name) + .and_then(|identities| identities.first()) + .cloned() + .ok_or_else(|| SpelTxError::MissingAccount { + name: account.name.clone(), + })?; + let (npk, vpk, identifier) = match &identity { + AccountIdentity::PrivatePdaForeign { + npk, + vpk, + identifier, + .. + } + | AccountIdentity::PrivatePdaShared { + npk, + vpk, + identifier, + .. + } => (npk, vpk, *identifier), + _ => { + return Err(SpelTxError::InvalidAccount { + account: account.name.clone(), + index: self.account_index(&account.name), + reason: "private PDA initialization requires a foreign or shared private PDA identity" + .to_string(), + }); + }, + }; + + let expected = + compute_private_pda_with_identifier(&self.program_id, seeds, npk, vpk, identifier); + if identity.account_id() != expected { + return Err(SpelTxError::InvalidAccount { + account: account.name.clone(), + index: self.account_index(&account.name), + reason: "private PDA identity does not match the derived address".to_string(), + }); + } + + Ok(identity) + } + + fn account(&self, name: &str) -> Option<&IdlAccountItem> { + self.instruction + .accounts + .iter() + .find(|account| account.name == name) + } + + fn account_index(&self, name: &str) -> usize { + self.instruction + .accounts + .iter() + .position(|account| account.name == name) + .unwrap_or_default() + } + + fn pda_error( + &self, + account: &str, + seed_index: Option, + reason: impl Into, + ) -> SpelTxError { + SpelTxError::PdaResolution { + account: account.to_owned(), + seed_index, + reason: reason.into(), + } + } +} + +fn constant_seed(value: &str) -> Option<[u8; 32]> { + (value.len() <= 32).then(|| { + let mut seed = [0; 32]; + seed[..value.len()].copy_from_slice(value.as_bytes()); + seed + }) +} diff --git a/spel-cli/src/tx/resolution.rs b/spel-cli/src/tx/resolution.rs new file mode 100644 index 00000000..5db2512a --- /dev/null +++ b/spel-cli/src/tx/resolution.rs @@ -0,0 +1,1835 @@ +//! Fallible, resolver-only IDL transaction inputs. +//! +//! This module resolves canonical IDL names, account identities, PDA accounts, and +//! instruction data. It does not read files, initialize a wallet, build a +//! transaction, submit a transaction, poll, print, or terminate the process. + +use std::collections::{BTreeMap, BTreeSet}; + +use crate::serialize::SerializeError; +use nssa::privacy_preserving_transaction::circuit::ProgramWithDependencies; +use nssa_core::program::{InstructionData, ProgramId}; +use spel_framework_core::idl::{IdlInstruction, IdlSeed, SpelIdl}; +use thiserror::Error; +use wallet::AccountIdentity; + +use super::{ + pda_resolution, + value::{self, ArgumentValue, ValueParseError}, +}; + +/// Raw caller input for resolving one IDL instruction. +/// +/// Construct this type with exact IDL account and argument names. The resolver +/// consumes it synchronously and follows IDL declaration order rather than map +/// order. +pub struct SpelInstructionRequest<'a> { + /// IDL containing the instruction to resolve. + pub idl: &'a SpelIdl, + /// Exact IDL instruction name. + pub instruction: &'a str, + /// Exact IDL account names mapped to fixed or rest account identities. + pub accounts: BTreeMap>, + /// Exact IDL argument names mapped to canonical or CLI-compatible input text. + /// + /// Canonical JSON is preferred for containers; established valid CLI forms + /// remain accepted. + pub arguments: BTreeMap, +} + +/// Resolved inputs for a public Wallet transaction build. +pub struct ResolvedPublicInstruction { + program_id: ProgramId, + accounts: Vec, + instruction_data: InstructionData, +} + +impl ResolvedPublicInstruction { + /// Returns the target program ID. + #[must_use] + pub const fn program_id(&self) -> ProgramId { + self.program_id + } + + /// Returns ordered Wallet account identities. + #[must_use] + pub fn accounts(&self) -> &[AccountIdentity] { + &self.accounts + } + + /// Returns the RISC0-serialized instruction words. + #[must_use] + pub fn instruction_data(&self) -> &[u32] { + &self.instruction_data + } + + /// Consumes the resolved instruction into Wallet build inputs. + #[must_use] + pub fn into_parts(self) -> (ProgramId, Vec, InstructionData) { + (self.program_id, self.accounts, self.instruction_data) + } +} + +/// Resolved inputs for a privacy-preserving Wallet transaction build. +pub struct ResolvedPrivateInstruction { + program: ProgramWithDependencies, + accounts: Vec, + instruction_data: InstructionData, +} + +impl ResolvedPrivateInstruction { + /// Returns the caller-supplied program and dependency binaries. + #[must_use] + pub const fn program(&self) -> &ProgramWithDependencies { + &self.program + } + + /// Returns ordered Wallet account identities. + #[must_use] + pub fn accounts(&self) -> &[AccountIdentity] { + &self.accounts + } + + /// Returns the RISC0-serialized instruction words. + #[must_use] + pub fn instruction_data(&self) -> &[u32] { + &self.instruction_data + } + + /// Consumes the resolved instruction into Wallet build inputs. + #[must_use] + pub fn into_parts( + self, + ) -> ( + ProgramWithDependencies, + Vec, + InstructionData, + ) { + (self.program, self.accounts, self.instruction_data) + } +} + +/// Structured failure returned by the resolver-only transaction API. +#[non_exhaustive] +#[derive(Debug, Error)] +pub enum SpelTxError { + /// No IDL instruction has the requested exact name. + #[error("unknown instruction `{instruction}`")] + UnknownInstruction { + /// Requested instruction name. + instruction: String, + }, + /// More than one IDL instruction has the requested exact name. + #[error("ambiguous instruction `{instruction}`")] + AmbiguousInstruction { + /// Requested instruction name. + instruction: String, + /// Number of matching instruction declarations. + matches: usize, + }, + /// The selected IDL instruction has an unsupported or malformed shape. + #[error("invalid IDL for instruction `{instruction}` at `{path}`: {reason}")] + InvalidIdl { + /// Selected instruction name. + instruction: String, + /// IDL-relative path of the invalid declaration. + path: String, + /// Redacted, human-readable explanation. + reason: String, + }, + /// A required account input is absent. + #[error("missing account `{name}`")] + MissingAccount { + /// Exact IDL account name. + name: String, + }, + /// An account input is unknown or forbidden for the IDL account shape. + #[error("unexpected account `{name}`")] + UnexpectedAccount { + /// Exact caller-supplied account name. + name: String, + }, + /// An account input has the wrong number of identities. + #[error("invalid account count for `{name}`: expected {expected}, got {actual}")] + InvalidAccountCount { + /// Exact IDL account name. + name: String, + /// Required identity count. + expected: usize, + /// Caller-supplied identity count. + actual: usize, + }, + /// An identity contradicts the selected resolver path or IDL account flags. + #[error("invalid account `{account}` at index {index}: {reason}")] + InvalidAccount { + /// Exact IDL account name. + account: String, + /// Zero-based final account position. + index: usize, + /// Redacted, human-readable explanation. + reason: String, + }, + /// A required argument input is absent. + #[error("missing argument `{name}`")] + MissingArgument { + /// Exact IDL argument name. + name: String, + }, + /// An argument input does not exist on the selected instruction. + #[error("unexpected argument `{name}`")] + UnexpectedArgument { + /// Exact caller-supplied argument name. + name: String, + }, + /// An argument cannot be parsed as its selected IDL type. + #[error("failed to parse argument `{name}` at {path:?}: {reason}")] + ArgumentParse { + /// Exact IDL argument name. + name: String, + /// Zero-based nested array/vector positions from outermost to innermost. + path: Vec, + /// Redacted, human-readable explanation. + reason: String, + }, + /// A PDA definition, dependency, or seed cannot be resolved. + #[error("failed to resolve PDA `{account}`: {reason}")] + PdaResolution { + /// Exact IDL PDA account name. + account: String, + /// Zero-based seed position, or `None` for a whole-PDA failure. + seed_index: Option, + /// Redacted, human-readable explanation. + reason: String, + }, + /// Two final account positions resolve to the same account ID. + #[error("duplicate resolved account `{account}` at index {index}")] + DuplicateResolvedAccount { + /// First IDL account name that resolved to this ID. + first_account: String, + /// First zero-based final account position. + first_index: usize, + /// Duplicate IDL account name. + account: String, + /// Duplicate zero-based final account position. + index: usize, + }, + /// RISC0 serialization of resolved instruction fields failed. + #[error("failed to serialize instruction `{instruction}`")] + InstructionSerialization { + /// Selected instruction name. + instruction: String, + /// Concrete RISC0 serialization failure. + #[source] + source: SerializeError, + }, +} + +/// Resolves one IDL instruction into direct public Wallet build inputs. +/// +/// ```no_run +/// use std::collections::BTreeMap; +/// +/// use nssa_core::program::ProgramId; +/// use spel::tx::{SpelInstructionRequest, resolve_public_instruction}; +/// use spel_framework_core::idl::SpelIdl; +/// use wallet::WalletCore; +/// +/// fn prepare(idl: &SpelIdl, program_id: ProgramId, wallet: &WalletCore) { +/// let request = SpelInstructionRequest { +/// idl, +/// instruction: "initialize", +/// accounts: BTreeMap::new(), +/// arguments: BTreeMap::new(), +/// }; +/// let resolved = resolve_public_instruction(request, program_id).unwrap(); +/// let (program_id, accounts, instruction_data) = resolved.into_parts(); +/// let _build = wallet.build_pub_tx(accounts, instruction_data, program_id); +/// } +/// ``` +/// +/// # Errors +/// +/// Returns [`SpelTxError`] when the selected IDL, caller input, account +/// identities, PDA dependencies, or instruction serialization is invalid. +pub fn resolve_public_instruction( + request: SpelInstructionRequest<'_>, + program_id: ProgramId, +) -> Result { + let resolved = resolve(request, program_id, ResolverPath::Public)?; + Ok(ResolvedPublicInstruction { + program_id, + accounts: resolved.accounts, + instruction_data: resolved.instruction_data, + }) +} + +/// Resolves one IDL instruction into direct privacy-preserving Wallet build inputs. +/// +/// ```no_run +/// use std::collections::BTreeMap; +/// +/// use nssa::privacy_preserving_transaction::circuit::ProgramWithDependencies; +/// use spel::tx::{SpelInstructionRequest, resolve_private_instruction}; +/// use spel_framework_core::idl::SpelIdl; +/// use wallet::WalletCore; +/// +/// fn prepare(idl: &SpelIdl, program: ProgramWithDependencies, wallet: &WalletCore) { +/// let request = SpelInstructionRequest { +/// idl, +/// instruction: "initialize", +/// accounts: BTreeMap::new(), +/// arguments: BTreeMap::new(), +/// }; +/// let resolved = resolve_private_instruction(request, program).unwrap(); +/// let (program, accounts, instruction_data) = resolved.into_parts(); +/// let _build = wallet.build_privacy_preserving_tx(accounts, instruction_data, &program); +/// } +/// ``` +/// +/// # Errors +/// +/// Returns [`SpelTxError`] when the selected IDL, caller input, account +/// identities, PDA dependencies, or instruction serialization is invalid. +pub fn resolve_private_instruction( + request: SpelInstructionRequest<'_>, + program: ProgramWithDependencies, +) -> Result { + let resolved = resolve(request, program.program.id(), ResolverPath::Private)?; + Ok(ResolvedPrivateInstruction { + program, + accounts: resolved.accounts, + instruction_data: resolved.instruction_data, + }) +} + +enum ResolverPath { + Public, + Private, +} + +impl ResolverPath { + const fn is_private(&self) -> bool { + matches!(self, Self::Private) + } +} + +struct ResolvedParts { + accounts: Vec, + instruction_data: InstructionData, +} + +fn resolve( + request: SpelInstructionRequest<'_>, + program_id: ProgramId, + path: ResolverPath, +) -> Result { + let SpelInstructionRequest { + idl, + instruction: instruction_name, + accounts: inputs, + arguments: raw_arguments, + } = request; + let (instruction_index, instruction) = select_instruction(idl, instruction_name)?; + + validate_selected_instruction(instruction, path.is_private())?; + validate_account_inputs(instruction, &inputs)?; + let arguments = parse_arguments(instruction, &raw_arguments)?; + validate_identities(instruction, &inputs, path.is_private())?; + let pdas = pda_resolution::resolve_pdas( + instruction, + &inputs, + &arguments, + program_id, + path.is_private(), + )?; + let accounts = flatten_accounts(instruction, &inputs, &pdas)?; + let variant_index = u32::try_from(instruction_index) + .map_err(|_| invalid_idl(instruction, "instructions", "instruction index exceeds u32"))?; + let fields: Vec<_> = instruction + .args + .iter() + .map(|argument| { + arguments + .get(&argument.name) + .ok_or_else(|| SpelTxError::MissingArgument { + name: argument.name.clone(), + }) + }) + .collect::>()?; + let instruction_data = + value::serialize_instruction(variant_index, &fields).map_err(|source| { + SpelTxError::InstructionSerialization { + instruction: instruction.name.clone(), + source, + } + })?; + + Ok(ResolvedParts { + accounts, + instruction_data, + }) +} + +fn select_instruction<'a>( + idl: &'a SpelIdl, + instruction: &str, +) -> Result<(usize, &'a IdlInstruction), SpelTxError> { + let matches: Vec<_> = idl + .instructions + .iter() + .enumerate() + .filter(|(_, candidate)| candidate.name == instruction) + .collect(); + match matches.as_slice() { + [] => Err(SpelTxError::UnknownInstruction { + instruction: instruction.to_owned(), + }), + [(index, selected)] => Ok((*index, *selected)), + _ => Err(SpelTxError::AmbiguousInstruction { + instruction: instruction.to_owned(), + matches: matches.len(), + }), + } +} + +fn validate_selected_instruction( + instruction: &IdlInstruction, + private_path: bool, +) -> Result<(), SpelTxError> { + let mut account_names = BTreeSet::new(); + let mut rest_seen = false; + for (index, account) in instruction.accounts.iter().enumerate() { + let path = format!("accounts[{index}]"); + if account.name.is_empty() { + return Err(invalid_idl(instruction, &path, "account name is empty")); + } + if !account_names.insert(&account.name) { + return Err(invalid_idl(instruction, &path, "duplicate account name")); + } + if account.rest { + if rest_seen || index + 1 != instruction.accounts.len() { + return Err(invalid_idl( + instruction, + &path, + "rest account must be the only final account", + )); + } + if account.pda.is_some() { + return Err(invalid_idl( + instruction, + &path, + "rest account cannot be a PDA", + )); + } + rest_seen = true; + } + if account.pda.is_some() && account.signer { + return Err(invalid_idl( + instruction, + &path, + "PDA account cannot be a signer", + )); + } + if account.init && !account.writable { + return Err(invalid_idl( + instruction, + &path, + "initialized account must be writable", + )); + } + if let Some(pda) = &account.pda { + if pda.private { + if !private_path { + return Err(invalid_idl( + instruction, + &path, + "public resolution does not support private PDAs", + )); + } + if !account.init { + return Err(invalid_idl( + instruction, + &path, + "private PDA reuse is unsupported", + )); + } + } + } + } + + let mut argument_names = BTreeSet::new(); + for (index, argument) in instruction.args.iter().enumerate() { + let path = format!("args[{index}]"); + if argument.name.is_empty() { + return Err(invalid_idl(instruction, &path, "argument name is empty")); + } + if !argument_names.insert(&argument.name) { + return Err(invalid_idl(instruction, &path, "duplicate argument name")); + } + value::validate_type(&argument.type_) + .map_err(|reason| invalid_idl(instruction, &path, reason))?; + } + + validate_pda_definitions(instruction)?; + + Ok(()) +} + +fn validate_pda_definitions(instruction: &IdlInstruction) -> Result<(), SpelTxError> { + for account in &instruction.accounts { + let Some(pda) = &account.pda else { + continue; + }; + if pda.seeds.is_empty() { + return Err(pda_resolution_error( + &account.name, + None, + "PDA requires at least one seed", + )); + } + + for (seed_index, seed) in pda.seeds.iter().enumerate() { + match seed { + IdlSeed::Const { value } if value.len() > 32 => { + return Err(pda_resolution_error( + &account.name, + Some(seed_index), + "constant seed exceeds 32 bytes", + )); + }, + IdlSeed::Const { .. } => {}, + IdlSeed::Account { path } => { + let target = pda_resolution::account_seed_target(instruction, path) + .ok_or_else(|| { + pda_resolution_error( + &account.name, + Some(seed_index), + "account seed references an undeclared account", + ) + })?; + if target.rest { + return Err(pda_resolution_error( + &account.name, + Some(seed_index), + "account seed cannot reference a rest account", + )); + } + }, + IdlSeed::Arg { path } => { + let argument = instruction + .args + .iter() + .find(|argument| argument.name == *path) + .ok_or_else(|| { + pda_resolution_error( + &account.name, + Some(seed_index), + "argument seed references an undeclared argument", + ) + })?; + value::validate_seed_type(&argument.type_).map_err(|reason| { + pda_resolution_error(&account.name, Some(seed_index), reason) + })?; + }, + } + } + } + Ok(()) +} + +fn validate_account_inputs( + instruction: &IdlInstruction, + inputs: &BTreeMap>, +) -> Result<(), SpelTxError> { + for name in inputs.keys() { + let Some(account) = instruction + .accounts + .iter() + .find(|account| account.name == *name) + else { + return Err(SpelTxError::UnexpectedAccount { name: name.clone() }); + }; + if matches!(&account.pda, Some(pda) if !pda.private) { + return Err(SpelTxError::UnexpectedAccount { name: name.clone() }); + } + } + + for account in &instruction.accounts { + if account.rest || matches!(&account.pda, Some(pda) if !pda.private) { + continue; + } + let Some(identities) = inputs.get(&account.name) else { + return Err(SpelTxError::MissingAccount { + name: account.name.clone(), + }); + }; + if identities.len() != 1 { + return Err(SpelTxError::InvalidAccountCount { + name: account.name.clone(), + expected: 1, + actual: identities.len(), + }); + } + } + + Ok(()) +} + +fn parse_arguments( + instruction: &IdlInstruction, + raw_arguments: &BTreeMap, +) -> Result, SpelTxError> { + for name in raw_arguments.keys() { + if !instruction + .args + .iter() + .any(|argument| argument.name == *name) + { + return Err(SpelTxError::UnexpectedArgument { name: name.clone() }); + } + } + + let mut arguments = BTreeMap::new(); + for argument in &instruction.args { + let raw = + raw_arguments + .get(&argument.name) + .ok_or_else(|| SpelTxError::MissingArgument { + name: argument.name.clone(), + })?; + let value = value::parse_argument(raw, &argument.type_) + .map_err(|error| argument_parse_error(&argument.name, error))?; + arguments.insert(argument.name.clone(), value); + } + Ok(arguments) +} + +fn validate_identities( + instruction: &IdlInstruction, + inputs: &BTreeMap>, + private_path: bool, +) -> Result<(), SpelTxError> { + for (account_index, account) in instruction.accounts.iter().enumerate() { + if account.pda.is_some() { + continue; + } + let identities = inputs + .get(&account.name) + .map(Vec::as_slice) + .unwrap_or_default(); + for (identity_index, identity) in identities.iter().enumerate() { + let index = account_index + identity_index; + if !private_path && !identity.is_public() { + return Err(SpelTxError::InvalidAccount { + account: account.name.clone(), + index, + reason: "public resolution does not accept private identities".to_string(), + }); + } + if account.init && matches!(identity, AccountIdentity::PublicNoSign(_)) { + return Err(SpelTxError::InvalidAccount { + account: account.name.clone(), + index, + reason: "initialized non-PDA account requires signing intent".to_string(), + }); + } + if account.signer && !identity_can_sign(identity) { + return Err(SpelTxError::InvalidAccount { + account: account.name.clone(), + index, + reason: "signer account requires signing-capable identity intent".to_string(), + }); + } + } + } + Ok(()) +} + +fn flatten_accounts( + instruction: &IdlInstruction, + inputs: &BTreeMap>, + pdas: &BTreeMap, +) -> Result, SpelTxError> { + let mut resolved = Vec::new(); + for account in &instruction.accounts { + if account.pda.is_some() { + let identity = + pdas.get(&account.name) + .cloned() + .ok_or_else(|| SpelTxError::PdaResolution { + account: account.name.clone(), + seed_index: None, + reason: "PDA did not resolve".to_string(), + })?; + resolved.push((account.name.clone(), identity)); + } else if account.rest { + if let Some(identities) = inputs.get(&account.name) { + resolved.extend( + identities + .iter() + .cloned() + .map(|identity| (account.name.clone(), identity)), + ); + } + } else { + let identity = inputs + .get(&account.name) + .and_then(|identities| identities.first()) + .cloned() + .ok_or_else(|| SpelTxError::MissingAccount { + name: account.name.clone(), + })?; + resolved.push((account.name.clone(), identity)); + } + } + + let mut seen = Vec::<(String, usize, nssa::AccountId)>::new(); + for (index, (account, identity)) in resolved.iter().enumerate() { + let account_id = identity.account_id(); + if let Some((first_account, first_index, _)) = + seen.iter().find(|(_, _, seen_id)| *seen_id == account_id) + { + return Err(SpelTxError::DuplicateResolvedAccount { + first_account: first_account.clone(), + first_index: *first_index, + account: account.clone(), + index, + }); + } + seen.push((account.clone(), index, account_id)); + } + + Ok(resolved.into_iter().map(|(_, identity)| identity).collect()) +} + +fn identity_can_sign(identity: &AccountIdentity) -> bool { + matches!( + identity, + AccountIdentity::Public(_) + | AccountIdentity::PublicKeycard { .. } + | AccountIdentity::PrivateOwned(_) + | AccountIdentity::PrivatePdaOwned(_) + | AccountIdentity::PrivateShared { .. } + | AccountIdentity::PrivatePdaShared { .. } + ) +} + +fn argument_parse_error(name: &str, error: ValueParseError) -> SpelTxError { + SpelTxError::ArgumentParse { + name: name.to_owned(), + path: error.path, + reason: error.reason, + } +} + +fn pda_resolution_error( + account: &str, + seed_index: Option, + reason: impl Into, +) -> SpelTxError { + SpelTxError::PdaResolution { + account: account.to_owned(), + seed_index, + reason: reason.into(), + } +} + +fn invalid_idl( + instruction: &IdlInstruction, + path: impl Into, + reason: impl Into, +) -> SpelTxError { + SpelTxError::InvalidIdl { + instruction: instruction.name.clone(), + path: path.into(), + reason: reason.into(), + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use nssa_core::{encryption::ViewingPublicKey, NullifierPublicKey}; + use risc0_zkvm::serde::Deserializer; + use serde::Deserialize; + use spel_framework_core::{ + idl::{IdlAccountItem, IdlArg, IdlPda, IdlSeed, IdlType}, + pda::{compute_pda, compute_private_pda_with_identifier, seed_from_str}, + }; + + use super::*; + + fn program_id() -> ProgramId { + [17; 8] + } + + fn make_idl(instructions: Vec) -> SpelIdl { + let mut idl = SpelIdl::new("resolver-test"); + idl.instructions = instructions; + idl + } + + fn instruction(name: &str, accounts: Vec, args: Vec) -> IdlInstruction { + IdlInstruction { + name: name.to_string(), + accounts, + args, + discriminator: None, + execution: None, + variant: None, + } + } + + fn account(name: &str) -> IdlAccountItem { + IdlAccountItem { + name: name.to_string(), + writable: false, + signer: false, + init: false, + owner: None, + pda: None, + rest: false, + visibility: vec![], + } + } + + fn pda_account(name: &str, private: bool, seeds: Vec) -> IdlAccountItem { + let mut account = account(name); + account.pda = Some(IdlPda { seeds, private }); + account + } + + fn arg(name: &str, type_: IdlType) -> IdlArg { + IdlArg { + name: name.to_string(), + type_, + } + } + + fn public_identity(byte: u8) -> AccountIdentity { + AccountIdentity::Public(nssa::AccountId::new([byte; 32])) + } + + fn identity_variants() -> Vec { + let vpk = ViewingPublicKey::from_seed(&[11; 32], &[12; 32]); + vec![ + AccountIdentity::Public(nssa::AccountId::new([1; 32])), + AccountIdentity::PublicNoSign(nssa::AccountId::new([2; 32])), + AccountIdentity::PublicKeycard { + account_id: nssa::AccountId::new([3; 32]), + key_path: "m/44'/60'/0'/0/0".to_string(), + }, + AccountIdentity::PrivateOwned(nssa::AccountId::new([4; 32])), + AccountIdentity::PrivateForeign { + npk: NullifierPublicKey([5; 32]), + vpk: vpk.clone(), + identifier: 5, + }, + AccountIdentity::PrivatePdaOwned(nssa::AccountId::new([6; 32])), + AccountIdentity::PrivatePdaForeign { + account_id: nssa::AccountId::new([7; 32]), + npk: NullifierPublicKey([7; 32]), + vpk: vpk.clone(), + identifier: 7, + }, + AccountIdentity::PrivateShared { + nsk: [8; 32], + npk: NullifierPublicKey([8; 32]), + vpk: vpk.clone(), + identifier: 8, + }, + AccountIdentity::PrivatePdaShared { + account_id: nssa::AccountId::new([9; 32]), + nsk: [9; 32], + npk: NullifierPublicKey([9; 32]), + vpk, + identifier: 9, + }, + ] + } + + #[test] + fn applies_public_and_private_identity_path_rules_to_all_wallet_variants() { + let idl = make_idl(vec![instruction( + "execute", + vec![account("account")], + vec![], + )]); + let identities = identity_variants(); + + for identity in identities.iter().take(3) { + assert!(resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::from([("account".to_string(), vec![identity.clone()])]), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .is_ok()); + } + + for identity in identities.iter().skip(3) { + let error = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::from([("account".to_string(), vec![identity.clone()])]), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("public resolution must reject a private identity"); + assert!(matches!( + error, + SpelTxError::InvalidAccount { + ref account, + index: 0, + .. + } if account == "account" + )); + } + + for identity in identities { + assert!(resolve( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::from([("account".to_string(), vec![identity])]), + arguments: BTreeMap::new(), + }, + program_id(), + ResolverPath::Private, + ) + .is_ok()); + } + } + + #[test] + fn applies_the_signer_intent_matrix_to_all_wallet_variants() { + let mut signer = account("signer"); + signer.signer = true; + let idl = make_idl(vec![instruction("execute", vec![signer], vec![])]); + + for (index, identity) in identity_variants().into_iter().enumerate() { + let result = resolve( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::from([("signer".to_string(), vec![identity])]), + arguments: BTreeMap::new(), + }, + program_id(), + ResolverPath::Private, + ); + let accepted = matches!(index, 0 | 2 | 3 | 5 | 7 | 8); + assert_eq!(result.is_ok(), accepted, "identity variant {index}"); + if !accepted { + assert!(matches!( + result, + Err(SpelTxError::InvalidAccount { + ref account, + index: 0, + .. + }) if account == "signer" + )); + } + } + } + + #[test] + fn resolves_in_idl_order_with_public_pda_and_rest_accounts() { + let payer = public_identity(1); + let member_one = public_identity(2); + let member_two = public_identity(3); + let state_seed = seed_from_str("state"); + let expected_state = + AccountIdentity::PublicNoSign(compute_pda(&program_id(), &[&state_seed])); + + let mut rest = account("members"); + rest.rest = true; + let idl = make_idl(vec![ + instruction("ignored", vec![], vec![]), + instruction( + "execute", + vec![ + account("payer"), + pda_account( + "state", + false, + vec![IdlSeed::Const { + value: "state".to_string(), + }], + ), + rest, + ], + vec![arg("amount", IdlType::Primitive("u32".to_string()))], + ), + ]); + let accounts = BTreeMap::from([ + ( + "members".to_string(), + vec![member_one.clone(), member_two.clone()], + ), + ("payer".to_string(), vec![payer.clone()]), + ]); + let arguments = BTreeMap::from([("amount".to_string(), "42".to_string())]); + + let resolved = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts, + arguments, + }, + program_id(), + ) + .unwrap(); + + assert_eq!(resolved.program_id(), program_id()); + assert_eq!( + resolved.accounts(), + &[payer, expected_state, member_one, member_two] + ); + assert_eq!(resolved.instruction_data(), &[1, 42]); + } + + #[test] + fn permits_empty_rest_inputs_and_rejects_malformed_rest_layouts() { + let mut rest = account("remaining"); + rest.rest = true; + let idl = make_idl(vec![instruction("execute", vec![rest], vec![])]); + + for accounts in [ + BTreeMap::new(), + BTreeMap::from([("remaining".to_string(), vec![])]), + ] { + let resolved = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts, + arguments: BTreeMap::new(), + }, + program_id(), + ) + .unwrap(); + assert!(resolved.accounts().is_empty()); + assert_eq!(resolved.instruction_data(), &[0]); + } + + let unknown = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::from([("unknown".to_string(), vec![])]), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject unknown account input"); + assert!(matches!( + unknown, + SpelTxError::UnexpectedAccount { ref name } if name == "unknown" + )); + + let mut malformed_rest = account("remaining"); + malformed_rest.rest = true; + let malformed_idl = make_idl(vec![instruction( + "malformed", + vec![malformed_rest, account("after")], + vec![], + )]); + let malformed = resolve_public_instruction( + SpelInstructionRequest { + idl: &malformed_idl, + instruction: "malformed", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject non-final rest account"); + assert!(matches!( + malformed, + SpelTxError::InvalidIdl { + ref instruction, + ref path, + .. + } if instruction == "malformed" && path == "accounts[0]" + )); + } + + #[test] + fn rejects_malformed_selected_idl_shapes_before_caller_input() { + let mut signer_pda = pda_account( + "state", + false, + vec![IdlSeed::Const { + value: "state".to_string(), + }], + ); + signer_pda.signer = true; + let signer_pda_idl = make_idl(vec![instruction("bad-pda", vec![signer_pda], vec![])]); + let signer_pda_error = resolve_public_instruction( + SpelInstructionRequest { + idl: &signer_pda_idl, + instruction: "bad-pda", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject a signer PDA"); + assert!(matches!( + signer_pda_error, + SpelTxError::InvalidIdl { + ref instruction, + ref path, + .. + } if instruction == "bad-pda" && path == "accounts[0]" + )); + + let unsupported_type_idl = make_idl(vec![instruction( + "bad-type", + vec![], + vec![arg( + "value", + IdlType::Defined { + defined: "Custom".to_string(), + }, + )], + )]); + let unsupported_type = resolve_public_instruction( + SpelInstructionRequest { + idl: &unsupported_type_idl, + instruction: "bad-type", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject defined instruction argument types"); + assert!(matches!( + unsupported_type, + SpelTxError::InvalidIdl { + ref instruction, + ref path, + .. + } if instruction == "bad-type" && path == "args[0]" + )); + + let mut private_pda = pda_account( + "state", + true, + vec![IdlSeed::Const { + value: "state".to_string(), + }], + ); + private_pda.writable = true; + let private_reuse_idl = make_idl(vec![instruction( + "private-reuse", + vec![private_pda], + vec![], + )]); + let private_reuse = resolve( + SpelInstructionRequest { + idl: &private_reuse_idl, + instruction: "private-reuse", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ResolverPath::Private, + ) + .err() + .expect("top-level private PDA reuse is unsupported"); + assert!(matches!( + private_reuse, + SpelTxError::InvalidIdl { + ref instruction, + ref path, + .. + } if instruction == "private-reuse" && path == "accounts[0]" + )); + } + + #[test] + fn resolves_exact_account_seed_name_before_id_alias() { + let owner = public_identity(1); + let literal_owner_id = public_identity(2); + let idl = make_idl(vec![instruction( + "derive", + vec![ + account("owner"), + account("owner.id"), + pda_account( + "state", + false, + vec![IdlSeed::Account { + path: "owner.id".to_string(), + }], + ), + ], + vec![], + )]); + let owner_id_seed = literal_owner_id.account_id().into_value(); + let expected = compute_pda(&program_id(), &[&owner_id_seed]); + + let resolved = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "derive", + accounts: BTreeMap::from([ + ("owner".to_string(), vec![owner.clone()]), + ("owner.id".to_string(), vec![literal_owner_id.clone()]), + ]), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .unwrap(); + + assert_eq!( + resolved.accounts(), + &[ + owner, + literal_owner_id, + AccountIdentity::PublicNoSign(expected), + ] + ); + } + + #[test] + fn ignores_malformed_unselected_instructions() { + let idl = make_idl(vec![ + instruction("broken", vec![pda_account("bad", false, vec![])], vec![]), + instruction("ready", vec![], vec![]), + ]); + + let resolved = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "ready", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .unwrap(); + + assert_eq!(resolved.instruction_data(), &[1]); + } + + #[test] + fn reports_exact_instruction_selection_errors() { + let duplicate_idl = make_idl(vec![ + instruction("same", vec![], vec![]), + instruction("same", vec![], vec![]), + ]); + let ambiguous = resolve_public_instruction( + SpelInstructionRequest { + idl: &duplicate_idl, + instruction: "same", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + ambiguous, + SpelTxError::AmbiguousInstruction { + ref instruction, + matches: 2, + } if instruction == "same" + )); + + let missing = resolve_public_instruction( + SpelInstructionRequest { + idl: &duplicate_idl, + instruction: "other", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + missing, + SpelTxError::UnknownInstruction { ref instruction } if instruction == "other" + )); + } + + #[test] + fn validates_account_input_keys_and_counts() { + let idl = make_idl(vec![instruction( + "execute", + vec![ + account("payer"), + pda_account( + "state", + false, + vec![IdlSeed::Const { + value: "state".to_string(), + }], + ), + ], + vec![], + )]); + + let missing = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!(missing, SpelTxError::MissingAccount { ref name } if name == "payer")); + + let public_pda_input = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::from([("state".to_string(), vec![public_identity(1)])]), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + public_pda_input, + SpelTxError::UnexpectedAccount { ref name } if name == "state" + )); + + let invalid_count = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::from([("payer".to_string(), vec![])]), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + invalid_count, + SpelTxError::InvalidAccountCount { + ref name, + expected: 1, + actual: 0, + } if name == "payer" + )); + } + + #[test] + fn validates_identity_intent_and_duplicate_resolved_accounts() { + let private_identity = AccountIdentity::PrivateOwned(nssa::AccountId::new([1; 32])); + let idl = make_idl(vec![instruction("execute", vec![account("payer")], vec![])]); + let public_error = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::from([("payer".to_string(), vec![private_identity])]), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + public_error, + SpelTxError::InvalidAccount { + ref account, + index: 0, + .. + } if account == "payer" + )); + + let mut initialized = account("new_account"); + initialized.init = true; + initialized.writable = true; + let init_idl = make_idl(vec![instruction("init", vec![initialized], vec![])]); + let init_error = resolve_public_instruction( + SpelInstructionRequest { + idl: &init_idl, + instruction: "init", + accounts: BTreeMap::from([( + "new_account".to_string(), + vec![AccountIdentity::PublicNoSign(nssa::AccountId::new([2; 32]))], + )]), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + init_error, + SpelTxError::InvalidAccount { + ref account, + index: 0, + .. + } if account == "new_account" + )); + + let mut rest = account("remaining"); + rest.rest = true; + let duplicate_idl = make_idl(vec![instruction( + "duplicate", + vec![account("first"), rest], + vec![], + )]); + let duplicate = public_identity(7); + let duplicate_error = resolve_public_instruction( + SpelInstructionRequest { + idl: &duplicate_idl, + instruction: "duplicate", + accounts: BTreeMap::from([ + ("first".to_string(), vec![duplicate.clone()]), + ("remaining".to_string(), vec![duplicate]), + ]), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + duplicate_error, + SpelTxError::DuplicateResolvedAccount { + ref first_account, + first_index: 0, + ref account, + index: 1, + } if first_account == "first" && account == "remaining" + )); + } + + #[test] + fn private_resolution_accepts_private_non_pda_and_checks_private_pda_identity() { + let owned = AccountIdentity::PrivateOwned(nssa::AccountId::new([4; 32])); + let non_pda_idl = make_idl(vec![instruction("private", vec![account("owner")], vec![])]); + let resolved = resolve( + SpelInstructionRequest { + idl: &non_pda_idl, + instruction: "private", + accounts: BTreeMap::from([("owner".to_string(), vec![owned.clone()])]), + arguments: BTreeMap::new(), + }, + program_id(), + ResolverPath::Private, + ) + .unwrap(); + assert_eq!(resolved.accounts, vec![owned]); + + let mut private_pda = pda_account( + "vault", + true, + vec![IdlSeed::Const { + value: "vault".to_string(), + }], + ); + private_pda.init = true; + private_pda.writable = true; + let private_pda_idl = make_idl(vec![instruction("create", vec![private_pda], vec![])]); + let seed = seed_from_str("vault"); + let npk = NullifierPublicKey([5; 32]); + let vpk = ViewingPublicKey::from_seed(&[6; 32], &[7; 32]); + let identifier = 9; + let account_id = + compute_private_pda_with_identifier(&program_id(), &[&seed], &npk, &vpk, identifier); + let identity = AccountIdentity::PrivatePdaForeign { + account_id, + npk, + vpk, + identifier, + }; + + let missing = resolve( + SpelInstructionRequest { + idl: &private_pda_idl, + instruction: "create", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ResolverPath::Private, + ) + .err() + .expect("private PDA input is required"); + assert!(matches!(missing, SpelTxError::MissingAccount { ref name } if name == "vault")); + + let invalid_count = resolve( + SpelInstructionRequest { + idl: &private_pda_idl, + instruction: "create", + accounts: BTreeMap::from([( + "vault".to_string(), + vec![identity.clone(), identity.clone()], + )]), + arguments: BTreeMap::new(), + }, + program_id(), + ResolverPath::Private, + ) + .err() + .expect("private PDA input must have exactly one identity"); + assert!(matches!( + invalid_count, + SpelTxError::InvalidAccountCount { + ref name, + expected: 1, + actual: 2, + } if name == "vault" + )); + + let unsupported_identity = resolve( + SpelInstructionRequest { + idl: &private_pda_idl, + instruction: "create", + accounts: BTreeMap::from([( + "vault".to_string(), + vec![AccountIdentity::PrivatePdaOwned(account_id)], + )]), + arguments: BTreeMap::new(), + }, + program_id(), + ResolverPath::Private, + ) + .err() + .expect("owned private PDA initialization is unsupported"); + assert!(matches!( + unsupported_identity, + SpelTxError::InvalidAccount { + ref account, + index: 0, + .. + } if account == "vault" + )); + + let resolved = resolve( + SpelInstructionRequest { + idl: &private_pda_idl, + instruction: "create", + accounts: BTreeMap::from([("vault".to_string(), vec![identity.clone()])]), + arguments: BTreeMap::new(), + }, + program_id(), + ResolverPath::Private, + ) + .unwrap(); + assert_eq!(resolved.accounts, vec![identity]); + + let mismatch = resolve( + SpelInstructionRequest { + idl: &private_pda_idl, + instruction: "create", + accounts: BTreeMap::from([( + "vault".to_string(), + vec![AccountIdentity::PrivatePdaForeign { + account_id: nssa::AccountId::new([8; 32]), + npk: NullifierPublicKey([5; 32]), + vpk: ViewingPublicKey::from_seed(&[6; 32], &[7; 32]), + identifier, + }], + )]), + arguments: BTreeMap::new(), + }, + program_id(), + ResolverPath::Private, + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + mismatch, + SpelTxError::InvalidAccount { + ref account, + index: 0, + .. + } if account == "vault" + )); + } + + #[test] + fn resolves_pda_dependencies_and_rejects_cycles_and_empty_seed_lists() { + let idl = make_idl(vec![instruction( + "derive", + vec![ + account("payer"), + pda_account( + "second", + false, + vec![ + IdlSeed::Account { + path: "first".to_string(), + }, + IdlSeed::Account { + path: "payer.id".to_string(), + }, + IdlSeed::Arg { + path: "sequence".to_string(), + }, + ], + ), + pda_account( + "first", + false, + vec![IdlSeed::Const { + value: "first".to_string(), + }], + ), + ], + vec![arg("sequence", IdlType::Primitive("u16".to_string()))], + )]); + let payer = public_identity(3); + let first_seed = seed_from_str("first"); + let first = compute_pda(&program_id(), &[&first_seed]); + let first_value = first.into_value(); + let payer_value = payer.account_id().into_value(); + let mut sequence_seed = [0; 32]; + sequence_seed[..2].copy_from_slice(&513_u16.to_le_bytes()); + let second = compute_pda(&program_id(), &[&first_value, &payer_value, &sequence_seed]); + + let resolved = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "derive", + accounts: BTreeMap::from([("payer".to_string(), vec![payer.clone()])]), + arguments: BTreeMap::from([("sequence".to_string(), "513".to_string())]), + }, + program_id(), + ) + .unwrap(); + assert_eq!( + resolved.accounts(), + &[ + payer, + AccountIdentity::PublicNoSign(second), + AccountIdentity::PublicNoSign(first), + ] + ); + + let cycle_idl = make_idl(vec![instruction( + "cycle", + vec![ + pda_account( + "a", + false, + vec![IdlSeed::Account { + path: "b".to_string(), + }], + ), + pda_account( + "b", + false, + vec![IdlSeed::Account { + path: "a".to_string(), + }], + ), + ], + vec![], + )]); + let cycle = resolve_public_instruction( + SpelInstructionRequest { + idl: &cycle_idl, + instruction: "cycle", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + cycle, + SpelTxError::PdaResolution { + ref account, + seed_index: Some(0), + .. + } if account == "b" + )); + + let malformed_idl = make_idl(vec![instruction( + "empty", + vec![pda_account("pda", false, vec![])], + vec![], + )]); + let malformed = resolve_public_instruction( + SpelInstructionRequest { + idl: &malformed_idl, + instruction: "empty", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + assert!(matches!( + malformed, + SpelTxError::PdaResolution { + ref account, + seed_index: None, + .. + } if account == "pda" + )); + + let unknown_seed_idl = make_idl(vec![instruction( + "unknown-seed", + vec![ + account("payer"), + pda_account( + "state", + false, + vec![IdlSeed::Arg { + path: "missing".to_string(), + }], + ), + ], + vec![], + )]); + let unknown_seed = resolve_public_instruction( + SpelInstructionRequest { + idl: &unknown_seed_idl, + instruction: "unknown-seed", + accounts: BTreeMap::new(), + arguments: BTreeMap::new(), + }, + program_id(), + ) + .err() + .expect("resolver must reject an unknown PDA seed argument"); + assert!(matches!( + unknown_seed, + SpelTxError::PdaResolution { + ref account, + seed_index: Some(0), + .. + } if account == "state" + )); + } + + #[test] + fn serializes_canonical_arguments_in_idl_order() { + #[derive(Debug, Deserialize, PartialEq)] + enum TestInstruction { + Ignored, + Execute { + bytes: [u8; 3], + values: Vec, + enabled: Option, + maximum: u128, + }, + } + + let idl = make_idl(vec![ + instruction("ignored", vec![], vec![]), + instruction( + "execute", + vec![], + vec![ + arg( + "bytes", + IdlType::Array { + array: (Box::new(IdlType::Primitive("u8".to_string())), 3), + }, + ), + arg( + "values", + IdlType::Vec { + vec: Box::new(IdlType::Primitive("u32".to_string())), + }, + ), + arg( + "enabled", + IdlType::Option { + option: Box::new(IdlType::Primitive("bool".to_string())), + }, + ), + arg("maximum", IdlType::Primitive("u128".to_string())), + ], + ), + ]); + let resolved = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::new(), + arguments: BTreeMap::from([ + ("values".to_string(), "[4,5]".to_string()), + ( + "maximum".to_string(), + "+340282366920938463463374607431768211455".to_string(), + ), + ("enabled".to_string(), "true".to_string()), + ("bytes".to_string(), "[1,2,3]".to_string()), + ]), + }, + program_id(), + ) + .unwrap(); + let instruction = + TestInstruction::deserialize(&mut Deserializer::new(resolved.instruction_data())) + .unwrap(); + + assert_eq!( + instruction, + TestInstruction::Execute { + bytes: [1, 2, 3], + values: vec![4, 5], + enabled: Some(true), + maximum: u128::MAX, + } + ); + } + + #[test] + fn reports_nested_argument_parse_paths() { + let idl = make_idl(vec![instruction( + "execute", + vec![], + vec![arg( + "values", + IdlType::Vec { + vec: Box::new(IdlType::Primitive("u32".to_string())), + }, + )], + )]); + let error = resolve_public_instruction( + SpelInstructionRequest { + idl: &idl, + instruction: "execute", + accounts: BTreeMap::new(), + arguments: BTreeMap::from([("values".to_string(), "[1,\"bad\"]".to_string())]), + }, + program_id(), + ) + .err() + .expect("resolver must reject invalid input"); + + assert!(matches!( + error, + SpelTxError::ArgumentParse { + ref name, + path: ref actual_path, + .. + } if name == "values" && actual_path == &[1] + )); + } +} diff --git a/spel-cli/src/tx/value.rs b/spel-cli/src/tx/value.rs new file mode 100644 index 00000000..a8f199e7 --- /dev/null +++ b/spel-cli/src/tx/value.rs @@ -0,0 +1,904 @@ +use crate::{ + parse::{parse_program_id, parse_value, ParsedValue}, + serialize::SerializeError, +}; +use nssa::AccountId; +use nssa_core::{encryption::ViewingPublicKey, program::ProgramId, NullifierPublicKey}; +use serde::ser::{SerializeSeq, SerializeTuple, SerializeTupleVariant}; +use serde::Serialize; +use serde_json::Value; +use spel_framework_core::{idl::IdlType, pda::parse_bytes32}; + +#[derive(Debug)] +pub(crate) struct ValueParseError { + pub(crate) path: Vec, + pub(crate) reason: String, +} + +impl ValueParseError { + fn new(path: Vec, reason: impl Into) -> Self { + Self { + path, + reason: reason.into(), + } + } +} + +pub(crate) enum ArgumentValue { + Bool(bool), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + U128(u128), + I8(i8), + I16(i16), + I32(i32), + I64(i64), + I128(i128), + String(String), + AccountId(AccountId), + ProgramId(ProgramId), + NullifierPublicKey(NullifierPublicKey), + ViewingPublicKey(ViewingPublicKey), + Array(Vec), + Vec(Vec), + Option(Option>), +} + +impl ArgumentValue { + pub(crate) fn seed_bytes(&self, ty: &IdlType) -> Result<[u8; 32], &'static str> { + match (self, ty) { + (Self::Array(values), IdlType::Array { array }) + if matches!(array.0.as_ref(), IdlType::Primitive(primitive) if primitive == "u8") + && (1..=32).contains(&array.1) => + { + let mut seed = [0; 32]; + for (index, value) in values.iter().enumerate() { + let Self::U8(value) = value else { + return Err("unsupported argument type for PDA seed"); + }; + seed[index] = *value; + } + Ok(seed) + }, + (Self::U8(value), IdlType::Primitive(primitive)) if primitive == "u8" => { + Ok(pad_seed(&value.to_le_bytes())) + }, + (Self::U16(value), IdlType::Primitive(primitive)) if primitive == "u16" => { + Ok(pad_seed(&value.to_le_bytes())) + }, + (Self::U32(value), IdlType::Primitive(primitive)) if primitive == "u32" => { + Ok(pad_seed(&value.to_le_bytes())) + }, + (Self::U64(value), IdlType::Primitive(primitive)) if primitive == "u64" => { + Ok(pad_seed(&value.to_le_bytes())) + }, + (Self::U128(value), IdlType::Primitive(primitive)) if primitive == "u128" => { + Ok(pad_seed(&value.to_le_bytes())) + }, + (Self::String(value), IdlType::Primitive(primitive)) + if matches!(primitive.as_str(), "string" | "String") => + { + if value.len() > 32 { + return Err("string argument exceeds 32 bytes for PDA seed"); + } + Ok(pad_seed(value.as_bytes())) + }, + (Self::AccountId(value), IdlType::Primitive(primitive)) + if primitive == "account_id" => + { + Ok(*value.value()) + }, + (Self::ProgramId(value), IdlType::Primitive(primitive)) + if primitive == "program_id" => + { + let mut seed = [0; 32]; + for (index, word) in value.iter().enumerate() { + seed[index * 4..(index + 1) * 4].copy_from_slice(&word.to_le_bytes()); + } + Ok(seed) + }, + _ => Err("unsupported argument type for PDA seed"), + } + } +} + +impl Serialize for ArgumentValue { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Self::Bool(value) => serializer.serialize_bool(*value), + Self::U8(value) => serializer.serialize_u8(*value), + Self::U16(value) => serializer.serialize_u16(*value), + Self::U32(value) => serializer.serialize_u32(*value), + Self::U64(value) => serializer.serialize_u64(*value), + Self::U128(value) => serializer.serialize_u128(*value), + Self::I8(value) => serializer.serialize_i8(*value), + Self::I16(value) => serializer.serialize_i16(*value), + Self::I32(value) => serializer.serialize_i32(*value), + Self::I64(value) => serializer.serialize_i64(*value), + Self::I128(value) => serializer.serialize_i128(*value), + Self::String(value) => serializer.serialize_str(value), + Self::AccountId(value) => value.serialize(serializer), + Self::ProgramId(value) => value.serialize(serializer), + Self::NullifierPublicKey(value) => value.serialize(serializer), + Self::ViewingPublicKey(value) => value.serialize(serializer), + Self::Array(values) => { + let mut tuple = serializer.serialize_tuple(values.len())?; + for value in values { + tuple.serialize_element(value)?; + } + tuple.end() + }, + Self::Vec(values) => { + let mut sequence = serializer.serialize_seq(Some(values.len()))?; + for value in values { + sequence.serialize_element(value)?; + } + sequence.end() + }, + Self::Option(None) => serializer.serialize_none(), + Self::Option(Some(value)) => serializer.serialize_some(value), + } + } +} + +pub(crate) fn validate_type(ty: &IdlType) -> Result<(), String> { + match ty { + IdlType::Primitive(primitive) if is_supported_primitive(primitive) => Ok(()), + IdlType::Primitive(_) => Err("unsupported instruction argument primitive".to_string()), + IdlType::Array { array } => validate_type(&array.0), + IdlType::Vec { vec } => validate_type(vec), + IdlType::Option { option } if matches!(option.as_ref(), IdlType::Option { .. }) => { + Err("immediate nested option is unsupported".to_string()) + }, + IdlType::Option { option } => validate_type(option), + IdlType::Defined { .. } => { + Err("defined instruction argument types are unsupported".to_string()) + }, + } +} + +pub(crate) fn validate_seed_type(ty: &IdlType) -> Result<(), &'static str> { + match ty { + IdlType::Array { array } + if matches!(array.0.as_ref(), IdlType::Primitive(primitive) if primitive == "u8") + && (1..=32).contains(&array.1) => + { + Ok(()) + }, + IdlType::Primitive(primitive) + if matches!( + primitive.as_str(), + "u8" | "u16" + | "u32" + | "u64" + | "u128" + | "string" + | "String" + | "account_id" + | "program_id" + ) => + { + Ok(()) + }, + _ => Err("unsupported argument type for PDA seed"), + } +} + +pub(crate) fn parse_argument(raw: &str, ty: &IdlType) -> Result { + match ty { + IdlType::Array { .. } | IdlType::Vec { .. } | IdlType::Option { .. } => { + match serde_json::from_str(raw) { + Ok(value) => match parse_json_value(&value, ty, vec![]) { + Ok(value) => Ok(value), + Err(json_error) => parse_legacy_argument(raw, ty).or(Err(json_error)), + }, + Err(_) => parse_legacy_argument(raw, ty), + } + }, + _ => parse_scalar(raw, ty, vec![]), + } +} + +pub(crate) fn serialize_instruction( + variant_index: u32, + fields: &[&ArgumentValue], +) -> Result, SerializeError> { + let instruction = SerializedInstruction { + variant_index, + fields, + }; + + risc0_zkvm::serde::to_vec(&instruction) + .map_err(|error| SerializeError::Risc0(error.to_string())) +} + +fn parse_json_value( + value: &Value, + ty: &IdlType, + path: Vec, +) -> Result { + match ty { + IdlType::Primitive(primitive) => parse_json_primitive(value, primitive, path), + IdlType::Array { array } => { + let values = value + .as_array() + .ok_or_else(|| ValueParseError::new(path.clone(), "expected JSON array"))?; + if values.len() != array.1 { + return Err(ValueParseError::new( + path, + "JSON array has an unexpected length", + )); + } + + let mut parsed = Vec::with_capacity(values.len()); + for (index, value) in values.iter().enumerate() { + let mut nested_path = path.clone(); + nested_path.push(index); + parsed.push(parse_json_value(value, &array.0, nested_path)?); + } + Ok(ArgumentValue::Array(parsed)) + }, + IdlType::Vec { vec } => { + let values = value + .as_array() + .ok_or_else(|| ValueParseError::new(path.clone(), "expected JSON array"))?; + let mut parsed = Vec::with_capacity(values.len()); + for (index, value) in values.iter().enumerate() { + let mut nested_path = path.clone(); + nested_path.push(index); + parsed.push(parse_json_value(value, vec, nested_path)?); + } + Ok(ArgumentValue::Vec(parsed)) + }, + IdlType::Option { option } if value.is_null() => Ok(ArgumentValue::Option(None)), + IdlType::Option { option } => Ok(ArgumentValue::Option(Some(Box::new(parse_json_value( + value, option, path, + )?)))), + IdlType::Defined { .. } => Err(ValueParseError::new( + path, + "defined instruction argument types are unsupported", + )), + } +} + +fn parse_json_primitive( + value: &Value, + primitive: &str, + path: Vec, +) -> Result { + match primitive { + "bool" => value + .as_bool() + .map(ArgumentValue::Bool) + .ok_or_else(|| ValueParseError::new(path, "expected JSON boolean")), + "string" + | "String" + | "account_id" + | "program_id" + | "nullifier_public_key" + | "viewing_public_key" => value + .as_str() + .ok_or_else(|| ValueParseError::new(path.clone(), "expected JSON string")) + .and_then(|raw| parse_primitive(raw, primitive, path)), + primitive if is_integer_primitive(primitive) => value + .as_number() + .ok_or_else(|| ValueParseError::new(path.clone(), "expected JSON integer")) + .and_then(|number| parse_primitive(&number.to_string(), primitive, path)), + _ => Err(ValueParseError::new( + path, + "unsupported instruction argument primitive", + )), + } +} + +fn parse_scalar( + raw: &str, + ty: &IdlType, + path: Vec, +) -> Result { + let IdlType::Primitive(primitive) = ty else { + return Err(ValueParseError::new( + path, + "expected instruction argument primitive", + )); + }; + parse_primitive(raw, primitive, path) +} + +fn parse_legacy_argument(raw: &str, ty: &IdlType) -> Result { + match ty { + IdlType::Array { .. } | IdlType::Vec { .. } => { + let value = parse_value(raw, ty) + .map_err(|_| invalid_legacy_value(legacy_error_path(raw, ty)))?; + argument_value_from_cli(value, ty) + }, + IdlType::Option { option } => { + if matches!(raw, "none" | "null") || raw.is_empty() { + return Ok(ArgumentValue::Option(None)); + } + parse_argument(raw, option).map(|value| ArgumentValue::Option(Some(Box::new(value)))) + }, + IdlType::Primitive(_) | IdlType::Defined { .. } => Err(invalid_legacy_value(vec![])), + } +} + +fn invalid_legacy_value(path: Vec) -> ValueParseError { + ValueParseError::new(path, "invalid legacy CLI value") +} + +fn legacy_error_path(raw: &str, ty: &IdlType) -> Vec { + match ty { + IdlType::Array { array } if matches!(array.0.as_ref(), IdlType::Primitive(primitive) if primitive == "u32") => { + csv_error_path(raw, Some(array.1), |value| value.parse::().is_ok()) + }, + IdlType::Vec { vec } => match vec.as_ref() { + IdlType::Primitive(primitive) if primitive == "u8" => { + csv_error_path(raw, None, |value| value.parse::().is_ok()) + }, + IdlType::Primitive(primitive) if primitive == "u32" => { + csv_error_path(raw, None, |value| value.parse::().is_ok()) + }, + IdlType::Array { array } if matches!(array.0.as_ref(), IdlType::Primitive(primitive) if primitive == "u8") => { + csv_error_path(raw, None, |value| { + legacy_byte_array_is_valid(value, array.1) + }) + }, + _ => Vec::new(), + }, + IdlType::Primitive(_) | IdlType::Option { .. } | IdlType::Defined { .. } => Vec::new(), + IdlType::Array { .. } => Vec::new(), + } +} + +fn csv_error_path(raw: &str, expected_len: Option, is_valid: F) -> Vec +where + F: Fn(&str) -> bool, +{ + let values: Vec<_> = raw.split(',').map(str::trim).collect(); + if expected_len.is_some_and(|expected_len| values.len() != expected_len) { + return Vec::new(); + } + values + .into_iter() + .position(|value| !is_valid(value)) + .map_or_else(Vec::new, |index| vec![index]) +} + +fn legacy_byte_array_is_valid(raw: &str, element_len: usize) -> bool { + if element_len == 32 { + return crate::hex::decode_bytes_32(raw).is_ok(); + } + + let hex = raw + .strip_prefix("0x") + .or_else(|| raw.strip_prefix("0X")) + .unwrap_or(raw); + crate::hex::hex_decode(hex).is_ok_and(|bytes| bytes.len() == element_len) +} + +fn argument_value_from_cli( + value: ParsedValue, + ty: &IdlType, +) -> Result { + match (value, ty) { + (ParsedValue::ByteArray(value), IdlType::Array { array }) + if matches!(array.0.as_ref(), IdlType::Primitive(primitive) if primitive == "u8") + && value.len() == array.1 => + { + Ok(ArgumentValue::Array( + value.into_iter().map(ArgumentValue::U8).collect(), + )) + }, + (ParsedValue::U32Array(value), IdlType::Array { array }) + if matches!(array.0.as_ref(), IdlType::Primitive(primitive) if primitive == "u32") + && value.len() == array.1 => + { + Ok(ArgumentValue::Array( + value.into_iter().map(ArgumentValue::U32).collect(), + )) + }, + (ParsedValue::ByteArray(value), IdlType::Vec { vec }) if matches!(vec.as_ref(), IdlType::Primitive(primitive) if primitive == "u8") => { + Ok(ArgumentValue::Vec( + value.into_iter().map(ArgumentValue::U8).collect(), + )) + }, + (ParsedValue::Raw(value), IdlType::Vec { vec }) + if value.is_empty() + && matches!(vec.as_ref(), IdlType::Primitive(primitive) if primitive == "u32") => + { + Ok(ArgumentValue::Vec(Vec::new())) + }, + (ParsedValue::Raw(value), ty) => Err(invalid_legacy_value(legacy_error_path(&value, ty))), + (ParsedValue::U32Array(value), IdlType::Vec { vec }) if matches!(vec.as_ref(), IdlType::Primitive(primitive) if primitive == "u32") => { + Ok(ArgumentValue::Vec( + value.into_iter().map(ArgumentValue::U32).collect(), + )) + }, + (ParsedValue::ByteArrayVec(values), IdlType::Vec { vec }) => { + let IdlType::Array { array } = vec.as_ref() else { + return Err(invalid_legacy_value(vec![])); + }; + if !matches!(array.0.as_ref(), IdlType::Primitive(primitive) if primitive == "u8") + || values.iter().any(|value| value.len() != array.1) + { + return Err(invalid_legacy_value(vec![])); + } + Ok(ArgumentValue::Vec( + values + .into_iter() + .map(|value| { + ArgumentValue::Array(value.into_iter().map(ArgumentValue::U8).collect()) + }) + .collect(), + )) + }, + _ => Err(invalid_legacy_value(vec![])), + } +} + +fn parse_primitive( + raw: &str, + primitive: &str, + path: Vec, +) -> Result { + macro_rules! parse_unsigned { + ($ty:ty, $variant:ident) => {{ + if !is_decimal(raw, false) { + return Err(ValueParseError::new( + path, + "invalid unsigned decimal integer", + )); + } + raw.parse::<$ty>() + .map(ArgumentValue::$variant) + .map_err(|_| ValueParseError::new(path, "integer is outside the supported range")) + }}; + } + + macro_rules! parse_signed { + ($ty:ty, $variant:ident) => {{ + if !is_decimal(raw, true) { + return Err(ValueParseError::new(path, "invalid signed decimal integer")); + } + raw.parse::<$ty>() + .map(ArgumentValue::$variant) + .map_err(|_| ValueParseError::new(path, "integer is outside the supported range")) + }}; + } + + match primitive { + "bool" => match raw { + "true" | "1" | "yes" => Ok(ArgumentValue::Bool(true)), + "false" | "0" | "no" => Ok(ArgumentValue::Bool(false)), + _ => Err(ValueParseError::new(path, "invalid boolean")), + }, + "u8" => parse_unsigned!(u8, U8), + "u16" => parse_unsigned!(u16, U16), + "u32" => parse_unsigned!(u32, U32), + "u64" => parse_unsigned!(u64, U64), + "u128" => parse_unsigned!(u128, U128), + "i8" => parse_signed!(i8, I8), + "i16" => parse_signed!(i16, I16), + "i32" => parse_signed!(i32, I32), + "i64" => parse_signed!(i64, I64), + "i128" => parse_signed!(i128, I128), + "string" | "String" => Ok(ArgumentValue::String(raw.to_owned())), + "account_id" => parse_bytes32(raw) + .map(AccountId::new) + .map(ArgumentValue::AccountId) + .map_err(|_| ValueParseError::new(path, "invalid account ID")), + "program_id" => parse_program_id(raw) + .map(ArgumentValue::ProgramId) + .map_err(|_| ValueParseError::new(path, "invalid program ID")), + "nullifier_public_key" => parse_bytes32(raw) + .map(NullifierPublicKey) + .map(ArgumentValue::NullifierPublicKey) + .map_err(|_| ValueParseError::new(path, "invalid nullifier public key")), + "viewing_public_key" => { + let hex = raw + .strip_prefix("0x") + .or_else(|| raw.strip_prefix("0X")) + .unwrap_or(raw); + crate::hex::hex_decode(hex) + .map_err(|_| ValueParseError::new(path.clone(), "invalid viewing public key")) + .and_then(|bytes| { + ViewingPublicKey::from_bytes(bytes) + .map(ArgumentValue::ViewingPublicKey) + .map_err(|_| ValueParseError::new(path, "invalid viewing public key")) + }) + }, + _ => Err(ValueParseError::new( + path, + "unsupported instruction argument primitive", + )), + } +} + +fn is_supported_primitive(primitive: &str) -> bool { + matches!( + primitive, + "bool" + | "u8" + | "u16" + | "u32" + | "u64" + | "u128" + | "i8" + | "i16" + | "i32" + | "i64" + | "i128" + | "string" + | "String" + | "account_id" + | "program_id" + | "nullifier_public_key" + | "viewing_public_key" + ) +} + +fn is_integer_primitive(primitive: &str) -> bool { + matches!( + primitive, + "u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32" | "i64" | "i128" + ) +} + +fn is_decimal(raw: &str, signed: bool) -> bool { + let digits = if signed { + raw.strip_prefix('+') + .or_else(|| raw.strip_prefix('-')) + .unwrap_or(raw) + } else { + raw.strip_prefix('+').unwrap_or(raw) + }; + !digits.is_empty() && digits.bytes().all(|byte| byte.is_ascii_digit()) +} + +fn pad_seed(value: &[u8]) -> [u8; 32] { + let mut seed = [0; 32]; + seed[..value.len()].copy_from_slice(value); + seed +} + +struct SerializedInstruction<'a> { + variant_index: u32, + fields: &'a [&'a ArgumentValue], +} + +impl Serialize for SerializedInstruction<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut instruction = + serializer.serialize_tuple_variant("", self.variant_index, "", self.fields.len())?; + for field in self.fields { + instruction.serialize_field(field)?; + } + instruction.end() + } +} + +#[cfg(test)] +mod tests { + use nssa::AccountId; + use risc0_zkvm::serde::Deserializer; + use serde::Deserialize; + + use super::*; + + fn primitive(name: &str) -> IdlType { + IdlType::Primitive(name.to_string()) + } + + fn array(element: IdlType, len: usize) -> IdlType { + IdlType::Array { + array: (Box::new(element), len), + } + } + + fn vector(element: IdlType) -> IdlType { + IdlType::Vec { + vec: Box::new(element), + } + } + + fn option(element: IdlType) -> IdlType { + IdlType::Option { + option: Box::new(element), + } + } + + #[test] + fn parses_every_supported_scalar_primitive() { + for (raw, name) in [ + ("true", "bool"), + ("+1", "u8"), + ("+1", "u16"), + ("+1", "u32"), + ("+1", "u64"), + ("+1", "u128"), + ("-1", "i8"), + ("-1", "i16"), + ("-1", "i32"), + ("-1", "i64"), + ("-1", "i128"), + ] { + assert!(parse_argument(raw, &primitive(name)).is_ok(), "{name}"); + } + assert!(parse_argument("raw string", &primitive("string")).is_ok()); + assert!(parse_argument("raw string", &primitive("String")).is_ok()); + assert!(parse_argument(&"11".repeat(32), &primitive("account_id")).is_ok()); + assert!(parse_argument(&"01000000".repeat(8), &primitive("program_id")).is_ok()); + assert!(parse_argument(&"22".repeat(32), &primitive("nullifier_public_key")).is_ok()); + assert!(parse_argument(&"33".repeat(1184), &primitive("viewing_public_key")).is_ok()); + + assert!(parse_argument("maybe", &primitive("bool")).is_err()); + assert!(parse_argument(" 1", &primitive("u32")).is_err()); + assert!(parse_argument("0x10", &primitive("u32")).is_err()); + } + + #[test] + fn accepts_cli_boolean_aliases() { + for (raw, expected) in [("1", true), ("yes", true), ("0", false), ("no", false)] { + assert!(matches!( + parse_argument(raw, &primitive("bool")), + Ok(ArgumentValue::Bool(value)) if value == expected + )); + } + } + + #[test] + fn accepts_cli_program_id_aliases() { + let bytes = [0x11; 32]; + let expected = parse_program_id(&format!("0x{}", "11".repeat(32))).unwrap(); + let inputs = [ + format!("0x{}", "11".repeat(32)), + AccountId::new(bytes).to_string(), + ]; + + for input in inputs { + assert!(matches!( + parse_argument(&input, &primitive("program_id")), + Ok(ArgumentValue::ProgramId(value)) if value == expected + )); + } + } + + #[test] + fn accepts_cli_array_vector_and_option_forms() { + let byte_array = array(primitive("u8"), 3); + let parsed = parse_argument("abc", &byte_array).unwrap(); + assert!(matches!( + parsed, + ArgumentValue::Array(values) + if matches!(values.as_slice(), [ + ArgumentValue::U8(b'a'), + ArgumentValue::U8(b'b'), + ArgumentValue::U8(b'c'), + ]) + )); + + let word_array = array(primitive("u32"), 3); + let parsed = parse_argument("1, 2, 3", &word_array).unwrap(); + assert!(matches!( + parsed, + ArgumentValue::Array(values) + if matches!(values.as_slice(), [ + ArgumentValue::U32(1), + ArgumentValue::U32(2), + ArgumentValue::U32(3), + ]) + )); + + let byte_vector = vector(primitive("u8")); + let parsed = parse_argument("1, 2, 3", &byte_vector).unwrap(); + assert!(matches!( + parsed, + ArgumentValue::Vec(values) + if matches!(values.as_slice(), [ + ArgumentValue::U8(1), + ArgumentValue::U8(2), + ArgumentValue::U8(3), + ]) + )); + + let word_vector = vector(primitive("u32")); + let parsed = parse_argument("1, 2, 3", &word_vector).unwrap(); + assert!(matches!( + parsed, + ArgumentValue::Vec(values) + if matches!(values.as_slice(), [ + ArgumentValue::U32(1), + ArgumentValue::U32(2), + ArgumentValue::U32(3), + ]) + )); + + let byte_array_vector = vector(array(primitive("u8"), 2)); + let parsed = parse_argument("0102,0304", &byte_array_vector).unwrap(); + let ArgumentValue::Vec(values) = parsed else { + panic!("expected byte-array vector"); + }; + let [ArgumentValue::Array(first), ArgumentValue::Array(second)] = values.as_slice() else { + panic!("expected two byte arrays"); + }; + assert!(matches!( + first.as_slice(), + [ArgumentValue::U8(1), ArgumentValue::U8(2)] + )); + assert!(matches!( + second.as_slice(), + [ArgumentValue::U8(3), ArgumentValue::U8(4)] + )); + + let optional_byte = option(primitive("u8")); + assert!(matches!( + parse_argument("1", &optional_byte), + Ok(ArgumentValue::Option(Some(value))) if matches!(*value, ArgumentValue::U8(1)) + )); + for raw in ["none", ""] { + assert!(matches!( + parse_argument(raw, &optional_byte), + Ok(ArgumentValue::Option(None)) + )); + } + } + + #[test] + fn rejects_partially_invalid_cli_csv() { + for ty in [vector(primitive("u8")), vector(primitive("u32"))] { + assert!(parse_argument("1,invalid,3", &ty).is_err()); + } + } + + #[test] + fn preserves_legacy_csv_error_paths() { + let cases = [ + ("1,invalid,3", vector(primitive("u8"))), + ("1,invalid,3", vector(primitive("u32"))), + ("1,invalid,3", array(primitive("u32"), 3)), + ("0102,invalid", vector(array(primitive("u8"), 2))), + ]; + + for (raw, ty) in cases { + let Err(error) = parse_argument(raw, &ty) else { + panic!("expected invalid legacy CSV: {raw}"); + }; + assert_eq!(error.path, vec![1], "{raw}"); + assert_eq!(error.reason, "invalid legacy CLI value"); + } + } + + #[test] + fn serializes_cli_and_json_container_forms_identically() { + let bytes = vector(primitive("u8")); + let cli_value = parse_argument("1, 2, 3", &bytes).unwrap(); + let json_value = parse_argument("[1, 2, 3]", &bytes).unwrap(); + + assert_eq!( + serialize_instruction(0, &[&cli_value]).unwrap(), + serialize_instruction(0, &[&json_value]).unwrap(), + ); + } + + #[test] + fn serializes_cli_parser_forms_identically() { + fn assert_matches_cli(raw: &str, ty: IdlType) { + let cli_value = parse_value(raw, &ty).unwrap(); + let cli_words = crate::serialize::serialize_to_risc0(0, &[(&ty, &cli_value)]).unwrap(); + let resolver_value = parse_argument(raw, &ty).unwrap(); + assert_eq!( + serialize_instruction(0, &[&resolver_value]).unwrap(), + cli_words, + "{raw}" + ); + } + + assert_matches_cli("yes", primitive("bool")); + assert_matches_cli("abc", array(primitive("u8"), 3)); + assert_matches_cli("1, 2, 3", array(primitive("u32"), 3)); + assert_matches_cli("1, 2, 3", vector(primitive("u8"))); + assert_matches_cli("1, 2, 3", vector(primitive("u32"))); + assert_matches_cli("", vector(primitive("u32"))); + assert_matches_cli("0102,0304", vector(array(primitive("u8"), 2))); + assert_matches_cli("1", option(primitive("u8"))); + assert_matches_cli("1,2,3,4,5,6,7,8", primitive("program_id")); + } + + #[test] + fn serializes_id_like_primitives_with_their_upstream_shapes() { + #[derive(Debug, Deserialize, PartialEq)] + enum TestInstruction { + Execute { + account_id: AccountId, + program_id: ProgramId, + nullifier_public_key: NullifierPublicKey, + viewing_public_key: ViewingPublicKey, + signed: i128, + }, + } + + let account_id = parse_argument(&"11".repeat(32), &primitive("account_id")).unwrap(); + let program_id = parse_argument("1,2,3,4,5,6,7,8", &primitive("program_id")).unwrap(); + let nullifier_public_key = + parse_argument(&"22".repeat(32), &primitive("nullifier_public_key")).unwrap(); + let expected_vpk = ViewingPublicKey::from_seed(&[0x33; 32], &[0x44; 32]); + let viewing_public_key = parse_argument( + &crate::hex::hex_encode(expected_vpk.to_bytes()), + &primitive("viewing_public_key"), + ) + .unwrap(); + let signed = parse_argument( + "-170141183460469231731687303715884105728", + &primitive("i128"), + ) + .unwrap(); + let words = serialize_instruction( + 0, + &[ + &account_id, + &program_id, + &nullifier_public_key, + &viewing_public_key, + &signed, + ], + ) + .unwrap(); + let decoded = TestInstruction::deserialize(&mut Deserializer::new(words.as_ref())).unwrap(); + + assert_eq!( + decoded, + TestInstruction::Execute { + account_id: AccountId::new([0x11; 32]), + program_id: [1, 2, 3, 4, 5, 6, 7, 8], + nullifier_public_key: NullifierPublicKey([0x22; 32]), + viewing_public_key: expected_vpk, + signed: i128::MIN, + } + ); + } + + #[test] + fn validates_and_encodes_pda_seed_argument_types() { + let unsigned = primitive("u16"); + let value = parse_argument("513", &unsigned).unwrap(); + let seed = value.seed_bytes(&unsigned).unwrap(); + assert_eq!(&seed[..2], &513_u16.to_le_bytes()); + assert!(seed[2..].iter().all(|byte| *byte == 0)); + + let byte_array = IdlType::Array { + array: (Box::new(primitive("u8")), 3), + }; + let value = parse_argument("[1,2,3]", &byte_array).unwrap(); + assert_eq!(value.seed_bytes(&byte_array).unwrap()[..3], [1, 2, 3]); + + let program = primitive("program_id"); + let value = parse_argument("1,2,3,4,5,6,7,8", &program).unwrap(); + let seed = value.seed_bytes(&program).unwrap(); + assert_eq!(&seed[..4], &1_u32.to_le_bytes()); + assert_eq!(&seed[28..], &8_u32.to_le_bytes()); + + let signed = primitive("i8"); + let value = parse_argument("-1", &signed).unwrap(); + assert!(validate_seed_type(&signed).is_err()); + assert!(value.seed_bytes(&signed).is_err()); + + let long_string = primitive("string"); + let value = parse_argument(&"x".repeat(33), &long_string).unwrap(); + assert!(value.seed_bytes(&long_string).is_err()); + + let nested_option = IdlType::Option { + option: Box::new(IdlType::Option { + option: Box::new(primitive("u8")), + }), + }; + assert!(validate_type(&nested_option).is_err()); + } +} diff --git a/spel-client-gen/README.md b/spel-client-gen/README.md index 10857f08..ce278172 100644 --- a/spel-client-gen/README.md +++ b/spel-client-gen/README.md @@ -156,7 +156,7 @@ cmake -B build && cmake --build build # Run it (set env vars or use the Settings page in the UI) MY_PROGRAM_PROGRAM_ID=<64-char-hex> \ -NSSA_WALLET_HOME_DIR=~/.wallet \ +LEE_WALLET_HOME_DIR=~/.wallet \ NSSA_SEQUENCER_URL=http://127.0.0.1:3040 \ ./build/MyProgramApp ``` @@ -199,7 +199,7 @@ make lgx # build portable LGX archive for Basecamp distribution The backend reads configuration from three sources in priority order: 1. **`QSettings`** (persisted from the Settings page) -2. **Environment variables**: `NSSA_WALLET_HOME_DIR`, `NSSA_SEQUENCER_URL`, `{PROGRAM}_PROGRAM_ID` +2. **Environment variables**: `LEE_WALLET_HOME_DIR`, `NSSA_SEQUENCER_URL`, `{PROGRAM}_PROGRAM_ID` 3. **Compiled-in FFI constant** from `{prog}_program_id()` ### Wallet page diff --git a/spel-ffi-compile-test/Cargo.toml b/spel-ffi-compile-test/Cargo.toml index 2aa034dc..dd96c4c5 100644 --- a/spel-ffi-compile-test/Cargo.toml +++ b/spel-ffi-compile-test/Cargo.toml @@ -8,11 +8,11 @@ publish = false spel-client-gen = { path = "../spel-client-gen" } [dependencies] -nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", package = "lee" } -nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", package = "lee_core" } -common = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0" } -sequencer_service_rpc = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", features = ["client"] } -wallet = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0" } +nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", package = "lee" } +nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", package = "lee_core" } +common = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63" } +sequencer_service_rpc = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", features = ["client"] } +wallet = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63" } serde_json = "1" serde = { version = "1", features = ["derive"] } borsh = "1.5" diff --git a/spel-framework-core/Cargo.toml b/spel-framework-core/Cargo.toml index af0a68f1..1de84e99 100644 --- a/spel-framework-core/Cargo.toml +++ b/spel-framework-core/Cargo.toml @@ -14,7 +14,7 @@ idl-gen = ["dep:syn", "dep:proc-macro2", "dep:toml"] host = ["dep:nssa"] [dependencies] -nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", features = ["host"], package = "lee_core" } +nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", features = ["host"], package = "lee_core" } borsh = { version = "1.0", features = ["derive"] } thiserror = "1.0" serde = { version = "1.0", features = ["derive"] } @@ -22,7 +22,7 @@ serde_json = "1.0" sha2 = "0.10" base58 = { version = "0.2", optional = true } -nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", optional = true, package = "lee" } +nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", optional = true, package = "lee" } syn = { version = "2.0", features = ["full", "extra-traits"], optional = true } proc-macro2 = { version = "1.0", optional = true } diff --git a/spel-framework-core/src/account_types.rs b/spel-framework-core/src/account_types.rs index 728706bb..86d66345 100644 --- a/spel-framework-core/src/account_types.rs +++ b/spel-framework-core/src/account_types.rs @@ -63,6 +63,8 @@ pub(crate) fn syn_type_to_idl_type(ty: &Type) -> IdlType { }, "ProgramId" => IdlType::Primitive("program_id".to_string()), "AccountId" => IdlType::Primitive("account_id".to_string()), + "NullifierPublicKey" => IdlType::Primitive("nullifier_public_key".to_string()), + "ViewingPublicKey" => IdlType::Primitive("viewing_public_key".to_string()), other => IdlType::Defined { defined: other.to_string(), }, diff --git a/spel-framework-core/src/idl.rs b/spel-framework-core/src/idl.rs index dd57c465..5e14113a 100644 --- a/spel-framework-core/src/idl.rs +++ b/spel-framework-core/src/idl.rs @@ -103,8 +103,9 @@ fn is_false(v: &bool) -> bool { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct IdlPda { pub seeds: Vec, - /// If true, this is a private PDA — address includes the caller's NullifierPublicKey. - /// Callers must supply `--npk ` to derive the address. + /// If true, this is a private PDA — address includes the caller's nullifier and + /// viewing public keys. + /// Callers must supply `--npk ` and `--vpk ` to derive the address. #[serde(default, skip_serializing_if = "is_false")] pub private: bool, } diff --git a/spel-framework-core/src/idl_gen.rs b/spel-framework-core/src/idl_gen.rs index eb0592e6..0fa3a3b6 100644 --- a/spel-framework-core/src/idl_gen.rs +++ b/spel-framework-core/src/idl_gen.rs @@ -177,7 +177,7 @@ fn generate_idl_inner( .collect(); Some(IdlPda { seeds, - private: false, + private: acc.constraints.private_pda, }) }; @@ -189,7 +189,11 @@ fn generate_idl_inner( owner: None, pda, rest: acc.is_rest, - visibility: vec![], + visibility: if acc.constraints.private_pda { + vec!["private".to_string()] + } else { + vec![] + }, } }) .collect(); @@ -560,6 +564,7 @@ struct AccountConstraints { init: bool, signer: bool, pda_seeds: Vec, + private_pda: bool, } #[derive(Clone)] @@ -696,6 +701,13 @@ fn parse_account_constraints(attrs: &[Attribute]) -> Result) ────────────────────────────── #[test] diff --git a/spel-framework-core/src/pda.rs b/spel-framework-core/src/pda.rs index 96d66ffc..6415131c 100644 --- a/spel-framework-core/src/pda.rs +++ b/spel-framework-core/src/pda.rs @@ -2,8 +2,9 @@ use base58::FromBase58; use nssa_core::account::AccountId; +use nssa_core::encryption::ViewingPublicKey; use nssa_core::program::{PdaSeed, ProgramId}; -use nssa_core::NullifierPublicKey; +use nssa_core::{Identifier, NullifierPublicKey}; use sha2::{Digest, Sha256}; /// Trait for converting a value into a 32-byte PDA seed. @@ -73,28 +74,18 @@ pub fn seed_from_str(s: &str) -> [u8; 32] { /// /// Panics if `seeds` is empty. pub fn compute_pda(program_id: &ProgramId, seeds: &[&[u8; 32]]) -> AccountId { - assert!(!seeds.is_empty(), "PDA requires at least one seed"); - - let combined = if seeds.len() == 1 { - *seeds[0] - } else { - let mut hasher = Sha256::new(); - for seed in seeds { - hasher.update(seed); - } - hasher.finalize().into() - }; - - let pda_seed = PdaSeed::new(combined); + let pda_seed = PdaSeed::new(combine_pda_seeds(seeds)); AccountId::for_public_pda(program_id, &pda_seed) } /// Derive a **private** PDA `AccountId` from a program ID, one or more 32-byte seeds, -/// and a `NullifierPublicKey`. +/// a `NullifierPublicKey`, and a `ViewingPublicKey`. /// /// The seed combining logic mirrors [`compute_pda`]; the difference is the final -/// derivation calls `AccountId::for_private_pda`, which includes the `npk` in the -/// hash so each controller group gets a unique address for the same seed. +/// derivation calls `AccountId::for_private_pda`, which includes both public keys +/// in the hash so each controller group gets a unique address for the same seed. +/// This compatibility helper uses identifier `0`; use +/// [`compute_private_pda_with_identifier`] for a different identifier. /// /// # Panics /// @@ -103,10 +94,33 @@ pub fn compute_private_pda( program_id: &ProgramId, seeds: &[&[u8; 32]], npk: &NullifierPublicKey, + vpk: &ViewingPublicKey, ) -> AccountId { + compute_private_pda_with_identifier(program_id, seeds, npk, vpk, 0) +} + +/// Derive a **private** PDA `AccountId` with an explicit private-account identifier. +/// +/// This uses the same seed composition as [`compute_pda`] and [`compute_private_pda`]. +/// +/// # Panics +/// +/// Panics if `seeds` is empty. +pub fn compute_private_pda_with_identifier( + program_id: &ProgramId, + seeds: &[&[u8; 32]], + npk: &NullifierPublicKey, + vpk: &ViewingPublicKey, + identifier: Identifier, +) -> AccountId { + let pda_seed = PdaSeed::new(combine_pda_seeds(seeds)); + AccountId::for_private_pda(program_id, &pda_seed, npk, vpk, identifier) +} + +fn combine_pda_seeds(seeds: &[&[u8; 32]]) -> [u8; 32] { assert!(!seeds.is_empty(), "PDA requires at least one seed"); - let combined = if seeds.len() == 1 { + if seeds.len() == 1 { *seeds[0] } else { let mut hasher = Sha256::new(); @@ -114,12 +128,7 @@ pub fn compute_private_pda( hasher.update(seed); } hasher.finalize().into() - }; - - let pda_seed = PdaSeed::new(combined); - // lez-core-v0.2.0 added a u128 `identifier` to private-PDA derivation; default - // to 0 (the common case) until callers need to pass a specific identifier. - AccountId::for_private_pda(program_id, &pda_seed, npk, 0) + } } /// Compute a PDA from a program ID and multiple [`ToSeed`] values. @@ -306,6 +315,26 @@ mod tests { assert_ne!(single, multi); } + #[test] + fn test_compute_private_pda_with_identifier() { + let program_id: ProgramId = [1u32; 8]; + let seed = seed_from_str("private"); + let npk = NullifierPublicKey([2u8; 32]); + let vpk = ViewingPublicKey::from_seed(&[3u8; 32], &[4u8; 32]); + let identifier = 7; + + let actual = + compute_private_pda_with_identifier(&program_id, &[&seed], &npk, &vpk, identifier); + let expected = + AccountId::for_private_pda(&program_id, &PdaSeed::new(seed), &npk, &vpk, identifier); + + assert_eq!(actual, expected); + assert_ne!( + actual, + compute_private_pda(&program_id, &[&seed], &npk, &vpk) + ); + } + #[test] #[should_panic(expected = "at least one seed")] fn test_compute_pda_empty_seeds() { diff --git a/spel-framework-core/src/spel_output.rs b/spel-framework-core/src/spel_output.rs index 2958300a..755fee49 100644 --- a/spel-framework-core/src/spel_output.rs +++ b/spel-framework-core/src/spel_output.rs @@ -5,6 +5,7 @@ //! `(Account, AutoClaim)` pairs into the correct `AccountPostState` values. use nssa_core::account::Account; +use nssa_core::encryption::ViewingPublicKey; use nssa_core::program::{AccountPostState, ChainedCall, Claim, PdaSeed, ValidityWindow}; use nssa_core::NullifierPublicKey; @@ -77,12 +78,15 @@ impl AutoClaim { /// /// Identical to [`pda_from_seeds`] in terms of the emitted claim — the circuit /// reuses `Claim::Pda(seed)` for private PDAs and derives the address via - /// `AccountId::for_private_pda` using the `npk` it receives separately through - /// `PrivacyPreservingCircuitInput.private_account_keys`. + /// `AccountId::for_private_pda` using the `npk` and `vpk` it receives separately. /// - /// The `npk` parameter is accepted for documentation clarity; it is not encoded - /// into the claim itself. - pub fn private_pda_from_seeds(seeds: &[&[u8]], _npk: &NullifierPublicKey) -> Self { + /// The key parameters are accepted for documentation clarity; they are not + /// encoded into the claim itself. + pub fn private_pda_from_seeds( + seeds: &[&[u8]], + _npk: &NullifierPublicKey, + _vpk: &ViewingPublicKey, + ) -> Self { Self::pda_from_seeds(seeds) } } diff --git a/spel-framework-macros/src/lib.rs b/spel-framework-macros/src/lib.rs index 88b4c44b..e0733c70 100644 --- a/spel-framework-macros/src/lib.rs +++ b/spel-framework-macros/src/lib.rs @@ -184,10 +184,12 @@ struct AccountConstraints { owner: Option, signer: bool, pda_seeds: Vec, - /// True when `private_pda` keyword is present — address includes the caller's npk. + /// True when `private_pda` keyword is present — address includes caller keys. private_pda: bool, /// Name of the instruction arg supplying the `NullifierPublicKey` for derivation. npk_arg: Option, + /// Name of the instruction arg supplying the `ViewingPublicKey` for derivation. + vpk_arg: Option, } /// A PDA seed definition from the `#[account(pda = ...)]` attribute. @@ -549,6 +551,23 @@ fn parse_instruction(func: ItemFn) -> syn::Result { } } + for account in &accounts { + validate_private_pda_key_arg( + &func, + &args, + account.constraints.npk_arg.as_deref(), + "npk", + "NullifierPublicKey", + )?; + validate_private_pda_key_arg( + &func, + &args, + account.constraints.vpk_arg.as_deref(), + "vpk", + "ViewingPublicKey", + )?; + } + Ok(InstructionInfo { fn_name, accounts, @@ -558,6 +577,42 @@ fn parse_instruction(func: ItemFn) -> syn::Result { }) } +fn validate_private_pda_key_arg( + func: &ItemFn, + args: &[ArgParam], + arg_name: Option<&str>, + constraint_name: &str, + expected_type: &str, +) -> syn::Result<()> { + let Some(arg_name) = arg_name else { + return Ok(()); + }; + let arg = args + .iter() + .find(|arg| arg.name == arg_name) + .ok_or_else(|| { + syn::Error::new_spanned( + &func.sig, + format!( + "`{constraint_name} = arg(\"{arg_name}\")` references a missing instruction argument" + ), + ) + })?; + let actual_type = match &arg.ty { + Type::Path(path) => path.path.segments.last().map(|segment| &segment.ident), + _ => None, + }; + if actual_type.is_none_or(|actual| actual != expected_type) { + return Err(syn::Error::new_spanned( + &arg.ty, + format!( + "`{constraint_name} = arg(\"{arg_name}\")` requires `{arg_name}: {expected_type}`" + ), + )); + } + Ok(()) +} + fn extract_param_name(pat_type: &PatType) -> syn::Result { match &*pat_type.pat { Pat::Ident(pat_ident) => Ok(pat_ident.ident.clone()), @@ -650,6 +705,23 @@ fn parse_account_constraints(attrs: &[Attribute]) -> syn::Result syn::Result(npk_name).is_err() { @@ -679,10 +757,17 @@ fn parse_account_constraints(attrs: &[Attribute]) -> syn::Result(vpk_name).is_err() { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!("`vpk` arg name `{vpk_name}` is not a valid Rust identifier"), + )); + } + } else if constraints.npk_arg.is_some() || constraints.vpk_arg.is_some() { return Err(syn::Error::new( proc_macro2::Span::call_site(), - "`npk` can only be used together with `private_pda`", + "`npk` and `vpk` can only be used together with `private_pda`", )); } @@ -905,8 +990,25 @@ fn generate_match_arms(mod_name: &Ident, instructions: &[InstructionInfo]) -> Ve }).collect() }; + // Collect vpk arg values for private PDA accounts + let vpk_arg_values: Vec = { + let mut names = Vec::new(); + for acc in &ix.accounts { + if let Some(ref vpk) = acc.constraints.vpk_arg { + if !names.contains(vpk) { + names.push(vpk.clone()); + } + } + } + names.iter().map(|name| { + let arg_ident = format_ident!("{}", name); + quote! { &#arg_ident } + }).collect() + }; + let all_extra_args: Vec = arg_seed_values.iter() .chain(npk_arg_values.iter()) + .chain(vpk_arg_values.iter()) .cloned() .collect(); @@ -1450,6 +1552,24 @@ fn generate_validation(instructions: &[InstructionInfo]) -> Vec { params }; + // Extra parameters for private PDA vpk args: __vpk_arg_: &ViewingPublicKey + let vpk_params: Vec = { + let mut seen: Vec = Vec::new(); + let mut params = Vec::new(); + for acc in &ix.accounts { + if let Some(ref vpk) = acc.constraints.vpk_arg { + if !seen.contains(vpk) { + seen.push(vpk.clone()); + let ident = format_ident!("__vpk_arg_{}", vpk); + params.push( + quote! { #ident: &nssa_core::encryption::ViewingPublicKey }, + ); + } + } + } + params + }; + // Generate PDA checks for accounts with pda_seeds let pda_checks: Vec = ix .accounts @@ -1497,15 +1617,18 @@ fn generate_validation(instructions: &[InstructionInfo]) -> Vec { .collect(); if acc.constraints.private_pda { - // Private PDA: address = for_private_pda(program_id, seed, npk) + // Private PDA: address = for_private_pda(program_id, seed, npk, vpk) let npk_name = acc.constraints.npk_arg.as_deref() .expect("private_pda without npk_arg — should have been caught in parse_account_constraints"); let npk_param = format_ident!("__npk_arg_{}", npk_name); + let vpk_name = acc.constraints.vpk_arg.as_deref() + .expect("private_pda without vpk_arg — should have been caught in parse_account_constraints"); + let vpk_param = format_ident!("__vpk_arg_{}", vpk_name); quote! { { #(#seed_exprs)* let __expected_id = spel_framework::pda::compute_private_pda( - self_program_id, &[#(#seed_refs),*], #npk_param + self_program_id, &[#(#seed_refs),*], #npk_param, #vpk_param ); if accounts[#idx].account_id != __expected_id { return Err(spel_framework::error::SpelError::PdaMismatch { @@ -1540,9 +1663,10 @@ fn generate_validation(instructions: &[InstructionInfo]) -> Vec { return quote! {}; } - // Combine all extra params: arg seeds first, then npk params + // Combine all extra params: arg seeds first, then private-PDA key params let all_validate_params: Vec = arg_seed_params.into_iter() .chain(npk_params) + .chain(vpk_params) .collect(); quote! { @@ -1636,6 +1760,12 @@ fn rust_type_to_idl_type_tokens(ty: &Type) -> proc_macro2::TokenStream { "AccountId" => { quote! { spel_framework::idl::IdlType::Primitive("account_id".to_string()) } }, + "NullifierPublicKey" => { + quote! { spel_framework::idl::IdlType::Primitive("nullifier_public_key".to_string()) } + }, + "ViewingPublicKey" => { + quote! { spel_framework::idl::IdlType::Primitive("viewing_public_key".to_string()) } + }, other => { let name = other.to_string(); quote! { spel_framework::idl::IdlType::Defined { defined: #name.to_string() } } @@ -1690,6 +1820,8 @@ fn rust_type_to_idl_json(ty: &Type) -> String { }, "ProgramId" => "\"program_id\"".to_string(), "AccountId" => "\"account_id\"".to_string(), + "NullifierPublicKey" => "\"nullifier_public_key\"".to_string(), + "ViewingPublicKey" => "\"viewing_public_key\"".to_string(), other => format!("{{\"defined\":\"{other}\"}}"), } }, diff --git a/spel-framework/Cargo.toml b/spel-framework/Cargo.toml index a6806e9e..a294c60b 100644 --- a/spel-framework/Cargo.toml +++ b/spel-framework/Cargo.toml @@ -11,8 +11,8 @@ host = ["dep:nssa", "spel-framework-core/host"] [dependencies] spel-framework-core = { path = "../spel-framework-core" } spel-framework-macros = { path = "../spel-framework-macros" } -nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", features = ["host"], package = "lee_core" } -nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", optional = true, package = "lee" } +nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", features = ["host"], package = "lee_core" } +nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", optional = true, package = "lee" } borsh = { version = "1.0", features = ["derive"] } serde_json = "1" diff --git a/test-modules/test_modules_ffi/Cargo.toml b/test-modules/test_modules_ffi/Cargo.toml index aeddcba1..c8665b70 100644 --- a/test-modules/test_modules_ffi/Cargo.toml +++ b/test-modules/test_modules_ffi/Cargo.toml @@ -7,11 +7,11 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", package = "lee" } -nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", package = "lee_core" } -common = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0" } -sequencer_service_rpc = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", features = ["client"] } -wallet = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0" } +nssa = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", package = "lee" } +nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", package = "lee_core" } +common = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63" } +sequencer_service_rpc = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", features = ["client"] } +wallet = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63" } spel-framework-core = { path = "../../spel-framework-core" } serde_json = "1" serde = { version = "1", features = ["derive"] } diff --git a/tests/e2e/fixture_program/Cargo.lock b/tests/e2e/fixture_program/Cargo.lock index 563c440f..db92bbbb 100644 --- a/tests/e2e/fixture_program/Cargo.lock +++ b/tests/e2e/fixture_program/Cargo.lock @@ -31,9 +31,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "ark-bn254" @@ -77,7 +77,7 @@ checksum = "e7e89fe77d1f0f4fe5b96dfc940923d88d17b6a773808124f21e764dfb063c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -128,7 +128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -141,7 +141,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -224,7 +224,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -246,20 +246,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.7", ] [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base58" @@ -293,9 +293,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "blake2" @@ -323,18 +323,18 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ "hybrid-array", ] [[package]] name = "borsh" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" dependencies = [ "borsh-derive", "bytes", @@ -343,41 +343,50 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" +checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", ] [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -388,24 +397,24 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "bytesize" -version = "2.3.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" +checksum = "3d7c8918969267b2932ffd5655509bbbea0833823058c378876953217f5fc50e" dependencies = [ "serde_core", ] [[package]] name = "cc" -version = "1.2.60" +version = "1.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" dependencies = [ "find-msvc-tools", "shlex", @@ -419,15 +428,15 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", "cipher", @@ -436,9 +445,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "num-traits", @@ -448,12 +457,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ - "block-buffer 0.12.0", - "crypto-common 0.2.1", + "block-buffer 0.12.1", + "crypto-common 0.2.2", "inout", ] @@ -531,9 +540,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -541,11 +550,11 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "hybrid-array", "rand_core 0.10.1", ] @@ -579,7 +588,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -590,14 +599,14 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "der" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" dependencies = [ "const-oid 0.10.2", "zeroize", @@ -609,7 +618,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -642,7 +650,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.119", "unicode-xid", ] @@ -654,7 +662,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "const-oid 0.9.6", - "crypto-common 0.1.6", + "crypto-common 0.1.7", "subtle", ] @@ -664,8 +672,8 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "block-buffer 0.12.0", - "crypto-common 0.2.1", + "block-buffer 0.12.1", + "crypto-common 0.2.2", ] [[package]] @@ -689,14 +697,14 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elf" @@ -718,22 +726,22 @@ checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" [[package]] name = "enum-ordinalize" -version = "4.3.2" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +checksum = "07f808d588c10e464ea6f7d3eaed500049eff30aaac103460f61828c2d65b3eb" dependencies = [ "enum-ordinalize-derive", ] [[package]] name = "enum-ordinalize-derive" -version = "4.3.2" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +checksum = "42e528e2d34ba8a67a1a650b86beae8ef69fc5fdb638016f386b973226590432" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -764,12 +772,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "foreign-types" version = "0.5.0" @@ -788,7 +790,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -797,11 +799,35 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -832,16 +858,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", "rand_core 0.10.1", - "wasip2", - "wasip3", ] [[package]] @@ -857,20 +881,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", - "foldhash", ] [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hex" @@ -886,9 +903,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hybrid-array" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ "ctutils", "typenum", @@ -918,12 +935,6 @@ dependencies = [ "cc", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -954,7 +965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -985,11 +996,12 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ - "once_cell", + "cfg-if", + "futures-util", "wasm-bindgen", ] @@ -1018,7 +1030,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd" dependencies = [ - "crypto-common 0.2.1", + "crypto-common 0.2.2", "rand_core 0.10.1", ] @@ -1031,16 +1043,10 @@ dependencies = [ "spin", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "lee_core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=lez-core-v0.2.0#fb8cbac40e0bda4f152415ff4f181cdc6bca6d4a" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?rev=7a40979c7f5b04a46a8665a3d07cd3a300dcae63#7a40979c7f5b04a46a8665a3d07cd3a300dcae63" dependencies = [ "base58", "borsh", @@ -1056,9 +1062,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.184" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -1068,9 +1074,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "malloc_buf" @@ -1083,9 +1089,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "merlin" @@ -1105,7 +1111,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.1", "block", "core-graphics-types", "foreign-types", @@ -1147,9 +1153,9 @@ checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e" [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -1157,9 +1163,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -1198,7 +1204,7 @@ checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1265,23 +1271,13 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - [[package]] name = "proc-macro-crate" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", + "toml_edit 0.25.13+spec-1.1.0", ] [[package]] @@ -1299,9 +1295,9 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.1", "num-traits", - "rand 0.9.3", + "rand 0.9.5", "rand_chacha 0.9.0", "rand_xorshift", "unarray", @@ -1309,9 +1305,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -1330,9 +1326,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "rand_chacha 0.3.1", "rand_core 0.6.4", @@ -1340,9 +1336,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_core 0.9.5", ] @@ -1411,7 +1407,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1427,7 +1423,7 @@ dependencies = [ "elf", "lazy_static", "postcard", - "rand 0.9.3", + "rand 0.9.5", "risc0-zkp", "risc0-zkvm-platform", "ruint", @@ -1608,14 +1604,14 @@ dependencies = [ [[package]] name = "ruint" -version = "1.17.2" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +checksum = "45caf26f647c19115bf9c453c70ffe4a4a3a6390dceebd942610584f99b8ddce" dependencies = [ "borsh", "proptest", - "rand 0.8.5", - "rand 0.9.3", + "rand 0.8.7", + "rand 0.9.5", "ruint-macro", "serde_core", "valuable", @@ -1639,9 +1635,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "schemars" @@ -1704,14 +1700,14 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -1731,11 +1727,12 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.18.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64", + "bs58", "chrono", "hex", "indexmap 1.9.3", @@ -1750,14 +1747,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.18.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1783,13 +1780,19 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "spel-framework" -version = "0.5.0" +version = "0.6.0" dependencies = [ "borsh", "lee_core", @@ -1800,7 +1803,7 @@ dependencies = [ [[package]] name = "spel-framework-core" -version = "0.5.0" +version = "0.6.0" dependencies = [ "base58", "borsh", @@ -1809,28 +1812,28 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.117", + "syn 2.0.119", "thiserror 1.0.69", "toml", ] [[package]] name = "spel-framework-macros" -version = "0.5.0" +version = "0.6.0" dependencies = [ "proc-macro2", "quote", "serde_json", "sha2", "spel-framework-core", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" [[package]] name = "spki" @@ -1849,7 +1852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1877,9 +1880,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -1912,7 +1915,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1923,17 +1926,16 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "time" -version = "0.3.47" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -1943,20 +1945,35 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", ] +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml" version = "0.8.23" @@ -2003,14 +2020,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.1", + "winnow 1.0.4", ] [[package]] @@ -2019,7 +2036,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.1", + "winnow 1.0.4", ] [[package]] @@ -2048,7 +2065,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2072,9 +2089,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "unarray" @@ -2114,27 +2131,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -2145,9 +2153,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2155,60 +2163,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.14.0", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap 2.14.0", - "semver", -] - [[package]] name = "windows-core" version = "0.62.2" @@ -2230,7 +2204,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2241,7 +2215,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2279,143 +2253,61 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap 2.14.0", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap 2.14.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.14.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/tests/e2e/fixture_program/Cargo.toml b/tests/e2e/fixture_program/Cargo.toml index c05e2db5..ae259962 100644 --- a/tests/e2e/fixture_program/Cargo.toml +++ b/tests/e2e/fixture_program/Cargo.toml @@ -8,6 +8,6 @@ publish = false [dependencies] spel-framework = { path = "../../../spel-framework" } -nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0", features = ["host"], package = "lee_core" } +nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", rev = "7a40979c7f5b04a46a8665a3d07cd3a300dcae63", features = ["host"], package = "lee_core" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/tests/e2e/fixture_program/src/lib.rs b/tests/e2e/fixture_program/src/lib.rs index 8c50aa42..d007b611 100644 --- a/tests/e2e/fixture_program/src/lib.rs +++ b/tests/e2e/fixture_program/src/lib.rs @@ -15,10 +15,8 @@ mod treasury { /// Initialize the treasury state. #[instruction] pub fn initialize( - #[account(init, pda = literal("treasury_state"))] - state: AccountWithMetadata, - #[account(signer)] - authority: AccountWithMetadata, + #[account(init, pda = literal("treasury_state"))] state: AccountWithMetadata, + #[account(signer)] authority: AccountWithMetadata, threshold: u64, ) -> SpelResult { Ok(SpelOutput::execute(vec![state, authority], vec![])) @@ -27,10 +25,8 @@ mod treasury { /// Create a user vault (PDA from arg seed). #[instruction] pub fn create_vault( - #[account(init, pda = arg("owner_key"))] - vault: AccountWithMetadata, - #[account(signer)] - owner: AccountWithMetadata, + #[account(init, pda = arg("owner_key"))] vault: AccountWithMetadata, + #[account(signer)] owner: AccountWithMetadata, owner_key: [u8; 32], ) -> SpelResult { Ok(SpelOutput::execute(vec![vault, owner], vec![])) @@ -39,10 +35,8 @@ mod treasury { /// Create a user config (PDA from literal + arg multi-seed). #[instruction] pub fn create_config( - #[account(init, pda = [literal("config"), arg("user_id")])] - config: AccountWithMetadata, - #[account(signer)] - admin: AccountWithMetadata, + #[account(init, pda = [literal("config"), arg("user_id")])] config: AccountWithMetadata, + #[account(signer)] admin: AccountWithMetadata, user_id: [u8; 32], ) -> SpelResult { Ok(SpelOutput::execute(vec![config, admin], vec![])) @@ -53,8 +47,7 @@ mod treasury { pub fn create_ledger( #[account(init, pda = [literal("ledger"), arg("user_id"), arg("seq")])] ledger: AccountWithMetadata, - #[account(signer)] - authority: AccountWithMetadata, + #[account(signer)] authority: AccountWithMetadata, user_id: u64, seq: u32, ) -> SpelResult { @@ -64,10 +57,8 @@ mod treasury { /// Register a named entity (PDA from arg + arg with String type). #[instruction] pub fn register_entity( - #[account(init, pda = [arg("domain"), arg("name")])] - entity: AccountWithMetadata, - #[account(signer)] - registrar: AccountWithMetadata, + #[account(init, pda = [arg("domain"), arg("name")])] entity: AccountWithMetadata, + #[account(signer)] registrar: AccountWithMetadata, domain: String, name: String, ) -> SpelResult { @@ -77,12 +68,9 @@ mod treasury { /// Transfer funds. #[instruction] pub fn transfer( - #[account(mut)] - from: AccountWithMetadata, - #[account(mut)] - to: AccountWithMetadata, - #[account(signer)] - signer: AccountWithMetadata, + #[account(mut)] from: AccountWithMetadata, + #[account(mut)] to: AccountWithMetadata, + #[account(signer)] signer: AccountWithMetadata, amount: u64, memo: String, ) -> SpelResult { @@ -94,33 +82,36 @@ mod treasury { /// and validation. #[instruction] pub fn create_record( - #[account(init, pda = account("owner"))] - record: AccountWithMetadata, - #[account(signer)] - owner: AccountWithMetadata, + #[account(init, pda = account("owner"))] record: AccountWithMetadata, + #[account(signer)] owner: AccountWithMetadata, ) -> SpelResult { Ok(SpelOutput::execute(vec![record, owner], vec![])) } - /// Initialize a private PDA — address is unique per (seed, npk) pair. + /// Initialize a private PDA — address is unique per (seed, npk, vpk) tuple. #[instruction] pub fn init_private_account( - #[account(init, private_pda, pda = literal("private_vault"), npk = arg("user_npk"))] + #[account( + init, + private_pda, + pda = literal("private_vault"), + npk = arg("user_npk"), + vpk = arg("user_vpk") + )] account: AccountWithMetadata, - #[account(signer)] - authority: AccountWithMetadata, + #[account(signer)] authority: AccountWithMetadata, user_npk: nssa_core::NullifierPublicKey, + user_vpk: nssa_core::encryption::ViewingPublicKey, ) -> SpelResult { + let _ = (&user_npk, &user_vpk); Ok(SpelOutput::execute(vec![account, authority], vec![])) } /// Batch update: one fixed authority + variable-length list of target accounts. #[instruction] pub fn batch_update( - #[account(signer)] - authority: AccountWithMetadata, - #[account(mut)] - targets: Vec, + #[account(signer)] authority: AccountWithMetadata, + #[account(mut)] targets: Vec, value: u64, ) -> SpelResult { let mut accounts = vec![authority]; @@ -133,10 +124,8 @@ mod treasury { #[instruction] pub fn initialize_holding( ctx: ProgramContext, - #[account(owner = self_program_id)] - definition: AccountWithMetadata, - #[account(init, signer)] - holding: AccountWithMetadata, + #[account(owner = self_program_id)] definition: AccountWithMetadata, + #[account(init, signer)] holding: AccountWithMetadata, ) -> SpelResult { // ctx.self_program_id and ctx.caller_program_id are available here let _ = ctx; // suppress unused warning in this example @@ -146,10 +135,7 @@ mod treasury { /// Demonstrate a validity window on the program output. /// The returned SpelOutput restricts the transaction to a specific block range. #[instruction] - pub fn emit_with_window( - #[account(signer)] - authority: AccountWithMetadata, - ) -> SpelResult { + pub fn emit_with_window(#[account(signer)] authority: AccountWithMetadata) -> SpelResult { Ok(SpelOutput::execute(vec![authority], vec![]) .try_with_block_validity_window(100u64..200) .expect("100..200 is a valid range")) @@ -210,7 +196,7 @@ mod tests { assert_eq!(ix.accounts.len(), 3); assert!(ix.accounts[0].writable); // from: mut assert!(ix.accounts[1].writable); // to: mut - assert!(ix.accounts[2].signer); // signer + assert!(ix.accounts[2].signer); // signer assert_eq!(ix.args.len(), 2); assert_eq!(ix.args[0].name, "amount"); assert_eq!(ix.args[1].name, "memo"); @@ -314,15 +300,11 @@ mod tests { let accounts = vec![ make_account_with_id(wrong_id, false), // vault — wrong address - make_account_with_id([2u8; 32], true), // owner — signer + make_account_with_id([2u8; 32], true), // owner — signer ]; - let result = treasury::__validate_create_vault( - &accounts, - &program_id, - &empty_ix_data(), - &owner_key, - ); + let result = + treasury::__validate_create_vault(&accounts, &program_id, &empty_ix_data(), &owner_key); let err = result.expect_err("should reject wrong PDA"); assert!( matches!(err, spel_framework::error::SpelError::PdaMismatch { .. }), @@ -338,15 +320,11 @@ mod tests { let accounts = vec![ make_account_with_id(*correct_id.value(), false), // vault — correct PDA - make_account_with_id([2u8; 32], true), // owner — signer + make_account_with_id([2u8; 32], true), // owner — signer ]; - let result = treasury::__validate_create_vault( - &accounts, - &program_id, - &empty_ix_data(), - &owner_key, - ); + let result = + treasury::__validate_create_vault(&accounts, &program_id, &empty_ix_data(), &owner_key); assert!(result.is_ok(), "correct PDA should pass: {result:?}"); } @@ -358,8 +336,7 @@ mod tests { let user_id = [99u8; 32]; let config_seed = spel_framework::pda::seed_from_str("config"); - let correct_id = - spel_framework::pda::compute_pda(&program_id, &[&config_seed, &user_id]); + let correct_id = spel_framework::pda::compute_pda(&program_id, &[&config_seed, &user_id]); let wrong_id = [0xAAu8; 32]; assert_ne!( nssa_core::account::AccountId::new(wrong_id), @@ -369,15 +346,11 @@ mod tests { let accounts = vec![ make_account_with_id(wrong_id, false), // config — wrong address - make_account_with_id([2u8; 32], true), // admin — signer + make_account_with_id([2u8; 32], true), // admin — signer ]; - let result = treasury::__validate_create_config( - &accounts, - &program_id, - &empty_ix_data(), - &user_id, - ); + let result = + treasury::__validate_create_config(&accounts, &program_id, &empty_ix_data(), &user_id); let err = result.expect_err("should reject wrong PDA"); assert!( matches!(err, spel_framework::error::SpelError::PdaMismatch { .. }), @@ -391,20 +364,15 @@ mod tests { let user_id = [99u8; 32]; let config_seed = spel_framework::pda::seed_from_str("config"); - let correct_id = - spel_framework::pda::compute_pda(&program_id, &[&config_seed, &user_id]); + let correct_id = spel_framework::pda::compute_pda(&program_id, &[&config_seed, &user_id]); let accounts = vec![ make_account_with_id(*correct_id.value(), false), // config — correct PDA - make_account_with_id([2u8; 32], true), // admin — signer + make_account_with_id([2u8; 32], true), // admin — signer ]; - let result = treasury::__validate_create_config( - &accounts, - &program_id, - &empty_ix_data(), - &user_id, - ); + let result = + treasury::__validate_create_config(&accounts, &program_id, &empty_ix_data(), &user_id); assert!(result.is_ok(), "correct PDA should pass: {result:?}"); } @@ -418,15 +386,10 @@ mod tests { let user_id: u64 = 42; let seq: u32 = 7; - let correct_id = spel_framework::pda::compute_pda_multi( - &program_id, - &[&"ledger", &user_id, &seq], - ); + let correct_id = + spel_framework::pda::compute_pda_multi(&program_id, &[&"ledger", &user_id, &seq]); let wrong_id = [0xBBu8; 32]; - assert_ne!( - nssa_core::account::AccountId::new(wrong_id), - correct_id, - ); + assert_ne!(nssa_core::account::AccountId::new(wrong_id), correct_id,); let accounts = vec![ make_account_with_id(wrong_id, false), @@ -455,10 +418,8 @@ mod tests { let user_id: u64 = 42; let seq: u32 = 7; - let correct_id = spel_framework::pda::compute_pda_multi( - &program_id, - &[&"ledger", &user_id, &seq], - ); + let correct_id = + spel_framework::pda::compute_pda_multi(&program_id, &[&"ledger", &user_id, &seq]); let accounts = vec![ make_account_with_id(*correct_id.value(), false), @@ -485,15 +446,9 @@ mod tests { let domain = String::from("gaming"); let name = String::from("player1"); - let correct_id = spel_framework::pda::compute_pda_multi( - &program_id, - &[&domain, &name], - ); + let correct_id = spel_framework::pda::compute_pda_multi(&program_id, &[&domain, &name]); let wrong_id = [0xCCu8; 32]; - assert_ne!( - nssa_core::account::AccountId::new(wrong_id), - correct_id, - ); + assert_ne!(nssa_core::account::AccountId::new(wrong_id), correct_id,); let accounts = vec![ make_account_with_id(wrong_id, false), @@ -522,10 +477,7 @@ mod tests { let domain = String::from("gaming"); let name = String::from("player1"); - let correct_id = spel_framework::pda::compute_pda_multi( - &program_id, - &[&domain, &name], - ); + let correct_id = spel_framework::pda::compute_pda_multi(&program_id, &[&domain, &name]); let accounts = vec![ make_account_with_id(*correct_id.value(), false), @@ -563,7 +515,10 @@ mod tests { // record (index 0): must be a PDA claim — not None, not Authorized assert!( - matches!(&claims[0], spel_framework::spel_output::AutoClaim::Claimed(_)), + matches!( + &claims[0], + spel_framework::spel_output::AutoClaim::Claimed(_) + ), "record claim should be Claimed(Pda(...)), got: {:?}", &claims[0] ); @@ -578,7 +533,7 @@ mod tests { // The encoded seed must be the owner_id bytes, not seed_from_str("owner"). let wrong_seed = spel_framework::pda::seed_from_str("owner"); let wrong_claim = spel_framework::spel_output::AutoClaim::Claimed( - nssa_core::program::Claim::Pda(nssa_core::program::PdaSeed::new(wrong_seed)) + nssa_core::program::Claim::Pda(nssa_core::program::PdaSeed::new(wrong_seed)), ); assert_ne!( claims[0], wrong_claim, @@ -587,7 +542,7 @@ mod tests { // It must match the claim built from the actual owner_id bytes. let correct_claim = spel_framework::spel_output::AutoClaim::Claimed( - nssa_core::program::Claim::Pda(nssa_core::program::PdaSeed::new(owner_id)) + nssa_core::program::Claim::Pda(nssa_core::program::PdaSeed::new(owner_id)), ); assert_eq!(claims[0], correct_claim); } @@ -600,7 +555,7 @@ mod tests { let accounts = vec![ make_account_with_id(*correct_pda.value(), false), // record — correct PDA - make_account_with_id(owner_id, true), // owner — signer + make_account_with_id(owner_id, true), // owner — signer ]; let result = treasury::__validate_create_record(&accounts, &program_id, &empty_ix_data()); @@ -614,7 +569,7 @@ mod tests { let accounts = vec![ make_account_with_id([0xFFu8; 32], false), // record — wrong address - make_account_with_id(owner_id, true), // owner — signer + make_account_with_id(owner_id, true), // owner — signer ]; let result = treasury::__validate_create_record(&accounts, &program_id, &empty_ix_data()); @@ -630,7 +585,11 @@ mod tests { #[test] fn handler_batch_update_callable() { let acc = make_account(true); - let targets = vec![make_account(false), make_account(false), make_account(false)]; + let targets = vec![ + make_account(false), + make_account(false), + make_account(false), + ]; let result = treasury::batch_update(acc, targets, 42); assert!(result.is_ok()); } @@ -646,7 +605,10 @@ mod tests { #[test] fn idl_has_batch_update_instruction() { let idl = __program_idl(); - let ix = idl.instructions.iter().find(|i| i.name == "batch_update") + let ix = idl + .instructions + .iter() + .find(|i| i.name == "batch_update") .expect("batch_update instruction should be in IDL"); assert_eq!(ix.args.len(), 1); assert_eq!(ix.args[0].name, "value"); @@ -658,9 +620,15 @@ mod tests { fn claims_batch_update_rest_count() { let claims = treasury::__claims_batch_update(3); assert_eq!(claims.len(), 4); // 1 fixed (authority) + 3 rest (targets) - assert!(matches!(&claims[0], spel_framework::spel_output::AutoClaim::None)); // authority + assert!(matches!( + &claims[0], + spel_framework::spel_output::AutoClaim::None + )); // authority for claim in &claims[1..] { - assert!(matches!(claim, spel_framework::spel_output::AutoClaim::None)); // targets + assert!(matches!( + claim, + spel_framework::spel_output::AutoClaim::None + )); // targets } } @@ -680,26 +648,36 @@ mod tests { nssa_core::NullifierPublicKey([byte; 32]) } + fn make_vpk(byte: u8) -> nssa_core::encryption::ViewingPublicKey { + nssa_core::encryption::ViewingPublicKey::from_seed(&[byte; 32], &[byte.wrapping_add(1); 32]) + } + #[test] fn validate_init_private_account_accepts_correct_address() { let program_id = test_program_id(); let npk = make_npk(0xAB); + let vpk = make_vpk(0xBC); let correct_id = spel_framework::pda::compute_private_pda( &program_id, &[&spel_framework::pda::seed_from_str("private_vault")], &npk, + &vpk, ); let accounts = vec![ make_account_with_id(*correct_id.value(), false), // account — correct private PDA - make_account_with_id([2u8; 32], true), // authority — signer + make_account_with_id([2u8; 32], true), // authority — signer ]; let result = treasury::__validate_init_private_account( &accounts, &program_id, &empty_ix_data(), &npk, + &vpk, + ); + assert!( + result.is_ok(), + "correct private PDA should pass: {result:?}" ); - assert!(result.is_ok(), "correct private PDA should pass: {result:?}"); } #[test] @@ -707,10 +685,12 @@ mod tests { let program_id = test_program_id(); let correct_npk = make_npk(0xAB); let wrong_npk = make_npk(0xCD); + let vpk = make_vpk(0xBC); let correct_id = spel_framework::pda::compute_private_pda( &program_id, &[&spel_framework::pda::seed_from_str("private_vault")], &correct_npk, + &vpk, ); // Supply the address for correct_npk but validate with wrong_npk let accounts = vec![ @@ -722,6 +702,7 @@ mod tests { &program_id, &empty_ix_data(), &wrong_npk, + &vpk, ); let err = result.expect_err("wrong npk should fail"); assert!( @@ -730,10 +711,41 @@ mod tests { ); } + #[test] + fn validate_init_private_account_rejects_wrong_vpk() { + let program_id = test_program_id(); + let npk = make_npk(0xAB); + let correct_vpk = make_vpk(0xBC); + let wrong_vpk = make_vpk(0xDE); + let correct_id = spel_framework::pda::compute_private_pda( + &program_id, + &[&spel_framework::pda::seed_from_str("private_vault")], + &npk, + &correct_vpk, + ); + let accounts = vec![ + make_account_with_id(*correct_id.value(), false), + make_account_with_id([2u8; 32], true), + ]; + let result = treasury::__validate_init_private_account( + &accounts, + &program_id, + &empty_ix_data(), + &npk, + &wrong_vpk, + ); + let err = result.expect_err("wrong vpk should fail"); + assert!( + matches!(err, spel_framework::error::SpelError::PdaMismatch { .. }), + "expected PdaMismatch, got: {err:?}" + ); + } + #[test] fn validate_init_private_account_rejects_public_pda_address() { let program_id = test_program_id(); let npk = make_npk(0xAB); + let vpk = make_vpk(0xBC); // Supply the PUBLIC PDA address — should be rejected let public_id = spel_framework::pda::compute_pda( &program_id, @@ -748,6 +760,7 @@ mod tests { &program_id, &empty_ix_data(), &npk, + &vpk, ); assert!( result.is_err(), @@ -757,10 +770,10 @@ mod tests { #[test] fn claims_init_private_account_emits_pda_claim() { - use spel_framework::spel_output::AutoClaim; use nssa_core::program::Claim; - // __claims_* takes no npk — Claim::Pda encodes only the seed; the circuit - // handles the (seed, npk) binding for private PDAs independently. + use spel_framework::spel_output::AutoClaim; + // __claims_* takes no keys — Claim::Pda encodes only the seed; the circuit + // handles the (seed, npk, vpk) binding for private PDAs independently. let claims = treasury::__claims_init_private_account(); assert_eq!(claims.len(), 2); assert!( @@ -773,13 +786,34 @@ mod tests { #[test] fn idl_init_private_account_marks_pda_as_private() { let idl = __program_idl(); - let ix = idl.instructions.iter() + let ix = idl + .instructions + .iter() .find(|i| i.name == "init_private_account") .expect("init_private_account must be in IDL"); let acc = &ix.accounts[0]; let pda = acc.pda.as_ref().expect("account must have PDA definition"); assert!(pda.private, "IDL PDA must be marked private"); - assert!(acc.visibility.iter().any(|v| v == "private"), "visibility must include 'private'"); + assert!( + acc.visibility.iter().any(|v| v == "private"), + "visibility must include 'private'" + ); + assert!( + matches!( + &ix.args[0].type_, + spel_framework::idl::IdlType::Primitive(name) + if name == "nullifier_public_key" + ), + "npk must use the nullifier_public_key primitive" + ); + assert!( + matches!( + &ix.args[1].type_, + spel_framework::idl::IdlType::Primitive(name) + if name == "viewing_public_key" + ), + "vpk must use the viewing_public_key primitive" + ); } // ── output filtering ───────────────────────────────────────────────────── @@ -790,7 +824,11 @@ mod tests { // ── ProgramContext + owner constraint tests ────────────────────────────── - fn make_account_with_owner(id: [u8; 32], owner: nssa_core::program::ProgramId, authorized: bool) -> AccountWithMetadata { + fn make_account_with_owner( + id: [u8; 32], + owner: nssa_core::program::ProgramId, + authorized: bool, + ) -> AccountWithMetadata { let mut account = nssa_core::account::Account::default(); account.program_owner = owner; AccountWithMetadata { @@ -803,7 +841,10 @@ mod tests { #[test] fn idl_excludes_context_from_initialize_holding() { let idl = __program_idl(); - let ix = idl.instructions.iter().find(|i| i.name == "initialize_holding") + let ix = idl + .instructions + .iter() + .find(|i| i.name == "initialize_holding") .expect("initialize_holding must be in IDL"); // Context must NOT appear in IDL accounts or args assert!(!ix.accounts.iter().any(|a| a.name == "ctx")); @@ -814,7 +855,10 @@ mod tests { #[test] fn idl_owner_constraint_reflected() { let idl = __program_idl(); - let ix = idl.instructions.iter().find(|i| i.name == "initialize_holding") + let ix = idl + .instructions + .iter() + .find(|i| i.name == "initialize_holding") .expect("initialize_holding must be in IDL"); // definition account has #[account(owner = self_program_id)] assert_eq!(ix.accounts[0].owner, Some("self_program_id".to_string())); @@ -837,7 +881,10 @@ mod tests { let definition = make_account_with_owner([3u8; 32], program_id, false); let holding = make_account(true); // init + signer let result = treasury::initialize_holding(ctx, definition, holding); - assert!(result.is_ok(), "handler should succeed with valid context and owner"); + assert!( + result.is_ok(), + "handler should succeed with valid context and owner" + ); } #[test] @@ -846,13 +893,18 @@ mod tests { let other_program: nssa_core::program::ProgramId = [99u32; 8]; let accounts = vec![ make_account_with_owner([3u8; 32], other_program, false), // definition — wrong owner - make_account_with_id([4u8; 32], true), // holding: init + signer (empty) + make_account_with_id([4u8; 32], true), // holding: init + signer (empty) ]; - let result = treasury::__validate_initialize_holding(&accounts, &program_id, &empty_ix_data()); + let result = + treasury::__validate_initialize_holding(&accounts, &program_id, &empty_ix_data()); let err = result.expect_err("should reject account with wrong owner"); assert!( - matches!(err, spel_framework::error::SpelError::AccountOwnerMismatch { .. }), - "expected AccountOwnerMismatch, got: {:?}", err + matches!( + err, + spel_framework::error::SpelError::AccountOwnerMismatch { .. } + ), + "expected AccountOwnerMismatch, got: {:?}", + err ); } @@ -872,12 +924,16 @@ mod tests { is_authorized: false, // not authorized → signer violation }, ]; - let result = treasury::__validate_initialize_holding(&accounts, &program_id, &empty_ix_data()); + let result = + treasury::__validate_initialize_holding(&accounts, &program_id, &empty_ix_data()); match result.unwrap_err() { spel_framework::error::SpelError::AccountOwnerMismatch { account_name } => { assert_eq!(account_name, "definition"); - } - other => panic!("expected AccountOwnerMismatch (owner check runs first), got: {:?}", other), + }, + other => panic!( + "expected AccountOwnerMismatch (owner check runs first), got: {:?}", + other + ), } } @@ -888,7 +944,8 @@ mod tests { make_account_with_owner([3u8; 32], program_id, false), // definition — correct owner make_account_with_id([4u8; 32], true), // holding: init + signer (empty) ]; - let result = treasury::__validate_initialize_holding(&accounts, &program_id, &empty_ix_data()); + let result = + treasury::__validate_initialize_holding(&accounts, &program_id, &empty_ix_data()); assert!(result.is_ok(), "correct owner should pass: {:?}", result); } }