diff --git a/.deny.toml b/.deny.toml index fb1ce3cf..59df488b 100644 --- a/.deny.toml +++ b/.deny.toml @@ -13,9 +13,9 @@ ignore = [ { id = "RUSTSEC-2025-0055", reason = "`tracing-subscriber` v0.2.25 pulled in by ark-relations v0.4.0 - will be addressed before mainnet" }, { id = "RUSTSEC-2025-0141", reason = "`bincode` is unmaintained but continuing to use it." }, { id = "RUSTSEC-2023-0089", reason = "atomic-polyfill is pulled transitively via risc0-zkvm; waiting on upstream fix (see https://github.com/risc0/risc0/issues/3453)" }, - { id = "RUSTSEC-2026-0097", reason = "`rand` v0.8.5 is present transitively from logos crates, modification may break integration" }, { id = "RUSTSEC-2026-0118", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" }, { id = "RUSTSEC-2026-0119", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" }, + { id = "RUSTSEC-2025-0137", reason = "newest `rint` depends on rustc v1.90.0 and we build artifacts with v1.88.8" }, ] yanked = "deny" unused-ignored-advisory = "deny" diff --git a/.gitignore b/.gitignore index b265e9aa..4605856c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ result wallet-ffi/wallet_ffi.h bedrock_signing_key integration_tests/configs/debug/ +venv/ +keycard_wallet/python/__pycache__/ +keycard_wallet/python/keycard-py/ diff --git a/Cargo.lock b/Cargo.lock index 27c1ae8e..5e02cf77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,9 +143,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -158,15 +158,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -220,9 +220,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.8.2" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" dependencies = [ "rustversion", ] @@ -611,7 +611,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -621,7 +621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -644,9 +644,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "asn1-rs" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -733,7 +733,7 @@ checksum = "86887daca11d02e0b04f37a9cb81888aae881397fb48ff66494e356aea97554a" dependencies = [ "itertools 0.10.5", "lazy_static", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -891,7 +891,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" dependencies = [ - "base64 0.22.1", + "base64", "http 1.4.0", "log", "url", @@ -936,9 +936,9 @@ dependencies = [ [[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 = "axum" @@ -975,12 +975,12 @@ dependencies = [ [[package]] name = "axum" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core 0.5.6", - "base64 0.22.1", + "base64", "bytes", "form_urlencoded", "futures-util", @@ -1094,12 +1094,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -1127,7 +1121,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -1173,9 +1167,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "bitvec" @@ -1224,13 +1218,13 @@ dependencies = [ [[package]] name = "bollard" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227aa051deec8d16bd9c34605e7aaf153f240e35483dd42f6f78903847934738" +checksum = "ee04c4c84f1f811b017f2fbb7dd8815c976e7ca98593de9c1e2afad0f636bff4" dependencies = [ "async-stream", - "base64 0.22.1", - "bitflags 2.11.0", + "base64", + "bitflags 2.11.1", "bollard-buildkit-proto", "bollard-stubs", "bytes", @@ -1248,7 +1242,7 @@ dependencies = [ "log", "num", "pin-project-lite", - "rand 0.9.3", + "rand 0.9.4", "rustls", "rustls-native-certs", "rustls-pki-types", @@ -1286,7 +1280,7 @@ version = "1.52.1-rc.29.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0a8ca8799131c1837d1282c3f81f31e76ceb0ce426e04a7fe1ccee3287c066" dependencies = [ - "base64 0.22.1", + "base64", "bollard-buildkit-proto", "bytes", "prost 0.14.3", @@ -1298,19 +1292,20 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" dependencies = [ "borsh-derive", + "bytes", "cfg_aliases", ] [[package]] name = "borsh-derive" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" dependencies = [ "once_cell", "proc-macro-crate", @@ -1434,7 +1429,7 @@ checksum = "befbfd072a8e81c02f8c507aefce431fe5e7d051f83d48a23ffc9b9fe5a11799" dependencies = [ "clap", "heck", - "indexmap 2.13.0", + "indexmap 2.14.0", "log", "proc-macro2", "quote", @@ -1447,9 +1442,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.56" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "jobserver", @@ -1491,7 +1486,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", - "cipher 0.5.1", + "cipher 0.5.2", "cpufeatures 0.3.0", "rand_core 0.10.1", ] @@ -1549,12 +1544,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", + "crypto-common 0.2.2", "inout 0.2.2", ] @@ -1571,9 +1566,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.60" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -1581,9 +1576,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -1593,9 +1588,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", @@ -1605,9 +1600,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "clock_core" @@ -1645,9 +1640,9 @@ checksum = "2550f75b8cfac212855f6b1885455df8eaee8fe8e246b647d69146142e016084" [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" @@ -1665,7 +1660,7 @@ version = "0.1.0" dependencies = [ "anyhow", "authenticated_transfer_core", - "base64 0.22.1", + "base64", "borsh", "clock_core", "hex", @@ -1690,26 +1685,25 @@ dependencies = [ [[package]] name = "config" -version = "0.15.19" +version = "0.15.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30fa8254caad766fc03cb0ccae691e14bf3bd72bfff27f72802ce729551b3d6" +checksum = "f316c6237b2d38be61949ecd15268a4c6ca32570079394a2444d9ce2c72a72d8" dependencies = [ "convert_case 0.6.0", "pathdiff", "serde_core", - "toml 0.9.12+spec-1.1.0", - "winnow", + "toml 1.1.2+spec-1.1.0", + "winnow 1.0.3", ] [[package]] name = "console" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "once_cell", "unicode-width", "windows-sys 0.61.2", ] @@ -1736,9 +1730,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" +checksum = "20d9a563d167a9cce0f94153382b33cb6eded6dfabff03c69ad65a28ea1514e0" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -1766,11 +1760,12 @@ checksum = "18f12cc9948ed9604230cdddc7c86e270f9401ccbe3c2e98a4378c5e7632212f" [[package]] name = "const_format" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" dependencies = [ "const_format_proc_macros", + "konst", ] [[package]] @@ -2014,9 +2009,9 @@ 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 = [ "hybrid-array", ] @@ -2028,7 +2023,7 @@ dependencies = [ "criterion", "key_protocol", "nssa_core", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -2100,12 +2095,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -2124,11 +2119,10 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", @@ -2149,26 +2143,26 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core 0.21.3", + "darling_core 0.23.0", "quote", "syn 2.0.117", ] [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "data-encoding-macro" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" +checksum = "3259c913752a86488b501ed8680446a5ed2d5aeac6e596cb23ba3800768ea32c" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -2176,12 +2170,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" +checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 2.0.117", + "syn 1.0.109", ] [[package]] @@ -2232,9 +2226,9 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", @@ -2377,7 +2371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea51e75cfa9371c4d760270c3da13516d7206121d668c1fbdd6fd83d1782b0f" dependencies = [ "derive_builder", - "indexmap 2.13.0", + "indexmap 2.14.0", "serde", "serde_yaml", ] @@ -2390,11 +2384,11 @@ checksum = "ccf673e0848ef09fa4aeeba78e681cf651c0c7d35f76ee38cec8e55bc32fa111" [[package]] name = "docker_credential" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d89dfcba45b4afad7450a99b39e751590463e45c04728cf555d36bb66940de8" +checksum = "29547a1dc60885a552306986316bc9701ba120c1a8db6769fa68691529ad373d" dependencies = [ - "base64 0.21.7", + "base64", "serde", "serde_json", ] @@ -2413,7 +2407,7 @@ checksum = "9ac1e888d6830712d565b2f3a974be3200be9296bc1b03db8251a4cbf18a4a34" dependencies = [ "digest", "futures", - "rand 0.8.5", + "rand 0.8.6", "reqwest", "thiserror 1.0.69", "tokio", @@ -2492,15 +2486,15 @@ dependencies = [ [[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 = "either_of" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f7f86eef3a7e4b9c2107583dbbbe3d9535c4b800796faf1774b82ba22033da" +checksum = "5060e0a4cbf26a87550792688ade88e6b8aec9208613631a7a363bda7bc2d4cd" dependencies = [ "paste", "pin-project-lite", @@ -2614,9 +2608,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" dependencies = [ "log", "regex", @@ -2624,9 +2618,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.9" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" dependencies = [ "anstream", "anstyle", @@ -2720,7 +2714,7 @@ dependencies = [ name = "explorer_service" version = "0.1.0" dependencies = [ - "axum 0.8.8", + "axum 0.8.9", "chrono", "clap", "console_error_panic_hook", @@ -2751,9 +2745,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "faucet_core" @@ -2810,13 +2804,12 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if", "libc", - "libredox", ] [[package]] @@ -2993,12 +2986,12 @@ checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-timer" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" dependencies = [ - "gloo-timers 0.2.6", - "send_wrapper 0.4.0", + "gloo-timers 0.4.0", + "send_wrapper", ] [[package]] @@ -3020,23 +3013,23 @@ dependencies = [ [[package]] name = "gdbstub" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf845b08f7c2ef3b5ad19f80779d43ae20d278652b91bb80adda65baf2d8ed6" +checksum = "5bafc7e33650ab9f05dcc16325f05d56b8d10393114e31a19a353b86fa60cfe7" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cfg-if", "log", "managed", "num-traits", - "paste", + "pastey", ] [[package]] name = "gdbstub_arch" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22dde0e1b68787036ccedd0b1ff6f953527a0e807e571fbe898975203027278f" +checksum = "6c02bfe7bd65f42bcda751456869dfa1eb2bd1c36e309b9ec27f4888d41cf258" dependencies = [ "gdbstub", "num-traits", @@ -3055,9 +3048,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "1.3.5" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +checksum = "dab9e9188e97a93276e1fe7b56401b851e2b45a46d045ca658100c1303ada649" dependencies = [ "rustversion", "serde_core", @@ -3124,7 +3117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator", - "indexmap 2.13.0", + "indexmap 2.14.0", "stable_deref_trait", ] @@ -3157,9 +3150,9 @@ dependencies = [ [[package]] name = "gloo-timers" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" dependencies = [ "futures-channel", "futures-core", @@ -3169,9 +3162,9 @@ dependencies = [ [[package]] name = "gloo-timers" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +checksum = "482ce8a491a501da4cd806bd190275363d674f2845005c6ddbd5d3e1dd54495d" dependencies = [ "futures-channel", "futures-core", @@ -3211,9 +3204,9 @@ checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f" [[package]] name = "h2" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" dependencies = [ "atomic-waker", "bytes", @@ -3221,7 +3214,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.4.0", - "indexmap 2.13.0", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -3285,9 +3278,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hashlink" @@ -3383,7 +3376,7 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.3", + "rand 0.9.4", "socket2 0.5.10", "thiserror 2.0.18", "tinyvec", @@ -3405,7 +3398,7 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.3", + "rand 0.9.4", "resolv-conf", "smallvec", "thiserror 2.0.18", @@ -3546,9 +3539,9 @@ dependencies = [ [[package]] name = "hybrid-array" -version = "0.4.8" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8655f91cd07f2b9d0c24137bd650fe69617773435ee5ec83022377777ce65ef1" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" dependencies = [ "typenum", ] @@ -3571,9 +3564,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -3586,7 +3579,6 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -3609,16 +3601,15 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http 1.4.0", "hyper", "hyper-util", "log", "rustls", - "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", @@ -3644,7 +3635,7 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "futures-channel", "futures-util", @@ -3702,12 +3693,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -3715,9 +3707,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -3728,9 +3720,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -3742,15 +3734,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -3762,15 +3754,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -3806,9 +3798,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -3862,7 +3854,7 @@ dependencies = [ "hyper", "hyper-util", "log", - "rand 0.8.5", + "rand 0.8.6", "tokio", "url", "xmltree", @@ -3883,7 +3875,7 @@ dependencies = [ "hyper", "hyper-util", "log", - "rand 0.9.3", + "rand 0.9.4", "tokio", "url", "xmltree", @@ -3961,7 +3953,7 @@ version = "0.1.0" dependencies = [ "anyhow", "base58", - "base64 0.22.1", + "base64", "common", "hex", "nssa", @@ -3994,12 +3986,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -4102,9 +4094,9 @@ checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" [[package]] name = "inventory" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ae045c87e7082cb72dab0ccd01ae075dd00141ddc108f43a0ea150a9e7227" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" dependencies = [ "rustversion", ] @@ -4128,16 +4120,6 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" -[[package]] -name = "iri-string" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -4182,9 +4164,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jf-crhf" @@ -4213,9 +4195,9 @@ dependencies = [ [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" dependencies = [ "jiff-static", "log", @@ -4226,9 +4208,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", @@ -4244,7 +4226,7 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.1", "log", "thiserror 1.0.69", "walkdir", @@ -4253,9 +4235,31 @@ dependencies = [ [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] [[package]] name = "jobserver" @@ -4269,10 +4273,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -4301,7 +4307,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf36eb27f8e13fa93dcb50ccb44c417e25b818cfa1a481b5470cd07b19c60b98" dependencies = [ - "base64 0.22.1", + "base64", "futures-channel", "futures-util", "gloo-net", @@ -4336,7 +4342,7 @@ dependencies = [ "jsonrpsee-types", "parking_lot", "pin-project", - "rand 0.9.3", + "rand 0.9.4", "rustc-hash", "serde", "serde_json", @@ -4354,7 +4360,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790bedefcec85321e007ff3af84b4e417540d5c87b3c9779b9e247d1bcc3dab8" dependencies = [ - "base64 0.22.1", + "base64", "http-body", "hyper", "hyper-rustls", @@ -4489,7 +4495,7 @@ dependencies = [ "k256", "nssa", "nssa_core", - "rand 0.8.5", + "rand 0.8.6", "serde", "sha2", "thiserror 2.0.18", @@ -4504,8 +4510,24 @@ dependencies = [ "pyo3", "serde", "serde_json", + "zeroize", ] +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "lazy-regex" version = "3.6.0" @@ -4546,12 +4568,12 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "leptos" -version = "0.8.17" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b540ac2868724738f0f5d00f00ec4640e587223774219c1baddc46bad46fb8e" +checksum = "efa3982e7fe36c1de68f91f3c9083124f389a975523881f3d7e3363362feda41" dependencies = [ "any_spawner", - "base64 0.22.1", + "base64", "cfg-if", "either_of", "futures", @@ -4565,11 +4587,11 @@ dependencies = [ "oco_ref", "or_poisoned", "paste", - "rand 0.9.3", + "rand 0.9.4", "reactive_graph", "rustc-hash", "rustc_version", - "send_wrapper 0.6.0", + "send_wrapper", "serde", "serde_json", "serde_qs", @@ -4588,12 +4610,12 @@ dependencies = [ [[package]] name = "leptos_axum" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196de3f5cde6a4c4cd254bb16dc6abd2efbf46cc3ae1b6c7da0731f77b4bdf61" +checksum = "d2ac7734eed700b0170dffbfc93b03491ed1f306622d79625323a21ed0eedac0" dependencies = [ "any_spawner", - "axum 0.8.8", + "axum 0.8.9", "futures", "hydration_context", "leptos", @@ -4611,9 +4633,9 @@ dependencies = [ [[package]] name = "leptos_config" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a2ac32008dda0d657f2147cc33336f4e743e091597db10f7a99d668e92a46d" +checksum = "0c06f751315bccc0d193fab302ac01d25bcfcd97474d4676440e7e3250dc3fc3" dependencies = [ "config", "regex", @@ -4631,7 +4653,7 @@ dependencies = [ "js-sys", "or_poisoned", "reactive_graph", - "send_wrapper 0.6.0", + "send_wrapper", "tachys", "wasm-bindgen", "web-sys", @@ -4645,7 +4667,7 @@ checksum = "9d2a0f220c8a5ef3c51199dfb9cdd702bc0eb80d52fbe70c7890adfaaae8a4b1" dependencies = [ "anyhow", "camino", - "indexmap 2.13.0", + "indexmap 2.14.0", "or_poisoned", "proc-macro2", "quote", @@ -4672,9 +4694,9 @@ dependencies = [ [[package]] name = "leptos_macro" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712325a77f1d050bf2897061ccaf2b075930aab36954980d658f04452686c474" +checksum = "9360df573fb57582384a8b7640a3de94ce6501d49be3b69f637cf11a42da484b" dependencies = [ "attribute-derive", "cfg-if", @@ -4701,19 +4723,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c3efe657b4c55ed2e078922786ffe20acfb71767c3dd913767b09a35c75c890" dependencies = [ "futures", - "indexmap 2.13.0", + "indexmap 2.14.0", "leptos", "or_poisoned", - "send_wrapper 0.6.0", + "send_wrapper", "wasm-bindgen", "web-sys", ] [[package]] name = "leptos_router" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35058d4096407b8369843b5b5d227588dfd57ecc9e9bda0567523f084dce69e8" +checksum = "c15158449162e099e2273442f7fd9b924f5cefd935d52af5755ec62aa819fa52" dependencies = [ "any_spawner", "either_of", @@ -4726,7 +4748,7 @@ dependencies = [ "percent-encoding", "reactive_graph", "rustc_version", - "send_wrapper 0.6.0", + "send_wrapper", "tachys", "thiserror 2.0.18", "url", @@ -4753,13 +4775,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da974775c5ccbb6bd64be7f53f75e8321542e28f21563a416574dbe4d5447eae" dependencies = [ "any_spawner", - "base64 0.22.1", + "base64", "codee", "futures", "hydration_context", "or_poisoned", "reactive_graph", - "send_wrapper 0.6.0", + "send_wrapper", "serde", "serde_json", "server_fn", @@ -4768,9 +4790,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.183" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" @@ -4793,9 +4815,9 @@ dependencies = [ [[package]] name = "liblzma-sys" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2db66f3268487b5033077f266da6777d057949b8f93c8ad82e441df25e6186" +checksum = "1a60851d15cd8c5346eca4ab8babff585be2ae4bc8097c067291d3ffe2add3b6" dependencies = [ "cc", "libc", @@ -4869,7 +4891,7 @@ dependencies = [ "libp2p-swarm", "quick-protobuf", "quick-protobuf-codec", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "thiserror 2.0.18", "tracing", @@ -4904,7 +4926,7 @@ dependencies = [ "parking_lot", "pin-project", "quick-protobuf", - "rand 0.8.5", + "rand 0.8.6", "rw-stream-sink", "thiserror 2.0.18", "tracing", @@ -4936,7 +4958,7 @@ checksum = "d558548fa3b5a8e9b66392f785921e363c57c05dcadfda4db0d41ae82d313e4a" dependencies = [ "async-channel", "asynchronous-codec", - "base64 0.22.1", + "base64", "byteorder", "bytes", "either", @@ -4952,7 +4974,7 @@ dependencies = [ "prometheus-client", "quick-protobuf", "quick-protobuf-codec", - "rand 0.8.5", + "rand 0.8.6", "regex", "serde", "sha2", @@ -4994,7 +5016,7 @@ dependencies = [ "k256", "multihash", "quick-protobuf", - "rand 0.8.5", + "rand 0.8.6", "serde", "sha2", "thiserror 2.0.18", @@ -5020,7 +5042,7 @@ dependencies = [ "libp2p-swarm", "quick-protobuf", "quick-protobuf-codec", - "rand 0.8.5", + "rand 0.8.6", "serde", "sha2", "smallvec", @@ -5042,7 +5064,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "socket2 0.5.10", "tokio", @@ -5080,7 +5102,7 @@ dependencies = [ "libp2p-identity", "libp2p-tls", "quinn", - "rand 0.8.5", + "rand 0.8.6", "ring", "rustls", "socket2 0.5.10", @@ -5101,7 +5123,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "tracing", ] @@ -5116,7 +5138,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.5", + "rand 0.8.6", "tracing", ] @@ -5136,7 +5158,7 @@ dependencies = [ "lru", "multistream-select", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "tokio", "tracing", @@ -5207,14 +5229,11 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.11.0", "libc", - "plain", - "redox_syscall 0.7.3", ] [[package]] @@ -5232,9 +5251,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.24" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4735e9cbde5aac84a5ce588f6b23a90b9b0b528f6c5a8db8a4aff300463a0839" +checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" dependencies = [ "cc", "pkg-config", @@ -5261,9 +5280,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "lock_api" @@ -5323,7 +5342,7 @@ version = "0.1.2" source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=ee281a447d95a951752461ee0a6e88eb4a0f17cf#ee281a447d95a951752461ee0a6e88eb4a0f17cf" dependencies = [ "ed25519-dalek", - "generic-array 1.3.5", + "generic-array 1.4.1", "hex", "logos-blockchain-blend-crypto", "logos-blockchain-groth16", @@ -5478,7 +5497,7 @@ dependencies = [ "libp2p-stream", "logos-blockchain-core", "logos-blockchain-cryptarchia-engine", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_with", "thiserror 1.0.69", @@ -5496,7 +5515,7 @@ dependencies = [ "ark-ff 0.4.2", "ark-groth16 0.4.0", "ark-serialize 0.4.2", - "generic-array 1.3.5", + "generic-array 1.4.1", "hex", "num-bigint 0.4.6", "serde", @@ -5527,7 +5546,7 @@ dependencies = [ "async-trait", "bytes", "ed25519-dalek", - "generic-array 1.3.5", + "generic-array 1.4.1", "hex", "logos-blockchain-groth16", "logos-blockchain-key-management-system-macros", @@ -5606,7 +5625,7 @@ dependencies = [ "logos-blockchain-utils", "logos-blockchain-utxotree", "num-bigint 0.4.6", - "rand 0.8.5", + "rand 0.8.6", "rpds", "serde", "serde_arrays", @@ -5633,7 +5652,7 @@ dependencies = [ "natpmp", "netdev", "num_enum", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_with", "thiserror 1.0.69", @@ -5667,7 +5686,7 @@ dependencies = [ "logos-blockchain-libp2p", "logos-blockchain-tracing", "overwatch", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "serde", "tokio", @@ -5804,7 +5823,7 @@ dependencies = [ "opentelemetry-otlp", "opentelemetry-semantic-conventions", "opentelemetry_sdk", - "rand 0.8.5", + "rand 0.8.6", "serde", "tokio", "tonic", @@ -5828,7 +5847,7 @@ dependencies = [ "const-hex", "humantime", "overwatch", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_with", "time", @@ -5884,7 +5903,7 @@ dependencies = [ "logos-blockchain-core", "logos-blockchain-groth16", "logos-blockchain-key-management-system-service", - "rand 0.8.5", + "rand 0.8.6", "reqwest", "rpds", "serde", @@ -6111,7 +6130,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block", "core-graphics-types", "foreign-types", @@ -6154,9 +6173,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -6230,11 +6249,10 @@ dependencies = [ [[package]] name = "multihash" -version = "0.19.4" +version = "0.19.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ace881e3f514092ce9efbcb8f413d0ad9763860b828981c2de51ddc666936c" +checksum = "577c63b00ad74d57e8c9aa870b5fccebf2fd64a308a5aee9f1bb88e4aea19447" dependencies = [ - "no_std_io2", "serde", "unsigned-varint 0.8.0", ] @@ -6338,7 +6356,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ce3636fa715e988114552619582b530481fd5ef176a1e5c1bf024077c2c9445" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "libc", "log", "netlink-packet-core 0.8.1", @@ -6403,7 +6421,7 @@ dependencies = [ "hex", "keccak", "log", - "rand 0.8.5", + "rand 0.8.6", "zeroize", ] @@ -6413,21 +6431,12 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cfg-if", "cfg_aliases", "libc", ] -[[package]] -name = "no_std_io2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3564ce7035b1e4778d8cb6cacebb5d766b5e8fe5a75b9e441e33fb61a872c6" -dependencies = [ - "memchr", -] - [[package]] name = "no_std_strings" version = "0.1.3" @@ -6468,7 +6477,7 @@ dependencies = [ "k256", "log", "nssa_core", - "rand 0.8.5", + "rand 0.8.6", "risc0-binfmt", "risc0-build", "risc0-zkvm", @@ -6553,7 +6562,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "zeroize", ] @@ -6569,9 +6578,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-derive" @@ -6627,9 +6636,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -6637,9 +6646,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro2", "quote", @@ -6696,9 +6705,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" @@ -6810,7 +6819,7 @@ dependencies = [ "futures-util", "opentelemetry", "percent-encoding", - "rand 0.9.3", + "rand 0.9.4", "thiserror 2.0.18", "tokio", "tokio-stream", @@ -6900,7 +6909,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link", ] @@ -6936,6 +6945,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + [[package]] name = "pathdiff" version = "0.2.3" @@ -6948,7 +6963,7 @@ version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ - "base64 0.22.1", + "base64", "serde_core", ] @@ -6969,18 +6984,18 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", @@ -6993,12 +7008,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkcs1" version = "0.7.5" @@ -7022,15 +7031,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plotters" @@ -7094,9 +7097,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] @@ -7116,9 +7119,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -7154,7 +7157,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.4+spec-1.1.0", + "toml_edit 0.25.11+spec-1.1.0", ] [[package]] @@ -7276,13 +7279,13 @@ dependencies = [ [[package]] name = "proptest" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "num-traits", - "rand 0.9.3", + "rand 0.9.4", "rand_chacha 0.9.0", "rand_xorshift", "unarray", @@ -7481,7 +7484,7 @@ dependencies = [ "bytes", "getrandom 0.3.4", "lru-slab", - "rand 0.9.3", + "rand 0.9.4", "ring", "rustc-hash", "rustls", @@ -7504,7 +7507,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -7558,9 +7561,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -7569,9 +7572,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -7649,9 +7652,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -7682,22 +7685,22 @@ dependencies = [ [[package]] name = "reactive_graph" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35774620b3da884a07341e9e36612e1509b1eb0553ef3bb76f1547dd1b797417" +checksum = "00c5a025366836190c7030e883cc2bcd9e384ff555336e3c7954741ca411b177" dependencies = [ "any_spawner", "async-lock", "futures", "guardian", "hydration_context", - "indexmap 2.13.0", + "indexmap 2.14.0", "or_poisoned", "paste", "pin-project-lite", "rustc-hash", "rustc_version", - "send_wrapper 0.6.0", + "send_wrapper", "serde", "slotmap", "thiserror 2.0.18", @@ -7706,26 +7709,26 @@ dependencies = [ [[package]] name = "reactive_stores" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e114642d342893571ff40b4e1da8ccdea907be44c649041eb7d8413b3fd95e8" +checksum = "c30fd35b7d299c591293bb69fed47a703eb2703b1cff0493e78b16ed007e5382" dependencies = [ "guardian", - "indexmap 2.13.0", + "indexmap 2.14.0", "itertools 0.14.0", "or_poisoned", "paste", "reactive_graph", "reactive_stores_macro", "rustc-hash", - "send_wrapper 0.6.0", + "send_wrapper", ] [[package]] name = "reactive_stores_macro" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b024812c47a6867b6cb32767a46182203f94e59eb88c69b032fd9caffa304ce" +checksum = "e5d8e790a5ae5ddf9b7fa380c728375b06858e0cca7d063a73b3408320c523e1" dependencies = [ "convert_case 0.11.0", "proc-macro-error2", @@ -7740,16 +7743,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "redox_syscall" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" -dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -7818,7 +7812,7 @@ version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "futures-core", "futures-util", @@ -7903,7 +7897,7 @@ dependencies = [ "elf", "lazy_static", "postcard", - "rand 0.9.3", + "rand 0.9.4", "risc0-zkp", "risc0-zkvm-platform", "ruint", @@ -7999,7 +7993,7 @@ dependencies = [ "hex", "lazy-regex", "metal", - "rand 0.9.3", + "rand 0.9.4", "rayon", "risc0-circuit-recursion-sys", "risc0-core", @@ -8043,7 +8037,7 @@ dependencies = [ "num-traits", "paste", "postcard", - "rand 0.9.3", + "rand 0.9.4", "rayon", "ringbuffer", "risc0-binfmt", @@ -8150,7 +8144,7 @@ dependencies = [ "ndarray", "parking_lot", "paste", - "rand 0.9.3", + "rand 0.9.4", "rand_core 0.9.5", "rayon", "risc0-core", @@ -8187,7 +8181,7 @@ dependencies = [ "num-traits", "object", "prost 0.13.5", - "rand 0.9.3", + "rand 0.9.4", "rayon", "risc0-binfmt", "risc0-build", @@ -8256,12 +8250,13 @@ dependencies = [ [[package]] name = "rpds" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e75f485e819d4d3015e6c0d55d02a4fd3db47c1993d9e603e0361fba2bffb34" +checksum = "e025feb26210bc196b908e72deb063b1b4000754304341cbc168a1e72c857ebc" dependencies = [ "archery", "serde", + "smallvec", ] [[package]] @@ -8286,7 +8281,7 @@ dependencies = [ "futures", "light-poseidon", "quote", - "rand 0.9.3", + "rand 0.9.4", "syn 1.0.109", "thiserror 2.0.18", "tiny-keccak", @@ -8358,14 +8353,14 @@ dependencies = [ [[package]] name = "ruint" -version = "1.17.2" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ "borsh", "proptest", - "rand 0.8.5", - "rand 0.9.3", + "rand 0.8.6", + "rand 0.9.4", "ruint-macro", "serde_core", "valuable", @@ -8386,9 +8381,9 @@ checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -8414,7 +8409,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys", @@ -8423,9 +8418,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "log", "once_cell", @@ -8450,9 +8445,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -8567,9 +8562,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -8638,7 +8633,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -8657,20 +8652,14 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", "serde_core", ] -[[package]] -name = "send_wrapper" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" - [[package]] name = "send_wrapper" version = "0.6.0" @@ -8699,7 +8688,7 @@ dependencies = [ "mempool", "nssa", "nssa_core", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", "storage", @@ -8811,9 +8800,9 @@ dependencies = [ [[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", @@ -8866,9 +8855,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -8887,15 +8876,16 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.17.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" +checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" dependencies = [ - "base64 0.22.1", + "base64", + "bs58", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.14.0", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -8906,11 +8896,11 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.17.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" +checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.117", @@ -8918,11 +8908,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.33" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0623d197252096520c6f2a5e1171ee436e5af99a5d7caa2891e55e61950e6d9" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "itoa", "ryu", "serde", @@ -8941,12 +8931,12 @@ dependencies = [ [[package]] name = "server_fn" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c799cec4e8e210dfb2f203aa97f0e82232c619e385ef4d011b17a58d6397c7b" +checksum = "5d60e4c1dfccd91fe0990141f69f1d5cf5679797ad53aa1b45e5bd658eb119f0" dependencies = [ - "axum 0.8.8", - "base64 0.22.1", + "axum 0.8.9", + "base64", "bytes", "const-str 1.1.0", "const_format", @@ -8961,7 +8951,7 @@ dependencies = [ "pin-project-lite", "rustc_version", "rustversion", - "send_wrapper 0.6.0", + "send_wrapper", "serde", "serde_json", "serde_qs", @@ -9063,9 +9053,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "slab" @@ -9130,13 +9120,13 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "futures", "http 1.4.0", "httparse", "log", - "rand 0.8.5", + "rand 0.8.6", "sha1", ] @@ -9249,6 +9239,12 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + [[package]] name = "syn" version = "1.0.109" @@ -9309,7 +9305,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -9320,7 +9316,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -9337,9 +9333,9 @@ dependencies = [ [[package]] name = "tachys" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f768750b0d5514f487772187d4b20c66f56faff4541b1faa5aad4975f5aee085" +checksum = "2989c94c59db8497727875aa561d4d0daa3cc79b5774d5ced48263f7091beff1" dependencies = [ "any_spawner", "async-trait", @@ -9349,7 +9345,7 @@ dependencies = [ "erased", "futures", "html-escape", - "indexmap 2.13.0", + "indexmap 2.14.0", "itertools 0.14.0", "js-sys", "next_tuple", @@ -9360,7 +9356,7 @@ dependencies = [ "reactive_stores", "rustc-hash", "rustc_version", - "send_wrapper 0.6.0", + "send_wrapper", "slotmap", "throw_error", "wasm-bindgen", @@ -9387,9 +9383,9 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "tempfile" -version = "3.26.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom 0.4.2", @@ -9622,9 +9618,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -9642,9 +9638,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -9674,9 +9670,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.50.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -9691,9 +9687,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -9724,9 +9720,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" dependencies = [ "futures-util", "log", @@ -9766,13 +9762,26 @@ version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde_core", - "serde_spanned 1.0.4", + "serde_spanned 1.1.1", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", ] [[package]] @@ -9795,9 +9804,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.0.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -9808,33 +9817,33 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", "toml_write", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml_edit" -version = "0.25.4+spec-1.1.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 1.0.0+spec-1.1.0", + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.3", ] [[package]] name = "toml_parser" -version = "1.0.9+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.3", ] [[package]] @@ -9845,19 +9854,19 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", - "axum 0.8.8", - "base64 0.22.1", + "axum 0.8.9", + "base64", "bytes", "h2", "http 1.4.0", @@ -9880,9 +9889,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" dependencies = [ "bytes", "prost 0.14.3", @@ -9897,7 +9906,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.13.0", + "indexmap 2.14.0", "pin-project-lite", "slab", "sync_wrapper", @@ -9910,11 +9919,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "bytes", "futures-core", "futures-util", @@ -9923,7 +9932,6 @@ dependencies = [ "http-body-util", "http-range-header", "httpdate", - "iri-string", "mime", "mime_guess", "percent-encoding", @@ -9933,7 +9941,7 @@ dependencies = [ "tower", "tower-layer", "tower-service", - "tracing", + "url", ] [[package]] @@ -9962,11 +9970,12 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" dependencies = [ "crossbeam-channel", + "symlink", "thiserror 2.0.18", "time", "tracing-subscriber 0.3.23", @@ -10035,9 +10044,9 @@ dependencies = [ [[package]] name = "tracing-loki" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3beec919fbdf99d719de8eda6adae3281f8a5b71ae40431f44dc7423053d34" +checksum = "a8d1ad78bf74c1790b0825ddc35ad1bb9498736c51c8437796b81cadf4916cd8" dependencies = [ "loki-api", "reqwest", @@ -10045,7 +10054,6 @@ dependencies = [ "serde_json", "snap", "tokio", - "tokio-stream", "tracing", "tracing-core", "tracing-log", @@ -10120,19 +10128,18 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" dependencies = [ "bytes", "data-encoding", "http 1.4.0", "httparse", "log", - "rand 0.9.3", + "rand 0.9.4", "sha1", "thiserror 2.0.18", - "utf-8", ] [[package]] @@ -10179,15 +10186,15 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "typetag" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be2212c8a9b9bcfca32024de14998494cf9a5dfa59ea1b829de98bac374b86bf" +checksum = "c5a897b12c6c1151ad0b138b8db50252dc301f93bc3b027db05eec82aeed298c" dependencies = [ "erased-serde", "inventory", @@ -10198,9 +10205,9 @@ dependencies = [ [[package]] name = "typetag-impl" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a7a9b72ba121f6f1f6c3632b85604cac41aedb5ddc70accbebb6cac83de846" +checksum = "cf808357c6ed7e13ba0f3277ec8d8f21b2d501274895104263985330c726c1c5" dependencies = [ "proc-macro2", "quote", @@ -10248,9 +10255,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" @@ -10312,26 +10319,26 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ - "base64 0.22.1", + "base64", "log", "percent-encoding", "rustls", "rustls-pki-types", "ureq-proto", - "utf-8", + "utf8-zero", ] [[package]] name = "ureq-proto" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ - "base64 0.22.1", + "base64", "http 1.4.0", "httparse", "log", @@ -10356,18 +10363,18 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -10382,9 +10389,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -10457,7 +10464,7 @@ dependencies = [ "nssa_core", "optfield", "pyo3", - "rand 0.8.5", + "rand 0.8.6", "rpassword", "sequencer_service_rpc", "serde", @@ -10504,11 +10511,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -10517,14 +10524,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -10535,23 +10542,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -10559,9 +10562,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -10572,9 +10575,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -10596,7 +10599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.14.0", "wasm-encoder", "wasmparser", ] @@ -10629,9 +10632,9 @@ dependencies = [ [[package]] name = "wasm_split_helpers" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a114b3073258dd5de3d812cdd048cca6842342755e828a14dbf15f843f2d1b84" +checksum = "d0cb6d1008be3c4c5abc31a407bfb8c8449ae14efc8561c1db821f79b9614b0a" dependencies = [ "async-once-cell", "wasm_split_macros", @@ -10639,9 +10642,9 @@ dependencies = [ [[package]] name = "wasm_split_macros" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56481f8ed1a9f9ae97ea7b08a5e2b12e8adf9a7818a6ba952b918e09c7be8bf0" +checksum = "d0a659ffe5c7f4538aa6357c07e3d73221cc61eba03bd9a081e14bc91ed09b8c" dependencies = [ "base16", "quote", @@ -10655,17 +10658,17 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "hashbrown 0.15.5", - "indexmap 2.13.0", + "indexmap 2.14.0", "semver", ] [[package]] name = "web-sys" -version = "0.3.91" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", @@ -10687,23 +10690,23 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.6", + "webpki-root-certs 1.0.7", ] [[package]] name = "webpki-root-certs" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -10894,6 +10897,15 @@ 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" @@ -10927,13 +10939,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "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" @@ -10955,6 +10984,12 @@ 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" @@ -10967,6 +11002,12 @@ 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" @@ -10979,12 +11020,24 @@ 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" @@ -10997,6 +11050,12 @@ 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" @@ -11009,6 +11068,12 @@ 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" @@ -11021,6 +11086,12 @@ 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" @@ -11033,6 +11104,12 @@ 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" @@ -11042,6 +11119,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -11051,6 +11137,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -11070,7 +11162,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.13.0", + "indexmap 2.14.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -11100,8 +11192,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", - "indexmap 2.13.0", + "bitflags 2.11.1", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -11120,7 +11212,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap 2.14.0", "log", "semver", "serde", @@ -11132,9 +11224,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wyz" @@ -11233,9 +11325,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -11244,9 +11336,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -11256,18 +11348,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.42" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.42" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -11276,18 +11368,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -11317,9 +11409,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -11328,9 +11420,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -11339,9 +11431,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", @@ -11359,7 +11451,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.13.0", + "indexmap 2.14.0", "memchr", "thiserror 2.0.18", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index f4a981ad..eda859fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,7 +133,6 @@ url = { version = "2.5.4", features = ["serde"] } tokio-retry = "0.3.0" schemars = "1.2" async-stream = "0.3.6" -criterion = { version = "0.8", features = ["html_reports"] } logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" } logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" } @@ -161,6 +160,8 @@ actix-web = { version = "4.13.0", default-features = false, features = [ clap = { version = "4.5.42", features = ["derive", "env"] } reqwest = { version = "0.12", features = ["json", "rustls-tls", "stream"] } pyo3 = { version = "0.24", features = ["auto-initialize"] } +zeroize = "1" +criterion = { version = "0.8", features = ["html_reports"] } # Profile for leptos WASM release builds [profile.wasm-release] diff --git a/artifacts/test_program_methods/pda_fund_spend_proxy.bin b/artifacts/test_program_methods/pda_fund_spend_proxy.bin index c649bb1f..9a04a29c 100644 Binary files a/artifacts/test_program_methods/pda_fund_spend_proxy.bin and b/artifacts/test_program_methods/pda_fund_spend_proxy.bin differ diff --git a/docs/LEZ testnet v0.1 tutorials/keycard.md b/docs/LEZ testnet v0.1 tutorials/keycard.md index 38feea4f..c01574b4 100644 --- a/docs/LEZ testnet v0.1 tutorials/keycard.md +++ b/docs/LEZ testnet v0.1 tutorials/keycard.md @@ -60,17 +60,41 @@ Unset it when done: unset KEYCARD_PIN ``` +## Pairing password + +The pairing password is used to establish a secure channel between the wallet and the card. It is set permanently on the card during `wallet keycard init` and must match on every subsequent re-pair. + +The default password (`KeycardDefaultPairing`) is [recommended](https://docs.keycard.tech/en/developers/core) for most users. Wallet CLI allows advance users the flexibility to set their own pairing password. + +To use a custom pairing password, set it before `init`: + +```bash +export KEYCARD_PAIRING_PASSWORD=my-custom-password +wallet keycard init +``` + +After a successful initializaation, subsequent commands (`connect`, transfers) use the cached pairing index and key — the pairing password is not needed again until the pairing is cleared. + +**Important:** if you initialized with a custom password, `KEYCARD_PAIRING_PASSWORD` must be set in every session where re-pairing can occur (after `disconnect`, or on a new machine). If the env var is missing then wallet CLI will attempt to use the default password. As a result, pairing will fail. + +Unset the pairing password variable when done: + +```bash +unset KEYCARD_PAIRING_PASSWORD +``` + ## Keycard Commands ### Keycard -| Command | Description | -|-----------------------------|------------------------------------------------------------| -| `wallet keycard available` | Checks whether a Keycard reader and card are accessible | -| `wallet keycard init` | Initializes a blank Keycard with a PIN and a generated PUK | -| `wallet keycard connect` | Establishes and saves a pairing with the Keycard | -| `wallet keycard disconnect` | Unpairs the Keycard and clears the saved pairing | -| `wallet keycard load` | Loads a mnemonic phrase onto the Keycard | +| Command | Description | +|----------------------------------|-----------------------------------------------------------------------| +| `wallet keycard available` | Checks whether a Keycard reader and card are accessible | +| `wallet keycard init` | Initializes a blank Keycard with a PIN and a generated PUK | +| `wallet keycard connect` | Establishes and saves a pairing with the Keycard | +| `wallet keycard disconnect` | Unpairs the Keycard and clears the saved pairing | +| `wallet keycard load` | Loads a mnemonic phrase onto the Keycard | +| `wallet keycard get-private-keys`| Prints NSK and VSK for a BIP-32 path — **debug builds only** (see below) | 1. Check keycard availability ```bash @@ -122,6 +146,31 @@ Keycard PIN: ✅ Keycard unpaired and pairing cleared. ``` +6. Get private keys for a BIP-32 path (**debug builds only**) + +`get-private-keys` exports the raw NSK and VSK for a derivation path. NSK gates nullifier creation and VSK gates note decryption — either key is sufficient to fully compromise that account's privacy. The command is only available in debug builds and requires `--reveal` to confirm intent. + +First install the wallet with the `keycard-debug` feature: +```bash +cargo install --path wallet --force --features keycard-debug +``` + +Then run the command: +```bash +wallet keycard get-private-keys --key-path "m/44'/60'/0'/0/0" --reveal + +# Output: +WARNING: NSK and VSK are being printed to stdout. Any terminal log, scrollback, or screen recording captures these keys. +Keycard PIN: +NSK: 55e505bf925e536c843a12ebc08c41ca5f4761eeeb7fa33725f0b44e6f1ac2e4 +VSK: 30f798893977a7b7263d1f77abf58e11e014428c92030d6a02fe363cceb41ffa +``` + +To restore the standard build without `keycard-debug` afterwards: +```bash +cargo install --path wallet --force +``` + ### Pinata (testnet) | Command | Description | @@ -213,25 +262,270 @@ Keycard PIN: Transaction hash is 7d4c1b8e2f903a56fd19084b3c8b25d07e8f243829bc50addf6e2c78b4b09e45 ``` +### Token program + +`--definition`, `--holder`, `--from`, and `--to` each accept any of: +- A BIP-32 key path — uses Keycard (e.g. `m/44'/60'/0'/0/0`) +- An account ID with privacy prefix (e.g. `Public/9bKm...`) +- An account label (e.g. `my-account`) + +The token program requires both the definition account and the holder/recipient to sign when both are owned. If only one is a Keycard path, only that account signs via the card; the other signs locally or is treated as foreign. + +**Shielded transfers** (public Keycard sender → private recipient) are supported. The Keycard signs the public sender's authorization; the ZK circuit handles the private recipient side. + +| Command | Description | +|--------------------|-------------------------------------------------------| +| `wallet token new` | Creates a new token definition with an initial supply | +| `wallet token send`| Transfers tokens between accounts | +| `wallet token mint`| Mints tokens to a holder account | +| `wallet token burn`| Burns tokens from a holder account | + +1. Create a new token — definition and supply both on Keycard +```bash +wallet token new \ + --definition-account-id "m/44'/60'/0'/0/2" \ + --supply-account-id "m/44'/60'/0'/0/3" \ + --name LEZ \ + --total-supply 100000 + +# Output: +Keycard PIN: +Transaction hash is a3f1c8e2049b7d56fe19084b3c8b25d07e8f243829bc50addf6e2c78b4b09d11 +Transaction data is ... +``` + +2. Transfer tokens between two Keycard accounts (public → public) +```bash +wallet token send \ + --from "m/44'/60'/0'/0/3" \ + --to "m/44'/60'/0'/0/6" \ + --amount 20000 + +# Output: +Keycard PIN: +Transaction hash is b2e4d9f1038c6e45ad28175c4d9c36e18bf9354930cd61beef59f3e89c5a0e22 +Transaction data is ... +``` + +3. Transfer tokens from a Keycard account to a private account (shielded) +```bash +wallet token send \ + --from "m/44'/60'/0'/0/6" \ + --to "Private/CJwKfrb3DFMmFvujQSB5ARcRTAa8EdP6eWm2hmSkF7Rb" \ + --amount 500 + +# Output: +Keycard PIN: +Transaction hash is c5f7e0a2149d8f67be39286d5eaa47f29cg0465041de72cff06a4f9ad6b1f33 +``` + +4. Mint tokens — Keycard definition account mints to a Keycard holder +```bash +wallet token mint \ + --definition "m/44'/60'/0'/0/2" \ + --holder "m/44'/60'/0'/0/6" \ + --amount 2000 + +# Output: +Keycard PIN: +Transaction hash is d6g8f1b3250e9a78cf4a397e6fbb58g3ah1567152ef83dgg17b5g0be7c2g0g44 +Transaction data is ... +``` + +5. Burn tokens — Keycard holder burns from its own account +```bash +wallet token burn \ + --definition "Public/9bKmZ4n7PqVRxEtY3dWsQjA2cHrFT5LpDoGXM8wJuNv6" \ + --holder "m/44'/60'/0'/0/6" \ + --amount 500 + +# Output: +Keycard PIN: +Transaction hash is e7h9g2c4361f0b89dg5b408f7gcc69h4bi2678263fg94ehh28c6h1cf8d3h1h55 +Transaction data is ... +``` + +### AMM program + +AMM operations are **public only** — all holdings involved must be public accounts. Keycard accounts can be used for any or all of the holding accounts. + +`--user-holding-a`, `--user-holding-b`, and `--user-holding-lp` each accept any of: +- A BIP-32 key path — uses Keycard (e.g. `m/44'/60'/0'/0/0`) +- An account ID with privacy prefix (e.g. `Public/9bKm...`) +- An account label (e.g. `my-account`) + +For swaps, only the seller's holding signs — the wallet identifies which holding corresponds to the input token and signs only that account. + +| Command | Description | +|----------------------------|-------------------------------------------------------| +| `wallet amm new` | Creates a new AMM liquidity pool | +| `wallet amm swap-exact-input` | Swaps specifying exact input amount | +| `wallet amm swap-exact-output` | Swaps specifying exact output amount | +| `wallet amm add-liquidity` | Adds liquidity to an existing pool | +| `wallet amm remove-liquidity` | Removes liquidity from a pool | + +1. Create a new AMM pool — all holdings on Keycard +```bash +wallet amm new \ + --user-holding-a "m/44'/60'/0'/0/6" \ + --user-holding-b "m/44'/60'/0'/0/7" \ + --user-holding-lp "m/44'/60'/0'/0/8" \ + --balance-a 10000 \ + --balance-b 10000 + +# Output: +Keycard PIN: +Transaction hash is f8i0h3d5472g1c90eh6c519g8hdd70i5cj3789374gh05fii39d7i2dg9e4i2i66 +Transaction data is ... +``` + +2. Swap exact input — Keycard account sells LEE, receives LEZ +```bash +wallet amm swap-exact-input \ + --user-holding-a "m/44'/60'/0'/0/6" \ + --user-holding-b "m/44'/60'/0'/0/7" \ + --amount-in 500 \ + --min-amount-out 1 \ + --token-definition "9bKmZ4n7PqVRxEtY3dWsQjA2cHrFT5LpDoGXM8wJuNv6" + +# Output: +Keycard PIN: +Transaction hash is g9j1i4e6583h2d01fi7d620h9iee81j6dk4890485hi16gjj40e8j3eh0f5j3j77 +Transaction data is ... +``` + +3. Add liquidity — all three holdings on Keycard +```bash +wallet amm add-liquidity \ + --user-holding-a "m/44'/60'/0'/0/6" \ + --user-holding-b "m/44'/60'/0'/0/7" \ + --user-holding-lp "m/44'/60'/0'/0/8" \ + --max-amount-a 1000 \ + --max-amount-b 1000 \ + --min-amount-lp 1 + +# Output: +Keycard PIN: +Transaction hash is h0k2j5f7694i3e12gj8e731i0jff92k7el5901596ij27hkk51f9k4fi1g6k4k88 +Transaction data is ... +``` + +4. Remove liquidity — LP holding on Keycard +```bash +wallet amm remove-liquidity \ + --user-holding-a "m/44'/60'/0'/0/6" \ + --user-holding-b "m/44'/60'/0'/0/7" \ + --user-holding-lp "m/44'/60'/0'/0/8" \ + --balance-lp 500 \ + --min-amount-a 1 \ + --min-amount-b 1 + +# Output: +Keycard PIN: +Transaction hash is i1l3k6g8705j4f23hk9f842j1kgg03l8fm6012607jk38ill62g0l5gj2h7l5l99 +Transaction data is ... +``` + +### ATA program + +The Associated Token Account program derives a deterministic token holding address from an owner account and a token definition. Keycard accounts can be used as the owner. + +`--owner` and `--from`/`--holder` accept any of: +- A BIP-32 key path — uses Keycard (e.g. `m/44'/60'/0'/0/0`) +- An account ID with privacy prefix (e.g. `Public/9bKm...`) +- An account label (e.g. `my-account`) + +| Command | Description | +|--------------------|------------------------------------------------------------------| +| `wallet ata address` | Derives and prints the ATA address (local only, no network) | +| `wallet ata create` | Creates the ATA on-chain | +| `wallet ata send` | Sends tokens from the owner's ATA to a recipient | +| `wallet ata burn` | Burns tokens from the owner's ATA | +| `wallet ata list` | Lists ATAs for a given owner across token definitions | + +1. Derive an ATA address for a Keycard account +```bash +# First resolve the Keycard account ID +OWNER_ID=$(wallet account id --account-id "m/44'/60'/0'/0/9") +wallet ata address \ + --owner "$OWNER_ID" \ + --token-definition "9bKmZ4n7PqVRxEtY3dWsQjA2cHrFT5LpDoGXM8wJuNv6" + +# Output: +DFMmFvujQSB5ARcRTAa8EdP6eWm2hmSkF7RbCJwKfrb3 +``` + +2. Create an ATA — Keycard account as owner +```bash +wallet ata create \ + --owner "m/44'/60'/0'/0/9" \ + --token-definition "9bKmZ4n7PqVRxEtY3dWsQjA2cHrFT5LpDoGXM8wJuNv6" + +# Output: +Keycard PIN: +Transaction hash is j2m4l7h9816k5g34il0g953k2lhh14m9gn7123718kl49jmm73h1m6hk3i8m6m00 +Transaction data is ... +``` + +3. Send tokens from a Keycard ATA to another account +```bash +wallet ata send \ + --from "m/44'/60'/0'/0/9" \ + --token-definition "9bKmZ4n7PqVRxEtY3dWsQjA2cHrFT5LpDoGXM8wJuNv6" \ + --to "DFMmFvujQSB5ARcRTAa8EdP6eWm2hmSkF7RbCJwKfrb3" \ + --amount 500 + +# Output: +Keycard PIN: +Transaction hash is k3n5m8i0927l6h45jm1h064l3mii25n0ho8234829lm50knn84i2n7il4j9n7n11 +Transaction data is ... +``` + +4. Burn tokens from a Keycard ATA +```bash +wallet ata burn \ + --holder "m/44'/60'/0'/0/9" \ + --token-definition "9bKmZ4n7PqVRxEtY3dWsQjA2cHrFT5LpDoGXM8wJuNv6" \ + --amount 200 + +# Output: +Keycard PIN: +Transaction hash is l4o6n9j1038m7i56kn2i175m4njj36o1ip9345930mn61loo95j3o8jm5k0o8o22 +Transaction data is ... +``` + ## Testing -Tests for Keycard commands are in `keycard_wallet/tests/keycard_tests.sh`. Run from the repo root with a Keycard connected: +Tests for Keycard commands are in `keycard_wallet/tests/`. + +| Test file | Description | +|---|---| +| `keycard_tests.sh` | Core Keycard wallet commands and `auth-transfer` commands | +| `keycard_tests_2.sh` | Tests Keycard wallet commands for `amma`, `token` and `ata` programs | +| `keycard_test_3.sh` | Demonstrates retrieving private account keys from keycard | +| `keycard_power_recovery_tests.sh` | Modified test file of `keycard_tests.sh` to test power recovery paths | + +Run from the repo root with a Keycard connected: ```bash bash keycard_wallet/tests/keycard_tests.sh +bash keycard_wallet/tests/keycard_tests_2.sh +bash keycard_wallet/tests/keycard_test_3.sh +bash keycard_wallet/tests/keycard_power_recovery_tests.sh ``` -## SigningGroups +## SigningGroup -`SigningGroups` (`wallet/src/signing.rs`) partitions a transaction's signers into two buckets — local accounts and Keycard accounts. This ensures that Python GIL is only used at most once per transaction, regardless of how many Keycard accounts are involved. +`SigningGroup` (`wallet/src/signing.rs`) partitions a transaction's signers into two buckets — local accounts and Keycard accounts. This ensures that Python GIL is only used at most once per transaction, regardless of how many Keycard accounts are involved. Local signers are resolved and signed in pure Rust. Keycard signers store only their BIP32 key path; all of them are signed inside a single Python session (`connect` / `close_session`) when `sign_all` is called. The command calls `needs_pin` to decide whether to prompt for a PIN before signing. Foreign recipient accounts — those with no local key and no Keycard path — are silently skipped and require neither a signature nor a nonce. ``` -SigningGroups { +SigningGroup { local: [(AccountId, PrivateKey)], // signed in pure Rust keycard: [(AccountId, BIP32Path)], // signed via a single Python/Keycard session } +``` ``` \ No newline at end of file diff --git a/integration_tests/tests/wallet_ffi.rs b/integration_tests/tests/wallet_ffi.rs index 2677e10e..9be04768 100644 --- a/integration_tests/tests/wallet_ffi.rs +++ b/integration_tests/tests/wallet_ffi.rs @@ -137,6 +137,7 @@ unsafe extern "C" { to_keys: *const FfiPrivateAccountKeys, to_identifier: *const FfiU128, amount: *const [u8; 16], + key_path: *const c_char, out_result: *mut FfiTransferResult, ) -> error::WalletFfiError; @@ -882,6 +883,7 @@ fn test_wallet_ffi_transfer_shielded() -> Result<()> { &raw const to_keys, &raw const to_identifier, &raw const amount, + std::ptr::null(), &raw mut transfer_result, ) .unwrap(); diff --git a/keycard_wallet/Cargo.toml b/keycard_wallet/Cargo.toml index 4f44d4ad..6a9d0668 100644 --- a/keycard_wallet/Cargo.toml +++ b/keycard_wallet/Cargo.toml @@ -13,3 +13,4 @@ pyo3.workspace = true log.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true +zeroize.workspace = true diff --git a/keycard_wallet/python/keycard_wallet.py b/keycard_wallet/python/keycard_wallet.py index 7e18636a..21e966cb 100644 --- a/keycard_wallet/python/keycard_wallet.py +++ b/keycard_wallet/python/keycard_wallet.py @@ -3,15 +3,18 @@ from keycard.exceptions import APDUError, TransportError from ecdsa import VerifyingKey, SECP256k1 from keycard.keycard import KeyCard - +from keycard.commands.export_lee_key import export_lee_key from mnemonic import Mnemonic from keycard import constants -import keycard +import os import secrets DEFAULT_PAIRING_PASSWORD = "KeycardDefaultPairing" +def _pairing_password() -> str: + return os.environ.get("KEYCARD_PAIRING_PASSWORD", DEFAULT_PAIRING_PASSWORD) + class KeycardWallet: def __init__(self): self.card = KeyCard() @@ -37,7 +40,7 @@ class KeycardWallet: return False return True - def initialize(self, pin: str) -> bool: + def initialize(self, pin: str, pairing_password: str | None = None) -> bool: try: self.card.select() @@ -45,14 +48,18 @@ class KeycardWallet: raise RuntimeError("Card is already initialized") puk = ''.join(secrets.choice('0123456789') for _ in range(12)) - self.card.init(pin, puk, DEFAULT_PAIRING_PASSWORD) + self.card.init(pin, puk, pairing_password or _pairing_password()) print(f"Keycard PUK: {puk}") print("Record this PUK and store it somewhere safe. It cannot be recovered.") return True except Exception as e: raise RuntimeError(f"Error initializing keycard: {e}") from e - def setup_communication(self, pin: str, password = DEFAULT_PAIRING_PASSWORD) -> bool: + def _reconnect(self) -> None: + self.card = KeyCard() + self.card.select() + + def _pair(self, pin: str, password: str) -> tuple[int, bytes]: self.card.select() if not self.card.is_initialized: @@ -70,14 +77,28 @@ class KeycardWallet: self.card.unpair(pairing_index) except Exception: pass - raise RuntimeError(f"Error setting up communication: {e}") from e + raise RuntimeError(f"Error opening secure channel after fresh pair: {e}") from e - return True + return pairing_index, pairing_key - def get_pairing_data(self) -> tuple[int, bytes]: - return (self.pairing_index, self.pairing_key) + def pair(self, pin: str, password: str | None = None) -> tuple[int, bytes]: + password = password or _pairing_password() + try: + return self._pair(pin, password) + except TransportError as e: + print(f"Transport error during fresh pair ({e}), attempting card reset and retry...") + try: + self._reconnect() + result = self._pair(pin, password) + print("Retry succeeded after card reset.") + return result + except TransportError as e2: + raise RuntimeError( + "Card lost power and did not recover after reset. " + "Try reseating the card in the reader." + ) from e2 - def setup_communication_with_pairing(self, pin: str, pairing_index: int, pairing_key: bytes) -> bool: + def _setup_communication_with_pairing(self, pin: str, pairing_index: int, pairing_key: bytes) -> bool: self.card.select() if not self.card.is_initialized: @@ -94,6 +115,22 @@ class KeycardWallet: return True + def setup_communication_with_pairing(self, pin: str, pairing_index: int, pairing_key: bytes) -> bool: + try: + return self._setup_communication_with_pairing(pin, pairing_index, pairing_key) + except TransportError as e: + print(f"Transport error during stored pairing ({e}), attempting card reset and retry...") + try: + self._reconnect() + result = self._setup_communication_with_pairing(pin, pairing_index, pairing_key) + print("Retry succeeded after card reset.") + return result + except TransportError as e2: + raise RuntimeError( + "Card lost power and did not recover after reset. " + "Try reseating the card in the reader." + ) from e2 + def close_session(self) -> bool: return True @@ -161,4 +198,24 @@ class KeycardWallet: return signature.signature except Exception as e: - raise RuntimeError(f"Error signing message: {e}") from e \ No newline at end of file + raise RuntimeError(f"Error signing message: {e}") from e + + def get_private_keys_for_path(self, path: str = "m/44'/60'/0'/0/0") -> bytes | None: + try: + if not self.card.is_secure_channel_open or not self.card.is_pin_verified: + return None + + private_keys = export_lee_key( + self.card, + constants.DerivationOption.DERIVE, + path + ) + + nsk = private_keys.lee_nsk + vsk = private_keys.lee_vsk + + return (nsk, vsk) + + except Exception as e: + raise RuntimeError(f"Error getting private keys: {e}") from e + diff --git a/keycard_wallet/src/lib.rs b/keycard_wallet/src/lib.rs index 134b6538..a27d99fd 100644 --- a/keycard_wallet/src/lib.rs +++ b/keycard_wallet/src/lib.rs @@ -3,9 +3,13 @@ use std::path::PathBuf; use nssa::{AccountId, PublicKey, Signature}; use pyo3::{prelude::*, types::PyAny}; use serde::{Deserialize, Serialize}; +use zeroize::Zeroizing; pub mod python_path; +/// NSK and VSK as fixed-length zeroizing byte arrays. +type PrivateKeyPair = (Zeroizing<[u8; 32]>, Zeroizing<[u8; 32]>); + // TODO: encrypt at rest alongside broader wallet storage encryption work. #[derive(Serialize, Deserialize)] pub struct KeycardPairingData { @@ -51,10 +55,10 @@ impl KeycardWallet { .extract() } - pub fn get_pairing_data(&self, py: Python<'_>) -> PyResult<(u8, Vec)> { + pub fn pair(&self, py: Python<'_>, pin: &str) -> PyResult<(u8, Vec)> { self.instance .bind(py) - .call_method0("get_pairing_data")? + .call_method1("pair", (pin,))? .extract() } @@ -91,20 +95,11 @@ impl KeycardWallet { { return Ok(()); } - self.setup_communication(py, pin)?; - if let Ok((index, key)) = self.get_pairing_data(py) { - save_pairing(&KeycardPairingData { index, key }); - } + let (index, key) = self.pair(py, pin)?; + save_pairing(&KeycardPairingData { index, key }); Ok(()) } - pub fn setup_communication(&self, py: Python<'_>, pin: &str) -> PyResult { - self.instance - .bind(py) - .call_method1("setup_communication", (pin,))? - .extract() - } - pub fn disconnect(&self, py: Python) -> PyResult { self.instance.bind(py).call_method0("disconnect")?.extract() } @@ -138,6 +133,10 @@ impl KeycardWallet { }) } + #[expect( + clippy::arithmetic_side_effects, + reason = "64 - s_stripped.len() is safe: s_stripped.len() ≤ 31 because py_signature.len() is in [32, 63]" + )] pub fn sign_message_for_path( &self, py: Python, @@ -150,6 +149,24 @@ impl KeycardWallet { .call_method1("sign_message_for_path", (message, path))? .extract()?; + // The keycard Python library strips leading zeros from S when S < 2^(8k) for some k. + // Left-pad S back to 32 bytes so the full signature is always 64 bytes (R || S). + let py_signature = if py_signature.len() < 64 { + if py_signature.len() < 32 { + return Err(PyErr::new::(format!( + "signature from keycard too short: {} bytes", + py_signature.len() + ))); + } + let s_stripped = &py_signature[32..]; + let mut padded = [0_u8; 64]; + padded[..32].copy_from_slice(&py_signature[..32]); + padded[(64 - s_stripped.len())..].copy_from_slice(s_stripped); + padded.to_vec() + } else { + py_signature + }; + let signature: [u8; 64] = py_signature.try_into().map_err(|vec: Vec| { PyErr::new::(format!( "Invalid signature length: expected 64 bytes, got {} (bytes: {:02x?})", @@ -190,11 +207,65 @@ impl KeycardWallet { Ok(()) } - pub fn get_account_id_for_path_with_connect(pin: &str, key_path: &str) -> PyResult { + pub fn get_public_account_id_for_path_with_connect( + pin: &str, + key_path: &str, + ) -> PyResult { let public_key = Self::get_public_key_for_path_with_connect(pin, key_path)?; Ok(format!("Public/{}", AccountId::from(&public_key))) } + + pub fn get_private_keys_for_path(&self, py: Python, path: &str) -> PyResult { + let (raw_nsk, raw_vsk): (Vec, Vec) = self + .instance + .bind(py) + .call_method1("get_private_keys_for_path", (path,))? + .extract()?; + + let raw_nsk = Zeroizing::new(raw_nsk); + let raw_vsk = Zeroizing::new(raw_vsk); + + let nsk = { + if raw_nsk.len() != 32 { + return Err(PyErr::new::(format!( + "expected 32-byte NSK from keycard, got {} bytes", + raw_nsk.len() + ))); + } + let mut arr = Zeroizing::new([0_u8; 32]); + arr.copy_from_slice(&raw_nsk); + arr + }; + + let vsk = { + if raw_vsk.len() != 32 { + return Err(PyErr::new::(format!( + "expected 32-byte VSK from keycard, got {} bytes", + raw_vsk.len() + ))); + } + let mut arr = Zeroizing::new([0_u8; 32]); + arr.copy_from_slice(&raw_vsk); + arr + }; + + Ok((nsk, vsk)) + } + + pub fn get_private_keys_for_path_with_connect( + pin: &str, + path: &str, + ) -> PyResult { + Python::with_gil(|py| { + python_path::add_python_path(py)?; + let wallet = Self::new(py)?; + wallet.connect(py, pin)?; + let result = wallet.get_private_keys_for_path(py, path); + drop(wallet.disconnect(py)); + result + }) + } } fn pairing_file_path() -> Option { diff --git a/keycard_wallet/tests/force_unpower.py b/keycard_wallet/tests/force_unpower.py new file mode 100755 index 00000000..427d2028 --- /dev/null +++ b/keycard_wallet/tests/force_unpower.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +""" +Forces the card in the first available reader into the unpowered state via +PC/SC SCARD_UNPOWER_CARD. Run immediately before a wallet command to simulate +the power-loss condition reported on some USB reader/driver combinations. + +Either: +- pcscd re-powers the card on the next SCardConnect, so wallet +commands will succeed without triggering the retry path. +- the card stays unpowered, triggering TransportError +and exercising the retry wrapper in pair() / setup_communication_with_pairing(). +""" +import sys +from smartcard.scard import ( + SCardEstablishContext, SCardListReaders, SCardConnect, SCardDisconnect, + SCARD_SCOPE_USER, SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T0, SCARD_PROTOCOL_T1, + SCARD_UNPOWER_CARD, +) + +hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) +hresult, reader_list = SCardListReaders(hcontext, []) + +if not reader_list: + print("force_unpower: no readers found, skipping.") + sys.exit(0) + +hresult, hcard, _ = SCardConnect( + hcontext, + reader_list[0], + SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, +) + +if hresult != 0: + print(f"force_unpower: SCardConnect failed (hresult={hresult:#010x}), skipping.") + sys.exit(0) + +SCardDisconnect(hcard, SCARD_UNPOWER_CARD) +print("force_unpower: card powered down.") diff --git a/keycard_wallet/tests/keycard_power_recovery_tests.sh b/keycard_wallet/tests/keycard_power_recovery_tests.sh new file mode 100755 index 00000000..3d8301f7 --- /dev/null +++ b/keycard_wallet/tests/keycard_power_recovery_tests.sh @@ -0,0 +1,117 @@ +#!/bin/bash +# Power-recovery variant of keycard_tests.sh. +# +# Forces a card power cycle before each keycard-backed wallet command to verify +# commands survive mid-session power loss. + +source venv/bin/activate + +export KEYCARD_PIN=111111 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +unpower() { + python "$SCRIPT_DIR/force_unpower.py" +} + +echo "Test: wallet keycard available" +wallet keycard available + +echo "" +echo "Test: wallet keycard load (after power cycle)" +export KEYCARD_MNEMONIC="fashion degree mountain wool question damp current pond grow dolphin chronic then" +unpower +wallet keycard load +unset KEYCARD_MNEMONIC + +echo "" +echo "Test: wallet auth-transfer init --account-id \"m/44'/60'/0'/0/0\" (after power cycle)" +unpower +wallet auth-transfer init --account-id "m/44'/60'/0'/0/0" + +echo "" +echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/0\" (after power cycle)" +unpower +wallet account get --account-id "m/44'/60'/0'/0/0" + +echo "" +echo "Test: wallet pinata claim --to \"m/44'/60'/0'/0/0\" (after power cycle)" +unpower +wallet pinata claim --to "m/44'/60'/0'/0/0" + +echo "" +echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/0\" (after power cycle)" +unpower +wallet account get --account-id "m/44'/60'/0'/0/0" + +echo "" +echo "Test: wallet auth-transfer init and send between two keycard accounts (after power cycle)" +unpower +wallet auth-transfer init --account-id "m/44'/60'/0'/0/1" +unpower +wallet auth-transfer send --amount 40 --from "m/44'/60'/0'/0/0" --to "m/44'/60'/0'/0/1" + +echo "" +echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/0\" (after power cycle)" +unpower +wallet account get --account-id "m/44'/60'/0'/0/0" + +echo "" +echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/1\" (after power cycle)" +unpower +wallet account get --account-id "m/44'/60'/0'/0/1" + +echo "" +echo "Test: create local wallet account" +LOCAL_ACCOUNT_ID=$(wallet account new public 2>&1 | grep -oP '(?<=Public/)\S+') +echo "Created local account: Public/${LOCAL_ACCOUNT_ID}" + +echo "" +echo "Test: wallet auth-transfer init local account" +wallet auth-transfer init --account-id "Public/${LOCAL_ACCOUNT_ID}" + +echo "" +echo "Test: wallet auth-transfer send from keycard to local account (after power cycle)" +unpower +wallet auth-transfer send --amount 10 --from "m/44'/60'/0'/0/0" --to "Public/${LOCAL_ACCOUNT_ID}" + +echo "" +echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/0\" (after power cycle)" +unpower +wallet account get --account-id "m/44'/60'/0'/0/0" + +echo "" +echo "Test: wallet account get --account-id \"Public/${LOCAL_ACCOUNT_ID}\" (after power cycle)" +unpower +wallet account get --account-id "Public/${LOCAL_ACCOUNT_ID}" + +echo "" +echo "Test: wallet auth-transfer send from local account to keycard account (after power cycle)" +unpower +wallet auth-transfer send --amount 10 --from "Public/${LOCAL_ACCOUNT_ID}" --to "m/44'/60'/0'/0/1" + +echo "" +echo "Test: wallet account get --account-id \"Public/${LOCAL_ACCOUNT_ID}\" (after power cycle)" +unpower +wallet account get --account-id "Public/${LOCAL_ACCOUNT_ID}" + +echo "" +echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/1\" (after power cycle)" +unpower +wallet account get --account-id "m/44'/60'/0'/0/1" + +echo "" +echo "Test: wallet auth-transfer send from keycard to foreign account (after power cycle)" +wallet account get --account-id "Public/7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo" +unpower +wallet auth-transfer send --amount 10 --from "m/44'/60'/0'/0/0" --to "Public/7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo" + +echo "" +echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/0\" (after power cycle)" +unpower +wallet account get --account-id "m/44'/60'/0'/0/0" + +echo "" +echo "Test: wallet account get foreign account (after power cycle)" +unpower +wallet account get --account-id "Public/7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo" diff --git a/keycard_wallet/tests/keycard_test_3.sh b/keycard_wallet/tests/keycard_test_3.sh new file mode 100755 index 00000000..5be1a3dd --- /dev/null +++ b/keycard_wallet/tests/keycard_test_3.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# keycard_test_3.sh — tests for `wallet keycard get-private-keys`. +# +# Prerequisites: +# 1. Run wallet_with_keycard.sh once to install dependencies. +# 2. Keycard reader inserted with card loaded (wallet keycard load has been run). + +source venv/bin/activate + +cargo install --path wallet --force --features keycard-debug + +export KEYCARD_PIN=111111 + +echo "=== Test: wallet keycard get-private-keys path 10 ===" +wallet keycard get-private-keys --key-path "m/44'/60'/0'/0/10" --reveal + +echo "=== Test: wallet keycard get-private-keys path 11 ===" +wallet keycard get-private-keys --key-path "m/44'/60'/0'/0/11" --reveal + +echo "" +echo "=== All get-private-keys tests finished ===" diff --git a/keycard_wallet/tests/keycard_tests.sh b/keycard_wallet/tests/keycard_tests.sh index e5ac2f2c..2344f101 100755 --- a/keycard_wallet/tests/keycard_tests.sh +++ b/keycard_wallet/tests/keycard_tests.sh @@ -28,7 +28,8 @@ wallet pinata claim --to "m/44'/60'/0'/0/0" echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/0\"" wallet account get --account-id "m/44'/60'/0'/0/0" -echo "Test: wallet auth-transfer init and send between two keycard accounts" +echo "" +echo "=== Test: Keycard account to Keycard account ===" wallet auth-transfer init --account-id "m/44'/60'/0'/0/1" wallet auth-transfer send --amount 40 --from "m/44'/60'/0'/0/0" --to "m/44'/60'/0'/0/1" @@ -38,7 +39,8 @@ wallet account get --account-id "m/44'/60'/0'/0/0" echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/1\"" wallet account get --account-id "m/44'/60'/0'/0/1" -# Send from keycard account to a local wallet account +echo "" +echo "=== Test: Keycard account to public local account ===" echo "Test: create local wallet account" LOCAL_ACCOUNT_ID=$(wallet account new public 2>&1 | grep -oP '(?<=Public/)\S+') echo "Created local account: Public/${LOCAL_ACCOUNT_ID}" @@ -56,7 +58,8 @@ wallet account get --account-id "m/44'/60'/0'/0/0" echo "Test: wallet account get --account-id \"Public/${LOCAL_ACCOUNT_ID}\"" wallet account get --account-id "Public/${LOCAL_ACCOUNT_ID}" -# Create a local wallet account, fund it, and send to keycard account (co-signed: local key + keycard) +echo "" +echo "=== Test: public local account to Keycard account ===" echo "Test: wallet auth-transfer send from local account to keycard account" wallet auth-transfer send --amount 10 --from "Public/${LOCAL_ACCOUNT_ID}" --to "m/44'/60'/0'/0/1" @@ -67,7 +70,8 @@ wallet account get --account-id "Public/${LOCAL_ACCOUNT_ID}" echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/1\"" wallet account get --account-id "m/44'/60'/0'/0/1" -# Send from keycard account to a local wallet account (foreign recipient — no signature needed) +echo "" +echo "=== Test: Keycard account to foreign recipient (no signature required) ===" echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/0\"" wallet account get --account-id "Public/7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo" @@ -79,3 +83,44 @@ wallet account get --account-id "m/44'/60'/0'/0/0" echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/0\"" wallet account get --account-id "Public/7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo" + +echo "" +echo "=== Test: Shielded auth-transfer to owned private account ===" + +wallet auth-transfer send --amount 2 \ + --from "m/44'/60'/0'/0/0" \ + --to-npk "55204e2934045b044f06d8222b454d46b54788f33c7dec4f6733d441703bb0e6" \ + --to-vpk "02a8626b0c0ad9383c5678dad48c3969b4174fb377cdb03a6259648032c774cec8" +echo "Shielded auth-transfer sent" + +sleep 15 +wallet account get --account-id "m/44'/60'/0'/0/0" + +echo "" +echo "=== Test: Deshielded auth-transfer: private account → keycard path 1 ===" + +PRIV_SENDER=$(wallet account new private | grep -o 'Private/[^[:space:]]*' | head -1) +echo "Fresh private sender account: $PRIV_SENDER" + +wallet auth-transfer init --account-id "$PRIV_SENDER" + +echo "Test: wallet pinata claim to private sender" +wallet pinata claim --to "$PRIV_SENDER" + +sleep 15 + +echo "priv-sender state after claim:" +wallet account get --account-id "$PRIV_SENDER" + +wallet auth-transfer send \ + --from "$PRIV_SENDER" \ + --to "m/44'/60'/0'/0/1" \ + --amount 5 +echo "Deshielded transfer of 5: $PRIV_SENDER → keycard path 1" + +sleep 15 + +echo "priv-sender state (balance should have decreased by 5):" +wallet account get --account-id "$PRIV_SENDER" +echo "Keycard path 1 state (balance should have increased by 5):" +wallet account get --account-id "m/44'/60'/0'/0/1" diff --git a/keycard_wallet/tests/keycard_tests_2.sh b/keycard_wallet/tests/keycard_tests_2.sh new file mode 100755 index 00000000..9a6450f4 --- /dev/null +++ b/keycard_wallet/tests/keycard_tests_2.sh @@ -0,0 +1,457 @@ +#!/usr/bin/env bash +# keycard_tests_2.sh — comprehensive token + AMM keycard integration tests. +# +# Prerequisites: +# 1. Run wallet_with_keycard.sh once to install dependencies. +# 2. Reset the local chain so all accounts are uninitialized. +# 3. Keycard reader inserted with card loaded. +# +# Keycard path layout: +# path 2 → LEZ token definition (keycard) +# path 3 → LEZ token supply (keycard) +# path 4 → LEE token definition (keycard) +# path 5 → LEE token supply (keycard) +# path 6 → LEZ holding (keycard — transfers, mint, burn, swap, liquidity) +# path 7 → LEE holding (keycard — swap, add/remove liquidity) +# path 8 → LP holding (keycard — add/remove liquidity) +# path 9 → ATA owner (keycard — ATA create, send, burn) +# +# Non-keycard accounts: +# pub-receiver → public account (target for keycard → public token transfer) +# priv-receiver → private account (target for keycard → private token transfer) +# amm-lez-fund → public LEZ holding used to seed the AMM pool +# amm-lee-fund → public LEE holding used to seed the AMM pool +# (LP holding for amm new is created fresh each run — no persistent label) + +source venv/bin/activate +export KEYCARD_PIN=111111 + +# ============================================================================= +# Keycard setup +# ============================================================================= +echo "" +echo "=== Keycard setup ===" +wallet keycard available +export KEYCARD_MNEMONIC="fashion degree mountain wool question damp current pond grow dolphin chronic then" +wallet keycard load +unset KEYCARD_MNEMONIC + +# ============================================================================= +# Create non-keycard wallet accounts +# ============================================================================= +echo "" +echo "=== Create non-keycard accounts ===" +wallet account new public --label pub-receiver 2>/dev/null || true + +wallet account new public --label amm-lez-fund 2>/dev/null || true +wallet account new public --label amm-lee-fund 2>/dev/null || true +wallet account new public --label amm-lp-fund 2>/dev/null || true + +# ============================================================================= +# (1) Create LEZ token — definition AND supply via keycard paths +# ============================================================================= +echo "" +echo "=== (1) Create LEZ token (keycard def=path2, supply=path3) ===" +wallet token new \ + --definition-account-id "m/44'/60'/0'/0/2" \ + --supply-account-id "m/44'/60'/0'/0/3" \ + --name LEZ \ + --total-supply 100000 +echo "LEZ token created" + +# ============================================================================= +# (2) Create LEE token — definition AND supply via keycard paths +# ============================================================================= +echo "" +echo "=== (2) Create LEE token (keycard def=path4, supply=path5) ===" +wallet token new \ + --definition-account-id "m/44'/60'/0'/0/4" \ + --supply-account-id "m/44'/60'/0'/0/5" \ + --name LEE \ + --total-supply 100000 +echo "LEE token created" + +sleep 15 + +LEZ_DEF_ID=$(wallet account id --account-id "m/44'/60'/0'/0/2") +LEE_DEF_ID=$(wallet account id --account-id "m/44'/60'/0'/0/4") +echo "LEZ definition ID: $LEZ_DEF_ID" +echo "LEE definition ID: $LEE_DEF_ID" + +echo "Keycard path 2 (LEZ definition) state:" +wallet account get --account-id "m/44'/60'/0'/0/2" +echo "Keycard path 3 (LEZ supply) state:" +wallet account get --account-id "m/44'/60'/0'/0/3" +echo "Keycard path 4 (LEE definition) state:" +wallet account get --account-id "m/44'/60'/0'/0/4" +echo "Keycard path 5 (LEE supply) state:" +wallet account get --account-id "m/44'/60'/0'/0/5" + +# ============================================================================= +# Initialize token holding accounts +# ============================================================================= +echo "" +echo "=== Initialize token holding accounts ===" + +# Keycard path 6: LEZ holding (mint 0 to initialize) +wallet token mint \ + --definition "m/44'/60'/0'/0/2" \ + --holder "m/44'/60'/0'/0/6" \ + --amount 0 +echo "LEZ holding initialized for keycard path 6" + +# Keycard path 7: LEE holding (different definition — safe to submit immediately) +wallet token mint \ + --definition "m/44'/60'/0'/0/4" \ + --holder "m/44'/60'/0'/0/7" \ + --amount 0 +echo "LEE holding initialized for keycard path 7" + +# Wait for path2 (LEZ def) and path4 (LEE def) nonces to be confirmed before reusing them +sleep 15 + +# pub-receiver: public LEZ holding +wallet token mint \ + --definition "m/44'/60'/0'/0/2" \ + --holder pub-receiver \ + --amount 0 +echo "LEZ holding initialized for pub-receiver" + +# amm-lee-fund: LEE holding (different definition — safe to submit with pub-receiver) +wallet token mint \ + --definition "m/44'/60'/0'/0/4" \ + --holder amm-lee-fund \ + --amount 0 +echo "LEE holding initialized for amm-lee-fund" + +# Wait for path2 nonce to be confirmed before the third LEZ mint +sleep 15 + +# amm-lez-fund: LEZ holding +wallet token mint \ + --definition "m/44'/60'/0'/0/2" \ + --holder amm-lez-fund \ + --amount 0 +echo "AMM seed holdings initialized" + +# ============================================================================= +# Fund keycard holdings and AMM seed accounts from supply +# ============================================================================= +echo "" +echo "=== Fund keycard holdings and AMM seed accounts ===" + +wallet token send \ + --from "m/44'/60'/0'/0/3" \ + --to "m/44'/60'/0'/0/6" \ + --amount 20000 +echo "Transferred 20000 LEZ → keycard path 6" + +wallet token send \ + --from "m/44'/60'/0'/0/5" \ + --to "m/44'/60'/0'/0/7" \ + --amount 20000 +echo "Transferred 20000 LEE → keycard path 7" + +# Wait for path3 and path5 nonces to be confirmed before reusing them +sleep 15 + +wallet token send \ + --from "m/44'/60'/0'/0/3" \ + --to amm-lez-fund \ + --amount 10000 +echo "Transferred 10000 LEZ → amm-lez-fund" + +wallet token send \ + --from "m/44'/60'/0'/0/5" \ + --to amm-lee-fund \ + --amount 10000 +echo "Transferred 10000 LEE → amm-lee-fund" + +sleep 15 + +echo "Keycard path 6 (LEZ holding) state (balance should be 20000):" +wallet account get --account-id "m/44'/60'/0'/0/6" +echo "Keycard path 7 (LEE holding) state (balance should be 20000):" +wallet account get --account-id "m/44'/60'/0'/0/7" +echo "amm-lez-fund state (balance should be 10000):" +wallet account get --account-id amm-lez-fund +echo "amm-lee-fund state (balance should be 10000):" +wallet account get --account-id amm-lee-fund + +# ============================================================================= +# (3) Token transfer: keycard path 6 (LEZ) → public account +# ============================================================================= +echo "" +echo "=== (3) Token transfer: keycard path 6 → pub-receiver (public) ===" +wallet token send \ + --from "m/44'/60'/0'/0/6" \ + --to pub-receiver \ + --amount 1000 +echo "Transferred 1000 LEZ: keycard path 6 → pub-receiver" + +sleep 15 + +echo "Keycard path 6 (LEZ) state (balance should be 19000):" +wallet account get --account-id "m/44'/60'/0'/0/6" +echo "pub-receiver state (balance should be 1000):" +wallet account get --account-id pub-receiver + +# ============================================================================= +# (4) Token transfer: keycard path 6 (LEZ) → private account (shielded) +# ============================================================================= +echo "" +echo "=== (4) Token transfer: keycard path 6 → priv-receiver (private, shielded) ===" +PRIV_RECEIVER=$(wallet account new private | grep -o 'Private/[^[:space:]]*' | head -1) +echo "Fresh private receiver account: $PRIV_RECEIVER" + +wallet token send \ + --from "m/44'/60'/0'/0/6" \ + --to "$PRIV_RECEIVER" \ + --amount 500 +echo "Shielded transfer of 500 LEZ: keycard path 6 → $PRIV_RECEIVER" + +wallet account sync-private + +sleep 15 + +echo "Keycard path 6 (LEZ) state (balance should be 18500):" +wallet account get --account-id "m/44'/60'/0'/0/6" +echo "priv-receiver state (balance should be 500):" +wallet account get --account-id "$PRIV_RECEIVER" + +# ============================================================================= +# (5) Token transfer: private account → keycard path 6 (deshielded) +# Uses priv-receiver from test (4) which holds 500 LEZ. +# The private sender is handled by the ZK circuit; the keycard recipient +# does not sign — resolve() derives its account ID from the card only. +# ============================================================================= +echo "" +echo "=== (5) Token transfer: priv-receiver (private) → keycard path 6 (deshielded) ===" + +wallet token send \ + --from "$PRIV_RECEIVER" \ + --to "m/44'/60'/0'/0/6" \ + --amount 300 +echo "Deshielded transfer of 300 LEZ: $PRIV_RECEIVER → keycard path 6" + +wallet account sync-private + +sleep 15 + +echo "priv-receiver state (balance should be 200):" +wallet account get --account-id "$PRIV_RECEIVER" +echo "Keycard path 6 (LEZ) state (balance should be 18800):" +wallet account get --account-id "m/44'/60'/0'/0/6" + +# ============================================================================= +# (6) Token mint with keycard — definition signed by keycard path 2 +# ============================================================================= +echo "" +echo "=== (6) Token mint: keycard def path 2 mints 2000 LEZ to keycard path 6 ===" +wallet token mint \ + --definition "m/44'/60'/0'/0/2" \ + --holder "m/44'/60'/0'/0/6" \ + --amount 2000 +echo "Minted 2000 LEZ to keycard path 6" + +sleep 15 + +echo "Keycard path 2 (LEZ definition) state (total supply should have increased):" +wallet account get --account-id "m/44'/60'/0'/0/2" +echo "Keycard path 6 (LEZ holding) state (balance should be 20500):" +wallet account get --account-id "m/44'/60'/0'/0/6" + +# ============================================================================= +# (7) Token burn with keycard — holder is keycard path 6 +# ============================================================================= +echo "" +echo "=== (7) Token burn: keycard path 6 burns 500 LEZ ===" +wallet token burn \ + --definition "Public/$LEZ_DEF_ID" \ + --holder "m/44'/60'/0'/0/6" \ + --amount 500 +echo "Burned 500 LEZ from keycard path 6" + +sleep 15 + +echo "Keycard path 2 (LEZ definition) state (total supply should reflect burn):" +wallet account get --account-id "m/44'/60'/0'/0/2" +echo "Keycard path 6 (LEZ holding) state (balance should be 20000):" +wallet account get --account-id "m/44'/60'/0'/0/6" + +# ============================================================================= +# (8) Create AMM pool for LEZ/LEE — without keycard +# ============================================================================= +echo "" +echo "=== (8) Create AMM pool for LEZ/LEE (without keycard) ===" + +wallet amm new \ + --user-holding-a amm-lez-fund \ + --user-holding-b amm-lee-fund \ + --user-holding-lp amm-lp-fund \ + --balance-a 10000 \ + --balance-b 10000 +echo "AMM pool created for LEZ/LEE" + +sleep 15 + +echo "amm-lez-fund state (balance should be 0 — contributed to pool):" +wallet account get --account-id amm-lez-fund +echo "amm-lee-fund state (balance should be 0 — contributed to pool):" +wallet account get --account-id amm-lee-fund +echo "Initial LP holding state (should hold initial LP tokens):" +wallet account get --account-id amm-lp-fund +LP_DEF_ID=$(wallet account get --account-id amm-lp-fund | grep -o '"definition_id":"[^"]*"' | awk -F'"' '{print $4}') +echo "LP token definition ID: $LP_DEF_ID" + +# ============================================================================= +# (9) Swap tokens owned by keycard accounts +# keycard path 7 (LEE) sells 500 LEE; keycard path 6 (LEZ) receives LEZ +# ============================================================================= +echo "" +echo "=== (9) Swap: keycard path 7 sells 500 LEE, keycard path 6 receives LEZ ===" +wallet amm swap-exact-input \ + --user-holding-a "m/44'/60'/0'/0/6" \ + --user-holding-b "m/44'/60'/0'/0/7" \ + --amount-in 500 \ + --min-amount-out 1 \ + --token-definition "$LEE_DEF_ID" +echo "Swap LEE → LEZ complete via keycard" + +sleep 15 + +echo "Keycard path 6 (LEZ holding) state (balance should have increased):" +wallet account get --account-id "m/44'/60'/0'/0/6" +echo "Keycard path 7 (LEE holding) state (balance should have decreased by 500):" +wallet account get --account-id "m/44'/60'/0'/0/7" + +# ============================================================================= +# (10) Add liquidity — keycard accounts for holding A (path 6), B (path 7), LP (path 8) +# ============================================================================= +echo "" +echo "=== (10) Add liquidity (keycard path 6=LEZ, path 7=LEE, path 8=LP) ===" +wallet amm add-liquidity \ + --user-holding-a "m/44'/60'/0'/0/6" \ + --user-holding-b "m/44'/60'/0'/0/7" \ + --user-holding-lp "m/44'/60'/0'/0/8" \ + --max-amount-a 1000 \ + --max-amount-b 1000 \ + --min-amount-lp 1 +echo "Add liquidity complete via keycard" + +sleep 15 + +echo "Keycard path 6 (LEZ holding) state (balance should have decreased):" +wallet account get --account-id "m/44'/60'/0'/0/6" +echo "Keycard path 7 (LEE holding) state (balance should have decreased):" +wallet account get --account-id "m/44'/60'/0'/0/7" +echo "Keycard path 8 (LP holding) state (should have received LP tokens):" +wallet account get --account-id "m/44'/60'/0'/0/8" + +# ============================================================================= +# (11) Remove liquidity — keycard accounts for holding A (path 6), B (path 7), LP (path 8) +# ============================================================================= +echo "" +echo "=== (11) Remove liquidity (keycard path 6=LEZ, path 7=LEE, path 8=LP) ===" +wallet amm remove-liquidity \ + --user-holding-a "m/44'/60'/0'/0/6" \ + --user-holding-b "m/44'/60'/0'/0/7" \ + --user-holding-lp "m/44'/60'/0'/0/8" \ + --balance-lp 500 \ + --min-amount-a 1 \ + --min-amount-b 1 +echo "Remove liquidity complete via keycard" + +sleep 15 + +echo "Keycard path 6 (LEZ holding) state (balance should have increased):" +wallet account get --account-id "m/44'/60'/0'/0/6" +echo "Keycard path 7 (LEE holding) state (balance should have increased):" +wallet account get --account-id "m/44'/60'/0'/0/7" +echo "Keycard path 8 (LP holding) state (balance should have decreased):" +wallet account get --account-id "m/44'/60'/0'/0/8" + +# ============================================================================= +# (12) ATA create — keycard path 9 as owner for LEZ +# ============================================================================= +echo "" +echo "=== (12) ATA create: keycard path 9 as owner, LEZ token ===" +ATA_OWNER_ID=$(wallet account id --account-id "m/44'/60'/0'/0/9") +echo "ATA owner (keycard path 9): $ATA_OWNER_ID" + +wallet ata create \ + --owner "m/44'/60'/0'/0/9" \ + --token-definition "$LEZ_DEF_ID" +echo "ATA created for keycard path 9 / LEZ" + +sleep 15 + +LEZ_ATA_ID=$(wallet ata address --owner "$ATA_OWNER_ID" --token-definition "$LEZ_DEF_ID") +echo "Keycard path 9 LEZ ATA ID: $LEZ_ATA_ID" +echo "ATA state (should be initialized with zero balance):" +wallet account get --account-id "Public/$LEZ_ATA_ID" + +# Fund the ATA from LEZ supply (path 3) — setup for tests 12 and 13 +wallet token send \ + --from "m/44'/60'/0'/0/3" \ + --to "Public/$LEZ_ATA_ID" \ + --amount 3000 +echo "Funded keycard path 9 ATA with 3000 LEZ" + +sleep 15 + +echo "ATA state after funding (balance should be 3000):" +wallet account get --account-id "Public/$LEZ_ATA_ID" + +# ============================================================================= +# (13) ATA send — keycard path 9's ATA → pub-receiver's ATA +# ============================================================================= +echo "" +echo "=== (13) ATA send: keycard path 9's ATA → pub-receiver's ATA ===" +PUB_RECEIVER_ID=$(wallet account id --account-id pub-receiver) +wallet ata create \ + --owner "Public/$PUB_RECEIVER_ID" \ + --token-definition "$LEZ_DEF_ID" +echo "ATA created for pub-receiver / LEZ" + +sleep 15 + +PUB_RECEIVER_ATA_ID=$(wallet ata address --owner "$PUB_RECEIVER_ID" --token-definition "$LEZ_DEF_ID") +echo "pub-receiver LEZ ATA ID: $PUB_RECEIVER_ATA_ID" +echo "pub-receiver ATA state (should be initialized with zero balance):" +wallet account get --account-id "Public/$PUB_RECEIVER_ATA_ID" + +wallet ata send \ + --from "m/44'/60'/0'/0/9" \ + --token-definition "$LEZ_DEF_ID" \ + --to "$PUB_RECEIVER_ATA_ID" \ + --amount 500 +echo "Sent 500 LEZ: keycard path 9 ATA → pub-receiver ATA" + +sleep 15 + +echo "Keycard path 9 ATA state (balance should be 2500):" +wallet account get --account-id "Public/$LEZ_ATA_ID" +echo "pub-receiver ATA state (balance should be 500):" +wallet account get --account-id "Public/$PUB_RECEIVER_ATA_ID" + +# ============================================================================= +# (14) ATA burn — keycard path 9's ATA burns 200 LEZ +# ============================================================================= +echo "" +echo "=== (14) ATA burn: keycard path 9's ATA burns 200 LEZ ===" +wallet ata burn \ + --holder "m/44'/60'/0'/0/9" \ + --token-definition "$LEZ_DEF_ID" \ + --amount 200 +echo "Burned 200 LEZ from keycard path 9 ATA" + +sleep 15 + +echo "Keycard path 9 ATA state (balance should be 2300):" +wallet account get --account-id "Public/$LEZ_ATA_ID" +echo "LEZ definition state (total supply should reflect burn):" +wallet account get --account-id "m/44'/60'/0'/0/2" + +echo "" +echo "=== All keycard token + AMM + ATA tests finished ===" diff --git a/wallet-ffi/src/transfer.rs b/wallet-ffi/src/transfer.rs index 8f3c47d7..a5cbeef0 100644 --- a/wallet-ffi/src/transfer.rs +++ b/wallet-ffi/src/transfer.rs @@ -1,11 +1,14 @@ //! Token transfer functions. -use std::{ffi::CString, ptr}; +use std::{ + ffi::{c_char, CStr, CString}, + ptr, +}; use nssa::AccountId; use wallet::{ account::AccountIdWithPrivacy, cli::CliAccountMention, - program_facades::native_token_transfer::NativeTokenTransfer, + program_facades::native_token_transfer::NativeTokenTransfer, AccountIdentity, }; use crate::{ @@ -17,6 +20,14 @@ use crate::{ FfiPrivateAccountKeys, }; +fn optional_c_str(ptr: *const c_char) -> Option { + if ptr.is_null() { + return None; + } + let c_str = unsafe { CStr::from_ptr(ptr) }; + c_str.to_str().ok().map(str::to_owned) +} + /// Send a public token transfer. /// /// Transfers tokens from one public account to another on the network. @@ -75,15 +86,10 @@ pub unsafe extern "C" fn wallet_ffi_transfer_public( let transfer = NativeTokenTransfer(&wallet); - let from_mention = CliAccountMention::Id(AccountIdWithPrivacy::Public(from_id)); - let to_mention = CliAccountMention::Id(AccountIdWithPrivacy::Public(to_id)); - match block_on(transfer.send_public_transfer( - from_id, - to_id, + AccountIdentity::Public(from_id), + AccountIdentity::Public(to_id), amount, - &from_mention, - &to_mention, )) { Ok(tx_hash) => { let tx_hash = CString::new(tx_hash.to_string()) @@ -140,6 +146,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded( to_keys: *const FfiPrivateAccountKeys, to_identifier: *const FfiU128, amount: *const [u8; 16], + key_path: *const c_char, out_result: *mut FfiTransferResult, ) -> WalletFfiError { let wrapper = match get_wallet(handle) { @@ -176,11 +183,15 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded( }; let to_identifier = u128::from_le_bytes(unsafe { (*to_identifier).data }); let amount = u128::from_le_bytes(unsafe { *amount }); + let from_mention = optional_c_str(key_path).map_or_else( + || CliAccountMention::Id(AccountIdWithPrivacy::Public(from_id)), + CliAccountMention::KeyPath, + ); let transfer = NativeTokenTransfer(&wallet); match block_on(transfer.send_shielded_transfer_to_outer_account( - from_id, + from_mention.into_public_identity(from_id), to_npk, to_vpk, to_identifier, @@ -262,7 +273,6 @@ pub unsafe extern "C" fn wallet_ffi_transfer_deshielded( let from_id = AccountId::new(unsafe { (*from).data }); let to_id = AccountId::new(unsafe { (*to).data }); let amount = u128::from_le_bytes(unsafe { *amount }); - let transfer = NativeTokenTransfer(&wallet); match block_on(transfer.send_deshielded_transfer(from_id, to_id, amount)) { @@ -357,7 +367,6 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private( }; let to_identifier = u128::from_le_bytes(unsafe { (*to_identifier).data }); let amount = u128::from_le_bytes(unsafe { *amount }); - let transfer = NativeTokenTransfer(&wallet); match block_on(transfer.send_private_transfer_to_outer_account( @@ -423,6 +432,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded_owned( from: *const FfiBytes32, to: *const FfiBytes32, amount: *const [u8; 16], + key_path: *const c_char, out_result: *mut FfiTransferResult, ) -> WalletFfiError { let wrapper = match get_wallet(handle) { @@ -446,10 +456,18 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded_owned( let from_id = AccountId::new(unsafe { (*from).data }); let to_id = AccountId::new(unsafe { (*to).data }); let amount = u128::from_le_bytes(unsafe { *amount }); + let from_mention = optional_c_str(key_path).map_or_else( + || CliAccountMention::Id(AccountIdWithPrivacy::Public(from_id)), + CliAccountMention::KeyPath, + ); let transfer = NativeTokenTransfer(&wallet); - match block_on(transfer.send_shielded_transfer(from_id, to_id, amount)) { + match block_on(transfer.send_shielded_transfer( + from_mention.into_public_identity(from_id), + to_id, + amount, + )) { Ok((tx_hash, _shared_key)) => { let tx_hash = CString::new(tx_hash.to_string()) .map_or(ptr::null_mut(), std::ffi::CString::into_raw); @@ -529,7 +547,6 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private_owned( let from_id = AccountId::new(unsafe { (*from).data }); let to_id = AccountId::new(unsafe { (*to).data }); let amount = u128::from_le_bytes(unsafe { *amount }); - let transfer = NativeTokenTransfer(&wallet); match block_on(transfer.send_private_transfer_to_owned_account(from_id, to_id, amount)) { @@ -603,9 +620,7 @@ pub unsafe extern "C" fn wallet_ffi_register_public_account( let transfer = NativeTokenTransfer(&wallet); - let mention = CliAccountMention::Id(AccountIdWithPrivacy::Public(account_id)); - - match block_on(transfer.register_account(account_id, &mention)) { + match block_on(transfer.register_account(AccountIdentity::Public(account_id))) { Ok(tx_hash) => { let tx_hash = CString::new(tx_hash.to_string()) .map_or(ptr::null_mut(), std::ffi::CString::into_raw); @@ -673,7 +688,6 @@ pub unsafe extern "C" fn wallet_ffi_register_private_account( }; let account_id = AccountId::new(unsafe { (*account_id).data }); - let transfer = NativeTokenTransfer(&wallet); match block_on(transfer.register_account_private(account_id)) { diff --git a/wallet-ffi/wallet_ffi.h b/wallet-ffi/wallet_ffi.h index adbb7b50..693e2d5c 100644 --- a/wallet-ffi/wallet_ffi.h +++ b/wallet-ffi/wallet_ffi.h @@ -787,6 +787,7 @@ enum WalletFfiError wallet_ffi_transfer_shielded(struct WalletHandle *handle, const struct FfiPrivateAccountKeys *to_keys, const struct FfiU128 *to_identifier, const uint8_t (*amount)[16], + const char *key_path, struct FfiTransferResult *out_result); /** @@ -894,6 +895,7 @@ enum WalletFfiError wallet_ffi_transfer_shielded_owned(struct WalletHandle *hand const struct FfiBytes32 *from, const struct FfiBytes32 *to, const uint8_t (*amount)[16], + const char *key_path, struct FfiTransferResult *out_result); /** diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 3aaa1753..c8ccd3ef 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -21,7 +21,7 @@ ata_core.workspace = true bip39.workspace = true pyo3.workspace = true rpassword = "7" -zeroize = "1" +zeroize.workspace = true keycard_wallet.workspace = true anyhow.workspace = true @@ -46,6 +46,11 @@ optfield = "0.4.0" url.workspace = true derive_more = { workspace = true, features = ["display"] } +[features] +# Enables `keycard get-private-keys` command that prints sensitive secret keys to terminal. +# Never enable in production builds. +keycard-debug = [] + [dev-dependencies] tempfile.workspace = true key_protocol = { workspace = true, features = ["test_utils"] } diff --git a/wallet/src/account_manager.rs b/wallet/src/account_manager.rs index 5caf2b22..f8d8bcbe 100644 --- a/wallet/src/account_manager.rs +++ b/wallet/src/account_manager.rs @@ -1,6 +1,7 @@ use anyhow::Result; use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; -use nssa::{AccountId, PrivateKey}; +use keycard_wallet::{KeycardWallet, python_path}; +use nssa::{AccountId, PrivateKey, PublicKey, Signature}; use nssa_core::{ Identifier, InputAccountIdentity, MembershipProof, NullifierPublicKey, NullifierSecretKey, SharedSecretKey, @@ -10,11 +11,16 @@ use nssa_core::{ use crate::{ExecutionFailureKind, WalletCore}; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum AccountIdentity { Public(AccountId), /// A public account without signing. Would not try to sign, even if account is owned. PublicNoSign(AccountId), + /// A public account from keycard. Mandatory signing. + PublicKeycard { + account_id: AccountId, + key_path: String, + }, PrivateOwned(AccountId), PrivateForeign { npk: NullifierPublicKey, @@ -57,7 +63,26 @@ impl AccountIdentity { /// Note: `PublicNoSign` still counts as public, the variant just suppresses the signing-key /// lookup. pub const fn is_public(&self) -> bool { - matches!(&self, Self::Public(_) | Self::PublicNoSign(_)) + matches!( + &self, + Self::Public(_) | Self::PublicNoSign(_) | Self::PublicKeycard { .. } + ) + } + + /// Returns the `AccountId` for public variants. Used by facades that need the raw ID + /// for derived-address computation alongside the identity. + #[must_use] + pub const fn public_account_id(&self) -> Option { + match self { + Self::Public(id) | Self::PublicNoSign(id) => Some(*id), + Self::PublicKeycard { account_id, .. } => Some(*account_id), + Self::PrivateOwned(_) + | Self::PrivateForeign { .. } + | Self::PrivatePdaOwned(_) + | Self::PrivatePdaForeign { .. } + | Self::PrivateShared { .. } + | Self::PrivatePdaShared { .. } => None, + } } #[must_use] @@ -86,11 +111,16 @@ enum State { account: AccountWithMetadata, sk: Option, }, + PublicKeycard { + account: AccountWithMetadata, + key_path: String, + }, Private(AccountPreparedData), } pub struct AccountManager { states: Vec, + pin: Option, } impl AccountManager { @@ -99,6 +129,7 @@ impl AccountManager { accounts: Vec, ) -> Result { let mut states = Vec::with_capacity(accounts.len()); + let mut pin = None; for account in accounts { let state = match account { @@ -124,6 +155,35 @@ impl AccountManager { State::Public { account, sk } } + AccountIdentity::PublicKeycard { + account_id, + key_path, + } => { + let acc = wallet + .get_account_public(account_id) + .await + .map_err(ExecutionFailureKind::SequencerError)?; + + let account = AccountWithMetadata::new(acc.clone(), true, account_id); + + if pin.is_none() { + pin = Some( + crate::helperfunctions::read_pin() + .map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::< + pyo3::exceptions::PyRuntimeError, + _, + >( + e.to_string() + )) + })? + .as_str() + .to_owned(), + ); + } + + State::PublicKeycard { account, key_path } + } AccountIdentity::PrivateOwned(account_id) => { let pre = private_key_tree_acc_preparation(wallet, account_id, false).await?; @@ -214,27 +274,33 @@ impl AccountManager { states.push(state); } - Ok(Self { states }) + Ok(Self { states, pin }) } pub fn pre_states(&self) -> Vec { self.states .iter() .map(|state| match state { - State::Public { account, .. } => account.clone(), + State::Public { account, .. } | State::PublicKeycard { account, .. } => { + account.clone() + } State::Private(pre) => pre.pre_state.clone(), }) .collect() } pub fn public_account_nonces(&self) -> Vec { - self.states - .iter() - .filter_map(|state| match state { - State::Public { account, sk } => sk.as_ref().map(|_| account.account.nonce), - State::Private(_) => None, - }) - .collect() + // Must match the signature order produced by sign_message(): local accounts first, + // keycard accounts second. + let local = self.states.iter().filter_map(|state| match state { + State::Public { account, sk } => sk.as_ref().map(|_| account.account.nonce), + State::PublicKeycard { .. } | State::Private(_) => None, + }); + let keycard = self.states.iter().filter_map(|state| match state { + State::PublicKeycard { account, .. } => Some(account.account.nonce), + State::Public { .. } | State::Private(_) => None, + }); + local.chain(keycard).collect() } pub fn private_account_keys(&self) -> Vec { @@ -247,7 +313,7 @@ impl AccountManager { vpk: pre.vpk.clone(), epk: pre.epk.clone(), }), - State::Public { .. } => None, + State::Public { .. } | State::PublicKeycard { .. } => None, }) .collect() } @@ -260,7 +326,7 @@ impl AccountManager { self.states .iter() .map(|state| match state { - State::Public { .. } => InputAccountIdentity::Public, + State::Public { .. } | State::PublicKeycard { .. } => InputAccountIdentity::Public, State::Private(pre) if pre.is_pda => match (pre.nsk, pre.proof.clone()) { (Some(nsk), Some(membership_proof)) => InputAccountIdentity::PrivatePdaUpdate { ssk: pre.ssk, @@ -304,21 +370,61 @@ impl AccountManager { self.states .iter() .filter_map(|state| match state { - State::Public { account, .. } => Some(account.account_id), + State::Public { account, .. } | State::PublicKeycard { account, .. } => { + Some(account.account_id) + } State::Private(_) => None, }) .collect() } - pub fn public_account_auth(&self) -> Vec<&PrivateKey> { + pub fn public_non_keycard_account_auth(&self) -> Vec<&PrivateKey> { self.states .iter() .filter_map(|state| match state { State::Public { sk, .. } => sk.as_ref(), - State::Private(_) => None, + State::PublicKeycard { .. } | State::Private(_) => None, }) .collect() } + + pub fn sign_message(&self, message_hash: [u8; 32]) -> Result> { + let mut sigs: Vec<(Signature, PublicKey)> = self + .public_non_keycard_account_auth() + .into_iter() + .map(|key| { + ( + Signature::new(key, &message_hash), + PublicKey::new_from_private_key(key), + ) + }) + .collect(); + + let keycard_paths: Vec<&str> = self + .states + .iter() + .filter_map(|state| match state { + State::PublicKeycard { key_path, .. } => Some(key_path.as_str()), + State::Private(_) | State::Public { .. } => None, + }) + .collect(); + + if let Some(pin) = self.pin.clone() { + pyo3::Python::with_gil(|py| -> pyo3::PyResult<()> { + python_path::add_python_path(py)?; + let wallet = KeycardWallet::new(py)?; + wallet.connect(py, &pin)?; + for path in keycard_paths { + sigs.push(wallet.sign_message_for_path(py, path, &message_hash)?); + } + let _res = wallet.close_session(py); + Ok(()) + }) + .map_err(anyhow::Error::from)?; + } + + Ok(sigs) + } } struct AccountPreparedData { diff --git a/wallet/src/cli/account.rs b/wallet/src/cli/account.rs index 1dcea1d5..97bbd095 100644 --- a/wallet/src/cli/account.rs +++ b/wallet/src/cli/account.rs @@ -15,6 +15,12 @@ use crate::{ /// Represents generic chain CLI subcommand. #[derive(Subcommand, Debug, Clone)] pub enum AccountSubcommand { + /// Resolve an account mention and print just the account ID (no privacy prefix). + Id { + /// Account id with privacy prefix, label, or BIP-32 key path. + #[arg(long)] + account_id: CliAccountMention, + }, /// Get account data. Get { /// Flag to get raw account data. @@ -261,6 +267,14 @@ impl WalletSubcommand for AccountSubcommand { wallet_core: &mut WalletCore, ) -> Result { match self { + Self::Id { account_id } => { + let resolved = account_id.resolve(wallet_core.storage())?; + let id = match resolved { + AccountIdWithPrivacy::Public(id) | AccountIdWithPrivacy::Private(id) => id, + }; + println!("{id}"); + Ok(SubcommandReturnValue::Empty) + } Self::Get { raw, keys, diff --git a/wallet/src/cli/keycard.rs b/wallet/src/cli/keycard.rs index ead1e84b..1dec07d9 100644 --- a/wallet/src/cli/keycard.rs +++ b/wallet/src/cli/keycard.rs @@ -16,6 +16,20 @@ pub enum KeycardSubcommand { Disconnect, Init, Load, + /// Retrieve the private keys (NSK, VSK) for a given BIP-32 key path. + /// + /// Prints raw key material to stdout — intended for debugging only. + /// Requires --reveal to confirm intent. + /// Only available when built with the `keycard-debug` feature. + #[cfg(feature = "keycard-debug")] + GetPrivateKeys { + /// BIP-32 derivation path, e.g. `m/44'/60'/0'/0/0`. + #[arg(long)] + key_path: String, + /// Confirm that raw NSK and VSK should be disclosed on stdout. + #[arg(long)] + reveal: bool, + }, } impl WalletSubcommand for KeycardSubcommand { @@ -26,7 +40,8 @@ impl WalletSubcommand for KeycardSubcommand { match self { Self::Available => { Python::with_gil(|py| { - python_path::add_python_path(py).expect("keycard_wallet.py not found"); + python_path::add_python_path(py) + .expect("`wallet::keycard::available`: unable to setup python path"); let wallet = KeycardWallet::new(py) .expect("`wallet::keycard::available`: invalid data received for pin"); @@ -47,7 +62,8 @@ impl WalletSubcommand for KeycardSubcommand { let pin = read_pin()?; Python::with_gil(|py| { - python_path::add_python_path(py).expect("keycard_wallet.py not found"); + python_path::add_python_path(py) + .expect("`wallet::keycard::connect`: unable to setup python path"); let wallet = KeycardWallet::new(py) .expect("`wallet::keycard::connect`: invalid keycard wallet provided"); @@ -66,7 +82,8 @@ impl WalletSubcommand for KeycardSubcommand { let pin = read_pin()?; Python::with_gil(|py| { - python_path::add_python_path(py).expect("keycard_wallet.py not found"); + python_path::add_python_path(py) + .expect("`wallet::keycard::disconnect`: unable to setup python path"); let wallet = KeycardWallet::new(py) .expect("`wallet::keycard::disconnect`: invalid keycard wallet provided"); @@ -89,7 +106,8 @@ impl WalletSubcommand for KeycardSubcommand { let pin = read_pin()?; Python::with_gil(|py| { - python_path::add_python_path(py).expect("keycard_wallet.py not found"); + python_path::add_python_path(py) + .expect("`wallet::keycard::init`: unable to setup python path"); let wallet = KeycardWallet::new(py) .expect("`wallet::keycard::init`: invalid keycard wallet provided"); @@ -111,7 +129,8 @@ impl WalletSubcommand for KeycardSubcommand { let mnemonic = read_mnemonic()?; Python::with_gil(|py| { - python_path::add_python_path(py).expect("keycard_wallet.py not found"); + python_path::add_python_path(py) + .expect("`wallet::keycard::load`: unable to setup python path"); let wallet = KeycardWallet::new(py) .expect("`wallet::keycard::load`: invalid keycard wallet provided"); @@ -131,6 +150,27 @@ impl WalletSubcommand for KeycardSubcommand { Ok(SubcommandReturnValue::Empty) } + #[cfg(feature = "keycard-debug")] + Self::GetPrivateKeys { key_path, reveal } => { + if !reveal { + eprintln!( + "WARNING: pass --reveal to print NSK and VSK. \ + Disclosing either key fully compromises the account's privacy." + ); + return Ok(SubcommandReturnValue::Empty); + } + eprintln!( + "WARNING: NSK and VSK are being printed to stdout. \ + Any terminal log, scrollback, or screen recording captures these keys." + ); + let pin = read_pin()?; + let (nsk, vsk) = + KeycardWallet::get_private_keys_for_path_with_connect(&pin, &key_path) + .map_err(anyhow::Error::from)?; + println!("NSK: {}", hex::encode(*nsk)); + println!("VSK: {}", hex::encode(*vsk)); + Ok(SubcommandReturnValue::Empty) + } } } } diff --git a/wallet/src/cli/mod.rs b/wallet/src/cli/mod.rs index 8acdfa67..5f0fcb44 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -138,13 +138,34 @@ impl CliAccountMention { Self::KeyPath(path) => { let pin = read_pin()?; let id_str = - keycard_wallet::KeycardWallet::get_account_id_for_path_with_connect(&pin, path) - .map_err(anyhow::Error::from)?; + keycard_wallet::KeycardWallet::get_public_account_id_for_path_with_connect( + &pin, path, + ) + .map_err(anyhow::Error::from)?; AccountIdWithPrivacy::from_str(&id_str) .map_err(|e| anyhow::anyhow!("Invalid account id from keycard: {e}")) } } } + + #[must_use] + pub fn key_path(&self) -> Option<&str> { + match self { + Self::KeyPath(path) => Some(path), + Self::Id(_) | Self::Label(_) => None, + } + } + + #[must_use] + pub fn into_public_identity(self, account_id: nssa::AccountId) -> crate::AccountIdentity { + match self { + Self::KeyPath(key_path) => crate::AccountIdentity::PublicKeycard { + account_id, + key_path, + }, + Self::Id(_) | Self::Label(_) => crate::AccountIdentity::Public(account_id), + } + } } impl FromStr for CliAccountMention { diff --git a/wallet/src/cli/programs/amm.rs b/wallet/src/cli/programs/amm.rs index 6173eaf3..1ef4fdba 100644 --- a/wallet/src/cli/programs/amm.rs +++ b/wallet/src/cli/programs/amm.rs @@ -131,25 +131,28 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { balance_a, balance_b, } => { - let user_holding_a = user_holding_a.resolve(wallet_core.storage())?; - let user_holding_b = user_holding_b.resolve(wallet_core.storage())?; - let user_holding_lp = user_holding_lp.resolve(wallet_core.storage())?; - match (user_holding_a, user_holding_b, user_holding_lp) { + let a_id = user_holding_a.resolve(wallet_core.storage())?; + let b_id = user_holding_b.resolve(wallet_core.storage())?; + let lp_id = user_holding_lp.resolve(wallet_core.storage())?; + match (a_id, b_id, lp_id) { ( - AccountIdWithPrivacy::Public(user_holding_a), - AccountIdWithPrivacy::Public(user_holding_b), - AccountIdWithPrivacy::Public(user_holding_lp), + AccountIdWithPrivacy::Public(a), + AccountIdWithPrivacy::Public(b), + AccountIdWithPrivacy::Public(lp), ) => { - Amm(wallet_core) + let tx_hash = Amm(wallet_core) .send_new_definition( - user_holding_a, - user_holding_b, - user_holding_lp, + user_holding_a.into_public_identity(a), + user_holding_b.into_public_identity(b), + user_holding_lp.into_public_identity(lp), balance_a, balance_b, ) .await?; - + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } _ => { @@ -165,23 +168,23 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { min_amount_out, token_definition, } => { - let user_holding_a = user_holding_a.resolve(wallet_core.storage())?; - let user_holding_b = user_holding_b.resolve(wallet_core.storage())?; - match (user_holding_a, user_holding_b) { - ( - AccountIdWithPrivacy::Public(user_holding_a), - AccountIdWithPrivacy::Public(user_holding_b), - ) => { - Amm(wallet_core) + let a_id = user_holding_a.resolve(wallet_core.storage())?; + let b_id = user_holding_b.resolve(wallet_core.storage())?; + match (a_id, b_id) { + (AccountIdWithPrivacy::Public(a), AccountIdWithPrivacy::Public(b)) => { + let tx_hash = Amm(wallet_core) .send_swap_exact_input( - user_holding_a, - user_holding_b, + user_holding_a.into_public_identity(a), + user_holding_b.into_public_identity(b), amount_in, min_amount_out, token_definition, ) .await?; - + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } _ => { @@ -197,23 +200,23 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { max_amount_in, token_definition, } => { - let user_holding_a = user_holding_a.resolve(wallet_core.storage())?; - let user_holding_b = user_holding_b.resolve(wallet_core.storage())?; - match (user_holding_a, user_holding_b) { - ( - AccountIdWithPrivacy::Public(user_holding_a), - AccountIdWithPrivacy::Public(user_holding_b), - ) => { - Amm(wallet_core) + let a_id = user_holding_a.resolve(wallet_core.storage())?; + let b_id = user_holding_b.resolve(wallet_core.storage())?; + match (a_id, b_id) { + (AccountIdWithPrivacy::Public(a), AccountIdWithPrivacy::Public(b)) => { + let tx_hash = Amm(wallet_core) .send_swap_exact_output( - user_holding_a, - user_holding_b, + user_holding_a.into_public_identity(a), + user_holding_b.into_public_identity(b), exact_amount_out, max_amount_in, token_definition, ) .await?; - + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } _ => { @@ -230,26 +233,29 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { max_amount_a, max_amount_b, } => { - let user_holding_a = user_holding_a.resolve(wallet_core.storage())?; - let user_holding_b = user_holding_b.resolve(wallet_core.storage())?; - let user_holding_lp = user_holding_lp.resolve(wallet_core.storage())?; - match (user_holding_a, user_holding_b, user_holding_lp) { + let a_id = user_holding_a.resolve(wallet_core.storage())?; + let b_id = user_holding_b.resolve(wallet_core.storage())?; + let lp_id = user_holding_lp.resolve(wallet_core.storage())?; + match (a_id, b_id, lp_id) { ( - AccountIdWithPrivacy::Public(user_holding_a), - AccountIdWithPrivacy::Public(user_holding_b), - AccountIdWithPrivacy::Public(user_holding_lp), + AccountIdWithPrivacy::Public(a), + AccountIdWithPrivacy::Public(b), + AccountIdWithPrivacy::Public(lp), ) => { - Amm(wallet_core) + let tx_hash = Amm(wallet_core) .send_add_liquidity( - user_holding_a, - user_holding_b, - user_holding_lp, + user_holding_a.into_public_identity(a), + user_holding_b.into_public_identity(b), + user_holding_lp.into_public_identity(lp), min_amount_lp, max_amount_a, max_amount_b, ) .await?; - + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } _ => { @@ -266,26 +272,29 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { min_amount_a, min_amount_b, } => { - let user_holding_a = user_holding_a.resolve(wallet_core.storage())?; - let user_holding_b = user_holding_b.resolve(wallet_core.storage())?; - let user_holding_lp = user_holding_lp.resolve(wallet_core.storage())?; - match (user_holding_a, user_holding_b, user_holding_lp) { + let a_id = user_holding_a.resolve(wallet_core.storage())?; + let b_id = user_holding_b.resolve(wallet_core.storage())?; + let lp_id = user_holding_lp.resolve(wallet_core.storage())?; + match (a_id, b_id, lp_id) { ( - AccountIdWithPrivacy::Public(user_holding_a), - AccountIdWithPrivacy::Public(user_holding_b), - AccountIdWithPrivacy::Public(user_holding_lp), + AccountIdWithPrivacy::Public(a), + AccountIdWithPrivacy::Public(b), + AccountIdWithPrivacy::Public(lp), ) => { - Amm(wallet_core) + let tx_hash = Amm(wallet_core) .send_remove_liquidity( - user_holding_a, - user_holding_b, - user_holding_lp, + a, + b, + user_holding_lp.into_public_identity(lp), balance_lp, min_amount_a, min_amount_b, ) .await?; - + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } _ => { diff --git a/wallet/src/cli/programs/ata.rs b/wallet/src/cli/programs/ata.rs index e4c30d9c..1b9b9428 100644 --- a/wallet/src/cli/programs/ata.rs +++ b/wallet/src/cli/programs/ata.rs @@ -91,14 +91,18 @@ impl WalletSubcommand for AtaSubcommand { owner, token_definition, } => { - let owner = owner.resolve(wallet_core.storage())?; + let owner_resolved = owner.resolve(wallet_core.storage())?; let definition_id = token_definition; - match owner { + match owner_resolved { AccountIdWithPrivacy::Public(owner_id) => { - Ata(wallet_core) - .send_create(owner_id, definition_id) + let tx_hash = Ata(wallet_core) + .send_create(owner.into_public_identity(owner_id), definition_id) .await?; + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } AccountIdWithPrivacy::Private(owner_id) => { @@ -127,15 +131,24 @@ impl WalletSubcommand for AtaSubcommand { to, amount, } => { - let from = from.resolve(wallet_core.storage())?; + let from_resolved = from.resolve(wallet_core.storage())?; let definition_id = token_definition; let to_id = to; - match from { + match from_resolved { AccountIdWithPrivacy::Public(from_id) => { - Ata(wallet_core) - .send_transfer(from_id, definition_id, to_id, amount) + let tx_hash = Ata(wallet_core) + .send_transfer( + from.into_public_identity(from_id), + definition_id, + to_id, + amount, + ) .await?; + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } AccountIdWithPrivacy::Private(from_id) => { @@ -163,14 +176,22 @@ impl WalletSubcommand for AtaSubcommand { token_definition, amount, } => { - let holder = holder.resolve(wallet_core.storage())?; + let holder_resolved = holder.resolve(wallet_core.storage())?; let definition_id = token_definition; - match holder { + match holder_resolved { AccountIdWithPrivacy::Public(holder_id) => { - Ata(wallet_core) - .send_burn(holder_id, definition_id, amount) + let tx_hash = Ata(wallet_core) + .send_burn( + holder.into_public_identity(holder_id), + definition_id, + amount, + ) .await?; + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } AccountIdWithPrivacy::Private(holder_id) => { diff --git a/wallet/src/cli/programs/native_token_transfer.rs b/wallet/src/cli/programs/native_token_transfer.rs index 87c38bef..c20c2726 100644 --- a/wallet/src/cli/programs/native_token_transfer.rs +++ b/wallet/src/cli/programs/native_token_transfer.rs @@ -5,7 +5,7 @@ use nssa::AccountId; use crate::{ AccDecodeData::Decode, - WalletCore, + AccountIdentity, WalletCore, account::AccountIdWithPrivacy, cli::{CliAccountMention, SubcommandReturnValue, WalletSubcommand}, program_facades::native_token_transfer::NativeTokenTransfer, @@ -60,7 +60,7 @@ impl WalletSubcommand for AuthTransferSubcommand { match resolved { AccountIdWithPrivacy::Public(pub_account_id) => { let tx_hash = NativeTokenTransfer(wallet_core) - .register_account(pub_account_id, &account_id) + .register_account(account_id.into_public_identity(pub_account_id)) .await?; println!("Transaction hash is {tx_hash}"); @@ -124,12 +124,11 @@ impl WalletSubcommand for AuthTransferSubcommand { } (Some(to), None, None) => match (from, to) { (AccountIdWithPrivacy::Public(from), AccountIdWithPrivacy::Public(to)) => { + let to_mention = to_account.expect("matched Some branch"); NativeTokenTransferProgramSubcommand::Public { - from, - to, + from: Some(from_account.into_public_identity(from)), + to: Some(to_mention.into_public_identity(to)), amount, - from_mention: from_account, - to_mention: to_account.expect("matched Some branch"), } } ( @@ -148,7 +147,7 @@ impl WalletSubcommand for AuthTransferSubcommand { (AccountIdWithPrivacy::Public(from), AccountIdWithPrivacy::Private(to)) => { NativeTokenTransferProgramSubcommand::Shielded( NativeTokenTransferProgramSubcommandShielded::ShieldedOwned { - from, + from: Some(from_account.into_public_identity(from)), to, amount, }, @@ -170,7 +169,7 @@ impl WalletSubcommand for AuthTransferSubcommand { AccountIdWithPrivacy::Public(from) => { NativeTokenTransferProgramSubcommand::Shielded( NativeTokenTransferProgramSubcommandShielded::ShieldedForeign { - from, + from: Some(from_account.into_public_identity(from)), to_npk, to_vpk, to_identifier, @@ -194,19 +193,13 @@ pub enum NativeTokenTransferProgramSubcommand { /// /// Public operation. Public { - /// from - valid 32 byte hex string. - #[arg(long)] - from: AccountId, - /// to - valid 32 byte hex string. - #[arg(long)] - to: AccountId, + #[arg(skip)] + from: Option, + #[arg(skip)] + to: Option, /// amount - amount of balance to move. #[arg(long)] amount: u128, - #[arg(skip)] - from_mention: CliAccountMention, - #[arg(skip)] - to_mention: CliAccountMention, }, /// Private execution. #[command(subcommand)] @@ -239,8 +232,8 @@ pub enum NativeTokenTransferProgramSubcommandShielded { /// Shielded operation. ShieldedOwned { /// from - valid 32 byte hex string. - #[arg(long)] - from: AccountId, + #[arg(skip)] + from: Option, /// to - valid 32 byte hex string. #[arg(long)] to: AccountId, @@ -252,9 +245,8 @@ pub enum NativeTokenTransferProgramSubcommandShielded { /// /// Shielded operation. ShieldedForeign { - /// from - valid 32 byte hex string. - #[arg(long)] - from: AccountId, + #[arg(skip)] + from: Option, /// `to_npk` - valid 32 byte hex string. #[arg(long)] to_npk: String, @@ -395,7 +387,11 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { match self { Self::ShieldedOwned { from, to, amount } => { let (tx_hash, secret) = NativeTokenTransfer(wallet_core) - .send_shielded_transfer(from, to, amount) + .send_shielded_transfer( + from.expect("from set during Send dispatch"), + to, + amount, + ) .await?; println!("Transaction hash is {tx_hash}"); @@ -435,7 +431,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { let (tx_hash, _) = NativeTokenTransfer(wallet_core) .send_shielded_transfer_to_outer_account( - from, + from.expect("from set during Send dispatch"), to_npk, to_vpk, to_identifier.unwrap_or_else(rand::random), @@ -487,15 +483,13 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } - Self::Public { - from, - to, - amount, - from_mention, - to_mention, - } => { + Self::Public { from, to, amount } => { let tx_hash = NativeTokenTransfer(wallet_core) - .send_public_transfer(from, to, amount, &from_mention, &to_mention) + .send_public_transfer( + from.expect("from is set during Send dispatch"), + to.expect("to is set during Send dispatch"), + amount, + ) .await?; println!("Transaction hash is {tx_hash}"); diff --git a/wallet/src/cli/programs/token.rs b/wallet/src/cli/programs/token.rs index 61e24e68..12abc309 100644 --- a/wallet/src/cli/programs/token.rs +++ b/wallet/src/cli/programs/token.rs @@ -5,7 +5,7 @@ use nssa::AccountId; use crate::{ AccDecodeData::Decode, - WalletCore, + AccountIdentity, WalletCore, account::AccountIdWithPrivacy, cli::{CliAccountMention, SubcommandReturnValue, WalletSubcommand}, program_facades::token::Token, @@ -114,20 +114,21 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { name, total_supply, } => { + let def_mention = definition_account_id.clone(); + let sup_mention = supply_account_id.clone(); let definition_account_id = definition_account_id.resolve(wallet_core.storage())?; let supply_account_id = supply_account_id.resolve(wallet_core.storage())?; let underlying_subcommand = match (definition_account_id, supply_account_id) { - ( - AccountIdWithPrivacy::Public(definition_account_id), - AccountIdWithPrivacy::Public(supply_account_id), - ) => TokenProgramSubcommand::Create( - CreateNewTokenProgramSubcommand::NewPublicDefPublicSupp { - definition_account_id, - supply_account_id, - name, - total_supply, - }, - ), + (AccountIdWithPrivacy::Public(_), AccountIdWithPrivacy::Public(_)) => { + TokenProgramSubcommand::Create( + CreateNewTokenProgramSubcommand::NewPublicDefPublicSupp { + definition_account_id: def_mention, + supply_account_id: sup_mention, + name, + total_supply, + }, + ) + } ( AccountIdWithPrivacy::Public(definition_account_id), AccountIdWithPrivacy::Private(supply_account_id), @@ -173,6 +174,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { to_identifier, amount, } => { + let from_mention = from.clone(); + let to_mention = to.clone(); let from = from.resolve(wallet_core.storage())?; let to = to .map(|account_mention| account_mention.resolve(wallet_core.storage())) @@ -192,11 +195,11 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { anyhow::bail!("List of public keys is uncomplete"); } (Some(to), None, None) => match (from, to) { - (AccountIdWithPrivacy::Public(from), AccountIdWithPrivacy::Public(to)) => { + (AccountIdWithPrivacy::Public(_), AccountIdWithPrivacy::Public(_)) => { TokenProgramSubcommand::Public( TokenProgramSubcommandPublic::TransferToken { - sender_account_id: from, - recipient_account_id: to, + sender_account_id: from_mention, + recipient_account_id: to_mention.expect("`wallet::cli::programs::token::Send`: Invalid to_mention account provided"), balance_to_move: amount, }, ) @@ -223,7 +226,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { (AccountIdWithPrivacy::Public(from), AccountIdWithPrivacy::Private(to)) => { TokenProgramSubcommand::Shielded( TokenProgramSubcommandShielded::TransferTokenShieldedOwned { - sender_account_id: from, + sender: Some(from_mention.into_public_identity(from)), recipient_account_id: to, balance_to_move: amount, }, @@ -242,7 +245,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { ), AccountIdWithPrivacy::Public(from) => TokenProgramSubcommand::Shielded( TokenProgramSubcommandShielded::TransferTokenShieldedForeign { - sender_account_id: from, + sender: Some(from_mention.into_public_identity(from)), recipient_npk: to_npk, recipient_vpk: to_vpk, recipient_identifier: to_identifier, @@ -259,17 +262,17 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { holder, amount, } => { + let holder_mention = holder.clone(); let definition = definition.resolve(wallet_core.storage())?; let holder = holder.resolve(wallet_core.storage())?; let underlying_subcommand = match (definition, holder) { - ( - AccountIdWithPrivacy::Public(definition), - AccountIdWithPrivacy::Public(holder), - ) => TokenProgramSubcommand::Public(TokenProgramSubcommandPublic::BurnToken { - definition_account_id: definition, - holder_account_id: holder, - amount, - }), + (AccountIdWithPrivacy::Public(definition), AccountIdWithPrivacy::Public(_)) => { + TokenProgramSubcommand::Public(TokenProgramSubcommandPublic::BurnToken { + definition_account_id: definition, + holder_account_id: holder_mention, + amount, + }) + } ( AccountIdWithPrivacy::Private(definition), AccountIdWithPrivacy::Private(holder), @@ -312,6 +315,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { holder_identifier, amount, } => { + let def_mention = definition.clone(); + let holder_mention = holder.clone(); let definition = definition.resolve(wallet_core.storage())?; let holder = holder .map(|account_mention| account_mention.resolve(wallet_core.storage())) @@ -331,16 +336,15 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { anyhow::bail!("List of public keys is uncomplete"); } (Some(holder), None, None) => match (definition, holder) { - ( - AccountIdWithPrivacy::Public(definition), - AccountIdWithPrivacy::Public(holder), - ) => TokenProgramSubcommand::Public( - TokenProgramSubcommandPublic::MintToken { - definition_account_id: definition, - holder_account_id: holder, - amount, - }, - ), + (AccountIdWithPrivacy::Public(_), AccountIdWithPrivacy::Public(_)) => { + TokenProgramSubcommand::Public( + TokenProgramSubcommandPublic::MintToken { + definition_account_id: def_mention, + holder_account_id: holder_mention.expect("`wallet::cli::programs::token::Mint`: Invalid holder_mention account provided"), + amount, + }, + ) + } ( AccountIdWithPrivacy::Private(definition), AccountIdWithPrivacy::Private(holder), @@ -430,9 +434,9 @@ pub enum TokenProgramSubcommandPublic { // Transfer tokens using the token program TransferToken { #[arg(short, long)] - sender_account_id: AccountId, + sender_account_id: CliAccountMention, #[arg(short, long)] - recipient_account_id: AccountId, + recipient_account_id: CliAccountMention, #[arg(short, long)] balance_to_move: u128, }, @@ -441,16 +445,16 @@ pub enum TokenProgramSubcommandPublic { #[arg(short, long)] definition_account_id: AccountId, #[arg(short, long)] - holder_account_id: AccountId, + holder_account_id: CliAccountMention, #[arg(short, long)] amount: u128, }, // Transfer tokens using the token program MintToken { #[arg(short, long)] - definition_account_id: AccountId, + definition_account_id: CliAccountMention, #[arg(short, long)] - holder_account_id: AccountId, + holder_account_id: CliAccountMention, #[arg(short, long)] amount: u128, }, @@ -555,8 +559,8 @@ pub enum TokenProgramSubcommandDeshielded { pub enum TokenProgramSubcommandShielded { // Transfer tokens using the token program TransferTokenShieldedOwned { - #[arg(short, long)] - sender_account_id: AccountId, + #[arg(skip)] + sender: Option, #[arg(short, long)] recipient_account_id: AccountId, #[arg(short, long)] @@ -564,8 +568,8 @@ pub enum TokenProgramSubcommandShielded { }, // Transfer tokens using the token program TransferTokenShieldedForeign { - #[arg(short, long)] - sender_account_id: AccountId, + #[arg(skip)] + sender: Option, /// `recipient_npk` - valid 32 byte hex string. #[arg(long)] recipient_npk: String, @@ -620,9 +624,9 @@ pub enum CreateNewTokenProgramSubcommand { /// Definition - public, supply - public. NewPublicDefPublicSupp { #[arg(short, long)] - definition_account_id: AccountId, + definition_account_id: CliAccountMention, #[arg(short, long)] - supply_account_id: AccountId, + supply_account_id: CliAccountMention, #[arg(short, long)] name: String, #[arg(short, long)] @@ -680,13 +684,28 @@ impl WalletSubcommand for TokenProgramSubcommandPublic { recipient_account_id, balance_to_move, } => { - Token(wallet_core) + let sender = sender_account_id.resolve(wallet_core.storage())?; + let recipient = recipient_account_id.resolve(wallet_core.storage())?; + let ( + AccountIdWithPrivacy::Public(sender_id), + AccountIdWithPrivacy::Public(recipient_id), + ) = (sender, recipient) + else { + anyhow::bail!( + "`TokenProgramSubcommandPublic::TransferToken`: Unexpected private account received." + ); + }; + let tx_hash = Token(wallet_core) .send_transfer_transaction( - sender_account_id, - recipient_account_id, + sender_account_id.into_public_identity(sender_id), + recipient_account_id.into_public_identity(recipient_id), balance_to_move, ) .await?; + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } Self::BurnToken { @@ -694,9 +713,23 @@ impl WalletSubcommand for TokenProgramSubcommandPublic { holder_account_id, amount, } => { - Token(wallet_core) - .send_burn_transaction(definition_account_id, holder_account_id, amount) + let holder = holder_account_id.resolve(wallet_core.storage())?; + let AccountIdWithPrivacy::Public(holder_id) = holder else { + anyhow::bail!( + "`TokenProgramSubcommandPublic::BurnToken`: holder account must be public." + ); + }; + let tx_hash = Token(wallet_core) + .send_burn_transaction( + definition_account_id, + holder_account_id.into_public_identity(holder_id), + amount, + ) .await?; + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } Self::MintToken { @@ -704,9 +737,26 @@ impl WalletSubcommand for TokenProgramSubcommandPublic { holder_account_id, amount, } => { - Token(wallet_core) - .send_mint_transaction(definition_account_id, holder_account_id, amount) + let definition = definition_account_id.resolve(wallet_core.storage())?; + let holder = holder_account_id.resolve(wallet_core.storage())?; + let (AccountIdWithPrivacy::Public(def_id), AccountIdWithPrivacy::Public(holder_id)) = + (definition, holder) + else { + anyhow::bail!( + "`TokenProgramSubcommandPublic::MintToken`: holder account must be public." + ); + }; + let tx_hash = Token(wallet_core) + .send_mint_transaction( + definition_account_id.into_public_identity(def_id), + holder_account_id.into_public_identity(holder_id), + amount, + ) .await?; + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } } @@ -1021,7 +1071,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { ) -> Result { match self { Self::TransferTokenShieldedForeign { - sender_account_id, + sender, recipient_npk, recipient_vpk, recipient_identifier, @@ -1041,7 +1091,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { let (tx_hash, _) = Token(wallet_core) .send_transfer_transaction_shielded_foreign_account( - sender_account_id, + sender.expect("sender set during Send dispatch"), recipient_npk, recipient_vpk, recipient_identifier.unwrap_or_else(rand::random), @@ -1062,13 +1112,13 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } Self::TransferTokenShieldedOwned { - sender_account_id, + sender, recipient_account_id, balance_to_move, } => { let (tx_hash, secret_recipient) = Token(wallet_core) .send_transfer_transaction_shielded_owned_account( - sender_account_id, + sender.expect("sender set during Send dispatch"), recipient_account_id, balance_to_move, ) @@ -1307,14 +1357,25 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand { name, total_supply, } => { - Token(wallet_core) + let definition = definition_account_id.resolve(wallet_core.storage())?; + let supply = supply_account_id.resolve(wallet_core.storage())?; + let (AccountIdWithPrivacy::Public(def_id), AccountIdWithPrivacy::Public(sup_id)) = + (definition, supply) + else { + anyhow::bail!("`NewPublicDefPublicSupp`: unexpected private account received."); + }; + let tx_hash = Token(wallet_core) .send_new_definition( - definition_account_id, - supply_account_id, + definition_account_id.into_public_identity(def_id), + supply_account_id.into_public_identity(sup_id), name, total_supply, ) .await?; + println!("Transaction hash is {tx_hash}"); + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + println!("Transaction data is {transfer_tx:?}"); + wallet_core.store_persistent_data()?; Ok(SubcommandReturnValue::Empty) } } diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 85c43806..6c89f3dc 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -1,5 +1,6 @@ #![expect( clippy::print_stdout, + clippy::print_stderr, reason = "This is a CLI application, printing to stdout and stderr is expected and convenient" )] #![expect( @@ -76,6 +77,8 @@ pub enum ExecutionFailureKind { AccountDataError(AccountId), #[error("Failed to build transaction: {0}")] TransactionBuildError(#[from] nssa::error::NssaError), + #[error("Failed to sign transaction: {0}")] + SignError(anyhow::Error), #[error(transparent)] KeycardError(#[from] pyo3::PyErr), } @@ -561,6 +564,7 @@ impl WalletCore { let acc_manager = account_manager::AccountManager::new(self, accounts).await?; let pre_states = acc_manager.pre_states(); + tx_pre_check( &pre_states .iter() @@ -574,27 +578,30 @@ impl WalletCore { instruction_data, acc_manager.account_identities(), &program.to_owned(), - ) - .unwrap(); + )?; let message = nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( acc_manager.public_account_ids(), - Vec::from_iter(acc_manager.public_account_nonces()), + acc_manager.public_account_nonces(), private_account_keys .iter() .map(|keys| (keys.npk, keys.vpk.clone(), keys.epk.clone())) .collect(), output, - ) - .unwrap(); + )?; + + let message_hash = message.hash(); + let signatures_public_keys = acc_manager + .sign_message(message_hash) + .map_err(ExecutionFailureKind::SignError)?; let witness_set = - nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( - &message, + nssa::privacy_preserving_transaction::witness_set::WitnessSet::from_raw_parts( + signatures_public_keys, proof, - &acc_manager.public_account_auth(), ); + let tx = PrivacyPreservingTransaction::new(message, witness_set); let shared_secrets: Vec<_> = private_account_keys @@ -649,7 +656,6 @@ impl WalletCore { let account_ids = acc_manager.public_account_ids(); let program_id = program.program.id(); let nonces = acc_manager.public_account_nonces(); - let private_keys = acc_manager.public_account_auth(); let message = nssa::public_transaction::Message::new_preserialized( program_id, @@ -658,8 +664,13 @@ impl WalletCore { instruction_data, ); + let message_hash = message.hash(); + let signatures_public_keys = acc_manager + .sign_message(message_hash) + .map_err(ExecutionFailureKind::SignError)?; + let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); + nssa::public_transaction::WitnessSet::from_raw_parts(signatures_public_keys); let tx = nssa::public_transaction::PublicTransaction::new(message, witness_set); diff --git a/wallet/src/program_facades/amm.rs b/wallet/src/program_facades/amm.rs index b84cb5d0..1676ee18 100644 --- a/wallet/src/program_facades/amm.rs +++ b/wallet/src/program_facades/amm.rs @@ -9,30 +9,37 @@ pub struct Amm<'wallet>(pub &'wallet WalletCore); impl Amm<'_> { pub async fn send_new_definition( &self, - user_holding_a: AccountId, - user_holding_b: AccountId, - user_holding_lp: AccountId, + user_holding_a: AccountIdentity, + user_holding_b: AccountIdentity, + user_holding_lp: AccountIdentity, balance_a: u128, balance_b: u128, ) -> Result { + let a_id = user_holding_a + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let b_id = user_holding_b + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let program = Program::amm(); let amm_program_id = Program::amm().id(); let user_a_acc = self .0 - .get_account_public(user_holding_a) + .get_account_public(a_id) .await .map_err(ExecutionFailureKind::SequencerError)?; let user_b_acc = self .0 - .get_account_public(user_holding_b) + .get_account_public(b_id) .await .map_err(ExecutionFailureKind::SequencerError)?; let definition_token_a_id = TokenHolding::try_from(&user_a_acc.data) - .map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_a))? + .map_err(|_err| ExecutionFailureKind::AccountDataError(a_id))? .definition_id(); let definition_token_b_id = TokenHolding::try_from(&user_b_acc.data) - .map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_b))? + .map_err(|_err| ExecutionFailureKind::AccountDataError(b_id))? .definition_id(); let amm_pool = @@ -55,9 +62,9 @@ impl Amm<'_> { AccountIdentity::PublicNoSign(vault_holding_a), AccountIdentity::PublicNoSign(vault_holding_b), AccountIdentity::PublicNoSign(pool_lp), - AccountIdentity::Public(user_holding_a), - AccountIdentity::Public(user_holding_b), - AccountIdentity::Public(user_holding_lp), + user_holding_a, + user_holding_b, + user_holding_lp, ], instruction_data, &program.into(), @@ -67,30 +74,37 @@ impl Amm<'_> { pub async fn send_swap_exact_input( &self, - user_holding_a: AccountId, - user_holding_b: AccountId, + user_holding_a: AccountIdentity, + user_holding_b: AccountIdentity, swap_amount_in: u128, min_amount_out: u128, token_definition_id_in: AccountId, ) -> Result { + let a_id = user_holding_a + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let b_id = user_holding_b + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let program = Program::amm(); let amm_program_id = Program::amm().id(); let user_a_acc = self .0 - .get_account_public(user_holding_a) + .get_account_public(a_id) .await .map_err(ExecutionFailureKind::SequencerError)?; let user_b_acc = self .0 - .get_account_public(user_holding_b) + .get_account_public(b_id) .await .map_err(ExecutionFailureKind::SequencerError)?; let definition_token_a_id = TokenHolding::try_from(&user_a_acc.data) - .map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_a))? + .map_err(|_err| ExecutionFailureKind::AccountDataError(a_id))? .definition_id(); let definition_token_b_id = TokenHolding::try_from(&user_b_acc.data) - .map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_b))? + .map_err(|_err| ExecutionFailureKind::AccountDataError(b_id))? .definition_id(); let amm_pool = @@ -114,15 +128,15 @@ impl Amm<'_> { } let user_a_signing_identity = if token_definition_id_in == definition_token_a_id { - AccountIdentity::Public(user_holding_a) + user_holding_a } else { - AccountIdentity::PublicNoSign(user_holding_a) + AccountIdentity::PublicNoSign(a_id) }; let user_b_signing_identity = if token_definition_id_in == definition_token_b_id { - AccountIdentity::Public(user_holding_b) + user_holding_b } else { - AccountIdentity::PublicNoSign(user_holding_b) + AccountIdentity::PublicNoSign(b_id) }; self.0 @@ -142,30 +156,37 @@ impl Amm<'_> { pub async fn send_swap_exact_output( &self, - user_holding_a: AccountId, - user_holding_b: AccountId, + user_holding_a: AccountIdentity, + user_holding_b: AccountIdentity, exact_amount_out: u128, max_amount_in: u128, token_definition_id_in: AccountId, ) -> Result { + let a_id = user_holding_a + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let b_id = user_holding_b + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let program = Program::amm(); let amm_program_id = Program::amm().id(); let user_a_acc = self .0 - .get_account_public(user_holding_a) + .get_account_public(a_id) .await .map_err(ExecutionFailureKind::SequencerError)?; let user_b_acc = self .0 - .get_account_public(user_holding_b) + .get_account_public(b_id) .await .map_err(ExecutionFailureKind::SequencerError)?; let definition_token_a_id = TokenHolding::try_from(&user_a_acc.data) - .map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_a))? + .map_err(|_err| ExecutionFailureKind::AccountDataError(a_id))? .definition_id(); let definition_token_b_id = TokenHolding::try_from(&user_b_acc.data) - .map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_b))? + .map_err(|_err| ExecutionFailureKind::AccountDataError(b_id))? .definition_id(); let amm_pool = @@ -189,15 +210,15 @@ impl Amm<'_> { } let user_a_signing_identity = if token_definition_id_in == definition_token_a_id { - AccountIdentity::Public(user_holding_a) + user_holding_a } else { - AccountIdentity::PublicNoSign(user_holding_a) + AccountIdentity::PublicNoSign(a_id) }; let user_b_signing_identity = if token_definition_id_in == definition_token_b_id { - AccountIdentity::Public(user_holding_b) + user_holding_b } else { - AccountIdentity::PublicNoSign(user_holding_b) + AccountIdentity::PublicNoSign(b_id) }; self.0 @@ -217,31 +238,38 @@ impl Amm<'_> { pub async fn send_add_liquidity( &self, - user_holding_a: AccountId, - user_holding_b: AccountId, - user_holding_lp: AccountId, + user_holding_a: AccountIdentity, + user_holding_b: AccountIdentity, + user_holding_lp: AccountIdentity, min_amount_liquidity: u128, max_amount_to_add_token_a: u128, max_amount_to_add_token_b: u128, ) -> Result { + let a_id = user_holding_a + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let b_id = user_holding_b + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let program = Program::amm(); let amm_program_id = Program::amm().id(); let user_a_acc = self .0 - .get_account_public(user_holding_a) + .get_account_public(a_id) .await .map_err(ExecutionFailureKind::SequencerError)?; let user_b_acc = self .0 - .get_account_public(user_holding_b) + .get_account_public(b_id) .await .map_err(ExecutionFailureKind::SequencerError)?; let definition_token_a_id = TokenHolding::try_from(&user_a_acc.data) - .map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_a))? + .map_err(|_err| ExecutionFailureKind::AccountDataError(a_id))? .definition_id(); let definition_token_b_id = TokenHolding::try_from(&user_b_acc.data) - .map_err(|_err| ExecutionFailureKind::AccountDataError(user_holding_b))? + .map_err(|_err| ExecutionFailureKind::AccountDataError(b_id))? .definition_id(); let amm_pool = @@ -264,9 +292,9 @@ impl Amm<'_> { AccountIdentity::PublicNoSign(vault_holding_a), AccountIdentity::PublicNoSign(vault_holding_b), AccountIdentity::PublicNoSign(pool_lp), - AccountIdentity::Public(user_holding_a), - AccountIdentity::Public(user_holding_b), - AccountIdentity::PublicNoSign(user_holding_lp), + user_holding_a, + user_holding_b, + user_holding_lp, ], instruction_data, &program.into(), @@ -278,7 +306,7 @@ impl Amm<'_> { &self, user_holding_a: AccountId, user_holding_b: AccountId, - user_holding_lp: AccountId, + user_holding_lp: AccountIdentity, remove_liquidity_amount: u128, min_amount_to_remove_token_a: u128, min_amount_to_remove_token_b: u128, @@ -325,7 +353,7 @@ impl Amm<'_> { AccountIdentity::PublicNoSign(pool_lp), AccountIdentity::PublicNoSign(user_holding_a), AccountIdentity::PublicNoSign(user_holding_b), - AccountIdentity::Public(user_holding_lp), + user_holding_lp, ], instruction_data, &program.into(), diff --git a/wallet/src/program_facades/ata.rs b/wallet/src/program_facades/ata.rs index 68d45bbe..6aba469c 100644 --- a/wallet/src/program_facades/ata.rs +++ b/wallet/src/program_facades/ata.rs @@ -14,9 +14,13 @@ pub struct Ata<'wallet>(pub &'wallet WalletCore); impl Ata<'_> { pub async fn send_create( &self, - owner_id: AccountId, + owner: AccountIdentity, definition_id: AccountId, ) -> Result { + let owner_id = owner + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let program = Program::ata(); let ata_program_id = program.id(); let ata_id = get_associated_token_account_id( @@ -30,7 +34,7 @@ impl Ata<'_> { self.0 .send_pub_tx( vec![ - AccountIdentity::Public(owner_id), + owner, AccountIdentity::PublicNoSign(definition_id), AccountIdentity::PublicNoSign(ata_id), ], @@ -42,11 +46,15 @@ impl Ata<'_> { pub async fn send_transfer( &self, - owner_id: AccountId, + owner: AccountIdentity, definition_id: AccountId, recipient_id: AccountId, amount: u128, ) -> Result { + let owner_id = owner + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let program = Program::ata(); let ata_program_id = program.id(); let sender_ata_id = get_associated_token_account_id( @@ -63,7 +71,7 @@ impl Ata<'_> { self.0 .send_pub_tx( vec![ - AccountIdentity::Public(owner_id), + owner, AccountIdentity::PublicNoSign(sender_ata_id), AccountIdentity::PublicNoSign(recipient_id), ], @@ -75,10 +83,14 @@ impl Ata<'_> { pub async fn send_burn( &self, - owner_id: AccountId, + owner: AccountIdentity, definition_id: AccountId, amount: u128, ) -> Result { + let owner_id = owner + .public_account_id() + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let program = Program::ata(); let ata_program_id = program.id(); let holder_ata_id = get_associated_token_account_id( @@ -95,7 +107,7 @@ impl Ata<'_> { self.0 .send_pub_tx( vec![ - AccountIdentity::Public(owner_id), + owner, AccountIdentity::PublicNoSign(holder_ata_id), AccountIdentity::PublicNoSign(definition_id), ], diff --git a/wallet/src/program_facades/native_token_transfer/public.rs b/wallet/src/program_facades/native_token_transfer/public.rs index b6c50c31..66930fef 100644 --- a/wallet/src/program_facades/native_token_transfer/public.rs +++ b/wallet/src/program_facades/native_token_transfer/public.rs @@ -1,131 +1,41 @@ use authenticated_transfer_core::Instruction as AuthTransferInstruction; -use common::{HashType, transaction::NSSATransaction}; -use nssa::{ - AccountId, PublicTransaction, - program::Program, - public_transaction::{Message, WitnessSet}, -}; -use pyo3::exceptions::PyRuntimeError; -use sequencer_service_rpc::RpcClient as _; +use common::HashType; +use nssa::program::Program; use super::NativeTokenTransfer; use crate::{ - ExecutionFailureKind, cli::CliAccountMention, helperfunctions::read_pin, signing::SigningGroups, + AccountIdentity, ExecutionFailureKind, + program_facades::native_token_transfer::auth_transfer_preparation, }; impl NativeTokenTransfer<'_> { pub async fn send_public_transfer( &self, - from: AccountId, - to: AccountId, + from: AccountIdentity, + to: AccountIdentity, balance_to_move: u128, - from_mention: &CliAccountMention, - to_mention: &CliAccountMention, ) -> Result { - let mut groups = SigningGroups::new(); - groups - .add_sender(from_mention, from, self.0) - .and_then(|()| groups.add_recipient(to_mention, to, self.0)) - .map_err(|e| { - ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( - e.to_string(), - )) - })?; + let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); - let program_id = Program::authenticated_transfer_program().id(); - let nonces = self - .0 - .get_accounts_nonces(groups.signing_ids()) + self.0 + .send_pub_tx_with_pre_check( + vec![from, to], + instruction_data, + &program.into(), + tx_pre_check, + ) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let message = Message::try_new( - program_id, - vec![from, to], - nonces, - AuthTransferInstruction::Transfer { - amount: balance_to_move, - }, - ) - .map_err(ExecutionFailureKind::TransactionBuildError)?; - - let pin = if groups.needs_pin() { - read_pin() - .map_err(|e| { - ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( - e.to_string(), - )) - })? - .as_str() - .to_owned() - } else { - String::new() - }; - - let sigs = groups.sign_all(&message.hash(), &pin).map_err(|e| { - ExecutionFailureKind::KeycardError(pyo3::PyErr::new::(e.to_string())) - })?; - - let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs)); - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn register_account( &self, - from: AccountId, - account_mention: &CliAccountMention, + account: AccountIdentity, ) -> Result { - let nonces = self - .0 - .get_accounts_nonces(vec![from]) + let program = Program::authenticated_transfer_program(); + let instruction_data = Program::serialize_instruction(AuthTransferInstruction::Initialize)?; + + self.0 + .send_pub_tx(vec![account], instruction_data, &program.into()) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let account_ids = vec![from]; - let program_id = Program::authenticated_transfer_program().id(); - let message = Message::try_new( - program_id, - account_ids, - nonces, - AuthTransferInstruction::Initialize, - ) - .map_err(ExecutionFailureKind::TransactionBuildError)?; - - let mut groups = SigningGroups::new(); - groups - .add_sender(account_mention, from, self.0) - .map_err(|e| { - ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( - e.to_string(), - )) - })?; - - let pin = if groups.needs_pin() { - read_pin() - .map_err(|e| { - ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( - e.to_string(), - )) - })? - .as_str() - .to_owned() - } else { - String::new() - }; - - let sigs = groups.sign_all(&message.hash(), &pin).map_err(|e| { - ExecutionFailureKind::KeycardError(pyo3::PyErr::new::(e.to_string())) - })?; - - let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs)); - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } } diff --git a/wallet/src/program_facades/native_token_transfer/shielded.rs b/wallet/src/program_facades/native_token_transfer/shielded.rs index 44916529..e745346a 100644 --- a/wallet/src/program_facades/native_token_transfer/shielded.rs +++ b/wallet/src/program_facades/native_token_transfer/shielded.rs @@ -8,16 +8,15 @@ use crate::{AccountIdentity, ExecutionFailureKind}; impl NativeTokenTransfer<'_> { pub async fn send_shielded_transfer( &self, - from: AccountId, + from: AccountIdentity, to: AccountId, balance_to_move: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); - self.0 .send_privacy_preserving_tx_with_pre_check( vec![ - AccountIdentity::Public(from), + from, self.0 .resolve_private_account(to) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -38,18 +37,17 @@ impl NativeTokenTransfer<'_> { pub async fn send_shielded_transfer_to_outer_account( &self, - from: AccountId, + from: AccountIdentity, to_npk: NullifierPublicKey, to_vpk: ViewingPublicKey, to_identifier: Identifier, balance_to_move: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); - self.0 .send_privacy_preserving_tx_with_pre_check( vec![ - AccountIdentity::Public(from), + from, AccountIdentity::PrivateForeign { npk: to_npk, vpk: to_vpk, diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs index 8e9b4f8f..4d026a66 100644 --- a/wallet/src/program_facades/token.rs +++ b/wallet/src/program_facades/token.rs @@ -10,8 +10,8 @@ pub struct Token<'wallet>(pub &'wallet WalletCore); impl Token<'_> { pub async fn send_new_definition( &self, - definition_account_id: AccountId, - supply_account_id: AccountId, + definition: AccountIdentity, + supply: AccountIdentity, name: String, total_supply: u128, ) -> Result { @@ -21,14 +21,7 @@ impl Token<'_> { Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 - .send_pub_tx( - vec![ - AccountIdentity::Public(definition_account_id), - AccountIdentity::Public(supply_account_id), - ], - instruction_data, - &program.into(), - ) + .send_pub_tx(vec![definition, supply], instruction_data, &program.into()) .await } @@ -131,8 +124,8 @@ impl Token<'_> { pub async fn send_transfer_transaction( &self, - sender_account_id: AccountId, - recipient_account_id: AccountId, + sender: AccountIdentity, + recipient: AccountIdentity, amount: u128, ) -> Result { let program = Program::token(); @@ -143,14 +136,7 @@ impl Token<'_> { Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 - .send_pub_tx( - vec![ - AccountIdentity::Public(sender_account_id), - AccountIdentity::Public(recipient_account_id), - ], - instruction_data, - &program.into(), - ) + .send_pub_tx(vec![sender, recipient], instruction_data, &program.into()) .await } @@ -261,7 +247,7 @@ impl Token<'_> { pub async fn send_transfer_transaction_shielded_owned_account( &self, - sender_account_id: AccountId, + sender: AccountIdentity, recipient_account_id: AccountId, amount: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { @@ -270,11 +256,10 @@ impl Token<'_> { }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); - self.0 .send_privacy_preserving_tx( vec![ - AccountIdentity::Public(sender_account_id), + sender, self.0 .resolve_private_account(recipient_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?, @@ -294,7 +279,7 @@ impl Token<'_> { pub async fn send_transfer_transaction_shielded_foreign_account( &self, - sender_account_id: AccountId, + sender: AccountIdentity, recipient_npk: NullifierPublicKey, recipient_vpk: ViewingPublicKey, recipient_identifier: Identifier, @@ -305,11 +290,10 @@ impl Token<'_> { }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); - self.0 .send_privacy_preserving_tx( vec![ - AccountIdentity::Public(sender_account_id), + sender, AccountIdentity::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, @@ -332,7 +316,7 @@ impl Token<'_> { pub async fn send_burn_transaction( &self, definition_account_id: AccountId, - holder_account_id: AccountId, + holder: AccountIdentity, amount: u128, ) -> Result { let program = Program::token(); @@ -344,10 +328,7 @@ impl Token<'_> { self.0 .send_pub_tx( - vec![ - AccountIdentity::PublicNoSign(definition_account_id), - AccountIdentity::Public(holder_account_id), - ], + vec![AccountIdentity::PublicNoSign(definition_account_id), holder], instruction_data, &program.into(), ) @@ -456,8 +437,8 @@ impl Token<'_> { pub async fn send_mint_transaction( &self, - definition_account_id: AccountId, - holder_account_id: AccountId, + definition: AccountIdentity, + holder: AccountIdentity, amount: u128, ) -> Result { let program = Program::token(); @@ -468,14 +449,7 @@ impl Token<'_> { Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 - .send_pub_tx( - vec![ - AccountIdentity::Public(definition_account_id), - AccountIdentity::Public(holder_account_id), - ], - instruction_data, - &program.into(), - ) + .send_pub_tx(vec![definition, holder], instruction_data, &program.into()) .await } diff --git a/wallet/src/signing.rs b/wallet/src/signing.rs index 7fb877ab..505dedd9 100644 --- a/wallet/src/signing.rs +++ b/wallet/src/signing.rs @@ -1,114 +1,33 @@ -use anyhow::Result; use keycard_wallet::{KeycardWallet, python_path}; -use nssa::{AccountId, PrivateKey, PublicKey, Signature}; +use pyo3::Python; -use crate::{WalletCore, cli::CliAccountMention}; - -/// Groups transaction signers by type to minimise Python GIL acquisition. -/// -/// Local signers are signed in pure Rust; all keycard signers share a single Python session -/// with one `connect` / `close_session` pair. -#[derive(Default)] -pub struct SigningGroups { - local: Vec<(AccountId, PrivateKey)>, - keycard: Vec<(AccountId, String)>, +/// Lazily opens and reuses a single Keycard session for all keycard signers in one transaction. +pub struct KeycardSessionContext { + pin: String, + wallet: Option, } -impl SigningGroups { - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a sender. Keycard paths are queued for the hardware session; local accounts - /// have their signing key resolved eagerly. Errors if no key is found. - pub fn add_sender( - &mut self, - mention: &CliAccountMention, - account_id: AccountId, - wallet_core: &WalletCore, - ) -> Result<()> { - if let CliAccountMention::KeyPath(path) = mention { - self.keycard.push((account_id, path.clone())); - return Ok(()); +impl KeycardSessionContext { + pub fn new(pin: impl Into) -> Self { + Self { + pin: pin.into(), + wallet: None, } - let key = wallet_core - .storage() - .key_chain() - .pub_account_signing_key(account_id) - .ok_or_else(|| anyhow::anyhow!("signing key not found for account {account_id}"))? - .clone(); - self.local.push((account_id, key)); - Ok(()) } - /// Add a recipient. Same as [`add_sender`] but silently skips accounts with no local - /// key and no keycard path — they are foreign and require neither a signature nor a nonce. - pub fn add_recipient( - &mut self, - mention: &CliAccountMention, - account_id: AccountId, - wallet_core: &WalletCore, - ) -> Result<()> { - if let CliAccountMention::KeyPath(path) = mention { - self.keycard.push((account_id, path.clone())); - return Ok(()); + pub fn get_or_connect(&mut self, py: Python<'_>) -> pyo3::PyResult<&KeycardWallet> { + if self.wallet.is_none() { + python_path::add_python_path(py)?; + let wallet = KeycardWallet::new(py)?; + wallet.connect(py, &self.pin)?; + self.wallet = Some(wallet); } - if let Some(key) = wallet_core - .storage() - .key_chain() - .pub_account_signing_key(account_id) - { - self.local.push((account_id, key.clone())); + Ok(self.wallet.as_ref().expect("wallet was just inserted")) + } + + pub fn close(self, py: Python<'_>) { + if let Some(w) = self.wallet { + let _res = w.close_session(py); } - Ok(()) - } - - /// Returns `true` when a PIN is required (at least one keycard signer is present). - #[must_use] - pub const fn needs_pin(&self) -> bool { - !self.keycard.is_empty() - } - - /// Account IDs that require a nonce (every non-foreign signer). - #[must_use] - pub fn signing_ids(&self) -> Vec { - self.local - .iter() - .map(|(id, _)| *id) - .chain(self.keycard.iter().map(|(id, _)| *id)) - .collect() - } - - /// Sign `hash` for every account in the group. - /// - /// Local accounts are signed in pure Rust. Keycard accounts share one Python session. - pub fn sign_all(&self, hash: &[u8; 32], pin: &str) -> Result> { - let mut sigs: Vec<(Signature, PublicKey)> = self - .local - .iter() - .map(|(_, key)| { - ( - Signature::new(key, hash), - PublicKey::new_from_private_key(key), - ) - }) - .collect(); - - if !self.keycard.is_empty() { - pyo3::Python::with_gil(|py| -> pyo3::PyResult<()> { - python_path::add_python_path(py)?; - let wallet = KeycardWallet::new(py)?; - wallet.connect(py, pin)?; - for (_, path) in &self.keycard { - sigs.push(wallet.sign_message_for_path(py, path, hash)?); - } - drop(wallet.close_session(py)); - Ok(()) - }) - .map_err(anyhow::Error::from)?; - } - - Ok(sigs) } }