diff --git a/.env.example-local b/.env.example-local new file mode 100644 index 0000000..ebdc768 --- /dev/null +++ b/.env.example-local @@ -0,0 +1,27 @@ +# SQlite Zone Demo - Local Development Environment +# Usage: ./run-local.sh --env-file PATH/TO/FILE/.env-local + +# Sequencer node endpoint (for submitting transactions) +SEQUENCER_NODE_ENDPOINT=https://devnet.blockchain.logos.co/node/0/ +SEQUENCER_NODE_AUTH_USERNAME= +SEQUENCER_NODE_AUTH_PASSWORD= + +#Path to Sequencer's DB +SEQUENCER_DB_PATH=./database.db + +# Path to signing key file +SEQUENCER_SIGNING_KEY_PATH= + +# Path to queue file for pending SQL statements +QUEUE_FILE=./query.txt + +# Path to checkpoint file for recovery +CHECKPOINT_PATH=./sequencer.checkpoint + +# Indexer node endpoint (for reading blocks - can be different node) +INDEXER_NODE_ENDPOINT=https://devnet.blockchain.logos.co/node/1/ +INDEXER_NODE_AUTH_USERNAME= +INDEXER_NODE_AUTH_PASSWORD= + +# Path to Indexer's local db +INDEXER_DB_PATH=./indexer.db \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0450f3d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,8527 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +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 0.14.7", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object", +] + +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "x11rb", +] + +[[package]] +name = "archery" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e0a5f99dfebb87bb342d0f53bb92c81842e100bbb915223e38349580e5441d" +dependencies = [ + "triomphe", +] + +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", + "zeroize", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-crypto-primitives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-relations", + "ark-serialize 0.4.2", + "ark-snark", + "ark-std 0.4.0", + "blake2", + "derivative", + "digest", + "sha2", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-poly 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.5", + "itertools 0.13.0", + "num-bigint", + "num-integer", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-groth16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" +dependencies = [ + "ark-crypto-primitives", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-relations", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.5", +] + +[[package]] +name = "ark-relations" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" +dependencies = [ + "ark-ff 0.4.2", + "ark-std 0.4.0", + "tracing", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive 0.4.2", + "ark-std 0.4.0", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-snark" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" +dependencies = [ + "ark-ff 0.4.2", + "ark-relations", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "asn1_der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4858a9d740c5007a9069007c3b4e91152d0506f13c1b31dd49051fd537656156" + +[[package]] +name = "astro-float" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96034cc871c05bb65ad7fb77e6a8bebf45d8b055ed0311769e2f83a1d373c1ec" +dependencies = [ + "astro-float-macro", + "astro-float-num", +] + +[[package]] +name = "astro-float-macro" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cfe0f6df5a74fb25b9e713470ad77e091f35f798730673c8772f26ed438963" +dependencies = [ + "astro-float-num", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "astro-float-num" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86887daca11d02e0b04f37a9cb81888aae881397fb48ff66494e356aea97554a" +dependencies = [ + "itertools 0.10.5", + "lazy_static", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel 2.5.0", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.4.1", + "futures-lite", + "rustix 1.1.4", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-std" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "asynchronous-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "attohttpc" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" +dependencies = [ + "http 0.2.12", + "log", + "url", +] + +[[package]] +name = "attohttpc" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" +dependencies = [ + "base64", + "http 1.4.0", + "log", + "url", +] + +[[package]] +name = "autocfg" +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", + "bytes", + "futures-util", + "http 1.4.0", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[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", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backon" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" +dependencies = [ + "fastrand", + "gloo-timers", + "tokio", +] + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str", + "match-lookup", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.117", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel 2.5.0", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "serde", + "static_assertions", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "condtype" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" + +[[package]] +name = "const-hex" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" +dependencies = [ + "cfg-if", + "cpufeatures", + "proptest", + "serde_core", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags 2.11.0", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix 0.38.44", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "typenum", +] + +[[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", + "fiat-crypto", + "rustc_version", + "serde", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.117", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "data-encoding-macro" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" +dependencies = [ + "data-encoding", + "syn 2.0.117", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "deluxe" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed332aaf752b459088acf3dd4eca323e3ef4b83c70a84ca48fb0ec5305f1488" +dependencies = [ + "deluxe-core", + "deluxe-macros", + "once_cell", + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "deluxe-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddada51c8576df9d6a8450c351ff63042b092c9458b8ac7d20f89cbd0ffd313" +dependencies = [ + "arrayvec", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 2.0.117", +] + +[[package]] +name = "deluxe-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87546d9c837f0b7557e47b8bd6eae52c3c223141b76aa233c345c9ab41d9117" +dependencies = [ + "deluxe-core", + "heck 0.4.1", + "if_chain", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "demo-sqlite-common" +version = "0.2.1" +dependencies = [ + "arboard", + "argon2", + "block-padding", + "chacha20poly1305", + "chrono", + "crypto-common", + "directories", + "logos-blockchain-zone-sdk", + "nanosql", + "rand 0.9.2", + "ratatui", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing-subscriber 0.3.23", + "zeroize", + "zxcvbn", +] + +[[package]] +name = "demo-sqlite-indexer" +version = "0.2.1" +dependencies = [ + "arboard", + "chrono", + "clap", + "demo-sqlite-common", + "futures", + "hex", + "logos-blockchain-common-http-client", + "logos-blockchain-core", + "logos-blockchain-zone-sdk", + "nanosql", + "ratatui", + "reqwest", + "rusqlite", + "tokio", + "tracing", + "tracing-subscriber 0.3.23", + "tui-textarea", + "zeroize", +] + +[[package]] +name = "demo-sqlite-sequencer" +version = "0.2.1" +dependencies = [ + "arboard", + "chrono", + "clap", + "demo-sqlite-common", + "fs2", + "hex", + "logos-blockchain-common-http-client", + "logos-blockchain-core", + "logos-blockchain-key-management-system-service", + "logos-blockchain-zone-sdk", + "nanosql", + "rand 0.9.2", + "ratatui", + "reqwest", + "rusqlite", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", + "tracing-subscriber 0.3.23", + "tui-textarea", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.0", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "divan" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a405457ec78b8fe08b0e32b4a3570ab5dff6dd16eb9e76a5ee0a9d9cbd898933" +dependencies = [ + "cfg-if", + "clap", + "condtype", + "divan-macros", + "libc", + "regex-lite", +] + +[[package]] +name = "divan-macros" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9556bc800956545d6420a640173e5ba7dfa82f38d3ea5a167eb555bc69ac3323" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlopen2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" +dependencies = [ + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "serde", + "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", + "rand_core 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array 0.14.7", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "enum-ordinalize" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fancy-regex" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-bounded" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91f328e7fb845fc832912fb6a34f40cf6d1888c92f974d1893a54e97b5ff542e" +dependencies = [ + "futures-timer", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" +dependencies = [ + "futures-io", + "rustls", + "rustls-pki-types", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +dependencies = [ + "rustversion", + "serde_core", + "typenum", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.4", + "windows-link", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hasher" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbba678b6567f27ce22870d951f4208e1dc2fef64993bd4521b1d497ef8a3aa" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex_fmt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" + +[[package]] +name = "hickory-proto" +version = "0.25.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d00147af6310f4392a31680db52a3ed45a2e0f68eb18e8c3fe5537ecc96d9e2" +dependencies = [ + "async-recursion", + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "once_cell", + "rand 0.9.2", + "socket2 0.5.10", + "thiserror 2.0.18", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.25.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5762f69ebdbd4ddb2e975cd24690bf21fe6b2604039189c26acddbc427f12887" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "moka", + "once_cell", + "parking_lot", + "rand 0.9.2", + "resolv-conf", + "smallvec", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http 1.4.0", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http 1.4.0", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http 1.4.0", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.3", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if-addrs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a05c691e1fae256cf7013d99dad472dc52d5543322761f83ec8d47eab40d2b" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "if-watch" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c02a5161c313f0cbdbadc511611893584a10a7b6153cb554bdf83ddce99ec2" +dependencies = [ + "async-io", + "core-foundation", + "fnv", + "futures", + "if-addrs", + "ipnet", + "log", + "netlink-packet-core 0.8.1", + "netlink-packet-route 0.28.0", + "netlink-proto", + "netlink-sys", + "rtnetlink", + "smol", + "system-configuration 0.7.0", + "tokio", + "windows", +] + +[[package]] +name = "if_chain" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" + +[[package]] +name = "igd-next" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b0d7d4541def58a37bf8efc559683f21edce7c82f0d866c93ac21f7e098f93" +dependencies = [ + "async-trait", + "attohttpc 0.24.1", + "bytes", + "futures", + "http 1.4.0", + "http-body-util", + "hyper", + "hyper-util", + "log", + "rand 0.8.5", + "tokio", + "url", + "xmltree", +] + +[[package]] +name = "igd-next" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516893339c97f6011282d5825ac94fc1c7aad5cad26bdc2d0cee068c0bf97f97" +dependencies = [ + "async-trait", + "attohttpc 0.30.1", + "bytes", + "futures", + "http 1.4.0", + "http-body-util", + "hyper", + "hyper-util", + "log", + "rand 0.9.2", + "tokio", + "url", + "xmltree", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png", + "tiff", +] + +[[package]] +name = "indexmap" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "inferno" +version = "0.11.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" +dependencies = [ + "ahash", + "indexmap", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "instability" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" +dependencies = [ + "darling 0.23.0", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ipconfig" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" +dependencies = [ + "socket2 0.6.3", + "widestring", + "windows-registry", + "windows-result", + "windows-sys 0.61.2", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jf-crhf" +version = "0.1.1" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=jf-crhf-v0.1.1#8f3dce0bc2bd161b4648f6ac029dcc1a23aaf4c5" +dependencies = [ + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "jf-poseidon2" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish.git?rev=dc166cf0f803c3e5067f9dfcc21e3dade986a447#dc166cf0f803c3e5067f9dfcc21e3dade986a447" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ff 0.4.2", + "ark-std 0.4.0", + "displaydoc", + "hex", + "jf-crhf", + "lazy_static", + "nimue", + "zeroize", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libp2p" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72dc443ddd0254cb49a794ed6b6728400ee446a0f7ab4a07d0209ee98de20e9" +dependencies = [ + "bytes", + "either", + "futures", + "futures-timer", + "getrandom 0.2.17", + "libp2p-allow-block-list", + "libp2p-autonat", + "libp2p-connection-limits", + "libp2p-core", + "libp2p-dns", + "libp2p-gossipsub", + "libp2p-identify", + "libp2p-identity", + "libp2p-kad", + "libp2p-mdns", + "libp2p-metrics", + "libp2p-quic", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-upnp", + "multiaddr", + "pin-project", + "rw-stream-sink", + "thiserror 2.0.18", +] + +[[package]] +name = "libp2p-allow-block-list" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38944b7cb981cc93f2f0fb411ff82d0e983bd226fbcc8d559639a3a73236568b" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", +] + +[[package]] +name = "libp2p-autonat" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e297bfc6cabb70c6180707f8fa05661b77ecb9cb67e8e8e1c469301358fa21d0" +dependencies = [ + "async-trait", + "asynchronous-codec", + "either", + "futures", + "futures-bounded", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-request-response", + "libp2p-swarm", + "quick-protobuf", + "quick-protobuf-codec", + "rand 0.8.5", + "rand_core 0.6.4", + "thiserror 2.0.18", + "tracing", + "web-time", +] + +[[package]] +name = "libp2p-connection-limits" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efe9323175a17caa8a2ed4feaf8a548eeef5e0b72d03840a0eab4bcb0210ce1c" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", +] + +[[package]] +name = "libp2p-core" +version = "0.43.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "249128cd37a2199aff30a7675dffa51caf073b51aa612d2f544b19932b9aebca" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "libp2p-identity", + "multiaddr", + "multihash", + "multistream-select", + "parking_lot", + "pin-project", + "quick-protobuf", + "rand 0.8.5", + "rw-stream-sink", + "thiserror 2.0.18", + "tracing", + "unsigned-varint 0.8.0", + "web-time", +] + +[[package]] +name = "libp2p-dns" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b780a1150214155b0ed1cdf09fbd2e1b0442604f9146a431d1b21d23eef7bd7" +dependencies = [ + "async-trait", + "futures", + "hickory-resolver", + "libp2p-core", + "libp2p-identity", + "parking_lot", + "smallvec", + "tracing", +] + +[[package]] +name = "libp2p-gossipsub" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d558548fa3b5a8e9b66392f785921e363c57c05dcadfda4db0d41ae82d313e4a" +dependencies = [ + "async-channel 2.5.0", + "asynchronous-codec", + "base64", + "byteorder", + "bytes", + "either", + "fnv", + "futures", + "futures-timer", + "getrandom 0.2.17", + "hashlink 0.9.1", + "hex_fmt", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "prometheus-client", + "quick-protobuf", + "quick-protobuf-codec", + "rand 0.8.5", + "regex", + "serde", + "sha2", + "tracing", + "web-time", +] + +[[package]] +name = "libp2p-identify" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c06862544f02d05d62780ff590cc25a75f5c2b9df38ec7a370dcae8bb873cf" +dependencies = [ + "asynchronous-codec", + "either", + "futures", + "futures-bounded", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "quick-protobuf", + "quick-protobuf-codec", + "smallvec", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "libp2p-identity" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "hkdf", + "k256", + "multihash", + "quick-protobuf", + "rand 0.8.5", + "serde", + "sha2", + "thiserror 2.0.18", + "tracing", + "zeroize", +] + +[[package]] +name = "libp2p-kad" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bab0466a27ebe955bcbc27328fae5429c5b48c915fd6174931414149802ec23" +dependencies = [ + "asynchronous-codec", + "bytes", + "either", + "fnv", + "futures", + "futures-bounded", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "quick-protobuf", + "quick-protobuf-codec", + "rand 0.8.5", + "serde", + "sha2", + "smallvec", + "thiserror 2.0.18", + "tracing", + "uint", + "web-time", +] + +[[package]] +name = "libp2p-mdns" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d0ba095e1175d797540e16b62e7576846b883cb5046d4159086837b36846cc" +dependencies = [ + "futures", + "hickory-proto", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "rand 0.8.5", + "smallvec", + "socket2 0.5.10", + "tokio", + "tracing", +] + +[[package]] +name = "libp2p-metrics" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce58c64292e87af624fcb86465e7dd8342e46a388d71e8fec0ab37ee789630a" +dependencies = [ + "futures", + "libp2p-core", + "libp2p-gossipsub", + "libp2p-identify", + "libp2p-identity", + "libp2p-kad", + "libp2p-swarm", + "pin-project", + "prometheus-client", + "web-time", +] + +[[package]] +name = "libp2p-plaintext" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e659439578fc6d305da8303834beb9d62f155f40e7f5b9d81c9f2b2c69d1926" +dependencies = [ + "asynchronous-codec", + "bytes", + "futures", + "libp2p-core", + "libp2p-identity", + "quick-protobuf", + "quick-protobuf-codec", + "tracing", +] + +[[package]] +name = "libp2p-quic" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41432a159b00424a0abaa2c80d786cddff81055ac24aa127e0cf375f7858d880" +dependencies = [ + "futures", + "futures-timer", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-tls", + "quinn", + "rand 0.8.5", + "ring", + "rustls", + "socket2 0.5.10", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "libp2p-request-response" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548fe44a80ff275d400f1b26b090d441d83ef73efabbeb6415f4ce37e5aed865" +dependencies = [ + "async-trait", + "futures", + "futures-bounded", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "rand 0.8.5", + "smallvec", + "tracing", +] + +[[package]] +name = "libp2p-stream" +version = "0.3.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826716f1ee125895f1fb44911413cba023485b552ff96c7a2159bd037ac619bb" +dependencies = [ + "futures", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "rand 0.8.5", + "tracing", +] + +[[package]] +name = "libp2p-swarm" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803399b4b6f68adb85e63ab573ac568154b193e9a640f03e0f2890eabbcb37f8" +dependencies = [ + "async-std", + "either", + "fnv", + "futures", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm-derive", + "lru", + "multistream-select", + "once_cell", + "rand 0.8.5", + "smallvec", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "libp2p-swarm-derive" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206e0aa0ebe004d778d79fb0966aa0de996c19894e2c0605ba2f8524dd4443d8" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "libp2p-swarm-test" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb6354e3a50496d750805f6cf33679bd698850d535602f42c61e465e0734d0b" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-plaintext", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-yamux", + "tracing", +] + +[[package]] +name = "libp2p-tcp" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65346fb4d36035b23fec4e7be4c320436ba53537ce9b6be1d1db1f70c905cad0" +dependencies = [ + "async-io", + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core", + "socket2 0.5.10", + "tokio", + "tracing", +] + +[[package]] +name = "libp2p-tls" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ff65a82e35375cbc31ebb99cacbbf28cb6c4fefe26bf13756ddcf708d40080" +dependencies = [ + "futures", + "futures-rustls", + "libp2p-core", + "libp2p-identity", + "rcgen", + "ring", + "rustls", + "rustls-webpki", + "thiserror 2.0.18", + "x509-parser", + "yasna", +] + +[[package]] +name = "libp2p-upnp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d457b9ecceb66e7199f049926fad447f1f17f040e8d29d690c086b4cab8ed14a" +dependencies = [ + "futures", + "futures-timer", + "igd-next 0.15.1", + "libp2p-core", + "libp2p-swarm", + "tokio", + "tracing", +] + +[[package]] +name = "libp2p-yamux" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f15df094914eb4af272acf9adaa9e287baa269943f32ea348ba29cfb9bfc60d8" +dependencies = [ + "either", + "futures", + "libp2p-core", + "thiserror 2.0.18", + "tracing", + "yamux 0.12.1", + "yamux 0.13.10", +] + +[[package]] +name = "libredox" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +dependencies = [ + "libc", +] + +[[package]] +name = "librocksdb-sys" +version = "0.17.3+10.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef2a00ee60fe526157c9023edab23943fae1ce2ab6f4abb2a807c1746835de9" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "libc", + "libz-sys", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8935b44e7c13394a179a438e0cebba0fe08fe01b54f152e29a93b5cf993fd4" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "light-poseidon" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a1ccadd0bb5a32c196da536fd72c59183de24a055f6bf0513bf845fefab862" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "num-bigint", + "thiserror 1.0.69", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +dependencies = [ + "value-bag", +] + +[[package]] +name = "logos-blockchain-blend-crypto" +version = "0.2.1" +dependencies = [ + "blake2", + "logos-blockchain-groth16", + "logos-blockchain-poq", + "logos-blockchain-poseidon2", + "logos-blockchain-utils", + "num-bigint", + "rs-merkle-tree", + "thiserror 1.0.69", +] + +[[package]] +name = "logos-blockchain-blend-message" +version = "0.2.1" +dependencies = [ + "blake2", + "derivative", + "hex", + "itertools 0.14.0", + "logos-blockchain-blend-crypto", + "logos-blockchain-blend-proofs", + "logos-blockchain-core", + "logos-blockchain-groth16", + "logos-blockchain-key-management-system-keys", + "logos-blockchain-utils", + "serde", + "serde-big-array", + "serde_with", + "test-log", + "thiserror 1.0.69", + "tracing", + "zeroize", +] + +[[package]] +name = "logos-blockchain-blend-proofs" +version = "0.2.1" +dependencies = [ + "const-hex", + "ed25519-dalek", + "generic-array 1.3.5", + "hex", + "logos-blockchain-blend-crypto", + "logos-blockchain-core", + "logos-blockchain-groth16", + "logos-blockchain-key-management-system-keys", + "logos-blockchain-pol", + "logos-blockchain-poq", + "logos-blockchain-utils", + "num-bigint", + "serde", + "thiserror 1.0.69", + "zeroize", +] + +[[package]] +name = "logos-blockchain-chain-broadcast-service" +version = "0.2.1" +dependencies = [ + "async-trait", + "derivative", + "futures", + "logos-blockchain-core", + "overwatch", + "serde", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "logos-blockchain-chain-service" +version = "0.2.1" +dependencies = [ + "async-trait", + "bytes", + "futures", + "logos-blockchain-chain-broadcast-service", + "logos-blockchain-core", + "logos-blockchain-cryptarchia-engine", + "logos-blockchain-cryptarchia-sync", + "logos-blockchain-groth16", + "logos-blockchain-key-management-system-keys", + "logos-blockchain-ledger", + "logos-blockchain-network-service", + "logos-blockchain-services-utils", + "logos-blockchain-storage-service", + "logos-blockchain-time-service", + "logos-blockchain-tracing", + "logos-blockchain-utils", + "logos-blockchain-utxotree", + "num-bigint", + "overwatch", + "rand 0.8.5", + "serde", + "serde_with", + "strum 0.27.2", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tracing", + "tracing-futures", + "utoipa", +] + +[[package]] +name = "logos-blockchain-circuits-prover" +version = "0.2.1" +dependencies = [ + "logos-blockchain-circuits-utils", + "tempfile", +] + +[[package]] +name = "logos-blockchain-circuits-utils" +version = "0.2.1" +dependencies = [ + "dirs", +] + +[[package]] +name = "logos-blockchain-common-http-client" +version = "0.2.1" +dependencies = [ + "futures", + "hex", + "logos-blockchain-chain-broadcast-service", + "logos-blockchain-chain-service", + "logos-blockchain-core", + "logos-blockchain-groth16", + "logos-blockchain-http-api-common", + "logos-blockchain-key-management-system-keys", + "reqwest", + "serde", + "serde_json", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "logos-blockchain-core" +version = "0.2.1" +dependencies = [ + "ark-ff 0.4.2", + "bincode", + "blake2", + "bytes", + "const-hex", + "divan", + "futures", + "hasher", + "hex", + "logos-blockchain-blend-proofs", + "logos-blockchain-cryptarchia-engine", + "logos-blockchain-groth16", + "logos-blockchain-key-management-system-keys", + "logos-blockchain-poc", + "logos-blockchain-pol", + "logos-blockchain-poseidon2", + "logos-blockchain-utils", + "logos-blockchain-utxotree", + "multiaddr", + "nom 8.0.0", + "num-bigint", + "rand 0.8.5", + "serde", + "serde_json", + "strum 0.27.2", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "logos-blockchain-cryptarchia-engine" +version = "0.2.1" +dependencies = [ + "cfg_eval", + "logos-blockchain-pol", + "logos-blockchain-utils", + "serde", + "serde_with", + "thiserror 1.0.69", + "time", + "tokio", + "tracing", +] + +[[package]] +name = "logos-blockchain-cryptarchia-sync" +version = "0.2.1" +dependencies = [ + "bytes", + "futures", + "libp2p", + "libp2p-stream", + "libp2p-swarm-test", + "logos-blockchain-core", + "logos-blockchain-cryptarchia-engine", + "rand 0.8.5", + "serde", + "serde_with", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "logos-blockchain-groth16" +version = "0.2.1" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-groth16", + "ark-serialize 0.4.2", + "bincode", + "generic-array 1.3.5", + "hex", + "num-bigint", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "logos-blockchain-http-api-common" +version = "0.2.1" +dependencies = [ + "axum", + "logos-blockchain-core", + "logos-blockchain-key-management-system-keys", + "logos-blockchain-tracing", + "pprof", + "serde", + "serde_json", + "serde_with", + "tokio", + "tracing", +] + +[[package]] +name = "logos-blockchain-key-management-system-keys" +version = "0.2.1" +dependencies = [ + "async-trait", + "bytes", + "ed25519-dalek", + "generic-array 1.3.5", + "hex", + "logos-blockchain-groth16", + "logos-blockchain-key-management-system-macros", + "logos-blockchain-poseidon2", + "logos-blockchain-utils", + "logos-blockchain-zksign", + "num-bigint", + "rand_core 0.6.4", + "serde", + "subtle", + "thiserror 2.0.18", + "tokio", + "tracing", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "logos-blockchain-key-management-system-macros" +version = "0.2.1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "logos-blockchain-key-management-system-operators" +version = "0.2.1" +dependencies = [ + "async-trait", + "logos-blockchain-blend-proofs", + "logos-blockchain-core", + "logos-blockchain-groth16", + "logos-blockchain-key-management-system-keys", + "logos-blockchain-poseidon2", + "logos-blockchain-utxotree", + "tokio", + "tracing", +] + +[[package]] +name = "logos-blockchain-key-management-system-service" +version = "0.2.1" +dependencies = [ + "async-trait", + "bytes", + "log", + "logos-blockchain-key-management-system-keys", + "logos-blockchain-key-management-system-operators", + "logos-blockchain-tracing", + "num-bigint", + "overwatch", + "rand 0.8.5", + "serde", + "serde_yaml", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "logos-blockchain-ledger" +version = "0.2.1" +dependencies = [ + "derivative", + "logos-blockchain-blend-crypto", + "logos-blockchain-blend-message", + "logos-blockchain-blend-proofs", + "logos-blockchain-core", + "logos-blockchain-cryptarchia-engine", + "logos-blockchain-groth16", + "logos-blockchain-key-management-system-keys", + "logos-blockchain-pol", + "logos-blockchain-utils", + "logos-blockchain-utxotree", + "num-bigint", + "rand 0.8.5", + "rpds", + "serde", + "serde_arrays", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "logos-blockchain-libp2p" +version = "0.2.1" +dependencies = [ + "async-trait", + "backon", + "blake2", + "either", + "futures", + "hex", + "igd-next 0.16.2", + "libp2p", + "libp2p-swarm-test", + "logos-blockchain-cryptarchia-sync", + "logos-blockchain-utils", + "multiaddr", + "natpmp", + "netdev", + "num_enum", + "rand 0.8.5", + "serde", + "serde_json", + "serde_with", + "thiserror 1.0.69", + "tokio", + "tracing", + "tracing-subscriber 0.3.23", + "zerocopy", +] + +[[package]] +name = "logos-blockchain-network-service" +version = "0.2.1" +dependencies = [ + "async-trait", + "chrono", + "futures", + "logos-blockchain-core", + "logos-blockchain-cryptarchia-sync", + "logos-blockchain-libp2p", + "logos-blockchain-tracing", + "logos-blockchain-utils", + "overwatch", + "rand 0.8.5", + "rand_chacha 0.3.1", + "serde", + "tokio", + "tokio-stream", + "tracing", + "tracing-subscriber 0.3.23", + "utoipa", +] + +[[package]] +name = "logos-blockchain-poc" +version = "0.2.1" +dependencies = [ + "logos-blockchain-circuits-prover", + "logos-blockchain-circuits-utils", + "logos-blockchain-groth16", + "logos-blockchain-witness-generator", + "num-bigint", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "logos-blockchain-pol" +version = "0.2.1" +dependencies = [ + "astro-float", + "logos-blockchain-circuits-prover", + "logos-blockchain-circuits-utils", + "logos-blockchain-groth16", + "logos-blockchain-utils", + "logos-blockchain-witness-generator", + "num-bigint", + "num-traits", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "logos-blockchain-poq" +version = "0.2.1" +dependencies = [ + "logos-blockchain-circuits-prover", + "logos-blockchain-circuits-utils", + "logos-blockchain-groth16", + "logos-blockchain-pol", + "logos-blockchain-utils", + "logos-blockchain-witness-generator", + "num-bigint", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "logos-blockchain-poseidon2" +version = "0.2.1" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ff 0.4.2", + "jf-poseidon2", + "num-bigint", +] + +[[package]] +name = "logos-blockchain-services-utils" +version = "0.2.1" +dependencies = [ + "async-trait", + "futures", + "log", + "overwatch", + "overwatch-derive", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "logos-blockchain-storage-service" +version = "0.2.1" +dependencies = [ + "async-trait", + "bytes", + "futures", + "logos-blockchain-core", + "logos-blockchain-cryptarchia-engine", + "logos-blockchain-tracing", + "overwatch", + "rocksdb", + "serde", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "logos-blockchain-time-service" +version = "0.2.1" +dependencies = [ + "async-trait", + "cfg_eval", + "futures", + "log", + "logos-blockchain-cryptarchia-engine", + "logos-blockchain-tracing", + "logos-blockchain-utils", + "overwatch", + "serde", + "serde_with", + "sntpc", + "thiserror 2.0.18", + "time", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "logos-blockchain-tracing" +version = "0.2.1" +dependencies = [ + "opentelemetry", + "opentelemetry-appender-tracing", + "opentelemetry-http", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "rand 0.8.5", + "serde", + "tokio", + "tracing", + "tracing-appender", + "tracing-gelf", + "tracing-loki", + "tracing-opentelemetry", + "tracing-subscriber 0.3.23", + "url", +] + +[[package]] +name = "logos-blockchain-utils" +version = "0.2.1" +dependencies = [ + "async-trait", + "blake2", + "cipher", + "const-hex", + "futures", + "humantime", + "nistrs", + "overwatch", + "rand 0.8.5", + "rand_chacha 0.3.1", + "serde", + "serde_json", + "serde_with", + "time", + "tokio", +] + +[[package]] +name = "logos-blockchain-utxotree" +version = "0.2.1" +dependencies = [ + "ark-ff 0.4.2", + "logos-blockchain-groth16", + "logos-blockchain-poseidon2", + "num-bigint", + "quickcheck", + "quickcheck_macros", + "rand 0.9.2", + "rpds", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "logos-blockchain-witness-generator" +version = "0.2.1" +dependencies = [ + "tempfile", +] + +[[package]] +name = "logos-blockchain-zksign" +version = "0.2.1" +dependencies = [ + "logos-blockchain-circuits-prover", + "logos-blockchain-circuits-utils", + "logos-blockchain-groth16", + "logos-blockchain-poseidon2", + "logos-blockchain-witness-generator", + "num-bigint", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "logos-blockchain-zone-sdk" +version = "0.2.1" +dependencies = [ + "async-trait", + "futures", + "logos-blockchain-common-http-client", + "logos-blockchain-core", + "logos-blockchain-key-management-system-service", + "num-bigint", + "reqwest", + "rpds", + "serde", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "loki-api" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc38a304f59a03e6efa3876766a48c70a766a93f88341c3fff4212834b8e327" +dependencies = [ + "prost 0.13.5", + "prost-types", +] + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "match-lookup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moka" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "equivalent", + "parking_lot", + "portable-atomic", + "smallvec", + "tagptr", + "uuid", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "multiaddr" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint 0.8.0", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" +dependencies = [ + "base-x", + "base256emoji", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" +dependencies = [ + "core2", + "serde", + "unsigned-varint 0.8.0", +] + +[[package]] +name = "multistream-select" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0df8e5eec2298a62b326ee4f0d7fe1a6b90a09dfcf9df37b38f947a8c42f19" +dependencies = [ + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint 0.7.2", +] + +[[package]] +name = "nalgebra" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "462fffe4002f4f2e1f6a9dcf12cc1a6fc0e15989014efc02a941d3e0f5dc2120" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "rand 0.8.5", + "rand_distr", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "nanosql" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "188fb00ba8dbabe88a5b62df8e0ec58cd4d44a0e5c0aaed857c37ca71e932b40" +dependencies = [ + "chrono", + "nanosql_macros", + "rusqlite", + "thiserror 2.0.18", +] + +[[package]] +name = "nanosql_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2d6e433b9f95614382ba5ba5c17bb1d64643978d0d22e4fe74a6be5a1b35664" +dependencies = [ + "deluxe", + "heck 0.5.0", + "proc-macro2", + "quote", + "sqlparser", + "syn 2.0.117", +] + +[[package]] +name = "natpmp" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77366fa8ce34e2e1322dd97da65f11a62f451bd3daae8be6993c00800f61dd07" +dependencies = [ + "async-trait", + "cc", + "netdev", + "tokio", +] + +[[package]] +name = "netdev" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f901362e84cd407be6f8cd9d3a46bccf09136b095792785401ea7d283c79b91d" +dependencies = [ + "dlopen2", + "ipnet", + "libc", + "netlink-packet-core 0.7.0", + "netlink-packet-route 0.17.1", + "netlink-sys", + "once_cell", + "system-configuration 0.6.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "netlink-packet-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-core" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3463cbb78394cb0141e2c926b93fc2197e473394b761986eca3b9da2c63ae0f4" +dependencies = [ + "paste", +] + +[[package]] +name = "netlink-packet-route" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "libc", + "netlink-packet-core 0.7.0", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ce3636fa715e988114552619582b530481fd5ef176a1e5c1bf024077c2c9445" +dependencies = [ + "bitflags 2.11.0", + "libc", + "log", + "netlink-packet-core 0.8.1", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror 1.0.69", +] + +[[package]] +name = "netlink-proto" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65d130ee111430e47eed7896ea43ca693c387f097dd97376bffafbf25812128" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core 0.8.1", + "netlink-sys", + "thiserror 2.0.18", +] + +[[package]] +name = "netlink-sys" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae" +dependencies = [ + "async-io", + "bytes", + "futures-util", + "libc", + "log", + "tokio", +] + +[[package]] +name = "nimue" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0dc7d3b2b7bd112c0cecf7d6f4f16a174ee7a98e27615b1d08256d0176588f2" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "digest", + "generic-array 0.14.7", + "hex", + "keccak", + "log", + "rand 0.8.5", + "zeroize", +] + +[[package]] +name = "nistrs" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0294ad7360bde48721ae03ec1c2f1ca45c2255cb2d9990285273fb361f47113" +dependencies = [ + "cfg-if", + "libm", + "rayon", + "rustfft", + "statrs", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-graphics", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "opentelemetry" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.18", +] + +[[package]] +name = "opentelemetry-appender-tracing" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6a1ac5ca3accf562b8c306fa8483c85f4390f768185ab775f242f7fe8fdcc2" +dependencies = [ + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "opentelemetry-http" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" +dependencies = [ + "async-trait", + "bytes", + "http 1.4.0", + "opentelemetry", + "reqwest", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" +dependencies = [ + "http 1.4.0", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost 0.14.3", + "reqwest", + "thiserror 2.0.18", + "tokio", + "tonic", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost 0.14.3", + "tonic", + "tonic-prost", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e62e29dfe041afb8ed2a6c9737ab57db4907285d999ef8ad3a59092a36bdc846" + +[[package]] +name = "opentelemetry_sdk" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "opentelemetry", + "percent-encoding", + "rand 0.9.2", + "thiserror 2.0.18", + "tokio", + "tokio-stream", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "overwatch" +version = "0.1.0" +source = "git+https://github.com/logos-co/Overwatch?rev=448c192#448c192895b8311c742b1726a1bb12ee314ad95c" +dependencies = [ + "async-trait", + "futures", + "overwatch-derive", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "overwatch-derive" +version = "0.1.0" +source = "git+https://github.com/logos-co/Overwatch?rev=448c192#448c192895b8311c742b1726a1bb12ee314ad95c" +dependencies = [ + "convert_case", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pin-project-lite" +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 = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "pprof" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38a01da47675efa7673b032bf8efd8214f1917d89685e07e395ab125ea42b187" +dependencies = [ + "aligned-vec", + "backtrace", + "cfg-if", + "criterion", + "findshlibs", + "inferno", + "libc", + "log", + "nix 0.26.4", + "once_cell", + "protobuf", + "protobuf-codegen", + "smallvec", + "spin", + "symbolic-demangle", + "tempfile", + "thiserror 2.0.18", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "primal-check" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" +dependencies = [ + "num-integer", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus-client" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" +dependencies = [ + "dtoa", + "itoa", + "parking_lot", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bitflags 2.11.0", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive 0.13.5", +] + +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive 0.14.3", +] + +[[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 = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost 0.13.5", +] + +[[package]] +name = "protobuf" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-codegen" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace" +dependencies = [ + "anyhow", + "once_cell", + "protobuf", + "protobuf-parse", + "regex", + "tempfile", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-parse" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" +dependencies = [ + "anyhow", + "indexmap", + "log", + "protobuf", + "protobuf-support", + "tempfile", + "thiserror 1.0.69", + "which", +] + +[[package]] +name = "protobuf-support" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" +dependencies = [ + "thiserror 1.0.69", +] + +[[package]] +name = "psm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "pxfm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quick-protobuf-codec" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0580ab32b169745d7a39db2ba969226ca16738931be152a3209b409de2474" +dependencies = [ + "asynchronous-codec", + "bytes", + "quick-protobuf", + "thiserror 1.0.69", + "unsigned-varint 0.8.0", +] + +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quickcheck" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95c589f335db0f6aaa168a7cd27b1fc6920f5e1470c804f814d9cd6e62a0f70b" +dependencies = [ + "rand 0.10.0", +] + +[[package]] +name = "quickcheck_macros" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a28b8493dd664c8b171dd944da82d933f7d456b829bfb236738e1fe06c5ba4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "futures-io", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.6.3", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.3", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "getrandom 0.4.2", + "rand_core 0.10.0", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags 2.11.0", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools 0.13.0", + "lru", + "paste", + "serde", + "strum 0.26.3", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rcgen" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "yasna", +] + +[[package]] +name = "recursive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" +dependencies = [ + "recursive-proc-macro-impl", + "stacker", +] + +[[package]] +name = "recursive-proc-macro-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http 1.4.0", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "resolv-conf" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rocksdb" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddb7af00d2b17dbd07d82c0063e25411959748ff03e8d4f96134c2ff41fce34f" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rpds" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e75f485e819d4d3015e6c0d55d02a4fd3db47c1993d9e603e0361fba2bffb34" +dependencies = [ + "archery", + "serde", +] + +[[package]] +name = "rs-merkle-tree" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3ef170810c387d31b64c0b59734abb0839dac2a8d137909e271bfdec9b1e0" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "byteorder", + "futures", + "light-poseidon", + "quote", + "rand 0.9.2", + "syn 1.0.109", + "thiserror 2.0.18", + "tiny-keccak", + "tokio", +] + +[[package]] +name = "rtnetlink" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b960d5d873a75b5be9761b1e73b146f52dddcd27bac75263f40fba686d4d7b5" +dependencies = [ + "async-global-executor", + "futures-channel", + "futures-util", + "log", + "netlink-packet-core 0.8.1", + "netlink-packet-route 0.28.0", + "netlink-proto", + "netlink-sys", + "nix 0.30.1", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "rusqlite" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c6d5e5acb6f6129fe3f7ba0a7fc77bca1942cb568535e18e7bc40262baf3110" +dependencies = [ + "bitflags 2.11.0", + "chrono", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink 0.10.0", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustfft" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rw-stream-sink" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c9026ff5d2f23da5e45bbc283f156383001bfb09c4e44256d02c1a685fe9a1" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_arrays" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a16b99c5ea4fe3daccd14853ad260ec00ea043b2708d1fd1da3106dcd8d9df" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +dependencies = [ + "base64", + "chrono", + "hex", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simba" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e82063457853d00243beda9952e910b82593e4b07ae9f721b9278a99a0d3d5c" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + +[[package]] +name = "sntpc" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f778a0f82b3cf5d75f858eceee38e84d5292f1d03415e88cc4ec45ca6ba8a2" +dependencies = [ + "cfg-if", + "tokio", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlparser" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66e3b7374ad4a6af849b08b3e7a6eda0edbd82f0fd59b57e22671bf16979899" +dependencies = [ + "log", + "recursive", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stacker" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "statrs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05bdbb8e4e78216a85785a85d3ec3183144f98d0097b9281802c019bb07a6f05" +dependencies = [ + "approx", + "lazy_static", + "nalgebra", + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.117", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "symbolic-common" +version = "12.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ca086c1eb5c7ee74b151ba83c6487d5d33f8c08ad991b86f3f58f6629e68d5" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "12.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa911a28a62823aaf2cc2e074212492a3ee69d0d926cc8f5b12b4a108ff5c0c" +dependencies = [ + "rustc-demangle", + "symbolic-common", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.11.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "test-log" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d53ac171c92a39e4769491c4b4dde7022c60042254b5fc044ae409d34a24d4" +dependencies = [ + "test-log-macros", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "test-log-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be35209fd0781c5401458ab66e4f98accf63553e8fae7425503e92fdd319783b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tonic" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +dependencies = [ + "async-trait", + "base64", + "bytes", + "http 1.4.0", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "sync_wrapper", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-prost" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" +dependencies = [ + "bytes", + "prost 0.14.3", + "tonic", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.11.0", + "bytes", + "futures-util", + "http 1.4.0", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +dependencies = [ + "crossbeam-channel", + "thiserror 2.0.18", + "time", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-gelf" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c0170f1bf67b749d4377c2da1d99d6e722600051ee53870cfb6f618611e29e" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "hostname", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-util", + "tracing-core", + "tracing-futures", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-loki" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3beec919fbdf99d719de8eda6adae3281f8a5b71ae40431f44dc7423053d34" +dependencies = [ + "loki-api", + "reqwest", + "serde", + "serde_json", + "snap", + "tokio", + "tokio-stream", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", + "tracing-subscriber 0.3.23", + "url", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc" +dependencies = [ + "js-sys", + "opentelemetry", + "smallvec", + "tracing", + "tracing-core", + "tracing-subscriber 0.3.23", + "web-time", +] + +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "triomphe" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tui-textarea" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5318dd619ed73c52a9417ad19046724effc1287fb75cdcc4eca1d6ac1acbae" +dependencies = [ + "crossterm", + "ratatui", + "unicode-width 0.2.0", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "unicode-xid" +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 = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" + +[[package]] +name = "unsigned-varint" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "utoipa" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c24e8ab68ff9ee746aad22d39b5535601e6416d1b0feeabf78be986a5c4392" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "uuid" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "value-bag" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +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.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.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.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.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.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.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.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix 1.1.4", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom 7.1.3", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "xmltree" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "yamux" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed0164ae619f2dc144909a9f082187ebb5893693d8c0196e8085283ccd4b776" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot", + "pin-project", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "yamux" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1991f6690292030e31b0144d73f5e8368936c58e45e7068254f7138b23b00672" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot", + "pin-project", + "rand 0.9.2", + "static_assertions", + "web-time", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zxcvbn" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad76e35b00ad53688d6b90c431cabe3cbf51f7a4a154739e04b63004ab1c736c" +dependencies = [ + "chrono", + "derive_builder", + "fancy-regex", + "itertools 0.13.0", + "lazy_static", + "regex", + "time", + "wasm-bindgen", + "web-sys", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6b9cafe --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,254 @@ +[workspace.package] +categories = ["cryptography", "cryptography::cryptocurrencies", "security"] +description = "Logos blockchain workspace crates. For more information please visit https://logos.co/." +edition = "2024" +keywords = ["blockchain", "privacy"] +license = "MIT or Apache-2.0" +readme = "README.md" +repository = "https://github.com/logos-blockchain/logos-blockchain" +version = "0.2.1" + +[workspace] +members = ["common", "sequencer", "indexer"] +resolver = "2" + +[workspace.dependencies] +# Demo crates +demo-sqlite-common = { path = "./common" } + +# logos-blockchain internal crates (paths adjusted for submodule location) +cfgsync-adapter = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-testing.git", rev = "f731791" } +cfgsync-artifacts = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-testing.git", rev = "f731791" } +cfgsync-core = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-testing.git", rev = "f731791" } +common-http-client = { default-features = false, package = "logos-blockchain-common-http-client", path = "./logos-blockchain/nodes/node/http-client" } +lb-api-service = { default-features = false, package = "logos-blockchain-api-service", path = "./logos-blockchain/services/api" } +lb-blend = { default-features = false, package = "logos-blockchain-blend", path = "./logos-blockchain/blend/core" } +lb-blend-crypto = { default-features = false, package = "logos-blockchain-blend-crypto", path = "./logos-blockchain/blend/crypto" } +lb-blend-message = { default-features = false, package = "logos-blockchain-blend-message", path = "./logos-blockchain/blend/message" } +lb-blend-network = { default-features = false, package = "logos-blockchain-blend-network", path = "./logos-blockchain/blend/network" } +lb-blend-proofs = { default-features = false, package = "logos-blockchain-blend-proofs", path = "./logos-blockchain/blend/proofs" } +lb-blend-scheduling = { default-features = false, package = "logos-blockchain-blend-scheduling", path = "./logos-blockchain/blend/scheduling" } +lb-blend-service = { default-features = false, package = "logos-blockchain-blend-service", path = "./logos-blockchain/services/blend" } +lb-cfgsync = { default-features = false, package = "logos-blockchain-cfgsync", path = "./logos-blockchain/testnet/cfgsync" } +lb-chain-broadcast-service = { default-features = false, package = "logos-blockchain-chain-broadcast-service", path = "./logos-blockchain/services/chain/broadcast-service" } +lb-chain-leader-service = { default-features = false, package = "logos-blockchain-chain-leader-service", path = "./logos-blockchain/services/chain/chain-leader" } +lb-chain-network-service = { default-features = false, package = "logos-blockchain-chain-network-service", path = "./logos-blockchain/services/chain/chain-network" } +lb-chain-service = { default-features = false, package = "logos-blockchain-chain-service", path = "./logos-blockchain/services/chain/chain-service" } +lb-chain-service-common = { default-features = false, package = "logos-blockchain-chain-service-common", path = "./logos-blockchain/services/chain/chain-common" } +lb-circuits-prover = { default-features = false, package = "logos-blockchain-circuits-prover", path = "./logos-blockchain/zk/circuits/prover" } +lb-circuits-utils = { default-features = false, package = "logos-blockchain-circuits-utils", path = "./logos-blockchain/zk/circuits/utils" } +lb-circuits-verifier = { default-features = false, package = "logos-blockchain-circuits-verifier", path = "./logos-blockchain/zk/circuits/verifier" } +lb-common-http-client = { default-features = false, package = "logos-blockchain-common-http-client", path = "./logos-blockchain/nodes/node/http-client" } +lb-core = { default-features = false, package = "logos-blockchain-core", path = "./logos-blockchain/core" } +lb-cryptarchia-engine = { default-features = false, package = "logos-blockchain-cryptarchia-engine", path = "./logos-blockchain/consensus/cryptarchia-engine" } +lb-cryptarchia-sync = { default-features = false, package = "logos-blockchain-cryptarchia-sync", path = "./logos-blockchain/consensus/cryptarchia-sync" } +lb-demo-archiver = { default-features = false, package = "logos-blockchain-demo-archiver", path = "./logos-blockchain/testnet/l2-sequencer-archival-demo/archiver" } +lb-demo-sequencer = { default-features = false, package = "logos-blockchain-demo-sequencer", path = "./logos-blockchain/testnet/l2-sequencer-archival-demo/sequencer" } +lb-groth16 = { default-features = false, package = "logos-blockchain-groth16", path = "./logos-blockchain/zk/groth16" } +lb-http-api-common = { default-features = false, package = "logos-blockchain-http-api-common", path = "./logos-blockchain/nodes/api-common" } +lb-key-management-system-keys = { default-features = false, package = "logos-blockchain-key-management-system-keys", path = "./logos-blockchain/kms/keys" } +lb-key-management-system-macros = { default-features = false, package = "logos-blockchain-key-management-system-macros", path = "./logos-blockchain/kms/macros" } +lb-key-management-system-operators = { default-features = false, package = "logos-blockchain-key-management-system-operators", path = "./logos-blockchain/kms/operators" } +lb-key-management-system-service = { default-features = false, package = "logos-blockchain-key-management-system-service", path = "./logos-blockchain/services/key-management-system" } +lb-ledger = { default-features = false, package = "logos-blockchain-ledger", path = "./logos-blockchain/ledger" } +lb-libp2p = { default-features = false, package = "logos-blockchain-libp2p", path = "./logos-blockchain/libp2p" } +lb-mmr = { default-features = false, package = "logos-blockchain-mmr", path = "./logos-blockchain/mmr" } +lb-network-service = { default-features = false, package = "logos-blockchain-network-service", path = "./logos-blockchain/services/network" } +lb-node = { default-features = false, package = "logos-blockchain-node", path = "./logos-blockchain/nodes/node/binary" } +lb-poc = { default-features = false, package = "logos-blockchain-poc", path = "./logos-blockchain/zk/proofs/poc" } +lb-pol = { default-features = false, package = "logos-blockchain-pol", path = "./logos-blockchain/zk/proofs/pol" } +lb-poq = { default-features = false, package = "logos-blockchain-poq", path = "./logos-blockchain/zk/proofs/poq" } +lb-poseidon2 = { default-features = false, package = "logos-blockchain-poseidon2", path = "./logos-blockchain/zk/poseidon2" } +lb-sdp-service = { default-features = false, package = "logos-blockchain-sdp-service", path = "./logos-blockchain/services/sdp" } +lb-services-utils = { default-features = false, package = "logos-blockchain-services-utils", path = "./logos-blockchain/services/utils" } +lb-storage-service = { default-features = false, package = "logos-blockchain-storage-service", path = "./logos-blockchain/services/storage" } +lb-system-sig-service = { default-features = false, package = "logos-blockchain-system-sig-service", path = "./logos-blockchain/services/system-sig" } +lb-testing-framework = { default-features = false, package = "testing_framework", path = "./logos-blockchain/tests/testing_framework" } +lb-tests = { default-features = false, package = "logos-blockchain-tests", path = "./logos-blockchain/tests" } +lb-time-service = { default-features = false, package = "logos-blockchain-time-service", path = "./logos-blockchain/services/time" } +lb-tracing = { default-features = false, package = "logos-blockchain-tracing", path = "./logos-blockchain/tracing" } +lb-tracing-service = { default-features = false, package = "logos-blockchain-tracing-service", path = "./logos-blockchain/services/tracing" } +lb-tx-service = { default-features = false, package = "logos-blockchain-tx-service", path = "./logos-blockchain/services/tx-service" } +lb-utils = { default-features = false, package = "logos-blockchain-utils", path = "./logos-blockchain/utils" } +lb-utxotree = { default-features = false, package = "logos-blockchain-utxotree", path = "./logos-blockchain/utxotree" } +lb-wallet = { default-features = false, package = "logos-blockchain-wallet", path = "./logos-blockchain/wallet" } +lb-wallet-http-client = { default-features = false, package = "logos-blockchain-wallet-http-client", path = "./logos-blockchain/wallet-http-client" } +lb-wallet-service = { default-features = false, package = "logos-blockchain-wallet-service", path = "./logos-blockchain/services/wallet" } +lb-witness-generator = { default-features = false, package = "logos-blockchain-witness-generator", path = "./logos-blockchain/zk/circuits/witness-generator" } +lb-zksign = { default-features = false, package = "logos-blockchain-zksign", path = "./logos-blockchain/zk/proofs/zksign" } +lb-zone-sdk = { default-features = false, package = "logos-blockchain-zone-sdk", path = "./logos-blockchain/zone-sdk" } +lb_network = { default-features = false, package = "logos-blockchain-network-service", path = "./logos-blockchain/services/network" } +testing-framework-core = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-testing.git", rev = "f731791" } +testing-framework-runner-compose = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-testing.git", rev = "f731791" } +testing-framework-runner-k8s = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-testing.git", rev = "f731791" } +testing-framework-runner-local = { default-features = false, git = "https://github.com/logos-blockchain/logos-blockchain-testing.git", rev = "f731791" } + +# External (same versions as logos-blockchain workspace) +async-trait = { default-features = false, version = "0.1" } +blake2 = { default-features = false, version = "0.10" } +bytes = { default-features = false, version = "1.3" } +cached = { default-features = false, version = "0.55.1" } +cfg-if = { default-features = false, version = "1.0.4" } +divan = { default-features = false, version = "0.1" } +ed25519-dalek = { default-features = false, version = "2" } +fork_stream = { default-features = false, version = "0.1.0" } +futures = { default-features = false, version = "0.3.32" } +futures-util = { default-features = false, version = "0.3.32" } +hex = { default-features = false, version = "0.4" } +kube = { default-features = false, features = ["client", "rustls-tls"], version = "0.87" } +libp2p = { default-features = false, version = "0.55" } +libp2p-stream = { default-features = false, version = "0.3.0-alpha" } +log = { default-features = false, version = "0.4" } +overwatch = { default-features = false, git = "https://github.com/logos-co/Overwatch", rev = "448c192" } +overwatch-derive = { default-features = false, git = "https://github.com/logos-co/Overwatch", rev = "448c192" } +rand = { default-features = false, version = "0.8" } +reqwest = { default-features = false, version = "0.12" } +serde = { default-features = false, version = "1.0" } +serde-big-array = { default-features = false, version = "0.5" } +serde_ignored = { default-features = false, version = "0.1" } +serde_json = { default-features = false, version = "1.0" } +serde_with = { default-features = false, version = "3.14.0" } +serde_yaml = { default-features = false, version = "0.9.33" } +subtle = { default-features = false, version = "2.6.1" } +tempfile = { default-features = false, version = "3" } +thiserror = { default-features = false, version = "2.0" } +time = { default-features = false, version = "0.3" } +tokio = { default-features = false, version = "1" } +tokio-stream = { default-features = false, version = "0.1.18" } +tracing = { default-features = false, version = "0.1" } +utoipa = { default-features = false, version = "4.0" } +utoipa-swagger-ui = { default-features = false, version = "7.0" } +uuid = { default-features = false, features = ["v4"], version = "1" } +x25519-dalek = { default-features = false, version = "2" } +zeroize = { default-features = false, version = "1" } + +[workspace.lints.clippy] +cargo = { level = "warn", priority = -1 } +nursery = { level = "warn", priority = -1 } +pedantic = { level = "warn", priority = -1 } +restriction = { level = "warn", priority = -1 } + +multiple_crate_versions = { level = "allow" } +similar_names = { level = "allow" } +absolute_paths = { level = "allow" } +alloc_instead_of_core = { level = "allow" } +arbitrary_source_item_ordering = { level = "allow" } +big_endian_bytes = { level = "allow" } +blanket_clippy_restriction_lints = { level = "allow" } +decimal_literal_representation = { level = "allow" } +default_numeric_fallback = { level = "allow" } +deref_by_slicing = { level = "allow" } +else_if_without_else = { level = "allow" } +exhaustive_enums = { level = "allow" } +exhaustive_structs = { level = "allow" } +exit = { level = "allow" } +expect_used = { level = "allow" } +field_scoped_visibility_modifiers = { level = "allow" } +float_arithmetic = { level = "allow" } +get_unwrap = { level = "allow" } +host_endian_bytes = { level = "allow" } +implicit_return = { level = "allow" } +integer_division_remainder_used = { level = "allow" } +iter_over_hash_type = { level = "allow" } +let_underscore_must_use = { level = "allow" } +let_underscore_untyped = { level = "allow" } +little_endian_bytes = { level = "allow" } +map_err_ignore = { level = "allow" } +min_ident_chars = { level = "allow" } +missing_asserts_for_indexing = { level = "allow" } +missing_docs_in_private_items = { level = "allow" } +missing_inline_in_public_items = { level = "allow" } +missing_trait_methods = { level = "allow" } +mixed_read_write_in_expression = { level = "allow" } +mod_module_files = { level = "allow" } +module_name_repetitions = { level = "allow" } +modulo_arithmetic = { level = "allow" } +panic = { level = "allow" } +panic_in_result_fn = { level = "allow" } +partial_pub_fields = { level = "allow" } +print_stderr = { level = "allow" } +print_stdout = { level = "allow" } +pub_use = { level = "allow" } +pub_with_shorthand = { level = "allow" } +question_mark_used = { level = "allow" } +self_named_module_files = { level = "allow" } +semicolon_inside_block = { level = "allow" } +single_call_fn = { level = "allow" } +single_char_lifetime_names = { level = "allow" } +std_instead_of_alloc = { level = "allow" } +std_instead_of_core = { level = "allow" } +struct_field_names = { level = "allow" } +unseparated_literal_suffix = { level = "allow" } +use_debug = { level = "allow" } +wildcard_enum_match_arm = { level = "allow" } +arithmetic_side_effects = { level = "allow" } +as_conversions = { level = "allow" } +as_pointer_underscore = { level = "allow" } +as_underscore = { level = "allow" } +assertions_on_result_states = { level = "allow" } +cast_possible_truncation = { level = "allow" } +cast_possible_wrap = { level = "allow" } +cast_precision_loss = { level = "allow" } +cast_sign_loss = { level = "allow" } +doc_paragraphs_missing_punctuation = { level = "allow" } +error_impl_error = { level = "allow" } +impl_trait_in_params = { level = "allow" } +indexing_slicing = { level = "allow" } +infinite_loop = { level = "allow" } +integer_division = { level = "allow" } +large_stack_frames = { level = "allow" } +missing_assert_message = { level = "allow" } +missing_errors_doc = { level = "allow" } +missing_panics_doc = { level = "allow" } +pattern_type_mismatch = { level = "allow" } +redundant_test_prefix = { level = "allow" } +ref_patterns = { level = "allow" } +renamed_function_params = { level = "allow" } +same_name_method = { level = "allow" } +shadow_reuse = { level = "allow" } +shadow_same = { level = "allow" } +shadow_unrelated = { level = "allow" } +tests_outside_test_module = { level = "allow" } +todo = { level = "allow" } +unchecked_time_subtraction = { level = "allow" } +unimplemented = { level = "allow" } +unreachable = { level = "allow" } +unwrap_in_result = { level = "allow" } +unwrap_used = { level = "allow" } + +[workspace.lints.rust] +unused_crate_dependencies = { level = "allow" } +unused_results = { level = "allow" } +ambiguous_negative_literals = { level = "warn" } +closure_returning_async_block = { level = "warn" } +deref_into_dyn_supertrait = { level = "warn" } +impl_trait_redundant_captures = { level = "warn" } +let_underscore_drop = { level = "warn" } +macro_use_extern_crate = { level = "warn" } +missing_unsafe_on_extern = { level = "warn" } +redundant_imports = { level = "warn" } +redundant_lifetimes = { level = "warn" } +single_use_lifetimes = { level = "warn" } +tail_expr_drop_order = { level = "warn" } +trivial_numeric_casts = { level = "warn" } +unit_bindings = { level = "warn" } +unsafe_attr_outside_unsafe = { level = "warn" } +unsafe_op_in_unsafe_fn = { level = "warn" } +unstable_features = { level = "warn" } +unused_extern_crates = { level = "warn" } +unused_import_braces = { level = "warn" } +unused_lifetimes = { level = "warn" } +unused_macro_rules = { level = "warn" } +unused_qualifications = { level = "warn" } +absolute_paths_not_starting_with_crate = { level = "allow" } +elided_lifetimes_in_paths = { level = "allow" } +ffi_unwind_calls = { level = "allow" } +impl_trait_overcaptures = { level = "allow" } +linker_messages = { level = "allow" } +missing_copy_implementations = { level = "allow" } +missing_debug_implementations = { level = "allow" } +missing_docs = { level = "allow" } +trivial_casts = { level = "allow" } +unreachable_pub = { level = "allow" } +unsafe_code = { level = "allow" } +variant_size_differences = { level = "allow" } diff --git a/README.md b/README.md index 9eab437..0770e0a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -# Logos SQLite Zone Sequencer and Indexer Demo - TUTORIAL SKELETON - -This directory contains a skeleton implementation of a Sovereign Zone solution using the Logos Blockchain as a simple database server. It is meant to be used in conjunction with the Zone SDK tutorial in the logos-docs repository. +# Logos SQLite Zone Sequencer and Indexer Demo +This directory contains a reference implementation of a Sovereign Zone solution using the Logos Blockchain as a simple database server. ## System Architecture In this demo, the sequencer acts as the primary maintainer of a [Steelsafe password manager](#steelsafe-a-pure-rust-safe-tui-password-manager), with DB updates published to the Logos Blockchain. Other parties, known as indexers, can follow these updates to reconstruct the same database locally as a read-only password manager. @@ -31,7 +30,22 @@ Each component is a standalone service that can be run independently or via Dock * **Rust**: For building the Sequencer and Indexer binaries, if running the helper script. * **Logos Node**: To read from and write to the Logos Blockchain. -### 1. Running the Sequencer +### 1. Configuration + +If you want the program to get information from the environment, copy the example environment file and fill in your information before exporting the variables. + +```bash +cp testnet/sqlite-zone-demo/.env.example-local testnet/sqlite-zone-demo/.env-local +set -a +source testnet/sqlite-zone-demo/.env-local +set +a +``` + +You can also provide these fields via command line arguments (see below). + +In either case, you will need access to a running **Logos Node**, as well as any credentials needed to interact with the node. If you are running a node locally, ensure the `SEQUENCER_NODE_ENDPOINT` and `INDEXER_NODE_ENDPOINT` in your `.env-local` both point to your local node. + +### 2. Running the Sequencer You can run the following file to execute the sequencer directly: `run-local.sh`. @@ -62,9 +76,9 @@ The information in the environment variables can also be provided to the script | `--checkpoint-path ./sequencer.checkpoint` | Path to the checkpoint file for crash recovery. | | `--channel-path ./channel.txt` | Path to the channel ID file (for the indexer to read). | -Running this script should allow you to enter SQL queries into the command line. +Running this script should allow you to interact with the read & write password manager. -### 2. Running the Indexer +### 3. Running the Indexer You can run the following file to execute the sequencer directly: `run-local.sh`. diff --git a/common/Cargo.toml b/common/Cargo.toml new file mode 100644 index 0000000..45c5f4c --- /dev/null +++ b/common/Cargo.toml @@ -0,0 +1,35 @@ +[package] +categories = { workspace = true } +description = "Shared utilities for SQLite zone demo crates" +edition = { workspace = true } +keywords = { workspace = true } +license = { workspace = true } +name = "demo-sqlite-common" +readme = { workspace = true } +repository = { workspace = true } +version = { workspace = true } + +[lints] +workspace = true + +[dependencies] +arboard = "3.4.1" +argon2 = { features = ["std", "zeroize"], version = "0.5" } +block-padding = { features = ["std"], version = "0.3" } +chacha20poly1305 = { features = ["std"], version = "0.10" } +chrono = { features = ["serde"], version = "0.4" } +crypto-common = { features = ["std"], version = "0.1.6" } +directories = "6.0" +logos-blockchain-zone-sdk = { path = "../logos-blockchain/zone-sdk" } +nanosql = { features = ["chrono"], version = "0.10.0" } +rand = { version = "0.9" } +ratatui = { features = ["serde"], version = "0.29" } +serde = { features = ["derive"], workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } +tracing-subscriber = { features = ["env-filter"], version = "0.3" } +zeroize = { features = ["alloc"], workspace = true } + +[dev-dependencies] +rand = { version = "0.9" } +zxcvbn = "3" diff --git a/common/src/config.rs b/common/src/config.rs new file mode 100644 index 0000000..1ae9fa8 --- /dev/null +++ b/common/src/config.rs @@ -0,0 +1,134 @@ +//! Configures the environment of the application: color themes, database path, +//! etc. + +use std::{fs::File, io::ErrorKind, path::Path}; + +use directories::{ProjectDirs, UserDirs}; +use ratatui::style::{Color, Style}; +use serde::Deserialize; + +use crate::error::{Error, Result, ResultExt as _}; + +/// Configures the environment of the application. +#[derive(Clone, Default, Debug, Deserialize)] +pub struct Config { + /// Colors and other TUI style settings. + #[serde(default)] + pub theme: Theme, +} + +impl Config { + /// Reads the config from the `.steelsaferc` file if it exists. + /// Otherwise, returns the default configuration. + /// + /// The config is first searched at the [permanent config directory][1], + /// and then under `$HOME` + /// + /// If the file exists but it contains syntax errors, an error is returned. + pub fn from_rc_file() -> Result { + // First, search in the config directory + if let Ok(project_dirs) = Self::project_dirs() { + let config_path = project_dirs.config_dir().join(".steelsaferc"); + if let Some(config_file) = Self::open_file_if_exists(&config_path)? { + // do NOT silently ignore JSON syntax/semantic errors! + return serde_json::from_reader(config_file).context("Invalid .steelsaferc"); + } + } + + // If not found, search in $HOME + if let Some(user_dirs) = UserDirs::new() { + let config_path = user_dirs.home_dir().join(".steelsaferc"); + if let Some(config_file) = Self::open_file_if_exists(&config_path)? { + return serde_json::from_reader(config_file).context("Invalid .steelsaferc"); + } + } + + // not found anywhere, return the built-in default config + Ok(Self::default()) + } + + fn project_dirs() -> Result { + ProjectDirs::from("org", "h2co3", "steelsafe").ok_or(Error::MissingDatabaseDir) + } + + fn open_file_if_exists(path: &Path) -> Result> { + match File::open(path) { + Ok(file) => Ok(Some(file)), + Err(error) => { + if [ErrorKind::NotFound, ErrorKind::PermissionDenied].contains(&error.kind()) { + Ok(None) + } else { + Err(Error::context(error, "Found .steelsaferc but cannot open")) + } + } + } + } +} + +/// A pair of background and foreground colors. +#[derive(Clone, Default, Debug, Deserialize)] +pub struct ColorPair { + /// The background color. + #[serde(default)] + pub bg: Option, + /// The foreground color. + #[serde(default)] + pub fg: Option, +} + +/// Colors and other TUI style settings. +#[derive(Clone, Default, Debug, Deserialize)] +pub struct Theme { + /// The default colors, for general content/text. + #[serde(default)] + pub default: ColorPair, + /// Colors for important content. + #[serde(default)] + pub highlight: ColorPair, + /// Colors for block/box borders. + #[serde(default)] + pub border: ColorPair, + /// Colors for block/box borders around important content. + #[serde(default)] + pub border_highlight: ColorPair, + /// Text and border colors for error reporting. + #[serde(default)] + pub error: ColorPair, +} + +impl Theme { + #[must_use] + pub fn default(&self) -> Style { + Style::default() + .bg(self.default.bg.unwrap_or(Color::Black)) + .fg(self.default.fg.unwrap_or(Color::LightYellow)) + } + + #[must_use] + pub fn highlight(&self) -> Style { + Style::default() + .bg(self.highlight.bg.unwrap_or(Color::LightYellow)) + .fg(self.highlight.fg.unwrap_or(Color::Black)) + } + + #[must_use] + pub fn border(&self) -> Style { + Style::default() + .bg(self.border.bg.unwrap_or(Color::Black)) + .fg(self.border.fg.unwrap_or(Color::LightCyan)) + } + + #[must_use] + pub fn border_highlight(&self) -> Style { + Style::default() + .bg(self.border_highlight.bg.unwrap_or(Color::LightYellow)) + .fg(self.border_highlight.fg.unwrap_or(Color::Cyan)) + } + + #[must_use] + pub fn error(&self) -> Style { + Style::default() + .bg(self.error.bg.unwrap_or(Color::LightYellow)) + .fg(self.error.fg.unwrap_or(Color::LightRed)) + } +} diff --git a/common/src/crypto.rs b/common/src/crypto.rs new file mode 100644 index 0000000..fac271d --- /dev/null +++ b/common/src/crypto.rs @@ -0,0 +1,440 @@ +//! Key derivation, encryption, and authentication. + +use std::iter; + +use argon2::Argon2; +/// The length of the per-item password salt, in bytes. +pub use argon2::RECOMMENDED_SALT_LEN; +use block_padding::{Iso7816, RawPadding as _}; +use chacha20poly1305::{ + KeyInit as _, XChaCha20Poly1305, + aead::{Aead as _, KeySizeUser, Payload}, +}; +use chrono::{DateTime, Utc}; +use crypto_common::typenum::Unsigned as _; +use rand::seq::IndexedRandom as _; +use serde::Serialize; +use zeroize::Zeroizing; + +use crate::error::Result; + +/// The length of the per-item authentication nonce, in bytes. +pub const NONCE_LEN: usize = 24; + +/// The length of the padding block size, in bytes. The plaintext secret will be +/// padded before encryption, so that its length is a multiple of this block +/// size. +pub const PADDING_BLOCK_SIZE: usize = 256; + +/// The set of characters that will be sampled for generating a strong, random +/// password. +/// +/// These are ASCII-only letters, digits, and printable punctuation characters +/// easily available on a US English keyboard and should readily be accepted by +/// most systems. +pub const PASSWORD_CHARSET: &[u8] = + b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,;:!?-+*/%=_@#$^&~()[]{}"; + +/// The length of randomly generated passwords. +/// +/// This provides `log_2(87^40)` ~= 257 bits of entropy, or just over 32 bytes, +/// requiring around 10^77 guesses on average using brute force. +/// This should satisfy even the most stringent requirements. +pub const PASSWORD_LEN: usize = 40; + +/// The pieces of data that are not encrypted but still validated using the +/// specified encryption password, for tamper detection. +/// +/// Fields are in alphabetical order, so that round-tripping through `Value` +/// results in bitwise-identical JSON. (This is a precautionary measure.) +#[derive(Clone, Copy, Debug, Serialize)] +struct AdditionalData<'a> { + account: Option<&'a str>, + label: &'a str, + last_modified_at: DateTime, +} + +/// The result of encrypting and authenticating the secret, and authenticating +/// the additional data, using the specified password. +/// +/// The salt for the Key +/// Derivation Function and the nonce for the authentication are generated +/// _inside_ the encryption function, so that the API ensures fresh, +/// cryptographically strong random values, so accidental re-use is prevented. +/// This means that the encryption function needs to return these as well. +#[derive(Clone, Debug)] +pub struct EncryptionOutput { + /// The already-encrypted and authenticated secret. + pub encrypted_secret: Vec, + /// The randomly-generated salt, used for seeding the KDF. + pub kdf_salt: [u8; RECOMMENDED_SALT_LEN], + /// The randomly-generated nonce, used for initializing the AEAD hash. + pub auth_nonce: [u8; NONCE_LEN], +} + +/// The plain old data input for encryption, except for the password. +#[derive(Clone, Copy, Debug)] +pub struct EncryptionInput<'a> { + pub plaintext_secret: &'a [u8], + pub label: &'a str, + pub account: Option<&'a str>, + pub last_modified_at: DateTime, +} + +impl EncryptionInput<'_> { + /// Encrypts and authenticates the secret, and authenticates the additional + /// data, using a key derived from the `encryption_password`. + pub fn encrypt_and_authenticate(self, encryption_password: &[u8]) -> Result { + // Pad the secret to a multiple of the block size. + // Directly extending the String could re-allocate, which would leave + // the contents of the old allocation in the memory, without zeroizing it. + // To prevent this, what we do instead is pre-allocate a buffer of the + // required size, then copy the secret over, and perform the padding in + // the new buffer. + let unpadded_secret = self.plaintext_secret; + let total_len = (unpadded_secret.len() / PADDING_BLOCK_SIZE + 1) * PADDING_BLOCK_SIZE; + let mut padded_secret = Zeroizing::new(vec![0x00u8; total_len]); + + padded_secret[..unpadded_secret.len()].copy_from_slice(unpadded_secret); + Iso7816::raw_pad(padded_secret.as_mut_slice(), unpadded_secret.len()); + + // Create the additional authenticated data. + let additional_data = AdditionalData { + account: self.account, + label: self.label, + last_modified_at: self.last_modified_at, + }; + let additional_data_str = serde_json::to_string(&additional_data)?; + + // Generate random salt and nonce. `rand::random()` uses a CSPRNG. + let kdf_salt: [u8; RECOMMENDED_SALT_LEN] = rand::random(); + let auth_nonce: [u8; NONCE_LEN] = rand::random(); + + // Create KDF context. + // This uses recommended parameters (19 MB memory, 2 rounds, 1 degree of + // parallelism). + let hasher = Argon2::default(); + + // The actual encryption key is cleared (overwritten with all 0s) upon drop. + let mut key = Zeroizing::new([0u8; ::KeySize::USIZE]); + hasher.hash_password_into(encryption_password, &kdf_salt, &mut *key)?; + + // Create encryption and authentication context. + let aead = XChaCha20Poly1305::new_from_slice(key.as_slice())?; + + // Actually perform the encryption and authentication. + let payload = Payload { + msg: padded_secret.as_slice(), + aad: additional_data_str.as_bytes(), + }; + let encrypted_secret = aead.encrypt(<_>::from(&auth_nonce), payload)?; + + Ok(EncryptionOutput { + encrypted_secret, + kdf_salt, + auth_nonce, + }) + } +} + +/// Plain old data input for decrypting and verifying the secret, and +/// verifying the authenticity of the non-encrypted additional data. +#[derive(Clone, Copy, Debug)] +pub struct DecryptionInput<'a> { + pub encrypted_secret: &'a [u8], + pub kdf_salt: [u8; RECOMMENDED_SALT_LEN], + pub auth_nonce: [u8; NONCE_LEN], + pub label: &'a str, + pub account: Option<&'a str>, + pub last_modified_at: DateTime, +} + +impl DecryptionInput<'_> { + /// Decrypts and verifies the secret, and verifies the additional data, + /// using a key derived from the `decryption_password`. + pub fn decrypt_and_verify(self, decryption_password: &[u8]) -> Result>> { + // Re-create the additional authenticated data. This helps detect when + // the displayed label or account have been tampered with in the database. + // This **must** be bitwise identical to the data used during encryption. + let additional_data = AdditionalData { + account: self.account, + label: self.label, + last_modified_at: self.last_modified_at, + }; + let additional_data_str = serde_json::to_string(&additional_data)?; + + // Create KDF context. + // This MUST use the same parameters as hashing during encryption. + let hasher = Argon2::default(); + + // The actual encryption key is cleared (overwritten with all 0s) upon drop. + let mut key = Zeroizing::new([0u8; ::KeySize::USIZE]); + hasher.hash_password_into(decryption_password, &self.kdf_salt, &mut *key)?; + + // Create decryption and verification context. + let aead = XChaCha20Poly1305::new_from_slice(key.as_slice())?; + + // Actually perform the decryption and verification. + let payload = Payload { + msg: self.encrypted_secret, + aad: additional_data_str.as_bytes(), + }; + let plaintext_secret = aead.decrypt(<_>::from(&self.auth_nonce), payload)?; + let mut plaintext_secret = Zeroizing::new(plaintext_secret); + + // Un-pad the decrypted plaintext + let unpadded_len = Iso7816::raw_unpad(plaintext_secret.as_slice())?.len(); + plaintext_secret.truncate(unpadded_len); + + Ok(plaintext_secret) + } +} + +/// Randomly generates a cryptographically strong (unpredictable) password. +pub fn generate_password() -> Zeroizing { + // `rng()` returns a CSPRNG. + let mut rng = rand::rng(); + + iter::from_fn(|| PASSWORD_CHARSET.choose(&mut rng)) + .copied() + .map(char::from) + .take(PASSWORD_LEN) + .collect::() + .into() +} + +#[cfg(test)] +mod tests { + use chrono::{Days, Utc}; + use rand::{ + Rng as _, RngCore as _, + distr::{SampleString as _, StandardUniform}, + }; + use zxcvbn::{Score, zxcvbn}; + + use super::{DecryptionInput, EncryptionInput, PADDING_BLOCK_SIZE, PASSWORD_LEN}; + use crate::error::{Error, Result}; + + #[test] + fn correct_encryption_and_decryption_succeeds() -> Result<()> { + let timestamp = Utc::now(); + let mut rng = rand::rng(); + let p0 = vec![]; // empty payload edge case + let mut p1 = vec![0u8; PADDING_BLOCK_SIZE - 1]; + let mut p2 = vec![0u8; PADDING_BLOCK_SIZE]; + let mut p3 = vec![0u8; PADDING_BLOCK_SIZE + 1]; + + rng.fill_bytes(&mut p1); + rng.fill_bytes(&mut p2); + rng.fill_bytes(&mut p3); + + for payload in [p0, p1, p2, p3] { + let password_len: usize = rng.random_range(8..64); + let password = StandardUniform.sample_string(&mut rng, password_len); + let encryption_input = EncryptionInput { + plaintext_secret: payload.as_slice(), + label: "the precise label does not matter", + account: Some("my uninteresting account name"), + last_modified_at: timestamp, + }; + + let output = encryption_input.encrypt_and_authenticate(password.as_bytes())?; + let decryption_input = DecryptionInput { + encrypted_secret: output.encrypted_secret.as_slice(), + kdf_salt: output.kdf_salt, + auth_nonce: output.auth_nonce, + label: encryption_input.label, + account: encryption_input.account, + last_modified_at: timestamp, + }; + let decrypted_secret = decryption_input.decrypt_and_verify(password.as_bytes())?; + + assert_eq!(decrypted_secret.as_slice(), payload.as_slice()); + } + + Ok(()) + } + + #[test] + fn incorrect_password_fails_decryption() -> Result<()> { + let timestamp = Utc::now(); + let mut rng = rand::rng(); + let p0 = vec![]; // empty payload edge case + let mut p1 = vec![0u8; PADDING_BLOCK_SIZE - 1]; + let mut p2 = vec![0u8; PADDING_BLOCK_SIZE]; + let mut p3 = vec![0u8; PADDING_BLOCK_SIZE + 1]; + + rng.fill_bytes(&mut p1); + rng.fill_bytes(&mut p2); + rng.fill_bytes(&mut p3); + + for payload in [p0, p1, p2, p3] { + let password_len: usize = rng.random_range(8..64); + let password = StandardUniform.sample_string(&mut rng, password_len); + let encryption_input = EncryptionInput { + plaintext_secret: payload.as_slice(), + label: "the precise label does not matter", + account: Some("my uninteresting account name"), + last_modified_at: timestamp, + }; + + let output = encryption_input.encrypt_and_authenticate(password.as_bytes())?; + let decryption_input = DecryptionInput { + encrypted_secret: output.encrypted_secret.as_slice(), + kdf_salt: output.kdf_salt, + auth_nonce: output.auth_nonce, + label: encryption_input.label, + account: encryption_input.account, + last_modified_at: timestamp, + }; + + let wrong_password = b"this is NOT the right password!"; + let result = decryption_input.decrypt_and_verify(wrong_password); + + assert!( + matches!( + result, + Err(Error::XChaCha20Poly1305(chacha20poly1305::Error)) + ), + "unexpected result: {result:#?}", + ); + } + + Ok(()) + } + + #[test] + #[expect(clippy::allow_attributes_without_reason)] + #[expect(clippy::string_slice)] + fn altered_additional_data_fails_verification() -> Result<()> { + let timestamp = Utc::now(); + let mut rng = rand::rng(); + let p0 = vec![]; // empty payload edge case + let mut p1 = vec![0u8; PADDING_BLOCK_SIZE - 1]; + let mut p2 = vec![0u8; PADDING_BLOCK_SIZE]; + let mut p3 = vec![0u8; PADDING_BLOCK_SIZE + 1]; + + rng.fill_bytes(&mut p1); + rng.fill_bytes(&mut p2); + rng.fill_bytes(&mut p3); + + for payload in [p0, p1, p2, p3] { + let password_len: usize = rng.random_range(8..64); + let password = StandardUniform.sample_string(&mut rng, password_len); + let encryption_input = EncryptionInput { + plaintext_secret: payload.as_slice(), + label: "the precise label does not matter", + account: Some("my uninteresting account name"), + last_modified_at: timestamp, + }; + + let output = encryption_input.encrypt_and_authenticate(password.as_bytes())?; + + // Case #1: the account is altered (None instead of Some) + { + let decryption_input = DecryptionInput { + encrypted_secret: output.encrypted_secret.as_slice(), + kdf_salt: output.kdf_salt, + auth_nonce: output.auth_nonce, + label: encryption_input.label, + account: None, + last_modified_at: timestamp, + }; + + let result = decryption_input.decrypt_and_verify(password.as_bytes()); + + assert!( + matches!( + result, + Err(Error::XChaCha20Poly1305(chacha20poly1305::Error)) + ), + "unexpected result: {result:#?}", + ); + }; + + // Case #2: the label is (slightly) altered + { + let decryption_input = DecryptionInput { + encrypted_secret: output.encrypted_secret.as_slice(), + kdf_salt: output.kdf_salt, + auth_nonce: output.auth_nonce, + label: &encryption_input.label[1..], + account: encryption_input.account, + last_modified_at: timestamp, + }; + + let result = decryption_input.decrypt_and_verify(password.as_bytes()); + + assert!( + matches!( + result, + Err(Error::XChaCha20Poly1305(chacha20poly1305::Error)) + ), + "unexpected result: {result:#?}", + ); + }; + + // Case #2: the last modification date is tampered with + { + let decryption_input = DecryptionInput { + encrypted_secret: output.encrypted_secret.as_slice(), + kdf_salt: output.kdf_salt, + auth_nonce: output.auth_nonce, + label: encryption_input.label, + account: encryption_input.account, + last_modified_at: timestamp.checked_sub_days(Days::new(1)).unwrap(), + }; + + let result = decryption_input.decrypt_and_verify(password.as_bytes()); + + assert!( + matches!( + result, + Err(Error::XChaCha20Poly1305(chacha20poly1305::Error)) + ), + "unexpected result: {result:#?}", + ); + } + } + + Ok(()) + } + + #[test] + fn generated_password_is_strong() { + for _ in 0..1024 { + let password = super::generate_password(); + + assert_eq!(password.len(), PASSWORD_LEN); + + let has_lower = password.chars().any(|c| c.is_ascii_lowercase()); + let has_upper = password.chars().any(|c| c.is_ascii_uppercase()); + let has_digit = password.chars().any(|c| c.is_ascii_digit()); + let has_punct = password.chars().any(|c| c.is_ascii_punctuation()); + + let char_class_count = u32::from(has_lower) + + u32::from(has_upper) + + u32::from(has_digit) + + u32::from(has_punct); + + // Digits are not always found because their probability is relatively low, + // so assert that at least a reasonable variety of characters is exhibited. + assert!(char_class_count >= 3); + + // Ensure that all characters are from the specified set. + assert!(password.chars().all(|c| { + !c.is_ascii_control() + && (c.is_ascii_lowercase() + || c.is_ascii_uppercase() + || c.is_ascii_digit() + || c.is_ascii_punctuation()) + })); + + // Evaluate password using the `zxcvbn` algorithm. It should never get anything + // but the maximal score, and it should not trigger any warnings/suggestions. + let entropy = zxcvbn(password.as_str(), &[]); + assert_eq!(entropy.score(), Score::Four); + assert!(entropy.feedback().is_none()); + } + } +} diff --git a/common/src/error.rs b/common/src/error.rs new file mode 100644 index 0000000..dbc2819 --- /dev/null +++ b/common/src/error.rs @@ -0,0 +1,135 @@ +//! Errors and results specific to Steelsafe. + +use std::{ + error::Error as StdError, + fmt::{self, Debug, Display, Formatter}, + io::Error as IoError, + str::Utf8Error, +}; + +use arboard::Error as ClipboardError; +use argon2::Error as Argon2Error; +use block_padding::UnpadError; +use chacha20poly1305::Error as XChaCha20Poly1305Error; +use crypto_common::InvalidLength; +use logos_blockchain_zone_sdk::indexer::Error as ZoneIndexerError; +use nanosql::{Error as SqlError, rusqlite::Error as RusqliteError}; +use serde_json::Error as JsonError; +use thiserror::Error; + +use crate::error::Error::Context; + +#[derive(Error)] +pub enum Error { + #[error("Can't re-open screen guard while one is already open")] + ScreenAlreadyOpen, + + #[error("Can't find database directory")] + MissingDatabaseDir, + + #[error("Label is required and must be a single line")] + LabelRequired, + + #[error("Secret is required")] + SecretRequired, + + #[error("Encryption (master) password is required and must be a single line")] + EncryptionPasswordRequired, + + #[error("Passwords do not match")] + ConfirmPasswordMismatch, + + #[error("Account name must be a single line if specified")] + AccountNameSingleLine, + + #[error("No item is currently selected")] + SelectionRequired, + + #[error("I/O error: {0}")] + Io(#[from] IoError), + + #[error("Secret is not valid UTF-8: {0}")] + Utf8(#[from] Utf8Error), + + #[error("JSON error: {0}")] + Json(#[from] JsonError), + + #[error("Database error: {0}")] + Db(#[from] SqlError), + + #[error("Rusqlite Database error: {0}")] + Sqlite(#[from] RusqliteError), + + #[error("Database schema version too high: need <= {expected}, got {actual}")] + SchemaVersionMismatch { expected: i64, actual: i64 }, + + #[error("Password hashing error: {0}")] + Argon2(#[from] Argon2Error), + + #[error("Encryption, decryption, or authentication error")] + XChaCha20Poly1305(#[from] XChaCha20Poly1305Error), + + #[error("Invalid padding in decrypted secret")] + Unpad(#[from] UnpadError), + + #[error(transparent)] + InvalidLength(#[from] InvalidLength), + + #[error(transparent)] + Clipboard(#[from] ClipboardError), + + #[error("{0}")] + InvalidChannelId(String), + + #[error("URL parse error: {0}")] + Url(String), + + #[error(transparent)] + ZoneIndexer(#[from] ZoneIndexerError), + + #[error("{message}: {source}")] + Context { + message: String, + #[source] + source: Box, + }, +} + +impl Error { + pub fn context(source: E, message: M) -> Self + where + E: StdError + Send + Sync + 'static, + M: Into, + { + Context { + message: message.into(), + source: Box::new(source), + } + } +} + +impl Debug for Error { + fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(self, formatter) + } +} + +pub type Result = core::result::Result; + +pub trait ResultExt { + fn context(self, message: M) -> Result + where + M: Into; +} + +impl ResultExt for Result +where + E: StdError + Send + Sync + 'static, +{ + fn context(self, message: M) -> Result + where + M: Into, + { + self.map_err(|error| Error::context(error, message)) + } +} diff --git a/common/src/lib.rs b/common/src/lib.rs new file mode 100644 index 0000000..3d47c15 --- /dev/null +++ b/common/src/lib.rs @@ -0,0 +1,7 @@ +#![forbid(unsafe_code)] + +pub mod config; +pub mod crypto; +pub mod error; +pub mod logging; +pub mod screen; diff --git a/common/src/logging.rs b/common/src/logging.rs new file mode 100644 index 0000000..65bdc84 --- /dev/null +++ b/common/src/logging.rs @@ -0,0 +1,51 @@ +//! Logging utilities for TUI applications running in raw terminal mode. + +use ratatui::crossterm::terminal; + +/// A writer that confines log output to the bottom half of the terminal. +/// +/// Before each write it: +/// 1. Sets the DECSTBM scroll region to the bottom half, so that any scroll +/// caused by new lines never displaces the top half. +/// 2. Moves the cursor to the last row of that region, so the newest line +/// always appears at the bottom and older lines scroll upward within the +/// region. +/// 3. Converts bare `\n` to `\r\n` for correct rendering in raw mode. +#[derive(Clone, Copy)] +pub struct RawModeWriter; + +impl std::io::Write for RawModeWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let mut out = std::io::stdout().lock(); + + let (_, height) = terminal::size().unwrap_or((80, 24)); + let top_row = height / 2 + 1; // 1-based: first row of bottom half + let bottom_row = height; // 1-based: last row of terminal + + // Set scroll region to the bottom half, then move cursor to its last row. + // Any subsequent \n will scroll only within [top_row, bottom_row]. + write!(out, "\x1b[{top_row};{bottom_row}r\x1b[{bottom_row};1H")?; + + let mut start = 0; + for i in 0..buf.len() { + if buf[i] == b'\n' && (i == 0 || buf[i - 1] != b'\r') { + out.write_all(&buf[start..i])?; + out.write_all(b"\r\n")?; + start = i + 1; + } + } + out.write_all(&buf[start..])?; + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + std::io::stdout().flush() + } +} + +impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for RawModeWriter { + type Writer = Self; + fn make_writer(&'a self) -> Self::Writer { + *self + } +} diff --git a/common/src/screen.rs b/common/src/screen.rs new file mode 100644 index 0000000..4d0c457 --- /dev/null +++ b/common/src/screen.rs @@ -0,0 +1,105 @@ +//! A guard object that makes sure the screen and terminal mode +//! is always restored, even when an error or panic occurs. + +use std::{ + io::{self, Stdout}, + ops::{Deref, DerefMut}, + sync::atomic::{AtomicBool, Ordering}, +}; + +use ratatui::{ + Terminal, + backend::CrosstermBackend, + crossterm::{ + ExecutableCommand as _, + event::{DisableMouseCapture, EnableMouseCapture}, + terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}, + }, +}; + +use crate::error::{Error, Result}; + +static IS_OPEN: AtomicBool = AtomicBool::new(false); + +#[derive(Debug)] +pub struct ScreenGuard { + terminal: Terminal>, +} + +impl ScreenGuard { + pub fn open() -> Result { + let mut result = Err(Error::ScreenAlreadyOpen); + + // only set the flag to true if we successfully acquired the terminal + let _ = IS_OPEN.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |flag| { + if flag { + result = Err(Error::ScreenAlreadyOpen); + return None; + } + + if let Err(error) = terminal::enable_raw_mode() { + result = Err(error.into()); + return None; + } + + if let Err(error) = io::stdout().execute(EnterAlternateScreen) { + result = Err(error.into()); + return None; + } + + if let Err(error) = io::stdout().execute(EnableMouseCapture) { + result = Err(error.into()); + return None; + } + + match Terminal::new(CrosstermBackend::new(io::stdout())) { + Ok(terminal) => { + result = Ok(Self { terminal }); + Some(true) + } + Err(error) => { + result = Err(error.into()); + None + } + } + }); + + result + } + + /* + pub fn close(mut self) -> Result<()> { + self.finalize() + } + */ + + fn finalize() -> Result<()> { + terminal::disable_raw_mode()?; + io::stdout().execute(DisableMouseCapture)?; + io::stdout().execute(LeaveAlternateScreen)?; + IS_OPEN.store(false, Ordering::SeqCst); + Ok(()) + } +} + +impl Deref for ScreenGuard { + type Target = Terminal>; + + fn deref(&self) -> &Self::Target { + &self.terminal + } +} + +impl DerefMut for ScreenGuard { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.terminal + } +} + +impl Drop for ScreenGuard { + fn drop(&mut self) { + if let Err(error) = Self::finalize() { + eprintln!("Error restoring terminal: {error:#}"); + } + } +} diff --git a/indexer/Cargo.toml b/indexer/Cargo.toml new file mode 100644 index 0000000..21a849e --- /dev/null +++ b/indexer/Cargo.toml @@ -0,0 +1,42 @@ +[package] +categories = { workspace = true } +description = "Sqlite zone indexer" +edition = { workspace = true } +keywords = { workspace = true } +license = { workspace = true } +name = "demo-sqlite-indexer" +readme = { workspace = true } +repository = { workspace = true } +version = { workspace = true } + +[lints] +workspace = true + +[dependencies] +arboard = "3.4.1" +chrono = { features = ["serde"], version = "0.4" } +clap = { features = ["derive", "env", "std"], version = "4" } +demo-sqlite-common = { workspace = true } +futures = { default-features = false, version = "0.3" } +hex = "0.4" +lb-common-http-client = { workspace = true } +lb-core = { workspace = true } +logos-blockchain-zone-sdk = { path = "../logos-blockchain/zone-sdk" } +nanosql = { features = ["chrono"], version = "0.10.0" } +rusqlite = { features = ["bundled"], version = "0.33" } +ratatui = { features = ["serde"], version = "0.29" } +reqwest = { features = ["json", "rustls-tls"], workspace = true } +tokio = { default-features = false, features = [ + "io-std", + "io-util", + "macros", + "net", + "rt-multi-thread", + "signal", + "sync", + "time", +], version = "1" } +tracing = { workspace = true } +tracing-subscriber = { features = ["env-filter"], version = "0.3" } +tui-textarea = "0.7" +zeroize = { features = ["alloc"], workspace = true } diff --git a/indexer/src/.steelsaferc b/indexer/src/.steelsaferc new file mode 100644 index 0000000..0cb1da3 --- /dev/null +++ b/indexer/src/.steelsaferc @@ -0,0 +1,24 @@ +{ + "theme": { + "default": { + "bg": "black", + "fg": "yellow" + }, + "highlight": { + "bg": "gray", + "fg": "black" + }, + "border": { + "bg": "black", + "fg": "bright_yellow" + }, + "border_highlight": { + "bg": "gray", + "fg": "dark_gray" + }, + "error": { + "bg": "red", + "fg": "white" + } + } +} diff --git a/indexer/src/db.rs b/indexer/src/db.rs new file mode 100644 index 0000000..34e0a74 --- /dev/null +++ b/indexer/src/db.rs @@ -0,0 +1,201 @@ +//! Describes and implements the password database. + +use std::{fs, path::Path}; + +use chrono::{DateTime, Utc}; +use nanosql::{ + AsSqlTy, Connection, ConnectionExt as _, FromSql, InsertInput, Null, Param, ResultRecord, + Table, ToSql, Value, +}; + +use crate::{ + crypto::{NONCE_LEN, RECOMMENDED_SALT_LEN}, + error::{Error, Result}, +}; + +/// The current version of the database schema. +const SCHEMA_VERSION: i64 = 1; + +/// Handle for the secrets database. +#[derive(Debug)] +pub struct DatabaseReadOnly { + connection: Connection, +} + +impl DatabaseReadOnly { + /// Opens the database at the specified path. + pub fn open(path: &str) -> Result { + if let Some(parent) = Path::new(path).parent() { + fs::create_dir_all(parent)?; + } + let mut connection = Connection::connect(path)?; + + connection.create_table::()?; + connection.create_table::()?; + + let schema_version = Self::schema_version(&connection)?; + + if SCHEMA_VERSION < schema_version { + return Err(Error::SchemaVersionMismatch { + expected: SCHEMA_VERSION, + actual: schema_version, + }); + } + + Ok(Self { connection }) + } + + pub fn execute_batch(&self, sql: &str) -> Result<()> { + self.connection.execute_batch(sql).map_err(Into::into) + } + + /// Retrieves the schema version of the database. + /// If the schema version was not yet set (because the database was just + /// created), then the schema version of the currently-running steelsafe + /// process will be inserted (and returned). + fn schema_version(connection: &Connection) -> nanosql::Result { + // If the schema version is not yet stored in the DB, then insert it. + // Otherwise, leave the existing version (ignore the insertion). + // We do not use a transaction, because we would need to commit the + // insert first in order to reliably read back the inserted value + // anyway. So, we just check if the insert succeeded, and if it did, + // simply return the current version -- this also ensures atomicity. + let metadata = Metadata { + key: MetadataKey::SchemaVersion, + value: Value::Integer(SCHEMA_VERSION), + }; + if connection.insert_or_ignore_one(metadata)?.is_some() { + Ok(SCHEMA_VERSION) + } else { + Self::metadata_by_key(connection, MetadataKey::SchemaVersion) + } + } + + fn metadata_by_key( + connection: &Connection, + key: MetadataKey, + ) -> nanosql::Result { + let Metadata { ref value, .. } = connection.select_by_key(key)?; + let value = T::column_result(value.into())?; + Ok(value) + } + + /// Returns the list of items in the database. + /// + /// The returned data is human-readable: it contains fields such as the + /// identifying name/label/title of the entry, the optional account + /// information, and the date of creation/last modification. It does not + /// return binary data such as the encrypted secret, the KDF salt, or + /// the authentication nonce. + /// + /// If the `search_term` is `None`, then all items are returned. + /// + /// If the `search_term` is `Some(_)`, then only items matching the search + /// term will be returned. The search term is interpreted as an SQL + /// `LIKE` pattern. The pattern will be matched against the label and + /// the account name, and entries matching either will be returned. + pub fn list_items_for_display(&self, search_term: Option<&str>) -> Result> { + self.connection + .compile_invoke(ListItemsForDisplay, search_term) + .map_err(Into::into) + } + + /// Retrieves a full item from the database based on its unique ID (primary + /// key). This includes encryption and authentication data: the + /// encrypted secret, the KDF salt, and the authentication nonce. + pub fn item_by_id(&self, id: u64) -> Result { + self.connection.select_by_key(id).map_err(Into::into) + } +} + +/// Describes a secret item. +#[derive(Clone, PartialEq, Eq, Debug, Table, ResultRecord)] +#[nanosql(insert_input_ty = AddItemInput<'p>)] +pub struct Item { + /// Unique identifier of the item. + #[nanosql(pk)] + pub uid: u64, + /// Human-readable identifier of the item. + #[nanosql(unique)] + pub label: String, + /// Username, email address, etc. for identification. `None` if not + /// applicable. + pub account: Option, + /// Last modification date of the item. If never modified, this is the + /// creation date. + pub last_modified_at: DateTime, + /// The encrypted and authenticated password data. + /// Also contains a copy of the other fields for the purpose of tamper + /// protection. + pub encrypted_secret: Vec, + /// The salt for the key derivation function. + /// + /// This is `UNIQUE`, acting as an additional line of defense against + /// salt re-use, which would result in two users with the same password + /// and salt getting identical encryption keys. + #[nanosql(unique)] + pub kdf_salt: [u8; RECOMMENDED_SALT_LEN], + /// The nonce for the authentication function. + /// + /// This is `UNIQUE`, acting as an additional line of defense against + /// nonce re-use, which would allow breaking encryption/authentication. + #[nanosql(unique)] + pub auth_nonce: [u8; NONCE_LEN], +} + +/// Used for adding an encrypted secret item to the database. +#[derive(Clone, Param, InsertInput)] +#[nanosql(table = Item)] +pub struct AddItemInput<'p> { + /// inserting a `NULL` into an `INTEGER PRIMARY KEY` auto-generates the PK + pub uid: Null, + pub label: &'p str, + pub account: Option<&'p str>, + pub last_modified_at: DateTime, + pub encrypted_secret: &'p [u8], + pub kdf_salt: [u8; RECOMMENDED_SALT_LEN], + pub auth_nonce: [u8; NONCE_LEN], +} + +/// Human-readable subset (projection) of the `Item` table. +/// Does not contain the secret or the encryption details (salt/nonce). +#[derive(Clone, Debug, ResultRecord)] +pub struct DisplayItem { + pub uid: u64, + pub label: String, + pub account: Option, + pub last_modified_at: DateTime, +} + +/// Internal technical bookkeeping data (e.g., database version). +#[derive(Clone, Debug, Table, Param, ResultRecord)] +struct Metadata { + #[nanosql(pk)] + key: MetadataKey, + value: Value, +} + +/// The kinds of metadata stored in the database. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, AsSqlTy, ToSql, FromSql, Param, ResultRecord)] +#[nanosql(rename_all = "lower_snake_case")] +enum MetadataKey { + /// The version of the database schema that determines its format. + SchemaVersion, +} + +nanosql::define_query! { + /// The optional parameter is a search/filter term. It works with SQLite `LIKE` syntax. + /// If not provided, no filtering will be performed, and all items will be returned. + ListItemsForDisplay<'p>: Option<&'p str> => Vec { + r#" + SELECT + "item"."uid" AS "uid", + "item"."label" AS "label", + "item"."account" AS "account", + "item"."last_modified_at" AS "last_modified_at" + FROM "item" + WHERE ?1 IS NULL OR "item"."label" LIKE ?1 OR "item"."account" LIKE ?1 + ORDER BY "item"."uid"; + "# + } +} diff --git a/indexer/src/indexer.rs b/indexer/src/indexer.rs new file mode 100644 index 0000000..54acf0d --- /dev/null +++ b/indexer/src/indexer.rs @@ -0,0 +1,120 @@ +use std::fs; + +use futures::StreamExt as _; +use lb_common_http_client::{BasicAuthCredentials, CommonHttpClient}; +use lb_core::mantle::ops::channel::ChannelId; +use logos_blockchain_zone_sdk::adapter::NodeHttpClient; +use logos_blockchain_zone_sdk::indexer::ZoneIndexer; +use reqwest::Url; +use tracing::{error, info}; + +use crate::{db::DatabaseReadOnly, error::Error}; + +pub type Result = std::result::Result; + +pub struct Indexer { + zone_indexer: ZoneIndexer, + db_path: String, +} + +fn parse_channel_id(channel_id_str: &str) -> Result { + let decoded = hex::decode(channel_id_str).map_err(|_| { + Error::InvalidChannelId(format!( + "INDEXER_CHANNEL_ID must be a valid hex string, got: '{channel_id_str}'" + )) + })?; + let channel_bytes: [u8; 32] = decoded.try_into().map_err(|v: Vec| { + Error::InvalidChannelId(format!( + "INDEXER_CHANNEL_ID must be exactly 64 hex characters (32 bytes), got {} characters ({} bytes)", + v.len() * 2, + v.len() + )) + })?; + Ok(ChannelId::from(channel_bytes)) +} + +impl Indexer { + pub fn new( + db_path: &str, + node_endpoint: &str, + channel_path: &str, + node_auth_username: Option, + node_auth_password: Option, + ) -> Result { + let node_url = Url::parse(node_endpoint).map_err(|e| Error::Url(e.to_string()))?; + + let basic_auth = node_auth_username + .map(|username| BasicAuthCredentials::new(username, node_auth_password)); + + let channel_id_str = fs::read_to_string(channel_path).map_err(|e| { + Error::InvalidChannelId(format!("Failed to read channel path '{channel_path}': {e}")) + })?; + let channel_id = parse_channel_id(channel_id_str.trim())?; + + info!("Channel ID: {}", hex::encode(channel_id.as_ref())); + + let node = NodeHttpClient::new(CommonHttpClient::new(basic_auth), node_url); + let zone_indexer = ZoneIndexer::new(channel_id, node); + + Ok(Self { zone_indexer, db_path: db_path.to_owned() }) + } + + pub async fn run(&self) { + let db = match DatabaseReadOnly::open(&self.db_path) { + Ok(db) => db, + Err(e) => { + error!("Failed to open database: {e}"); + return; + } + }; + + loop { + info!("Connecting to zone block stream..."); + let stream = match self.zone_indexer.follow().await { + Ok(s) => s, + Err(e) => { + error!("Failed to connect to block stream: {e}"); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + continue; + } + }; + info!("Connected to zone block stream"); + + futures::pin_mut!(stream); + while let Some(zone_msg) = stream.next().await { + let logos_blockchain_zone_sdk::ZoneMessage::Block(zone_block) = zone_msg else { + continue; + }; + let sql_text = match String::from_utf8(zone_block.data) { + Ok(s) => s, + Err(e) => { + error!("Zone block data is not valid UTF-8: {e}"); + continue; + } + }; + + let statements: Vec<&str> = sql_text + .lines() + .map(|l| l.trim().trim_end_matches(';').trim()) + .filter(|s| !s.is_empty()) + .collect(); + + if statements.is_empty() { + continue; + } + + info!("Applying {} SQL statement(s)", statements.len()); + + for stmt in &statements { + if let Err(e) = db.execute_batch(stmt) { + error!("Failed to execute SQL '{}': {e}", stmt); + } + } + info!("Applied {} statement(s)", statements.len()); + } + + error!("Zone block stream ended, reconnecting..."); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + } + } +} diff --git a/indexer/src/lib.rs b/indexer/src/lib.rs new file mode 100644 index 0000000..8d155c3 --- /dev/null +++ b/indexer/src/lib.rs @@ -0,0 +1,112 @@ +#![forbid(unsafe_code)] +#![allow(clippy::allow_attributes_without_reason)] + +pub mod db; +pub mod indexer; +pub use demo_sqlite_common::{config, crypto, error, screen}; + +mod tui; + +use clap::Parser; +use demo_sqlite_common::logging::RawModeWriter; +use indexer::Indexer; +use tracing::{error, info}; +use tracing_subscriber::{EnvFilter, layer::SubscriberExt as _, util::SubscriberInitExt as _}; + +#[derive(Parser, Debug)] +#[command(about = "SQLite zone indexer - replay zone blocks into a local SQLite database")] +pub struct IndexerArgs { + /// Logos blockchain node HTTP endpoint + #[arg( + long, + default_value = "http://localhost:8080", + env = "INDEXER_NODE_ENDPOINT" + )] + pub node_url: String, + + /// Path to the `SQLite` database file + #[arg(long, default_value = "./data/indexer.db", env = "INDEXER_DB_PATH")] + pub db_path: String, + + /// Path to the channel ID file + #[arg(long, default_value = "./data/channel.txt")] + channel_path: String, + + /// Basic auth username for node endpoint + #[arg(long, env = "INDEXER_NODE_AUTH_USERNAME")] + pub node_auth_username: Option, + + /// Basic auth password for node endpoint + #[arg(long, env = "INDEXER_NODE_AUTH_PASSWORD")] + pub node_auth_password: Option, +} + +use crate::{config::Config, db::DatabaseReadOnly, error::Result, screen::ScreenGuard, tui::State}; + +#[derive(Debug)] +struct App { + screen: ScreenGuard, + state: State, +} + +impl App { + fn new(state: State) -> Result { + Ok(Self { + screen: ScreenGuard::open()?, + state, + }) + } + + /// The main run loop. + fn run_app(mut self) -> Result<()> { + while self.state.is_running() { + self.screen.draw(|frame| self.state.draw(frame))?; + self.state.handle_events(); + } + + Ok(()) + } +} + +#[expect(clippy::unused_async)] +pub async fn run(args: IndexerArgs) -> Result<()> { + tracing_subscriber::registry() + .with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"))) + .with(tracing_subscriber::fmt::layer().with_writer(RawModeWriter)) + .init(); + + info!("Sqlite Indexer starting up..."); + info!(" Logos blockchain Node: {}", args.node_url); + + let db = DatabaseReadOnly::open(&args.db_path)?; + + let indexer = match Indexer::new( + &args.db_path, + &args.node_url, + &args.channel_path, + args.node_auth_username, + args.node_auth_password, + ) { + Ok(i) => i, + Err(e) => { + error!("Indexer initialization failed: {e}"); + std::process::exit(1); + } + }; + info!("Indexer ready"); + + std::thread::spawn(move || { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("failed to build tokio runtime for indexer"); + rt.block_on(indexer.run()); + }); + info!("Background indexer started"); + + let config = Config::from_rc_file()?; + let state = State::new(db, config.theme)?; + let app = App::new(state)?; + + app.run_app() +} diff --git a/indexer/src/main.rs b/indexer/src/main.rs new file mode 100644 index 0000000..90f6707 --- /dev/null +++ b/indexer/src/main.rs @@ -0,0 +1,8 @@ +use clap::Parser as _; +use demo_sqlite_indexer::{IndexerArgs, run}; + +#[tokio::main] +async fn main() { + let args = IndexerArgs::parse(); + drop(run(args).await); +} diff --git a/indexer/src/tui.rs b/indexer/src/tui.rs new file mode 100644 index 0000000..02a54cc --- /dev/null +++ b/indexer/src/tui.rs @@ -0,0 +1,522 @@ +#![allow(clippy::allow_attributes_without_reason)] + +//! The bulk of the actual user interface logic. + +use std::{ + fmt::{self, Debug, Formatter}, + ops::{ControlFlow, Deref, DerefMut}, + time::Duration, +}; + +use arboard::Clipboard; +use ratatui::{ + Frame, + crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers, MouseEventKind}, + layout::{Constraint, Margin, Rect}, + style::Modifier, + widgets::{ + Clear, Paragraph, Row, Table, TableState, + block::{Block, BorderType}, + }, +}; +use tui_textarea::TextArea; +use zeroize::Zeroizing; + +use crate::{ + config::Theme, + crypto::DecryptionInput, + db::{DatabaseReadOnly, DisplayItem}, + error::{Error, Result}, +}; + +/// The top-level UI state, the basis of rendering. +#[derive(Debug)] +pub struct State { + db: DatabaseReadOnly, + clipboard: ClipboardDebugWrapper, + theme: Theme, + is_running: bool, + passwd_entry: Option, + find: Option, + popup_error: Option, + items: Vec, + table_state: TableState, +} + +impl State { + pub fn new(db: DatabaseReadOnly, theme: Theme) -> Result { + let items = db.list_items_for_display(None)?; + let clipboard = ClipboardDebugWrapper(Clipboard::new()?); + + let table_state = + TableState::new().with_selected(if items.is_empty() { None } else { Some(0) }); + + Ok(Self { + db, + clipboard, + theme, + is_running: true, + passwd_entry: None, + find: None, + popup_error: None, + items, + table_state, + }) + } + + /// Returns `true` as long as the application should run. + /// Once this returns `false`, the application will exit. + pub const fn is_running(&self) -> bool { + self.is_running + } + + /// Top-level widget rendering. + pub fn draw(&mut self, frame: &mut Frame) { + let half_screen = { + let full = frame.area(); + Rect { + height: full.height / 2, + ..full + } + }; + let bottom_input_height = 3; + let mut table_area = { + let mut area = half_screen; + area.height -= bottom_input_height; + area + }; + let bottom_input_area = Rect { + x: table_area.x, + y: table_area.y + table_area.height, + width: table_area.width, + height: bottom_input_height, + }; + let table = self.main_table(); + + if let Some(passwd_entry) = self.passwd_entry.as_mut() { + frame.render_widget(&passwd_entry.enc_pass, bottom_input_area); + } else if let Some(find_state) = self.find.as_mut() { + frame.render_widget(&find_state.search_term, bottom_input_area); + } else { + table_area = half_screen; + } + + frame.render_stateful_widget(table, table_area, &mut self.table_state); + + if let Some(error) = self.popup_error.as_ref() { + let margin = Margin { + horizontal: half_screen.width.saturating_sub(72 + 2) / 2, + vertical: half_screen.height.saturating_sub(3 + 2) / 2, + }; + let dialog_area = half_screen.inner(margin); + let modal = self.error_modal(error); + + frame.render_widget(Clear, dialog_area); + frame.render_widget(modal, dialog_area); + } + } + + fn main_table(&self) -> Table<'static> { + Table::new( + self.items.iter().map(|item| { + Row::new([ + item.label.clone(), + item.account.clone().unwrap_or_default(), + item.last_modified_at.format("%F %T").to_string(), + ]) + }), + [ + Constraint::Percentage(40), + Constraint::Percentage(40), + Constraint::Min(24), + ], + ) + .header( + Row::new(["Title", "Username or account", "Modified at (UTC)"]) + .style(self.theme.default().add_modifier(Modifier::BOLD)), + ) + .row_highlight_style(Modifier::REVERSED) + .block( + Block::bordered() + .title(format!( + " SteelSafe v{} (read-only) ", + env!("CARGO_PKG_VERSION") + )) + .title_bottom(" [C]opy secret ") + .title_bottom(" [F]ind ") + .title_bottom(" [1] First ") + .title_bottom(" [0] Last ") + .title_bottom(" [R]efresh ") + .title_bottom(" [Q]uit ") + .border_type(BorderType::Rounded) + .border_style(if self.main_table_has_focus() { + self.theme.border().add_modifier(Modifier::BOLD) + } else { + self.theme.border() + }), + ) + .style(self.theme.default()) + } + + fn error_modal(&self, error: &Error) -> Paragraph<'static> { + let block = Block::bordered() + .title(" Error ") + .title_bottom(" Close ") + .border_type(BorderType::Rounded) + .border_style(self.theme.error().add_modifier(Modifier::BOLD)); + + Paragraph::new(format!("\n{error}\n")) + .centered() + .block(block) + .style(self.theme.error()) + } + + /// Event polling and error handling. + pub fn handle_events(&mut self) { + if let Err(error) = self.handle_events_impl() { + self.popup_error = Some(error); + } + } + + /// The bulk of the actual event handling logic. + fn handle_events_impl(&mut self) -> Result<()> { + if !event::poll(Duration::from_millis(50))? { + return Ok(()); + } + let event = event::read()?; + + let event = match self.handle_error_input(event)? { + ControlFlow::Break(()) => return Ok(()), + ControlFlow::Continue(event) => event, + }; + let event = match self.handle_passwd_entry_input(event)? { + ControlFlow::Break(()) => return Ok(()), + ControlFlow::Continue(event) => event, + }; + let event = match self.handle_find_input(event)? { + ControlFlow::Break(()) => return Ok(()), + ControlFlow::Continue(event) => event, + }; + + self.handle_main_table_event(&event) + } + + /// Handles events when the main table has focus. + fn handle_main_table_event(&mut self, event: &Event) -> Result<()> { + if let Event::Mouse(mouse) = event { + match mouse.kind { + MouseEventKind::ScrollDown => { + self.table_state.select_next(); + } + MouseEventKind::ScrollUp => { + self.table_state.select_previous(); + } + _ => {} + } + return Ok(()); + } + + let Event::Key(key) = event else { + return Ok(()); + }; + + if key.kind != KeyEventKind::Press { + return Ok(()); + } + + match key.code { + KeyCode::Up | KeyCode::Char('k' | 'K') => { + self.table_state.select_previous(); + } + KeyCode::Down | KeyCode::Tab | KeyCode::Char('j' | 'J') => { + self.table_state.select_next(); + } + KeyCode::Char('1') => { + self.table_state.select_first(); + } + KeyCode::Char('0') => { + self.table_state.select_last(); + } + KeyCode::Char('r' | 'R') => { + self.sync_data(true)?; + } + KeyCode::Char('c' | 'C') | KeyCode::Enter => { + self.passwd_entry = Some(PasswordEntryState::with_theme(self.theme.clone())); + } + KeyCode::Char('f' | 'F' | '/') => { + // if we are already in find mode, do NOT reset + // the search term, just give back focus. + if let Some(find_state) = self.find.as_mut() { + find_state.set_focus(true); + } else { + self.find = Some(FindItemState::with_theme(self.theme.clone())); + } + } + KeyCode::Char('q' | 'Q') => { + self.is_running = false; + } + _ => {} + } + + Ok(()) + } + + /// Handles events when the error modal is open. + #[expect(clippy::unnecessary_wraps)] + fn handle_error_input(&mut self, event: Event) -> Result> { + if self.popup_error.is_none() { + return Ok(ControlFlow::Continue(event)); + } + + if let Event::Key(evt) = event + && evt.code == KeyCode::Esc + { + self.popup_error = None; + } + + Ok(ControlFlow::Break(())) + } + + /// Handles events for the password entry panel before decrypting a secret. + fn handle_passwd_entry_input(&mut self, event: Event) -> Result> { + let Some(passwd_entry) = self.passwd_entry.as_mut() else { + return Ok(ControlFlow::Continue(event)); + }; + + match event { + Event::Key(evt) => match evt.code { + KeyCode::Esc => { + self.passwd_entry = None; + } + KeyCode::Enter => { + let password = Zeroizing::new(passwd_entry.enc_pass.lines().join("\n")); + self.passwd_entry = None; + self.copy_secret_to_clipboard(&password)?; + } + KeyCode::Char('h' | 'H') if evt.modifiers.contains(KeyModifiers::CONTROL) => { + passwd_entry.toggle_show_enc_pass(); + } + _ => { + passwd_entry.enc_pass.input(event); + } + }, + _ => { + passwd_entry.enc_pass.input(event); + } + } + + Ok(ControlFlow::Break(())) + } + + /// Handles events for the Find panel. + fn handle_find_input(&mut self, event: Event) -> Result> { + let Some(find_state) = self.find.as_mut() else { + return Ok(ControlFlow::Continue(event)); + }; + + match event { + Event::Key(evt) => match evt.code { + KeyCode::Esc => { + self.find = None; + self.sync_data(true)?; + Ok(ControlFlow::Break(())) + } + KeyCode::Enter if find_state.has_focus => { + find_state.set_focus(false); + Ok(ControlFlow::Break(())) + } + _ if find_state.has_focus => { + find_state.search_term.input(event); + self.sync_data(true)?; + Ok(ControlFlow::Break(())) + } + _ => Ok(ControlFlow::Continue(event)), + }, + _ => Ok(ControlFlow::Continue(event)), + } + } + + /// Reloads the contents of the database from disk to memory. + /// If `adjust_selection` is set, the last item of the table + /// will be selected. This is useful after certain operations + /// that act destructively on the table state (e.g., search). + fn sync_data(&mut self, adjust_selection: bool) -> Result<()> { + let search_term = self.find.as_ref().and_then(|find_state| { + find_state + .search_term + .lines() + .first() + .map(|line| format!("%{}%", line.trim())) + }); + self.items = self.db.list_items_for_display(search_term.as_deref())?; + + #[expect(unused_parens)] + if (adjust_selection + && !self.items.is_empty() + && self + .table_state + .selected() + .is_none_or(|idx| idx >= self.items.len())) + { + self.table_state.select_last(); + } + + Ok(()) + } + + /// Actually copy the decrypted plaintext secret to the clipboard. + /// We can't zeroize the clipboard content, so we don't even bother. + fn copy_secret_to_clipboard(&mut self, enc_pass: &str) -> Result<()> { + let index = self + .table_state + .selected() + .ok_or(Error::SelectionRequired)?; + let uid = self.items[index].uid; + let item = self.db.item_by_id(uid)?; + + let input = DecryptionInput { + encrypted_secret: &item.encrypted_secret, + kdf_salt: item.kdf_salt, + auth_nonce: item.auth_nonce, + label: item.label.as_str(), + account: item.account.as_deref(), + last_modified_at: item.last_modified_at, + }; + let plaintext_secret = input.decrypt_and_verify(enc_pass.as_bytes())?; + + // we do NOT use `String::from_utf8()`, because that would copy the + // bytes, and complicate correct zeroization of the secret on error. + let secret_str = std::str::from_utf8(&plaintext_secret)?; + + self.clipboard.set_text(secret_str).map_err(Into::into) + } + + /// The main table has focus when none of the other widgets do. + fn main_table_has_focus(&self) -> bool { + (self.find.is_none() || self.find.as_ref().is_some_and(|find| !find.has_focus)) + && self.passwd_entry.is_none() + && self.popup_error.is_none() + } +} + +#[derive(Debug)] +struct PasswordEntryState { + is_visible: bool, + enc_pass: TextArea<'static>, + theme: Theme, +} + +impl PasswordEntryState { + fn with_theme(theme: Theme) -> Self { + let mut enc_pass = TextArea::default(); + enc_pass.set_style(theme.default()); + + // set up text field style + let mut state = Self { + is_visible: false, + enc_pass, + theme, + }; + state.set_visible(false); + state + } + + fn toggle_show_enc_pass(&mut self) { + self.set_visible(!self.is_visible); + } + + fn set_visible(&mut self, is_visible: bool) { + self.is_visible = is_visible; + + if self.is_visible { + self.enc_pass.clear_mask_char(); + } else { + self.enc_pass.set_mask_char('\u{25cf}'); + } + + let show_hide_title = format!( + " <^H> {} password ", + if self.is_visible { "Hide" } else { "Show" }, + ); + + self.enc_pass.set_block( + Block::bordered() + .title(" Enter decryption (master) password ") + .title_bottom(" OK ") + .title_bottom(" Cancel ") + .title_bottom(show_hide_title) + .border_type(BorderType::Rounded) + .border_style(self.theme.border().add_modifier(Modifier::BOLD)), + ); + } +} + +#[derive(Debug)] +struct FindItemState { + search_term: TextArea<'static>, + has_focus: bool, + theme: Theme, +} + +impl FindItemState { + fn with_theme(theme: Theme) -> Self { + let mut search_term = TextArea::default(); + + search_term.set_block( + Block::bordered() + .title(" Search term ") + .title_bottom(" Focus secrets ") + .title_bottom(" Exit search ") + .border_type(BorderType::Rounded), + ); + + let mut state = Self { + search_term, + has_focus: true, + theme, + }; + state.set_focus(true); + state + } + + fn set_focus(&mut self, has_focus: bool) { + self.has_focus = has_focus; + + let block = self.search_term.block().cloned().unwrap_or_default(); + + if self.has_focus { + self.search_term + .set_style(self.theme.default().add_modifier(Modifier::BOLD)); + self.search_term + .set_block(block.border_style(self.theme.border().add_modifier(Modifier::BOLD))); + } else { + self.search_term.set_style(self.theme.default()); + self.search_term + .set_block(block.border_style(self.theme.border())); + } + } +} + +/// The sole purpose of this is to implement `Debug` so that it doesn't break +/// literally everything. +struct ClipboardDebugWrapper(Clipboard); + +impl Debug for ClipboardDebugWrapper { + fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { + formatter.debug_struct("Clipboard").finish_non_exhaustive() + } +} + +impl Deref for ClipboardDebugWrapper { + type Target = Clipboard; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ClipboardDebugWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/logos-blockchain b/logos-blockchain index a40f26e..379d647 160000 --- a/logos-blockchain +++ b/logos-blockchain @@ -1 +1 @@ -Subproject commit a40f26eb40efb750a7b0411a9359e2f3945dfe90 +Subproject commit 379d6475fbba60978b62feb0f08b9b310d05e18b diff --git a/run-local.sh b/run-local.sh index a74da61..05eb71f 100755 --- a/run-local.sh +++ b/run-local.sh @@ -24,6 +24,9 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/logos-blockchain" && pwd)" DATA_DIR="$SCRIPT_DIR/data" +# Will be overwritten by env file if set there +BUILD_DIR="$SCRIPT_DIR" + # Colors RED='\033[0;31m' GREEN='\033[0;32m' @@ -90,7 +93,7 @@ while [[ $# -gt 0 ]]; do CHECKPOINT_PATH="$2" shift 2 ;; - --sequencer-node-endpoint) + --indexer-node-endpoint) INDEXER_NODE_ENDPOINT="$2" shift 2 ;; @@ -151,18 +154,18 @@ mkdir -p "$DATA_DIR" LOCAL_IP=$(ipconfig getifaddr en0 2>/dev/null || hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost") # Check if binaries exist, if not build them -SEQUENCER_BIN="$REPO_ROOT/target/release/demo-sqlite-sequencer" -INDEXER_BIN="$REPO_ROOT/target/release/demo-sqlite-indexer" +SEQUENCER_BIN="$BUILD_DIR/target/release/demo-sqlite-sequencer" +INDEXER_BIN="$BUILD_DIR/target/release/demo-sqlite-indexer" if [[ "$SERVICE" == "sequencer" ]]; then echo -e "${YELLOW}Building sequencer...${NC}" - cd "$REPO_ROOT" + cd "$SCRIPT_DIR" cargo build --release -p demo-sqlite-sequencer fi if [[ "$SERVICE" == "indexer" ]]; then echo -e "${YELLOW}Building indexer...${NC}" - cd "$REPO_ROOT" + cd "$SCRIPT_DIR" cargo build --release -p demo-sqlite-indexer fi diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..95e32ea --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.94.0" +components = ["clippy"] diff --git a/sequencer/Cargo.toml b/sequencer/Cargo.toml new file mode 100644 index 0000000..93d8056 --- /dev/null +++ b/sequencer/Cargo.toml @@ -0,0 +1,45 @@ +[package] +categories = { workspace = true } +description = "Sqlite sequencer" +edition = { workspace = true } +keywords = { workspace = true } +license = { workspace = true } +name = "demo-sqlite-sequencer" +readme = { workspace = true } +repository = { workspace = true } +version = { workspace = true } + +[lints] +workspace = true + +[dependencies] +arboard = "3.4.1" +chrono = { features = ["serde"], version = "0.4" } +clap = { features = ["derive", "env", "std"], version = "4" } +demo-sqlite-common = { workspace = true } +fs2 = "0.4" +hex = "0.4" +lb-common-http-client = { workspace = true } +lb-core = { workspace = true } +lb-key-management-system-service = { workspace = true } +logos-blockchain-zone-sdk = { path = "../logos-blockchain/zone-sdk" } +nanosql = { features = ["chrono"], version = "0.10.0" } +rand = { version = "0.9" } +ratatui = { features = ["serde"], version = "0.29" } +reqwest = { features = ["json", "rustls-tls"], workspace = true } +rusqlite = { features = ["bundled", "trace"], version = "0.33.0" } +serde_json = { workspace = true } +thiserror = { workspace = true } +tokio = { default-features = false, features = [ + "io-std", + "io-util", + "macros", + "net", + "rt-multi-thread", + "signal", + "sync", +], version = "1" } +tracing = { workspace = true } +tracing-subscriber = { features = ["env-filter"], version = "0.3" } +tui-textarea = "0.7" +zeroize = { features = ["alloc"], workspace = true } diff --git a/sequencer/src/.steelsaferc b/sequencer/src/.steelsaferc new file mode 100644 index 0000000..0cb1da3 --- /dev/null +++ b/sequencer/src/.steelsaferc @@ -0,0 +1,24 @@ +{ + "theme": { + "default": { + "bg": "black", + "fg": "yellow" + }, + "highlight": { + "bg": "gray", + "fg": "black" + }, + "border": { + "bg": "black", + "fg": "bright_yellow" + }, + "border_highlight": { + "bg": "gray", + "fg": "dark_gray" + }, + "error": { + "bg": "red", + "fg": "white" + } + } +} diff --git a/sequencer/src/db.rs b/sequencer/src/db.rs new file mode 100644 index 0000000..df5b600 --- /dev/null +++ b/sequencer/src/db.rs @@ -0,0 +1,335 @@ +//! Describes and implements the password database. + +use std::{fs, fs::File, io::Write as _, path::Path, sync::Mutex}; + +use chrono::{DateTime, Utc}; +use nanosql::{ + AsSqlTy, Connection, ConnectionExt as _, FromSql, InsertInput, Null, Param, ResultRecord, + Table, ToSql, Value, + rusqlite::trace::{TraceEvent, TraceEventCodes}, +}; + +use crate::{ + crypto::{NONCE_LEN, RECOMMENDED_SALT_LEN}, + error::{Error, Result}, +}; + +/// The current version of the database schema. +const SCHEMA_VERSION: i64 = 1; + +static TRACE_FILE: Mutex> = Mutex::new(None); + +/// Handle for the secrets database. +#[derive(Debug)] +pub struct Database { + connection: Connection, +} + +impl Database { + /// Opens the database at the specified path. + pub fn open(path: &str, queue_path: &str) -> Result { + if let Some(parent) = Path::new(queue_path).parent() { + fs::create_dir_all(parent)?; + } + let queue_file = fs::OpenOptions::new() + .create(true) + .append(true) + .open(queue_path)?; + *TRACE_FILE.lock().unwrap() = Some(queue_file); + + if let Some(parent) = Path::new(path).parent() { + fs::create_dir_all(parent)?; + } + let mut connection = Connection::connect(path)?; + + connection.create_table::()?; + connection.create_table::()?; + + let schema_version = Self::schema_version(&connection)?; + + if SCHEMA_VERSION < schema_version { + return Err(Error::SchemaVersionMismatch { + expected: SCHEMA_VERSION, + actual: schema_version, + }); + } + + connection.trace_v2(TraceEventCodes::SQLITE_TRACE_STMT, Some(Self::trace_fn)); + + Ok(Self { connection }) + } + + fn trace_fn(event: TraceEvent<'_>) { + if let TraceEvent::Stmt(stmt, _) = event + && let Some(sql) = stmt.expanded_sql() + { + if sql.trim_start().to_uppercase().starts_with("SELECT") { + return; + } + if let Ok(mut guard) = TRACE_FILE.lock() + && let Some(file) = guard.as_mut() + { + let normalized = sql.split_whitespace().collect::>().join(" "); + let _unused = writeln!(file, "{normalized}"); + } + } + } + + /// Retrieves the schema version of the database. + /// If the schema version was not yet set (because the database was just + /// created), then the schema version of the currently-running steelsafe + /// process will be inserted (and returned). + fn schema_version(connection: &Connection) -> nanosql::Result { + // If the schema version is not yet stored in the DB, then insert it. + // Otherwise, leave the existing version (ignore the insertion). + // We do not use a transaction, because we would need to commit the + // insert first in order to reliably read back the inserted value + // anyway. So, we just check if the insert succeeded, and if it did, + // simply return the current version -- this also ensures atomicity. + let metadata = Metadata { + key: MetadataKey::SchemaVersion, + value: Value::Integer(SCHEMA_VERSION), + }; + if connection.insert_or_ignore_one(metadata)?.is_some() { + Ok(SCHEMA_VERSION) + } else { + Self::metadata_by_key(connection, MetadataKey::SchemaVersion) + } + } + + fn metadata_by_key( + connection: &Connection, + key: MetadataKey, + ) -> nanosql::Result { + let Metadata { ref value, .. } = connection.select_by_key(key)?; + let value = T::column_result(value.into())?; + Ok(value) + } + + /// Returns the list of items in the database. + /// + /// The returned data is human-readable: it contains fields such as the + /// identifying name/label/title of the entry, the optional account + /// information, and the date of creation/last modification. It does not + /// return binary data such as the encrypted secret, the KDF salt, or + /// the authentication nonce. + /// + /// If the `search_term` is `None`, then all items are returned. + /// + /// If the `search_term` is `Some(_)`, then only items matching the search + /// term will be returned. The search term is interpreted as an SQL + /// `LIKE` pattern. The pattern will be matched against the label and + /// the account name, and entries matching either will be returned. + pub fn list_items_for_display(&self, search_term: Option<&str>) -> Result> { + self.connection + .compile_invoke(ListItemsForDisplay, search_term) + .map_err(Into::into) + } + + /// Creates a new entry in the database using an already-encrypted secret. + pub fn add_item(&self, input: AddItemInput<'_>) -> Result { + self.connection.insert_one(input).map_err(Into::into) + } + + /// Retrieves a full item from the database based on its unique ID (primary + /// key). This includes encryption and authentication data: the + /// encrypted secret, the KDF salt, and the authentication nonce. + pub fn item_by_id(&self, id: u64) -> Result { + self.connection.select_by_key(id).map_err(Into::into) + } +} + +/// Describes a secret item. +#[derive(Clone, PartialEq, Eq, Debug, Table, ResultRecord)] +#[nanosql(insert_input_ty = AddItemInput<'p>)] +pub struct Item { + /// Unique identifier of the item. + #[nanosql(pk)] + pub uid: u64, + /// Human-readable identifier of the item. + #[nanosql(unique)] + pub label: String, + /// Username, email address, etc. for identification. `None` if not + /// applicable. + pub account: Option, + /// Last modification date of the item. If never modified, this is the + /// creation date. + pub last_modified_at: DateTime, + /// The encrypted and authenticated password data. + /// Also contains a copy of the other fields for the purpose of tamper + /// protection. + pub encrypted_secret: Vec, + /// The salt for the key derivation function. + /// + /// This is `UNIQUE`, acting as an additional line of defense against + /// salt re-use, which would result in two users with the same password + /// and salt getting identical encryption keys. + #[nanosql(unique)] + pub kdf_salt: [u8; RECOMMENDED_SALT_LEN], + /// The nonce for the authentication function. + /// + /// This is `UNIQUE`, acting as an additional line of defense against + /// nonce re-use, which would allow breaking encryption/authentication. + #[nanosql(unique)] + pub auth_nonce: [u8; NONCE_LEN], +} + +/// Used for adding an encrypted secret item to the database. +#[derive(Clone, Param, InsertInput)] +#[nanosql(table = Item)] +pub struct AddItemInput<'p> { + /// inserting a `NULL` into an `INTEGER PRIMARY KEY` auto-generates the PK + pub uid: Null, + pub label: &'p str, + pub account: Option<&'p str>, + pub last_modified_at: DateTime, + pub encrypted_secret: &'p [u8], + pub kdf_salt: [u8; RECOMMENDED_SALT_LEN], + pub auth_nonce: [u8; NONCE_LEN], +} + +/// Human-readable subset (projection) of the `Item` table. +/// Does not contain the secret or the encryption details (salt/nonce). +#[derive(Clone, Debug, ResultRecord)] +pub struct DisplayItem { + pub uid: u64, + pub label: String, + pub account: Option, + pub last_modified_at: DateTime, +} + +/// Internal technical bookkeeping data (e.g., database version). +#[derive(Clone, Debug, Table, Param, ResultRecord)] +struct Metadata { + #[nanosql(pk)] + key: MetadataKey, + value: Value, +} + +/// The kinds of metadata stored in the database. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, AsSqlTy, ToSql, FromSql, Param, ResultRecord)] +#[nanosql(rename_all = "lower_snake_case")] +enum MetadataKey { + /// The version of the database schema that determines its format. + SchemaVersion, +} + +nanosql::define_query! { + /// The optional parameter is a search/filter term. It works with SQLite `LIKE` syntax. + /// If not provided, no filtering will be performed, and all items will be returned. + ListItemsForDisplay<'p>: Option<&'p str> => Vec { + r#" + SELECT + "item"."uid" AS "uid", + "item"."label" AS "label", + "item"."account" AS "account", + "item"."last_modified_at" AS "last_modified_at" + FROM "item" + WHERE ?1 IS NULL OR "item"."label" LIKE ?1 OR "item"."account" LIKE ?1 + ORDER BY "item"."uid"; + "# + } +} + +#[cfg(test)] +mod tests { + use chrono::Utc; + use nanosql::{ + Error as NanosqlError, Null, + rusqlite::{Error as SqliteError, ErrorCode}, + }; + + use super::{AddItemInput, Database}; + use crate::{ + crypto::{NONCE_LEN, RECOMMENDED_SALT_LEN}, + error::{Error, Result}, + }; + + #[test] + fn salt_uniqueness_is_enforced() -> Result<()> { + let db = Database::open(":memory:", ":queue:")?; + let salt: [u8; RECOMMENDED_SALT_LEN] = *b"Qk2Dw5aV65Ie8y7t"; + let nonce_1: [u8; NONCE_LEN] = *b"lMVXTMT2z2giginHeWwIajy4"; + let nonce_2: [u8; NONCE_LEN] = *b"rZNaJw3dBHmiqGhfUxLbjL6x"; + + let input_1 = AddItemInput { + uid: Null, + label: "Some label", + account: Some("first@account.com"), + last_modified_at: Utc::now(), + encrypted_secret: b"EncrYpt3d S3cre7!123", + kdf_salt: salt, + auth_nonce: nonce_1, + }; + let input_2 = AddItemInput { + uid: Null, + label: "a completely different title", + account: Some("second@otherserver.org"), + last_modified_at: Utc::now(), + encrypted_secret: b"$#an0ther-c1pherteXt-of_diff3rent^LENGTH%", + kdf_salt: salt, + auth_nonce: nonce_2, + }; + + // We should be able to add the first item sucessfully. + let item = db.add_item(input_1)?; + assert_eq!(db.item_by_id(item.uid)?, item); + + // The second item has an identical salt, so insertion must fail + // due to the violation of the UNIQUE constraint. + let error = db + .add_item(input_2) + .expect_err("item with duplicate salt added"); + let Error::Db(NanosqlError::Sqlite(SqliteError::SqliteFailure(error, _))) = error else { + panic!("unexpected error: {error}"); + }; + + assert_eq!(error.code, ErrorCode::ConstraintViolation); + + Ok(()) + } + + #[test] + fn nonce_uniqueness_is_enforced() -> Result<()> { + let db = Database::open(":memory:", ":queue:")?; + let salt_1: [u8; RECOMMENDED_SALT_LEN] = *b"NdBIIex0BLnkThWH"; + let salt_2: [u8; RECOMMENDED_SALT_LEN] = *b"xS8HYP2XAjgSnEOJ"; + let nonce: [u8; NONCE_LEN] = *b"vb4yngPRSgEOrBLNGw8YcGpG"; + + let input_1 = AddItemInput { + uid: Null, + label: "Not a useful label", + account: Some("foo@bar.qux"), + last_modified_at: Utc::now(), + encrypted_secret: b"more stuff, I've run out of ideas", + kdf_salt: salt_1, + auth_nonce: nonce, + }; + let input_2 = AddItemInput { + uid: Null, + label: "...but neither is this!", + account: Some("lol@wut.gov"), + last_modified_at: Utc::now(), + encrypted_secret: b"some different blob", + kdf_salt: salt_2, + auth_nonce: nonce, + }; + + // We should be able to add the first item sucessfully. + let item = db.add_item(input_1)?; + assert_eq!(db.item_by_id(item.uid)?, item); + + // The second item has an identical nonce, so insertion must fail + // due to the violation of the UNIQUE constraint. + let error = db + .add_item(input_2) + .expect_err("item with duplicate nonce added"); + let Error::Db(NanosqlError::Sqlite(SqliteError::SqliteFailure(error, _))) = error else { + panic!("unexpected error: {error}"); + }; + + assert_eq!(error.code, ErrorCode::ConstraintViolation); + + Ok(()) + } +} diff --git a/sequencer/src/lib.rs b/sequencer/src/lib.rs new file mode 100644 index 0000000..7460377 --- /dev/null +++ b/sequencer/src/lib.rs @@ -0,0 +1,133 @@ +#![forbid(unsafe_code)] +#![allow(clippy::allow_attributes_without_reason)] + +pub mod db; +pub mod sequencer; +pub use demo_sqlite_common::{config, crypto, error, screen}; + +mod tui; + +use std::sync::Arc; + +use clap::Parser; +use demo_sqlite_common::logging::RawModeWriter; +use sequencer::Sequencer; +use tracing::{error, info}; +use tracing_subscriber::{EnvFilter, layer::SubscriberExt as _, util::SubscriberInitExt as _}; + +#[derive(Parser, Debug)] +#[command(about = "SQLite zone sequencer")] +pub struct SequencerArgs { + /// Logos blockchain node HTTP endpoint + #[arg( + long, + default_value = "http://localhost:8080", + env = "SEQUENCER_NODE_ENDPOINT" + )] + pub node_url: String, + + /// Path to the `SQLite` database file + #[arg(long, default_value = "./data/database.db", env = "SEQUENCER_DB_PATH")] + pub db_path: String, + + /// Path to the signing key file (created if it doesn't exist) + #[arg( + long, + default_value = "./data/sequencer.key", + env = "SEQUENCER_SIGNING_KEY_PATH" + )] + pub key_path: String, + + /// Basic auth username for node endpoint + #[arg(long, env = "SEQUENCER_NODE_AUTH_USERNAME")] + pub node_auth_username: Option, + + /// Basic auth password for node endpoint + #[arg(long, env = "SEQUENCER_NODE_AUTH_PASSWORD")] + pub node_auth_password: Option, + + /// Path to the queue file for pending SQL statements + #[arg(long, default_value = "./data/queue.txt", env = "SEQUENCER_QUEUE_FILE")] + pub queue_file: String, + + /// Path to the checkpoint file for crash recovery + #[arg( + long, + default_value = "./data/sequencer.checkpoint", + env = "CHECKPOINT_PATH" + )] + checkpoint_path: String, + + /// Path to the channel ID file + #[arg(long, default_value = "./data/channel.txt", env = "CHANNEL_PATH")] + channel_path: String, +} + +use crate::{config::Config, db::Database, error::Result, screen::ScreenGuard, tui::State}; + +#[derive(Debug)] +struct App { + screen: ScreenGuard, + state: State, +} + +impl App { + fn new(state: State) -> Result { + Ok(Self { + screen: ScreenGuard::open()?, + state, + }) + } + + /// The main run loop. + fn run_app(mut self) -> Result<()> { + while self.state.is_running() { + self.screen.draw(|frame| self.state.draw(frame))?; + self.state.handle_events(); + } + + Ok(()) + } +} + +#[expect(clippy::unused_async)] +pub async fn run(args: SequencerArgs) -> Result<()> { + tracing_subscriber::registry() + .with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"))) + .with(tracing_subscriber::fmt::layer().with_writer(RawModeWriter)) + .init(); + + info!("Sqlite Sequencer starting up..."); + info!(" Logos blockchain Node: {}", args.node_url); + + let db = Database::open(&args.db_path, &args.queue_file)?; + + let sequencer = match Sequencer::new( + &args.node_url, + &args.key_path, + args.node_auth_username, + args.node_auth_password, + &args.queue_file, + &args.checkpoint_path, + &args.channel_path, + ) { + Ok(s) => Arc::new(s), + Err(e) => { + error!("Sequencer initialization failed: {e}"); + std::process::exit(1); + } + }; + info!("Sequencer ready"); + + let sequencer_clone = Arc::clone(&sequencer); + tokio::spawn(async move { + sequencer_clone.run_processing_loop().await; + }); + info!("Background processor started"); + + let config = Config::from_rc_file()?; + let state = State::new(db, config.theme)?; + let app = App::new(state)?; + + app.run_app() +} diff --git a/sequencer/src/main.rs b/sequencer/src/main.rs new file mode 100644 index 0000000..ded1e18 --- /dev/null +++ b/sequencer/src/main.rs @@ -0,0 +1,8 @@ +use clap::Parser as _; +use demo_sqlite_sequencer::{SequencerArgs, run}; + +#[tokio::main] +async fn main() { + let args = SequencerArgs::parse(); + drop(run(args).await); +} diff --git a/sequencer/src/sequencer.rs b/sequencer/src/sequencer.rs new file mode 100644 index 0000000..1da72a8 --- /dev/null +++ b/sequencer/src/sequencer.rs @@ -0,0 +1,207 @@ +use std::{ + fs, + fs::OpenOptions, + io, + io::{BufRead as _, BufReader}, + path::Path, + time::Duration, +}; + +use fs2::FileExt as _; +use lb_common_http_client::{BasicAuthCredentials, CommonHttpClient}; +use lb_core::mantle::ops::channel::ChannelId; +use lb_key_management_system_service::keys::{ED25519_SECRET_KEY_SIZE, Ed25519Key}; +use logos_blockchain_zone_sdk::adapter::NodeHttpClient; +use logos_blockchain_zone_sdk::sequencer::{ + Error as ZoneSequencerError, SequencerCheckpoint, SequencerHandle, ZoneSequencer, +}; +use nanosql::rusqlite::Error as SqliteError; +use reqwest::Url; +use thiserror::Error; +use tokio::time::sleep; +use tracing::{debug, info}; + +#[derive(Debug, Error)] +pub enum SequencerError { + #[error("Zone sequencer error: {0}")] + ZoneSequencer(#[from] ZoneSequencerError), + #[error("URL parse error: {0}")] + Url(String), + #[error("IO error: {0}")] + Io(#[from] io::Error), + #[error("SQLite error: {0}")] + Sqlite(#[from] SqliteError), + #[error("Invalid key file: expected {expected} bytes, got {actual}")] + InvalidKeyFile { expected: usize, actual: usize }, + #[error("{0}")] + InvalidChannelId(String), + #[error("Timeout: {0}")] + Timeout(String), +} + +pub type Result = std::result::Result; + +/// The sequencer that handles transactions using the Zone SDK +pub struct Sequencer { + handle: SequencerHandle, + queue_file: String, + checkpoint_path: String, +} + +/// Load signing key from file or generate a new one if it doesn't exist +fn load_or_create_signing_key(path: &Path) -> Result { + if path.exists() { + debug!("Loading existing signing key from {:?}", path); + let key_bytes = fs::read(path)?; + if key_bytes.len() != ED25519_SECRET_KEY_SIZE { + return Err(SequencerError::InvalidKeyFile { + expected: ED25519_SECRET_KEY_SIZE, + actual: key_bytes.len(), + }); + } + let key_array: [u8; ED25519_SECRET_KEY_SIZE] = + key_bytes.try_into().expect("length already checked"); + Ok(Ed25519Key::from_bytes(&key_array)) + } else { + debug!("Generating new signing key and saving to {:?}", path); + let mut key_bytes = [0u8; ED25519_SECRET_KEY_SIZE]; + rand::RngCore::fill_bytes(&mut rand::rng(), &mut key_bytes); + fs::write(path, key_bytes)?; + Ok(Ed25519Key::from_bytes(&key_bytes)) + } +} + +fn save_checkpoint(path: &Path, checkpoint: &SequencerCheckpoint) { + let data = serde_json::to_vec(checkpoint).expect("failed to serialize checkpoint"); + fs::write(path, data).expect("failed to write checkpoint file"); +} + +fn load_checkpoint(path: &Path) -> Option { + if !path.exists() { + return None; + } + let data = fs::read(path).expect("failed to read checkpoint file"); + Some(serde_json::from_slice(&data).expect("failed to deserialize checkpoint")) +} + +impl Sequencer { + pub fn new( + node_endpoint: &str, + signing_key_path: &str, + node_auth_username: Option, + node_auth_password: Option, + queue_file: &str, + checkpoint_path: &str, + channel_path: &str, + ) -> Result { + let node_url = Url::parse(node_endpoint).map_err(|e| SequencerError::Url(e.to_string()))?; + + info!("{}", node_url.clone().to_string()); + + let basic_auth = node_auth_username + .map(|username| BasicAuthCredentials::new(username, node_auth_password)); + + for path in [signing_key_path, checkpoint_path, channel_path] { + if let Some(parent) = Path::new(path).parent() { + fs::create_dir_all(parent)?; + } + } + + let checkpoint = load_checkpoint(Path::new(&checkpoint_path)); + if checkpoint.is_some() { + println!(" Restored checkpoint from {checkpoint_path}"); + } + + let signing_key = load_or_create_signing_key(Path::new(signing_key_path))?; + let channel_id = ChannelId::from(signing_key.public_key().to_bytes()); + fs::write(channel_path, hex::encode(channel_id.as_ref())) + .expect("failed to write channel id"); + + let node = NodeHttpClient::new(CommonHttpClient::new(basic_auth), node_url); + let (zone_sequencer, handle) = ZoneSequencer::init(channel_id, signing_key, node, checkpoint); + zone_sequencer.spawn(); + + Ok(Self { + handle, + queue_file: queue_file.to_owned(), + checkpoint_path: checkpoint_path.to_owned(), + }) + } + + /// Drain the queue file and return all pending queries + fn queue_drain(&self) -> Result> { + let file = OpenOptions::new() + .read(true) + .write(true) + .open(self.queue_file.clone())?; + + file.lock_exclusive()?; + + let reader = BufReader::new(&file); + let mut queue_vec = Vec::new(); + for query in reader.lines() { + queue_vec.push(query?.clone()); + } + + file.set_len(0)?; + + Ok(queue_vec) + } + + /// Process all pending queries as a single inscription + async fn process_pending_batch(&self) -> Result<()> { + let pending = self.queue_drain()?; + if pending.is_empty() { + return Ok(()); + } + + let count = pending.len(); + debug!("Processing batch of {} queries", count); + + let data = pending.join("\n").into_bytes(); + let result = self.handle.publish_message(data).await?; + + info!( + "Inscription published with tx_hash: {:?}", + result.inscription_id + ); + + save_checkpoint(Path::new(&self.checkpoint_path), &result.checkpoint); + + Ok(()) + } + + /// Check if the queue file is empty + pub fn queue_is_empty(&self) -> Result { + match fs::metadata(self.queue_file.clone()) { + Ok(meta) => Ok(meta.len() == 0), + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(true), + Err(e) => Err(e.into()), + } + } + + /// Background processing loop - call this in a spawned task + pub async fn run_processing_loop(&self) { + let poll_interval = Duration::from_millis(100); + + loop { + let is_empty = match self.queue_is_empty() { + Ok(empty) => empty, + Err(e) => { + tracing::error!("Failed to check queue: {}", e); + sleep(poll_interval).await; + continue; + } + }; + + if is_empty { + sleep(poll_interval).await; + continue; + } + + if let Err(e) = self.process_pending_batch().await { + tracing::error!("Batch processing failed: {}", e); + } + } + } +} diff --git a/sequencer/src/tui.rs b/sequencer/src/tui.rs new file mode 100644 index 0000000..6f4e7e5 --- /dev/null +++ b/sequencer/src/tui.rs @@ -0,0 +1,881 @@ +#![allow(clippy::allow_attributes_without_reason)] + +//! The bulk of the actual user interface logic. + +use std::{ + fmt::{self, Debug, Formatter}, + mem, + ops::{ControlFlow, Deref, DerefMut}, + time::Duration, +}; + +use arboard::Clipboard; +use nanosql::Utc; +use ratatui::{ + Frame, + crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers, MouseEventKind}, + layout::{Constraint, Margin, Offset, Rect}, + style::Modifier, + text::Line, + widgets::{ + Clear, Paragraph, Row, Table, TableState, + block::{Block, BorderType}, + }, +}; +use tui_textarea::TextArea; +use zeroize::Zeroizing; + +use crate::{ + config::Theme, + crypto::{DecryptionInput, EncryptionInput}, + db::{AddItemInput, Database, DisplayItem, Item}, + error::{Error, Result}, +}; + +/// The top-level UI state, the basis of rendering. +#[derive(Debug)] +pub struct State { + db: Database, + clipboard: ClipboardDebugWrapper, + theme: Theme, + is_running: bool, + passwd_entry: Option, + find: Option, + new_item: Option, + popup_error: Option, + items: Vec, + table_state: TableState, +} + +impl State { + pub fn new(db: Database, theme: Theme) -> Result { + let items = db.list_items_for_display(None)?; + let clipboard = ClipboardDebugWrapper(Clipboard::new()?); + + let table_state = + TableState::new().with_selected(if items.is_empty() { None } else { Some(0) }); + + Ok(Self { + db, + clipboard, + theme, + is_running: true, + passwd_entry: None, + find: None, + new_item: None, + popup_error: None, + items, + table_state, + }) + } + + /// Returns `true` as long as the application should run. + /// Once this returns `false`, the application will exit. + pub const fn is_running(&self) -> bool { + self.is_running + } + + /// Top-level widget rendering. + pub fn draw(&mut self, frame: &mut Frame) { + let half_screen = { + let full = frame.area(); + Rect { + height: full.height / 2, + ..full + } + }; + let bottom_input_height = 3; + let mut table_area = { + let mut area = half_screen; + area.height -= bottom_input_height; + area + }; + let bottom_input_area = Rect { + x: table_area.x, + y: table_area.y + table_area.height, + width: table_area.width, + height: bottom_input_height, + }; + let table = self.main_table(); + + if let Some(passwd_entry) = self.passwd_entry.as_mut() { + frame.render_widget(&passwd_entry.enc_pass, bottom_input_area); + } else if let Some(find_state) = self.find.as_mut() { + frame.render_widget(&find_state.search_term, bottom_input_area); + } else { + table_area = half_screen; + } + + frame.render_stateful_widget(table, table_area, &mut self.table_state); + + if let Some(error) = self.popup_error.as_ref() { + let margin = Margin { + horizontal: half_screen.width.saturating_sub(72 + 2) / 2, + vertical: half_screen.height.saturating_sub(3 + 2) / 2, + }; + let dialog_area = half_screen.inner(margin); + let modal = self.error_modal(error); + + frame.render_widget(Clear, dialog_area); + frame.render_widget(modal, dialog_area); + } else if let Some(new_item) = self.new_item.as_ref() { + let inputs_total_height = new_item.text_areas().len() as u16 * 3; + let margin = Margin { + horizontal: half_screen.width.saturating_sub(72 + 2) / 2, + vertical: half_screen.height.saturating_sub(inputs_total_height + 2) / 2, + }; + let dialog_area = half_screen.inner(margin); + let outer = self.new_item_background(new_item); + + frame.render_widget(Clear, dialog_area); + frame.render_widget(&outer, dialog_area); + + let label_rect = Rect { + height: 3, + ..outer.inner(dialog_area) + }; + let desc_rect = label_rect.offset(Offset { x: 0, y: 3 }); + let secret_rect = desc_rect.offset(Offset { x: 0, y: 3 }); + let passwd_rect = secret_rect.offset(Offset { x: 0, y: 3 }); + let confirm_rect = passwd_rect.offset(Offset { x: 0, y: 3 }); + + frame.render_widget(&new_item.label, label_rect); + frame.render_widget(&new_item.account, desc_rect); + frame.render_widget(&new_item.secret, secret_rect); + frame.render_widget(&new_item.enc_pass, passwd_rect); + frame.render_widget(&new_item.confirm, confirm_rect); + } + } + + fn main_table(&self) -> Table<'static> { + Table::new( + self.items.iter().map(|item| { + Row::new([ + item.label.clone(), + item.account.clone().unwrap_or_default(), + item.last_modified_at.format("%F %T").to_string(), + ]) + }), + [ + Constraint::Percentage(40), + Constraint::Percentage(40), + Constraint::Min(24), + ], + ) + .header( + Row::new(["Title", "Username or account", "Modified at (UTC)"]) + .style(self.theme.default().add_modifier(Modifier::BOLD)), + ) + .row_highlight_style(Modifier::REVERSED) + .block( + Block::bordered() + .title(format!(" SteelSafe v{} ", env!("CARGO_PKG_VERSION"))) + .title_bottom(" [C]opy secret ") + .title_bottom(" [F]ind ") + .title_bottom(" [1] First ") + .title_bottom(" [0] Last ") + .title_bottom(" [N]ew item ") + .title_bottom(" [Q]uit ") + .border_type(BorderType::Rounded) + .border_style(if self.main_table_has_focus() { + self.theme.border().add_modifier(Modifier::BOLD) + } else { + self.theme.border() + }), + ) + .style(self.theme.default()) + } + + fn error_modal(&self, error: &Error) -> Paragraph<'static> { + let block = Block::bordered() + .title(" Error ") + .title_bottom(" Close ") + .border_type(BorderType::Rounded) + .border_style(self.theme.error().add_modifier(Modifier::BOLD)); + + Paragraph::new(format!("\n{error}\n")) + .centered() + .block(block) + .style(self.theme.error()) + } + + fn new_item_background(&self, state: &NewItemState) -> Block<'static> { + Block::bordered() + .title(" New secret item ") + .title_top(Line::from(" <^G> Generate password ").right_aligned()) + .title_bottom(" Save ") + .title_bottom(" Cancel ") + .title_bottom(format!( + " <^H> {} secret ", + if state.show_secret { "Hide" } else { "Show" } + )) + .title_bottom(format!( + " <^E> {} encr passwd ", + if state.show_enc_pass { "Hide" } else { "Show" } + )) + .border_type(BorderType::Rounded) + .style(self.theme.border_highlight()) + .border_style(self.theme.border_highlight().add_modifier(Modifier::BOLD)) + } + + /// Event polling and error handling. + pub fn handle_events(&mut self) { + if let Err(error) = self.handle_events_impl() { + self.popup_error = Some(error); + } + } + + /// The bulk of the actual event handling logic. + fn handle_events_impl(&mut self) -> Result<()> { + if !event::poll(Duration::from_millis(50))? { + return Ok(()); + } + let event = event::read()?; + + let event = match self.handle_error_input(event)? { + ControlFlow::Break(()) => return Ok(()), + ControlFlow::Continue(event) => event, + }; + let event = match self.handle_passwd_entry_input(event)? { + ControlFlow::Break(()) => return Ok(()), + ControlFlow::Continue(event) => event, + }; + let event = match self.handle_find_input(event)? { + ControlFlow::Break(()) => return Ok(()), + ControlFlow::Continue(event) => event, + }; + let event = match self.handle_new_input(event)? { + ControlFlow::Break(()) => return Ok(()), + ControlFlow::Continue(event) => event, + }; + + self.handle_main_table_event(&event) + } + + /// Handles events when the main table has focus. + #[expect(clippy::unnecessary_wraps)] + fn handle_main_table_event(&mut self, event: &Event) -> Result<()> { + if let Event::Mouse(mouse) = event { + match mouse.kind { + MouseEventKind::ScrollDown => { + self.table_state.select_next(); + } + MouseEventKind::ScrollUp => { + self.table_state.select_previous(); + } + _ => {} + } + return Ok(()); + } + + let Event::Key(key) = event else { + return Ok(()); + }; + + if key.kind != KeyEventKind::Press { + return Ok(()); + } + + match key.code { + KeyCode::Up | KeyCode::Char('k' | 'K') => { + self.table_state.select_previous(); + } + KeyCode::Down | KeyCode::Tab | KeyCode::Char('j' | 'J') => { + self.table_state.select_next(); + } + KeyCode::Char('1') => { + self.table_state.select_first(); + } + KeyCode::Char('0') => { + self.table_state.select_last(); + } + KeyCode::Char('c' | 'C') | KeyCode::Enter => { + self.passwd_entry = Some(PasswordEntryState::with_theme(self.theme.clone())); + } + KeyCode::Char('f' | 'F' | '/') => { + // if we are already in find mode, do NOT reset + // the search term, just give back focus. + if let Some(find_state) = self.find.as_mut() { + find_state.set_focus(true); + } else { + self.find = Some(FindItemState::with_theme(self.theme.clone())); + } + } + KeyCode::Char('n' | 'N') => { + self.new_item = Some(NewItemState::with_theme(self.theme.clone())); + } + KeyCode::Char('q' | 'Q') => { + self.is_running = false; + } + _ => {} + } + + Ok(()) + } + + /// Handles events when the error modal is open. + #[expect(clippy::unnecessary_wraps)] + fn handle_error_input(&mut self, event: Event) -> Result> { + if self.popup_error.is_none() { + return Ok(ControlFlow::Continue(event)); + } + + if let Event::Key(evt) = event + && evt.code == KeyCode::Esc + { + self.popup_error = None; + } + + Ok(ControlFlow::Break(())) + } + + /// Handles events for the password entry panel before decrypting a secret. + fn handle_passwd_entry_input(&mut self, event: Event) -> Result> { + let Some(passwd_entry) = self.passwd_entry.as_mut() else { + return Ok(ControlFlow::Continue(event)); + }; + + match event { + Event::Key(evt) => match evt.code { + KeyCode::Esc => { + self.passwd_entry = None; + } + KeyCode::Enter => { + let password = Zeroizing::new(passwd_entry.enc_pass.lines().join("\n")); + self.passwd_entry = None; + self.copy_secret_to_clipboard(&password)?; + } + KeyCode::Char('h' | 'H') if evt.modifiers.contains(KeyModifiers::CONTROL) => { + passwd_entry.toggle_show_enc_pass(); + } + _ => { + passwd_entry.enc_pass.input(event); + } + }, + _ => { + passwd_entry.enc_pass.input(event); + } + } + + Ok(ControlFlow::Break(())) + } + + /// Handles events for the Find panel. + fn handle_find_input(&mut self, event: Event) -> Result> { + let Some(find_state) = self.find.as_mut() else { + return Ok(ControlFlow::Continue(event)); + }; + + match event { + Event::Key(evt) => match evt.code { + KeyCode::Esc => { + self.find = None; + self.sync_data(true)?; + Ok(ControlFlow::Break(())) + } + KeyCode::Enter if find_state.has_focus => { + find_state.set_focus(false); + Ok(ControlFlow::Break(())) + } + _ if find_state.has_focus => { + find_state.search_term.input(event); + self.sync_data(true)?; + Ok(ControlFlow::Break(())) + } + _ => Ok(ControlFlow::Continue(event)), + }, + _ => Ok(ControlFlow::Continue(event)), + } + } + + /// Handles events for the "New item" dialog. + fn handle_new_input(&mut self, event: Event) -> Result> { + // if the input text area is not open, ignore the event and give it back right + // away + let Some(new_item) = self.new_item.as_mut() else { + return Ok(ControlFlow::Continue(event)); + }; + + match event { + Event::Key(evt) => match evt.code { + KeyCode::Esc => { + self.new_item = None; + } + KeyCode::Down | KeyCode::Tab => { + new_item.cycle_forward(); + } + KeyCode::Up => { + new_item.cycle_back(); + } + KeyCode::Enter => { + // close dialog even if an error occurred + let new_item = self + .new_item + .take() + .expect("just checked that new_item is Some"); + let added = new_item.add_item(&self.db)?; + + self.sync_data(false)?; + + if let Some((idx, _item)) = self + .items + .iter() + .enumerate() + .rev() // the new item will _usually_ be the last one + .find(|(_idx, item)| item.uid == added.uid) + { + self.table_state.select(Some(idx)); + } + } + KeyCode::Char('h' | 'H') if evt.modifiers.contains(KeyModifiers::CONTROL) => { + new_item.toggle_show_secret(); + } + KeyCode::Char('e' | 'E') if evt.modifiers.contains(KeyModifiers::CONTROL) => { + new_item.toggle_show_enc_pass(); + } + KeyCode::Char('g' | 'G') if evt.modifiers.contains(KeyModifiers::CONTROL) => { + new_item.generate_random_password(); + } + _ => { + new_item.focused_text_area().input(event); + } + }, + _ => { + new_item.focused_text_area().input(event); + } + } + + Ok(ControlFlow::Break(())) + } + + /// Reloads the contents of the database from disk to memory. + /// If `adjust_selection` is set, the last item of the table + /// will be selected. This is useful after certain operations + /// that act destructively on the table state (e.g., search). + fn sync_data(&mut self, adjust_selection: bool) -> Result<()> { + let search_term = self.find.as_ref().and_then(|find_state| { + find_state + .search_term + .lines() + .first() + .map(|line| format!("%{}%", line.trim())) + }); + self.items = self.db.list_items_for_display(search_term.as_deref())?; + + #[expect(unused_parens)] + if (adjust_selection + && !self.items.is_empty() + && self + .table_state + .selected() + .is_none_or(|idx| idx >= self.items.len())) + { + self.table_state.select_last(); + } + + Ok(()) + } + + /// Actually copy the decrypted plaintext secret to the clipboard. + /// We can't zeroize the clipboard content, so we don't even bother. + fn copy_secret_to_clipboard(&mut self, enc_pass: &str) -> Result<()> { + let index = self + .table_state + .selected() + .ok_or(Error::SelectionRequired)?; + let uid = self.items[index].uid; + let item = self.db.item_by_id(uid)?; + + let input = DecryptionInput { + encrypted_secret: &item.encrypted_secret, + kdf_salt: item.kdf_salt, + auth_nonce: item.auth_nonce, + label: item.label.as_str(), + account: item.account.as_deref(), + last_modified_at: item.last_modified_at, + }; + let plaintext_secret = input.decrypt_and_verify(enc_pass.as_bytes())?; + + // we do NOT use `String::from_utf8()`, because that would copy the + // bytes, and complicate correct zeroization of the secret on error. + let secret_str = std::str::from_utf8(&plaintext_secret)?; + + self.clipboard.set_text(secret_str).map_err(Into::into) + } + + /// The main table has focus when none of the other widgets do. + fn main_table_has_focus(&self) -> bool { + (self.find.is_none() || self.find.as_ref().is_some_and(|find| !find.has_focus)) + && self.passwd_entry.is_none() + && self.new_item.is_none() + && self.popup_error.is_none() + } +} + +#[derive(Debug)] +struct PasswordEntryState { + is_visible: bool, + enc_pass: TextArea<'static>, + theme: Theme, +} + +impl PasswordEntryState { + fn with_theme(theme: Theme) -> Self { + let mut enc_pass = TextArea::default(); + enc_pass.set_style(theme.default()); + + // set up text field style + let mut state = Self { + is_visible: false, + enc_pass, + theme, + }; + state.set_visible(false); + state + } + + fn toggle_show_enc_pass(&mut self) { + self.set_visible(!self.is_visible); + } + + fn set_visible(&mut self, is_visible: bool) { + self.is_visible = is_visible; + + if self.is_visible { + self.enc_pass.clear_mask_char(); + } else { + self.enc_pass.set_mask_char('\u{25cf}'); + } + + let show_hide_title = format!( + " <^H> {} password ", + if self.is_visible { "Hide" } else { "Show" }, + ); + + self.enc_pass.set_block( + Block::bordered() + .title(" Enter decryption (master) password ") + .title_bottom(" OK ") + .title_bottom(" Cancel ") + .title_bottom(show_hide_title) + .border_type(BorderType::Rounded) + .border_style(self.theme.border().add_modifier(Modifier::BOLD)), + ); + } +} + +#[derive(Debug)] +struct FindItemState { + search_term: TextArea<'static>, + has_focus: bool, + theme: Theme, +} + +impl FindItemState { + fn with_theme(theme: Theme) -> Self { + let mut search_term = TextArea::default(); + + search_term.set_block( + Block::bordered() + .title(" Search term ") + .title_bottom(" Focus secrets ") + .title_bottom(" Exit search ") + .border_type(BorderType::Rounded), + ); + + let mut state = Self { + search_term, + has_focus: true, + theme, + }; + state.set_focus(true); + state + } + + fn set_focus(&mut self, has_focus: bool) { + self.has_focus = has_focus; + + let block = self.search_term.block().cloned().unwrap_or_default(); + + if self.has_focus { + self.search_term + .set_style(self.theme.default().add_modifier(Modifier::BOLD)); + self.search_term + .set_block(block.border_style(self.theme.border().add_modifier(Modifier::BOLD))); + } else { + self.search_term.set_style(self.theme.default()); + self.search_term + .set_block(block.border_style(self.theme.border())); + } + } +} + +#[derive(Debug)] +struct NewItemState { + label: TextArea<'static>, + account: TextArea<'static>, + secret: TextArea<'static>, + enc_pass: TextArea<'static>, + confirm: TextArea<'static>, + focused: FocusedTextArea, + show_secret: bool, + show_enc_pass: bool, + theme: Theme, +} + +impl NewItemState { + fn with_theme(theme: Theme) -> Self { + let mut state = Self { + label: TextArea::default(), + account: TextArea::default(), + secret: TextArea::default(), + enc_pass: TextArea::default(), + confirm: TextArea::default(), + focused: FocusedTextArea::default(), + show_secret: false, + show_enc_pass: false, + theme, + }; + + // set initial styles + state.set_show_secret(false); + state.set_show_enc_pass(false); + + let props = [ + ("Title or label", true), + ("Username or account", false), + ("Secret (to be stored)", true), + ("Encryption (master) password", true), + ("Confirm master password", true), + ]; + let border_style = state.theme.border_highlight(); + + for (ta, (title, required)) in state.text_areas_mut().into_iter().zip(props) { + ta.set_block( + Block::bordered() + .title(format!(" {title} ")) + .border_type(BorderType::Rounded) + .border_style(border_style), + ); + ta.set_placeholder_text(if required { "Required" } else { "Optional" }); + } + + state.set_focused_text_area(FocusedTextArea::default()); + state + } + + fn text_areas(&self) -> Vec<&TextArea<'static>> { + vec![ + &self.label, + &self.account, + &self.secret, + &self.enc_pass, + &self.confirm, + ] + } + + fn text_areas_mut(&mut self) -> Vec<&mut TextArea<'static>> { + vec![ + &mut self.label, + &mut self.account, + &mut self.secret, + &mut self.enc_pass, + &mut self.confirm, + ] + } + + const fn focused_text_area(&mut self) -> &mut TextArea<'static> { + match self.focused { + FocusedTextArea::Label => &mut self.label, + FocusedTextArea::Account => &mut self.account, + FocusedTextArea::Secret => &mut self.secret, + FocusedTextArea::EncPass => &mut self.enc_pass, + FocusedTextArea::Confirm => &mut self.confirm, + } + } + + fn set_focused_text_area(&mut self, which: FocusedTextArea) { + self.focused = which; + + let highlight_style = self.theme.highlight(); + + for ta in self.text_areas_mut() { + if let Some(block) = ta.block() { + ta.set_block(block.clone().style(highlight_style)); + } + } + + let ta = self.focused_text_area(); + + if let Some(block) = ta.block() { + ta.set_block( + block + .clone() + .style(highlight_style.add_modifier(Modifier::BOLD)), + ); + } + } + + fn cycle_forward(&mut self) { + self.set_focused_text_area(self.focused.next()); + } + + fn cycle_back(&mut self) { + self.set_focused_text_area(self.focused.prev()); + } + + fn set_show_secret(&mut self, flag: bool) { + self.show_secret = flag; + + if flag { + self.secret.clear_mask_char(); + } else { + self.secret.set_mask_char('\u{25cf}'); + } + } + + fn set_show_enc_pass(&mut self, flag: bool) { + self.show_enc_pass = flag; + + if flag { + self.enc_pass.clear_mask_char(); + self.confirm.clear_mask_char(); + } else { + self.enc_pass.set_mask_char('\u{25cf}'); + self.confirm.set_mask_char('\u{25cf}'); + } + } + + fn toggle_show_secret(&mut self) { + self.set_show_secret(!self.show_secret); + } + + fn toggle_show_enc_pass(&mut self) { + self.set_show_enc_pass(!self.show_enc_pass); + } + + fn generate_random_password(&mut self) { + let password = crate::crypto::generate_password(); + self.secret.select_all(); + self.secret.insert_str(password.as_str()); + } + + fn add_item(self, db: &Database) -> Result { + let label = match self.label.lines() { + [line] if !line.trim().is_empty() => line.trim(), + _ => return Err(Error::LabelRequired), + }; + let account = match self.account.lines() { + [] => None, + [line] => { + if line.trim().is_empty() { + None + } else { + Some(line.trim()) + } + } + _ => return Err(Error::AccountNameSingleLine), + }; + + // Steal the contents of the secret and wrap it in a `Zeroizing`, so + // that it's cleared upon drop (even if an error occurs). + let secret_lines = Zeroizing::new(self.secret.into_lines()); + let secret = match secret_lines.as_slice() { + [] => return Err(Error::SecretRequired), + [line] if line.is_empty() => return Err(Error::SecretRequired), + lines => Zeroizing::new(lines.join("\n")), + }; + + // Do the same to the encryption password. + let mut enc_pass_lines = Zeroizing::new(self.enc_pass.into_lines()); + let enc_pass = match enc_pass_lines.as_mut_slice() { + [line] if !line.is_empty() => Zeroizing::new(mem::take(line)), + _ => return Err(Error::EncryptionPasswordRequired), + }; + + let confirm_pass_lines = Zeroizing::new(self.confirm.into_lines()); + let confirm_pass = Zeroizing::new(confirm_pass_lines.join("\n")); + + if enc_pass != confirm_pass { + return Err(Error::ConfirmPasswordMismatch); + } + + let encryption_input = EncryptionInput { + plaintext_secret: secret.as_bytes(), + label, + account, + last_modified_at: Utc::now(), + }; + let encryption_output = encryption_input.encrypt_and_authenticate(enc_pass.as_bytes())?; + + db.add_item(AddItemInput { + uid: nanosql::Null, // generate fresh unique ID + label, + account, + last_modified_at: encryption_input.last_modified_at, + encrypted_secret: encryption_output.encrypted_secret.as_slice(), + kdf_salt: encryption_output.kdf_salt, + auth_nonce: encryption_output.auth_nonce, + }) + } +} + +#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)] +enum FocusedTextArea { + #[default] + Label, + Account, + Secret, + EncPass, + Confirm, +} + +impl FocusedTextArea { + const fn next(self) -> Self { + use FocusedTextArea::{Account, Confirm, EncPass, Label, Secret}; + + match self { + Label => Account, + Account => Secret, + Secret => EncPass, + EncPass => Confirm, + Confirm => Label, + } + } + + const fn prev(self) -> Self { + use FocusedTextArea::{Account, Confirm, EncPass, Label, Secret}; + + match self { + Label => Confirm, + Account => Label, + Secret => Account, + EncPass => Secret, + Confirm => EncPass, + } + } +} + +/// The sole purpose of this is to implement `Debug` so that it doesn't break +/// literally everything. +struct ClipboardDebugWrapper(Clipboard); + +impl Debug for ClipboardDebugWrapper { + fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { + formatter.debug_struct("Clipboard").finish_non_exhaustive() + } +} + +impl Deref for ClipboardDebugWrapper { + type Target = Clipboard; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ClipboardDebugWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +}