diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ccd43e9dc..fbb1e27ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -272,7 +272,7 @@ jobs: - uses: rust-lang/crates-io-auth-action@v1 id: auth - + - name: Publish to crates.io env: CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} diff --git a/.gitignore b/.gitignore index e00b85c21..7a07856f4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ swift/ios_build/ swift/local_build/ swift/tests/Sources/ swift/tests/.build/ - # Kotlin bindings and native libs kotlin/libs/ kotlin/walletkit/src/main/java/uniffi/ diff --git a/Cargo.lock b/Cargo.lock index 512b86661..91d51e180 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,36 +4,46 @@ version = 4 [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] [[package]] name = "adler2" -version = "2.0.0" +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 = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -61,9 +71,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502b004e05578e537ce0284843ba3dfaf6a0d5c530f5c20454411aded561289" +checksum = "2b5033b86af2c64e1b29b8446b7b6c48a0094ccea0b5c408111b6d218c418894" dependencies = [ "alloy-consensus", "alloy-contract", @@ -81,9 +91,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.2.1" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5848366a4f08dca1caca0a6151294a4799fe2e59ba25df100491d92e0b921b1c" +checksum = "90f374d3c6d729268bbe2d0e0ff992bb97898b2df756691a62ee1d5f0506bc39" dependencies = [ "alloy-primitives", "num_enum", @@ -92,9 +102,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3a590d13de3944675987394715f37537b50b856e3b23a0e66e97d963edbf38" +checksum = "ed1958f0294ecc05ebe7b3c9a8662a3e221c2523b7f2bcd94c7a651efbd510bf" dependencies = [ "alloy-eips", "alloy-primitives", @@ -114,14 +124,14 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-consensus-any" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f28f769d5ea999f0d8a105e434f483456a15b4e1fcb08edbbbe1650a497ff6d" +checksum = "f752e99497ddc39e22d547d7dfe516af10c979405a034ed90e69b914b7dddeae" dependencies = [ "alloy-consensus", "alloy-eips", @@ -133,9 +143,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990fa65cd132a99d3c3795a82b9f93ec82b81c7de3bab0bf26ca5c73286f7186" +checksum = "f2140796bc79150b1b7375daeab99750f0ff5e27b1f8b0aa81ccde229c7f02a2" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -150,14 +160,14 @@ dependencies = [ "futures", "futures-util", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-core" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca96214615ec8cf3fa2a54b32f486eb49100ca7fe7eb0b8c1137cd316e7250a" +checksum = "e5cc496da53ef2eaa129dcbd05a56eee94026cc88eb40ef297ea3171ea637e56" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -168,9 +178,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdff496dd4e98a81f4861e66f7eaf5f2488971848bb42d9c892f871730245c8" +checksum = "787cb8baf0e681d995c4a4d49713d56b91083930ae42d5d277a07276cafbe76f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -192,7 +202,7 @@ dependencies = [ "alloy-rlp", "crc", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -217,18 +227,31 @@ dependencies = [ "alloy-rlp", "borsh", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-eip7928" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3231de68d5d6e75332b7489cfcc7f4dfabeba94d990a10e4b923af0e6623540" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "serde", ] [[package]] name = "alloy-eips" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09535cbc646b0e0c6fcc12b7597eaed12cf86dff4c4fba9507a61e71b94f30eb" +checksum = "813a67f87e56b38554d18b182616ee5006e8e2bf9df96a0df8bf29dff1d52e3f" dependencies = [ "alloy-eip2124", "alloy-eip2930", "alloy-eip7702", + "alloy-eip7928", "alloy-primitives", "alloy-rlp", "alloy-serde", @@ -240,14 +263,14 @@ dependencies = [ "serde", "serde_with", "sha2", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-genesis" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1005520ccf89fa3d755e46c1d992a9e795466c2e7921be2145ef1f749c5727de" +checksum = "05864eef929c4d28895ae4b4d8ac9c6753c4df66e873b9c8fafc8089b59c1502" dependencies = [ "alloy-eips", "alloy-primitives", @@ -258,9 +281,9 @@ dependencies = [ [[package]] name = "alloy-hardforks" -version = "0.2.2" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40cc82a2283e3ce6317bc1f0134ea50d20e8c1965393045ee952fb28a65ddbd" +checksum = "3165210652f71dfc094b051602bafd691f506c54050a174b1cba18fb5ef706a3" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -271,9 +294,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5513d5e6bd1cba6bdcf5373470f559f320c05c8c59493b6e98912fbe6733943f" +checksum = "dad32d0724b70717ce15a76b48f11a4438010b85a90f728f476e99d5a6b06379" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -283,24 +306,24 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b626409c98ba43aaaa558361bca21440c88fd30df7542c7484b9c7a1489cdb" +checksum = "d2dd146b3de349a6ffaa4e4e319ab3a90371fb159fb0bddeb1c7bbe8b1792eff" dependencies = [ "alloy-primitives", "alloy-sol-types", - "http 1.3.1", + "http 1.4.0", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-network" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89924fdcfeee0e0fa42b1f10af42f92802b5d16be614a70897382565663bf7cf" +checksum = "8c12278ffbb8872dfba3b2f17d8ea5e8503c2df5155d9bc5ee342794bde505c3" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -319,14 +342,14 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-network-primitives" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0dbe56ff50065713ff8635d8712a0895db3ad7f209db9793ad8fcb6b1734aa" +checksum = "833037c04917bc2031541a60e8249e4ab5500e24c637c1c62e95e963a655d66f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -337,9 +360,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287de64d2236ca3f36b5eb03a39903f62a74848ae78a6ec9d0255eebb714f077" +checksum = "35c4f71997ada33b6aa3114d663a93b33a9af9d57bc5e21d79861f7491915e42" dependencies = [ "alloy-genesis", "alloy-hardforks", @@ -352,16 +375,16 @@ dependencies = [ "rand 0.8.5", "serde_json", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "url", ] [[package]] name = "alloy-primitives" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28" +checksum = "f1841ce147fa6cfdeb6b7751e2583b605bb8eef4238a2d3e0880b5cb3aba0e83" dependencies = [ "alloy-rlp", "bytes", @@ -369,27 +392,27 @@ dependencies = [ "const-hex", "derive_more", "foldhash", - "getrandom 0.3.2", - "hashbrown 0.16.0", - "indexmap 2.9.0", + "getrandom 0.3.4", + "hashbrown 0.16.1", + "indexmap 2.13.0", "itoa", "k256", "keccak-asm", "paste", "proptest", - "rand 0.9.1", + "rand 0.9.2", + "rapidhash", "ruint", "rustc-hash", "serde", "sha3", - "tiny-keccak", ] [[package]] name = "alloy-provider" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b56f7a77513308a21a2ba0e9d57785a9d9d2d609e77f4e71a78a1192b83ff2d" +checksum = "eafa840b0afe01c889a3012bb2fde770a544f74eab2e2870303eb0a5fb869c48" dependencies = [ "alloy-chains", "alloy-consensus", @@ -416,10 +439,10 @@ dependencies = [ "lru", "parking_lot", "pin-project", - "reqwest 0.12.22", + "reqwest 0.12.28", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -428,9 +451,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6c1d995bff8d011f7cd6c81820d51825e6e06d6db73914c1630ecf544d83d6" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -439,20 +462,20 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40e1ef334153322fd878d07e86af7a529bcb86b2439525920a88eba87bcf943" +checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "alloy-rpc-client" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff01723afc25ec4c5b04de399155bef7b6a96dfde2475492b1b7b4e7a4f46445" +checksum = "12768ae6303ec764905a8a7cd472aea9072f9f9c980d18151e26913da8ae0123" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -460,7 +483,7 @@ dependencies = [ "alloy-transport-http", "futures", "pin-project", - "reqwest 0.12.22", + "reqwest 0.12.28", "serde", "serde_json", "tokio", @@ -473,9 +496,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e82145856df8abb1fefabef58cdec0f7d9abf337d4abd50c1ed7e581634acdd" +checksum = "ae8eb0e5d6c48941b61ab76fabab4af66f7d88309a98aa14ad3dec7911c1eba3" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -485,9 +508,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212ca1c1dab27f531d3858f8b1a2d6bfb2da664be0c1083971078eb7b71abe4b" +checksum = "a1cf5a093e437dfd62df48e480f24e1a3807632358aad6816d7a52875f1c04aa" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -496,9 +519,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5715d0bf7efbd360873518bd9f6595762136b5327a9b759a8c42ccd9b5e44945" +checksum = "28e97603095020543a019ab133e0e3dc38cd0819f19f19bdd70c642404a54751" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -512,14 +535,14 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-serde" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ed8531cae8d21ee1c6571d0995f8c9f0652a6ef6452fde369283edea6ab7138" +checksum = "946a0d413dbb5cd9adba0de5f8a1a34d5b77deda9b69c1d7feed8fc875a1aa26" dependencies = [ "alloy-primitives", "serde", @@ -528,9 +551,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb10ccd49d0248df51063fce6b716f68a315dd912d55b32178c883fd48b4021d" +checksum = "2f7481dc8316768f042495eaf305d450c32defbc9bce09d8bf28afcd956895bb" dependencies = [ "alloy-primitives", "async-trait", @@ -538,14 +561,14 @@ dependencies = [ "either", "elliptic-curve", "k256", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-signer-local" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4d992d44e6c414ece580294abbadb50e74cfd4eaa69787350a4dfd4b20eaa1b" +checksum = "1259dac1f534a4c66c1d65237c89915d0010a2a91d6c3b0bada24dc5ee0fb917" dependencies = [ "alloy-consensus", "alloy-network", @@ -554,47 +577,47 @@ dependencies = [ "async-trait", "k256", "rand 0.8.5", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-sol-macro" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ce480400051b5217f19d6e9a82d9010cdde20f1ae9c00d53591e4a1afbb312" +checksum = "25d4b140adc0e4f7bd2b4928d2772fbdf4db114312c4619eff08eba7b6440b9e" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" +checksum = "0512730c0658dab978c9706b592c378974fe49cd14614083a37f7f396aaf2eb5" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.9.0", + "indexmap 2.13.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" +checksum = "b7f5b5f9e6fc8af3420d527cab5eecddc9c437227df251b3c7eb70b0941d8bd5" dependencies = [ "alloy-json-abi", "const-hex", @@ -604,15 +627,15 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.101", + "syn 2.0.114", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954d1b2533b9b2c7959652df3076954ecb1122a28cc740aa84e7b0a49f6ac0a9" +checksum = "d6dbdf239d997b705e1c23cc8bb43f301db615b187379fa923d87723d47fcd31" dependencies = [ "serde", "winnow", @@ -620,9 +643,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70319350969a3af119da6fb3e9bddb1bce66c9ea933600cb297c8b1850ad2a3c" +checksum = "67a8a2c35dbb545c6945b933957556693839df252c712194bf201c9e528670a3" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -632,9 +655,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f50a9516736d22dd834cc2240e5bf264f338667cc1d9e514b55ec5a78b987ca" +checksum = "78f169b85eb9334871db986e7eaf59c58a03d86a30cc68b846573d47ed0656bb" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -645,7 +668,7 @@ dependencies = [ "parking_lot", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tower", "tracing", @@ -655,13 +678,13 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a18b541a6197cf9a084481498a766fdf32fefda0c35ea6096df7d511025e9f1" +checksum = "019821102e70603e2c141954418255bec539ef64ac4117f8e84fb493769acf73" dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.12.22", + "reqwest 0.12.28", "serde_json", "tower", "tracing", @@ -686,14 +709,14 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2289a842d02fe63f8c466db964168bb2c7a9fdfb7b24816dbb17d45520575fb" +checksum = "45ceac797eb8a56bdf5ab1fab353072c17d472eab87645ca847afe720db3246d" dependencies = [ - "darling 0.21.3", + "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -707,15 +730,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "ark-bn254" @@ -790,7 +813,7 @@ checksum = "e7e89fe77d1f0f4fe5b96dfc940923d88d17b6a773808124f21e764dfb063c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -824,7 +847,7 @@ dependencies = [ "ark-std 0.5.0", "educe", "fnv", - "hashbrown 0.15.3", + "hashbrown 0.15.5", "itertools 0.13.0", "num-bigint", "num-integer", @@ -920,7 +943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -958,7 +981,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -1019,7 +1042,7 @@ dependencies = [ "ark-std 0.5.0", "educe", "fnv", - "hashbrown 0.15.3", + "hashbrown 0.15.5", "rayon", ] @@ -1119,7 +1142,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -1233,7 +1256,7 @@ dependencies = [ "rustc-hash", "serde", "serde_derive", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -1250,7 +1273,7 @@ dependencies = [ "rustc-hash", "serde", "serde_derive", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -1289,9 +1312,9 @@ dependencies = [ [[package]] name = "async-compat" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bab94bde396a3f7b4962e396fdad640e241ed797d4d8d77fc8c237d14c58fc0" +checksum = "a1ba85bc55464dcbf728b56d97e119d673f4cf9062be330a9a26f3acf504a590" dependencies = [ "futures-core", "futures-io", @@ -1302,13 +1325,12 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.23" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" +checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" dependencies = [ - "brotli", - "futures-core", - "memchr", + "compression-codecs", + "compression-core", "pin-project-lite", "tokio", ] @@ -1332,18 +1354,18 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -1369,20 +1391,20 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.15.3" +version = "1.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e84ce723ab67259cfeb9877c6a639ee9eb7a27b28123abd71db7f0d5d0cc9d86" +checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256" dependencies = [ "aws-lc-sys", "zeroize", @@ -1390,9 +1412,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a442ece363113bd4bd4c8b18977a7798dd4d3c3383f34fb61936960e8f4ad8" +checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" dependencies = [ "cc", "cmake", @@ -1413,9 +1435,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", @@ -1423,7 +1445,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1446,9 +1468,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "basic-toml" @@ -1485,15 +1507,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitcoin-io" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin_hashes" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ "bitcoin-io", "hex-conservative", @@ -1507,9 +1529,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitvec" @@ -1534,15 +1556,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "cpufeatures", ] [[package]] @@ -1556,9 +1579,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.14" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c79a94619fade3c0b887670333513a67ac28a6a7e653eb260bf0d4103db38d" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" dependencies = [ "cc", "glob", @@ -1586,14 +1609,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "brotli" -version = "8.0.1" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1612,9 +1635,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byte-slice-cast" @@ -1624,9 +1647,9 @@ checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -1636,18 +1659,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] [[package]] name = "c-kzg" -version = "2.1.1" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7318cfa722931cb5fe0838b98d3ce5621e75f6a6408abc21721d80de9223f2e4" +checksum = "e00bf4b112b07b505472dbefd19e37e53307e2bfed5a79e0cc161d58ccd0e687" dependencies = [ "blst", "cc", @@ -1660,11 +1683,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.9" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -1684,17 +1707,17 @@ checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", - "semver 1.0.26", + "semver 1.0.27", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "cc" -version = "1.2.52" +version = "1.2.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" dependencies = [ "find-msvc-tools", "jobserver", @@ -1704,9 +1727,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -1714,18 +1737,42 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[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.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -1755,6 +1802,17 @@ dependencies = [ "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 = "circom-witness-rs" version = "0.2.2" @@ -1780,9 +1838,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785" dependencies = [ "clap_builder", "clap_derive", @@ -1790,9 +1848,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61" dependencies = [ "anstyle", "clap_lex", @@ -1801,21 +1859,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "cmake" @@ -1828,15 +1886,18 @@ dependencies = [ [[package]] name = "cobs" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.18", +] [[package]] name = "codespan-reporting" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" dependencies = [ "serde", "termcolor", @@ -1872,11 +1933,11 @@ dependencies = [ [[package]] name = "colored" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1889,17 +1950,32 @@ dependencies = [ "memchr", ] +[[package]] +name = "compression-codecs" +version = "0.4.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" +dependencies = [ + "brotli", + "compression-core", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + [[package]] name = "const-hex" -version = "1.14.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" +checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" dependencies = [ "cfg-if", "cpufeatures", - "hex", "proptest", - "serde", + "serde_core", ] [[package]] @@ -1910,9 +1986,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_format" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] @@ -1930,9 +2006,18 @@ dependencies = [ [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "core-foundation" @@ -1961,9 +2046,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -2007,9 +2092,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-bigint" @@ -2025,11 +2110,12 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -2040,16 +2126,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "cxx" -version = "1.0.180" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ecd70e33fb57b5fec1608d572bf8dc382f5385a19529056b32307a29ac329be" +checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" dependencies = [ "cc", + "cxx-build", "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", @@ -2059,59 +2146,49 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.158" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a8232661d66dcf713394726157d3cfe0a89bfc85f52d6e9f9bbc2306797fe7" +checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" dependencies = [ "cc", "codespan-reporting", + "indexmap 2.13.0", "proc-macro2", "quote", "scratch", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.180" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64320fd0856fdf2421f8404b67d41e91291cbcfa3d57457b390f0a2618ee9a68" +checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" dependencies = [ "clap", "codespan-reporting", - "indexmap 2.9.0", + "indexmap 2.13.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "cxxbridge-flags" -version = "1.0.180" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e40964f209961217b972415a8e3a0c23299076a0b2dfe79fa8366b5e5c833e" +checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" [[package]] name = "cxxbridge-macro" -version = "1.0.180" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51afdec15d8072d1b69f54f645edaf54250088a7e54c4fe493016781278136bd" +checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.13.0", "proc-macro2", "quote", - "rustversion", - "syn 2.0.101", -] - -[[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", + "syn 2.0.114", ] [[package]] @@ -2120,22 +2197,8 @@ version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", -] - -[[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", - "syn 2.0.101", + "darling_core", + "darling_macro", ] [[package]] @@ -2150,18 +2213,7 @@ dependencies = [ "quote", "serde", "strsim", - "syn 2.0.101", -] - -[[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.101", + "syn 2.0.114", ] [[package]] @@ -2170,9 +2222,9 @@ version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ - "darling_core 0.21.3", + "darling_core", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -2207,12 +2259,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -2228,33 +2280,35 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e73f2692d4bd3cac41dca28934a39894200c9fabf49586d77d0e5954af1d7902" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case", "proc-macro2", "quote", - "syn 2.0.101", + "rustc_version 0.4.1", + "syn 2.0.114", "unicode-xid", ] @@ -2287,7 +2341,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -2304,9 +2358,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ecdsa" @@ -2332,7 +2386,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -2394,27 +2448,27 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "enum-ordinalize" -version = "4.3.0" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" dependencies = [ "enum-ordinalize-derive", ] [[package]] name = "enum-ordinalize-derive" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -2425,12 +2479,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2443,6 +2497,18 @@ dependencies = [ "once_cell", ] +[[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 = "fastrand" version = "2.3.0" @@ -2483,9 +2549,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "fixed-hash" @@ -2513,9 +2579,9 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -2597,7 +2663,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -2649,42 +2715,42 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gloo-timers" @@ -2722,9 +2788,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ "bytes", "fnv", @@ -2732,7 +2798,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.9.0", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -2741,17 +2807,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.9.0", + "http 1.4.0", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -2760,12 +2826,13 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -2797,26 +2864,39 @@ 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.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", ] [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", "foldhash", "serde", + "serde_core", +] + +[[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]] @@ -2841,24 +2921,21 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] [[package]] name = "hex-conservative" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" dependencies = [ "arrayvec", ] @@ -2869,6 +2946,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[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" @@ -2891,12 +2977,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -2918,7 +3003,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -2929,7 +3014,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -2956,14 +3041,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.9", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2972,20 +3057,22 @@ dependencies = [ [[package]] name = "hyper" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", - "h2 0.4.10", - "http 1.3.1", + "futures-core", + "h2 0.4.13", + "http 1.4.0", "http-body 1.0.1", "httparse", "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -3007,41 +3094,40 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", - "http 1.3.1", - "hyper 1.6.0", + "http 1.4.0", + "hyper 1.8.1", "hyper-util", - "rustls 0.23.27", + "rustls 0.23.36", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tower-service", - "webpki-roots 0.26.11", + "webpki-roots 1.0.5", ] [[package]] name = "hyper-util" -version = "0.1.13" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", - "hyper 1.6.0", + "hyper 1.8.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.9", + "socket2 0.6.2", "tokio", "tower-service", "tracing", @@ -3049,9 +3135,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -3073,21 +3159,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -3096,110 +3183,72 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", + "icu_locale_core", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] [[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "ident_case" -version = "1.0.1" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -3208,9 +3257,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -3233,14 +3282,14 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "indenter" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" @@ -3255,24 +3304,23 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] -name = "io-uring" -version = "0.7.8" +name = "inout" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "libc", + "generic-array", ] [[package]] @@ -3283,9 +3331,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -3320,9 +3368,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jobserver" @@ -3330,15 +3378,15 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -3386,15 +3434,26 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] name = "link-cplusplus" @@ -3407,31 +3466,30 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru" @@ -3439,7 +3497,7 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" dependencies = [ - "hashbrown 0.16.0", + "hashbrown 0.16.1", ] [[package]] @@ -3450,9 +3508,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "mach2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" dependencies = [ "libc", ] @@ -3465,7 +3523,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -3479,15 +3537,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] @@ -3527,22 +3585,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -3564,21 +3622,22 @@ dependencies = [ [[package]] name = "mockito" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7760e0e418d9b7e5777c0374009ca4c93861b9066f18cb334a20ce50ab63aa48" +checksum = "7e0603425789b4a70fcc4ac4f5a46a566c116ee3e2a6b768dc623f7719c611de" dependencies = [ "assert-json-diff", "bytes", "colored", - "futures-util", - "http 1.3.1", + "futures-core", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "log", - "rand 0.9.1", + "pin-project-lite", + "rand 0.9.2", "regex", "serde_json", "serde_urlencoded", @@ -3615,7 +3674,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3631,9 +3690,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-integer" @@ -3656,9 +3715,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ "hermit-abi", "libc", @@ -3666,29 +3725,30 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "nybbles" -version = "0.4.1" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "675b3a54e5b12af997abc8b6638b0aee51a28caedab70d4967e0d5db3a3f1d06" +checksum = "7b5676b5c379cf5b03da1df2b3061c4a4e2aa691086a56ac923e08c143f53f59" dependencies = [ "alloy-rlp", "cfg-if", @@ -3700,9 +3760,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -3713,17 +3773,23 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "owo-colors" -version = "4.2.0" +version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" +checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" [[package]] name = "parity-scale-codec" -version = "3.7.4" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9fde3d0718baf5bc92f577d652001da0f8d54cd03a7974e118d04fc888dc23d" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ "arrayvec", "bitvec", @@ -3737,21 +3803,21 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.7.4" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581c837bb6b9541ce7faa9377c20616e4fb7650f6b0f68bc93c827ee504fb7b3" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -3759,15 +3825,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -3778,18 +3844,17 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" dependencies = [ "memchr", - "thiserror 2.0.17", "ucd-trie", ] @@ -3810,7 +3875,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -3835,17 +3900,34 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[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 = "postcard" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" dependencies = [ "cobs", "embedded-io 0.4.0", @@ -3854,6 +3936,15 @@ dependencies = [ "serde", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -3866,7 +3957,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.25", + "zerocopy", ] [[package]] @@ -3882,9 +3973,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] @@ -3908,31 +3999,30 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.0", - "lazy_static", + "bitflags 2.10.0", "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand 0.9.2", + "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -3948,9 +4038,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases", @@ -3958,9 +4048,9 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.27", - "socket2 0.5.9", - "thiserror 2.0.17", + "rustls 0.23.36", + "socket2 0.6.2", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -3968,20 +4058,20 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.2", + "getrandom 0.3.4", "lru-slab", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustc-hash", - "rustls 0.23.27", + "rustls 0.23.36", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -3989,32 +4079,32 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.12" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.9", + "socket2 0.6.2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -4036,12 +4126,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", "serde", ] @@ -4062,7 +4152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -4071,33 +4161,42 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", "serde", ] [[package]] name = "rand_xorshift" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.6.4", + "rand_core 0.9.5", +] + +[[package]] +name = "rapidhash" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" +dependencies = [ + "rustversion", ] [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -4105,9 +4204,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -4115,18 +4214,38 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ - "bitflags 2.9.0", + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -4136,9 +4255,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -4147,9 +4266,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" @@ -4162,7 +4281,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", @@ -4194,36 +4313,34 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "async-compression", "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", - "hyper-rustls 0.27.5", + "hyper 1.8.1", + "hyper-rustls 0.27.7", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.27", + "rustls 0.23.36", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tokio-rustls 0.26.2", - "tokio-util", + "tokio-rustls 0.26.4", "tower", "tower-http", "tower-service", @@ -4231,7 +4348,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 1.0.0", + "webpki-roots 1.0.5", ] [[package]] @@ -4252,7 +4369,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -4289,7 +4406,7 @@ dependencies = [ "primitive-types", "proptest", "rand 0.8.5", - "rand 0.9.1", + "rand 0.9.2", "rlp", "ruint-macro", "serde_core", @@ -4303,11 +4420,25 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags 2.10.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" @@ -4336,20 +4467,20 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.26", + "semver 1.0.27", ] [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4366,16 +4497,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "aws-lc-rs", "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.2", + "rustls-webpki 0.103.9", "subtle", "zeroize", ] @@ -4391,9 +4522,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "web-time", "zeroize", @@ -4411,9 +4542,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.2" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "aws-lc-rs", "ring", @@ -4423,15 +4554,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -4441,9 +4572,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -4454,6 +4585,30 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -4462,9 +4617,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" [[package]] name = "scroll" @@ -4483,7 +4638,7 @@ checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -4641,7 +4796,7 @@ dependencies = [ "proc-macro2", "quote", "semaphore-rs-depth-config", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -4686,7 +4841,7 @@ dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", "ark-groth16 0.4.0", - "getrandom 0.2.16", + "getrandom 0.2.17", "hex", "lazy_static", "ruint", @@ -4780,11 +4935,12 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] @@ -4823,19 +4979,20 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -4852,17 +5009,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", - "serde", - "serde_derive", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.0", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -4870,14 +5028,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ - "darling 0.20.11", + "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -4971,18 +5129,15 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] @@ -4995,9 +5150,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -5005,12 +5160,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5034,9 +5189,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -5061,15 +5216,14 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -5091,9 +5245,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -5102,14 +5256,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.4.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" +checksum = "e629391be459480417646f7ca78894442249262a6e095dbd6e4ab6988cfafce0" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -5135,7 +5289,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -5144,7 +5298,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7dddc5f0fee506baf8b9fdb989e242f17e4b11c61dfbb0635b705217199eea" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "byteorder", "enum-as-inner", "libc", @@ -5187,35 +5341,34 @@ dependencies = [ [[package]] name = "taceo-ark-serde-compat" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "070074111809a0c9344738814302f6271bd79bb38cc1427e889c2b063e66e5e0" +checksum = "9085f4762d38839ca1ab55c7eab2a24961b8eb1fdb639fabd1d015703a389f13" dependencies = [ - "ark-bn254 0.5.0", "ark-ec 0.5.0", + "ark-ff 0.5.0", "serde", "taceo-ark-babyjubjub", - "thiserror 2.0.17", ] [[package]] name = "taceo-ark-serde-compat" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9085f4762d38839ca1ab55c7eab2a24961b8eb1fdb639fabd1d015703a389f13" +checksum = "2a680eee0351c4c95c6a21cccf3327ae85614254f698ef9e4714f9246347df2d" dependencies = [ "ark-bn254 0.5.0", "ark-ec 0.5.0", "ark-ff 0.5.0", + "ark-serialize 0.5.0", "serde", - "taceo-ark-babyjubjub", ] [[package]] name = "taceo-circom-types" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3561b87890d0e3f4787fd08fc07d96c7457139b22b64fa22b6a61e8d8e16cd4" +checksum = "5c3ebb25dbaef5d1d81d45697a64012f782594d696221af0bed04b12ef436983" dependencies = [ "ark-bn254 0.5.0", "ark-ec 0.5.0", @@ -5230,16 +5383,16 @@ dependencies = [ "rayon", "serde", "serde_json", - "taceo-ark-serde-compat 0.2.1", - "thiserror 2.0.17", + "taceo-ark-serde-compat 0.3.0", + "thiserror 2.0.18", "tracing", ] [[package]] name = "taceo-eddsa-babyjubjub" -version = "0.5.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791e41273cc025e07fbca9fcd1d143a3d8cbe3515de8aa6dab91549c9a0af245" +checksum = "965bab462ca0ece0fe2616282f7f8bd5568dbf31611a88748163a99ab4ee15d6" dependencies = [ "ark-ec 0.5.0", "ark-ff 0.5.0", @@ -5250,16 +5403,16 @@ dependencies = [ "rand 0.8.5", "serde", "taceo-ark-babyjubjub", - "taceo-ark-serde-compat 0.1.0", - "taceo-poseidon2 0.1.0", + "taceo-ark-serde-compat 0.2.1", + "taceo-poseidon2 0.2.1", "zeroize", ] [[package]] name = "taceo-groth16" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3efa0cf1b7e336c54092e291fbc8403cccc89947737a6fb6f97398882bef9c6" +checksum = "1347d8d865fa80afba27470d77b5bf9391dd24d6f9bc7da42b3b973246888894" dependencies = [ "ark-ec 0.5.0", "ark-ff 0.5.0", @@ -5292,7 +5445,7 @@ dependencies = [ "sha2", "taceo-circom-types", "taceo-groth16", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] @@ -5325,8 +5478,8 @@ dependencies = [ "taceo-ark-babyjubjub", "taceo-oprf-core", "taceo-oprf-types", - "taceo-poseidon2 0.2.0", - "thiserror 2.0.17", + "taceo-poseidon2 0.2.1", + "thiserror 2.0.18", "tokio", "tokio-tungstenite", "tracing", @@ -5350,7 +5503,7 @@ dependencies = [ "subtle", "taceo-ark-babyjubjub", "taceo-ark-serde-compat 0.2.1", - "taceo-poseidon2 0.2.0", + "taceo-poseidon2 0.2.1", "uuid", "zeroize", ] @@ -5364,7 +5517,7 @@ dependencies = [ "alloy", "ark-ff 0.5.0", "eyre", - "http 1.3.1", + "http 1.4.0", "serde", "taceo-ark-babyjubjub", "taceo-ark-serde-compat 0.2.1", @@ -5390,9 +5543,9 @@ dependencies = [ [[package]] name = "taceo-poseidon2" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbf106fb8682ee4e057872a18f431828bd467c28d2ead469e4c84dbf6ce5ec6" +checksum = "ac59d3df4c827b3496bff929aebd6440997a5c2e946f46ff4fdd76f318447581" dependencies = [ "ark-bn254 0.5.0", "ark-ff 0.5.0", @@ -5409,15 +5562,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5449,11 +5602,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -5464,28 +5617,27 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -5499,30 +5651,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" dependencies = [ "num-conv", "time-core", @@ -5539,9 +5691,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -5549,9 +5701,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -5564,32 +5716,29 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", - "slab", - "socket2 0.6.0", + "socket2 0.6.2", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -5604,19 +5753,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.27", + "rustls 0.23.36", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -5626,12 +5775,10 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +checksum = "3f6d24790a10a7af737693a3e8f1d03faef7e6ca0cc99aae5066f533766de545" dependencies = [ - "async-stream", - "bytes", "futures-core", "tokio", "tokio-stream", @@ -5645,19 +5792,19 @@ checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", - "rustls 0.23.27", + "rustls 0.23.36", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tungstenite", "webpki-roots 0.26.11", ] [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -5677,26 +5824,39 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.13.0", "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ "winnow", ] [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -5709,17 +5869,22 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.9.0", + "async-compression", + "bitflags 2.10.0", "bytes", + "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", + "http-body-util", "iri-string", "pin-project-lite", + "tokio", + "tokio-util", "tower", "tower-layer", "tower-service", @@ -5739,9 +5904,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -5750,20 +5915,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -5776,7 +5941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", ] [[package]] @@ -5801,9 +5966,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -5831,22 +5996,22 @@ checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", - "http 1.3.1", + "http 1.4.0", "httparse", "log", - "rand 0.9.1", - "rustls 0.23.27", + "rand 0.9.2", + "rustls 0.23.36", "rustls-pki-types", "sha1", - "thiserror 2.0.17", + "thiserror 2.0.18", "utf-8", ] [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" @@ -5874,15 +6039,21 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -5892,9 +6063,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "uniffi" -version = "0.29.2" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd1d240101ba3b9d7532ae86d9cb64d9a7ff63e13a2b7b9e94a32a601d8233" +checksum = "3291800a6b06569f7d3e15bdb6dc235e0f0c8bd3eb07177f430057feb076415f" dependencies = [ "anyhow", "camino", @@ -5916,9 +6087,9 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.29.2" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0525f06d749ea80d8049dc0bb038bb87941e3d909eefa76b6f0a5589b59ac5" +checksum = "a04b99fa7796eaaa7b87976a0dbdd1178dc1ee702ea00aca2642003aef9b669e" dependencies = [ "anyhow", "askama 0.13.1", @@ -5928,7 +6099,7 @@ dependencies = [ "glob", "goblin", "heck", - "indexmap 2.9.0", + "indexmap 2.13.0", "once_cell", "serde", "tempfile", @@ -5942,9 +6113,9 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.29.2" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aed2f0204e942bb9c11c9f11a323b4abf70cf11b2e5957d60b3f2728430f6c6f" +checksum = "025a05cba02ee22b6624ac3d257e59c7395319ea8fe1aae33a7cdb4e2a3016cc" dependencies = [ "anyhow", "camino", @@ -5953,9 +6124,9 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.29.2" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fa8eb4d825b4ed095cb13483cba6927c3002b9eb603cef9b7688758cc3772e" +checksum = "f38a9a27529ccff732f8efddb831b65b1e07f7dea3fd4cacd4a35a8c4b253b98" dependencies = [ "anyhow", "async-compat", @@ -5966,22 +6137,22 @@ dependencies = [ [[package]] name = "uniffi_internal_macros" -version = "0.29.2" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b547d69d699e52f2129fde4b57ae0d00b5216e59ed5b56097c95c86ba06095" +checksum = "09acd2ce09c777dd65ee97c251d33c8a972afc04873f1e3b21eb3492ade16933" dependencies = [ "anyhow", - "indexmap 2.9.0", + "indexmap 2.13.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "uniffi_macros" -version = "0.29.2" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f1de72edc8cb9201c7d650e3678840d143e4499004571aac49e6cb1b17da43" +checksum = "5596f178c4f7aafa1a501c4e0b96236a96bc2ef92bdb453d83e609dad0040152" dependencies = [ "camino", "fs-err", @@ -5989,16 +6160,16 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.101", + "syn 2.0.114", "toml", "uniffi_meta", ] [[package]] name = "uniffi_meta" -version = "0.29.2" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acc9204632f6a555b2cba7c8852c5523bc1aa5f3eff605c64af5054ea28b72e" +checksum = "beadc1f460eb2e209263c49c4f5b19e9a02e00a3b2b393f78ad10d766346ecff" dependencies = [ "anyhow", "siphasher", @@ -6008,22 +6179,22 @@ dependencies = [ [[package]] name = "uniffi_pipeline" -version = "0.29.2" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b5336a9a925b358183837d31541d12590b7fcec373256d3770de02dff24c69" +checksum = "dd76b3ac8a2d964ca9fce7df21c755afb4c77b054a85ad7a029ad179cc5abb8a" dependencies = [ "anyhow", "heck", - "indexmap 2.9.0", + "indexmap 2.13.0", "tempfile", "uniffi_internal_macros", ] [[package]] name = "uniffi_udl" -version = "0.29.2" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95e73373d85f04736bc51997d3e6855721144ec4384cae9ca8513c80615e129" +checksum = "4319cf905911d70d5b97ce0f46f101619a22e9a189c8c46d797a9955e9233716" dependencies = [ "anyhow", "textwrap", @@ -6031,6 +6202,16 @@ dependencies = [ "weedle2", ] +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -6039,14 +6220,15 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -6055,12 +6237,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -6069,13 +6245,13 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.18.1" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -6085,6 +6261,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[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" @@ -6125,28 +6307,35 @@ dependencies = [ "alloy", "alloy-core", "alloy-primitives", + "chacha20poly1305", "chrono", + "ciborium", "ctor", "dotenvy", "hex", + "hkdf", "log", "mockito", "rand 0.8.5", "regex", - "reqwest 0.12.22", + "reqwest 0.12.28", "ruint", - "rustls 0.23.27", + "rusqlite", + "rustls 0.23.36", "secrecy", "semaphore-rs", "serde", "serde_json", + "sha2", "strum", "subtle", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-test", "uniffi", + "uuid", "world-id-core", + "zeroize", ] [[package]] @@ -6160,52 +6349,40 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.101", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -6214,9 +6391,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6224,31 +6401,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.101", - "wasm-bindgen-backend", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] [[package]] name = "wasmtimer" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" dependencies = [ "futures", "js-sys", @@ -6260,9 +6437,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -6290,14 +6467,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.0", + "webpki-roots 1.0.5", ] [[package]] name = "webpki-roots" -version = "1.0.0" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" dependencies = [ "rustls-pki-types", ] @@ -6313,17 +6490,17 @@ dependencies = [ [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6337,67 +6514,61 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.1.1", + "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] name = "windows-link" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.1.1", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.1.1", + "windows-link", ] [[package]] @@ -6420,11 +6591,20 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.52.6", + "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]] @@ -6451,13 +6631,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -6470,6 +6667,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -6482,6 +6685,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -6494,12 +6703,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -6512,6 +6733,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -6524,6 +6751,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -6536,6 +6769,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -6548,11 +6787,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -6568,13 +6813,10 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.0", -] +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "world-id-core" @@ -6595,9 +6837,9 @@ dependencies = [ "hex", "k256", "rand 0.8.5", - "reqwest 0.12.22", + "reqwest 0.12.28", "ruint", - "rustls 0.23.27", + "rustls 0.23.36", "secrecy", "serde", "serde_json", @@ -6613,8 +6855,8 @@ dependencies = [ "thiserror 1.0.69", "tokio", "tracing", - "tracing-subscriber 0.3.20", - "webpki-roots 1.0.0", + "tracing-subscriber 0.3.22", + "webpki-roots 1.0.5", "world-id-primitives", ] @@ -6648,17 +6890,11 @@ dependencies = [ "url", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wyz" @@ -6671,11 +6907,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -6683,54 +6918,34 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", "synstructure", ] [[package]] name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" -dependencies = [ - "zerocopy-derive 0.8.25", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "71ddd76bcebeed25db614f82bf31a9f4222d3fbba300e6fb6c00afa26cbd4d9d" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "d8187381b52e32220d50b255276aa16a084ec0a9017a0ca2152a1f55c539758d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] [[package]] @@ -6750,35 +6965,46 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", ] [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -6787,11 +7013,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.114", ] + +[[package]] +name = "zmij" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" diff --git a/kotlin/README.md b/kotlin/README.md index 9585b3158..adf15f084 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -22,5 +22,6 @@ This folder contains support files for WalletKit to work in Kotlin: ## Kotlin project structure The Kotlin project has two members: + - `walletkit`: The main WalletKit library with UniFFI bindings for Kotlin. - `walletkit-tests`: Unit tests to assert the Kotlin bindings behave as intended (foreign tests). diff --git a/kotlin/walletkit-android/build.gradle.kts b/kotlin/walletkit-android/build.gradle.kts new file mode 100644 index 000000000..e6f047448 --- /dev/null +++ b/kotlin/walletkit-android/build.gradle.kts @@ -0,0 +1,80 @@ +import java.io.ByteArrayOutputStream + +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") + id("maven-publish") +} + +kotlin { + jvmToolchain(17) +} + +android { + namespace = "org.world.walletkit" + compileSdk = 33 + + defaultConfig { + minSdk = 23 + @Suppress("deprecation") + targetSdk = 33 + consumerProguardFiles("consumer-rules.pro") + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" + } + + publishing { + singleVariant("release") { + withSourcesJar() + } + } +} + +afterEvaluate { + publishing { + publications { + create("maven") { + groupId = "org.world" + artifactId = "walletkit-android" + + // Read version from Cargo.toml + val cargoToml = file("../../Cargo.toml") + val versionRegex = """version\s*=\s*"([^"]+)"""".toRegex() + val cargoContent = cargoToml.readText() + version = versionRegex.find(cargoContent)?.groupValues?.get(1) + ?: throw GradleException("Could not find version in Cargo.toml") + + afterEvaluate { + from(components["release"]) + } + } + } + + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/worldcoin/walletkit") + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } + } +} + +dependencies { + // UniFFI requires JNA for native calls + implementation("net.java.dev.jna:jna:5.13.0") + implementation("androidx.core:core-ktx:1.8.0") + implementation("androidx.appcompat:appcompat:1.4.1") + implementation("com.google.android.material:material:1.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") +} diff --git a/kotlin/walletkit-android/consumer-rules.pro b/kotlin/walletkit-android/consumer-rules.pro new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/kotlin/walletkit-android/consumer-rules.pro @@ -0,0 +1 @@ + diff --git a/swift/tests/WalletKitTests/AuthenticatorTests.swift b/swift/tests/WalletKitTests/AuthenticatorTests.swift deleted file mode 100644 index c370cdb05..000000000 --- a/swift/tests/WalletKitTests/AuthenticatorTests.swift +++ /dev/null @@ -1,400 +0,0 @@ -import XCTest -@testable import WalletKit - -final class AuthenticatorTests: XCTestCase { - - let testRpcUrl = "https://worldchain-sepolia.g.alchemy.com/public" - - // MARK: - Helper Functions - - struct U256HexTestCase { - let hexInput: String - let expectedDecimal: String - let expectedHex: String - } - - func generateRandomSeed() -> Data { - var bytes = [UInt8](repeating: 0, count: 32) - for index in 0..<32 { - bytes[index] = UInt8.random(in: 0...255) - } - return Data(bytes) - } - - // MARK: - U256Wrapper Tests - - func testU256WrapperFromU64() { - let value: UInt64 = 12345 - let u256 = U256Wrapper.fromU64(value: value) - XCTAssertEqual(u256.toDecimalString(), "12345") - } - - func testU256WrapperFromU32() { - let value: UInt32 = 54321 - let u256 = U256Wrapper.fromU32(value: value) - XCTAssertEqual(u256.toDecimalString(), "54321") - } - - func testU256WrapperFromU64MaxValue() { - // Test with max u64 value - let maxU64 = UInt64.max - let u256 = U256Wrapper.fromU64(value: maxU64) - XCTAssertEqual(u256.toDecimalString(), "18446744073709551615") - XCTAssertEqual(u256.toHexString(), "0x000000000000000000000000000000000000000000000000ffffffffffffffff") - } - - func testU256WrapperFromU32MaxValue() { - // Test with max u32 value - let maxU32 = UInt32.max - let u256 = U256Wrapper.fromU32(value: maxU32) - XCTAssertEqual(u256.toDecimalString(), "4294967295") - } - - func testU256WrapperTryFromHexString() throws { - let hexString = "0x1a2b3c4d5e6f" - let u256 = try U256Wrapper.tryFromHexString(hexString: hexString) - XCTAssertNotNil(u256) - // Verify the hex round-trips correctly - XCTAssertTrue(u256.toHexString().hasSuffix("1a2b3c4d5e6f")) - } - - func testU256WrapperTryFromHexStringWithoutPrefix() throws { - let hexString = "1a2b3c4d5e6f" - let u256 = try U256Wrapper.tryFromHexString(hexString: hexString) - XCTAssertNotNil(u256) - } - - func testU256WrapperDeterministicHexParsing() throws { - // Test with known values from Rust tests - let testCases: [U256HexTestCase] = [ - U256HexTestCase( - hexInput: "0x0000000000000000000000000000000000000000000000000000000000000001", - expectedDecimal: "1", - expectedHex: "0x0000000000000000000000000000000000000000000000000000000000000001" - ), - U256HexTestCase( - hexInput: "0x000000000000000000000000000000000000000000000000000000000000002a", - expectedDecimal: "42", - expectedHex: "0x000000000000000000000000000000000000000000000000000000000000002a" - ), - U256HexTestCase( - hexInput: "0x00000000000000000000000000000000000000000000000000000000000f423f", - expectedDecimal: "999999", - expectedHex: "0x00000000000000000000000000000000000000000000000000000000000f423f" - ), - U256HexTestCase( - hexInput: "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", - expectedDecimal: "80084422859880547211683076133703299733277748156566366325829078699459944778998", - expectedHex: "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" - ) - ] - - for testCase in testCases { - let u256 = try U256Wrapper.tryFromHexString(hexString: testCase.hexInput) - XCTAssertEqual( - u256.toDecimalString(), - testCase.expectedDecimal, - "Decimal mismatch for \(testCase.hexInput)" - ) - XCTAssertEqual( - u256.toHexString(), - testCase.expectedHex, - "Hex mismatch for \(testCase.hexInput)" - ) - } - } - - func testU256WrapperHexRoundTrip() throws { - // Test that parsing and formatting hex strings round-trips correctly - let hexStrings = [ - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x00000000000000000000000000000000000000000000000000000000000000ff", - "0x0000000000000000000000000000000000000000000000000000000000001234", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ] - - for hexString in hexStrings { - let u256 = try U256Wrapper.tryFromHexString(hexString: hexString) - XCTAssertEqual(u256.toHexString(), hexString, "Round-trip failed for \(hexString)") - } - } - - func testU256WrapperInvalidHexString() { - XCTAssertThrowsError(try U256Wrapper.tryFromHexString(hexString: "0xZZZ")) { error in - XCTAssertTrue(error is WalletKitError) - } - } - - func testU256WrapperInvalidHexStrings() { - // Test multiple invalid inputs - let invalidInputs = [ - "0xZZZZ", - "1g", - "not a hex string", - "0xGGGG" - ] - - for invalidInput in invalidInputs { - XCTAssertThrowsError(try U256Wrapper.tryFromHexString(hexString: invalidInput)) { error in - XCTAssertTrue(error is WalletKitError, "Should throw WalletKitError for: \(invalidInput)") - } - } - } - - func testU256WrapperEmptyString() throws { - // Empty string parses as 0 (after trimming "0x", "" is passed to radix parser) - let u256 = try U256Wrapper.tryFromHexString(hexString: "") - XCTAssertEqual(u256.toDecimalString(), "0") - XCTAssertEqual(u256.toHexString(), "0x0000000000000000000000000000000000000000000000000000000000000000") - } - - func testU256WrapperFromLimbs() throws { - // Test with simple value [1, 0, 0, 0] - let limbs: [UInt64] = [1, 0, 0, 0] - let u256 = try U256Wrapper.fromLimbs(limbs: limbs) - XCTAssertEqual(u256.toDecimalString(), "1") - } - - func testU256WrapperFromLimbsComplexValue() throws { - // Test with complex limb values from Rust tests - let limbs: [UInt64] = [1, 0, 0, 2161727821137838080] - let u256 = try U256Wrapper.fromLimbs(limbs: limbs) - XCTAssertEqual( - u256.toHexString(), - "0x1e00000000000000000000000000000000000000000000000000000000000001" - ) - } - - func testU256WrapperFromLimbsInvalidLength() { - // Must be exactly 4 limbs - XCTAssertThrowsError(try U256Wrapper.fromLimbs(limbs: [1, 0, 0])) { error in - XCTAssertTrue(error is WalletKitError) - } - - XCTAssertThrowsError(try U256Wrapper.fromLimbs(limbs: [1, 0, 0, 0, 5])) { error in - XCTAssertTrue(error is WalletKitError) - } - - XCTAssertThrowsError(try U256Wrapper.fromLimbs(limbs: [])) { error in - XCTAssertTrue(error is WalletKitError) - } - } - - func testU256WrapperToHexString() { - let u256 = U256Wrapper.fromU64(value: 42) - let hexString = u256.toHexString() - // Should be padded to 66 characters (0x + 64 hex digits) - XCTAssertEqual(hexString.count, 66) - XCTAssertTrue(hexString.hasPrefix("0x")) - XCTAssertTrue(hexString.hasSuffix("2a")) - } - - func testU256WrapperToHexStringPadding() { - // Test that small values are properly padded - let testCases: [(UInt64, String)] = [ - (1, "0x0000000000000000000000000000000000000000000000000000000000000001"), - (2, "0x0000000000000000000000000000000000000000000000000000000000000002"), - (255, "0x00000000000000000000000000000000000000000000000000000000000000ff") - ] - - for (value, expectedHex) in testCases { - let u256 = U256Wrapper.fromU64(value: value) - XCTAssertEqual(u256.toHexString(), expectedHex) - } - } - - func testU256WrapperIntoLimbs() { - let u256 = U256Wrapper.fromU64(value: 12345) - let limbs = u256.intoLimbs() - XCTAssertEqual(limbs.count, 4) - XCTAssertEqual(limbs[0], 12345) - XCTAssertEqual(limbs[1], 0) - XCTAssertEqual(limbs[2], 0) - XCTAssertEqual(limbs[3], 0) - } - - func testU256WrapperLimbsRoundTrip() throws { - // Test that converting to/from limbs round-trips correctly - let originalLimbs: [UInt64] = [12345, 67890, 11111, 22222] - let u256 = try U256Wrapper.fromLimbs(limbs: originalLimbs) - let resultLimbs = u256.intoLimbs() - - XCTAssertEqual(resultLimbs, originalLimbs) - } - - func testU256WrapperZeroValue() { - let u256 = U256Wrapper.fromU64(value: 0) - XCTAssertEqual(u256.toDecimalString(), "0") - XCTAssertEqual(u256.toHexString(), "0x0000000000000000000000000000000000000000000000000000000000000000") - - let limbs = u256.intoLimbs() - XCTAssertEqual(limbs, [0, 0, 0, 0]) - } - - func testU256WrapperMultipleConversions() throws { - // Test creating U256 from different sources and verifying consistency - let value: UInt64 = 999999 - - let fromU64 = U256Wrapper.fromU64(value: value) - let fromHex = try U256Wrapper.tryFromHexString( - hexString: "0x00000000000000000000000000000000000000000000000000000000000f423f" - ) - let fromLimbs = try U256Wrapper.fromLimbs(limbs: [999999, 0, 0, 0]) - - // All should produce the same decimal string - XCTAssertEqual(fromU64.toDecimalString(), "999999") - XCTAssertEqual(fromHex.toDecimalString(), "999999") - XCTAssertEqual(fromLimbs.toDecimalString(), "999999") - - // All should produce the same hex string - let expectedHex = "0x00000000000000000000000000000000000000000000000000000000000f423f" - XCTAssertEqual(fromU64.toHexString(), expectedHex) - XCTAssertEqual(fromHex.toHexString(), expectedHex) - XCTAssertEqual(fromLimbs.toHexString(), expectedHex) - } - - // MARK: - Authenticator Initialization Tests - - func testInvalidSeedEmpty() async { - let emptySeed = Data() - - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: emptySeed, - rpcUrl: testRpcUrl, - environment: .staging - ) - ) { error in - if let walletError = error as? WalletKitError, - case .InvalidInput(let attribute, _) = walletError { - XCTAssertEqual(attribute, "seed") - } else { - XCTFail("Expected InvalidInput for seed, got \(error)") - } - } - } - - func testInvalidSeedTooShort() async { - let shortSeed = Data(repeating: 0, count: 16) - - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: shortSeed, - rpcUrl: testRpcUrl, - environment: .staging - ) - ) { error in - if let walletError = error as? WalletKitError, - case .InvalidInput(let attribute, _) = walletError { - XCTAssertEqual(attribute, "seed") - } else { - XCTFail("Expected InvalidInput for seed, got \(error)") - } - } - } - - func testInvalidSeedTooLong() async { - let longSeed = Data(repeating: 0, count: 64) - - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: longSeed, - rpcUrl: testRpcUrl, - environment: .staging - ) - ) { error in - if let walletError = error as? WalletKitError, - case .InvalidInput(let attribute, _) = walletError { - XCTAssertEqual(attribute, "seed") - } else { - XCTFail("Expected InvalidInput for seed, got \(error)") - } - } - } - - func testInvalidRpcUrlEmpty() async { - let seed = generateRandomSeed() - - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: seed, - rpcUrl: "", - environment: .staging - ) - ) { error in - if let walletError = error as? WalletKitError, - case .InvalidInput(let attribute, _) = walletError { - XCTAssertEqual(attribute, "rpc_url") - } else { - XCTFail("Expected InvalidInput for rpc_url, got \(error)") - } - } - } - - func testMultipleEnvironments() async { - let seed = generateRandomSeed() - let environments: [Environment] = [.staging, .production] - - for environment in environments { - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: seed, - rpcUrl: testRpcUrl, - environment: environment - ) - ) { error in - // Should throw an error for non-existent account in any environment - XCTAssertTrue(error is WalletKitError, "Should throw WalletKitError for \(environment)") - } - } - } - - func testValidSeedLength() { - let validSeed = Data(repeating: 0, count: 32) - XCTAssertEqual(validSeed.count, 32, "Valid seed should be 32 bytes") - } - - func testGenerateRandomSeedLength() { - let seed = generateRandomSeed() - XCTAssertEqual(seed.count, 32, "Generated seed should be 32 bytes") - } - - func testGenerateRandomSeedRandomness() { - // Generate multiple seeds and verify they're different - let seed1 = generateRandomSeed() - let seed2 = generateRandomSeed() - let seed3 = generateRandomSeed() - - XCTAssertNotEqual(seed1, seed2, "Seeds should be random and different") - XCTAssertNotEqual(seed2, seed3, "Seeds should be random and different") - XCTAssertNotEqual(seed1, seed3, "Seeds should be random and different") - } - - // MARK: - Helper for async error assertions - - func XCTAssertThrowsErrorAsync( - _ expression: @autoclosure () async throws -> T, - _ message: @autoclosure () -> String = "", - file: StaticString = #filePath, - line: UInt = #line, - _ errorHandler: (_ error: Error) -> Void = { _ in } - ) async { - do { - _ = try await expression() - XCTFail(message(), file: file, line: line) - } catch { - errorHandler(error) - } - } - - // MARK: - Environment Tests - - func testEnvironmentValues() { - // Just verify environments exist and can be created - let staging = Environment.staging - let production = Environment.production - - XCTAssertNotNil(staging) - XCTAssertNotNil(production) - } -} diff --git a/swift/tests/WalletKitTests/SimpleTest.swift b/swift/tests/WalletKitTests/SimpleTest.swift new file mode 100644 index 000000000..fb30acd50 --- /dev/null +++ b/swift/tests/WalletKitTests/SimpleTest.swift @@ -0,0 +1,8 @@ +import XCTest +@testable import WalletKit + +final class SimpleTest: XCTestCase { + func simpleTest() { + XCTAssertEqual(1, 1) + } +} diff --git a/walletkit-core/Cargo.toml b/walletkit-core/Cargo.toml index 26d02e22e..343cefe0e 100644 --- a/walletkit-core/Cargo.toml +++ b/walletkit-core/Cargo.toml @@ -25,7 +25,9 @@ alloy-core = { workspace = true } alloy-primitives = { workspace = true } ctor = "0.2" hex = "0.4" +hkdf = { version = "0.12", optional = true } log = "0.4" +rand = { version = "0.8", optional = true } reqwest = { version = "0.12", default-features = false, features = [ "json", "brotli", @@ -40,15 +42,29 @@ secrecy = "0.10" semaphore-rs = { version = "0.5" } serde = "1" serde_json = "1" +sha2 = { version = "0.10", optional = true } strum = { version = "0.27", features = ["derive"] } subtle = "2" thiserror = "2" tokio = { version = "1", features = ["sync"] } +zeroize = "1" +rusqlite = { version = "0.32", features = [ + "bundled-sqlcipher", +], optional = true } +uuid = { version = "1.10", features = ["v4"], optional = true } uniffi = { workspace = true, features = ["build", "tokio"] } world-id-core = { workspace = true, optional = true } +ciborium = { version = "0.2.2", optional = true } [dev-dependencies] -alloy = { version = "1", default-features = false, features = ["getrandom", "json", "contract", "node-bindings", "signer-local"] } +alloy = { version = "1", default-features = false, features = [ + "getrandom", + "json", + "contract", + "node-bindings", + "signer-local", +] } +chacha20poly1305 = "0.10" chrono = "0.4.41" dotenvy = "0.15.7" mockito = "1.6" @@ -63,7 +79,15 @@ default = ["common-apps", "semaphore", "v4"] common-apps = [] http-tests = [] semaphore = ["semaphore-rs/depth_30"] -v4 = ["world-id-core"] +storage = [ + "dep:ciborium", + "dep:hkdf", + "dep:rand", + "dep:rusqlite", + "dep:sha2", + "dep:uuid", +] +v4 = ["world-id-core", "storage"] # Before conventions were introduced for external nullifiers with `app_id` & `action`, raw field elements were used. # This feature flag adds support to operate with such external nullifiers. diff --git a/walletkit-core/src/authenticator.rs b/walletkit-core/src/authenticator/mod.rs similarity index 99% rename from walletkit-core/src/authenticator.rs rename to walletkit-core/src/authenticator/mod.rs index fcf2cffaf..c369582cd 100644 --- a/walletkit-core/src/authenticator.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -11,6 +11,9 @@ use crate::{ primitives::ParseFromForeignBinding, Environment, U256Wrapper, }; +mod storage; +mod utils; + /// The Authenticator is the main component with which users interact with the World ID Protocol. #[derive(Debug, uniffi::Object)] pub struct Authenticator(CoreAuthenticator); diff --git a/walletkit-core/src/authenticator/storage.rs b/walletkit-core/src/authenticator/storage.rs new file mode 100644 index 000000000..962126470 --- /dev/null +++ b/walletkit-core/src/authenticator/storage.rs @@ -0,0 +1,323 @@ +use std::sync::Arc; + +use serde::{Deserialize, Serialize}; +use world_id_core::primitives::authenticator::AuthenticatorPublicKeySet; +use world_id_core::primitives::merkle::MerkleInclusionProof; +use world_id_core::primitives::TREE_DEPTH; +use world_id_core::{ + requests::ProofRequest, Credential, FieldElement, OnchainKeyRepresentable, +}; + +use crate::error::WalletKitError; +use crate::storage::{CredentialStore, ReplayGuardKind, ReplayGuardResult}; +use crate::U256Wrapper; + +use super::utils::{leaf_index_to_u64, parse_fixed_bytes, u256_to_hex}; +use super::Authenticator; + +/// Buffer cached proofs to remain valid during on-chain verification. +const MERKLE_PROOF_VALIDITY_BUFFER_SECS: u64 = 120; + +#[uniffi::export(async_runtime = "tokio")] +impl Authenticator { + /// Initializes storage using the authenticator's leaf index. + /// + /// # Errors + /// + /// Returns an error if the leaf index is invalid or storage initialization fails. + #[allow(clippy::needless_pass_by_value)] + pub fn init_storage( + &self, + storage: Arc, + now: u64, + ) -> Result<(), WalletKitError> { + let leaf_index = leaf_index_to_u64(&self.leaf_index())?; + storage.init(leaf_index, now)?; + Ok(()) + } + + /// Fetches an inclusion proof, using the storage cache when possible. + /// + /// The cached payload uses `CachedInclusionProof` CBOR encoding and is keyed by + /// (`registry_kind`, `root`, `leaf_index`). + /// + /// Returns the decoded proof with hex-encoded field elements and compressed + /// authenticator public keys. + /// + /// # Errors + /// + /// Returns an error if fetching or caching the proof fails. + #[allow(clippy::future_not_send)] + pub async fn fetch_inclusion_proof_cached( + &self, + storage: Arc, + registry_kind: u8, + root: Vec, + now: u64, + ttl_seconds: u64, + ) -> Result { + let root = parse_fixed_bytes::<32>(root, "root")?; + let valid_before = now.saturating_add(MERKLE_PROOF_VALIDITY_BUFFER_SECS); + if let Some(bytes) = + storage.merkle_cache_get(registry_kind, root.to_vec(), valid_before)? + { + if let Some(cached) = CachedInclusionProof::deserialize(&bytes) { + return inclusion_proof_payload_from_cached(&cached); + } + } + + let (proof, key_set) = self.0.fetch_inclusion_proof().await?; + let payload = CachedInclusionProof { + proof: proof.clone(), + authenticator_pubkeys: key_set, + }; + let payload_bytes = payload.serialize()?; + let proof_root = { + let mut bytes = Vec::new(); + proof.root.serialize_as_bytes(&mut bytes)?; + parse_fixed_bytes::<32>(bytes, "field_element")? + }; + if proof_root != root { + return Err(WalletKitError::InvalidInput { + attribute: "root".to_string(), + reason: "fetched proof root does not match requested root".to_string(), + }); + } + storage.merkle_cache_put( + registry_kind, + root.to_vec(), + payload_bytes, + now, + ttl_seconds, + )?; + inclusion_proof_payload_from_cached(&payload) + } + + /// Generates a proof and enforces replay safety via storage. + /// + /// The proof request and credential are supplied as JSON strings. + /// + /// # Errors + /// + /// Returns an error if the proof generation or storage update fails. + #[allow(clippy::too_many_arguments)] + #[allow(clippy::future_not_send)] + #[allow(clippy::unused_async)] + pub async fn generate_proof_with_replay_guard( + &self, + storage: Arc, + proof_request_json: &str, + credential_json: &str, + credential_sub_blinding_factor: &U256Wrapper, + request_id: Vec, + now: u64, + ttl_seconds: u64, + ) -> Result { + let proof_request = + ProofRequest::from_json(proof_request_json).map_err(|err| { + WalletKitError::InvalidInput { + attribute: "proof_request".to_string(), + reason: err.to_string(), + } + })?; + let credential: Credential = + serde_json::from_str(credential_json).map_err(|err| { + WalletKitError::InvalidInput { + attribute: "credential".to_string(), + reason: err.to_string(), + } + })?; + let request_id = parse_fixed_bytes::<32>(request_id, "request_id")?; + let credential_sub_blinding_factor = FieldElement::try_from( + credential_sub_blinding_factor.0, + ) + .map_err(|err| WalletKitError::InvalidInput { + attribute: "credential_sub_blinding_factor".to_string(), + reason: err.to_string(), + })?; + + if let Some(bytes) = storage + .replay_guard_get(request_id.to_vec(), now) + .map_err(WalletKitError::from)? + { + return Ok(ReplayGuardResult { + kind: ReplayGuardKind::Replay, + bytes, + }); + } + let (proof, nullifier) = tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + self.0 + .generate_proof( + proof_request, + credential, + credential_sub_blinding_factor, + ) + .await + }) + })?; + let proof_bytes = serialize_proof_package(&proof, nullifier)?; + let nullifier_bytes = { + let mut bytes = Vec::new(); + nullifier.serialize_as_bytes(&mut bytes)?; + parse_fixed_bytes::<32>(bytes, "field_element")? + }; + storage + .begin_replay_guard( + request_id.to_vec(), + nullifier_bytes.to_vec(), + proof_bytes, + now, + ttl_seconds, + ) + .map_err(WalletKitError::from) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct CachedInclusionProof { + proof: MerkleInclusionProof, + authenticator_pubkeys: AuthenticatorPublicKeySet, +} + +impl CachedInclusionProof { + fn serialize(&self) -> Result, WalletKitError> { + let mut bytes = Vec::new(); + ciborium::ser::into_writer(self, &mut bytes).map_err(|err| { + WalletKitError::SerializationError { + error: err.to_string(), + } + })?; + Ok(bytes) + } + + fn deserialize(bytes: &[u8]) -> Option { + ciborium::de::from_reader(bytes).ok() + } +} + +#[derive(Debug, Clone, uniffi::Record)] +pub struct InclusionProofPayload { + /// Merkle root as hex string. + pub root: String, + /// Leaf index for the account. + pub leaf_index: u64, + /// Sibling path as hex strings. + pub siblings: Vec, + /// Compressed authenticator public keys as hex strings. + pub authenticator_pubkeys: Vec, +} + +fn inclusion_proof_payload_from_cached( + payload: &CachedInclusionProof, +) -> Result { + let authenticator_pubkeys = payload + .authenticator_pubkeys + .iter() + .map(|pk| { + let encoded = pk.to_ethereum_representation().map_err(|err| { + WalletKitError::Generic { + error: format!("failed to encode authenticator pubkey: {err}"), + } + })?; + Ok(u256_to_hex(encoded)) + }) + .collect::, WalletKitError>>()?; + + Ok(InclusionProofPayload { + root: payload.proof.root.to_string(), + leaf_index: payload.proof.leaf_index, + siblings: payload + .proof + .siblings + .iter() + .map(std::string::ToString::to_string) + .collect(), + authenticator_pubkeys, + }) +} +fn serialize_proof_package( + proof: &impl Serialize, + nullifier: FieldElement, +) -> Result, WalletKitError> { + let mut bytes = Vec::new(); + ciborium::ser::into_writer(&(proof, nullifier), &mut bytes).map_err(|err| { + WalletKitError::SerializationError { + error: err.to_string(), + } + })?; + Ok(bytes) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::storage::tests_utils::InMemoryStorageProvider; + use crate::storage::CredentialStore; + use std::fs; + use std::path::{Path, PathBuf}; + use uuid::Uuid; + + fn temp_root() -> PathBuf { + let mut path = std::env::temp_dir(); + path.push(format!("walletkit-auth-storage-{}", Uuid::new_v4())); + path + } + + fn cleanup_storage(root: &Path) { + let paths = crate::storage::StoragePaths::new(root); + let vault = paths.vault_db_path(); + let cache = paths.cache_db_path(); + let lock = paths.lock_path(); + let _ = fs::remove_file(&vault); + let _ = fs::remove_file(vault.with_extension("sqlite-wal")); + let _ = fs::remove_file(vault.with_extension("sqlite-shm")); + let _ = fs::remove_file(&cache); + let _ = fs::remove_file(cache.with_extension("sqlite-wal")); + let _ = fs::remove_file(cache.with_extension("sqlite-shm")); + let _ = fs::remove_file(lock); + let _ = fs::remove_dir_all(paths.worldid_dir()); + let _ = fs::remove_dir_all(paths.root()); + } + + #[test] + fn test_cached_inclusion_round_trip() { + let root = temp_root(); + let provider = InMemoryStorageProvider::new(&root); + let store = CredentialStore::from_provider(&provider).expect("store"); + store.init(42, 100).expect("init storage"); + + let siblings = [FieldElement::from(0u64); TREE_DEPTH]; + let root_fe = FieldElement::from(123u64); + let proof = MerkleInclusionProof::new(root_fe, 42, siblings); + let key_set = AuthenticatorPublicKeySet::new(None).expect("key set"); + let payload = CachedInclusionProof { + proof: proof.clone(), + authenticator_pubkeys: key_set, + }; + let payload_bytes = payload.serialize().expect("serialize"); + let root_bytes: [u8; 32] = { + let mut bytes = Vec::new(); + proof + .root + .serialize_as_bytes(&mut bytes) + .expect("serialize field element"); + parse_fixed_bytes::<32>(bytes, "field_element") + .expect("field element bytes") + }; + + store + .merkle_cache_put(1, root_bytes.to_vec(), payload_bytes, 100, 60) + .expect("cache put"); + let valid_before = 110; + let cached = store + .merkle_cache_get(1, root_bytes.to_vec(), valid_before) + .expect("cache get") + .expect("cache hit"); + let decoded = CachedInclusionProof::deserialize(&cached).expect("decode"); + assert_eq!(decoded.proof.leaf_index, 42); + assert_eq!(decoded.proof.root, root_fe); + assert_eq!(decoded.authenticator_pubkeys.len(), 0); + cleanup_storage(&root); + } +} diff --git a/walletkit-core/src/authenticator/utils.rs b/walletkit-core/src/authenticator/utils.rs new file mode 100644 index 000000000..7ac54113b --- /dev/null +++ b/walletkit-core/src/authenticator/utils.rs @@ -0,0 +1,35 @@ +use std::convert::TryFrom; + +use crate::error::WalletKitError; +use crate::U256Wrapper; +use ruint::aliases::U256; + +/// Converts a leaf index from `U256Wrapper` to `u64`. +/// +/// # Errors +/// +/// Returns an error if the leaf index does not fit in a `u64`. +pub(super) fn leaf_index_to_u64( + leaf_index: &U256Wrapper, +) -> Result { + u64::try_from(leaf_index.0).map_err(|_| WalletKitError::InvalidInput { + attribute: "leaf_index".to_string(), + reason: "leaf index does not fit in u64".to_string(), + }) +} + +pub(super) fn parse_fixed_bytes( + bytes: Vec, + label: &str, +) -> Result<[u8; N], WalletKitError> { + bytes + .try_into() + .map_err(|bytes: Vec| WalletKitError::InvalidInput { + attribute: label.to_string(), + reason: format!("length mismatch: expected {N}, got {}", bytes.len()), + }) +} + +pub(super) fn u256_to_hex(value: U256) -> String { + format!("{value:#066x}") +} diff --git a/walletkit-core/src/defaults.rs b/walletkit-core/src/defaults.rs index fefca5300..e6538d28f 100644 --- a/walletkit-core/src/defaults.rs +++ b/walletkit-core/src/defaults.rs @@ -1,5 +1,5 @@ use alloy_primitives::{address, Address}; -use world_id_core::primitives::{Config, PrimitiveError}; +use world_id_core::primitives::Config; use crate::{error::WalletKitError, Environment}; @@ -15,15 +15,6 @@ pub trait DefaultConfig { Self: Sized; } -fn map_config_error(e: PrimitiveError) -> WalletKitError { - if let PrimitiveError::InvalidInput { attribute, reason } = e { - return WalletKitError::InvalidInput { attribute, reason }; - } - WalletKitError::Generic { - error: format!("Config initialization error: {e}"), - } -} - impl DefaultConfig for Config { fn from_environment( environment: &Environment, @@ -40,7 +31,7 @@ impl DefaultConfig for Config { vec![], 2, ) - .map_err(map_config_error), + .map_err(WalletKitError::from), Environment::Production => Self::new( rpc_url, @@ -51,7 +42,7 @@ impl DefaultConfig for Config { vec![], 2, ) - .map_err(map_config_error), + .map_err(WalletKitError::from), } } } diff --git a/walletkit-core/src/error.rs b/walletkit-core/src/error.rs index b94bc7ac5..e41999fce 100644 --- a/walletkit-core/src/error.rs +++ b/walletkit-core/src/error.rs @@ -1,5 +1,9 @@ use thiserror::Error; +#[cfg(feature = "v4")] +use world_id_core::primitives::PrimitiveError; +#[cfg(feature = "storage")] +use crate::storage::StorageError; #[cfg(feature = "v4")] use world_id_core::AuthenticatorError; @@ -99,6 +103,30 @@ impl From for WalletKitError { } } +#[cfg(feature = "v4")] +impl From for WalletKitError { + fn from(error: PrimitiveError) -> Self { + match error { + PrimitiveError::InvalidInput { attribute, reason } => { + Self::InvalidInput { attribute, reason } + } + PrimitiveError::Serialization(error) => Self::SerializationError { error }, + PrimitiveError::Deserialization(reason) => Self::InvalidInput { + attribute: "deserialization".to_string(), + reason, + }, + PrimitiveError::NotInField => Self::InvalidInput { + attribute: "field_element".to_string(), + reason: "Provided value is not in the field".to_string(), + }, + PrimitiveError::OutOfBounds => Self::InvalidInput { + attribute: "index".to_string(), + reason: "Provided index is out of bounds".to_string(), + }, + } + } +} + impl From for WalletKitError { fn from(error: semaphore_rs::protocol::ProofError) -> Self { Self::ProofGeneration { @@ -107,6 +135,15 @@ impl From for WalletKitError { } } +#[cfg(feature = "storage")] +impl From for WalletKitError { + fn from(error: StorageError) -> Self { + Self::Generic { + error: error.to_string(), + } + } +} + #[cfg(feature = "v4")] impl From for WalletKitError { fn from(error: AuthenticatorError) -> Self { @@ -128,17 +165,7 @@ impl From for WalletKitError { error: body, status: Some(status.as_u16()), }, - AuthenticatorError::PrimitiveError(error) => { - use world_id_core::primitives::PrimitiveError; - match error { - PrimitiveError::InvalidInput { attribute, reason } => { - Self::InvalidInput { attribute, reason } - } - _ => Self::Generic { - error: error.to_string(), - }, - } - } + AuthenticatorError::PrimitiveError(error) => Self::from(error), _ => Self::AuthenticatorError { error: error.to_string(), diff --git a/walletkit-core/src/lib.rs b/walletkit-core/src/lib.rs index 551a758de..449cf2b65 100644 --- a/walletkit-core/src/lib.rs +++ b/walletkit-core/src/lib.rs @@ -62,6 +62,10 @@ pub mod logger; mod u256; pub use u256::U256Wrapper; +/// Credential storage primitives for World ID v4. +#[cfg(feature = "storage")] +pub mod storage; + #[cfg(feature = "v4")] mod authenticator; #[cfg(feature = "v4")] diff --git a/walletkit-core/src/storage/cache/maintenance.rs b/walletkit-core/src/storage/cache/maintenance.rs new file mode 100644 index 000000000..97834b2d9 --- /dev/null +++ b/walletkit-core/src/storage/cache/maintenance.rs @@ -0,0 +1,84 @@ +//! Cache DB maintenance helpers (integrity checks, rebuilds). + +use std::fs; +use std::path::Path; + +use rusqlite::Connection; + +use crate::storage::error::StorageResult; +use crate::storage::sqlcipher; + +use super::schema; +use super::util::{map_io_err, map_sqlcipher_err}; + +/// Opens the cache DB or rebuilds it if integrity checks fail. +/// +/// # Errors +/// +/// Returns an error if the database cannot be opened or rebuilt. +pub(super) fn open_or_rebuild( + path: &Path, + k_intermediate: [u8; 32], +) -> StorageResult { + match open_prepared(path, k_intermediate) { + Ok(conn) => { + let integrity_ok = + sqlcipher::integrity_check(&conn).map_err(map_sqlcipher_err)?; + if integrity_ok { + Ok(conn) + } else { + drop(conn); + rebuild(path, k_intermediate) + } + } + Err(err) => rebuild(path, k_intermediate).map_or_else(|_| Err(err), Ok), + } +} + +/// Opens the cache DB, applies `SQLCipher` settings, and ensures schema. +/// +/// # Errors +/// +/// Returns an error if the DB cannot be opened or configured. +fn open_prepared(path: &Path, k_intermediate: [u8; 32]) -> StorageResult { + let conn = sqlcipher::open_connection(path, false).map_err(map_sqlcipher_err)?; + sqlcipher::apply_key(&conn, k_intermediate).map_err(map_sqlcipher_err)?; + sqlcipher::configure_connection(&conn).map_err(map_sqlcipher_err)?; + schema::ensure_schema(&conn)?; + Ok(conn) +} + +/// Rebuilds the cache database by deleting and recreating it. +/// +/// # Errors +/// +/// Returns an error if deletion or re-open fails. +fn rebuild(path: &Path, k_intermediate: [u8; 32]) -> StorageResult { + delete_cache_files(path)?; + open_prepared(path, k_intermediate) +} + +/// Deletes the cache DB and its WAL/SHM sidecar files if present. +/// +/// # Errors +/// +/// Returns an error for IO failures other than missing files. +fn delete_cache_files(path: &Path) -> StorageResult<()> { + delete_if_exists(path)?; + delete_if_exists(&path.with_extension("sqlite-wal"))?; + delete_if_exists(&path.with_extension("sqlite-shm"))?; + Ok(()) +} + +/// Deletes the file at `path` if it exists. +/// +/// # Errors +/// +/// Returns an error for IO failures other than missing files. +fn delete_if_exists(path: &Path) -> StorageResult<()> { + match fs::remove_file(path) { + Ok(()) => Ok(()), + Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()), + Err(err) => Err(map_io_err(&err)), + } +} diff --git a/walletkit-core/src/storage/cache/merkle.rs b/walletkit-core/src/storage/cache/merkle.rs new file mode 100644 index 000000000..b30fdea3d --- /dev/null +++ b/walletkit-core/src/storage/cache/merkle.rs @@ -0,0 +1,46 @@ +//! Merkle proof cache helpers. + +use rusqlite::Connection; + +use crate::storage::error::StorageResult; + +use super::util::{ + cache_entry_times, get_cache_entry, merkle_cache_key, prune_expired_entries, + upsert_cache_entry, +}; + +/// Fetches a cached Merkle proof if it is still valid. +/// +/// # Errors +/// +/// Returns an error if the query or conversion fails. +pub(super) fn get( + conn: &Connection, + registry_kind: u8, + root: [u8; 32], + leaf_index: u64, + valid_before: u64, +) -> StorageResult>> { + let key = merkle_cache_key(registry_kind, root, leaf_index); + get_cache_entry(conn, key.as_slice(), valid_before) +} + +/// Inserts or replaces a cached Merkle proof with a TTL. +/// +/// # Errors +/// +/// Returns an error if pruning or insert fails. +pub(super) fn put( + conn: &Connection, + registry_kind: u8, + root: [u8; 32], + leaf_index: u64, + proof_bytes: &[u8], + now: u64, + ttl_seconds: u64, +) -> StorageResult<()> { + prune_expired_entries(conn, now)?; + let key = merkle_cache_key(registry_kind, root, leaf_index); + let times = cache_entry_times(now, ttl_seconds)?; + upsert_cache_entry(conn, key.as_slice(), proof_bytes, times) +} diff --git a/walletkit-core/src/storage/cache/mod.rs b/walletkit-core/src/storage/cache/mod.rs new file mode 100644 index 000000000..e14054820 --- /dev/null +++ b/walletkit-core/src/storage/cache/mod.rs @@ -0,0 +1,401 @@ +//! Encrypted cache database for credential storage. + +use std::path::Path; + +use rusqlite::Connection; + +use crate::storage::error::StorageResult; +use crate::storage::lock::StorageLockGuard; +use crate::storage::types::ReplayGuardResult; + +mod maintenance; +mod merkle; +mod nullifiers; +mod schema; +mod session; +mod util; + +/// Encrypted cache database wrapper. +/// +/// Stores non-authoritative, regenerable data (proof cache, session keys, replay guard) +/// to improve performance without affecting correctness if rebuilt. +#[derive(Debug)] +pub struct CacheDb { + conn: Connection, +} + +impl CacheDb { + /// Opens or creates the encrypted cache database at `path`. + /// + /// If integrity checks fail, the cache is rebuilt since its contents can be + /// regenerated from authoritative sources. + /// + /// # Errors + /// + /// Returns an error if the database cannot be opened or rebuilt. + pub fn new( + path: &Path, + k_intermediate: [u8; 32], + _lock: &StorageLockGuard, + ) -> StorageResult { + let conn = maintenance::open_or_rebuild(path, k_intermediate)?; + Ok(Self { conn }) + } + + /// Fetches a cached Merkle proof if it remains valid beyond `valid_before`. + /// + /// Returns `None` when missing or expired so callers can refetch from the + /// indexer without relying on stale proofs. + /// + /// # Errors + /// + /// Returns an error if the query fails. + pub fn merkle_cache_get( + &self, + registry_kind: u8, + root: [u8; 32], + leaf_index: u64, + valid_before: u64, + ) -> StorageResult>> { + merkle::get(&self.conn, registry_kind, root, leaf_index, valid_before) + } + + /// Inserts a cached Merkle proof with a TTL. + /// Uses the database current time for `inserted_at`. + /// + /// Existing entries for the same (registry, root, leaf index) are replaced. + /// + /// # Errors + /// + /// Returns an error if the insert fails. + #[allow(clippy::too_many_arguments)] + #[allow(clippy::needless_pass_by_value)] + pub fn merkle_cache_put( + &mut self, + _lock: &StorageLockGuard, + registry_kind: u8, + root: [u8; 32], + leaf_index: u64, + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult<()> { + merkle::put( + &self.conn, + registry_kind, + root, + leaf_index, + proof_bytes.as_ref(), + now, + ttl_seconds, + ) + } + + /// Fetches a cached session key if present. + /// + /// This value is the per-RP session seed (aka `session_id_r_seed` in the + /// protocol). It is derived from `K_intermediate` and `rp_id` and is used to + /// derive the per-session `r` that feeds the sessionId commitment. The cache + /// is an optional performance hint and may be missing or expired. + /// + /// # Errors + /// + /// Returns an error if the query fails. + pub fn session_key_get(&self, rp_id: [u8; 32]) -> StorageResult> { + session::get(&self.conn, rp_id) + } + + /// Stores a session key with a TTL. + /// + /// The key is cached per relying party (`rp_id`) and replaced on insert. + /// + /// # Errors + /// + /// Returns an error if the insert fails. + pub fn session_key_put( + &mut self, + _lock: &StorageLockGuard, + rp_id: [u8; 32], + k_session: [u8; 32], + ttl_seconds: u64, + ) -> StorageResult<()> { + session::put(&self.conn, rp_id, k_session, ttl_seconds) + } + + /// Checks for a prior disclosure by request id. + /// + /// Returns the original proof bytes to make disclosure idempotent. + /// + /// # Errors + /// + /// Returns an error if the query fails. + pub fn replay_guard_get( + &self, + request_id: [u8; 32], + now: u64, + ) -> StorageResult>> { + nullifiers::replay_guard_bytes_for_request_id(&self.conn, request_id, now) + } + + /// Enforces replay safety for replay guard. + /// + /// Ensures a nullifier is disclosed at most once and that repeated requests + /// return the previously stored proof bytes. + /// + /// # Errors + /// + /// Returns an error if the disclosure conflicts with an existing nullifier. + pub fn begin_replay_guard( + &mut self, + _lock: &StorageLockGuard, + request_id: [u8; 32], + nullifier: [u8; 32], + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult { + nullifiers::begin_replay_guard( + &mut self.conn, + request_id, + nullifier, + proof_bytes, + now, + ttl_seconds, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::storage::error::StorageError; + use crate::storage::lock::StorageLock; + use crate::storage::types::ReplayGuardKind; + use std::fs; + use std::path::PathBuf; + use std::time::Duration; + use uuid::Uuid; + + fn temp_cache_path() -> PathBuf { + let mut path = std::env::temp_dir(); + path.push(format!("walletkit-cache-{}.sqlite", Uuid::new_v4())); + path + } + + fn cleanup_cache_files(path: &Path) { + let _ = fs::remove_file(path); + let wal_path = path.with_extension("sqlite-wal"); + let shm_path = path.with_extension("sqlite-shm"); + let _ = fs::remove_file(wal_path); + let _ = fs::remove_file(shm_path); + } + + fn temp_lock_path() -> PathBuf { + let mut path = std::env::temp_dir(); + path.push(format!("walletkit-cache-lock-{}.lock", Uuid::new_v4())); + path + } + + fn cleanup_lock_file(path: &Path) { + let _ = fs::remove_file(path); + } + + #[test] + fn test_cache_create_and_open() { + let path = temp_cache_path(); + let key = [0x11u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let db = CacheDb::new(&path, key, &guard).expect("create cache"); + drop(db); + CacheDb::new(&path, key, &guard).expect("open cache"); + cleanup_cache_files(&path); + cleanup_lock_file(&lock_path); + } + + #[test] + fn test_cache_rebuild_on_corruption() { + let path = temp_cache_path(); + let key = [0x22u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = CacheDb::new(&path, key, &guard).expect("create cache"); + let rp_id = [0x01u8; 32]; + let k_session = [0x02u8; 32]; + db.session_key_put(&guard, rp_id, k_session, 1000) + .expect("put session key"); + drop(db); + + fs::write(&path, b"corrupt").expect("corrupt cache file"); + + let db = CacheDb::new(&path, key, &guard).expect("rebuild cache"); + let value = db.session_key_get(rp_id).expect("get session key"); + assert!(value.is_none()); + cleanup_cache_files(&path); + cleanup_lock_file(&lock_path); + } + + #[test] + fn test_merkle_cache_ttl() { + let path = temp_cache_path(); + let key = [0x33u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = CacheDb::new(&path, key, &guard).expect("create cache"); + let root = [0xABu8; 32]; + db.merkle_cache_put(&guard, 1, root, 42, vec![1, 2, 3], 100, 10) + .expect("put merkle proof"); + let valid_before = 105; + let hit = db + .merkle_cache_get(1, root, 42, valid_before) + .expect("get merkle proof"); + assert!(hit.is_some()); + let miss = db + .merkle_cache_get(1, root, 42, 111) + .expect("get merkle proof"); + assert!(miss.is_none()); + cleanup_cache_files(&path); + cleanup_lock_file(&lock_path); + } + + #[test] + fn test_session_cache_ttl() { + let path = temp_cache_path(); + let key = [0x44u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = CacheDb::new(&path, key, &guard).expect("create cache"); + let rp_id = [0x55u8; 32]; + let k_session = [0x66u8; 32]; + db.session_key_put(&guard, rp_id, k_session, 1) + .expect("put session key"); + let hit = db.session_key_get(rp_id).expect("get session key"); + assert!(hit.is_some()); + std::thread::sleep(Duration::from_secs(2)); + let miss = db.session_key_get(rp_id).expect("get session key"); + assert!(miss.is_none()); + cleanup_cache_files(&path); + cleanup_lock_file(&lock_path); + } + + #[test] + fn test_replay_guard_replay_returns_original_bytes() { + let path = temp_cache_path(); + let key = [0x77u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = CacheDb::new(&path, key, &guard).expect("create cache"); + let request_id = [0x10u8; 32]; + let nullifier = [0x20u8; 32]; + let first = vec![1, 2, 3]; + let second = vec![9, 9, 9]; + + let fresh = db + .begin_replay_guard(&guard, request_id, nullifier, first.clone(), 100, 1000) + .expect("first disclosure"); + assert_eq!( + fresh, + ReplayGuardResult { + kind: ReplayGuardKind::Fresh, + bytes: first.clone(), + } + ); + + let replay = db + .begin_replay_guard(&guard, request_id, nullifier, second, 101, 1000) + .expect("replay disclosure"); + assert_eq!( + replay, + ReplayGuardResult { + kind: ReplayGuardKind::Replay, + bytes: first, + } + ); + cleanup_cache_files(&path); + cleanup_lock_file(&lock_path); + } + + #[test] + fn test_replay_guard_request_id_lookup() { + let path = temp_cache_path(); + let key = [0x66u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = CacheDb::new(&path, key, &guard).expect("create cache"); + let request_id = [0x55u8; 32]; + let nullifier = [0x44u8; 32]; + let payload = vec![4, 5, 6]; + + db.begin_replay_guard(&guard, request_id, nullifier, payload.clone(), 100, 10) + .expect("disclosure"); + + let hit = db.replay_guard_get(request_id, 105).expect("lookup"); + assert_eq!(hit, Some(payload)); + + let miss = db.replay_guard_get(request_id, 111).expect("lookup"); + assert!(miss.is_none()); + cleanup_cache_files(&path); + cleanup_lock_file(&lock_path); + } + + #[test] + fn test_replay_guard_nullifier_conflict_errors() { + let path = temp_cache_path(); + let key = [0x88u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = CacheDb::new(&path, key, &guard).expect("create cache"); + let request_id_a = [0x01u8; 32]; + let request_id_b = [0x02u8; 32]; + let nullifier = [0x03u8; 32]; + + db.begin_replay_guard(&guard, request_id_a, nullifier, vec![4], 100, 1000) + .expect("first disclosure"); + + let err = db + .begin_replay_guard(&guard, request_id_b, nullifier, vec![5], 101, 1000) + .expect_err("nullifier conflict"); + match err { + StorageError::NullifierAlreadyDisclosed => {} + other => panic!("unexpected error: {other}"), + } + cleanup_cache_files(&path); + cleanup_lock_file(&lock_path); + } + + #[test] + fn test_replay_guard_expiry_allows_new_insert() { + let path = temp_cache_path(); + let key = [0x99u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = CacheDb::new(&path, key, &guard).expect("create cache"); + let request_id_a = [0x0Au8; 32]; + let request_id_b = [0x0Bu8; 32]; + let nullifier = [0x0Cu8; 32]; + + db.begin_replay_guard(&guard, request_id_a, nullifier, vec![7], 100, 10) + .expect("first disclosure"); + + let fresh = db + .begin_replay_guard(&guard, request_id_b, nullifier, vec![8], 111, 10) + .expect("second disclosure after expiry"); + assert_eq!( + fresh, + ReplayGuardResult { + kind: ReplayGuardKind::Fresh, + bytes: vec![8], + } + ); + cleanup_cache_files(&path); + cleanup_lock_file(&lock_path); + } +} diff --git a/walletkit-core/src/storage/cache/nullifiers.rs b/walletkit-core/src/storage/cache/nullifiers.rs new file mode 100644 index 000000000..760d9b490 --- /dev/null +++ b/walletkit-core/src/storage/cache/nullifiers.rs @@ -0,0 +1,72 @@ +//! Used-nullifier cache helpers for replay protection. +//! +//! Tracks request ids and nullifiers to enforce single-use disclosures while +//! remaining idempotent for retries within the TTL window. + +use rusqlite::{Connection, TransactionBehavior}; + +use crate::storage::error::{StorageError, StorageResult}; +use crate::storage::types::{ReplayGuardKind, ReplayGuardResult}; + +use super::util::{ + cache_entry_times, commit_transaction, get_cache_entry, insert_cache_entry, + map_db_err, prune_expired_entries, replay_nullifier_key, replay_request_key, +}; + +/// Fetches stored proof bytes for a request id if still valid. +/// +/// # Errors +/// +/// Returns an error if the query or conversion fails. +pub(super) fn replay_guard_bytes_for_request_id( + conn: &Connection, + request_id: [u8; 32], + now: u64, +) -> StorageResult>> { + let key = replay_request_key(request_id); + get_cache_entry(conn, key.as_slice(), now) +} + +/// Enforces replay-safety for disclosures within a single transaction. +/// +/// # Errors +/// +/// Returns an error if the nullifier was already disclosed or on DB failures. +pub(super) fn begin_replay_guard( + conn: &mut Connection, + request_id: [u8; 32], + nullifier: [u8; 32], + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, +) -> StorageResult { + let tx = conn + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(|err| map_db_err(&err))?; + prune_expired_entries(&tx, now)?; + + let request_key = replay_request_key(request_id); + let existing_proof = get_cache_entry(&tx, request_key.as_slice(), now)?; + if let Some(bytes) = existing_proof { + commit_transaction(tx)?; + return Ok(ReplayGuardResult { + kind: ReplayGuardKind::Replay, + bytes, + }); + } + + let nullifier_key = replay_nullifier_key(nullifier); + let existing_request = get_cache_entry(&tx, nullifier_key.as_slice(), now)?; + if existing_request.is_some() { + return Err(StorageError::NullifierAlreadyDisclosed); + } + + let times = cache_entry_times(now, ttl_seconds)?; + insert_cache_entry(&tx, request_key.as_slice(), proof_bytes.as_ref(), times)?; + insert_cache_entry(&tx, nullifier_key.as_slice(), request_id.as_ref(), times)?; + commit_transaction(tx)?; + Ok(ReplayGuardResult { + kind: ReplayGuardKind::Fresh, + bytes: proof_bytes, + }) +} diff --git a/walletkit-core/src/storage/cache/schema.rs b/walletkit-core/src/storage/cache/schema.rs new file mode 100644 index 000000000..9f86f82ea --- /dev/null +++ b/walletkit-core/src/storage/cache/schema.rs @@ -0,0 +1,105 @@ +//! Cache database schema management. + +use rusqlite::{Connection, OptionalExtension}; + +use crate::storage::error::StorageResult; + +use super::util::map_db_err; + +const CACHE_SCHEMA_VERSION: i64 = 2; + +/// Ensures the cache schema is present and at the expected version. +/// +/// # Errors +/// +/// Returns an error if schema creation or migration fails. +pub(super) fn ensure_schema(conn: &Connection) -> StorageResult<()> { + conn.execute_batch( + "CREATE TABLE IF NOT EXISTS cache_meta ( + schema_version INTEGER NOT NULL, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL + );", + ) + .map_err(|err| map_db_err(&err))?; + + let existing: Option = conn + .query_row( + "SELECT schema_version FROM cache_meta LIMIT 1;", + [], + |row| row.get(0), + ) + .optional() + .map_err(|err| map_db_err(&err))?; + + match existing { + Some(version) if version == CACHE_SCHEMA_VERSION => { + ensure_entries_schema(conn)?; + } + Some(_) => { + reset_schema(conn)?; + } + None => { + ensure_entries_schema(conn)?; + insert_meta(conn)?; + } + } + Ok(()) +} + +/// Ensures the `cache_entries` table and indexes exist. +/// +/// # Errors +/// +/// Returns an error if schema creation fails. +fn ensure_entries_schema(conn: &Connection) -> StorageResult<()> { + conn.execute_batch( + "CREATE TABLE IF NOT EXISTS cache_entries ( + key_bytes BLOB NOT NULL, + value_bytes BLOB NOT NULL, + inserted_at INTEGER NOT NULL, + expires_at INTEGER NOT NULL, + PRIMARY KEY (key_bytes) + ); + + CREATE INDEX IF NOT EXISTS idx_cache_entries_expiry + ON cache_entries (expires_at);", + ) + .map_err(|err| map_db_err(&err))?; + Ok(()) +} + +/// Drops legacy cache tables and recreates the current schema. +/// +/// # Errors +/// +/// Returns an error if the reset or re-init fails. +fn reset_schema(conn: &Connection) -> StorageResult<()> { + conn.execute_batch( + "DROP TABLE IF EXISTS used_nullifiers; + DROP TABLE IF EXISTS merkle_proof_cache; + DROP TABLE IF EXISTS session_keys; + DROP TABLE IF EXISTS cache_entries;", + ) + .map_err(|err| map_db_err(&err))?; + ensure_entries_schema(conn)?; + conn.execute("DELETE FROM cache_meta;", []) + .map_err(|err| map_db_err(&err))?; + insert_meta(conn)?; + Ok(()) +} + +/// Inserts the current schema version into `cache_meta`. +/// +/// # Errors +/// +/// Returns an error if the insert fails. +fn insert_meta(conn: &Connection) -> StorageResult<()> { + conn.execute( + "INSERT INTO cache_meta (schema_version, created_at, updated_at) + VALUES (?1, strftime('%s','now'), strftime('%s','now'))", + [CACHE_SCHEMA_VERSION], + ) + .map_err(|err| map_db_err(&err))?; + Ok(()) +} diff --git a/walletkit-core/src/storage/cache/session.rs b/walletkit-core/src/storage/cache/session.rs new file mode 100644 index 000000000..b3daec68f --- /dev/null +++ b/walletkit-core/src/storage/cache/session.rs @@ -0,0 +1,62 @@ +//! Session key cache helpers. + +use std::time::{SystemTime, UNIX_EPOCH}; + +use rusqlite::Connection; + +use crate::storage::error::{StorageError, StorageResult}; + +use super::util::{ + cache_entry_times, get_cache_entry, parse_fixed_bytes, prune_expired_entries, + session_cache_key, upsert_cache_entry, +}; + +/// Fetches a cached session key if it is still valid. +/// +/// # Errors +/// +/// Returns an error if the query fails or the cached bytes are malformed. +pub(super) fn get( + conn: &Connection, + rp_id: [u8; 32], +) -> StorageResult> { + let now = current_unix_timestamp()?; + let key = session_cache_key(rp_id); + let raw = get_cache_entry(conn, key.as_slice(), now)?; + match raw { + Some(bytes) => Ok(Some(parse_fixed_bytes::<32>(&bytes, "k_session")?)), + None => Ok(None), + } +} + +/// Stores a session key with a TTL. +/// +/// # Errors +/// +/// Returns an error if pruning or insert fails. +pub(super) fn put( + conn: &Connection, + rp_id: [u8; 32], + k_session: [u8; 32], + ttl_seconds: u64, +) -> StorageResult<()> { + let now = current_unix_timestamp()?; + let key = session_cache_key(rp_id); + prune_expired_entries(conn, now)?; + let times = cache_entry_times(now, ttl_seconds)?; + upsert_cache_entry(conn, key.as_slice(), k_session.as_ref(), times) +} + +/// Returns the current unix timestamp in seconds. +/// +/// # Errors +/// +/// Returns an error if system time is before the unix epoch. +fn current_unix_timestamp() -> StorageResult { + let duration = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|err| { + StorageError::CacheDb(format!("system time before unix epoch: {err}")) + })?; + Ok(duration.as_secs()) +} diff --git a/walletkit-core/src/storage/cache/util.rs b/walletkit-core/src/storage/cache/util.rs new file mode 100644 index 000000000..db2e4727a --- /dev/null +++ b/walletkit-core/src/storage/cache/util.rs @@ -0,0 +1,221 @@ +//! Shared helpers for cache database operations. + +use rusqlite::{params, Connection, OptionalExtension, Transaction}; +use std::io; + +use crate::storage::error::{StorageError, StorageResult}; +use crate::storage::sqlcipher::SqlcipherError; + +/// Maps a rusqlite error into a cache storage error. +pub(super) fn map_db_err(err: &rusqlite::Error) -> StorageError { + StorageError::CacheDb(err.to_string()) +} + +/// Maps a `SQLCipher` error into a cache storage error. +pub(super) fn map_sqlcipher_err(err: SqlcipherError) -> StorageError { + match err { + SqlcipherError::Sqlite(err) => StorageError::CacheDb(err.to_string()), + SqlcipherError::CipherUnavailable => StorageError::CacheDb(err.to_string()), + } +} + +/// Maps an IO error into a cache storage error. +pub(super) fn map_io_err(err: &io::Error) -> StorageError { + StorageError::CacheDb(err.to_string()) +} + +/// Parses a fixed-length array from the provided bytes. +/// +/// # Errors +/// +/// Returns an error if the input length does not match `N`. +pub(super) fn parse_fixed_bytes( + bytes: &[u8], + label: &str, +) -> StorageResult<[u8; N]> { + if bytes.len() != N { + return Err(StorageError::CacheDb(format!( + "{label} length mismatch: expected {N}, got {}", + bytes.len() + ))); + } + let mut out = [0u8; N]; + out.copy_from_slice(bytes); + Ok(out) +} + +pub(super) const CACHE_KEY_PREFIX_MERKLE: u8 = 0x01; +pub(super) const CACHE_KEY_PREFIX_SESSION: u8 = 0x02; +pub(super) const CACHE_KEY_PREFIX_REPLAY_REQUEST: u8 = 0x03; +pub(super) const CACHE_KEY_PREFIX_REPLAY_NULLIFIER: u8 = 0x04; + +/// Timestamps for cache entry insertion and expiry. +#[derive(Clone, Copy, Debug)] +pub(super) struct CacheEntryTimes { + inserted_at: i64, + expires_at: i64, +} + +/// Builds timestamps for cache entry inserts. +/// +/// # Errors +/// +/// Returns an error if timestamps do not fit into `i64`. +pub(super) fn cache_entry_times( + now: u64, + ttl_seconds: u64, +) -> StorageResult { + let expires_at = expiry_timestamp(now, ttl_seconds); + Ok(CacheEntryTimes { + inserted_at: to_i64(now, "now")?, + expires_at: to_i64(expires_at, "expires_at")?, + }) +} + +/// Removes expired cache entries before inserting new ones. +/// +/// # Errors +/// +/// Returns an error if the deletion fails. +pub(super) fn prune_expired_entries(conn: &Connection, now: u64) -> StorageResult<()> { + let now_i64 = to_i64(now, "now")?; + conn.execute( + "DELETE FROM cache_entries WHERE expires_at <= ?1", + params![now_i64], + ) + .map_err(|err| map_db_err(&err))?; + Ok(()) +} + +/// Inserts or replaces a cache entry row. +/// +/// # Errors +/// +/// Returns an error if the insert fails. +pub(super) fn upsert_cache_entry( + conn: &Connection, + key: &[u8], + value: &[u8], + times: CacheEntryTimes, +) -> StorageResult<()> { + conn.execute( + "INSERT OR REPLACE INTO cache_entries ( + key_bytes, + value_bytes, + inserted_at, + expires_at + ) VALUES (?1, ?2, ?3, ?4)", + params![key, value, times.inserted_at, times.expires_at], + ) + .map_err(|err| map_db_err(&err))?; + Ok(()) +} + +/// Inserts a cache entry row. +/// +/// # Errors +/// +/// Returns an error if the insert fails. +pub(super) fn insert_cache_entry( + conn: &Connection, + key: &[u8], + value: &[u8], + times: CacheEntryTimes, +) -> StorageResult<()> { + conn.execute( + "INSERT INTO cache_entries ( + key_bytes, + value_bytes, + inserted_at, + expires_at + ) VALUES (?1, ?2, ?3, ?4)", + params![key, value, times.inserted_at, times.expires_at], + ) + .map_err(|err| map_db_err(&err))?; + Ok(()) +} + +/// Fetches a cache entry if it is still valid. +/// +/// # Errors +/// +/// Returns an error if the query or conversion fails. +pub(super) fn get_cache_entry( + conn: &Connection, + key: &[u8], + valid_before: u64, +) -> StorageResult>> { + let valid_before_i64 = to_i64(valid_before, "valid_before")?; + conn.query_row( + "SELECT value_bytes + FROM cache_entries + WHERE key_bytes = ?1 + AND expires_at > ?2", + params![key, valid_before_i64], + |row| row.get(0), + ) + .optional() + .map_err(|err| map_db_err(&err)) +} + +/// Commits a `Transaction` with mapped cache errors. +/// +/// # Errors +/// +/// Returns an error if the commit fails. +pub(super) fn commit_transaction(tx: Transaction) -> StorageResult<()> { + tx.commit().map_err(|err| map_db_err(&err))?; + Ok(()) +} + +/// Builds a cache key by prefixing the payload with a type byte. +fn cache_key_with_prefix(prefix: u8, payload: &[u8]) -> Vec { + let mut key = Vec::with_capacity(1 + payload.len()); + key.push(prefix); + key.extend_from_slice(payload); + key +} + +/// Builds the cache key for a Merkle proof entry. +pub(super) fn merkle_cache_key( + registry_kind: u8, + root: [u8; 32], + leaf_index: u64, +) -> Vec { + let mut payload = Vec::with_capacity(1 + 32 + 8); + payload.push(registry_kind); + payload.extend_from_slice(root.as_ref()); + payload.extend_from_slice(&leaf_index.to_be_bytes()); + cache_key_with_prefix(CACHE_KEY_PREFIX_MERKLE, &payload) +} + +/// Builds the cache key for a session key entry. +pub(super) fn session_cache_key(rp_id: [u8; 32]) -> Vec { + cache_key_with_prefix(CACHE_KEY_PREFIX_SESSION, rp_id.as_ref()) +} + +/// Builds the cache key for a replay-guard request entry. +pub(super) fn replay_request_key(request_id: [u8; 32]) -> Vec { + cache_key_with_prefix(CACHE_KEY_PREFIX_REPLAY_REQUEST, request_id.as_ref()) +} + +/// Builds the cache key for a replay-guard nullifier entry. +pub(super) fn replay_nullifier_key(nullifier: [u8; 32]) -> Vec { + cache_key_with_prefix(CACHE_KEY_PREFIX_REPLAY_NULLIFIER, nullifier.as_ref()) +} + +/// Computes an expiry timestamp using saturating addition. +pub(super) const fn expiry_timestamp(now: u64, ttl_seconds: u64) -> u64 { + now.saturating_add(ttl_seconds) +} + +/// Converts a `u64` into `i64` for `SQLite` parameter bindings. +/// +/// # Errors +/// +/// Returns an error if the value cannot fit into `i64`. +pub(super) fn to_i64(value: u64, label: &str) -> StorageResult { + i64::try_from(value).map_err(|_| { + StorageError::CacheDb(format!("{label} out of range for i64: {value}")) + }) +} diff --git a/walletkit-core/src/storage/credential_storage.rs b/walletkit-core/src/storage/credential_storage.rs new file mode 100644 index 000000000..46d08bc17 --- /dev/null +++ b/walletkit-core/src/storage/credential_storage.rs @@ -0,0 +1,629 @@ +//! Storage facade implementing the credential storage API. + +use std::sync::{Arc, Mutex}; + +use super::error::{StorageError, StorageResult}; +use super::keys::StorageKeys; +use super::lock::{StorageLock, StorageLockGuard}; +use super::paths::StoragePaths; +use super::traits::StorageProvider; +use super::traits::{AtomicBlobStore, DeviceKeystore}; +use super::types::{CredentialRecord, Nullifier, ReplayGuardResult, RequestId}; +use super::{CacheDb, VaultDb}; + +/// Public-facing storage API used by `WalletKit` v4 flows. +pub trait CredentialStorage { + /// Initializes storage and validates the account leaf index. + /// + /// Loads or creates the account key envelope, opens the vault/cache, and + /// ensures the stored leaf index matches the provided value. + /// + /// # Errors + /// + /// Returns an error if storage initialization fails or the leaf index is invalid. + fn init(&mut self, leaf_index: u64, now: u64) -> StorageResult<()>; + + /// Lists active credential metadata, optionally filtered by issuer schema ID. + /// + /// # Errors + /// + /// Returns an error if the credential query fails. + fn list_credentials( + &self, + issuer_schema_id: Option, + now: u64, + ) -> StorageResult>; + + /// Stores a credential and optional associated data. + /// + /// # Errors + /// + /// Returns an error if the credential cannot be stored. + #[allow(clippy::too_many_arguments)] + fn store_credential( + &mut self, + issuer_schema_id: u64, + subject_blinding_factor: [u8; 32], + genesis_issued_at: u64, + expires_at: u64, + credential_blob: Vec, + associated_data: Option>, + now: u64, + ) -> StorageResult; + + /// Fetches a cached Merkle proof if it remains valid beyond `valid_before`. + /// + /// # Errors + /// + /// Returns an error if the cache lookup fails. + fn merkle_cache_get( + &self, + registry_kind: u8, + root: [u8; 32], + valid_before: u64, + ) -> StorageResult>>; + + /// Inserts a cached Merkle proof with a TTL. + /// + /// # Errors + /// + /// Returns an error if the cache insert fails. + fn merkle_cache_put( + &mut self, + registry_kind: u8, + root: [u8; 32], + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult<()>; + + /// Checks for a prior replay guard entry by request id. + /// + /// # Errors + /// + /// Returns an error if the cache lookup fails. + fn replay_guard_get( + &self, + request_id: RequestId, + now: u64, + ) -> StorageResult>>; + + /// Enforces replay safety for replay guard. + /// + /// Returns the stored proof bytes on replay and rejects nullifier reuse + /// across different request ids. + /// + /// # Errors + /// + /// Returns an error if the nullifier is already disclosed or the cache + /// operation fails. + fn begin_replay_guard( + &mut self, + request_id: RequestId, + nullifier: Nullifier, + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult; +} + +/// Concrete storage implementation backed by `SQLCipher` databases. +#[derive(uniffi::Object)] +pub struct CredentialStore { + inner: Mutex, +} + +struct CredentialStoreInner { + lock: StorageLock, + keystore: Arc, + blob_store: Arc, + paths: StoragePaths, + state: Option, +} + +struct StorageState { + #[allow(dead_code)] + keys: StorageKeys, + vault: VaultDb, + cache: CacheDb, + leaf_index: u64, +} + +impl CredentialStoreInner { + /// Creates a new storage handle from a platform provider. + /// + /// # Errors + /// + /// Returns an error if the storage lock cannot be opened. + pub fn from_provider(provider: &dyn StorageProvider) -> StorageResult { + let paths = provider.paths(); + Self::new( + paths.as_ref().clone(), + provider.keystore(), + provider.blob_store(), + ) + } + + /// Creates a new storage handle from explicit components. + /// + /// # Errors + /// + /// Returns an error if the storage lock cannot be opened. + pub fn new( + paths: StoragePaths, + keystore: Arc, + blob_store: Arc, + ) -> StorageResult { + let lock = StorageLock::open(&paths.lock_path())?; + Ok(Self { + lock, + keystore, + blob_store, + paths, + state: None, + }) + } + + fn guard(&self) -> StorageResult { + self.lock.lock() + } + + fn state(&self) -> StorageResult<&StorageState> { + self.state.as_ref().ok_or(StorageError::NotInitialized) + } + + fn state_mut(&mut self) -> StorageResult<&mut StorageState> { + self.state.as_mut().ok_or(StorageError::NotInitialized) + } +} + +#[uniffi::export] +impl CredentialStore { + /// Creates a new storage handle from explicit components. + /// + /// # Errors + /// + /// Returns an error if the storage lock cannot be opened. + #[uniffi::constructor] + pub fn new_with_components( + paths: Arc, + keystore: Arc, + blob_store: Arc, + ) -> StorageResult { + let paths = Arc::try_unwrap(paths).unwrap_or_else(|arc| (*arc).clone()); + let inner = CredentialStoreInner::new(paths, keystore, blob_store)?; + Ok(Self { + inner: Mutex::new(inner), + }) + } + + /// Creates a new storage handle from a platform provider. + /// + /// # Errors + /// + /// Returns an error if the storage lock cannot be opened. + #[uniffi::constructor] + #[allow(clippy::needless_pass_by_value)] + pub fn from_provider_arc( + provider: Arc, + ) -> StorageResult { + let inner = CredentialStoreInner::from_provider(provider.as_ref())?; + Ok(Self { + inner: Mutex::new(inner), + }) + } + + /// Returns the storage paths used by this handle. + /// + /// # Errors + /// + /// Returns an error if the storage mutex is poisoned. + pub fn storage_paths(&self) -> StorageResult> { + self.lock_inner().map(|inner| Arc::new(inner.paths.clone())) + } + + /// Initializes storage and validates the account leaf index. + /// + /// # Errors + /// + /// Returns an error if initialization fails or the leaf index mismatches. + pub fn init(&self, leaf_index: u64, now: u64) -> StorageResult<()> { + let mut inner = self.lock_inner()?; + inner.init(leaf_index, now) + } + + /// Lists active credential metadata, optionally filtered by issuer schema ID. + /// + /// # Errors + /// + /// Returns an error if the credential query fails. + pub fn list_credentials( + &self, + issuer_schema_id: Option, + now: u64, + ) -> StorageResult> { + self.lock_inner()?.list_credentials(issuer_schema_id, now) + } + + /// Stores a credential and optional associated data. + /// + /// # Errors + /// + /// Returns an error if the credential cannot be stored. + #[allow(clippy::too_many_arguments)] + pub fn store_credential( + &self, + issuer_schema_id: u64, + subject_blinding_factor: Vec, + genesis_issued_at: u64, + expires_at: u64, + credential_blob: Vec, + associated_data: Option>, + now: u64, + ) -> StorageResult { + let subject_blinding_factor = parse_fixed_bytes::<32>( + subject_blinding_factor, + "subject_blinding_factor", + )?; + let credential_id = self.lock_inner()?.store_credential( + issuer_schema_id, + subject_blinding_factor, + genesis_issued_at, + expires_at, + credential_blob, + associated_data, + now, + )?; + Ok(credential_id) + } + + /// Fetches a cached Merkle proof if it remains valid beyond `valid_before`. + /// + /// # Errors + /// + /// Returns an error if the cache lookup fails. + pub fn merkle_cache_get( + &self, + registry_kind: u8, + root: Vec, + valid_before: u64, + ) -> StorageResult>> { + let root = parse_fixed_bytes::<32>(root, "root")?; + self.lock_inner()? + .merkle_cache_get(registry_kind, root, valid_before) + } + + /// Inserts a cached Merkle proof with a TTL. + /// + /// # Errors + /// + /// Returns an error if the cache insert fails. + pub fn merkle_cache_put( + &self, + registry_kind: u8, + root: Vec, + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult<()> { + let root = parse_fixed_bytes::<32>(root, "root")?; + self.lock_inner()?.merkle_cache_put( + registry_kind, + root, + proof_bytes, + now, + ttl_seconds, + ) + } + + /// Checks for a prior replay guard entry by request id. + /// + /// # Errors + /// + /// Returns an error if the cache lookup fails. + pub fn replay_guard_get( + &self, + request_id: Vec, + now: u64, + ) -> StorageResult>> { + let request_id = parse_fixed_bytes::<32>(request_id, "request_id")?; + self.lock_inner()?.replay_guard_get(request_id, now) + } + + /// Enforces replay safety for replay guard. + /// + /// # Errors + /// + /// Returns an error if the disclosure conflicts or storage fails. + pub fn begin_replay_guard( + &self, + request_id: Vec, + nullifier: Vec, + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult { + let request_id = parse_fixed_bytes::<32>(request_id, "request_id")?; + let nullifier = parse_fixed_bytes::<32>(nullifier, "nullifier")?; + let result = self.lock_inner()?.begin_replay_guard( + request_id, + nullifier, + proof_bytes, + now, + ttl_seconds, + )?; + Ok(result) + } +} + +fn parse_fixed_bytes( + bytes: Vec, + label: &str, +) -> StorageResult<[u8; N]> { + bytes.try_into().map_err(|bytes: Vec| { + StorageError::Serialization(format!( + "{label} length mismatch: expected {N}, got {}", + bytes.len() + )) + }) +} + +impl CredentialStore { + fn lock_inner( + &self, + ) -> StorageResult> { + self.inner + .lock() + .map_err(|_| StorageError::Lock("storage mutex poisoned".to_string())) + } +} + +impl CredentialStorage for CredentialStoreInner { + fn init(&mut self, leaf_index: u64, now: u64) -> StorageResult<()> { + let guard = self.guard()?; + if let Some(state) = &mut self.state { + state.vault.init_leaf_index(&guard, leaf_index, now)?; + state.leaf_index = leaf_index; + return Ok(()); + } + + let keys = StorageKeys::init( + self.keystore.as_ref(), + self.blob_store.as_ref(), + &guard, + now, + )?; + let vault = + VaultDb::new(&self.paths.vault_db_path(), keys.intermediate_key(), &guard)?; + let cache = + CacheDb::new(&self.paths.cache_db_path(), keys.intermediate_key(), &guard)?; + let mut state = StorageState { + keys, + vault, + cache, + leaf_index, + }; + state.vault.init_leaf_index(&guard, leaf_index, now)?; + self.state = Some(state); + Ok(()) + } + + fn list_credentials( + &self, + issuer_schema_id: Option, + now: u64, + ) -> StorageResult> { + let state = self.state()?; + state.vault.list_credentials(issuer_schema_id, now) + } + + fn store_credential( + &mut self, + issuer_schema_id: u64, + subject_blinding_factor: [u8; 32], + genesis_issued_at: u64, + expires_at: u64, + credential_blob: Vec, + associated_data: Option>, + now: u64, + ) -> StorageResult { + let guard = self.guard()?; + let state = self.state_mut()?; + state.vault.store_credential( + &guard, + issuer_schema_id, + subject_blinding_factor, + genesis_issued_at, + expires_at, + credential_blob, + associated_data, + now, + ) + } + + fn merkle_cache_get( + &self, + registry_kind: u8, + root: [u8; 32], + valid_before: u64, + ) -> StorageResult>> { + let state = self.state()?; + state.cache.merkle_cache_get( + registry_kind, + root, + state.leaf_index, + valid_before, + ) + } + + fn merkle_cache_put( + &mut self, + registry_kind: u8, + root: [u8; 32], + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult<()> { + let guard = self.guard()?; + let state = self.state_mut()?; + state.cache.merkle_cache_put( + &guard, + registry_kind, + root, + state.leaf_index, + proof_bytes, + now, + ttl_seconds, + ) + } + + fn replay_guard_get( + &self, + request_id: RequestId, + now: u64, + ) -> StorageResult>> { + let state = self.state()?; + state.cache.replay_guard_get(request_id, now) + } + + fn begin_replay_guard( + &mut self, + request_id: RequestId, + nullifier: Nullifier, + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult { + let guard = self.guard()?; + let state = self.state_mut()?; + state.cache.begin_replay_guard( + &guard, + request_id, + nullifier, + proof_bytes, + now, + ttl_seconds, + ) + } +} + +impl CredentialStore { + /// Creates a new storage handle from a platform provider. + /// + /// # Errors + /// + /// Returns an error if the storage lock cannot be opened. + pub fn from_provider(provider: &dyn StorageProvider) -> StorageResult { + let inner = CredentialStoreInner::from_provider(provider)?; + Ok(Self { + inner: Mutex::new(inner), + }) + } + + /// Creates a new storage handle from explicit components. + /// + /// # Errors + /// + /// Returns an error if the storage lock cannot be opened. + pub fn new( + paths: StoragePaths, + keystore: Arc, + blob_store: Arc, + ) -> StorageResult { + let inner = CredentialStoreInner::new(paths, keystore, blob_store)?; + Ok(Self { + inner: Mutex::new(inner), + }) + } + + /// Returns the storage paths used by this handle. + /// + /// # Errors + /// + /// Returns an error if the storage mutex is poisoned. + pub fn paths(&self) -> StorageResult { + self.lock_inner().map(|inner| inner.paths.clone()) + } +} + +impl CredentialStorage for CredentialStore { + fn init(&mut self, leaf_index: u64, now: u64) -> StorageResult<()> { + let mut inner = self.lock_inner()?; + inner.init(leaf_index, now) + } + + fn list_credentials( + &self, + issuer_schema_id: Option, + now: u64, + ) -> StorageResult> { + let inner = self.lock_inner()?; + inner.list_credentials(issuer_schema_id, now) + } + + #[allow(clippy::too_many_arguments)] + fn store_credential( + &mut self, + issuer_schema_id: u64, + subject_blinding_factor: [u8; 32], + genesis_issued_at: u64, + expires_at: u64, + credential_blob: Vec, + associated_data: Option>, + now: u64, + ) -> StorageResult { + let mut inner = self.lock_inner()?; + inner.store_credential( + issuer_schema_id, + subject_blinding_factor, + genesis_issued_at, + expires_at, + credential_blob, + associated_data, + now, + ) + } + + fn merkle_cache_get( + &self, + registry_kind: u8, + root: [u8; 32], + valid_before: u64, + ) -> StorageResult>> { + let inner = self.lock_inner()?; + inner.merkle_cache_get(registry_kind, root, valid_before) + } + + fn merkle_cache_put( + &mut self, + registry_kind: u8, + root: [u8; 32], + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult<()> { + let mut inner = self.lock_inner()?; + inner.merkle_cache_put(registry_kind, root, proof_bytes, now, ttl_seconds) + } + + fn replay_guard_get( + &self, + request_id: RequestId, + now: u64, + ) -> StorageResult>> { + let inner = self.lock_inner()?; + inner.replay_guard_get(request_id, now) + } + + fn begin_replay_guard( + &mut self, + request_id: RequestId, + nullifier: Nullifier, + proof_bytes: Vec, + now: u64, + ttl_seconds: u64, + ) -> StorageResult { + let mut inner = self.lock_inner()?; + inner.begin_replay_guard(request_id, nullifier, proof_bytes, now, ttl_seconds) + } +} diff --git a/walletkit-core/src/storage/envelope.rs b/walletkit-core/src/storage/envelope.rs new file mode 100644 index 000000000..abad8e190 --- /dev/null +++ b/walletkit-core/src/storage/envelope.rs @@ -0,0 +1,73 @@ +//! Account key envelope persistence helpers. + +use serde::{Deserialize, Serialize}; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +use super::error::{StorageError, StorageResult}; + +const ENVELOPE_VERSION: u32 = 1; + +#[derive(Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] +pub(crate) struct AccountKeyEnvelope { + pub(crate) version: u32, + pub(crate) wrapped_k_intermediate: Vec, + pub(crate) created_at: u64, + pub(crate) updated_at: u64, +} + +impl AccountKeyEnvelope { + pub(crate) const fn new(wrapped_k_intermediate: Vec, now: u64) -> Self { + Self { + version: ENVELOPE_VERSION, + wrapped_k_intermediate, + created_at: now, + updated_at: now, + } + } + + pub(crate) fn serialize(&self) -> StorageResult> { + let mut bytes = Vec::new(); + ciborium::ser::into_writer(self, &mut bytes) + .map_err(|err| StorageError::Serialization(err.to_string()))?; + Ok(bytes) + } + + pub(crate) fn deserialize(bytes: &[u8]) -> StorageResult { + let envelope: Self = ciborium::de::from_reader(bytes) + .map_err(|err| StorageError::Serialization(err.to_string()))?; + if envelope.version != ENVELOPE_VERSION { + return Err(StorageError::UnsupportedEnvelopeVersion(envelope.version)); + } + Ok(envelope) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_envelope_round_trip() { + let envelope = AccountKeyEnvelope::new(vec![1, 2, 3], 123); + let bytes = envelope.serialize().expect("serialize"); + let decoded = AccountKeyEnvelope::deserialize(&bytes).expect("deserialize"); + assert_eq!(decoded.version, ENVELOPE_VERSION); + assert_eq!(decoded.wrapped_k_intermediate, vec![1, 2, 3]); + assert_eq!(decoded.created_at, 123); + assert_eq!(decoded.updated_at, 123); + } + + #[test] + fn test_envelope_version_mismatch() { + let mut envelope = AccountKeyEnvelope::new(vec![1, 2, 3], 123); + envelope.version = ENVELOPE_VERSION + 1; + let bytes = envelope.serialize().expect("serialize"); + match AccountKeyEnvelope::deserialize(&bytes) { + Err(StorageError::UnsupportedEnvelopeVersion(version)) => { + assert_eq!(version, ENVELOPE_VERSION + 1); + } + Err(err) => panic!("unexpected error: {err}"), + Ok(_) => panic!("expected error"), + } + } +} diff --git a/walletkit-core/src/storage/error.rs b/walletkit-core/src/storage/error.rs new file mode 100644 index 000000000..7fcb9fe45 --- /dev/null +++ b/walletkit-core/src/storage/error.rs @@ -0,0 +1,81 @@ +//! Error types for credential storage components. + +use thiserror::Error; + +/// Result type for storage operations. +pub type StorageResult = Result; + +/// Errors raised by credential storage primitives. +#[derive(Debug, Error, uniffi::Error)] +pub enum StorageError { + /// Errors coming from the device keystore. + #[error("keystore error: {0}")] + Keystore(String), + + /// Errors coming from the blob store. + #[error("blob store error: {0}")] + BlobStore(String), + + /// Errors coming from the storage lock. + #[error("storage lock error: {0}")] + Lock(String), + + /// Serialization/deserialization failures. + #[error("serialization error: {0}")] + Serialization(String), + + /// Cryptographic failures (AEAD, HKDF, etc.). + #[error("crypto error: {0}")] + Crypto(String), + + /// Invalid or malformed account key envelope. + #[error("invalid envelope: {0}")] + InvalidEnvelope(String), + + /// Unsupported envelope version. + #[error("unsupported envelope version: {0}")] + UnsupportedEnvelopeVersion(u32), + + /// Errors coming from the vault database. + #[error("vault db error: {0}")] + VaultDb(String), + + /// Errors coming from the cache database. + #[error("cache db error: {0}")] + CacheDb(String), + + /// Leaf index mismatch during initialization. + #[error("leaf index mismatch: expected {expected}, got {provided}")] + InvalidLeafIndex { + /// Leaf index stored in the vault. + expected: u64, + /// Leaf index provided by the caller. + provided: u64, + }, + + /// Vault database integrity check failed. + #[error("vault integrity check failed: {0}")] + CorruptedVault(String), + + /// Storage has not been initialized yet. + #[error("storage not initialized")] + NotInitialized, + + /// Nullifier already disclosed for a different request. + #[error("nullifier already disclosed")] + NullifierAlreadyDisclosed, + + /// Credential not found in the vault. + #[error("credential not found")] + CredentialNotFound, + + /// Unexpected `UniFFI` callback error. + #[error("unexpected uniffi callback error: {0}")] + UnexpectedUniFFICallbackError(String), +} + +impl From for StorageError { + fn from(error: uniffi::UnexpectedUniFFICallbackError) -> Self { + Self::UnexpectedUniFFICallbackError(error.reason) + } +} diff --git a/walletkit-core/src/storage/keys.rs b/walletkit-core/src/storage/keys.rs new file mode 100644 index 000000000..b99433753 --- /dev/null +++ b/walletkit-core/src/storage/keys.rs @@ -0,0 +1,166 @@ +//! Key hierarchy management for credential storage. + +use rand::{rngs::OsRng, RngCore}; +use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; + +use super::{ + envelope::AccountKeyEnvelope, + error::{StorageError, StorageResult}, + lock::StorageLockGuard, + traits::{AtomicBlobStore, DeviceKeystore}, + ACCOUNT_KEYS_FILENAME, ACCOUNT_KEY_ENVELOPE_AD, +}; + +/// In-memory account keys derived from the account key envelope. +/// +/// Keys are held in memory for the lifetime of the storage handle. +#[derive(Zeroize, ZeroizeOnDrop)] +#[allow(clippy::struct_field_names)] +pub struct StorageKeys { + intermediate_key: [u8; 32], +} + +impl StorageKeys { + /// Initializes storage keys by opening or creating the account key envelope. + /// + /// # Errors + /// + /// Returns an error if the envelope cannot be read, decrypted, or parsed, + /// or if persistence to the blob store fails. + pub fn init( + keystore: &dyn DeviceKeystore, + blob_store: &dyn AtomicBlobStore, + _lock: &StorageLockGuard, + now: u64, + ) -> StorageResult { + if let Some(bytes) = blob_store.read(ACCOUNT_KEYS_FILENAME.to_string())? { + let envelope = AccountKeyEnvelope::deserialize(&bytes)?; + let wrapped_k_intermediate = envelope.wrapped_k_intermediate.clone(); + let k_intermediate_bytes = Zeroizing::new(keystore.open_sealed( + ACCOUNT_KEY_ENVELOPE_AD.to_vec(), + wrapped_k_intermediate, + )?); + let k_intermediate = + parse_key_32(k_intermediate_bytes.as_slice(), "K_intermediate")?; + Ok(Self { + intermediate_key: k_intermediate, + }) + } else { + let k_intermediate = random_key(); + let wrapped_k_intermediate = keystore + .seal(ACCOUNT_KEY_ENVELOPE_AD.to_vec(), k_intermediate.to_vec())?; + let envelope = AccountKeyEnvelope::new(wrapped_k_intermediate, now); + let bytes = envelope.serialize()?; + blob_store.write_atomic(ACCOUNT_KEYS_FILENAME.to_string(), bytes)?; + Ok(Self { + intermediate_key: k_intermediate, + }) + } + } + + /// Returns the intermediate key. Treat this as sensitive material. + #[must_use] + pub const fn intermediate_key(&self) -> [u8; 32] { + self.intermediate_key + } +} + +fn random_key() -> [u8; 32] { + let mut key = [0u8; 32]; + OsRng.fill_bytes(&mut key); + key +} + +fn parse_key_32(bytes: &[u8], label: &str) -> StorageResult<[u8; 32]> { + if bytes.len() != 32 { + return Err(StorageError::InvalidEnvelope(format!( + "{label} length mismatch: expected 32, got {}", + bytes.len() + ))); + } + let mut out = [0u8; 32]; + out.copy_from_slice(bytes); + Ok(out) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::storage::lock::StorageLock; + use crate::storage::tests_utils::{InMemoryBlobStore, InMemoryKeystore}; + use uuid::Uuid; + + fn temp_lock_path() -> std::path::PathBuf { + let mut path = std::env::temp_dir(); + path.push(format!("walletkit-keys-lock-{}.lock", Uuid::new_v4())); + path + } + + #[test] + fn test_storage_keys_round_trip() { + let keystore = InMemoryKeystore::new(); + let blob_store = InMemoryBlobStore::new(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let keys_first = + StorageKeys::init(&keystore, &blob_store, &guard, 100).expect("init"); + let keys_second = + StorageKeys::init(&keystore, &blob_store, &guard, 200).expect("init"); + + assert_eq!(keys_first.intermediate_key, keys_second.intermediate_key); + let _ = std::fs::remove_file(lock_path); + } + + #[test] + fn test_storage_keys_keystore_mismatch_fails() { + let keystore = InMemoryKeystore::new(); + let blob_store = InMemoryBlobStore::new(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + StorageKeys::init(&keystore, &blob_store, &guard, 123).expect("init"); + + let other_keystore = InMemoryKeystore::new(); + match StorageKeys::init(&other_keystore, &blob_store, &guard, 456) { + Err( + StorageError::Crypto(_) + | StorageError::InvalidEnvelope(_) + | StorageError::Keystore(_), + ) => {} + Err(err) => panic!("unexpected error: {err}"), + Ok(_) => panic!("expected error"), + } + let _ = std::fs::remove_file(lock_path); + } + + #[test] + fn test_storage_keys_tampered_envelope_fails() { + let keystore = InMemoryKeystore::new(); + let blob_store = InMemoryBlobStore::new(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + StorageKeys::init(&keystore, &blob_store, &guard, 123).expect("init"); + + let mut bytes = blob_store + .read(ACCOUNT_KEYS_FILENAME.to_string()) + .expect("read") + .expect("present"); + bytes[0] ^= 0xFF; + blob_store + .write_atomic(ACCOUNT_KEYS_FILENAME.to_string(), bytes) + .expect("write"); + + match StorageKeys::init(&keystore, &blob_store, &guard, 456) { + Err( + StorageError::Serialization(_) + | StorageError::Crypto(_) + | StorageError::UnsupportedEnvelopeVersion(_), + ) => {} + Err(err) => panic!("unexpected error: {err}"), + Ok(_) => panic!("expected error"), + } + let _ = std::fs::remove_file(lock_path); + } +} diff --git a/walletkit-core/src/storage/lock.rs b/walletkit-core/src/storage/lock.rs new file mode 100644 index 000000000..f421890ab --- /dev/null +++ b/walletkit-core/src/storage/lock.rs @@ -0,0 +1,286 @@ +//! File-based storage lock for serializing writes. + +use std::fs::{self, File, OpenOptions}; +use std::path::Path; +use std::sync::Arc; + +use super::error::{StorageError, StorageResult}; + +/// A file-backed lock that serializes storage mutations across processes. +#[derive(Debug, Clone)] +pub struct StorageLock { + file: Arc, +} + +impl StorageLock { + /// Opens or creates the lock file at `path`. + /// + /// # Errors + /// + /// Returns an error if the file cannot be opened or created. + pub fn open(path: &Path) -> StorageResult { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent).map_err(|err| map_io_err(&err))?; + } + let file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(false) + .open(path) + .map_err(|err| map_io_err(&err))?; + Ok(Self { + file: Arc::new(file), + }) + } + + /// Acquires the exclusive lock. + /// + /// # Errors + /// + /// Returns an error if the lock cannot be acquired. + pub fn lock(&self) -> StorageResult { + lock_exclusive(&self.file).map_err(|err| map_io_err(&err))?; + Ok(StorageLockGuard { + file: Arc::clone(&self.file), + }) + } + + /// Attempts to acquire the exclusive lock without blocking. + /// + /// # Errors + /// + /// Returns an error if the lock attempt fails for reasons other than + /// the lock being held by another process. + pub fn try_lock(&self) -> StorageResult> { + if try_lock_exclusive(&self.file).map_err(|err| map_io_err(&err))? { + Ok(Some(StorageLockGuard { + file: Arc::clone(&self.file), + })) + } else { + Ok(None) + } + } +} + +/// Guard that holds an exclusive lock for its lifetime. +#[derive(Debug)] +pub struct StorageLockGuard { + file: Arc, +} + +impl Drop for StorageLockGuard { + fn drop(&mut self) { + let _ = unlock(&self.file); + } +} + +fn map_io_err(err: &std::io::Error) -> StorageError { + StorageError::Lock(err.to_string()) +} + +#[cfg(unix)] +fn lock_exclusive(file: &File) -> std::io::Result<()> { + let fd = std::os::unix::io::AsRawFd::as_raw_fd(file); + let result = unsafe { flock(fd, LOCK_EX) }; + if result == 0 { + Ok(()) + } else { + Err(std::io::Error::last_os_error()) + } +} + +#[cfg(unix)] +fn try_lock_exclusive(file: &File) -> std::io::Result { + let fd = std::os::unix::io::AsRawFd::as_raw_fd(file); + let result = unsafe { flock(fd, LOCK_EX | LOCK_NB) }; + if result == 0 { + Ok(true) + } else { + let err = std::io::Error::last_os_error(); + if err.kind() == std::io::ErrorKind::WouldBlock { + Ok(false) + } else { + Err(err) + } + } +} + +#[cfg(unix)] +fn unlock(file: &File) -> std::io::Result<()> { + let fd = std::os::unix::io::AsRawFd::as_raw_fd(file); + let result = unsafe { flock(fd, LOCK_UN) }; + if result == 0 { + Ok(()) + } else { + Err(std::io::Error::last_os_error()) + } +} + +#[cfg(unix)] +use std::os::raw::c_int; + +#[cfg(unix)] +const LOCK_EX: c_int = 2; +#[cfg(unix)] +const LOCK_NB: c_int = 4; +#[cfg(unix)] +const LOCK_UN: c_int = 8; + +#[cfg(unix)] +extern "C" { + fn flock(fd: c_int, operation: c_int) -> c_int; +} + +#[cfg(windows)] +fn lock_exclusive(file: &File) -> std::io::Result<()> { + lock_file(file, 0) +} + +#[cfg(windows)] +fn try_lock_exclusive(file: &File) -> std::io::Result { + match lock_file(file, LOCKFILE_FAIL_IMMEDIATELY) { + Ok(()) => Ok(true), + Err(err) => { + if err.raw_os_error() == Some(ERROR_LOCK_VIOLATION) { + Ok(false) + } else { + Err(err) + } + } + } +} + +#[cfg(windows)] +fn unlock(file: &File) -> std::io::Result<()> { + let handle = std::os::windows::io::AsRawHandle::as_raw_handle(file) as HANDLE; + let mut overlapped: OVERLAPPED = unsafe { std::mem::zeroed() }; + let result = unsafe { UnlockFileEx(handle, 0, 1, 0, &mut overlapped) }; + if result != 0 { + Ok(()) + } else { + Err(std::io::Error::last_os_error()) + } +} + +#[cfg(windows)] +fn lock_file(file: &File, flags: u32) -> std::io::Result<()> { + let handle = std::os::windows::io::AsRawHandle::as_raw_handle(file) as HANDLE; + let mut overlapped: OVERLAPPED = unsafe { std::mem::zeroed() }; + let result = unsafe { + LockFileEx( + handle, + LOCKFILE_EXCLUSIVE_LOCK | flags, + 0, + 1, + 0, + &mut overlapped, + ) + }; + if result != 0 { + Ok(()) + } else { + Err(std::io::Error::last_os_error()) + } +} + +#[cfg(windows)] +type HANDLE = *mut std::ffi::c_void; + +#[cfg(windows)] +#[repr(C)] +struct OVERLAPPED { + internal: usize, + internal_high: usize, + offset: u32, + offset_high: u32, + h_event: HANDLE, +} + +#[cfg(windows)] +const LOCKFILE_EXCLUSIVE_LOCK: u32 = 0x2; +#[cfg(windows)] +const LOCKFILE_FAIL_IMMEDIATELY: u32 = 0x1; +#[cfg(windows)] +const ERROR_LOCK_VIOLATION: i32 = 33; + +#[cfg(windows)] +extern "system" { + fn LockFileEx( + h_file: HANDLE, + flags: u32, + reserved: u32, + bytes_to_lock_low: u32, + bytes_to_lock_high: u32, + overlapped: *mut OVERLAPPED, + ) -> i32; + fn UnlockFileEx( + h_file: HANDLE, + reserved: u32, + bytes_to_unlock_low: u32, + bytes_to_unlock_high: u32, + overlapped: *mut OVERLAPPED, + ) -> i32; +} + +#[cfg(test)] +mod tests { + use super::*; + use uuid::Uuid; + + fn temp_lock_path() -> std::path::PathBuf { + let mut path = std::env::temp_dir(); + path.push(format!("walletkit-lock-{}.lock", Uuid::new_v4())); + path + } + + #[test] + fn test_lock_is_exclusive() { + let path = temp_lock_path(); + let lock_a = StorageLock::open(&path).expect("open lock"); + let guard = lock_a.lock().expect("acquire lock"); + + let lock_b = StorageLock::open(&path).expect("open lock"); + let blocked = lock_b.try_lock().expect("try lock"); + assert!(blocked.is_none()); + + drop(guard); + let guard = lock_b.try_lock().expect("try lock"); + assert!(guard.is_some()); + + let _ = std::fs::remove_file(path); + } + + #[test] + fn test_lock_serializes_across_threads() { + let path = temp_lock_path(); + let lock = StorageLock::open(&path).expect("open lock"); + + let (locked_tx, locked_rx) = std::sync::mpsc::channel(); + let (release_tx, release_rx) = std::sync::mpsc::channel(); + let (released_tx, released_rx) = std::sync::mpsc::channel(); + + let path_clone = path.clone(); + let thread_a = std::thread::spawn(move || { + let guard = lock.lock().expect("lock in thread"); + locked_tx.send(()).expect("signal locked"); + release_rx.recv().expect("wait release"); + drop(guard); + released_tx.send(()).expect("signal released"); + let _ = std::fs::remove_file(path_clone); + }); + + locked_rx.recv().expect("wait locked"); + let lock_b = StorageLock::open(&path).expect("open lock"); + let blocked = lock_b.try_lock().expect("try lock"); + assert!(blocked.is_none()); + + release_tx.send(()).expect("release"); + released_rx.recv().expect("wait released"); + + let guard = lock_b.try_lock().expect("try lock"); + assert!(guard.is_some()); + + thread_a.join().expect("thread join"); + } +} diff --git a/walletkit-core/src/storage/mod.rs b/walletkit-core/src/storage/mod.rs new file mode 100644 index 000000000..0214089ca --- /dev/null +++ b/walletkit-core/src/storage/mod.rs @@ -0,0 +1,32 @@ +//! Credential storage primitives: key envelope and key hierarchy helpers. + +pub mod cache; +pub mod credential_storage; +pub mod envelope; +pub mod error; +pub mod keys; +pub mod lock; +pub mod paths; +pub(crate) mod sqlcipher; +pub mod traits; +pub mod types; +pub mod vault; + +pub use cache::CacheDb; +pub use credential_storage::{CredentialStorage, CredentialStore}; +pub use error::{StorageError, StorageResult}; +pub use keys::StorageKeys; +pub use lock::{StorageLock, StorageLockGuard}; +pub use paths::StoragePaths; +pub use traits::{AtomicBlobStore, DeviceKeystore, StorageProvider}; +pub use types::{ + BlobKind, ContentId, CredentialRecord, Nullifier, ReplayGuardKind, + ReplayGuardResult, RequestId, +}; +pub use vault::VaultDb; + +pub(crate) const ACCOUNT_KEYS_FILENAME: &str = "account_keys.bin"; +pub(crate) const ACCOUNT_KEY_ENVELOPE_AD: &[u8] = b"worldid:account-key-envelope"; + +#[cfg(test)] +pub(crate) mod tests_utils; diff --git a/walletkit-core/src/storage/paths.rs b/walletkit-core/src/storage/paths.rs new file mode 100644 index 000000000..65820b2c4 --- /dev/null +++ b/walletkit-core/src/storage/paths.rs @@ -0,0 +1,94 @@ +//! Storage path helpers. + +use std::path::{Path, PathBuf}; + +const VAULT_FILENAME: &str = "account.vault.sqlite"; +const CACHE_FILENAME: &str = "account.cache.sqlite"; +const LOCK_FILENAME: &str = "lock"; + +/// Paths for credential storage artifacts under `/worldid`. +#[derive(Debug, Clone, uniffi::Object)] +pub struct StoragePaths { + root: PathBuf, + worldid_dir: PathBuf, +} + +impl StoragePaths { + /// Builds storage paths rooted at `root`. + #[must_use] + pub fn new(root: impl AsRef) -> Self { + let root = root.as_ref().to_path_buf(); + let worldid_dir = root.join("worldid"); + Self { root, worldid_dir } + } + + /// Returns the storage root directory. + #[must_use] + pub fn root(&self) -> &Path { + &self.root + } + + /// Returns the World ID storage directory. + #[must_use] + pub fn worldid_dir(&self) -> &Path { + &self.worldid_dir + } + + /// Returns the path to the vault database. + #[must_use] + pub fn vault_db_path(&self) -> PathBuf { + self.worldid_dir.join(VAULT_FILENAME) + } + + /// Returns the path to the cache database. + #[must_use] + pub fn cache_db_path(&self) -> PathBuf { + self.worldid_dir.join(CACHE_FILENAME) + } + + /// Returns the path to the lock file. + #[must_use] + pub fn lock_path(&self) -> PathBuf { + self.worldid_dir.join(LOCK_FILENAME) + } +} + +#[uniffi::export] +impl StoragePaths { + /// Builds storage paths rooted at `root`. + #[uniffi::constructor] + #[must_use] + pub fn from_root(root: String) -> Self { + Self::new(PathBuf::from(root)) + } + + /// Returns the storage root directory as a string. + #[must_use] + pub fn root_path_string(&self) -> String { + self.root.to_string_lossy().to_string() + } + + /// Returns the World ID storage directory as a string. + #[must_use] + pub fn worldid_dir_path_string(&self) -> String { + self.worldid_dir.to_string_lossy().to_string() + } + + /// Returns the path to the vault database as a string. + #[must_use] + pub fn vault_db_path_string(&self) -> String { + self.vault_db_path().to_string_lossy().to_string() + } + + /// Returns the path to the cache database as a string. + #[must_use] + pub fn cache_db_path_string(&self) -> String { + self.cache_db_path().to_string_lossy().to_string() + } + + /// Returns the path to the lock file as a string. + #[must_use] + pub fn lock_path_string(&self) -> String { + self.lock_path().to_string_lossy().to_string() + } +} diff --git a/walletkit-core/src/storage/sqlcipher.rs b/walletkit-core/src/storage/sqlcipher.rs new file mode 100644 index 000000000..2cf006e38 --- /dev/null +++ b/walletkit-core/src/storage/sqlcipher.rs @@ -0,0 +1,92 @@ +//! Shared `SQLCipher` helpers for storage databases. + +use std::fmt; +use std::path::Path; + +use rusqlite::{Connection, OpenFlags}; +use zeroize::{Zeroize, Zeroizing}; + +/// `SQLCipher` helper errors. +#[derive(Debug)] +pub enum SqlcipherError { + /// `SQLite` error. + Sqlite(rusqlite::Error), + /// `SQLCipher` is unavailable in the current build. + CipherUnavailable, +} + +impl fmt::Display for SqlcipherError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Sqlite(err) => write!(f, "{err}"), + Self::CipherUnavailable => write!(f, "sqlcipher not available"), + } + } +} + +impl From for SqlcipherError { + fn from(err: rusqlite::Error) -> Self { + Self::Sqlite(err) + } +} + +/// Result type for `SQLCipher` helper operations. +pub type SqlcipherResult = Result; + +/// Opens a `SQLite` connection with consistent flags. +/// +/// Pass `read_only = true` for read-only access; `false` enables read/write +/// access and creates the database if needed. +pub(super) fn open_connection( + path: &Path, + read_only: bool, +) -> SqlcipherResult { + let flags = if read_only { + OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_FULL_MUTEX + } else { + OpenFlags::SQLITE_OPEN_READ_WRITE + | OpenFlags::SQLITE_OPEN_CREATE + | OpenFlags::SQLITE_OPEN_FULL_MUTEX + }; + Ok(Connection::open_with_flags(path, flags)?) +} + +/// Applies `SQLCipher` keying and validates cipher availability. +pub(super) fn apply_key( + conn: &Connection, + mut k_intermediate: [u8; 32], +) -> SqlcipherResult<()> { + let key_hex = Zeroizing::new(hex::encode(k_intermediate)); + let pragma = Zeroizing::new(format!("PRAGMA key = \"x'{}'\";", key_hex.as_str())); + conn.execute_batch(&pragma)?; + let cipher_version: String = + conn.query_row("PRAGMA cipher_version;", [], |row| row.get(0))?; + if cipher_version.trim().is_empty() { + return Err(SqlcipherError::CipherUnavailable); + } + k_intermediate.zeroize(); + Ok(()) +} + +/// Configures durable WAL settings. +/// +/// Rationale: +/// - `journal_mode = WAL` enables concurrent readers during writes +/// - `synchronous = FULL` maximizes crash consistency +pub(super) fn configure_connection(conn: &Connection) -> SqlcipherResult<()> { + conn.execute_batch( + "PRAGMA foreign_keys = ON; + PRAGMA journal_mode = WAL; + PRAGMA synchronous = FULL;", + )?; + Ok(()) +} + +/// Runs an integrity check. +/// +/// Uses `PRAGMA integrity_check` to detect corruption on open. +pub(super) fn integrity_check(conn: &Connection) -> SqlcipherResult { + let result: String = + conn.query_row("PRAGMA integrity_check;", [], |row| row.get(0))?; + Ok(result.trim() == "ok") +} diff --git a/walletkit-core/src/storage/tests_utils.rs b/walletkit-core/src/storage/tests_utils.rs new file mode 100644 index 000000000..865b510cf --- /dev/null +++ b/walletkit-core/src/storage/tests_utils.rs @@ -0,0 +1,161 @@ +//! Test helpers for credential storage. + +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use chacha20poly1305::{ + aead::{Aead, KeyInit, Payload}, + Key, XChaCha20Poly1305, XNonce, +}; +use rand::{rngs::OsRng, RngCore}; + +use std::path::Path; + +use super::{ + error::StorageError, + paths::StoragePaths, + traits::{DeviceKeystore, StorageProvider}, + AtomicBlobStore, +}; + +pub struct InMemoryKeystore { + key: [u8; 32], +} + +impl InMemoryKeystore { + pub fn new() -> Self { + let mut key = [0u8; 32]; + OsRng.fill_bytes(&mut key); + Self { key } + } +} + +impl Default for InMemoryKeystore { + fn default() -> Self { + Self::new() + } +} + +impl DeviceKeystore for InMemoryKeystore { + fn seal( + &self, + associated_data: Vec, + plaintext: Vec, + ) -> Result, StorageError> { + let cipher = XChaCha20Poly1305::new(Key::from_slice(&self.key)); + let mut nonce_bytes = [0u8; 24]; + OsRng.fill_bytes(&mut nonce_bytes); + let ciphertext = cipher + .encrypt( + XNonce::from_slice(&nonce_bytes), + Payload { + msg: &plaintext, + aad: &associated_data, + }, + ) + .map_err(|err| StorageError::Crypto(err.to_string()))?; + let mut out = Vec::with_capacity(nonce_bytes.len() + ciphertext.len()); + out.extend_from_slice(&nonce_bytes); + out.extend_from_slice(&ciphertext); + Ok(out) + } + + fn open_sealed( + &self, + associated_data: Vec, + ciphertext: Vec, + ) -> Result, StorageError> { + if ciphertext.len() < 24 { + return Err(StorageError::InvalidEnvelope( + "keystore ciphertext too short".to_string(), + )); + } + let (nonce_bytes, payload) = ciphertext.split_at(24); + let cipher = XChaCha20Poly1305::new(Key::from_slice(&self.key)); + cipher + .decrypt( + XNonce::from_slice(nonce_bytes), + Payload { + msg: payload, + aad: &associated_data, + }, + ) + .map_err(|err| StorageError::Crypto(err.to_string())) + } +} + +pub struct InMemoryBlobStore { + blobs: Mutex>>, +} + +impl InMemoryBlobStore { + pub fn new() -> Self { + Self { + blobs: Mutex::new(HashMap::new()), + } + } +} + +impl Default for InMemoryBlobStore { + fn default() -> Self { + Self::new() + } +} + +impl AtomicBlobStore for InMemoryBlobStore { + fn read(&self, path: String) -> Result>, StorageError> { + let guard = self + .blobs + .lock() + .map_err(|_| StorageError::BlobStore("mutex poisoned".to_string()))?; + Ok(guard.get(&path).cloned()) + } + + fn write_atomic(&self, path: String, bytes: Vec) -> Result<(), StorageError> { + self.blobs + .lock() + .map_err(|_| StorageError::BlobStore("mutex poisoned".to_string()))? + .insert(path, bytes); + Ok(()) + } + + fn delete(&self, path: String) -> Result<(), StorageError> { + self.blobs + .lock() + .map_err(|_| StorageError::BlobStore("mutex poisoned".to_string()))? + .remove(&path); + Ok(()) + } +} + +pub struct InMemoryStorageProvider { + keystore: Arc, + blob_store: Arc, + paths: Arc, +} + +impl InMemoryStorageProvider { + pub fn new(root: impl AsRef) -> Self { + Self { + keystore: Arc::new(InMemoryKeystore::new()), + blob_store: Arc::new(InMemoryBlobStore::new()), + paths: Arc::new(StoragePaths::new(root)), + } + } +} + +impl StorageProvider for InMemoryStorageProvider { + fn keystore(&self) -> Arc { + self.keystore.clone() + } + + fn blob_store(&self) -> Arc { + self.blob_store.clone() + } + + fn paths(&self) -> Arc { + Arc::clone(&self.paths) + } +} diff --git a/walletkit-core/src/storage/traits.rs b/walletkit-core/src/storage/traits.rs new file mode 100644 index 000000000..0a82e5551 --- /dev/null +++ b/walletkit-core/src/storage/traits.rs @@ -0,0 +1,90 @@ +//! Platform interfaces for credential storage. +//! +//! ## Key structure +//! +//! - `K_device`: device-bound root key managed by `DeviceKeystore`. +//! - `account_keys.bin`: account key envelope stored via `AtomicBlobStore` and +//! containing `DeviceKeystore::seal` of `K_intermediate` with associated data +//! `worldid:account-key-envelope`. +//! - `K_intermediate`: 32-byte per-account key unsealed at init and kept in +//! memory for the lifetime of the storage handle. +//! - `SQLCipher` databases: `account.vault.sqlite` (authoritative) and +//! `account.cache.sqlite` (non-authoritative) are opened with `K_intermediate`. +//! - Derived keys: per relying-party session keys may be derived from +//! `K_intermediate` and cached in `account.cache.sqlite` for performance. +//! cached in `account.cache.sqlite` for performance. + +use std::sync::Arc; + +use super::error::StorageResult; +use super::paths::StoragePaths; + +/// Device keystore interface used to seal and open account keys. +#[uniffi::export(with_foreign)] +pub trait DeviceKeystore: Send + Sync { + /// Seals plaintext under the device-bound key, authenticating `associated_data`. + /// + /// The associated data is not encrypted, but it is integrity-protected as part + /// of the seal operation. Any mismatch when opening must fail. + /// + /// # Errors + /// + /// Returns an error if the keystore refuses the operation or the seal fails. + fn seal( + &self, + associated_data: Vec, + plaintext: Vec, + ) -> StorageResult>; + + /// Opens ciphertext under the device-bound key, verifying `associated_data`. + /// + /// The same associated data used during sealing must be supplied or the open + /// operation must fail. + /// + /// # Errors + /// + /// Returns an error if authentication fails or the keystore cannot open. + fn open_sealed( + &self, + associated_data: Vec, + ciphertext: Vec, + ) -> StorageResult>; +} + +/// Atomic blob store for small binary files (e.g., `account_keys.bin`). +#[uniffi::export(with_foreign)] +pub trait AtomicBlobStore: Send + Sync { + /// Reads the blob at `path`, if present. + /// + /// # Errors + /// + /// Returns an error if the read fails. + fn read(&self, path: String) -> StorageResult>>; + + /// Writes bytes atomically to `path`. + /// + /// # Errors + /// + /// Returns an error if the write fails. + fn write_atomic(&self, path: String, bytes: Vec) -> StorageResult<()>; + + /// Deletes the blob at `path`. + /// + /// # Errors + /// + /// Returns an error if the delete fails. + fn delete(&self, path: String) -> StorageResult<()>; +} + +/// Provider responsible for platform-specific storage components and paths. +#[uniffi::export(with_foreign)] +pub trait StorageProvider: Send + Sync { + /// Returns the device keystore implementation. + fn keystore(&self) -> Arc; + + /// Returns the blob store implementation. + fn blob_store(&self) -> Arc; + + /// Returns the storage paths selected by the platform. + fn paths(&self) -> Arc; +} diff --git a/walletkit-core/src/storage/types.rs b/walletkit-core/src/storage/types.rs new file mode 100644 index 000000000..40833812a --- /dev/null +++ b/walletkit-core/src/storage/types.rs @@ -0,0 +1,75 @@ +//! Public types for credential storage. + +use super::error::{StorageError, StorageResult}; + +/// Kind of blob stored in the vault. +/// +/// Blob records (stored in the `blob_objects` table) carry a kind tag that +/// distinguishes credential payloads from associated data. +#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Enum)] +#[repr(u8)] +pub enum BlobKind { + /// Credential blob payload. + CredentialBlob = 1, + /// Associated data payload. + AssociatedData = 2, +} + +impl BlobKind { + pub(crate) const fn as_i64(self) -> i64 { + self as i64 + } +} + +impl TryFrom for BlobKind { + type Error = StorageError; + + fn try_from(value: i64) -> StorageResult { + match value { + 1 => Ok(Self::CredentialBlob), + 2 => Ok(Self::AssociatedData), + _ => Err(StorageError::VaultDb(format!("invalid blob kind {value}"))), + } + } +} + +/// Content identifier for stored blobs. +pub type ContentId = [u8; 32]; + +/// Request identifier for replay guard. +pub type RequestId = [u8; 32]; + +/// Nullifier identifier used for replay safety. +pub type Nullifier = [u8; 32]; + +/// In-memory representation of stored credential metadata. +/// +/// This is intentionally small and excludes blobs; full credential payloads can +/// be fetched separately to avoid heavy list queries. +#[derive(Debug, Clone, PartialEq, Eq, uniffi::Record)] +pub struct CredentialRecord { + /// Credential identifier. + pub credential_id: u64, + /// Issuer schema identifier. + pub issuer_schema_id: u64, + /// Expiry timestamp (seconds). + pub expires_at: u64, +} + +/// FFI-friendly replay guard result kind. +#[derive(Debug, Clone, PartialEq, Eq, uniffi::Enum)] +pub enum ReplayGuardKind { + /// Stored bytes for the first disclosure of a request. + Fresh, + /// Stored bytes replayed for an existing request. + Replay, +} + +/// Replay guard result. +#[derive(Debug, Clone, PartialEq, Eq, uniffi::Record)] +pub struct ReplayGuardResult { + /// Result kind. + pub kind: ReplayGuardKind, + /// Stored proof package bytes. + pub bytes: Vec, +} diff --git a/walletkit-core/src/storage/vault/helpers.rs b/walletkit-core/src/storage/vault/helpers.rs new file mode 100644 index 000000000..5f7cee6ec --- /dev/null +++ b/walletkit-core/src/storage/vault/helpers.rs @@ -0,0 +1,53 @@ +use rusqlite::Row; +use sha2::{Digest, Sha256}; + +use crate::storage::error::{StorageError, StorageResult}; +use crate::storage::sqlcipher::SqlcipherError; +use crate::storage::types::{BlobKind, ContentId, CredentialRecord}; + +const CONTENT_ID_PREFIX: &[u8] = b"worldid:blob"; + +pub(super) fn compute_content_id(blob_kind: BlobKind, plaintext: &[u8]) -> ContentId { + let mut hasher = Sha256::new(); + hasher.update(CONTENT_ID_PREFIX); + hasher.update([blob_kind as u8]); + hasher.update(plaintext); + let digest = hasher.finalize(); + let mut out = [0u8; 32]; + out.copy_from_slice(&digest); + out +} + +pub(super) fn map_record(row: &Row<'_>) -> StorageResult { + let credential_id: i64 = row.get(0).map_err(|err| map_db_err(&err))?; + let issuer_schema_id: i64 = row.get(1).map_err(|err| map_db_err(&err))?; + let expires_at: i64 = row.get(2).map_err(|err| map_db_err(&err))?; + Ok(CredentialRecord { + credential_id: to_u64(credential_id, "credential_id")?, + issuer_schema_id: to_u64(issuer_schema_id, "issuer_schema_id")?, + expires_at: to_u64(expires_at, "expires_at")?, + }) +} + +pub(super) fn to_i64(value: u64, label: &str) -> StorageResult { + i64::try_from(value).map_err(|_| { + StorageError::VaultDb(format!("{label} out of range for i64: {value}")) + }) +} + +pub(super) fn to_u64(value: i64, label: &str) -> StorageResult { + u64::try_from(value).map_err(|_| { + StorageError::VaultDb(format!("{label} out of range for u64: {value}")) + }) +} + +pub(super) fn map_db_err(err: &rusqlite::Error) -> StorageError { + StorageError::VaultDb(err.to_string()) +} + +pub(super) fn map_sqlcipher_err(err: SqlcipherError) -> StorageError { + match err { + SqlcipherError::Sqlite(err) => StorageError::VaultDb(err.to_string()), + SqlcipherError::CipherUnavailable => StorageError::VaultDb(err.to_string()), + } +} diff --git a/walletkit-core/src/storage/vault/mod.rs b/walletkit-core/src/storage/vault/mod.rs new file mode 100644 index 000000000..ab07ccec7 --- /dev/null +++ b/walletkit-core/src/storage/vault/mod.rs @@ -0,0 +1,233 @@ +//! Encrypted vault database for credential storage. + +mod helpers; +mod schema; +#[cfg(test)] +mod tests; + +use std::path::Path; + +use rusqlite::{params, params_from_iter, Connection}; + +use super::error::{StorageError, StorageResult}; +use super::lock::StorageLockGuard; +use super::sqlcipher; +use super::types::{BlobKind, CredentialRecord}; +use helpers::{ + compute_content_id, map_db_err, map_record, map_sqlcipher_err, to_i64, to_u64, +}; +use schema::{ensure_schema, VAULT_SCHEMA_VERSION}; + +/// Encrypted vault database wrapper. +#[derive(Debug)] +pub struct VaultDb { + conn: Connection, +} + +impl VaultDb { + /// Opens or creates the encrypted vault database at `path`. + /// + /// # Errors + /// + /// Returns an error if the database cannot be opened, keyed, or initialized. + pub fn new( + path: &Path, + k_intermediate: [u8; 32], + _lock: &StorageLockGuard, + ) -> StorageResult { + let conn = + sqlcipher::open_connection(path, false).map_err(map_sqlcipher_err)?; + sqlcipher::apply_key(&conn, k_intermediate).map_err(map_sqlcipher_err)?; + sqlcipher::configure_connection(&conn).map_err(map_sqlcipher_err)?; + ensure_schema(&conn)?; + let db = Self { conn }; + if !db.check_integrity()? { + return Err(StorageError::CorruptedVault( + "integrity_check failed".to_string(), + )); + } + Ok(db) + } + + /// Initializes or validates the leaf index for this vault. + /// + /// The leaf index is the account's position in the registry tree and must be + /// consistent for all subsequent operations. A mismatch returns an error. + /// + /// # Errors + /// + /// Returns an error if the stored leaf index does not match. + pub fn init_leaf_index( + &mut self, + _lock: &StorageLockGuard, + leaf_index: u64, + now: u64, + ) -> StorageResult<()> { + let leaf_index_i64 = to_i64(leaf_index, "leaf_index")?; + let now_i64 = to_i64(now, "now")?; + let tx = self.conn.transaction().map_err(|err| map_db_err(&err))?; + let stored = tx + .query_row( + "INSERT INTO vault_meta (schema_version, leaf_index, created_at, updated_at) + VALUES (?1, ?2, ?3, ?3) + ON CONFLICT(schema_version) DO UPDATE SET + leaf_index = CASE + WHEN vault_meta.leaf_index IS NULL + THEN excluded.leaf_index + ELSE vault_meta.leaf_index + END + RETURNING leaf_index", + params![VAULT_SCHEMA_VERSION, leaf_index_i64, now_i64], + |row| row.get::<_, i64>(0), + ) + .map_err(|err| map_db_err(&err))?; + if stored != leaf_index_i64 { + let expected = to_u64(stored, "leaf_index")?; + return Err(StorageError::InvalidLeafIndex { + expected, + provided: leaf_index, + }); + } + tx.commit().map_err(|err| map_db_err(&err))?; + Ok(()) + } + + /// Stores a credential and optional associated data. + /// + /// Blob content is deduplicated by content id to avoid storing identical + /// payloads multiple times. + /// + /// # Errors + /// + /// Returns an error if any insert fails. + #[allow(clippy::too_many_arguments)] + #[allow(clippy::needless_pass_by_value)] + pub fn store_credential( + &mut self, + _lock: &StorageLockGuard, + issuer_schema_id: u64, + subject_blinding_factor: [u8; 32], + genesis_issued_at: u64, + expires_at: u64, + credential_blob: Vec, + associated_data: Option>, + now: u64, + ) -> StorageResult { + let credential_blob_id = + compute_content_id(BlobKind::CredentialBlob, &credential_blob); + let associated_data_id = associated_data + .as_ref() + .map(|bytes| compute_content_id(BlobKind::AssociatedData, bytes)); + let now_i64 = to_i64(now, "now")?; + let issuer_schema_id_i64 = to_i64(issuer_schema_id, "issuer_schema_id")?; + let genesis_issued_at_i64 = to_i64(genesis_issued_at, "genesis_issued_at")?; + let expires_at_i64 = to_i64(expires_at, "expires_at")?; + + let tx = self.conn.transaction().map_err(|err| map_db_err(&err))?; + tx.execute( + "INSERT OR IGNORE INTO blob_objects (content_id, blob_kind, created_at, bytes) + VALUES (?1, ?2, ?3, ?4)", + params![ + credential_blob_id.as_ref(), + BlobKind::CredentialBlob.as_i64(), + now_i64, + credential_blob + ], + ) + .map_err(|err| map_db_err(&err))?; + + if let Some(data) = associated_data { + let cid = associated_data_id.as_ref().ok_or_else(|| { + StorageError::VaultDb("associated data CID must be present".to_string()) + })?; + tx.execute( + "INSERT OR IGNORE INTO blob_objects (content_id, blob_kind, created_at, bytes) + VALUES (?1, ?2, ?3, ?4)", + params![ + cid.as_ref(), + BlobKind::AssociatedData.as_i64(), + now_i64, + data + ], + ) + .map_err(|err| map_db_err(&err))?; + } + + let credential_id = tx + .query_row( + "INSERT INTO credential_records ( + issuer_schema_id, + subject_blinding_factor, + genesis_issued_at, + expires_at, + updated_at, + credential_blob_cid, + associated_data_cid + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) + RETURNING credential_id", + params![ + issuer_schema_id_i64, + subject_blinding_factor.as_ref(), + genesis_issued_at_i64, + expires_at_i64, + now_i64, + credential_blob_id.as_ref(), + associated_data_id.as_ref().map(AsRef::as_ref) + ], + |row| row.get::<_, i64>(0), + ) + .map_err(|err| map_db_err(&err))?; + + tx.commit().map_err(|err| map_db_err(&err))?; + to_u64(credential_id, "credential_id") + } + + /// Lists active credential metadata, optionally filtered by issuer schema. + /// + /// # Errors + /// + /// Returns an error if the query fails. + pub fn list_credentials( + &self, + issuer_schema_id: Option, + now: u64, + ) -> StorageResult> { + let mut records = Vec::new(); + let expires = to_i64(now, "now")?; + let issuer_schema_id_i64 = issuer_schema_id + .map(|value| to_i64(value, "issuer_schema_id")) + .transpose()?; + let mut sql = String::from( + "SELECT + cr.credential_id, + cr.issuer_schema_id, + cr.expires_at + FROM credential_records cr + WHERE cr.expires_at > ?1", + ); + let mut params: Vec<&dyn rusqlite::ToSql> = vec![&expires]; + if let Some(ref issuer_schema_id_i64) = issuer_schema_id_i64 { + sql.push_str(" AND cr.issuer_schema_id = ?2"); + params.push(issuer_schema_id_i64); + } + sql.push_str(" ORDER BY cr.updated_at DESC"); + + let mut stmt = self.conn.prepare(&sql).map_err(|err| map_db_err(&err))?; + let mut rows = stmt + .query(params_from_iter(params)) + .map_err(|err| map_db_err(&err))?; + while let Some(row) = rows.next().map_err(|err| map_db_err(&err))? { + records.push(map_record(row)?); + } + Ok(records) + } + + /// Runs an integrity check on the vault database. + /// + /// # Errors + /// + /// Returns an error if the check cannot be executed. + pub fn check_integrity(&self) -> StorageResult { + sqlcipher::integrity_check(&self.conn).map_err(map_sqlcipher_err) + } +} diff --git a/walletkit-core/src/storage/vault/schema.rs b/walletkit-core/src/storage/vault/schema.rs new file mode 100644 index 000000000..ff368f0ac --- /dev/null +++ b/walletkit-core/src/storage/vault/schema.rs @@ -0,0 +1,57 @@ +use rusqlite::Connection; + +use crate::storage::error::StorageResult; + +use super::helpers::map_db_err; + +pub(super) const VAULT_SCHEMA_VERSION: i64 = 1; + +pub(super) fn ensure_schema(conn: &Connection) -> StorageResult<()> { + conn.execute_batch( + "CREATE TABLE IF NOT EXISTS vault_meta ( + schema_version INTEGER NOT NULL, + leaf_index INTEGER, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL + ); + + CREATE UNIQUE INDEX IF NOT EXISTS idx_vault_meta_schema_version + ON vault_meta (schema_version); + + CREATE TRIGGER IF NOT EXISTS vault_meta_set_updated_at + AFTER UPDATE ON vault_meta + FOR EACH ROW + BEGIN + UPDATE vault_meta + SET updated_at = CAST(strftime('%s','now') AS INTEGER) + WHERE schema_version = NEW.schema_version; + END; + + CREATE TABLE IF NOT EXISTS credential_records ( + credential_id INTEGER NOT NULL PRIMARY KEY, + issuer_schema_id INTEGER NOT NULL, + subject_blinding_factor BLOB NOT NULL, + genesis_issued_at INTEGER NOT NULL, + expires_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL, + credential_blob_cid BLOB NOT NULL, + associated_data_cid BLOB + ); + + CREATE INDEX IF NOT EXISTS idx_cred_by_issuer_schema + ON credential_records (issuer_schema_id, updated_at DESC); + + CREATE INDEX IF NOT EXISTS idx_cred_by_expiry + ON credential_records (expires_at); + + CREATE TABLE IF NOT EXISTS blob_objects ( + content_id BLOB NOT NULL, + blob_kind INTEGER NOT NULL, + created_at INTEGER NOT NULL, + bytes BLOB NOT NULL, + PRIMARY KEY (content_id) + );", + ) + .map_err(|err| map_db_err(&err))?; + Ok(()) +} diff --git a/walletkit-core/src/storage/vault/tests.rs b/walletkit-core/src/storage/vault/tests.rs new file mode 100644 index 000000000..7268c35d3 --- /dev/null +++ b/walletkit-core/src/storage/vault/tests.rs @@ -0,0 +1,289 @@ +use super::helpers::{compute_content_id, map_db_err}; +use super::*; +use crate::storage::lock::StorageLock; +use std::fs; +use std::path::{Path, PathBuf}; +use uuid::Uuid; + +fn temp_vault_path() -> PathBuf { + let mut path = std::env::temp_dir(); + path.push(format!("walletkit-vault-{}.sqlite", Uuid::new_v4())); + path +} + +fn cleanup_vault_files(path: &Path) { + let _ = fs::remove_file(path); + let wal_path = path.with_extension("sqlite-wal"); + let shm_path = path.with_extension("sqlite-shm"); + let _ = fs::remove_file(wal_path); + let _ = fs::remove_file(shm_path); +} + +fn temp_lock_path() -> PathBuf { + let mut path = std::env::temp_dir(); + path.push(format!("walletkit-vault-lock-{}.lock", Uuid::new_v4())); + path +} + +fn cleanup_lock_file(path: &Path) { + let _ = fs::remove_file(path); +} + +fn sample_blinding_factor() -> [u8; 32] { + [0x11u8; 32] +} + +#[test] +fn test_vault_create_and_open() { + let path = temp_vault_path(); + let key = [0x42u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let db = VaultDb::new(&path, key, &guard).expect("create vault"); + drop(db); + VaultDb::new(&path, key, &guard).expect("open vault"); + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_vault_wrong_key_fails() { + let path = temp_vault_path(); + let key = [0x01u8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + VaultDb::new(&path, key, &guard).expect("create vault"); + let err = VaultDb::new(&path, [0x02u8; 32], &guard).expect_err("wrong key"); + match err { + StorageError::VaultDb(_) | StorageError::CorruptedVault(_) => {} + _ => panic!("unexpected error: {err}"), + } + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_leaf_index_set_once() { + let path = temp_vault_path(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = VaultDb::new(&path, [0x03u8; 32], &guard).expect("create vault"); + db.init_leaf_index(&guard, 42, 100) + .expect("init leaf index"); + db.init_leaf_index(&guard, 42, 200) + .expect("init leaf index again"); + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_leaf_index_immutable() { + let path = temp_vault_path(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = VaultDb::new(&path, [0x04u8; 32], &guard).expect("create vault"); + db.init_leaf_index(&guard, 7, 100).expect("init leaf index"); + let err = db.init_leaf_index(&guard, 8, 200).expect_err("mismatch"); + match err { + StorageError::InvalidLeafIndex { .. } => {} + _ => panic!("unexpected error: {err}"), + } + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_store_credential_without_associated_data() { + let path = temp_vault_path(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = VaultDb::new(&path, [0x05u8; 32], &guard).expect("create vault"); + let credential_id = db + .store_credential( + &guard, + 10, + sample_blinding_factor(), + 123, + 2000, + b"credential".to_vec(), + None, + 1000, + ) + .expect("store credential"); + let records = db.list_credentials(None, 1000).expect("list credentials"); + assert_eq!(records.len(), 1); + assert_eq!(records[0].credential_id, credential_id); + assert_eq!(records[0].issuer_schema_id, 10); + assert_eq!(records[0].expires_at, 2000); + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_store_credential_with_associated_data() { + let path = temp_vault_path(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = VaultDb::new(&path, [0x06u8; 32], &guard).expect("create vault"); + db.store_credential( + &guard, + 11, + sample_blinding_factor(), + 456, + 2000, + b"credential-2".to_vec(), + Some(b"associated".to_vec()), + 1000, + ) + .expect("store credential"); + let records = db.list_credentials(None, 1000).expect("list credentials"); + assert_eq!(records.len(), 1); + assert_eq!(records[0].issuer_schema_id, 11); + assert_eq!(records[0].expires_at, 2000); + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_content_id_determinism() { + let a = compute_content_id(BlobKind::CredentialBlob, b"data"); + let b = compute_content_id(BlobKind::CredentialBlob, b"data"); + assert_eq!(a, b); +} + +#[test] +fn test_content_id_deduplication() { + let path = temp_vault_path(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = VaultDb::new(&path, [0x07u8; 32], &guard).expect("create vault"); + db.store_credential( + &guard, + 12, + sample_blinding_factor(), + 1, + 2000, + b"same".to_vec(), + None, + 1000, + ) + .expect("store credential"); + db.store_credential( + &guard, + 12, + sample_blinding_factor(), + 1, + 2000, + b"same".to_vec(), + None, + 1001, + ) + .expect("store credential"); + let count: i64 = db + .conn + .query_row("SELECT COUNT(*) FROM blob_objects", [], |row| row.get(0)) + .map_err(|err| map_db_err(&err)) + .expect("count blobs"); + assert_eq!(count, 1); + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_list_credentials_by_issuer() { + let path = temp_vault_path(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = VaultDb::new(&path, [0x08u8; 32], &guard).expect("create vault"); + db.store_credential( + &guard, + 100, + sample_blinding_factor(), + 1, + 2000, + b"issuer-a".to_vec(), + None, + 1000, + ) + .expect("store credential"); + db.store_credential( + &guard, + 200, + sample_blinding_factor(), + 1, + 2000, + b"issuer-b".to_vec(), + None, + 1000, + ) + .expect("store credential"); + let records = db + .list_credentials(Some(200), 1000) + .expect("list credentials"); + assert_eq!(records.len(), 1); + assert_eq!(records[0].issuer_schema_id, 200); + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_list_credentials_excludes_expired() { + let path = temp_vault_path(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let mut db = VaultDb::new(&path, [0x09u8; 32], &guard).expect("create vault"); + db.store_credential( + &guard, + 300, + sample_blinding_factor(), + 1, + 900, + b"expired".to_vec(), + None, + 1000, + ) + .expect("store credential"); + let records = db.list_credentials(None, 1000).expect("list credentials"); + assert!(records.is_empty()); + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_vault_integrity_check() { + let path = temp_vault_path(); + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + let db = VaultDb::new(&path, [0x0Au8; 32], &guard).expect("create vault"); + assert!(db.check_integrity().expect("integrity")); + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} + +#[test] +fn test_vault_corruption_handling() { + let path = temp_vault_path(); + let key = [0x0Bu8; 32]; + let lock_path = temp_lock_path(); + let lock = StorageLock::open(&lock_path).expect("open lock"); + let guard = lock.lock().expect("lock"); + VaultDb::new(&path, key, &guard).expect("create vault"); + fs::write(&path, b"corrupt").expect("corrupt file"); + let err = VaultDb::new(&path, key, &guard).expect_err("corrupt vault"); + match err { + StorageError::VaultDb(_) | StorageError::CorruptedVault(_) => {} + _ => panic!("unexpected error: {err}"), + } + cleanup_vault_files(&path); + cleanup_lock_file(&lock_path); +} diff --git a/walletkit-core/tests/credential_storage_integration.rs b/walletkit-core/tests/credential_storage_integration.rs new file mode 100644 index 000000000..bec879b3a --- /dev/null +++ b/walletkit-core/tests/credential_storage_integration.rs @@ -0,0 +1,271 @@ +#![cfg(feature = "storage")] + +use std::collections::HashMap; +use std::fs; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, Mutex}; + +use chacha20poly1305::{ + aead::{Aead, KeyInit, Payload}, + Key, XChaCha20Poly1305, XNonce, +}; +use rand::{rngs::OsRng, RngCore}; +use uuid::Uuid; + +use walletkit_core::storage::{ + AtomicBlobStore, CredentialStorage, CredentialStore, DeviceKeystore, + ReplayGuardKind, ReplayGuardResult, StoragePaths, StorageProvider, +}; + +struct InMemoryKeystore { + key: [u8; 32], +} + +impl InMemoryKeystore { + fn new() -> Self { + let mut key = [0u8; 32]; + OsRng.fill_bytes(&mut key); + Self { key } + } +} + +impl DeviceKeystore for InMemoryKeystore { + fn seal( + &self, + associated_data: Vec, + plaintext: Vec, + ) -> Result, walletkit_core::storage::StorageError> { + let cipher = XChaCha20Poly1305::new(Key::from_slice(&self.key)); + let mut nonce_bytes = [0u8; 24]; + OsRng.fill_bytes(&mut nonce_bytes); + let ciphertext = cipher + .encrypt( + XNonce::from_slice(&nonce_bytes), + Payload { + msg: &plaintext, + aad: &associated_data, + }, + ) + .map_err(|err| { + walletkit_core::storage::StorageError::Crypto(err.to_string()) + })?; + let mut out = Vec::with_capacity(nonce_bytes.len() + ciphertext.len()); + out.extend_from_slice(&nonce_bytes); + out.extend_from_slice(&ciphertext); + Ok(out) + } + + fn open_sealed( + &self, + associated_data: Vec, + ciphertext: Vec, + ) -> Result, walletkit_core::storage::StorageError> { + if ciphertext.len() < 24 { + return Err(walletkit_core::storage::StorageError::InvalidEnvelope( + "keystore ciphertext too short".to_string(), + )); + } + let (nonce_bytes, payload) = ciphertext.split_at(24); + let cipher = XChaCha20Poly1305::new(Key::from_slice(&self.key)); + cipher + .decrypt( + XNonce::from_slice(nonce_bytes), + Payload { + msg: payload, + aad: &associated_data, + }, + ) + .map_err(|err| { + walletkit_core::storage::StorageError::Crypto(err.to_string()) + }) + } +} + +struct InMemoryBlobStore { + blobs: Mutex>>, +} + +impl InMemoryBlobStore { + fn new() -> Self { + Self { + blobs: Mutex::new(HashMap::new()), + } + } +} + +impl AtomicBlobStore for InMemoryBlobStore { + fn read( + &self, + path: String, + ) -> Result>, walletkit_core::storage::StorageError> { + let guard = self.blobs.lock().map_err(|_| { + walletkit_core::storage::StorageError::BlobStore( + "mutex poisoned".to_string(), + ) + })?; + Ok(guard.get(&path).cloned()) + } + + fn write_atomic( + &self, + path: String, + bytes: Vec, + ) -> Result<(), walletkit_core::storage::StorageError> { + self.blobs + .lock() + .map_err(|_| { + walletkit_core::storage::StorageError::BlobStore( + "mutex poisoned".to_string(), + ) + })? + .insert(path, bytes); + Ok(()) + } + + fn delete( + &self, + path: String, + ) -> Result<(), walletkit_core::storage::StorageError> { + self.blobs + .lock() + .map_err(|_| { + walletkit_core::storage::StorageError::BlobStore( + "mutex poisoned".to_string(), + ) + })? + .remove(&path); + Ok(()) + } +} + +struct InMemoryStorageProvider { + keystore: Arc, + blob_store: Arc, + paths: Arc, +} + +impl InMemoryStorageProvider { + fn new(root: impl AsRef) -> Self { + Self { + keystore: Arc::new(InMemoryKeystore::new()), + blob_store: Arc::new(InMemoryBlobStore::new()), + paths: Arc::new(StoragePaths::new(root)), + } + } +} + +impl StorageProvider for InMemoryStorageProvider { + fn keystore(&self) -> Arc { + self.keystore.clone() + } + + fn blob_store(&self) -> Arc { + self.blob_store.clone() + } + + fn paths(&self) -> Arc { + Arc::clone(&self.paths) + } +} + +fn temp_root() -> PathBuf { + let mut path = std::env::temp_dir(); + path.push(format!("walletkit-storage-{}", Uuid::new_v4())); + path +} + +fn cleanup_storage(root: &Path) { + let paths = StoragePaths::new(root); + let vault = paths.vault_db_path(); + let cache = paths.cache_db_path(); + let lock = paths.lock_path(); + let _ = fs::remove_file(&vault); + let _ = fs::remove_file(vault.with_extension("sqlite-wal")); + let _ = fs::remove_file(vault.with_extension("sqlite-shm")); + let _ = fs::remove_file(&cache); + let _ = fs::remove_file(cache.with_extension("sqlite-wal")); + let _ = fs::remove_file(cache.with_extension("sqlite-shm")); + let _ = fs::remove_file(lock); + let _ = fs::remove_dir_all(paths.worldid_dir()); + let _ = fs::remove_dir_all(paths.root()); +} + +#[test] +fn test_storage_flow_end_to_end() { + let root = temp_root(); + let provider = InMemoryStorageProvider::new(&root); + let mut store = CredentialStore::from_provider(&provider).expect("store"); + + store.init(42, 100).expect("init"); + + let credential_id = CredentialStorage::store_credential( + &mut store, + 7, + [0x11u8; 32], + 1_700_000_000, + 1_800_000_000, + vec![1, 2, 3], + Some(vec![4, 5, 6]), + 100, + ) + .expect("store credential"); + + let records = CredentialStorage::list_credentials(&store, None, 101) + .expect("list credentials"); + assert_eq!(records.len(), 1); + let record = &records[0]; + assert_eq!(record.credential_id, credential_id); + assert_eq!(record.issuer_schema_id, 7); + assert_eq!(record.expires_at, 1_800_000_000); + + let root_bytes = [0xAAu8; 32]; + CredentialStorage::merkle_cache_put(&mut store, 1, root_bytes, vec![9, 9], 100, 10) + .expect("cache put"); + let valid_before = 105; + let hit = CredentialStorage::merkle_cache_get(&store, 1, root_bytes, valid_before) + .expect("cache get"); + assert_eq!(hit, Some(vec![9, 9])); + let miss = CredentialStorage::merkle_cache_get(&store, 1, root_bytes, 111) + .expect("cache get"); + assert!(miss.is_none()); + + let request_id = [0xABu8; 32]; + let nullifier = [0xCDu8; 32]; + let fresh = CredentialStorage::begin_replay_guard( + &mut store, + request_id, + nullifier, + vec![1, 2], + 200, + 50, + ) + .expect("disclose"); + assert_eq!( + fresh, + ReplayGuardResult { + kind: ReplayGuardKind::Fresh, + bytes: vec![1, 2], + } + ); + let cached = CredentialStorage::replay_guard_get(&store, request_id, 210) + .expect("disclosure lookup"); + assert_eq!(cached, Some(vec![1, 2])); + let replay = CredentialStorage::begin_replay_guard( + &mut store, + request_id, + nullifier, + vec![9, 9], + 201, + 50, + ) + .expect("replay"); + assert_eq!( + replay, + ReplayGuardResult { + kind: ReplayGuardKind::Replay, + bytes: vec![1, 2], + } + ); + + cleanup_storage(&root); +} diff --git a/walletkit-core/tests/solidity.rs b/walletkit-core/tests/solidity.rs index 6a4afccff..2e54c37fb 100644 --- a/walletkit-core/tests/solidity.rs +++ b/walletkit-core/tests/solidity.rs @@ -1,15 +1,19 @@ +#[cfg(all(feature = "legacy-nullifiers", feature = "common-apps"))] +use alloy::primitives::Address; use alloy::{ node_bindings::AnvilInstance, - primitives::{address, Address, U256}, + primitives::{address, U256}, providers::{ext::AnvilApi, ProviderBuilder, WalletProvider}, signers::local::PrivateKeySigner, sol, sol_types::SolValue, }; +#[cfg(all(feature = "legacy-nullifiers", feature = "common-apps"))] use chrono::{Days, Utc}; +#[cfg(all(feature = "legacy-nullifiers", feature = "common-apps"))] +use walletkit_core::common_apps::AddressBook; use walletkit_core::{ - common_apps::AddressBook, proof::ProofContext, world_id::WorldId, CredentialType, - Environment, + proof::ProofContext, world_id::WorldId, CredentialType, Environment, }; sol!( @@ -64,6 +68,7 @@ fn setup_anvil() -> AnvilInstance { anvil } +#[cfg(all(feature = "legacy-nullifiers", feature = "common-apps"))] sol!( /// The World ID Address Book allows verifying wallet addresses using a World ID for a period of time. /// @@ -85,6 +90,7 @@ sol!( } ); +#[cfg(all(feature = "legacy-nullifiers", feature = "common-apps"))] #[tokio::test] async fn test_address_book_proof_verification_on_chain() { // set up a World Chain Sepolia fork with the `WorldIdAddressBook` contract.