diff --git a/Cargo.lock b/Cargo.lock index 289ce5a9..ab27c933 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,41 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.8.12" @@ -448,7 +483,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.5.3", "tracing", "wasmtimer", ] @@ -495,7 +530,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.5.3", "tracing", "url", "wasmtimer", @@ -749,7 +784,7 @@ dependencies = [ "serde_json", "thiserror 2.0.18", "tokio", - "tower", + "tower 0.5.3", "tracing", "url", "wasmtimer", @@ -766,7 +801,7 @@ dependencies = [ "itertools 0.14.0", "reqwest 0.12.28", "serde_json", - "tower", + "tower 0.5.3", "tracing", "url", ] @@ -882,7 +917,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -893,7 +928,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -918,7 +953,7 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.52.0", + "windows-sys 0.60.2", "x11rb", ] @@ -1331,13 +1366,40 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core 0.4.5", + "bytes", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit 0.7.3", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 1.0.2", + "tower 0.5.3", + "tower-layer", + "tower-service", +] + [[package]] name = "axum" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" dependencies = [ - "axum-core", + "axum-core 0.5.6", "bytes", "form_urlencoded", "futures-util", @@ -1347,7 +1409,7 @@ dependencies = [ "hyper 1.8.1", "hyper-util", "itoa", - "matchit", + "matchit 0.8.4", "memchr", "mime", "percent-encoding", @@ -1358,12 +1420,32 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tower", + "tower 0.5.3", "tower-layer", "tower-service", "tracing", ] +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-core" version = "0.5.6" @@ -1407,6 +1489,18 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bech32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" + [[package]] name = "bimap" version = "0.6.3" @@ -1496,6 +1590,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -1540,6 +1643,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "sha2", + "tinyvec", +] + [[package]] name = "bstr" version = "1.12.1" @@ -1732,6 +1845,57 @@ dependencies = [ "error-code", ] +[[package]] +name = "coins-bip32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c43ff7fd9ff522219058808a259e61423335767b1071d5b346de60d9219657" +dependencies = [ + "bs58", + "coins-core", + "digest 0.10.7", + "hmac", + "k256", + "serde", + "sha2", + "thiserror 1.0.69", +] + +[[package]] +name = "coins-bip39" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4587c0b4064da887ed39a6522f577267d57e58bdd583178cd877d721b56a2e" +dependencies = [ + "bitvec", + "coins-bip32", + "hmac", + "once_cell", + "pbkdf2", + "rand 0.8.5", + "sha2", + "thiserror 1.0.69", +] + +[[package]] +name = "coins-core" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3aeeec621f4daec552e9d28befd58020a78cfc364827d06a753e8bc13c6c4b" +dependencies = [ + "base64 0.21.7", + "bech32 0.9.1", + "bs58", + "const-hex", + "digest 0.10.7", + "generic-array", + "ripemd", + "serde", + "sha2", + "sha3", + "thiserror 1.0.69", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -1928,7 +2092,7 @@ dependencies = [ "mio", "parking_lot", "rustix 0.38.44", - "signal-hook", + "signal-hook 0.3.18", "signal-hook-mio", "winapi", ] @@ -1981,6 +2145,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -1990,6 +2155,42 @@ version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b10589d1a5e400d61f9f38f12f884cfd080ff345de8f17efda36fe0e4a02aa8" +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "darling" version = "0.20.11" @@ -2257,7 +2458,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2323,6 +2524,30 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "educe" version = "0.6.0" @@ -2406,7 +2631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2505,6 +2730,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -2768,6 +2999,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "glob" version = "0.3.3" @@ -2906,6 +3147,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -3071,6 +3321,19 @@ dependencies = [ "webpki-roots 1.0.6", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper 1.8.1", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -3474,6 +3737,7 @@ dependencies = [ "once_cell", "serdect", "sha2", + "signature", ] [[package]] @@ -3628,6 +3892,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "matchit" version = "0.8.4" @@ -3787,7 +4057,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4074,6 +4344,12 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl" version = "0.10.75" @@ -4124,6 +4400,76 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ows-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f00266c0839f93538876db165b625fe2a7f181f436c8c4d2d584e997577f50" +dependencies = [ + "chrono", + "serde", + "serde_json", + "thiserror 2.0.18", + "uuid", +] + +[[package]] +name = "ows-lib" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e52f48c157be2ed6b0f35f612187941bccf749b856d631bee904908538af41a" +dependencies = [ + "base64 0.22.1", + "chrono", + "getrandom 0.2.17", + "hex", + "ows-core", + "ows-signer", + "prost", + "rand 0.8.5", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.18", + "tokio", + "tonic", + "uuid", + "zeroize", +] + +[[package]] +name = "ows-signer" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7705428542500d423211651560732b43d87c45e7bc9e3f161366f1d351349b" +dependencies = [ + "aes-gcm", + "base64 0.22.1", + "bech32 0.11.1", + "blake2", + "bs58", + "coins-bip32", + "coins-bip39", + "digest 0.10.7", + "ed25519-dalek", + "hex", + "hkdf", + "hmac", + "k256", + "libc", + "ows-core", + "rand 0.8.5", + "ripemd", + "scrypt", + "serde", + "serde_json", + "sha2", + "sha3", + "signal-hook 0.4.3", + "thiserror 2.0.18", + "zeroize", +] + [[package]] name = "p256" version = "0.13.2" @@ -4189,6 +4535,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" @@ -4348,6 +4705,18 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -4514,6 +4883,29 @@ dependencies = [ "unarray", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "pxfm" version = "0.1.28" @@ -4608,7 +5000,7 @@ dependencies = [ "once_cell", "socket2 0.6.2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -4898,7 +5290,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls 0.26.4", "tokio-util", - "tower", + "tower 0.5.3", "tower-http", "tower-service", "url", @@ -5645,7 +6037,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5658,7 +6050,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5829,6 +6221,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" dependencies = [ + "password-hash", "pbkdf2", "salsa20", "sha2", @@ -6180,6 +6573,16 @@ dependencies = [ "signal-hook-registry", ] +[[package]] +name = "signal-hook" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b57709da74f9ff9f4a27dce9526eec25ca8407c45a7887243b031a58935fb8e" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-mio" version = "0.2.5" @@ -6188,7 +6591,7 @@ checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", - "signal-hook", + "signal-hook 0.3.18", ] [[package]] @@ -6474,7 +6877,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6508,7 +6911,7 @@ name = "tempo-common" version = "0.2.0" dependencies = [ "alloy", - "axum", + "axum 0.8.8", "clap", "colored", "dirs", @@ -6517,6 +6920,8 @@ dependencies = [ "hex", "hostname", "mpp", + "ows-core", + "ows-lib", "posthog-rs", "reqwest 0.12.28", "rusqlite", @@ -6583,7 +6988,7 @@ version = "0.2.0" dependencies = [ "alloy", "assert_cmd", - "axum", + "axum 0.8.8", "base64 0.22.1", "clap", "fs2", @@ -6624,7 +7029,7 @@ name = "tempo-test" version = "0.2.0" dependencies = [ "assert_cmd", - "axum", + "axum 0.8.8", "rusqlite", "serde_json", "tempfile", @@ -6638,7 +7043,7 @@ version = "0.2.0" dependencies = [ "alloy", "assert_cmd", - "axum", + "axum 0.8.8", "base64 0.22.1", "clap", "clap_complete", @@ -6972,6 +7377,36 @@ version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.7.9", + "base64 0.22.1", + "bytes", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2 0.5.10", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "toon-format" version = "0.4.4" @@ -6995,6 +7430,26 @@ dependencies = [ "unicode-width 0.2.0", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.5.3" @@ -7024,7 +7479,7 @@ dependencies = [ "http-body 1.0.1", "iri-string", "pin-project-lite", - "tower", + "tower 0.5.3", "tower-layer", "tower-service", ] @@ -7225,6 +7680,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index ac3ea536..f64dd870 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ hex = "0.4" minisign = "0.9" hostname = "0.4" mpp = { git = "https://github.com/tempoxyz/mpp-rs.git", branch = "main", features = ["tempo", "evm", "utils", "client", "server"] } +ows-core = "1.0.0" +ows-lib = "1.0.0" posthog-rs = "0.4.7" qrcode = { version = "0.14", default-features = false } reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] } diff --git a/crates/tempo-common/Cargo.toml b/crates/tempo-common/Cargo.toml index 881f67f8..99d3957e 100644 --- a/crates/tempo-common/Cargo.toml +++ b/crates/tempo-common/Cargo.toml @@ -23,6 +23,8 @@ getrandom.workspace = true hex.workspace = true hostname.workspace = true mpp.workspace = true +ows-core.workspace = true +ows-lib.workspace = true posthog-rs.workspace = true reqwest.workspace = true rusqlite.workspace = true diff --git a/crates/tempo-common/src/keys/mod.rs b/crates/tempo-common/src/keys/mod.rs index 892b69f3..9cad70ab 100644 --- a/crates/tempo-common/src/keys/mod.rs +++ b/crates/tempo-common/src/keys/mod.rs @@ -2,6 +2,7 @@ pub mod authorization; mod io; +pub mod ows; mod keystore; mod model; mod signer; diff --git a/crates/tempo-common/src/keys/ows.rs b/crates/tempo-common/src/keys/ows.rs new file mode 100644 index 00000000..3f066eb1 --- /dev/null +++ b/crates/tempo-common/src/keys/ows.rs @@ -0,0 +1,105 @@ +//! OWS (Open Wallet Standard) wallet operations. + +use zeroize::Zeroizing; + +use crate::error::{ConfigError, TempoError}; + +/// Create a new OWS wallet. Returns the wallet name. +pub fn create_wallet(name: &str) -> Result { + ows_lib::create_wallet(name, Some(12), None, None) + .map_err(|e| TempoError::Config(ConfigError::Missing(format!("OWS create_wallet: {e}"))))?; + Ok(name.to_string()) +} + +/// Find an existing OWS wallet whose name starts with `prefix`. +pub fn find_wallet(prefix: &str) -> Option { + let wallets = ows_lib::list_wallets(None).ok()?; + wallets + .iter() + .find(|w| w.name.starts_with(prefix)) + .map(|w| w.name.clone()) +} + +/// Decrypt the EVM signing key from an OWS wallet. +pub fn export_private_key(name_or_id: &str) -> Result, TempoError> { + let key_bytes = ows_lib::decrypt_signing_key( + name_or_id, ows_core::ChainType::Evm, "", None, None, + ) + .map_err(|e| TempoError::Config(ConfigError::Missing(format!("OWS decrypt: {e}"))))?; + Ok(Zeroizing::new(format!("0x{}", hex::encode(key_bytes.expose())))) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Uses a custom vault path so tests don't touch the real ~/.ows. + fn create_in(name: &str, vault: &std::path::Path) -> String { + ows_lib::create_wallet(name, Some(12), None, Some(vault)).unwrap(); + name.to_string() + } + + fn export_in(name: &str, vault: &std::path::Path) -> Zeroizing { + let key_bytes = ows_lib::decrypt_signing_key( + name, ows_core::ChainType::Evm, "", None, Some(vault), + ).unwrap(); + Zeroizing::new(format!("0x{}", hex::encode(key_bytes.expose()))) + } + + fn find_in(prefix: &str, vault: &std::path::Path) -> Option { + let wallets = ows_lib::list_wallets(Some(vault)).ok()?; + wallets.iter().find(|w| w.name.starts_with(prefix)).map(|w| w.name.clone()) + } + + #[test] + fn create_and_export_round_trip() { + let vault = tempfile::tempdir().unwrap(); + let name = create_in("test-wallet", vault.path()); + let key = export_in(&name, vault.path()); + // Should be a valid 0x-prefixed 32-byte hex key. + assert!(key.starts_with("0x")); + assert_eq!(key.len(), 66); // 0x + 64 hex chars + } + + #[test] + fn find_wallet_by_prefix() { + let vault = tempfile::tempdir().unwrap(); + create_in("tempo-abc123", vault.path()); + create_in("polymarket-def456", vault.path()); + + assert_eq!(find_in("tempo", vault.path()), Some("tempo-abc123".to_string())); + assert_eq!(find_in("polymarket", vault.path()), Some("polymarket-def456".to_string())); + assert_eq!(find_in("nonexistent", vault.path()), None); + } + + #[test] + fn reuse_existing_wallet_same_address() { + let vault = tempfile::tempdir().unwrap(); + create_in("tempo-first", vault.path()); + + let key1 = export_in("tempo-first", vault.path()); + let key2 = export_in("tempo-first", vault.path()); + assert_eq!(*key1, *key2, "same wallet should produce same key"); + } + + #[test] + fn different_wallets_different_keys() { + let vault = tempfile::tempdir().unwrap(); + create_in("wallet-a", vault.path()); + create_in("wallet-b", vault.path()); + + let key_a = export_in("wallet-a", vault.path()); + let key_b = export_in("wallet-b", vault.path()); + assert_ne!(*key_a, *key_b); + } + + #[test] + fn exported_key_is_zeroizing() { + let vault = tempfile::tempdir().unwrap(); + create_in("zero-test", vault.path()); + let key = export_in("zero-test", vault.path()); + // Key is Zeroizing — verify it's valid before drop. + assert!(key.starts_with("0x")); + // After drop, memory is wiped (can't test directly, but type guarantees it). + } +} diff --git a/crates/tempo-wallet/src/app.rs b/crates/tempo-wallet/src/app.rs index 3da79c92..5059b793 100644 --- a/crates/tempo-wallet/src/app.rs +++ b/crates/tempo-wallet/src/app.rs @@ -25,7 +25,7 @@ pub(crate) async fn run(mut cli: Cli) -> Result<(), TempoError> { |ctx| async move { let cmd_name = command_name(&command); let result = match command { - Commands::Login => login::run(&ctx).await, + Commands::Login { ows } => login::run(&ctx, ows).await, Commands::Refresh => refresh::run(&ctx).await, Commands::Logout { yes } => logout::run(&ctx, yes), Commands::Completions { shell } => completions::run(&ctx, shell), @@ -63,7 +63,7 @@ pub(crate) async fn run(mut cli: Cli) -> Result<(), TempoError> { /// Derive a short analytics-friendly name from a parsed command. const fn command_name(command: &Commands) -> &'static str { match command { - Commands::Login => "login", + Commands::Login { .. } => "login", Commands::Refresh => "refresh", Commands::Logout { .. } => "logout", Commands::Completions { .. } => "completions", diff --git a/crates/tempo-wallet/src/args.rs b/crates/tempo-wallet/src/args.rs index 14c3dd0b..316a847d 100644 --- a/crates/tempo-wallet/src/args.rs +++ b/crates/tempo-wallet/src/args.rs @@ -31,7 +31,11 @@ pub(crate) struct Cli { pub(crate) enum Commands { /// Sign up or log in to your Tempo wallet #[command(display_order = 1)] - Login, + Login { + /// Use OWS encrypted vault instead of passkey (fully headless) + #[arg(long)] + ows: bool, + }, /// Refresh your access key without logging out #[command(display_order = 2)] Refresh, diff --git a/crates/tempo-wallet/src/commands/login.rs b/crates/tempo-wallet/src/commands/login.rs index 9026ecb7..f4bd280d 100644 --- a/crates/tempo-wallet/src/commands/login.rs +++ b/crates/tempo-wallet/src/commands/login.rs @@ -23,7 +23,10 @@ use tempo_common::{ const CALLBACK_TIMEOUT_SECS: u64 = 900; // 15 minutes const POLL_INTERVAL_SECS: u64 = 2; -pub(crate) async fn run(ctx: &Context) -> Result<(), TempoError> { +pub(crate) async fn run(ctx: &Context, ows: bool) -> Result<(), TempoError> { + if ows { + return run_ows(ctx).await; + } run_impl(ctx, false).await } @@ -81,6 +84,90 @@ async fn run_impl(ctx: &Context, force_reauth: bool) -> Result<(), TempoError> { show_whoami(ctx, Some(&keys), None).await } +/// OWS login — headless alternative to passkey. Same scoped access key, +/// same $100 limit, same 30-day expiry. Root key in OWS vault instead +/// of passkey secure enclave. +async fn run_ows(ctx: &Context) -> Result<(), TempoError> { + let already_logged_in = ctx.keys.has_key_for_network(ctx.network); + + let needs_reauth = if already_logged_in { + is_key_revoked_or_expired(ctx).await + } else { + false + }; + + if needs_reauth { + invalidate_stale_key(ctx, false)?; + } + + if already_logged_in && !needs_reauth { + if ctx.output_format == OutputFormat::Text { + eprintln!("Already logged in.\n"); + } + let keys = ctx.keys.reload()?; + return show_whoami(ctx, Some(&keys), None).await; + } + + // Reuse existing OWS wallet for this network, or create a new one. + let network_prefix = ctx.network.as_str(); + let ows_wallet_name = if let Some(existing) = tempo_common::keys::ows::find_wallet(network_prefix) { + existing + } else { + let mut nonce = [0u8; 4]; + getrandom::fill(&mut nonce).map_err(|source| KeyError::SigningOperationSource { + operation: "generate wallet nonce", + source: Box::new(source), + })?; + let name = format!("{}-{}", network_prefix, hex::encode(nonce)); + tempo_common::keys::ows::create_wallet(&name)? + }; + + // Derive wallet address from root key. + let exported_key = tempo_common::keys::ows::export_private_key(&ows_wallet_name)?; + let root_signer = tempo_common::keys::parse_private_key_signer(&exported_key)?; + let wallet_address = root_signer.address(); + drop(exported_key); + + // Generate scoped access key and sign authorization (same defaults as passkey). + let access_signer = PrivateKeySigner::random(); + let auth = tempo_common::keys::authorization::sign( + &root_signer, + &access_signer, + ctx.network.chain_id(), + )?; + drop(root_signer); + + // Reuse save_keys() — same save path as passkey login. + save_keys( + ctx.network, + &ctx.keys, + AuthCallback { + account_address: format!("{wallet_address:#x}"), + key_authorization: Some(auth.hex), + duration_secs: 0, + }, + access_signer, + )?; + + ctx.track( + analytics::WALLET_CREATED, + WalletCreatedPayload { + wallet_type: "ows".to_string(), + }, + ); + ctx.track_event(analytics::KEY_CREATED); + if let Some(ref a) = ctx.analytics { + a.identify(&ctx.keys); + } + + if ctx.output_format == OutputFormat::Text { + eprintln!("\nWallet connected!\n"); + } + + let keys = ctx.keys.reload()?; + show_whoami(ctx, Some(&keys), None).await +} + /// Check whether the stored access key has been revoked or has expired on-chain. /// /// Returns `true` when the key is definitively invalid, `false` otherwise