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 3d7d2921..9b6b1597 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.6.3", "tracing", - "windows-sys 0.59.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/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/examples/program_deployment/src/bin/run_hello_world_private.rs b/examples/program_deployment/src/bin/run_hello_world_private.rs index 27ac2079..11a6c0f6 100644 --- a/examples/program_deployment/src/bin/run_hello_world_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_private.rs @@ -52,6 +52,7 @@ async fn main() { accounts, Program::serialize_instruction(greeting).unwrap(), &program.into(), + None, ) .await .unwrap(); diff --git a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs index 4fac3eec..f01c83e7 100644 --- a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs @@ -60,6 +60,7 @@ async fn main() { accounts, Program::serialize_instruction(instruction).unwrap(), &program_with_dependencies, + None, ) .await .unwrap(); diff --git a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs index a1c2517e..7e5fa5d8 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs @@ -106,6 +106,7 @@ async fn main() { accounts, Program::serialize_instruction(instruction).unwrap(), &program.into(), + None, ) .await .unwrap(); @@ -147,6 +148,7 @@ async fn main() { accounts, Program::serialize_instruction(instruction).unwrap(), &program.into(), + None, ) .await .unwrap(); diff --git a/integration_tests/tests/private_pda.rs b/integration_tests/tests/private_pda.rs index f9969d98..d1e79ab3 100644 --- a/integration_tests/tests/private_pda.rs +++ b/integration_tests/tests/private_pda.rs @@ -57,6 +57,7 @@ async fn fund_private_pda( Program::serialize_instruction((seed, amount, auth_transfer_id, true)) .context("failed to serialize pda_fund_spend_proxy fund instruction")?, proxy_program, + None, ) .await .map_err(|e| anyhow::anyhow!("{e}"))?; @@ -93,6 +94,7 @@ async fn spend_private_pda( Program::serialize_instruction((seed, amount, auth_transfer_id, false)) .context("failed to serialize pda_fund_spend_proxy instruction")?, spend_program, + None, ) .await .map_err(|e| anyhow::anyhow!("{e}"))?; 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..a2342d9c 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" \ No newline at end of file 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/test_fixtures/src/setup.rs b/test_fixtures/src/setup.rs index c43590d0..54bdc493 100644 --- a/test_fixtures/src/setup.rs +++ b/test_fixtures/src/setup.rs @@ -298,6 +298,7 @@ async fn claim_funds_from_vault_to_private( ], instruction_data, &program_with_dependencies, + None, ) .await .context("Failed to submit private vault claim transaction")?; diff --git a/wallet-ffi/src/transfer.rs b/wallet-ffi/src/transfer.rs index 8f3c47d7..3b36971b 100644 --- a/wallet-ffi/src/transfer.rs +++ b/wallet-ffi/src/transfer.rs @@ -1,6 +1,9 @@ //! Token transfer functions. -use std::{ffi::CString, ptr}; +use std::{ + ffi::{c_char, CStr, CString}, + ptr, +}; use nssa::AccountId; use wallet::{ @@ -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. @@ -140,6 +151,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,6 +188,10 @@ 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); @@ -185,6 +201,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded( to_vpk, to_identifier, amount, + &from_mention, )) { Ok((tx_hash, _shared_key)) => { let tx_hash = CString::new(tx_hash.to_string()) @@ -262,7 +279,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 +373,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 +438,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 +462,14 @@ 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_id, to_id, amount, &from_mention)) { 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 +549,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)) { @@ -673,7 +692,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..a09010ee 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,9 @@ optfield = "0.4.0" url.workspace = true derive_more = { workspace = true, features = ["display"] } +[features] +keycard-debug = [] + [dev-dependencies] tempfile.workspace = true key_protocol = { workspace = true, features = ["test_utils"] } 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..30443de9 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -138,13 +138,23 @@ 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, + } + } } impl FromStr for CliAccountMention { diff --git a/wallet/src/cli/programs/amm.rs b/wallet/src/cli/programs/amm.rs index 6173eaf3..f2d546c6 100644 --- a/wallet/src/cli/programs/amm.rs +++ b/wallet/src/cli/programs/amm.rs @@ -131,25 +131,31 @@ 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, + a, + b, + lp, balance_a, balance_b, + &user_holding_a, + &user_holding_b, + &user_holding_lp, ) .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 +171,25 @@ 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, + a, + b, amount_in, min_amount_out, token_definition, + &user_holding_a, + &user_holding_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) } _ => { @@ -197,23 +205,25 @@ 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, + a, + b, exact_amount_out, max_amount_in, token_definition, + &user_holding_a, + &user_holding_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) } _ => { @@ -230,26 +240,32 @@ 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, + a, + b, + lp, min_amount_lp, max_amount_a, max_amount_b, + &user_holding_a, + &user_holding_b, + &user_holding_lp, ) .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 +282,30 @@ 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, + lp, balance_lp, min_amount_a, min_amount_b, + &user_holding_lp, ) .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..c2b96a85 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_id, definition_id, &owner) .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,19 @@ 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_id, definition_id, to_id, amount, &from) .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 +171,18 @@ 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_id, definition_id, amount, &holder) .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..724bcca4 100644 --- a/wallet/src/cli/programs/native_token_transfer.rs +++ b/wallet/src/cli/programs/native_token_transfer.rs @@ -151,6 +151,7 @@ impl WalletSubcommand for AuthTransferSubcommand { from, to, amount, + from_mention: from_account, }, ) } @@ -175,6 +176,7 @@ impl WalletSubcommand for AuthTransferSubcommand { to_vpk, to_identifier, amount, + from_mention: from_account, }, ) } @@ -247,6 +249,8 @@ pub enum NativeTokenTransferProgramSubcommandShielded { /// amount - amount of balance to move. #[arg(long)] amount: u128, + #[arg(skip)] + from_mention: CliAccountMention, }, /// Send native token transfer from `from` to `to` for `amount`. /// @@ -267,6 +271,8 @@ pub enum NativeTokenTransferProgramSubcommandShielded { /// amount - amount of balance to move. #[arg(long)] amount: u128, + #[arg(skip)] + from_mention: CliAccountMention, }, } @@ -393,9 +399,14 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { wallet_core: &mut WalletCore, ) -> Result { match self { - Self::ShieldedOwned { from, to, amount } => { + Self::ShieldedOwned { + from, + to, + amount, + from_mention, + } => { let (tx_hash, secret) = NativeTokenTransfer(wallet_core) - .send_shielded_transfer(from, to, amount) + .send_shielded_transfer(from, to, amount, &from_mention) .await?; println!("Transaction hash is {tx_hash}"); @@ -421,6 +432,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { to_vpk, to_identifier, amount, + from_mention, } => { let to_npk_res = hex::decode(to_npk)?; let mut to_npk = [0; 32]; @@ -440,6 +452,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { to_vpk, to_identifier.unwrap_or_else(rand::random), amount, + &from_mention, ) .await?; diff --git a/wallet/src/cli/programs/token.rs b/wallet/src/cli/programs/token.rs index 61e24e68..6137812f 100644 --- a/wallet/src/cli/programs/token.rs +++ b/wallet/src/cli/programs/token.rs @@ -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, }, ) @@ -226,6 +229,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { sender_account_id: from, recipient_account_id: to, balance_to_move: amount, + sender_mention: from_mention, }, ) } @@ -247,6 +251,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { recipient_vpk: to_vpk, recipient_identifier: to_identifier, balance_to_move: amount, + sender_mention: from_mention, }, ), }, @@ -259,17 +264,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 +317,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 +338,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 +436,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 +447,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, }, @@ -561,6 +567,8 @@ pub enum TokenProgramSubcommandShielded { recipient_account_id: AccountId, #[arg(short, long)] balance_to_move: u128, + #[arg(skip)] + sender_mention: CliAccountMention, }, // Transfer tokens using the token program TransferTokenShieldedForeign { @@ -577,6 +585,8 @@ pub enum TokenProgramSubcommandShielded { recipient_identifier: Option, #[arg(short, long)] balance_to_move: u128, + #[arg(skip)] + sender_mention: CliAccountMention, }, // Burn tokens using the token program BurnTokenShielded { @@ -620,9 +630,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 +690,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!("Only public accounts supported for token transfer"); + }; + let tx_hash = Token(wallet_core) .send_transfer_transaction( - sender_account_id, - recipient_account_id, + sender_id, + recipient_id, balance_to_move, + &sender_account_id, + &recipient_account_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) } Self::BurnToken { @@ -694,9 +719,22 @@ 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!("Only public holder account supported for token burn"); + }; + let tx_hash = Token(wallet_core) + .send_burn_transaction( + definition_account_id, + holder_id, + amount, + &holder_account_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) } Self::MintToken { @@ -704,9 +742,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!("Only public accounts supported for token mint"); + }; + let tx_hash = Token(wallet_core) + .send_mint_transaction( + def_id, + holder_id, + amount, + &definition_account_id, + &holder_account_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) } } @@ -1026,6 +1081,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { recipient_vpk, recipient_identifier, balance_to_move, + sender_mention, } => { let recipient_npk_res = hex::decode(recipient_npk)?; let mut recipient_npk = [0; 32]; @@ -1046,6 +1102,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { recipient_vpk, recipient_identifier.unwrap_or_else(rand::random), balance_to_move, + &sender_mention, ) .await?; @@ -1065,12 +1122,14 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { sender_account_id, recipient_account_id, balance_to_move, + sender_mention, } => { let (tx_hash, secret_recipient) = Token(wallet_core) .send_transfer_transaction_shielded_owned_account( sender_account_id, recipient_account_id, balance_to_move, + &sender_mention, ) .await?; @@ -1307,14 +1366,27 @@ 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!("Only public accounts supported for new token definition"); + }; + let tx_hash = Token(wallet_core) .send_new_definition( - definition_account_id, - supply_account_id, + def_id, + sup_id, name, total_supply, + &definition_account_id, + &supply_account_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) } } diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 7360b56a..429e0df7 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( @@ -14,15 +15,20 @@ use bip39::Mnemonic; use common::{HashType, transaction::NSSATransaction}; use config::WalletConfig; use key_protocol::key_management::key_tree::chain_index::ChainIndex; +use keycard_wallet::KeycardWallet; use log::info; use nssa::{ - Account, AccountId, PrivacyPreservingTransaction, + Account, AccountId, PrivacyPreservingTransaction, PublicKey, PublicTransaction, Signature, privacy_preserving_transaction::{ circuit::ProgramWithDependencies, message::EncryptedAccountData, }, + program::Program, + public_transaction::WitnessSet as PublicWitnessSet, }; use nssa_core::{ - Commitment, MembershipProof, SharedSecretKey, account::Nonce, program::InstructionData, + Commitment, MembershipProof, SharedSecretKey, + account::{AccountWithMetadata, Nonce}, + program::InstructionData, }; pub use privacy_preserving_tx::PrivacyPreservingAccount; use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder}; @@ -31,8 +37,10 @@ use tokio::io::AsyncWriteExt as _; use crate::{ account::{AccountIdWithPrivacy, Label}, + cli::CliAccountMention, config::WalletConfigOverrides, poller::TxPoller, + signing::SigningGroup, storage::key_chain::SharedAccountEntry, }; @@ -80,6 +88,20 @@ pub enum ExecutionFailureKind { KeycardError(#[from] pyo3::PyErr), } +impl ExecutionFailureKind { + /// Convert an [`anyhow::Error`] (e.g. from [`SigningGroup`]) into a keycard error. + #[must_use] + #[expect( + clippy::needless_pass_by_value, + reason = "used as a method reference in map_err" + )] + pub fn from_anyhow(e: anyhow::Error) -> Self { + Self::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + } +} + #[expect(clippy::partial_pub_fields, reason = "TODO: make all fields private")] pub struct WalletCore { config_path: PathBuf, @@ -542,15 +564,76 @@ impl WalletCore { Ok(()) } + /// Send a public transaction, fetching nonces automatically from + /// [`SigningGroup::signing_ids`]. + pub async fn send_public_tx( + &self, + program: &Program, + account_ids: Vec, + instruction: T, + groups: SigningGroup, + ) -> Result { + let nonces = self + .get_accounts_nonces(groups.signing_ids()) + .await + .map_err(ExecutionFailureKind::SequencerError)?; + self.send_public_tx_with_nonces(program, account_ids, nonces, instruction, groups) + .await + } + + /// Send a public transaction with caller-supplied nonces. + /// + /// Use this when the caller needs to assemble or augment nonces before submission + /// (e.g. injecting a keycard account nonce that was fetched separately). + pub async fn send_public_tx_with_nonces( + &self, + program: &Program, + account_ids: Vec, + nonces: Vec, + instruction: T, + groups: SigningGroup, + ) -> Result { + let message = nssa::public_transaction::Message::try_new( + program.id(), + account_ids, + nonces, + instruction, + )?; + + let pin = if groups.needs_pin() { + crate::helperfunctions::read_pin() + .map_err(ExecutionFailureKind::from_anyhow)? + .as_str() + .to_owned() + } else { + String::new() + }; + + let sigs = groups + .sign_all(&message.hash(), &pin) + .map_err(ExecutionFailureKind::from_anyhow)?; + + let tx = PublicTransaction::new(message, PublicWitnessSet::from_raw_parts(sigs)); + Ok(self + .sequencer_client + .send_transaction(NSSATransaction::Public(tx)) + .await?) + } + pub async fn send_privacy_preserving_tx( &self, accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, + mention: Option<&CliAccountMention>, ) -> Result<(HashType, Vec), ExecutionFailureKind> { - self.send_privacy_preserving_tx_with_pre_check(accounts, instruction_data, program, |_| { - Ok(()) - }) + self.send_privacy_preserving_tx_with_pre_check( + accounts, + instruction_data, + program, + |_| Ok(()), + mention, + ) .await } @@ -560,10 +643,67 @@ impl WalletCore { instruction_data: InstructionData, program: &ProgramWithDependencies, tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, + mention: Option<&CliAccountMention>, ) -> Result<(HashType, Vec), ExecutionFailureKind> { let acc_manager = privacy_preserving_tx::AccountManager::new(self, accounts).await?; - let pre_states = acc_manager.pre_states(); + let mut pre_states = acc_manager.pre_states(); + + let (keycard_account, keycard_pin, keycard_path) = if let Some(key_path_str) = + mention.and_then(CliAccountMention::key_path) + { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::< + pyo3::exceptions::PyRuntimeError, + _, + >(e.to_string())) + })?; + let account_id_str = + KeycardWallet::get_public_account_id_for_path_with_connect(&pin, key_path_str)?; + let account_id: AccountId = match account_id_str + .parse::() + .expect("`wallet::lib::send_privacy_preserving_tx_with_pre_check`: invalid account id parsed") + { + AccountIdWithPrivacy::Public(id) | AccountIdWithPrivacy::Private(id) => id, + }; + let account = self + .get_account_public(account_id) + .await + .expect("`wallet::lib::send_privacy_preserving_tx_with_pre_check`: unable to retrieve public account"); + let pin_str = pin.as_str().to_owned(); + ( + Some(AccountWithMetadata { + account, + is_authorized: true, + account_id, + }), + Some(pin_str), + Some(key_path_str.to_owned()), + ) + } else { + (None, None, None) + }; + + let mut nonces: Vec = acc_manager.public_account_nonces().into_iter().collect(); + + let mut account_ids: Vec = acc_manager.public_account_ids(); + + if let Some(acc) = keycard_account.as_ref() { + if acc_manager.public_account_ids().contains(&acc.account_id) { + if let Some(pre) = pre_states + .iter_mut() + .find(|p| p.account_id == acc.account_id) + { + pre.is_authorized = true; + } + nonces.push(acc.account.nonce); + } else { + nonces.push(acc.account.nonce); + account_ids.push(acc.account_id); + pre_states.push(acc.clone()); + } + } + tx_pre_check( &pre_states .iter() @@ -582,8 +722,8 @@ impl WalletCore { 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()), + account_ids, + nonces, private_account_keys .iter() .map(|keys| (keys.npk, keys.vpk.clone(), keys.epk.clone())) @@ -593,11 +733,38 @@ impl WalletCore { .unwrap(); let witness_set = - nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( - &message, - proof, - &acc_manager.public_account_auth(), - ); + if let (Some(pin), Some(path)) = (keycard_pin.as_deref(), keycard_path.as_deref()) { + let hash = message.hash(); + let local_auth = acc_manager.public_account_auth(); + let mut sigs: Vec<(Signature, PublicKey)> = local_auth + .iter() + .map(|&key| { + ( + Signature::new(key, &hash), + PublicKey::new_from_private_key(key), + ) + }) + .collect(); + let keycard_sig = pyo3::Python::with_gil(|py| { + let mut ctx = crate::signing::KeycardSessionContext::new(pin); + let result = ctx + .get_or_connect(py) + .and_then(|w| w.sign_message_for_path(py, path, &hash)); + ctx.close(py); + result + }) + .map_err(ExecutionFailureKind::KeycardError)?; + sigs.push(keycard_sig); + nssa::privacy_preserving_transaction::witness_set::WitnessSet::from_raw_parts( + sigs, proof, + ) + } else { + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + &message, + proof, + &acc_manager.public_account_auth(), + ) + }; let tx = PrivacyPreservingTransaction::new(message, witness_set); let shared_secrets: Vec<_> = private_account_keys diff --git a/wallet/src/program_facades/amm.rs b/wallet/src/program_facades/amm.rs index 2ac2ab79..0a3fef32 100644 --- a/wallet/src/program_facades/amm.rs +++ b/wallet/src/program_facades/amm.rs @@ -1,13 +1,13 @@ use amm_core::{compute_liquidity_token_pda, compute_pool_pda, compute_vault_pda}; -use common::{HashType, transaction::NSSATransaction}; +use common::HashType; use nssa::{AccountId, program::Program}; -use sequencer_service_rpc::RpcClient as _; use token_core::TokenHolding; -use crate::{ExecutionFailureKind, WalletCore}; +use crate::{ExecutionFailureKind, WalletCore, cli::CliAccountMention, signing::SigningGroup}; pub struct Amm<'wallet>(pub &'wallet WalletCore); impl Amm<'_> { + #[expect(clippy::too_many_arguments, reason = "each parameter is distinct")] pub async fn send_new_definition( &self, user_holding_a: AccountId, @@ -15,6 +15,9 @@ impl Amm<'_> { user_holding_lp: AccountId, balance_a: u128, balance_b: u128, + a_mention: &CliAccountMention, + b_mention: &CliAccountMention, + lp_mention: &CliAccountMention, ) -> Result { let program = Program::amm(); let amm_program_id = Program::amm().id(); @@ -58,69 +61,19 @@ impl Amm<'_> { user_holding_lp, ]; - let mut nonces = self - .0 - .get_accounts_nonces(vec![user_holding_a, user_holding_b]) + let mut groups = SigningGroup::new(); + groups + .add_required(a_mention, user_holding_a, self.0) + .and_then(|()| groups.add_required(b_mention, user_holding_b, self.0)) + .and_then(|()| groups.add_optional(lp_mention, user_holding_lp, self.0)) + .map_err(ExecutionFailureKind::from_anyhow)?; + + self.0 + .send_public_tx(&program, account_ids, instruction, groups) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let mut private_keys = Vec::new(); - - let signing_key_a = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_a) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(signing_key_a); - - let signing_key_b = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_b) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(signing_key_b); - - if let Some(signing_key_lp) = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_lp) - { - private_keys.push(signing_key_lp); - let lp_nonces = self - .0 - .get_accounts_nonces(vec![user_holding_lp]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - nonces.extend(lp_nonces); - } else { - println!( - "Liquidity pool tokens receiver's account ({user_holding_lp}) private key not found in wallet. Proceeding with only liquidity provider's keys." - ); - } - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } + #[expect(clippy::too_many_arguments, reason = "each parameter is distinct")] pub async fn send_swap_exact_input( &self, user_holding_a: AccountId, @@ -128,6 +81,8 @@ impl Amm<'_> { swap_amount_in: u128, min_amount_out: u128, token_definition_id_in: AccountId, + a_mention: &CliAccountMention, + b_mention: &CliAccountMention, ) -> Result { let instruction = amm_core::Instruction::SwapExactInput { swap_amount_in, @@ -168,49 +123,26 @@ impl Amm<'_> { user_holding_b, ]; - let account_id_auth = if definition_token_a_id == token_definition_id_in { - user_holding_a + let (account_id_auth, seller_mention) = if definition_token_a_id == token_definition_id_in { + (user_holding_a, a_mention) } else if definition_token_b_id == token_definition_id_in { - user_holding_b + (user_holding_b, b_mention) } else { return Err(ExecutionFailureKind::AccountDataError( token_definition_id_in, )); }; - let nonces = self - .0 - .get_accounts_nonces(vec![account_id_auth]) + let mut groups = SigningGroup::new(); + groups + .add_required(seller_mention, account_id_auth, self.0) + .map_err(ExecutionFailureKind::from_anyhow)?; + self.0 + .send_public_tx(&program, account_ids, instruction, groups) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let signing_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(account_id_auth) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } + #[expect(clippy::too_many_arguments, reason = "each parameter is distinct")] pub async fn send_swap_exact_output( &self, user_holding_a: AccountId, @@ -218,6 +150,8 @@ impl Amm<'_> { exact_amount_out: u128, max_amount_in: u128, token_definition_id_in: AccountId, + a_mention: &CliAccountMention, + b_mention: &CliAccountMention, ) -> Result { let instruction = amm_core::Instruction::SwapExactOutput { exact_amount_out, @@ -258,49 +192,26 @@ impl Amm<'_> { user_holding_b, ]; - let account_id_auth = if definition_token_a_id == token_definition_id_in { - user_holding_a + let (account_id_auth, seller_mention) = if definition_token_a_id == token_definition_id_in { + (user_holding_a, a_mention) } else if definition_token_b_id == token_definition_id_in { - user_holding_b + (user_holding_b, b_mention) } else { return Err(ExecutionFailureKind::AccountDataError( token_definition_id_in, )); }; - let nonces = self - .0 - .get_accounts_nonces(vec![account_id_auth]) + let mut groups = SigningGroup::new(); + groups + .add_required(seller_mention, account_id_auth, self.0) + .map_err(ExecutionFailureKind::from_anyhow)?; + self.0 + .send_public_tx(&program, account_ids, instruction, groups) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let signing_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(account_id_auth) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } + #[expect(clippy::too_many_arguments, reason = "each parameter is distinct")] pub async fn send_add_liquidity( &self, user_holding_a: AccountId, @@ -309,6 +220,9 @@ impl Amm<'_> { min_amount_liquidity: u128, max_amount_to_add_token_a: u128, max_amount_to_add_token_b: u128, + a_mention: &CliAccountMention, + b_mention: &CliAccountMention, + lp_mention: &CliAccountMention, ) -> Result { let instruction = amm_core::Instruction::AddLiquidity { min_amount_liquidity, @@ -352,48 +266,19 @@ impl Amm<'_> { user_holding_lp, ]; - let nonces = self - .0 - .get_accounts_nonces(vec![user_holding_a, user_holding_b]) + let mut groups = SigningGroup::new(); + groups + .add_required(a_mention, user_holding_a, self.0) + .and_then(|()| groups.add_required(b_mention, user_holding_b, self.0)) + .and_then(|()| groups.add_optional(lp_mention, user_holding_lp, self.0)) + .map_err(ExecutionFailureKind::from_anyhow)?; + + self.0 + .send_public_tx(&program, account_ids, instruction, groups) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let signing_key_a = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_a) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let signing_key_b = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_b) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = nssa::public_transaction::WitnessSet::for_message( - &message, - &[signing_key_a, signing_key_b], - ); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } + #[expect(clippy::too_many_arguments, reason = "each parameter is distinct")] pub async fn send_remove_liquidity( &self, user_holding_a: AccountId, @@ -402,6 +287,7 @@ impl Amm<'_> { remove_liquidity_amount: u128, min_amount_to_remove_token_a: u128, min_amount_to_remove_token_b: u128, + lp_mention: &CliAccountMention, ) -> Result { let instruction = amm_core::Instruction::RemoveLiquidity { remove_liquidity_amount, @@ -445,36 +331,12 @@ impl Amm<'_> { user_holding_lp, ]; - let nonces = self - .0 - .get_accounts_nonces(vec![user_holding_lp]) + let mut groups = SigningGroup::new(); + groups + .add_required(lp_mention, user_holding_lp, self.0) + .map_err(ExecutionFailureKind::from_anyhow)?; + self.0 + .send_public_tx(&program, account_ids, instruction, groups) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let signing_key_lp = self - .0 - .storage - .key_chain() - .pub_account_signing_key(user_holding_lp) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key_lp]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } } diff --git a/wallet/src/program_facades/ata.rs b/wallet/src/program_facades/ata.rs index bdb72620..09f11eea 100644 --- a/wallet/src/program_facades/ata.rs +++ b/wallet/src/program_facades/ata.rs @@ -1,14 +1,16 @@ use std::collections::HashMap; use ata_core::{compute_ata_seed, get_associated_token_account_id}; -use common::{HashType, transaction::NSSATransaction}; +use common::HashType; use nssa::{ AccountId, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, }; use nssa_core::SharedSecretKey; -use sequencer_service_rpc::RpcClient as _; -use crate::{ExecutionFailureKind, PrivacyPreservingAccount, WalletCore}; +use crate::{ + ExecutionFailureKind, PrivacyPreservingAccount, WalletCore, cli::CliAccountMention, + signing::SigningGroup, +}; pub struct Ata<'wallet>(pub &'wallet WalletCore); @@ -17,6 +19,7 @@ impl Ata<'_> { &self, owner_id: AccountId, definition_id: AccountId, + owner_mention: &CliAccountMention, ) -> Result { let program = Program::ata(); let ata_program_id = program.id(); @@ -26,36 +29,15 @@ impl Ata<'_> { ); let account_ids = vec![owner_id, definition_id, ata_id]; - - let nonces = self - .0 - .get_accounts_nonces(vec![owner_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let Some(signing_key) = self.0.storage.key_chain().pub_account_signing_key(owner_id) else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let instruction = ata_core::Instruction::Create { ata_program_id }; - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - )?; - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) + let mut groups = SigningGroup::new(); + groups + .add_required(owner_mention, owner_id, self.0) + .map_err(ExecutionFailureKind::from_anyhow)?; + self.0 + .send_public_tx(&program, account_ids, instruction, groups) + .await } pub async fn send_transfer( @@ -64,6 +46,7 @@ impl Ata<'_> { definition_id: AccountId, recipient_id: AccountId, amount: u128, + owner_mention: &CliAccountMention, ) -> Result { let program = Program::ata(); let ata_program_id = program.id(); @@ -73,39 +56,18 @@ impl Ata<'_> { ); let account_ids = vec![owner_id, sender_ata_id, recipient_id]; - - let nonces = self - .0 - .get_accounts_nonces(vec![owner_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let Some(signing_key) = self.0.storage.key_chain().pub_account_signing_key(owner_id) else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let instruction = ata_core::Instruction::Transfer { ata_program_id, amount, }; - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - )?; - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) + let mut groups = SigningGroup::new(); + groups + .add_required(owner_mention, owner_id, self.0) + .map_err(ExecutionFailureKind::from_anyhow)?; + self.0 + .send_public_tx(&program, account_ids, instruction, groups) + .await } pub async fn send_burn( @@ -113,6 +75,7 @@ impl Ata<'_> { owner_id: AccountId, definition_id: AccountId, amount: u128, + owner_mention: &CliAccountMention, ) -> Result { let program = Program::ata(); let ata_program_id = program.id(); @@ -122,39 +85,18 @@ impl Ata<'_> { ); let account_ids = vec![owner_id, holder_ata_id, definition_id]; - - let nonces = self - .0 - .get_accounts_nonces(vec![owner_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let Some(signing_key) = self.0.storage.key_chain().pub_account_signing_key(owner_id) else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let instruction = ata_core::Instruction::Burn { ata_program_id, amount, }; - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - )?; - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) + let mut groups = SigningGroup::new(); + groups + .add_required(owner_mention, owner_id, self.0) + .map_err(ExecutionFailureKind::from_anyhow)?; + self.0 + .send_public_tx(&program, account_ids, instruction, groups) + .await } pub async fn send_create_private_owner( @@ -181,7 +123,12 @@ impl Ata<'_> { ]; self.0 - .send_privacy_preserving_tx(accounts, instruction_data, &ata_with_token_dependency()) + .send_privacy_preserving_tx( + accounts, + instruction_data, + &ata_with_token_dependency(), + None, + ) .await .map(|(hash, mut secrets)| { let secret = secrets.pop().expect("expected owner's secret"); @@ -218,7 +165,12 @@ impl Ata<'_> { ]; self.0 - .send_privacy_preserving_tx(accounts, instruction_data, &ata_with_token_dependency()) + .send_privacy_preserving_tx( + accounts, + instruction_data, + &ata_with_token_dependency(), + None, + ) .await .map(|(hash, mut secrets)| { let secret = secrets.pop().expect("expected owner's secret"); @@ -254,7 +206,12 @@ impl Ata<'_> { ]; self.0 - .send_privacy_preserving_tx(accounts, instruction_data, &ata_with_token_dependency()) + .send_privacy_preserving_tx( + accounts, + instruction_data, + &ata_with_token_dependency(), + None, + ) .await .map(|(hash, mut secrets)| { let secret = secrets.pop().expect("expected owner's secret"); diff --git a/wallet/src/program_facades/native_token_transfer/deshielded.rs b/wallet/src/program_facades/native_token_transfer/deshielded.rs index d4bde39f..5a941d24 100644 --- a/wallet/src/program_facades/native_token_transfer/deshielded.rs +++ b/wallet/src/program_facades/native_token_transfer/deshielded.rs @@ -24,6 +24,7 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, + None, ) .await .map(|(resp, secrets)| { diff --git a/wallet/src/program_facades/native_token_transfer/private.rs b/wallet/src/program_facades/native_token_transfer/private.rs index e8438d08..c940dfb6 100644 --- a/wallet/src/program_facades/native_token_transfer/private.rs +++ b/wallet/src/program_facades/native_token_transfer/private.rs @@ -24,6 +24,7 @@ impl NativeTokenTransfer<'_> { vec![account], Program::serialize_instruction(instruction).unwrap(), &Program::authenticated_transfer_program().into(), + None, ) .await .map(|(resp, secrets)| { @@ -58,6 +59,7 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, + None, ) .await .map(|(resp, secrets)| { @@ -91,6 +93,7 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, + None, ) .await .map(|(resp, secrets)| { diff --git a/wallet/src/program_facades/native_token_transfer/public.rs b/wallet/src/program_facades/native_token_transfer/public.rs index b6c50c31..68c8e0be 100644 --- a/wallet/src/program_facades/native_token_transfer/public.rs +++ b/wallet/src/program_facades/native_token_transfer/public.rs @@ -1,17 +1,9 @@ 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::{AccountId, program::Program}; use super::NativeTokenTransfer; -use crate::{ - ExecutionFailureKind, cli::CliAccountMention, helperfunctions::read_pin, signing::SigningGroups, -}; +use crate::{ExecutionFailureKind, cli::CliAccountMention, signing::SigningGroup}; impl NativeTokenTransfer<'_> { pub async fn send_public_transfer( @@ -22,56 +14,22 @@ impl NativeTokenTransfer<'_> { from_mention: &CliAccountMention, to_mention: &CliAccountMention, ) -> Result { - let mut groups = SigningGroups::new(); + let mut groups = SigningGroup::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(), - )) - })?; + .add_required(from_mention, from, self.0) + .and_then(|()| groups.add_optional(to_mention, to, self.0)) + .map_err(ExecutionFailureKind::from_anyhow)?; - let program_id = Program::authenticated_transfer_program().id(); - let nonces = self - .0 - .get_accounts_nonces(groups.signing_ids()) + self.0 + .send_public_tx( + &Program::authenticated_transfer_program(), + vec![from, to], + AuthTransferInstruction::Transfer { + amount: balance_to_move, + }, + groups, + ) .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( @@ -79,53 +37,18 @@ impl NativeTokenTransfer<'_> { from: AccountId, account_mention: &CliAccountMention, ) -> Result { - let nonces = self - .0 - .get_accounts_nonces(vec![from]) - .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(); + let mut groups = SigningGroup::new(); groups - .add_sender(account_mention, from, self.0) - .map_err(|e| { - ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( - e.to_string(), - )) - })?; + .add_required(account_mention, from, self.0) + .map_err(ExecutionFailureKind::from_anyhow)?; - 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?) + self.0 + .send_public_tx( + &Program::authenticated_transfer_program(), + vec![from], + AuthTransferInstruction::Initialize, + groups, + ) + .await } } diff --git a/wallet/src/program_facades/native_token_transfer/shielded.rs b/wallet/src/program_facades/native_token_transfer/shielded.rs index 98dd0081..e221e87e 100644 --- a/wallet/src/program_facades/native_token_transfer/shielded.rs +++ b/wallet/src/program_facades/native_token_transfer/shielded.rs @@ -3,7 +3,7 @@ use nssa::AccountId; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; use super::{NativeTokenTransfer, auth_transfer_preparation}; -use crate::{ExecutionFailureKind, PrivacyPreservingAccount}; +use crate::{ExecutionFailureKind, PrivacyPreservingAccount, cli::CliAccountMention}; impl NativeTokenTransfer<'_> { pub async fn send_shielded_transfer( @@ -11,9 +11,9 @@ impl NativeTokenTransfer<'_> { from: AccountId, to: AccountId, balance_to_move: u128, + from_mention: &CliAccountMention, ) -> 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![ @@ -25,6 +25,7 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, + Some(from_mention), ) .await .map(|(resp, secrets)| { @@ -43,9 +44,9 @@ impl NativeTokenTransfer<'_> { to_vpk: ViewingPublicKey, to_identifier: Identifier, balance_to_move: u128, + from_mention: &CliAccountMention, ) -> 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![ @@ -59,6 +60,7 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, + Some(from_mention), ) .await .map(|(resp, secrets)| { diff --git a/wallet/src/program_facades/pinata.rs b/wallet/src/program_facades/pinata.rs index 0575455e..fd37016a 100644 --- a/wallet/src/program_facades/pinata.rs +++ b/wallet/src/program_facades/pinata.rs @@ -62,6 +62,7 @@ impl Pinata<'_> { ], nssa::program::Program::serialize_instruction(solution).unwrap(), &nssa::program::Program::pinata().into(), + None, ) .await .map(|(resp, secrets)| { diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs index 7197b1f0..c16b6365 100644 --- a/wallet/src/program_facades/token.rs +++ b/wallet/src/program_facades/token.rs @@ -1,10 +1,12 @@ -use common::{HashType, transaction::NSSATransaction}; +use common::HashType; use nssa::{AccountId, program::Program}; use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; -use sequencer_service_rpc::RpcClient as _; use token_core::Instruction; -use crate::{ExecutionFailureKind, PrivacyPreservingAccount, WalletCore}; +use crate::{ + ExecutionFailureKind, PrivacyPreservingAccount, WalletCore, cli::CliAccountMention, + signing::SigningGroup, +}; pub struct Token<'wallet>(pub &'wallet WalletCore); @@ -15,48 +17,21 @@ impl Token<'_> { supply_account_id: AccountId, name: String, total_supply: u128, + definition_mention: &CliAccountMention, + supply_mention: &CliAccountMention, ) -> Result { let account_ids = vec![definition_account_id, supply_account_id]; - let program_id = nssa::program::Program::token().id(); let instruction = Instruction::NewFungibleDefinition { name, total_supply }; - let nonces = self - .0 - .get_accounts_nonces(account_ids.clone()) + + let mut groups = SigningGroup::new(); + groups + .add_required(definition_mention, definition_account_id, self.0) + .and_then(|()| groups.add_required(supply_mention, supply_account_id, self.0)) + .map_err(ExecutionFailureKind::from_anyhow)?; + + self.0 + .send_public_tx(&Program::token(), account_ids, instruction, groups) .await - .map_err(ExecutionFailureKind::SequencerError)?; - let message = nssa::public_transaction::Message::try_new( - program_id, - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let def_private_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(definition_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - let supply_private_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(supply_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let witness_set = nssa::public_transaction::WitnessSet::for_message( - &message, - &[def_private_key, supply_private_key], - ); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_new_definition_private_owned_supply( @@ -80,6 +55,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -112,6 +88,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -146,6 +123,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -161,63 +139,23 @@ impl Token<'_> { sender_account_id: AccountId, recipient_account_id: AccountId, amount: u128, + sender_mention: &CliAccountMention, + recipient_mention: &CliAccountMention, ) -> Result { let account_ids = vec![sender_account_id, recipient_account_id]; - let program_id = nssa::program::Program::token().id(); let instruction = Instruction::Transfer { amount_to_transfer: amount, }; - let mut nonces = self - .0 - .get_accounts_nonces(vec![sender_account_id]) + + let mut groups = SigningGroup::new(); + groups + .add_required(sender_mention, sender_account_id, self.0) + .and_then(|()| groups.add_optional(recipient_mention, recipient_account_id, self.0)) + .map_err(ExecutionFailureKind::from_anyhow)?; + + self.0 + .send_public_tx(&Program::token(), account_ids, instruction, groups) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let mut private_keys = Vec::new(); - let sender_sk = self - .0 - .storage - .key_chain() - .pub_account_signing_key(sender_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(sender_sk); - - if let Some(recipient_sk) = self - .0 - .storage - .key_chain() - .pub_account_signing_key(recipient_account_id) - { - private_keys.push(recipient_sk); - let recipient_nonces = self - .0 - .get_accounts_nonces(vec![recipient_account_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - nonces.extend(recipient_nonces); - } else { - println!( - "Receiver's account ({recipient_account_id}) private key not found in wallet. Proceeding with only sender's key." - ); - } - - let message = nssa::public_transaction::Message::try_new( - program_id, - account_ids, - nonces, - instruction, - ) - .unwrap(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_transfer_transaction_private_owned_account( @@ -244,6 +182,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -282,6 +221,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -314,6 +254,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -330,13 +271,13 @@ impl Token<'_> { sender_account_id: AccountId, recipient_account_id: AccountId, amount: u128, + sender_mention: &CliAccountMention, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Transfer { amount_to_transfer: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); - self.0 .send_privacy_preserving_tx( vec![ @@ -347,6 +288,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + Some(sender_mention), ) .await .map(|(resp, secrets)| { @@ -365,13 +307,13 @@ impl Token<'_> { recipient_vpk: ViewingPublicKey, recipient_identifier: Identifier, amount: u128, + sender_mention: &CliAccountMention, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Transfer { amount_to_transfer: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); - self.0 .send_privacy_preserving_tx( vec![ @@ -384,6 +326,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + Some(sender_mention), ) .await .map(|(resp, secrets)| { @@ -400,41 +343,21 @@ impl Token<'_> { definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, + holder_mention: &CliAccountMention, ) -> Result { let account_ids = vec![definition_account_id, holder_account_id]; let instruction = Instruction::Burn { amount_to_burn: amount, }; - let nonces = self - .0 - .get_accounts_nonces(vec![holder_account_id]) + let mut groups = SigningGroup::new(); + groups + .add_required(holder_mention, holder_account_id, self.0) + .map_err(ExecutionFailureKind::from_anyhow)?; + + self.0 + .send_public_tx(&Program::token(), account_ids, instruction, groups) .await - .map_err(ExecutionFailureKind::SequencerError)?; - let message = nssa::public_transaction::Message::try_new( - Program::token().id(), - account_ids, - nonces, - instruction, - ) - .expect("Instruction should serialize"); - - let signing_key = self - .0 - .storage - .key_chain() - .pub_account_signing_key(holder_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_burn_transaction_private_owned_account( @@ -461,6 +384,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -493,6 +417,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -526,6 +451,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -542,63 +468,23 @@ impl Token<'_> { definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, + definition_mention: &CliAccountMention, + holder_mention: &CliAccountMention, ) -> Result { let account_ids = vec![definition_account_id, holder_account_id]; let instruction = Instruction::Mint { amount_to_mint: amount, }; - let mut nonces = self - .0 - .get_accounts_nonces(vec![definition_account_id]) + let mut groups = SigningGroup::new(); + groups + .add_required(definition_mention, definition_account_id, self.0) + .and_then(|()| groups.add_optional(holder_mention, holder_account_id, self.0)) + .map_err(ExecutionFailureKind::from_anyhow)?; + + self.0 + .send_public_tx(&Program::token(), account_ids, instruction, groups) .await - .map_err(ExecutionFailureKind::SequencerError)?; - - let mut private_keys = Vec::new(); - let definition_sk = self - .0 - .storage - .key_chain() - .pub_account_signing_key(definition_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(definition_sk); - - if let Some(holder_sk) = self - .0 - .storage - .key_chain() - .pub_account_signing_key(holder_account_id) - { - private_keys.push(holder_sk); - let recipient_nonces = self - .0 - .get_accounts_nonces(vec![holder_account_id]) - .await - .map_err(ExecutionFailureKind::SequencerError)?; - nonces.extend(recipient_nonces); - } else { - println!( - "Holder's account ({holder_account_id}) private key not found in wallet. Proceeding with only definition's key." - ); - } - - let message = nssa::public_transaction::Message::try_new( - Program::token().id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self - .0 - .sequencer_client - .send_transaction(NSSATransaction::Public(tx)) - .await?) } pub async fn send_mint_transaction_private_owned_account( @@ -625,6 +511,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -663,6 +550,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -695,6 +583,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -728,6 +617,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { @@ -765,6 +655,7 @@ impl Token<'_> { ], instruction_data, &Program::token().into(), + None, ) .await .map(|(resp, secrets)| { diff --git a/wallet/src/signing.rs b/wallet/src/signing.rs index 7fb877ab..47dd9ec1 100644 --- a/wallet/src/signing.rs +++ b/wallet/src/signing.rs @@ -1,6 +1,7 @@ use anyhow::Result; use keycard_wallet::{KeycardWallet, python_path}; use nssa::{AccountId, PrivateKey, PublicKey, Signature}; +use pyo3::Python; use crate::{WalletCore, cli::CliAccountMention}; @@ -9,12 +10,12 @@ use crate::{WalletCore, cli::CliAccountMention}; /// 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 { +pub struct SigningGroup { local: Vec<(AccountId, PrivateKey)>, keycard: Vec<(AccountId, String)>, } -impl SigningGroups { +impl SigningGroup { #[must_use] pub fn new() -> Self { Self::default() @@ -22,7 +23,7 @@ impl SigningGroups { /// 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( + pub fn add_required( &mut self, mention: &CliAccountMention, account_id: AccountId, @@ -42,9 +43,9 @@ impl SigningGroups { Ok(()) } - /// Add a recipient. Same as [`add_sender`] but silently skips accounts with no local + /// Add a recipient. Same as [`add_required`] 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( + pub fn add_optional( &mut self, mention: &CliAccountMention, account_id: AccountId, @@ -112,3 +113,37 @@ impl SigningGroups { Ok(sigs) } } + +/// Lazily opens and reuses a single Keycard session for all keycard signers in one transaction. +pub struct KeycardSessionContext { + pin: String, + wallet: Option, +} + +impl KeycardSessionContext { + pub fn new(pin: impl Into) -> Self { + Self { + pin: pin.into(), + wallet: None, + } + } + + pub fn get_or_connect<'py>( + &'py mut self, + py: Python<'py>, + ) -> pyo3::PyResult<&'py 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); + } + Ok(self.wallet.as_ref().unwrap()) + } + + pub fn close(self, py: Python<'_>) { + if let Some(w) = self.wallet { + drop(w.close_session(py)); + } + } +}