From f6a0c32b9d8a6b7b353a5e91693008b870b68bc9 Mon Sep 17 00:00:00 2001 From: CleverWild Date: Fri, 10 Apr 2026 00:42:43 +0200 Subject: [PATCH 1/2] feat: rustc and clippy linting --- AGENTS.md | 21 + CLAUDE.md | 21 + server/Cargo.lock | 678 ++++++++++++------ server/Cargo.toml | 197 ++++- server/clippy.toml | 19 + server/crates/arbiter-client/src/auth.rs | 26 +- .../arbiter-client/src/bin/test_connect.rs | 10 +- server/crates/arbiter-client/src/client.rs | 31 +- server/crates/arbiter-client/src/lib.rs | 2 +- server/crates/arbiter-client/src/storage.rs | 16 +- server/crates/arbiter-client/src/transport.rs | 15 +- .../crates/arbiter-client/src/wallets/evm.rs | 12 +- server/crates/arbiter-crypto/src/authn/v1.rs | 1 + server/crates/arbiter-crypto/src/hashing.rs | 2 +- server/crates/arbiter-crypto/src/safecell.rs | 2 +- server/crates/arbiter-macros/src/hashable.rs | 2 +- server/crates/arbiter-macros/src/utils.rs | 25 +- server/crates/arbiter-proto/src/url.rs | 2 +- .../arbiter-server/src/actors/bootstrap.rs | 29 +- .../arbiter-server/src/actors/client/auth.rs | 159 ++-- .../arbiter-server/src/actors/client/mod.rs | 6 +- .../src/actors/client/session.rs | 22 +- .../arbiter-server/src/actors/evm/mod.rs | 38 +- .../client_connect_approval.rs | 7 +- .../src/actors/flow_coordinator/mod.rs | 2 +- .../src/actors/keyholder/mod.rs | 140 ++-- .../crates/arbiter-server/src/actors/mod.rs | 10 +- .../src/actors/user_agent/auth.rs | 4 +- .../src/actors/user_agent/auth/state.rs | 68 +- .../src/actors/user_agent/mod.rs | 2 +- .../src/actors/user_agent/session.rs | 28 +- .../actors/user_agent/session/connection.rs | 150 ++-- .../src/actors/user_agent/session/state.rs | 2 - .../crates/arbiter-server/src/context/mod.rs | 10 +- .../crates/arbiter-server/src/context/tls.rs | 31 +- .../src/crypto/encryption/v1.rs | 32 +- .../arbiter-server/src/crypto/integrity/v1.rs | 49 +- .../crates/arbiter-server/src/crypto/mod.rs | 27 +- server/crates/arbiter-server/src/db/mod.rs | 26 +- server/crates/arbiter-server/src/db/models.rs | 70 +- server/crates/arbiter-server/src/evm/abi.rs | 2 +- server/crates/arbiter-server/src/evm/mod.rs | 34 +- .../crates/arbiter-server/src/evm/policies.rs | 13 +- .../src/evm/policies/ether_transfer/mod.rs | 37 +- .../src/evm/policies/ether_transfer/tests.rs | 10 +- .../src/evm/policies/token_transfers/mod.rs | 30 +- .../src/evm/policies/token_transfers/tests.rs | 16 +- .../arbiter-server/src/evm/safe_signer.rs | 3 +- server/crates/arbiter-server/src/evm/utils.rs | 2 +- .../crates/arbiter-server/src/grpc/client.rs | 3 +- .../arbiter-server/src/grpc/client/auth.rs | 55 +- .../arbiter-server/src/grpc/client/evm.rs | 2 +- .../arbiter-server/src/grpc/client/vault.rs | 8 +- .../src/grpc/common/outbound.rs | 30 +- .../src/grpc/request_tracker.rs | 2 +- .../src/grpc/user_agent/auth.rs | 4 +- .../arbiter-server/src/grpc/user_agent/evm.rs | 6 +- .../src/grpc/user_agent/inbound.rs | 15 +- .../src/grpc/user_agent/outbound.rs | 6 +- .../src/grpc/user_agent/sdk_client.rs | 19 +- .../src/grpc/user_agent/vault.rs | 6 +- server/crates/arbiter-server/src/lib.rs | 3 +- .../arbiter-server/tests/client/auth.rs | 67 +- .../crates/arbiter-server/tests/common/mod.rs | 5 +- .../tests/keyholder/concurrency.rs | 4 +- .../tests/keyholder/lifecycle.rs | 26 +- .../arbiter-server/tests/keyholder/storage.rs | 16 +- .../arbiter-server/tests/user_agent/auth.rs | 44 +- .../arbiter-server/tests/user_agent/unseal.rs | 8 +- 69 files changed, 1491 insertions(+), 979 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fb2d230..01c57ad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -100,6 +100,27 @@ diesel migration generate --migration-dir crates/arbiter-server/migration diesel migration run --migration-dir crates/arbiter-server/migrations ``` +### Code Conventions + +**`#[must_use]` Attribute:** +Apply the `#[must_use]` attribute to return types of functions where the return value is critical and should not be accidentally ignored. This is commonly used for: + +- Methods that return `bool` indicating success/failure or validation state +- Any function where ignoring the return value indicates a logic error + +Do not apply `#[must_use]` redundantly to items (types or functions) that are already annotated with `#[must_use]`. + +Example: + +```rust +#[must_use] +pub fn verify(&self, nonce: i32, context: &[u8], signature: &Signature) -> bool { + // verification logic +} +``` + +This forces callers to either use the return value or explicitly ignore it with `let _ = ...;`, preventing silent failures. + ## User Agent (Flutter + Rinf at `useragent/`) The Flutter app uses [Rinf](https://rinf.cunarist.org) to call Rust code. The Rust logic lives in `useragent/native/hub/` as a separate crate that uses `arbiter-useragent` for the gRPC client. diff --git a/CLAUDE.md b/CLAUDE.md index c3c3357..00b453d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -100,6 +100,27 @@ diesel migration generate --migration-dir crates/arbiter-server/migration diesel migration run --migration-dir crates/arbiter-server/migrations ``` +### Code Conventions + +**`#[must_use]` Attribute:** +Apply the `#[must_use]` attribute to return types of functions where the return value is critical and should not be accidentally ignored. This is commonly used for: + +- Methods that return `bool` indicating success/failure or validation state +- Any function where ignoring the return value indicates a logic error + +Do not apply `#[must_use]` redundantly to items (types or functions) that are already annotated with `#[must_use]`. + +Example: + +```rust +#[must_use] +pub fn verify(&self, nonce: i32, context: &[u8], signature: &Signature) -> bool { + // verification logic +} +``` + +This forces callers to either use the return value or explicitly ignore it with `let _ = ...;`, preventing silent failures. + ## User Agent (Flutter + Rinf at `useragent/`) The Flutter app uses [Rinf](https://rinf.cunarist.org) to call Rust code. The Rust logic lives in `useragent/native/hub/` as a separate crate that uses `arbiter-useragent` for the gRPC client. diff --git a/server/Cargo.lock b/server/Cargo.lock index ce49a42..7d49415 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -44,9 +44,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4973038846323e4e69a433916522195dce2947770076c03078fc21c80ea0f1c4" +checksum = "50ab0cd8afe573d1f7dc2353698a51b1f93aec362c8211e28cfd3948c6adba39" dependencies = [ "alloy-consensus", "alloy-contract", @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.2.32" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9247f0a399ef71aeb68f497b2b8fb348014f742b50d3b83b1e00dfe1b7d64b3d" +checksum = "f4e9e31d834fe25fe991b8884e4b9f0e59db4a97d86e05d1464d6899c013cd62" dependencies = [ "alloy-primitives", "num_enum", @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c0dc44157867da82c469c13186015b86abef209bf0e41625e4b68bac61d728" +checksum = "7f16daaf7e1f95f62c6c3bf8a3fc3d78b08ae9777810c0bb5e94966c7cd57ef0" dependencies = [ "alloy-eips", "alloy-primitives", @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4cdb42df3871cd6b346d6a938ec2ba69a9a0f49d1f82714bc5c48349268434" +checksum = "118998d9015332ab1b4720ae1f1e3009491966a0349938a1f43ff45a8a4c6299" dependencies = [ "alloy-consensus", "alloy-eips", @@ -119,9 +119,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca63b7125a981415898ffe2a2a696c83696c9c6bdb1671c8a912946bbd8e49e7" +checksum = "7ac9e0c34dc6bce643b182049cdfcca1b8ce7d9c260cbdd561f511873b7e26cd" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -137,6 +137,7 @@ dependencies = [ "futures-util", "serde_json", "thiserror 2.0.18", + "tracing", ] [[package]] @@ -220,9 +221,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f7ef09f21bd1e9cb8a686f168cb4a206646804567f0889eadb8dcc4c9288c8" +checksum = "e6ef28c9fdad22d4eec52d894f5f2673a0895f1e5ef196734568e68c0f6caca8" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -239,14 +240,13 @@ dependencies = [ "serde", "serde_with", "sha2 0.10.9", - "thiserror 2.0.18", ] [[package]] name = "alloy-genesis" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9cf3b99f46615fbf7dc1add0c96553abb7bf88fc9ec70dfbe7ad0b47ba7fe8" +checksum = "bbf9480307b09d22876efb67d30cadd9013134c21f3a17ec9f93fd7536d38024" dependencies = [ "alloy-eips", "alloy-primitives", @@ -271,9 +271,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff42cd777eea61f370c0b10f2648a1c81e0b783066cd7269228aa993afd487f7" +checksum = "422d110f1c40f1f8d0e5562b0b649c35f345fccb7093d9f02729943dcd1eef71" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cbca04f9b410fdc51aaaf88433cbac761213905a65fe832058bcf6690585762" +checksum = "7197a66d94c4de1591cdc16a9bcea5f8cccd0da81b865b49aef97b1b4016e0fa" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -312,9 +312,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d6d15e069a8b11f56bef2eccbad2a873c6dd4d4c81d04dda29710f5ea52f04" +checksum = "eb82711d59a43fdfd79727c99f270b974c784ec4eb5728a0d0d22f26716c87ef" dependencies = [ "alloy-consensus", "alloy-eips", @@ -336,7 +336,7 @@ dependencies = [ "derive_more", "foldhash 0.2.0", "hashbrown 0.16.1", - "indexmap 2.13.0", + "indexmap 2.13.1", "itoa", "k256", "keccak-asm", @@ -352,9 +352,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d181c8cc7cf4805d7e589bf4074d56d55064fa1a979f005a45a62b047616d870" +checksum = "bf6b18b929ef1d078b834c3631e9c925177f3b23ddc6fa08a722d13047205876" dependencies = [ "alloy-chains", "alloy-consensus", @@ -391,9 +391,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93e50f64a77ad9c5470bf2ad0ca02f228da70c792a8f06634801e202579f35e" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -402,9 +402,9 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8849c74c9ca0f5a03da1c865e3eb6f768df816e67dd3721a398a8a7e398011" +checksum = "f36834a5c0a2fa56e171bf256c34d70fca07d0c0031583edea1c4946b7889c9e" dependencies = [ "proc-macro2", "quote", @@ -413,9 +413,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2792758a93ae32a32e9047c843d536e1448044f78422d71bf7d7c05149e103f" +checksum = "94fcc9604042ca80bd37aa5e232ea1cd851f337e31e2babbbb345bc0b1c30de3" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -436,9 +436,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bdcbf9dfd5eea8bfeb078b1d906da8cd3a39c4d4dbe7a628025648e323611f6" +checksum = "4faad925d3a669ffc15f43b3deec7fbdf2adeb28a4d6f9cf4bc661698c0f8f4b" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -448,9 +448,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd720b63f82b457610f2eaaf1f32edf44efffe03ae25d537632e7d23e7929e1a" +checksum = "3823026d1ed239a40f12364fac50726c8daf1b6ab8077a97212c5123910429ed" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -459,9 +459,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2dc411f13092f237d2bf6918caf80977fc2f51485f9b90cb2a2f956912c8c9" +checksum = "59c095f92c4e1ff4981d89e9aa02d5f98c762a1980ab66bec49c44be11349da2" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -471,7 +471,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", - "itertools 0.13.0", + "itertools 0.14.0", "serde", "serde_json", "serde_with", @@ -480,9 +480,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ce1e0dbf7720eee747700e300c99aac01b1a95bb93f493a01e78ee28bb1a37" +checksum = "11ece63b89294b8614ab3f483560c08d016930f842bf36da56bf0b764a15c11e" dependencies = [ "alloy-primitives", "serde", @@ -491,9 +491,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2425c6f314522c78e8198979c8cbf6769362be4da381d4152ea8eefce383535d" +checksum = "43f447aefab0f1c0649f71edc33f590992d4e122bc35fb9cdbbf67d4421ace85" dependencies = [ "alloy-primitives", "async-trait", @@ -506,9 +506,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ecb71ee53d8d9c3fa7bac17542c8116ebc7a9726c91b1bf333ec3d04f5a789" +checksum = "f721f4bf2e4812e5505aaf5de16ef3065a8e26b9139ac885862d00b5a55a659a" dependencies = [ "alloy-consensus", "alloy-network", @@ -544,7 +544,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.13.0", + "indexmap 2.13.1", "proc-macro-error2", "proc-macro2", "quote", @@ -595,9 +595,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa186e560d523d196580c48bf00f1bf62e63041f28ecf276acc22f8b27bb9f53" +checksum = "8098f965442a9feb620965ba4b4be5e2b320f4ec5a3fff6bfa9e1ff7ef42bed1" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -618,13 +618,13 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa501ad58dd20acddbfebc65b52e60f05ebf97c52fa40d1b35e91f5e2da0ad0e" +checksum = "e8597d36d546e1dab822345ad563243ec3920e199322cb554ce56c8ef1a1e2e7" dependencies = [ "alloy-json-rpc", "alloy-transport", - "itertools 0.13.0", + "itertools 0.14.0", "reqwest", "serde_json", "tower", @@ -650,11 +650,11 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa0c53e8c1e1ef4d01066b01c737fb62fc9397ab52c6e7bb5669f97d281b9bc" +checksum = "d69722eddcdf1ce096c3ab66cf8116999363f734eb36fe94a148f4f71c85da84" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.117", @@ -1119,9 +1119,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.39.0" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" +checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" dependencies = [ "cc", "cmake", @@ -1382,9 +1382,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.57" +version = "1.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" dependencies = [ "find-msvc-tools", "jobserver", @@ -1392,6 +1392,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.4" @@ -1466,9 +1472,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] @@ -1480,15 +1486,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" [[package]] -name = "console" -version = "0.15.11" +name = "combine" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "once_cell", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1544,6 +1559,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1717,7 +1742,6 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "serde", "strsim", "syn 2.0.117", ] @@ -1731,6 +1755,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "serde", "strsim", "syn 2.0.117", ] @@ -2027,7 +2052,7 @@ dependencies = [ "curve25519-dalek 5.0.0-pre.6", "ed25519", "rand_core 0.10.0", - "sha2 0.11.0-rc.5", + "sha2 0.11.0", "subtle", "zeroize", ] @@ -2132,9 +2157,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fastrlp" @@ -2175,7 +2200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb42427514b063d97ce21d5199f36c0c307d981434a6be32582bc79fe5bd2303" dependencies = [ "expander", - "indexmap 2.13.0", + "indexmap 2.13.1", "proc-macro-crate", "proc-macro2", "quote", @@ -2483,7 +2508,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.13.0", + "indexmap 2.13.1", "slab", "tokio", "tokio-util", @@ -2607,9 +2632,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8655f91cd07f2b9d0c24137bd650fe69617773435ee5ec83022377777ce65ef1" +checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" dependencies = [ "typenum", "zeroize", @@ -2617,9 +2642,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -2632,7 +2657,6 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -2652,7 +2676,6 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", ] [[package]] @@ -2717,12 +2740,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -2730,9 +2754,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -2743,9 +2767,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -2757,15 +2781,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -2777,15 +2801,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -2862,9 +2886,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -2883,9 +2907,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.46.3" +version = "1.47.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4" +checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e" dependencies = [ "console", "once_cell", @@ -2901,9 +2925,9 @@ checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" dependencies = [ "memchr", "serde", @@ -2933,12 +2957,65 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -2951,10 +3028,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -2976,9 +3055,8 @@ dependencies = [ [[package]] name = "kameo" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c4af7638c67029fd6821d02813c3913c803784648725d4df4082c9b91d7cbb1" +version = "0.20.0" +source = "git+https://github.com/tqwewe/kameo#49a18fbbd62cff35ecf856b6a97a21939efb82b5" dependencies = [ "downcast-rs", "dyn-clone", @@ -2991,9 +3069,8 @@ dependencies = [ [[package]] name = "kameo_macros" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13c324e2d8c8e126e63e66087448b4267e263e6cb8770c56d10a9d0d279d9e2" +version = "0.20.0" +source = "git+https://github.com/tqwewe/kameo#49a18fbbd62cff35ecf856b6a97a21939efb82b5" dependencies = [ "heck", "proc-macro2", @@ -3022,9 +3099,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b646a74e746cd25045aa0fd42f4f7f78aa6d119380182c7e63a5593c4ab8df6f" +checksum = "fa468878266ad91431012b3e5ef1bf9b170eab22883503a318d46857afa4579a" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -3044,9 +3121,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.183" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "libm" @@ -3072,9 +3149,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "lock_api" @@ -3224,9 +3301,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -3304,9 +3381,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-integer" @@ -3402,6 +3479,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "owo-colors" version = "4.3.0" @@ -3510,7 +3593,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", "hashbrown 0.15.5", - "indexmap 2.13.0", + "indexmap 2.13.1", ] [[package]] @@ -3590,9 +3673,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -3733,7 +3816,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" dependencies = [ "heck", - "itertools 0.13.0", + "itertools 0.14.0", "log", "multimap", "petgraph", @@ -3754,7 +3837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -3836,9 +3919,9 @@ checksum = "95067976aca6421a523e491fce939a3e65249bac4b977adee0ee9771568e8aa3" [[package]] name = "pulldown-cmark" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14104c5a24d9bcf7eb2c24753e0f49fe14555d8bd565ea3d38e4b4303267259d" +checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad" dependencies = [ "bitflags", "memchr", @@ -3886,6 +3969,7 @@ version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ + "aws-lc-rs", "bytes", "getrandom 0.3.4", "lru-slab", @@ -4120,9 +4204,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.28" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ "base64", "bytes", @@ -4140,9 +4224,9 @@ dependencies = [ "quinn", "rustls", "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", - "serde_urlencoded", "sync_wrapper", "tokio", "tokio-rustls", @@ -4153,7 +4237,6 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", ] [[package]] @@ -4283,9 +4366,9 @@ checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc-hex" @@ -4308,7 +4391,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.27", + "semver 1.0.28", ] [[package]] @@ -4342,13 +4425,24 @@ dependencies = [ "aws-lc-rs", "log", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -4359,6 +4453,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.10" @@ -4390,10 +4511,22 @@ dependencies = [ ] [[package]] -name = "ryu" -version = "1.0.23" +name = "same-file" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] [[package]] name = "schemars" @@ -4479,6 +4612,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.11.0" @@ -4490,9 +4646,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "semver-parser" @@ -4548,25 +4704,13 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_with" version = "3.18.0" @@ -4577,7 +4721,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.13.1", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -4621,12 +4765,12 @@ dependencies = [ [[package]] name = "sha2" -version = "0.11.0-rc.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", "digest 0.11.2", ] @@ -4652,9 +4796,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b31139435f327c93c6038ed350ae4588e2c70a13d50599509fee6349967ba35a" +checksum = "59cbb88c189d6352cc8ae96a39d19c7ecad8f7330b29461187f2587fdc2988d5" dependencies = [ "cc", "cfg-if", @@ -4707,9 +4851,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "similar" @@ -4963,12 +5107,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5093,9 +5237,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -5118,9 +5262,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" dependencies = [ "bytes", "libc", @@ -5136,9 +5280,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -5204,32 +5348,32 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.0.1+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.5+spec-1.1.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 1.0.1+spec-1.1.0", + "indexmap 2.13.1", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.0", + "winnow 1.0.1", ] [[package]] name = "toml_parser" -version = "1.0.10+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.0", + "winnow 1.0.1", ] [[package]] @@ -5311,7 +5455,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.13.0", + "indexmap 2.13.1", "pin-project-lite", "slab", "sync_wrapper", @@ -5469,9 +5613,9 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" @@ -5534,9 +5678,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "js-sys", "wasm-bindgen", @@ -5569,6 +5713,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -5604,9 +5758,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -5617,23 +5771,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5641,9 +5791,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -5654,9 +5804,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -5678,7 +5828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.13.1", "wasm-encoder", "wasmparser", ] @@ -5691,8 +5841,8 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags", "hashbrown 0.15.5", - "indexmap 2.13.0", - "semver 1.0.27", + "indexmap 2.13.1", + "semver 1.0.28", ] [[package]] @@ -5711,9 +5861,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.91" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" dependencies = [ "js-sys", "wasm-bindgen", @@ -5730,10 +5880,10 @@ dependencies = [ ] [[package]] -name = "webpki-roots" +name = "webpki-root-certs" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ "rustls-pki-types", ] @@ -5754,6 +5904,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -5821,18 +5980,18 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.42.2", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] @@ -5855,6 +6014,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -5888,6 +6062,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5900,6 +6080,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -5912,6 +6098,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5936,6 +6128,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -5948,6 +6146,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -5960,6 +6164,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -5972,6 +6182,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -5995,9 +6211,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" dependencies = [ "memchr", ] @@ -6030,7 +6246,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.13.0", + "indexmap 2.13.1", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -6061,7 +6277,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags", - "indexmap 2.13.0", + "indexmap 2.13.1", "log", "serde", "serde_derive", @@ -6080,9 +6296,9 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap 2.13.1", "log", - "semver 1.0.27", + "semver 1.0.28", "serde", "serde_derive", "serde_json", @@ -6092,9 +6308,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wyz" @@ -6146,9 +6362,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -6157,9 +6373,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -6169,18 +6385,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -6189,18 +6405,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -6230,9 +6446,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -6241,9 +6457,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -6252,9 +6468,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", diff --git a/server/Cargo.toml b/server/Cargo.toml index f13b0c2..a58b31a 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -4,47 +4,166 @@ members = [ ] resolver = "3" -[workspace.lints.clippy] -disallowed-methods = "deny" - [workspace.dependencies] -tonic = { version = "0.14.5", features = [ - "deflate", - "gzip", - "tls-connect-info", - "zstd", -] } -tracing = "0.1.44" -tokio = { version = "1.50.0", features = ["full"] } -ed25519-dalek = { version = "3.0.0-pre.6", features = ["rand_core"] } -chrono = { version = "0.4.44", features = ["serde"] } -rand = "0.10.0" -rustls = { version = "0.23.37", features = ["aws-lc-rs", "logging", "prefer-post-quantum", "std"], default-features = false } -smlang = "0.8.0" -thiserror = "2.0.18" -async-trait = "0.1.89" -futures = "0.3.32" -tokio-stream = { version = "0.1.18", features = ["full"] } -kameo = "0.19.2" -prost-types = { version = "0.14.3", features = ["chrono"] } -x25519-dalek = { version = "2.0.1", features = ["getrandom"] } -rstest = "0.26.1" -rustls-pki-types = "1.14.0" alloy = "1.7.3" -rcgen = { version = "0.14.7", features = [ - "aws_lc_rs", - "pem", - "x509-parser", - "zeroize", -], default-features = false } -k256 = { version = "0.13.4", features = ["ecdsa", "pkcs8"] } -rsa = { version = "0.9", features = ["sha2"] } -sha2 = "0.10" -spki = "0.7" -prost = "0.14.3" -miette = { version = "7.6.0", features = ["fancy", "serde"] } -mutants = "0.0.4" -ml-dsa = { version = "0.1.0-rc.8", features = ["zeroize"] } +async-trait = "0.1.89" base64 = "0.22.1" +chrono = { version = "0.4.44", features = ["serde"] } +ed25519-dalek = { version = "3.0.0-pre.6", features = ["rand_core"] } +futures = "0.3.32" hmac = "0.12.1" +k256 = { version = "0.13.4", features = ["ecdsa", "pkcs8"] } +kameo = { version = "0.20.0", git = "https://github.com/tqwewe/kameo" } # hold this until new patch version is released +miette = { version = "7.6.0", features = ["fancy", "serde"] } +ml-dsa = { version = "0.1.0-rc.8", features = ["zeroize"] } +mutants = "0.0.4" +prost = "0.14.3" +prost-types = { version = "0.14.3", features = ["chrono"] } +rand = "0.10.0" +rcgen = { version = "0.14.7", features = [ "aws_lc_rs", "pem", "x509-parser", "zeroize" ], default-features = false } +rsa = { version = "0.9", features = ["sha2"] } +rstest = "0.26.1" +rustls = { version = "0.23.37", features = ["aws-lc-rs", "logging", "prefer-post-quantum", "std"], default-features = false } +rustls-pki-types = "1.14.0" +sha2 = "0.10" +smlang = "0.8.0" +spki = "0.7" +thiserror = "2.0.18" +tokio = { version = "1.50.0", features = ["full"] } +tokio-stream = { version = "0.1.18", features = ["full"] } +tonic = { version = "0.14.5", features = [ "deflate", "gzip", "tls-connect-info", "zstd" ] } +tracing = "0.1.44" +x25519-dalek = { version = "2.0.1", features = ["getrandom"] } + +[workspace.lints.rust] +missing_unsafe_on_extern = "deny" +unsafe_attr_outside_unsafe = "deny" +unsafe_op_in_unsafe_fn = "deny" +unstable_features = "deny" + +deprecated_safe_2024 = "warn" +ffi_unwind_calls = "warn" +fuzzy_provenance_casts = "warn" +linker_messages = "warn" +lossy_provenance_casts = "warn" +must_not_suspend = "warn" + +elided_lifetimes_in_paths = "warn" +explicit_outlives_requirements = "warn" +impl-trait-overcaptures = "warn" +impl-trait-redundant-captures = "warn" +redundant_lifetimes = "warn" +single_use_lifetimes = "warn" +unused_lifetimes = "warn" + +macro_use_extern_crate = "warn" +redundant_imports = "warn" +unqualified_local_imports = "warn" +unused_import_braces = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +unit_bindings = "warn" + +# missing_docs = "warn" # ENABLE BY THE FIRST MAJOR VERSION!! +unnameable_types = "warn" +variant_size_differences = "warn" + +[workspace.lints.clippy] +derive_partial_eq_without_eq = "allow" +future_not_send = "allow" +inconsistent_struct_constructor = "allow" +inline_always = "allow" +missing_errors_doc = "allow" +missing_fields_in_debug = "allow" +missing_panics_doc = "allow" +must_use_candidate = "allow" +needless_pass_by_ref_mut = "allow" +pub_underscore_fields = "allow" +redundant_pub_crate = "allow" +uninhabited_references = "allow" # safe with unsafe_code = "forbid" and standard uninhabited pattern (match *self {}) + +# restriction lints +alloc_instead_of_core = "warn" +allow_attributes_without_reason = "warn" +as_conversions = "warn" +assertions_on_result_states = "warn" +cfg_not_test = "warn" +clone_on_ref_ptr = "warn" +cognitive_complexity = "warn" +create_dir = "warn" +dbg_macro = "warn" +decimal_literal_representation = "warn" +default_union_representation = "warn" +deref_by_slicing = "warn" +disallowed_script_idents = "warn" +doc_include_without_cfg = "warn" +empty_drop = "warn" +empty_enum_variants_with_brackets = "warn" +empty_structs_with_brackets = "warn" +error_impl_error = "warn" +exit = "warn" +filetype_is_file = "warn" +float_arithmetic = "warn" +float_cmp_const = "warn" +fn_to_numeric_cast_any = "warn" +get_unwrap = "warn" +if_then_some_else_none = "warn" +indexing_slicing = "warn" +infinite_loop = "warn" +inline_asm_x86_att_syntax = "warn" +inline_asm_x86_intel_syntax = "warn" +integer_division = "warn" +large_include_file = "warn" +lossy_float_literal = "warn" +map_with_unused_argument_over_ranges = "warn" +mem_forget = "warn" +missing_assert_message = "warn" +mixed_read_write_in_expression = "warn" +modulo_arithmetic = "warn" +multiple_unsafe_ops_per_block = "warn" +mutex_atomic = "warn" +mutex_integer = "warn" +needless_raw_strings = "warn" +non_ascii_literal = "warn" +non_zero_suggestions = "warn" +panic = "warn" +panic_in_result_fn = "warn" +pathbuf_init_then_push = "warn" +pointer_format = "warn" +precedence_bits = "warn" +pub_without_shorthand = "warn" +rc_buffer = "warn" +rc_mutex = "warn" +redundant_test_prefix = "warn" +redundant_type_annotations = "warn" +ref_patterns = "warn" +renamed_function_params = "warn" +rest_pat_in_fully_bound_structs = "warn" +return_and_then = "warn" +semicolon_inside_block = "warn" +str_to_string = "warn" +string_add = "warn" +string_lit_chars_any = "warn" +string_slice = "warn" +suspicious_xor_used_as_pow = "warn" +try_err = "warn" +undocumented_unsafe_blocks = "warn" +uninlined_format_args = "warn" +unnecessary_safety_comment = "warn" +unnecessary_safety_doc = "warn" +unnecessary_self_imports = "warn" +unneeded_field_pattern = "warn" +unused_result_ok = "warn" +verbose_file_reads = "warn" +wildcard_dependencies = "warn" +# prod panicking macro. ENABLE BY THE FIRST MAJOR VERSION!! +# todo = "warn" +# unimplemented = "warn" + +disallowed_methods = "deny" + +cargo = { level = "warn", priority = -1 } +nursery = { level = "warn", priority = -1 } +pedantic = { level = "warn", priority = -1 } diff --git a/server/clippy.toml b/server/clippy.toml index d0fc168..bed3c74 100644 --- a/server/clippy.toml +++ b/server/clippy.toml @@ -7,3 +7,22 @@ disallowed-methods = [ { path = "rsa::traits::Decryptor::decrypt", reason = "RSA decryption is forbidden (RUSTSEC-2023-0071 Marvin Attack). This blocks decrypt() on rsa::{pkcs1v15,oaep}::DecryptingKey." }, { path = "rsa::traits::RandomizedDecryptor::decrypt_with_rng", reason = "RSA decryption is forbidden (RUSTSEC-2023-0071 Marvin Attack). This blocks decrypt_with_rng() on rsa::{pkcs1v15,oaep}::DecryptingKey." }, ] + +allow-indexing-slicing-in-tests = true +allow-panic-in-tests = true +check-inconsistent-struct-field-initializers = true +suppress-restriction-lint-in-const = true +allow-renamed-params-for = [ + "core::convert::From", + "core::convert::TryFrom", + "core::str::FromStr", + "kameo::actor::Actor", +] + +module-items-ordered-within-groupings = ["UPPER_SNAKE_CASE"] +source-item-ordering = ["enum"] +trait-assoc-item-kinds-order = [ + "const", + "type", + "fn", +] # community tested standard diff --git a/server/crates/arbiter-client/src/auth.rs b/server/crates/arbiter-client/src/auth.rs index e6068e5..534d1d0 100644 --- a/server/crates/arbiter-client/src/auth.rs +++ b/server/crates/arbiter-client/src/auth.rs @@ -23,20 +23,20 @@ use crate::{ #[derive(Debug, thiserror::Error)] pub enum AuthError { - #[error("Auth challenge was not returned by server")] - MissingAuthChallenge, - #[error("Client approval denied by User Agent")] ApprovalDenied, + #[error("Auth challenge was not returned by server")] + MissingAuthChallenge, + #[error("No User Agents online to approve client")] NoUserAgentsOnline, - #[error("Unexpected auth response payload")] - UnexpectedAuthResponse, - #[error("Signing key storage error")] Storage(#[from] StorageError), + + #[error("Unexpected auth response payload")] + UnexpectedAuthResponse, } fn map_auth_result(code: i32) -> AuthError { @@ -55,7 +55,7 @@ async fn send_auth_challenge_request( transport: &mut ClientTransport, metadata: ClientMetadata, key: &SigningKey, -) -> std::result::Result<(), AuthError> { +) -> Result<(), AuthError> { transport .send(ClientRequest { request_id: next_request_id(), @@ -76,7 +76,7 @@ async fn send_auth_challenge_request( async fn receive_auth_challenge( transport: &mut ClientTransport, -) -> std::result::Result { +) -> Result { let response = transport .recv() .await @@ -97,7 +97,7 @@ async fn send_auth_challenge_solution( transport: &mut ClientTransport, key: &SigningKey, challenge: AuthChallenge, -) -> std::result::Result<(), AuthError> { +) -> Result<(), AuthError> { let challenge_payload = format_challenge(challenge.nonce, &challenge.pubkey); let signature = key .sign_message(&challenge_payload, CLIENT_CONTEXT) @@ -117,9 +117,7 @@ async fn send_auth_challenge_solution( .map_err(|_| AuthError::UnexpectedAuthResponse) } -async fn receive_auth_confirmation( - transport: &mut ClientTransport, -) -> std::result::Result<(), AuthError> { +async fn receive_auth_confirmation(transport: &mut ClientTransport) -> Result<(), AuthError> { let response = transport .recv() .await @@ -140,11 +138,11 @@ async fn receive_auth_confirmation( } } -pub(crate) async fn authenticate( +pub async fn authenticate( transport: &mut ClientTransport, metadata: ClientMetadata, key: &SigningKey, -) -> std::result::Result<(), AuthError> { +) -> Result<(), AuthError> { send_auth_challenge_request(transport, metadata, key).await?; let challenge = receive_auth_challenge(transport).await?; send_auth_challenge_solution(transport, key, challenge).await?; diff --git a/server/crates/arbiter-client/src/bin/test_connect.rs b/server/crates/arbiter-client/src/bin/test_connect.rs index 311d333..3e9466a 100644 --- a/server/crates/arbiter-client/src/bin/test_connect.rs +++ b/server/crates/arbiter-client/src/bin/test_connect.rs @@ -29,16 +29,16 @@ async fn main() { } }; - println!("{:#?}", url); + println!("{url:#?}"); let metadata = ClientMetadata { - name: "arbiter-client test_connect".to_string(), - description: Some("Manual connection smoke test".to_string()), - version: Some(env!("CARGO_PKG_VERSION").to_string()), + name: "arbiter-client test_connect".to_owned(), + description: Some("Manual connection smoke test".to_owned()), + version: Some(env!("CARGO_PKG_VERSION").to_owned()), }; match ArbiterClient::connect(url, metadata).await { Ok(_) => println!("Connected and authenticated successfully."), - Err(err) => eprintln!("Failed to connect: {:#?}", err), + Err(err) => eprintln!("Failed to connect: {err:#?}"), } } diff --git a/server/crates/arbiter-client/src/client.rs b/server/crates/arbiter-client/src/client.rs index b540647..b291294 100644 --- a/server/crates/arbiter-client/src/client.rs +++ b/server/crates/arbiter-client/src/client.rs @@ -18,33 +18,39 @@ use crate::{ use crate::wallets::evm::ArbiterEvmWallet; #[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("gRPC error")] - Grpc(#[from] tonic::Status), +pub enum ArbiterClientError { + #[error("Authentication error")] + Authentication(#[from] AuthError), #[error("Could not establish connection")] Connection(#[from] tonic::transport::Error), - #[error("Invalid server URI")] - InvalidUri(#[from] http::uri::InvalidUri), + #[error("gRPC error")] + Grpc(#[from] tonic::Status), #[error("Invalid CA certificate")] InvalidCaCert(#[from] webpki::Error), - #[error("Authentication error")] - Authentication(#[from] AuthError), + #[error("Invalid server URI")] + InvalidUri(#[from] http::uri::InvalidUri), #[error("Storage error")] Storage(#[from] StorageError), } pub struct ArbiterClient { - #[allow(dead_code)] + #[expect( + dead_code, + reason = "transport will be used in future methods for sending requests and receiving responses" + )] transport: Arc>, } impl ArbiterClient { - pub async fn connect(url: ArbiterUrl, metadata: ClientMetadata) -> Result { + pub async fn connect( + url: ArbiterUrl, + metadata: ClientMetadata, + ) -> Result { let storage = FileSigningKeyStorage::from_default_location()?; Self::connect_with_storage(url, metadata, &storage).await } @@ -53,7 +59,7 @@ impl ArbiterClient { url: ArbiterUrl, metadata: ClientMetadata, storage: &S, - ) -> Result { + ) -> Result { let key = storage.load_or_create()?; Self::connect_with_key(url, metadata, key).await } @@ -62,7 +68,7 @@ impl ArbiterClient { url: ArbiterUrl, metadata: ClientMetadata, key: SigningKey, - ) -> Result { + ) -> Result { let anchor = webpki::anchor_from_trusted_cert(&url.ca_cert)?.to_owned(); let tls = ClientTlsConfig::new().trust_anchor(anchor); @@ -89,7 +95,8 @@ impl ArbiterClient { } #[cfg(feature = "evm")] - pub async fn evm_wallets(&self) -> Result, Error> { + #[expect(clippy::unused_async, reason = "false positive")] + pub async fn evm_wallets(&self) -> Result, ArbiterClientError> { todo!("fetch EVM wallet list from server") } } diff --git a/server/crates/arbiter-client/src/lib.rs b/server/crates/arbiter-client/src/lib.rs index 6e4516c..54071ab 100644 --- a/server/crates/arbiter-client/src/lib.rs +++ b/server/crates/arbiter-client/src/lib.rs @@ -5,7 +5,7 @@ mod transport; pub mod wallets; pub use auth::AuthError; -pub use client::{ArbiterClient, Error}; +pub use client::{ArbiterClient, ArbiterClientError}; pub use storage::{FileSigningKeyStorage, SigningKeyStorage, StorageError}; #[cfg(feature = "evm")] diff --git a/server/crates/arbiter-client/src/storage.rs b/server/crates/arbiter-client/src/storage.rs index 55d4a46..446ec18 100644 --- a/server/crates/arbiter-client/src/storage.rs +++ b/server/crates/arbiter-client/src/storage.rs @@ -4,15 +4,15 @@ use std::path::{Path, PathBuf}; #[derive(Debug, thiserror::Error)] pub enum StorageError { - #[error("I/O error")] - Io(#[from] std::io::Error), - #[error("Invalid signing key length in storage: expected {expected} bytes, got {actual} bytes")] InvalidKeyLength { expected: usize, actual: usize }, + + #[error("I/O error")] + Io(#[from] std::io::Error), } pub trait SigningKeyStorage { - fn load_or_create(&self) -> std::result::Result; + fn load_or_create(&self) -> Result; } #[derive(Debug, Clone)] @@ -27,11 +27,11 @@ impl FileSigningKeyStorage { Self { path: path.into() } } - pub fn from_default_location() -> std::result::Result { + pub fn from_default_location() -> Result { Ok(Self::new(home_path()?.join(Self::DEFAULT_FILE_NAME))) } - fn read_key(path: &Path) -> std::result::Result { + fn read_key(path: &Path) -> Result { let bytes = std::fs::read(path)?; let raw: [u8; 32] = bytes @@ -45,7 +45,7 @@ impl FileSigningKeyStorage { } impl SigningKeyStorage for FileSigningKeyStorage { - fn load_or_create(&self) -> std::result::Result { + fn load_or_create(&self) -> Result { if let Some(parent) = self.path.parent() { std::fs::create_dir_all(parent)?; } @@ -125,7 +125,7 @@ mod tests { assert_eq!(expected, 32); assert_eq!(actual, 31); } - other => panic!("unexpected error: {other:?}"), + other @ StorageError::Io(_) => panic!("unexpected error: {other:?}"), } std::fs::remove_file(path).expect("temp key file should be removable"); diff --git a/server/crates/arbiter-client/src/transport.rs b/server/crates/arbiter-client/src/transport.rs index 7332e89..4730932 100644 --- a/server/crates/arbiter-client/src/transport.rs +++ b/server/crates/arbiter-client/src/transport.rs @@ -2,15 +2,15 @@ use arbiter_proto::proto::client::{ClientRequest, ClientResponse}; use std::sync::atomic::{AtomicI32, Ordering}; use tokio::sync::mpsc; -pub(crate) const BUFFER_LENGTH: usize = 16; +pub const BUFFER_LENGTH: usize = 16; static NEXT_REQUEST_ID: AtomicI32 = AtomicI32::new(1); -pub(crate) fn next_request_id() -> i32 { +pub fn next_request_id() -> i32 { NEXT_REQUEST_ID.fetch_add(1, Ordering::Relaxed) } #[derive(Debug, thiserror::Error)] -pub(crate) enum ClientSignError { +pub enum ClientSignError { #[error("Transport channel closed")] ChannelClosed, @@ -18,7 +18,7 @@ pub(crate) enum ClientSignError { ConnectionClosed, } -pub(crate) struct ClientTransport { +pub struct ClientTransport { pub(crate) sender: mpsc::Sender, pub(crate) receiver: tonic::Streaming, } @@ -27,18 +27,17 @@ impl ClientTransport { pub(crate) async fn send( &mut self, request: ClientRequest, - ) -> std::result::Result<(), ClientSignError> { + ) -> Result<(), ClientSignError> { self.sender .send(request) .await .map_err(|_| ClientSignError::ChannelClosed) } - pub(crate) async fn recv(&mut self) -> std::result::Result { + pub(crate) async fn recv(&mut self) -> Result { match self.receiver.message().await { Ok(Some(resp)) => Ok(resp), - Ok(None) => Err(ClientSignError::ConnectionClosed), - Err(_) => Err(ClientSignError::ConnectionClosed), + Ok(None) | Err(_) => Err(ClientSignError::ConnectionClosed), } } } diff --git a/server/crates/arbiter-client/src/wallets/evm.rs b/server/crates/arbiter-client/src/wallets/evm.rs index 5c975c9..743f264 100644 --- a/server/crates/arbiter-client/src/wallets/evm.rs +++ b/server/crates/arbiter-client/src/wallets/evm.rs @@ -59,7 +59,11 @@ pub struct ArbiterEvmWallet { } impl ArbiterEvmWallet { - pub(crate) fn new(transport: Arc>, address: Address) -> Self { + #[expect( + dead_code, + reason = "new will be used in future methods for creating wallets with different parameters" + )] + pub(crate) const fn new(transport: Arc>, address: Address) -> Self { Self { transport, address, @@ -67,11 +71,12 @@ impl ArbiterEvmWallet { } } - pub fn address(&self) -> Address { + pub const fn address(&self) -> Address { self.address } - pub fn with_chain_id(mut self, chain_id: ChainId) -> Self { + #[must_use] + pub const fn with_chain_id(mut self, chain_id: ChainId) -> Self { self.chain_id = Some(chain_id); self } @@ -146,6 +151,7 @@ impl TxSigner for ArbiterEvmWallet { .recv() .await .map_err(|_| Error::other("failed to receive evm sign transaction response"))?; + drop(transport); if response.request_id != Some(request_id) { return Err(Error::other( diff --git a/server/crates/arbiter-crypto/src/authn/v1.rs b/server/crates/arbiter-crypto/src/authn/v1.rs index c33de49..87f0659 100644 --- a/server/crates/arbiter-crypto/src/authn/v1.rs +++ b/server/crates/arbiter-crypto/src/authn/v1.rs @@ -35,6 +35,7 @@ impl PublicKey { self.0.encode().0.to_vec() } + #[must_use] pub fn verify(&self, nonce: i32, context: &[u8], signature: &Signature) -> bool { self.0.verify_with_context( &format_challenge(nonce, &self.to_bytes()), diff --git a/server/crates/arbiter-crypto/src/hashing.rs b/server/crates/arbiter-crypto/src/hashing.rs index 48b54ee..4c89fed 100644 --- a/server/crates/arbiter-crypto/src/hashing.rs +++ b/server/crates/arbiter-crypto/src/hashing.rs @@ -49,7 +49,7 @@ impl Hashable for Vec { } } -impl Hashable for HashSet { +impl Hashable for HashSet { fn hash(&self, hasher: &mut H) { let ref_sorted = { let mut sorted = self.iter().collect::>(); diff --git a/server/crates/arbiter-crypto/src/safecell.rs b/server/crates/arbiter-crypto/src/safecell.rs index 80dc57e..3ab8fb8 100644 --- a/server/crates/arbiter-crypto/src/safecell.rs +++ b/server/crates/arbiter-crypto/src/safecell.rs @@ -29,7 +29,7 @@ pub trait SafeCellHandle { let mut cell = Self::new(T::default()); { let mut handle = cell.write(); - f(handle.deref_mut()); + f(&mut *handle); } cell } diff --git a/server/crates/arbiter-macros/src/hashable.rs b/server/crates/arbiter-macros/src/hashable.rs index 7473497..a647c0a 100644 --- a/server/crates/arbiter-macros/src/hashable.rs +++ b/server/crates/arbiter-macros/src/hashable.rs @@ -20,7 +20,7 @@ pub(crate) fn derive(input: &DeriveInput) -> TokenStream { } } -fn hashable_struct(input: &DeriveInput, struct_data: &syn::DataStruct) -> TokenStream { +fn hashable_struct(input: &DeriveInput, struct_data: &DataStruct) -> TokenStream { let ident = &input.ident; let hashable_trait = HASHABLE_TRAIT_PATH.to_path(); let hmac_digest = HMAC_DIGEST_PATH.to_path(); diff --git a/server/crates/arbiter-macros/src/utils.rs b/server/crates/arbiter-macros/src/utils.rs index 460aa94..c1ccf2e 100644 --- a/server/crates/arbiter-macros/src/utils.rs +++ b/server/crates/arbiter-macros/src/utils.rs @@ -1,19 +1,24 @@ -pub struct ToPath(pub &'static str); +pub(crate) struct ToPath(pub &'static str); impl ToPath { - pub fn to_path(&self) -> syn::Path { + pub(crate) fn to_path(&self) -> syn::Path { syn::parse_str(self.0).expect("Invalid path") } } macro_rules! ensure_path { - ($path:path) => {{ - #[cfg(test)] - #[expect(unused_imports)] - use $path as _; - ToPath(stringify!($path)) - }}; + ($path:path as $name:ident) => { + const _: () = { + #[cfg(test)] + #[expect( + unused_imports, + reason = "Ensures the path is valid and will cause a compile error if not" + )] + use $path as _; + }; + pub(crate) const $name: ToPath = ToPath(stringify!($path)); + }; } -pub const HASHABLE_TRAIT_PATH: ToPath = ensure_path!(::arbiter_crypto::hashing::Hashable); -pub const HMAC_DIGEST_PATH: ToPath = ensure_path!(::arbiter_crypto::hashing::Digest); +ensure_path!(::arbiter_crypto::hashing::Hashable as HASHABLE_TRAIT_PATH); +ensure_path!(::arbiter_crypto::hashing::Digest as HMAC_DIGEST_PATH); diff --git a/server/crates/arbiter-proto/src/url.rs b/server/crates/arbiter-proto/src/url.rs index 321df0b..8294cc0 100644 --- a/server/crates/arbiter-proto/src/url.rs +++ b/server/crates/arbiter-proto/src/url.rs @@ -105,7 +105,7 @@ mod tests { #[rstest] - fn test_parsing_correctness( + fn parsing_correctness( #[values("127.0.0.1", "localhost", "192.168.1.1", "some.domain.com")] host: &str, #[values(None, Some("token123".to_string()))] bootstrap_token: Option, diff --git a/server/crates/arbiter-server/src/actors/bootstrap.rs b/server/crates/arbiter-server/src/actors/bootstrap.rs index cef154a..a7e673f 100644 --- a/server/crates/arbiter-server/src/actors/bootstrap.rs +++ b/server/crates/arbiter-server/src/actors/bootstrap.rs @@ -13,8 +13,8 @@ const TOKEN_LENGTH: usize = 64; pub async fn generate_token() -> Result { let rng: StdRng = make_rng(); - let token: String = rng.sample_iter(Alphanumeric).take(TOKEN_LENGTH).fold( - Default::default(), + let token = rng.sample_iter(Alphanumeric).take(TOKEN_LENGTH).fold( + String::default(), |mut accum, char| { accum += char.to_string().as_str(); accum @@ -27,15 +27,15 @@ pub async fn generate_token() -> Result { } #[derive(Error, Debug)] -pub enum Error { +pub enum BootstrappError { #[error("Database error: {0}")] Database(#[from] db::PoolError), - #[error("Database query error: {0}")] - Query(#[from] diesel::result::Error), - #[error("I/O error: {0}")] Io(#[from] std::io::Error), + + #[error("Database query error: {0}")] + Query(#[from] diesel::result::Error), } #[derive(Actor)] @@ -44,7 +44,7 @@ pub struct Bootstrapper { } impl Bootstrapper { - pub async fn new(db: &DatabasePool) -> Result { + pub async fn new(db: &DatabasePool) -> Result { let row_count: i64 = { let mut conn = db.get().await?; @@ -69,16 +69,13 @@ impl Bootstrapper { impl Bootstrapper { #[message] pub fn is_correct_token(&self, token: String) -> bool { - match &self.token { - Some(expected) => { - let expected_bytes = expected.as_bytes(); - let token_bytes = token.as_bytes(); + self.token.as_ref().is_some_and(|expected| { + let expected_bytes = expected.as_bytes(); + let token_bytes = token.as_bytes(); - let choice = expected_bytes.ct_eq(token_bytes); - bool::from(choice) - } - None => false, - } + let choice = expected_bytes.ct_eq(token_bytes); + bool::from(choice) + }) } #[message] diff --git a/server/crates/arbiter-server/src/actors/client/auth.rs b/server/crates/arbiter-server/src/actors/client/auth.rs index a9ff7c2..0bf49ae 100644 --- a/server/crates/arbiter-server/src/actors/client/auth.rs +++ b/server/crates/arbiter-server/src/actors/client/auth.rs @@ -1,6 +1,7 @@ use arbiter_crypto::authn::{self, CLIENT_CONTEXT}; use arbiter_proto::{ ClientMetadata, + proto::client::auth::{AuthChallenge as ProtoAuthChallenge, AuthResult as ProtoAuthResult}, transport::{Bi, expect_message}, }; use chrono::Utc; @@ -27,34 +28,60 @@ use crate::{ }; #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -pub enum Error { - #[error("Database pool unavailable")] - DatabasePoolUnavailable, - #[error("Database operation failed")] - DatabaseOperationFailed, - #[error("Integrity check failed")] - IntegrityCheckFailed, - #[error("Invalid challenge solution")] - InvalidChallengeSolution, +pub enum ClientAuthError { #[error("Client approval request failed")] ApproveError(#[from] ApproveError), + + #[error("Database operation failed")] + DatabaseOperationFailed, + + #[error("Database pool unavailable")] + DatabasePoolUnavailable, + + #[error("Integrity check failed")] + IntegrityCheckFailed, + + #[error("Invalid challenge solution")] + InvalidChallengeSolution, + #[error("Transport error")] Transport, } -impl From for Error { +impl From for ClientAuthError { fn from(e: diesel::result::Error) -> Self { error!(?e, "Database error"); Self::DatabaseOperationFailed } } +impl From for arbiter_proto::proto::client::auth::AuthResult { + fn from(value: ClientAuthError) -> Self { + match value { + ClientAuthError::ApproveError(e) => match e { + ApproveError::Denied => Self::ApprovalDenied, + ApproveError::Internal => Self::Internal, + ApproveError::Upstream(flow_coordinator::ApprovalError::NoUserAgentsConnected) => { + Self::NoUserAgentsOnline + } // ApproveError::Upstream(_) => Self::Internal, + }, + ClientAuthError::DatabaseOperationFailed + | ClientAuthError::DatabasePoolUnavailable + | ClientAuthError::IntegrityCheckFailed + | ClientAuthError::Transport => Self::Internal, + ClientAuthError::InvalidChallengeSolution => Self::InvalidSignature, + } + } +} + #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] pub enum ApproveError { - #[error("Internal error")] - Internal, #[error("Client connection denied by user agents")] Denied, + + #[error("Internal error")] + Internal, + #[error("Upstream error: {0}")] Upstream(flow_coordinator::ApprovalError), } @@ -79,16 +106,28 @@ pub enum Outbound { AuthSuccess, } +impl From for arbiter_proto::proto::client::auth::response::Payload { + fn from(value: Outbound) -> Self { + match value { + Outbound::AuthChallenge { pubkey, nonce } => Self::Challenge(ProtoAuthChallenge { + pubkey: pubkey.to_bytes(), + nonce, + }), + Outbound::AuthSuccess => Self::Result(ProtoAuthResult::Success.into()), + } + } +} + /// Returns the current nonce and client ID for a registered client. /// Returns `None` if the pubkey is not registered. async fn get_current_nonce_and_id( db: &db::DatabasePool, pubkey: &authn::PublicKey, -) -> Result, Error> { +) -> Result, ClientAuthError> { let pubkey_bytes = pubkey.to_bytes(); let mut conn = db.get().await.map_err(|e| { error!(error = ?e, "Database pool error"); - Error::DatabasePoolUnavailable + ClientAuthError::DatabasePoolUnavailable })?; program_client::table .filter(program_client::public_key.eq(&pubkey_bytes)) @@ -98,7 +137,7 @@ async fn get_current_nonce_and_id( .optional() .map_err(|e| { error!(error = ?e, "Database error"); - Error::DatabaseOperationFailed + ClientAuthError::DatabaseOperationFailed }) } @@ -106,15 +145,15 @@ async fn verify_integrity( db: &db::DatabasePool, keyholder: &ActorRef, pubkey: &authn::PublicKey, -) -> Result<(), Error> { +) -> Result<(), ClientAuthError> { let mut db_conn = db.get().await.map_err(|e| { error!(error = ?e, "Database pool error"); - Error::DatabasePoolUnavailable + ClientAuthError::DatabasePoolUnavailable })?; let (id, nonce) = get_current_nonce_and_id(db, pubkey).await?.ok_or_else(|| { error!("Client not found during integrity verification"); - Error::DatabaseOperationFailed + ClientAuthError::DatabaseOperationFailed })?; let attestation = integrity::verify_entity( @@ -129,12 +168,12 @@ async fn verify_integrity( .await .map_err(|e| { error!(?e, "Integrity verification failed"); - Error::IntegrityCheckFailed + ClientAuthError::IntegrityCheckFailed })?; if attestation != AttestationStatus::Attested { error!("Integrity attestation unavailable for client {id}"); - return Err(Error::IntegrityCheckFailed); + return Err(ClientAuthError::IntegrityCheckFailed); } Ok(()) @@ -146,13 +185,13 @@ async fn create_nonce( db: &db::DatabasePool, keyholder: &ActorRef, pubkey: &authn::PublicKey, -) -> Result { +) -> Result { let pubkey_bytes = pubkey.to_bytes(); let pubkey = pubkey.clone(); let mut conn = db.get().await.map_err(|e| { error!(error = ?e, "Database pool error"); - Error::DatabasePoolUnavailable + ClientAuthError::DatabasePoolUnavailable })?; conn.exclusive_transaction(|conn| { @@ -178,7 +217,7 @@ async fn create_nonce( .await .map_err(|e| { error!(?e, "Integrity sign failed after nonce update"); - Error::DatabaseOperationFailed + ClientAuthError::DatabaseOperationFailed })?; Ok(new_nonce) @@ -190,7 +229,7 @@ async fn create_nonce( async fn approve_new_client( actors: &crate::actors::GlobalActors, profile: ClientProfile, -) -> Result<(), Error> { +) -> Result<(), ClientAuthError> { let result = actors .flow_coordinator .ask(RequestClientApproval { client: profile }) @@ -198,14 +237,14 @@ async fn approve_new_client( match result { Ok(true) => Ok(()), - Ok(false) => Err(Error::ApproveError(ApproveError::Denied)), + Ok(false) => Err(ClientAuthError::ApproveError(ApproveError::Denied)), Err(SendError::HandlerError(e)) => { error!(error = ?e, "Approval upstream error"); - Err(Error::ApproveError(ApproveError::Upstream(e))) + Err(ClientAuthError::ApproveError(ApproveError::Upstream(e))) } Err(e) => { error!(error = ?e, "Approval request to flow coordinator failed"); - Err(Error::ApproveError(ApproveError::Internal)) + Err(ClientAuthError::ApproveError(ApproveError::Internal)) } } } @@ -215,14 +254,14 @@ async fn insert_client( keyholder: &ActorRef, pubkey: &authn::PublicKey, metadata: &ClientMetadata, -) -> Result { - use crate::db::schema::{client_metadata, program_client}; +) -> Result { + use crate::db::schema::client_metadata; let pubkey = pubkey.clone(); let metadata = metadata.clone(); let mut conn = db.get().await.map_err(|e| { error!(error = ?e, "Database pool error"); - Error::DatabasePoolUnavailable + ClientAuthError::DatabasePoolUnavailable })?; conn.exclusive_transaction(|conn| { @@ -264,7 +303,7 @@ async fn insert_client( .await .map_err(|e| { error!(error = ?e, "Failed to sign integrity tag for new client key"); - Error::DatabaseOperationFailed + ClientAuthError::DatabaseOperationFailed })?; Ok(client_id) @@ -277,14 +316,14 @@ async fn sync_client_metadata( db: &db::DatabasePool, client_id: i32, metadata: &ClientMetadata, -) -> Result<(), Error> { +) -> Result<(), ClientAuthError> { use crate::db::schema::{client_metadata, client_metadata_history}; let now = SqliteTimestamp(Utc::now()); let mut conn = db.get().await.map_err(|e| { error!(error = ?e, "Database pool error"); - Error::DatabasePoolUnavailable + ClientAuthError::DatabasePoolUnavailable })?; conn.exclusive_transaction(|conn| { @@ -340,7 +379,7 @@ async fn sync_client_metadata( .await .map_err(|e| { error!(error = ?e, "Database error"); - Error::DatabaseOperationFailed + ClientAuthError::DatabaseOperationFailed }) } @@ -348,9 +387,9 @@ async fn challenge_client( transport: &mut T, pubkey: authn::PublicKey, nonce: i32, -) -> Result<(), Error> +) -> Result<(), ClientAuthError> where - T: Bi> + ?Sized, + T: Bi> + ?Sized, { transport .send(Ok(Outbound::AuthChallenge { @@ -360,51 +399,51 @@ where .await .map_err(|e| { error!(error = ?e, "Failed to send auth challenge"); - Error::Transport + ClientAuthError::Transport })?; let signature = expect_message(transport, |req: Inbound| match req { Inbound::AuthChallengeSolution { signature } => Some(signature), - _ => None, + Inbound::AuthChallengeRequest { .. } => None, }) .await .map_err(|e| { error!(error = ?e, "Failed to receive challenge solution"); - Error::Transport + ClientAuthError::Transport })?; if !pubkey.verify(nonce, CLIENT_CONTEXT, &signature) { error!("Challenge solution verification failed"); - return Err(Error::InvalidChallengeSolution); + return Err(ClientAuthError::InvalidChallengeSolution); } Ok(()) } -pub async fn authenticate(props: &mut ClientConnection, transport: &mut T) -> Result +pub async fn authenticate( + props: &mut ClientConnection, + transport: &mut T, +) -> Result where - T: Bi> + Send + ?Sized, + T: Bi> + Send + ?Sized, { let Some(Inbound::AuthChallengeRequest { pubkey, metadata }) = transport.recv().await else { - return Err(Error::Transport); + return Err(ClientAuthError::Transport); }; - let client_id = match get_current_nonce_and_id(&props.db, &pubkey).await? { - Some((id, _)) => { - verify_integrity(&props.db, &props.actors.key_holder, &pubkey).await?; - id - } - None => { - approve_new_client( - &props.actors, - ClientProfile { - pubkey: pubkey.clone(), - metadata: metadata.clone(), - }, - ) - .await?; - insert_client(&props.db, &props.actors.key_holder, &pubkey, &metadata).await? - } + let client_id = if let Some((id, _)) = get_current_nonce_and_id(&props.db, &pubkey).await? { + verify_integrity(&props.db, &props.actors.key_holder, &pubkey).await?; + id + } else { + approve_new_client( + &props.actors, + ClientProfile { + pubkey: pubkey.clone(), + metadata: metadata.clone(), + }, + ) + .await?; + insert_client(&props.db, &props.actors.key_holder, &pubkey, &metadata).await? }; sync_client_metadata(&props.db, client_id, &metadata).await?; @@ -416,7 +455,7 @@ where .await .map_err(|e| { error!(error = ?e, "Failed to send auth success"); - Error::Transport + ClientAuthError::Transport })?; Ok(client_id) diff --git a/server/crates/arbiter-server/src/actors/client/mod.rs b/server/crates/arbiter-server/src/actors/client/mod.rs index 13e5733..b560ce3 100644 --- a/server/crates/arbiter-server/src/actors/client/mod.rs +++ b/server/crates/arbiter-server/src/actors/client/mod.rs @@ -31,7 +31,7 @@ pub struct ClientConnection { } impl ClientConnection { - pub fn new(db: db::DatabasePool, actors: GlobalActors) -> Self { + pub const fn new(db: db::DatabasePool, actors: GlobalActors) -> Self { Self { db, actors } } } @@ -41,10 +41,10 @@ pub mod session; pub async fn connect_client(mut props: ClientConnection, transport: &mut T) where - T: Bi> + Send + ?Sized, + T: Bi> + Send + ?Sized, { let fut = auth::authenticate(&mut props, transport); - println!("authenticate future size: {}", std::mem::size_of_val(&fut)); + println!("authenticate future size: {}", size_of_val(&fut)); match fut.await { Ok(client_id) => { ClientSession::spawn(ClientSession::new(props, client_id)); diff --git a/server/crates/arbiter-server/src/actors/client/session.rs b/server/crates/arbiter-server/src/actors/client/session.rs index 184c650..35b5fb2 100644 --- a/server/crates/arbiter-server/src/actors/client/session.rs +++ b/server/crates/arbiter-server/src/actors/client/session.rs @@ -21,7 +21,7 @@ pub struct ClientSession { } impl ClientSession { - pub(crate) fn new(props: ClientConnection, client_id: i32) -> Self { + pub(crate) const fn new(props: ClientConnection, client_id: i32) -> Self { Self { props, client_id } } } @@ -29,14 +29,16 @@ impl ClientSession { #[messages] impl ClientSession { #[message] - pub(crate) async fn handle_query_vault_state(&mut self) -> Result { + pub(crate) async fn handle_query_vault_state( + &mut self, + ) -> Result { use crate::actors::keyholder::GetState; let vault_state = match self.props.actors.key_holder.ask(GetState {}).await { Ok(state) => state, Err(err) => { error!(?err, actor = "client", "keyholder.query.failed"); - return Err(Error::Internal); + return Err(ClientSessionError::Internal); } }; @@ -75,7 +77,7 @@ impl ClientSession { impl Actor for ClientSession { type Args = Self; - type Error = Error; + type Error = ClientSessionError; async fn on_start( args: Self::Args, @@ -86,13 +88,13 @@ impl Actor for ClientSession { .flow_coordinator .ask(RegisterClient { actor: this }) .await - .map_err(|_| Error::ConnectionRegistrationFailed)?; + .map_err(|_| ClientSessionError::ConnectionRegistrationFailed)?; Ok(args) } } impl ClientSession { - pub fn new_test(db: db::DatabasePool, actors: GlobalActors) -> Self { + pub const fn new_test(db: db::DatabasePool, actors: GlobalActors) -> Self { let props = ClientConnection::new(db, actors); Self { props, @@ -102,7 +104,7 @@ impl ClientSession { } #[derive(Debug, thiserror::Error)] -pub enum Error { +pub enum ClientSessionError { #[error("Connection registration failed")] ConnectionRegistrationFailed, #[error("Internal error")] @@ -111,9 +113,9 @@ pub enum Error { #[derive(Debug, thiserror::Error)] pub enum SignTransactionRpcError { - #[error("Policy evaluation failed")] - Vet(#[from] VetError), - #[error("Internal error")] Internal, + + #[error("Policy evaluation failed")] + Vet(#[from] VetError), } diff --git a/server/crates/arbiter-server/src/actors/evm/mod.rs b/server/crates/arbiter-server/src/actors/evm/mod.rs index c31cdd0..ed744c1 100644 --- a/server/crates/arbiter-server/src/actors/evm/mod.rs +++ b/server/crates/arbiter-server/src/actors/evm/mod.rs @@ -1,4 +1,6 @@ -use alloy::{consensus::TxEip1559, primitives::Address, signers::Signature}; +use alloy::{ + consensus::TxEip1559, network::TxSignerSync as _, primitives::Address, signers::Signature, +}; use diesel::{ ExpressionMethods, OptionalExtension as _, QueryDsl, SelectableHelper as _, dsl::insert_into, }; @@ -35,7 +37,7 @@ pub enum SignTransactionError { Database(#[from] DatabaseError), #[error("Keyholder error: {0}")] - Keyholder(#[from] crate::actors::keyholder::Error), + Keyholder(#[from] crate::actors::keyholder::KeyHolderError), #[error("Keyholder mailbox error")] KeyholderSend, @@ -48,9 +50,9 @@ pub enum SignTransactionError { } #[derive(Debug, thiserror::Error)] -pub enum Error { +pub enum EvmActorError { #[error("Keyholder error: {0}")] - Keyholder(#[from] crate::actors::keyholder::Error), + Keyholder(#[from] crate::actors::keyholder::KeyHolderError), #[error("Keyholder mailbox error")] KeyholderSend, @@ -59,7 +61,7 @@ pub enum Error { Database(#[from] DatabaseError), #[error("Integrity violation: {0}")] - Integrity(#[from] integrity::Error), + Integrity(#[from] integrity::IntegrityError), } #[derive(Actor)] @@ -88,7 +90,7 @@ impl EvmActor { #[messages] impl EvmActor { #[message] - pub async fn generate(&mut self) -> Result<(i32, Address), Error> { + pub async fn generate(&mut self) -> Result<(i32, Address), EvmActorError> { let (mut key_cell, address) = safe_signer::generate(&mut self.rng); let plaintext = key_cell.read_inline(|reader| SafeCell::new(reader.to_vec())); @@ -97,7 +99,7 @@ impl EvmActor { .keyholder .ask(CreateNew { plaintext }) .await - .map_err(|_| Error::KeyholderSend)?; + .map_err(|_| EvmActorError::KeyholderSend)?; let mut conn = self.db.get().await.map_err(DatabaseError::from)?; let wallet_id = insert_into(schema::evm_wallet::table) @@ -114,7 +116,7 @@ impl EvmActor { } #[message] - pub async fn list_wallets(&self) -> Result, Error> { + pub async fn list_wallets(&self) -> Result, EvmActorError> { let mut conn = self.db.get().await.map_err(DatabaseError::from)?; let rows: Vec = schema::evm_wallet::table .select(models::EvmWallet::as_select()) @@ -136,7 +138,7 @@ impl EvmActor { &mut self, basic: SharedGrantSettings, grant: SpecificGrant, - ) -> Result { + ) -> Result { match grant { SpecificGrant::EtherTransfer(settings) => self .engine @@ -145,7 +147,7 @@ impl EvmActor { specific: settings, }) .await - .map_err(Error::from), + .map_err(EvmActorError::from), SpecificGrant::TokenTransfer(settings) => self .engine .create_grant::(CombinedSettings { @@ -153,12 +155,13 @@ impl EvmActor { specific: settings, }) .await - .map_err(Error::from), + .map_err(EvmActorError::from), } } #[message] - pub async fn useragent_delete_grant(&mut self, _grant_id: i32) -> Result<(), Error> { + #[expect(clippy::unused_async, reason = "reserved for impl")] + pub async fn useragent_delete_grant(&mut self, _grant_id: i32) -> Result<(), EvmActorError> { // let mut conn = self.db.get().await.map_err(DatabaseError::from)?; // let keyholder = self.keyholder.clone(); @@ -183,11 +186,15 @@ impl EvmActor { } #[message] - pub async fn useragent_list_grants(&mut self) -> Result>, Error> { + pub async fn useragent_list_grants( + &mut self, + ) -> Result>, EvmActorError> { match self.engine.list_all_grants().await { Ok(grants) => Ok(grants), - Err(ListError::Database(db_err)) => Err(Error::Database(db_err)), - Err(ListError::Integrity(integrity_err)) => Err(Error::Integrity(integrity_err)), + Err(ListError::Database(db_err)) => Err(EvmActorError::Database(db_err)), + Err(ListError::Integrity(integrity_err)) => { + Err(EvmActorError::Integrity(integrity_err)) + } } } @@ -267,7 +274,6 @@ impl EvmActor { .evaluate_transaction(wallet_access, transaction.clone(), RunKind::Execution) .await?; - use alloy::network::TxSignerSync as _; Ok(signer.sign_transaction_sync(&mut transaction)?) } } diff --git a/server/crates/arbiter-server/src/actors/flow_coordinator/client_connect_approval.rs b/server/crates/arbiter-server/src/actors/flow_coordinator/client_connect_approval.rs index c5b20c3..94aa13e 100644 --- a/server/crates/arbiter-server/src/actors/flow_coordinator/client_connect_approval.rs +++ b/server/crates/arbiter-server/src/actors/flow_coordinator/client_connect_approval.rs @@ -41,7 +41,7 @@ impl Actor for ClientApprovalController { async fn on_start( Args { client, - mut user_agents, + user_agents, reply, }: Self::Args, actor_ref: ActorRef, @@ -52,8 +52,9 @@ impl Actor for ClientApprovalController { reply: Some(reply), }; - for user_agent in user_agents.drain(..) { + for user_agent in user_agents { actor_ref.link(&user_agent).await; + let _ = user_agent .tell(BeginNewClientApproval { client: client.clone(), @@ -85,7 +86,7 @@ impl Actor for ClientApprovalController { #[messages] impl ClientApprovalController { #[message(ctx)] - pub async fn client_approval_answer(&mut self, approved: bool, ctx: &mut Context) { + pub fn client_approval_answer(&mut self, approved: bool, ctx: &mut Context) { if !approved { // Denial wins immediately regardless of other pending responses. self.send_reply(Ok(false)); diff --git a/server/crates/arbiter-server/src/actors/flow_coordinator/mod.rs b/server/crates/arbiter-server/src/actors/flow_coordinator/mod.rs index 2e0aa9a..2defcc3 100644 --- a/server/crates/arbiter-server/src/actors/flow_coordinator/mod.rs +++ b/server/crates/arbiter-server/src/actors/flow_coordinator/mod.rs @@ -92,7 +92,7 @@ impl FlowCoordinator { } #[message(ctx)] - pub async fn request_client_approval( + pub fn request_client_approval( &mut self, client: ClientProfile, ctx: &mut Context>>, diff --git a/server/crates/arbiter-server/src/actors/keyholder/mod.rs b/server/crates/arbiter-server/src/actors/keyholder/mod.rs index 64387bc..0fad969 100644 --- a/server/crates/arbiter-server/src/actors/keyholder/mod.rs +++ b/server/crates/arbiter-server/src/actors/keyholder/mod.rs @@ -36,19 +36,12 @@ enum State { } #[derive(Debug, thiserror::Error)] -pub enum Error { +pub enum KeyHolderError { #[error("Keyholder is already bootstrapped")] AlreadyBootstrapped, - #[error("Keyholder is not bootstrapped")] - NotBootstrapped, - #[error("Invalid key provided")] - InvalidKey, - #[error("Requested aead entry not found")] - NotFound, - - #[error("Encryption error: {0}")] - Encryption(#[from] chacha20poly1305::aead::Error), + #[error("Broken database")] + BrokenDatabase, #[error("Database error: {0}")] DatabaseConnection(#[from] db::PoolError), @@ -56,11 +49,21 @@ pub enum Error { #[error("Database transaction error: {0}")] DatabaseTransaction(#[from] diesel::result::Error), - #[error("Broken database")] - BrokenDatabase, + #[error("Encryption error: {0}")] + Encryption(#[from] chacha20poly1305::aead::Error), + + #[error("Invalid key provided")] + InvalidKey, + + #[error("Keyholder is not bootstrapped")] + NotBootstrapped, + + #[error("Requested aead entry not found")] + NotFound, } /// Manages vault root key and tracks current state of the vault (bootstrapped/unbootstrapped, sealed/unsealed). +/// /// Provides API for encrypting and decrypting data using the vault root key. /// Abstraction over database to make sure nonces are never reused and encryption keys are never exposed in plaintext outside of this actor. #[derive(Actor)] @@ -71,7 +74,7 @@ pub struct KeyHolder { #[messages] impl KeyHolder { - pub async fn new(db: db::DatabasePool) -> Result { + pub async fn new(db: db::DatabasePool) -> Result { let state = { let mut conn = db.get().await?; @@ -94,7 +97,10 @@ impl KeyHolder { // Exclusive transaction to avoid race condtions if multiple keyholders write // additional layer of protection against nonce-reuse - async fn get_new_nonce(pool: &db::DatabasePool, root_key_id: i32) -> Result { + async fn get_new_nonce( + pool: &db::DatabasePool, + root_key_id: i32, + ) -> Result { let mut conn = pool.get().await?; let nonce = conn @@ -106,12 +112,12 @@ impl KeyHolder { .first(conn) .await?; - let mut nonce = Nonce::try_from(current_nonce.as_slice()).map_err(|_| { + let mut nonce = Nonce::try_from(current_nonce.as_slice()).map_err(|()| { error!( "Broken database: invalid nonce for root key history id={}", root_key_id ); - Error::BrokenDatabase + KeyHolderError::BrokenDatabase })?; nonce.increment(); @@ -121,7 +127,7 @@ impl KeyHolder { .execute(conn) .await?; - Result::<_, Error>::Ok(nonce) + Result::<_, KeyHolderError>::Ok(nonce) }) }) .await?; @@ -130,9 +136,12 @@ impl KeyHolder { } #[message] - pub async fn bootstrap(&mut self, seal_key_raw: SafeCell>) -> Result<(), Error> { + pub async fn bootstrap( + &mut self, + seal_key_raw: SafeCell>, + ) -> Result<(), KeyHolderError> { if !matches!(self.state, State::Unbootstrapped) { - return Err(Error::AlreadyBootstrapped); + return Err(KeyHolderError::AlreadyBootstrapped); } let salt = v1::generate_salt(); let mut seal_key = derive_key(seal_key_raw, &salt); @@ -148,7 +157,7 @@ impl KeyHolder { .encrypt(&root_key_nonce, v1::ROOT_KEY_TAG, root_key_reader) .map_err(|err| { error!(?err, "Fatal bootstrap error"); - Error::Encryption(err) + KeyHolderError::Encryption(err) }) })?; @@ -192,12 +201,15 @@ impl KeyHolder { } #[message] - pub async fn try_unseal(&mut self, seal_key_raw: SafeCell>) -> Result<(), Error> { + pub async fn try_unseal( + &mut self, + seal_key_raw: SafeCell>, + ) -> Result<(), KeyHolderError> { let State::Sealed { root_key_history_id, } = &self.state else { - return Err(Error::NotBootstrapped); + return Err(KeyHolderError::NotBootstrapped); }; // We don't want to hold connection while doing expensive KDF work @@ -213,16 +225,16 @@ impl KeyHolder { let salt = ¤t_key.salt; let salt = v1::Salt::try_from(salt.as_slice()).map_err(|_| { error!("Broken database: invalid salt for root key"); - Error::BrokenDatabase + KeyHolderError::BrokenDatabase })?; let mut seal_key = derive_key(seal_key_raw, &salt); let mut root_key = SafeCell::new(current_key.ciphertext.clone()); - let nonce = v1::Nonce::try_from(current_key.root_key_encryption_nonce.as_slice()).map_err( - |_| { + let nonce = Nonce::try_from(current_key.root_key_encryption_nonce.as_slice()).map_err( + |()| { error!("Broken database: invalid nonce for root key"); - Error::BrokenDatabase + KeyHolderError::BrokenDatabase }, )?; @@ -230,14 +242,14 @@ impl KeyHolder { .decrypt_in_place(&nonce, v1::ROOT_KEY_TAG, &mut root_key) .map_err(|err| { error!(?err, "Failed to unseal root key: invalid seal key"); - Error::InvalidKey + KeyHolderError::InvalidKey })?; self.state = State::Unsealed { root_key_history_id: current_key.id, root_key: KeyCell::try_from(root_key).map_err(|err| { error!(?err, "Broken database: invalid encryption key size"); - Error::BrokenDatabase + KeyHolderError::BrokenDatabase })?, }; @@ -247,9 +259,9 @@ impl KeyHolder { } #[message] - pub async fn decrypt(&mut self, aead_id: i32) -> Result>, Error> { + pub async fn decrypt(&mut self, aead_id: i32) -> Result>, KeyHolderError> { let State::Unsealed { root_key, .. } = &mut self.state else { - return Err(Error::NotBootstrapped); + return Err(KeyHolderError::NotBootstrapped); }; let row: models::AeadEncrypted = { @@ -260,15 +272,15 @@ impl KeyHolder { .first(&mut conn) .await .optional()? - .ok_or(Error::NotFound)? + .ok_or(KeyHolderError::NotFound)? }; - let nonce = v1::Nonce::try_from(row.current_nonce.as_slice()).map_err(|_| { + let nonce = Nonce::try_from(row.current_nonce.as_slice()).map_err(|()| { error!( "Broken database: invalid nonce for aead_encrypted id={}", aead_id ); - Error::BrokenDatabase + KeyHolderError::BrokenDatabase })?; let mut output = SafeCell::new(row.ciphertext); root_key.decrypt_in_place(&nonce, v1::TAG, &mut output)?; @@ -277,14 +289,17 @@ impl KeyHolder { // Creates new `aead_encrypted` entry in the database and returns it's ID #[message] - pub async fn create_new(&mut self, mut plaintext: SafeCell>) -> Result { + pub async fn create_new( + &mut self, + mut plaintext: SafeCell>, + ) -> Result { let State::Unsealed { root_key, root_key_history_id, .. } = &mut self.state else { - return Err(Error::NotBootstrapped); + return Err(KeyHolderError::NotBootstrapped); }; // Order matters here - `get_new_nonce` acquires connection, so we need to call it before next acquire @@ -320,21 +335,19 @@ impl KeyHolder { } #[message] - pub fn sign_integrity(&mut self, mac_input: Vec) -> Result<(i32, Vec), Error> { + pub fn sign_integrity(&mut self, mac_input: Vec) -> Result<(i32, Vec), KeyHolderError> { let State::Unsealed { root_key, root_key_history_id, } = &mut self.state else { - return Err(Error::NotBootstrapped); + return Err(KeyHolderError::NotBootstrapped); }; - let mut hmac = root_key - .0 - .read_inline(|k| match HmacSha256::new_from_slice(k) { - Ok(v) => v, - Err(_) => unreachable!("HMAC accepts keys of any size"), - }); + let mut hmac = root_key.0.read_inline(|k| { + HmacSha256::new_from_slice(k) + .unwrap_or_else(|_| unreachable!("HMAC accepts keys of any size")) + }); hmac.update(&root_key_history_id.to_be_bytes()); hmac.update(&mac_input); @@ -348,25 +361,23 @@ impl KeyHolder { mac_input: Vec, expected_mac: Vec, key_version: i32, - ) -> Result { + ) -> Result { let State::Unsealed { root_key, root_key_history_id, } = &mut self.state else { - return Err(Error::NotBootstrapped); + return Err(KeyHolderError::NotBootstrapped); }; if *root_key_history_id != key_version { return Ok(false); } - let mut hmac = root_key - .0 - .read_inline(|k| match HmacSha256::new_from_slice(k) { - Ok(v) => v, - Err(_) => unreachable!("HMAC accepts keys of any size"), - }); + let mut hmac = root_key.0.read_inline(|k| { + HmacSha256::new_from_slice(k) + .unwrap_or_else(|_| unreachable!("HMAC accepts keys of any size")) + }); hmac.update(&key_version.to_be_bytes()); hmac.update(&mac_input); @@ -374,13 +385,13 @@ impl KeyHolder { } #[message] - pub fn seal(&mut self) -> Result<(), Error> { + pub fn seal(&mut self) -> Result<(), KeyHolderError> { let State::Unsealed { root_key_history_id, .. } = &self.state else { - return Err(Error::NotBootstrapped); + return Err(KeyHolderError::NotBootstrapped); }; self.state = State::Sealed { root_key_history_id: *root_key_history_id, @@ -391,12 +402,7 @@ impl KeyHolder { #[cfg(test)] mod tests { - use diesel::SelectableHelper; - - use diesel_async::RunQueryDsl; - - use crate::db::{self}; - use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; + use arbiter_crypto::safecell::SafeCellHandle as _; use super::*; @@ -412,12 +418,12 @@ mod tests { async fn nonce_monotonic_even_when_nonce_allocation_interleaves() { let db = db::create_test_pool().await; let mut actor = bootstrapped_actor(&db).await; - let root_key_history_id = match actor.state { - State::Unsealed { - root_key_history_id, - .. - } => root_key_history_id, - _ => panic!("expected unsealed state"), + let State::Unsealed { + root_key_history_id, + .. + } = actor.state + else { + panic!("expected unsealed state"); }; let n1 = KeyHolder::get_new_nonce(&db, root_key_history_id) @@ -429,8 +435,8 @@ mod tests { assert!(n2.to_vec() > n1.to_vec(), "nonce must increase"); let mut conn = db.get().await.unwrap(); - let root_row: models::RootKeyHistory = schema::root_key_history::table - .select(models::RootKeyHistory::as_select()) + let root_row = schema::root_key_history::table + .select(RootKeyHistory::as_select()) .first(&mut conn) .await .unwrap(); diff --git a/server/crates/arbiter-server/src/actors/mod.rs b/server/crates/arbiter-server/src/actors/mod.rs index 8ff1fce..768ce1f 100644 --- a/server/crates/arbiter-server/src/actors/mod.rs +++ b/server/crates/arbiter-server/src/actors/mod.rs @@ -11,18 +11,18 @@ use crate::{ pub mod bootstrap; pub mod client; -mod evm; +pub mod evm; pub mod flow_coordinator; pub mod keyholder; pub mod user_agent; #[derive(Error, Debug)] -pub enum SpawnError { +pub enum GlobalActorsSpawnError { #[error("Failed to spawn Bootstrapper actor")] - Bootstrapper(#[from] bootstrap::Error), + Bootstrapper(#[from] bootstrap::BootstrappError), #[error("Failed to spawn KeyHolder actor")] - KeyHolder(#[from] keyholder::Error), + KeyHolder(#[from] keyholder::KeyHolderError), } /// Long-lived actors that are shared across all connections and handle global state and operations @@ -35,7 +35,7 @@ pub struct GlobalActors { } impl GlobalActors { - pub async fn spawn(db: db::DatabasePool) -> Result { + pub async fn spawn(db: db::DatabasePool) -> Result { let key_holder = KeyHolder::spawn(KeyHolder::new(db.clone()).await?); Ok(Self { bootstrapper: Bootstrapper::spawn(Bootstrapper::new(&db).await?), diff --git a/server/crates/arbiter-server/src/actors/user_agent/auth.rs b/server/crates/arbiter-server/src/actors/user_agent/auth.rs index 00d2d55..c93e18c 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/auth.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/auth.rs @@ -7,7 +7,9 @@ use crate::actors::user_agent::{ auth::state::{AuthContext, AuthStateMachine}, }; mod state; -use state::*; +use state::{ + AuthError, AuthEvents, AuthStates, BootstrapAuthRequest, ChallengeRequest, ChallengeSolution, +}; #[derive(Debug, Clone)] pub enum Inbound { diff --git a/server/crates/arbiter-server/src/actors/user_agent/auth/state.rs b/server/crates/arbiter-server/src/actors/user_agent/auth/state.rs index 60bcf6f..792172b 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/auth/state.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/auth/state.rs @@ -204,14 +204,14 @@ pub struct AuthContext<'a, T> { } impl<'a, T> AuthContext<'a, T> { - pub fn new(conn: &'a mut UserAgentConnection, transport: T) -> Self { + pub const fn new(conn: &'a mut UserAgentConnection, transport: T) -> Self { Self { conn, transport } } } impl AuthStateMachineContext for AuthContext<'_, T> where - T: Bi> + Send, + T: Bi> + Send, { type Error = Error; @@ -237,8 +237,6 @@ where }) } - #[allow(missing_docs)] - #[allow(clippy::result_unit_err)] async fn verify_bootstrap_token( &mut self, BootstrapAuthRequest { pubkey, token }: BootstrapAuthRequest, @@ -261,28 +259,23 @@ where return Err(Error::InvalidBootstrapToken); } - match token_ok { - true => { - register_key(&self.conn.db, &self.conn.actors.key_holder, &pubkey).await?; - self.transport - .send(Ok(Outbound::AuthSuccess)) - .await - .map_err(|_| Error::Transport)?; - Ok(pubkey) - } - false => { - error!("Invalid bootstrap token provided"); - self.transport - .send(Err(Error::InvalidBootstrapToken)) - .await - .map_err(|_| Error::Transport)?; - Err(Error::InvalidBootstrapToken) - } + if token_ok { + register_key(&self.conn.db, &self.conn.actors.key_holder, &pubkey).await?; + self.transport + .send(Ok(Outbound::AuthSuccess)) + .await + .map_err(|_| Error::Transport)?; + Ok(pubkey) + } else { + error!("Invalid bootstrap token provided"); + self.transport + .send(Err(Error::InvalidBootstrapToken)) + .await + .map_err(|_| Error::Transport)?; + Err(Error::InvalidBootstrapToken) } } - #[allow(missing_docs)] - #[allow(clippy::unused_unit)] async fn verify_solution( &mut self, ChallengeContext { @@ -291,28 +284,25 @@ where }: &ChallengeContext, ChallengeSolution { solution }: ChallengeSolution, ) -> Result { - let signature = authn::Signature::try_from(solution.as_slice()).map_err(|_| { + let signature = authn::Signature::try_from(solution.as_slice()).map_err(|()| { error!("Failed to decode signature in challenge solution"); Error::InvalidChallengeSolution })?; let valid = key.verify(*challenge_nonce, USERAGENT_CONTEXT, &signature); - match valid { - true => { - self.transport - .send(Ok(Outbound::AuthSuccess)) - .await - .map_err(|_| Error::Transport)?; - Ok(key.clone()) - } - false => { - self.transport - .send(Err(Error::InvalidChallengeSolution)) - .await - .map_err(|_| Error::Transport)?; - Err(Error::InvalidChallengeSolution) - } + if valid { + self.transport + .send(Ok(Outbound::AuthSuccess)) + .await + .map_err(|_| Error::Transport)?; + Ok(key.clone()) + } else { + self.transport + .send(Err(Error::InvalidChallengeSolution)) + .await + .map_err(|_| Error::Transport)?; + Err(Error::InvalidChallengeSolution) } } } diff --git a/server/crates/arbiter-server/src/actors/user_agent/mod.rs b/server/crates/arbiter-server/src/actors/user_agent/mod.rs index 5e87d23..d921a6b 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/mod.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/mod.rs @@ -28,7 +28,7 @@ pub struct UserAgentConnection { } impl UserAgentConnection { - pub fn new(db: db::DatabasePool, actors: GlobalActors) -> Self { + pub const fn new(db: db::DatabasePool, actors: GlobalActors) -> Self { Self { db, actors } } } diff --git a/server/crates/arbiter-server/src/actors/user_agent/session.rs b/server/crates/arbiter-server/src/actors/user_agent/session.rs index d3410bd..d88827c 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/session.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/session.rs @@ -17,28 +17,28 @@ mod state; use state::{DummyContext, UserAgentEvents, UserAgentStateMachine}; #[derive(Debug, Error)] -pub enum Error { - #[error("State transition failed")] - State, - +pub enum UserAgentSessionError { #[error("Internal error: {message}")] Internal { message: Cow<'static, str> }, + + #[error("State transition failed")] + State, } -impl From for Error { +impl From for UserAgentSessionError { fn from(err: crate::db::PoolError) -> Self { error!(?err, "Database pool error"); Self::internal("Database pool error") } } -impl From for Error { +impl From for UserAgentSessionError { fn from(err: diesel::result::Error) -> Self { error!(?err, "Database error"); Self::internal("Database error") } } -impl Error { +impl UserAgentSessionError { pub fn internal(message: impl Into>) -> Self { Self::Internal { message: message.into(), @@ -67,7 +67,7 @@ impl UserAgentSession { props, state: UserAgentStateMachine::new(DummyContext), sender, - pending_client_approvals: Default::default(), + pending_client_approvals: HashMap::default(), } } @@ -87,10 +87,10 @@ impl UserAgentSession { Self::new(UserAgentConnection::new(db, actors), Box::new(DummySender)) } - fn transition(&mut self, event: UserAgentEvents) -> Result<(), Error> { + fn transition(&mut self, event: UserAgentEvents) -> Result<(), UserAgentSessionError> { self.state.process_event(event).map_err(|e| { error!(?e, "State transition failed"); - Error::State + UserAgentSessionError::State })?; Ok(()) } @@ -132,11 +132,11 @@ impl UserAgentSession { impl Actor for UserAgentSession { type Args = Self; - type Error = Error; + type Error = UserAgentSessionError; async fn on_start( args: Self::Args, - this: kameo::prelude::ActorRef, + this: ActorRef, ) -> Result { args.props .actors @@ -150,7 +150,9 @@ impl Actor for UserAgentSession { ?err, "Failed to register user agent connection with flow coordinator" ); - Error::internal("Failed to register user agent connection with flow coordinator") + UserAgentSessionError::internal( + "Failed to register user agent connection with flow coordinator", + ) })?; Ok(args) } diff --git a/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs b/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs index 71f4067..ad3f16f 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs @@ -11,12 +11,13 @@ use diesel_async::{AsyncConnection, RunQueryDsl}; use kameo::error::SendError; use kameo::messages; use kameo::prelude::Context; +use thiserror::Error; use tracing::{error, info}; use x25519_dalek::{EphemeralSecret, PublicKey}; -use crate::actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer; +use crate::{actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer, evm::policies::SharedGrantSettings}; use crate::actors::keyholder::KeyHolderState; -use crate::actors::user_agent::session::Error; +use crate::actors::user_agent::session::UserAgentSessionError; use crate::actors::{ evm::{ ClientSignTransaction, Generate, ListWallets, SignTransactionError as EvmSignError, @@ -34,10 +35,12 @@ use crate::db::models::{ use crate::evm::policies::{Grant, SpecificGrant}; impl UserAgentSession { - fn take_unseal_secret(&mut self) -> Result<(EphemeralSecret, PublicKey), Error> { + fn take_unseal_secret(&self) -> Result<(EphemeralSecret, PublicKey), UserAgentSessionError> { let UserAgentStates::WaitingForUnsealKey(unseal_context) = self.state.state() else { error!("Received encrypted key in invalid state"); - return Err(Error::internal("Invalid state for unseal encrypted key")); + return Err(UserAgentSessionError::internal( + "Invalid state for unseal encrypted key", + )); }; let ephemeral_secret = { @@ -47,13 +50,14 @@ impl UserAgentSession { )] let mut secret_lock = unseal_context.secret.lock().unwrap(); let secret = secret_lock.take(); - match secret { - Some(secret) => secret, - None => { - drop(secret_lock); - error!("Ephemeral secret already taken"); - return Err(Error::internal("Ephemeral secret already taken")); - } + if let Some(secret) = secret { + secret + } else { + drop(secret_lock); + error!("Ephemeral secret already taken"); + return Err(UserAgentSessionError::internal( + "Ephemeral secret already taken", + )); } }; @@ -79,7 +83,7 @@ impl UserAgentSession { }); match decryption_result { - Ok(_) => Ok(key_buffer), + Ok(()) => Ok(key_buffer), Err(err) => { error!(?err, "Failed to decrypt encrypted key material"); Err(()) @@ -97,7 +101,7 @@ pub enum UnsealError { #[error("Invalid key provided for unsealing")] InvalidKey, #[error("Internal error during unsealing process")] - General(#[from] super::Error), + General(#[from] UserAgentSessionError), } #[derive(Debug, Error)] @@ -108,7 +112,7 @@ pub enum BootstrapError { AlreadyBootstrapped, #[error("Internal error during bootstrapping process")] - General(#[from] super::Error), + General(#[from] UserAgentSessionError), } #[derive(Debug, Error)] @@ -132,16 +136,16 @@ pub enum GrantMutationError { #[messages] impl UserAgentSession { #[message] - pub async fn handle_unseal_request( + pub fn handle_unseal_request( &mut self, - client_pubkey: x25519_dalek::PublicKey, - ) -> Result { + client_pubkey: PublicKey, + ) -> Result { let secret = EphemeralSecret::random(); let public_key = PublicKey::from(&secret); self.transition(UserAgentEvents::UnsealRequest(UnsealContext { - secret: Mutex::new(Some(secret)), client_public_key: client_pubkey, + secret: Mutex::new(Some(secret)), }))?; Ok(UnsealStartResponse { @@ -158,27 +162,24 @@ impl UserAgentSession { ) -> Result<(), UnsealError> { let (ephemeral_secret, client_public_key) = match self.take_unseal_secret() { Ok(values) => values, - Err(Error::State) => { + Err(UserAgentSessionError::State) => { self.transition(UserAgentEvents::ReceivedInvalidKey)?; return Err(UnsealError::InvalidKey); } Err(_err) => { - return Err(Error::internal("Failed to take unseal secret").into()); + return Err(UserAgentSessionError::internal("Failed to take unseal secret").into()); } }; - let seal_key_buffer = match Self::decrypt_client_key_material( + let Ok(seal_key_buffer) = Self::decrypt_client_key_material( ephemeral_secret, client_public_key, &nonce, &ciphertext, &associated_data, - ) { - Ok(buffer) => buffer, - Err(()) => { - self.transition(UserAgentEvents::ReceivedInvalidKey)?; - return Err(UnsealError::InvalidKey); - } + ) else { + self.transition(UserAgentEvents::ReceivedInvalidKey)?; + return Err(UnsealError::InvalidKey); }; match self @@ -190,12 +191,12 @@ impl UserAgentSession { }) .await { - Ok(_) => { + Ok(()) => { info!("Successfully unsealed key with client-provided key"); self.transition(UserAgentEvents::ReceivedValidKey)?; Ok(()) } - Err(SendError::HandlerError(keyholder::Error::InvalidKey)) => { + Err(SendError::HandlerError(keyholder::KeyHolderError::InvalidKey)) => { self.transition(UserAgentEvents::ReceivedInvalidKey)?; Err(UnsealError::InvalidKey) } @@ -207,7 +208,7 @@ impl UserAgentSession { Err(err) => { error!(?err, "Failed to send unseal request to keyholder"); self.transition(UserAgentEvents::ReceivedInvalidKey)?; - Err(Error::internal("Vault actor error").into()) + Err(UserAgentSessionError::internal("Vault actor error").into()) } } } @@ -221,25 +222,22 @@ impl UserAgentSession { ) -> Result<(), BootstrapError> { let (ephemeral_secret, client_public_key) = match self.take_unseal_secret() { Ok(values) => values, - Err(Error::State) => { + Err(UserAgentSessionError::State) => { self.transition(UserAgentEvents::ReceivedInvalidKey)?; return Err(BootstrapError::InvalidKey); } Err(err) => return Err(err.into()), }; - let seal_key_buffer = match Self::decrypt_client_key_material( + let Ok(seal_key_buffer) = Self::decrypt_client_key_material( ephemeral_secret, client_public_key, &nonce, &ciphertext, &associated_data, - ) { - Ok(buffer) => buffer, - Err(()) => { - self.transition(UserAgentEvents::ReceivedInvalidKey)?; - return Err(BootstrapError::InvalidKey); - } + ) else { + self.transition(UserAgentEvents::ReceivedInvalidKey)?; + return Err(BootstrapError::InvalidKey); }; match self @@ -251,12 +249,12 @@ impl UserAgentSession { }) .await { - Ok(_) => { + Ok(()) => { info!("Successfully bootstrapped vault with client-provided key"); self.transition(UserAgentEvents::ReceivedValidKey)?; Ok(()) } - Err(SendError::HandlerError(keyholder::Error::AlreadyBootstrapped)) => { + Err(SendError::HandlerError(keyholder::KeyHolderError::AlreadyBootstrapped)) => { self.transition(UserAgentEvents::ReceivedInvalidKey)?; Err(BootstrapError::AlreadyBootstrapped) } @@ -268,7 +266,7 @@ impl UserAgentSession { Err(err) => { error!(?err, "Failed to send bootstrap request to keyholder"); self.transition(UserAgentEvents::ReceivedInvalidKey)?; - Err(BootstrapError::General(Error::internal( + Err(BootstrapError::General(UserAgentSessionError::internal( "Vault actor error", ))) } @@ -279,14 +277,16 @@ impl UserAgentSession { #[messages] impl UserAgentSession { #[message] - pub(crate) async fn handle_query_vault_state(&mut self) -> Result { + pub(crate) async fn handle_query_vault_state( + &mut self, + ) -> Result { use crate::actors::keyholder::GetState; let vault_state = match self.props.actors.key_holder.ask(GetState {}).await { Ok(state) => state, Err(err) => { error!(?err, actor = "useragent", "keyholder.query.failed"); - return Err(Error::internal("Vault is in broken state")); + return Err(UserAgentSessionError::internal("Vault is in broken state")); } }; @@ -297,26 +297,32 @@ impl UserAgentSession { #[messages] impl UserAgentSession { #[message] - pub(crate) async fn handle_evm_wallet_create(&mut self) -> Result<(i32, Address), Error> { + pub(crate) async fn handle_evm_wallet_create( + &mut self, + ) -> Result<(i32, Address), UserAgentSessionError> { match self.props.actors.evm.ask(Generate {}).await { Ok(address) => Ok(address), - Err(SendError::HandlerError(err)) => Err(Error::internal(format!( + Err(SendError::HandlerError(err)) => Err(UserAgentSessionError::internal(format!( "EVM wallet generation failed: {err}" ))), Err(err) => { error!(?err, "EVM actor unreachable during wallet create"); - Err(Error::internal("EVM actor unreachable")) + Err(UserAgentSessionError::internal("EVM actor unreachable")) } } } #[message] - pub(crate) async fn handle_evm_wallet_list(&mut self) -> Result, Error> { + pub(crate) async fn handle_evm_wallet_list( + &mut self, + ) -> Result, UserAgentSessionError> { match self.props.actors.evm.ask(ListWallets {}).await { Ok(wallets) => Ok(wallets), Err(err) => { error!(?err, "EVM wallet list failed"); - Err(Error::internal("Failed to list EVM wallets")) + Err(UserAgentSessionError::internal( + "Failed to list EVM wallets", + )) } } } @@ -325,12 +331,14 @@ impl UserAgentSession { #[messages] impl UserAgentSession { #[message] - pub(crate) async fn handle_grant_list(&mut self) -> Result>, Error> { + pub(crate) async fn handle_grant_list( + &mut self, + ) -> Result>, UserAgentSessionError> { match self.props.actors.evm.ask(UseragentListGrants {}).await { Ok(grants) => Ok(grants), Err(err) => { error!(?err, "EVM grant list failed"); - Err(Error::internal("Failed to list EVM grants")) + Err(UserAgentSessionError::internal("Failed to list EVM grants")) } } } @@ -338,8 +346,8 @@ impl UserAgentSession { #[message] pub(crate) async fn handle_grant_create( &mut self, - basic: crate::evm::policies::SharedGrantSettings, - grant: crate::evm::policies::SpecificGrant, + basic: SharedGrantSettings, + grant: SpecificGrant, ) -> Result { match self .props @@ -357,6 +365,7 @@ impl UserAgentSession { } #[message] + #[expect(clippy::unused_async, reason = "false positive")] pub(crate) async fn handle_grant_delete( &mut self, grant_id: i32, @@ -374,7 +383,7 @@ impl UserAgentSession { // Err(GrantMutationError::Internal) // } // } - let _ = grant_id; + let _ = grant_id; todo!() } @@ -411,7 +420,7 @@ impl UserAgentSession { pub(crate) async fn handle_grant_evm_wallet_access( &mut self, entries: Vec, - ) -> Result<(), Error> { + ) -> Result<(), UserAgentSessionError> { let mut conn = self.props.db.get().await?; conn.transaction(|conn| { Box::pin(async move { @@ -425,7 +434,7 @@ impl UserAgentSession { .await?; } - Result::<_, Error>::Ok(()) + Result::<_, UserAgentSessionError>::Ok(()) }) }) .await?; @@ -436,7 +445,7 @@ impl UserAgentSession { pub(crate) async fn handle_revoke_evm_wallet_access( &mut self, entries: Vec, - ) -> Result<(), Error> { + ) -> Result<(), UserAgentSessionError> { let mut conn = self.props.db.get().await?; conn.transaction(|conn| { Box::pin(async move { @@ -448,7 +457,7 @@ impl UserAgentSession { .await?; } - Result::<_, Error>::Ok(()) + Result::<_, UserAgentSessionError>::Ok(()) }) }) .await?; @@ -458,10 +467,9 @@ impl UserAgentSession { #[message] pub(crate) async fn handle_list_wallet_access( &mut self, - ) -> Result, Error> { + ) -> Result, UserAgentSessionError> { let mut conn = self.props.db.get().await?; - use crate::db::schema::evm_wallet_access; - let access_entries = evm_wallet_access::table + let access_entries = crate::db::schema::evm_wallet_access::table .select(EvmWalletAccess::as_select()) .load::<_>(&mut conn) .await?; @@ -476,14 +484,14 @@ impl UserAgentSession { &mut self, approved: bool, pubkey: authn::PublicKey, - ctx: &mut Context>, - ) -> Result<(), Error> { - let pending_approval = match self.pending_client_approvals.remove(&pubkey.to_bytes()) { - Some(approval) => approval, - None => { - error!("Received client connection response for unknown client"); - return Err(Error::internal("Unknown client in connection response")); - } + ctx: &Context>, + ) -> Result<(), UserAgentSessionError> { + let Some(pending_approval) = self.pending_client_approvals.remove(&pubkey.to_bytes()) + else { + error!("Received client connection response for unknown client"); + return Err(UserAgentSessionError::internal( + "Unknown client in connection response", + )); }; pending_approval @@ -495,7 +503,9 @@ impl UserAgentSession { ?err, "Failed to send client approval response to controller" ); - Error::internal("Failed to send client approval response to controller") + UserAgentSessionError::internal( + "Failed to send client approval response to controller", + ) })?; ctx.actor_ref().unlink(&pending_approval.controller).await; @@ -506,7 +516,7 @@ impl UserAgentSession { #[message] pub(crate) async fn handle_sdk_client_list( &mut self, - ) -> Result, Error> { + ) -> Result, UserAgentSessionError> { use crate::db::schema::{client_metadata, program_client}; let mut conn = self.props.db.get().await?; diff --git a/server/crates/arbiter-server/src/actors/user_agent/session/state.rs b/server/crates/arbiter-server/src/actors/user_agent/session/state.rs index 23ab674..4c57ad4 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/session/state.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/session/state.rs @@ -19,8 +19,6 @@ smlang::statemachine!( pub struct DummyContext; impl UserAgentStateMachineContext for DummyContext { - #[allow(missing_docs)] - #[allow(clippy::unused_unit)] fn generate_temp_keypair(&mut self, event_data: UnsealContext) -> Result { Ok(event_data) } diff --git a/server/crates/arbiter-server/src/context/mod.rs b/server/crates/arbiter-server/src/context/mod.rs index dd44655..820133c 100644 --- a/server/crates/arbiter-server/src/context/mod.rs +++ b/server/crates/arbiter-server/src/context/mod.rs @@ -25,22 +25,22 @@ pub enum InitError { Tls(#[from] tls::InitError), #[error("Actor spawn failed: {0}")] - ActorSpawn(#[from] crate::actors::SpawnError), + ActorSpawn(#[from] crate::actors::GlobalActorsSpawnError), #[error("I/O Error: {0}")] Io(#[from] std::io::Error), } -pub struct _ServerContextInner { +pub struct __ServerContextInner { pub db: db::DatabasePool, pub tls: TlsManager, pub actors: GlobalActors, } #[derive(Clone)] -pub struct ServerContext(Arc<_ServerContextInner>); +pub struct ServerContext(Arc<__ServerContextInner>); impl std::ops::Deref for ServerContext { - type Target = _ServerContextInner; + type Target = __ServerContextInner; fn deref(&self) -> &Self::Target { &self.0 @@ -49,7 +49,7 @@ impl std::ops::Deref for ServerContext { impl ServerContext { pub async fn new(db: db::DatabasePool) -> Result { - Ok(Self(Arc::new(_ServerContextInner { + Ok(Self(Arc::new(__ServerContextInner { actors: GlobalActors::spawn(db.clone()).await?, tls: TlsManager::new(db.clone()).await?, db, diff --git a/server/crates/arbiter-server/src/context/tls.rs b/server/crates/arbiter-server/src/context/tls.rs index 786b68f..4cceffa 100644 --- a/server/crates/arbiter-server/src/context/tls.rs +++ b/server/crates/arbiter-server/src/context/tls.rs @@ -22,9 +22,10 @@ use crate::db::{ }; const ENCODE_CONFIG: pem::EncodeConfig = { - let line_ending = match cfg!(target_family = "windows") { - true => pem::LineEnding::CRLF, - false => pem::LineEnding::LF, + let line_ending = if cfg!(target_family = "windows") { + pem::LineEnding::CRLF + } else { + pem::LineEnding::LF }; pem::EncodeConfig::new().set_line_ending(line_ending) }; @@ -52,11 +53,14 @@ pub enum InitError { pub type PemCert = String; -pub fn encode_cert_to_pem(cert: &CertificateDer) -> PemCert { +pub fn encode_cert_to_pem(cert: &CertificateDer<'_>) -> PemCert { pem::encode_config(&Pem::new("CERTIFICATE", cert.to_vec()), ENCODE_CONFIG) } -#[allow(unused)] +#[expect( + unused, + reason = "may be needed for future cert rotation implementation" +)] struct SerializedTls { cert_pem: PemCert, cert_key_pem: String, @@ -85,7 +89,7 @@ impl TlsCa { let cert_key_pem = certified_issuer.key().serialize_pem(); - #[allow( + #[expect( clippy::unwrap_used, reason = "Broken cert couldn't bootstrap server anyway" )] @@ -124,7 +128,11 @@ impl TlsCa { }) } - #[allow(unused)] + #[expect( + unused, + clippy::unnecessary_wraps, + reason = "may be needed for future cert rotation implementation" + )] fn serialize(&self) -> Result { let cert_key_pem = self.issuer.key().serialize_pem(); Ok(SerializedTls { @@ -133,7 +141,10 @@ impl TlsCa { }) } - #[allow(unused)] + #[expect( + unused, + reason = "may be needed for future cert rotation implementation" + )] fn try_deserialize(cert_pem: &str, cert_key_pem: &str) -> Result { let keypair = KeyPair::from_pem(cert_key_pem).map_err(InitError::KeyDeserializationError)?; @@ -234,10 +245,10 @@ impl TlsManager { } } - pub fn cert(&self) -> &CertificateDer<'static> { + pub const fn cert(&self) -> &CertificateDer<'static> { &self.cert } - pub fn ca_cert(&self) -> &CertificateDer<'static> { + pub const fn ca_cert(&self) -> &CertificateDer<'static> { &self.ca_cert } diff --git a/server/crates/arbiter-server/src/crypto/encryption/v1.rs b/server/crates/arbiter-server/src/crypto/encryption/v1.rs index e2b7c04..b909979 100644 --- a/server/crates/arbiter-server/src/crypto/encryption/v1.rs +++ b/server/crates/arbiter-server/src/crypto/encryption/v1.rs @@ -5,8 +5,8 @@ use rand::{ rngs::{StdRng, SysRng}, }; -pub const ROOT_KEY_TAG: &[u8] = "arbiter/seal/v1".as_bytes(); -pub const TAG: &[u8] = "arbiter/private-key/v1".as_bytes(); +pub const ROOT_KEY_TAG: &[u8] = b"arbiter/seal/v1"; +pub const TAG: &[u8] = b"arbiter/private-key/v1"; pub const NONCE_LENGTH: usize = 24; @@ -15,11 +15,13 @@ pub struct Nonce(pub [u8; NONCE_LENGTH]); impl Nonce { pub fn increment(&mut self) { for i in (0..self.0.len()).rev() { - if self.0[i] == 0xFF { - self.0[i] = 0; - } else { - self.0[i] += 1; - break; + if let Some(byte) = self.0.get_mut(i) { + if *byte == 0xFF { + *byte = 0; + } else { + *byte += 1; + break; + } } } } @@ -45,19 +47,14 @@ pub type Salt = [u8; ArgonSalt::RECOMMENDED_LENGTH]; pub fn generate_salt() -> Salt { let mut salt = Salt::default(); - #[allow( - clippy::unwrap_used, - reason = "Rng failure is unrecoverable and should panic" - )] - let mut rng = StdRng::try_from_rng(&mut SysRng).unwrap(); + let mut rng = + StdRng::try_from_rng(&mut SysRng).expect("Rng failure is unrecoverable and should panic"); rng.fill_bytes(&mut salt); salt } #[cfg(test)] mod tests { - use std::ops::Deref as _; - use super::*; use crate::crypto::derive_key; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; @@ -75,7 +72,7 @@ mod tests { let key1_reader = key1.0.read(); let key2_reader = key2.0.read(); - assert_eq!(key1_reader.deref(), key2_reader.deref()); + assert_eq!(&*key1_reader, &*key2_reader); } #[test] @@ -86,14 +83,13 @@ mod tests { let mut key = derive_key(password, &salt); let key_reader = key.0.read(); - let key_ref = key_reader.deref(); - assert_ne!(key_ref.as_slice(), &[0u8; 32][..]); + assert_ne!(key_reader.as_slice(), &[0u8; 32][..]); } #[test] // We should fuzz this - pub fn test_nonce_increment() { + pub fn nonce_increment() { let mut nonce = Nonce([0u8; NONCE_LENGTH]); nonce.increment(); diff --git a/server/crates/arbiter-server/src/crypto/integrity/v1.rs b/server/crates/arbiter-server/src/crypto/integrity/v1.rs index 1b89df8..3908a3f 100644 --- a/server/crates/arbiter-server/src/crypto/integrity/v1.rs +++ b/server/crates/arbiter-server/src/crypto/integrity/v1.rs @@ -18,12 +18,12 @@ use crate::{ }; #[derive(Debug, thiserror::Error)] -pub enum Error { +pub enum IntegrityError { #[error("Database error: {0}")] Database(#[from] db::DatabaseError), #[error("KeyHolder error: {0}")] - Keyholder(#[from] keyholder::Error), + Keyholder(#[from] keyholder::KeyHolderError), #[error("KeyHolder mailbox error")] KeyholderSend, @@ -67,6 +67,11 @@ fn payload_hash(payload: &impl Hashable) -> [u8; 32] { } fn push_len_prefixed(out: &mut Vec, bytes: &[u8]) { + #[expect( + clippy::cast_possible_truncation, + clippy::as_conversions, + reason = "fixme! #85" + )] out.extend_from_slice(&(bytes.len() as u32).to_be_bytes()); out.extend_from_slice(bytes); } @@ -106,7 +111,7 @@ pub async fn sign_entity( keyholder: &ActorRef, entity: &E, entity_id: impl IntoId, -) -> Result<(), Error> { +) -> Result<(), IntegrityError> { let payload_hash = payload_hash(&entity); let entity_id = entity_id.into_id(); @@ -117,8 +122,8 @@ pub async fn sign_entity( .ask(SignIntegrity { mac_input }) .await .map_err(|err| match err { - kameo::error::SendError::HandlerError(inner) => Error::Keyholder(inner), - _ => Error::KeyholderSend, + SendError::HandlerError(inner) => IntegrityError::Keyholder(inner), + _ => IntegrityError::KeyholderSend, })?; insert_into(integrity_envelope::table) @@ -127,7 +132,7 @@ pub async fn sign_entity( entity_id, payload_version: E::VERSION, key_version, - mac: mac.to_vec(), + mac: mac.clone(), }) .on_conflict(( integrity_envelope::entity_id, @@ -151,7 +156,7 @@ pub async fn verify_entity( keyholder: &ActorRef, entity: &E, entity_id: impl IntoId, -) -> Result { +) -> Result { let entity_id = entity_id.into_id(); let envelope: IntegrityEnvelope = integrity_envelope::table .filter(integrity_envelope::entity_kind.eq(E::KIND)) @@ -159,14 +164,14 @@ pub async fn verify_entity( .first(conn) .await .map_err(|err| match err { - diesel::result::Error::NotFound => Error::MissingEnvelope { + diesel::result::Error::NotFound => IntegrityError::MissingEnvelope { entity_kind: E::KIND, }, - other => Error::Database(db::DatabaseError::from(other)), + other => IntegrityError::Database(db::DatabaseError::from(other)), })?; if envelope.payload_version != E::VERSION { - return Err(Error::PayloadVersionMismatch { + return Err(IntegrityError::PayloadVersionMismatch { entity_kind: E::KIND, expected: E::VERSION, found: envelope.payload_version, @@ -186,13 +191,13 @@ pub async fn verify_entity( match result { Ok(true) => Ok(AttestationStatus::Attested), - Ok(false) => Err(Error::MacMismatch { + Ok(false) => Err(IntegrityError::MacMismatch { entity_kind: E::KIND, }), - Err(SendError::HandlerError(keyholder::Error::NotBootstrapped)) => { + Err(SendError::HandlerError(keyholder::KeyHolderError::NotBootstrapped)) => { Ok(AttestationStatus::Unavailable) } - Err(_) => Err(Error::KeyholderSend), + Err(_) => Err(IntegrityError::KeyholderSend), } } @@ -208,7 +213,7 @@ mod tests { }; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; - use super::{Error, Integrable, sign_entity, verify_entity}; + use super::{Integrable, IntegrityError, sign_entity, verify_entity}; #[derive(Clone, arbiter_macros::Hashable)] struct DummyEntity { payload_version: i32, @@ -231,12 +236,12 @@ mod tests { #[tokio::test] async fn sign_writes_envelope_and_verify_passes() { + const ENTITY_ID: &[u8] = b"entity-id-7"; + let db = db::create_test_pool().await; let keyholder = bootstrapped_keyholder(&db).await; let mut conn = db.get().await.unwrap(); - const ENTITY_ID: &[u8] = b"entity-id-7"; - let entity = DummyEntity { payload_version: 1, payload: b"payload-v1".to_vec(), @@ -262,12 +267,12 @@ mod tests { #[tokio::test] async fn tampered_mac_fails_verification() { + const ENTITY_ID: &[u8] = b"entity-id-11"; + let db = db::create_test_pool().await; let keyholder = bootstrapped_keyholder(&db).await; let mut conn = db.get().await.unwrap(); - const ENTITY_ID: &[u8] = b"entity-id-11"; - let entity = DummyEntity { payload_version: 1, payload: b"payload-v1".to_vec(), @@ -288,17 +293,17 @@ mod tests { let err = verify_entity(&mut conn, &keyholder, &entity, ENTITY_ID) .await .unwrap_err(); - assert!(matches!(err, Error::MacMismatch { .. })); + assert!(matches!(err, IntegrityError::MacMismatch { .. })); } #[tokio::test] async fn changed_payload_fails_verification() { + const ENTITY_ID: &[u8] = b"entity-id-21"; + let db = db::create_test_pool().await; let keyholder = bootstrapped_keyholder(&db).await; let mut conn = db.get().await.unwrap(); - const ENTITY_ID: &[u8] = b"entity-id-21"; - let entity = DummyEntity { payload_version: 1, payload: b"payload-v1".to_vec(), @@ -316,6 +321,6 @@ mod tests { let err = verify_entity(&mut conn, &keyholder, &tampered, ENTITY_ID) .await .unwrap_err(); - assert!(matches!(err, Error::MacMismatch { .. })); + assert!(matches!(err, IntegrityError::MacMismatch { .. })); } } diff --git a/server/crates/arbiter-server/src/crypto/mod.rs b/server/crates/arbiter-server/src/crypto/mod.rs index 5a11898..210433f 100644 --- a/server/crates/arbiter-server/src/crypto/mod.rs +++ b/server/crates/arbiter-server/src/crypto/mod.rs @@ -1,5 +1,3 @@ -use std::ops::Deref as _; - use argon2::{Algorithm, Argon2}; use chacha20poly1305::{ AeadInPlace, Key, KeyInit as _, XChaCha20Poly1305, XNonce, @@ -41,11 +39,8 @@ impl TryFrom>> for KeyCell { impl KeyCell { pub fn new_secure_random() -> Self { let key = SafeCell::new_inline(|key_buffer: &mut Key| { - #[allow( - clippy::unwrap_used, - reason = "Rng failure is unrecoverable and should panic" - )] - let mut rng = StdRng::try_from_rng(&mut SysRng).unwrap(); + let mut rng = StdRng::try_from_rng(&mut SysRng) + .expect("Rng failure is unrecoverable and should panic"); rng.fill_bytes(key_buffer); }); @@ -59,8 +54,7 @@ impl KeyCell { mut buffer: impl AsMut>, ) -> Result<(), Error> { let key_reader = self.0.read(); - let key_ref = key_reader.deref(); - let cipher = XChaCha20Poly1305::new(key_ref); + let cipher = XChaCha20Poly1305::new(&key_reader); let nonce = XNonce::from_slice(nonce.0.as_ref()); let buffer = buffer.as_mut(); cipher.encrypt_in_place(nonce, associated_data, buffer) @@ -72,8 +66,7 @@ impl KeyCell { buffer: &mut SafeCell>, ) -> Result<(), Error> { let key_reader = self.0.read(); - let key_ref = key_reader.deref(); - let cipher = XChaCha20Poly1305::new(key_ref); + let cipher = XChaCha20Poly1305::new(&key_reader); let nonce = XNonce::from_slice(nonce.0.as_ref()); let mut buffer = buffer.write(); let buffer: &mut Vec = buffer.as_mut(); @@ -87,8 +80,7 @@ impl KeyCell { plaintext: impl AsRef<[u8]>, ) -> Result, Error> { let key_reader = self.0.read(); - let key_ref = key_reader.deref(); - let mut cipher = XChaCha20Poly1305::new(key_ref); + let mut cipher = XChaCha20Poly1305::new(&key_reader); let nonce = XNonce::from_slice(nonce.0.as_ref()); let ciphertext = cipher.encrypt( @@ -116,20 +108,15 @@ pub fn derive_key(mut password: SafeCell>, salt: &Salt) -> KeyCell { } }; - #[allow(clippy::unwrap_used)] let hasher = Argon2::new(Algorithm::Argon2id, argon2::Version::V0x13, params); let mut key = SafeCell::new(Key::default()); password.read_inline(|password_source| { let mut key_buffer = key.write(); let key_buffer: &mut [u8] = key_buffer.as_mut(); - #[allow( - clippy::unwrap_used, - reason = "Better fail completely than return a weak key" - )] hasher - .hash_password_into(password_source.deref(), salt, key_buffer) - .unwrap(); + .hash_password_into(password_source, salt, key_buffer) + .expect("Better fail completely than return a weak key"); }); key.into() diff --git a/server/crates/arbiter-server/src/db/mod.rs b/server/crates/arbiter-server/src/db/mod.rs index 9971ad2..1c5012c 100644 --- a/server/crates/arbiter-server/src/db/mod.rs +++ b/server/crates/arbiter-server/src/db/mod.rs @@ -23,14 +23,14 @@ const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); #[derive(Error, Debug)] pub enum DatabaseSetupError { - #[error("Failed to determine home directory")] - HomeDir(std::io::Error), + #[error(transparent)] + ConcurrencySetup(diesel::result::Error), #[error(transparent)] Connection(diesel::ConnectionError), - #[error(transparent)] - ConcurrencySetup(diesel::result::Error), + #[error("Failed to determine home directory")] + HomeDir(std::io::Error), #[error(transparent)] Migration(Box), @@ -41,10 +41,11 @@ pub enum DatabaseSetupError { #[derive(Error, Debug)] pub enum DatabaseError { - #[error("Database connection error")] - Pool(#[from] PoolError), #[error("Database query error")] Connection(#[from] diesel::result::Error), + + #[error("Database connection error")] + Pool(#[from] PoolError), } #[tracing::instrument(level = "info")] @@ -93,13 +94,16 @@ fn initialize_database(url: &str) -> Result<(), DatabaseSetupError> { } #[tracing::instrument(level = "info")] +/// Creates a connection pool for the `SQLite` database. +/// +/// # Panics +/// Panics if the database path is not valid UTF-8. pub async fn create_pool(url: Option<&str>) -> Result { let database_url = url.map(String::from).unwrap_or( - #[allow(clippy::expect_used)] database_path()? .to_str() .expect("database path is not valid UTF-8") - .to_string(), + .to_owned(), ); initialize_database(&database_url)?; @@ -134,19 +138,19 @@ pub async fn create_pool(url: Option<&str>) -> Result DatabasePool { use rand::distr::{Alphanumeric, SampleString as _}; let tempfile_name = Alphanumeric.sample_string(&mut rand::rng(), 16); let file = std::env::temp_dir().join(tempfile_name); - #[allow(clippy::expect_used)] let url = file .to_str() .expect("temp file path is not valid UTF-8") - .to_string(); + .to_owned(); - #[allow(clippy::expect_used)] create_pool(Some(&url)) .await .expect("Failed to create test database pool") diff --git a/server/crates/arbiter-server/src/db/models.rs b/server/crates/arbiter-server/src/db/models.rs index dba14dc..1b55804 100644 --- a/server/crates/arbiter-server/src/db/models.rs +++ b/server/crates/arbiter-server/src/db/models.rs @@ -1,5 +1,7 @@ -#![allow(unused)] -#![allow(clippy::all)] +#![allow( + clippy::duplicated_attributes, + reason = "restructed's #[view] causes false positives" +)] use crate::db::schema::{ self, aead_encrypted, arbiter_settings, evm_basic_grant, evm_ether_transfer_grant, @@ -7,7 +9,6 @@ use crate::db::schema::{ evm_token_transfer_log, evm_token_transfer_volume_limit, evm_transaction_log, evm_wallet, integrity_envelope, root_key_history, tls_history, }; -use chrono::{DateTime, Utc}; use diesel::{prelude::*, sqlite::Sqlite}; use restructed::Models; @@ -27,16 +28,16 @@ pub mod types { pub struct SqliteTimestamp(pub DateTime); impl SqliteTimestamp { pub fn now() -> Self { - SqliteTimestamp(Utc::now()) + Self(Utc::now()) } } - impl From> for SqliteTimestamp { - fn from(dt: chrono::DateTime) -> Self { - SqliteTimestamp(dt) + impl From> for SqliteTimestamp { + fn from(dt: DateTime) -> Self { + Self(dt) } } - impl From for chrono::DateTime { + impl From for DateTime { fn from(ts: SqliteTimestamp) -> Self { ts.0 } @@ -47,6 +48,11 @@ pub mod types { &'b self, out: &mut diesel::serialize::Output<'b, '_, Sqlite>, ) -> diesel::serialize::Result { + #[expect( + clippy::cast_possible_truncation, + clippy::as_conversions, + reason = "fixme! #84; this will break up in 2038 :3" + )] let unix_timestamp = self.0.timestamp() as i32; out.set_value(unix_timestamp); Ok(IsNull::No) @@ -69,7 +75,47 @@ pub mod types { let datetime = DateTime::from_timestamp(unix_timestamp, 0).ok_or("Timestamp is out of bounds")?; - Ok(SqliteTimestamp(datetime)) + Ok(Self(datetime)) + } + } + + #[derive(Debug, FromSqlRow, AsExpression, Clone)] + #[diesel(sql_type = Integer)] + #[repr(transparent)] // hint compiler to optimize the wrapper struct away + pub struct ChainId(pub i32); + + #[expect( + clippy::cast_sign_loss, + clippy::cast_possible_truncation, + clippy::as_conversions, + reason = "safe because chain_id is stored as i32 but is guaranteed to be a valid ChainId by the API when creating grants" + )] + const _: () = { + impl From for alloy::primitives::ChainId { + fn from(chain_id: ChainId) -> Self { + chain_id.0 as Self + } + } + impl From for ChainId { + fn from(chain_id: alloy::primitives::ChainId) -> Self { + Self(chain_id as _) + } + } + }; + + impl FromSql for ChainId { + fn from_sql( + bytes: ::RawValue<'_>, + ) -> diesel::deserialize::Result { + FromSql::::from_sql(bytes).map(Self) + } + } + impl ToSql for ChainId { + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, Sqlite>, + ) -> diesel::serialize::Result { + ToSql::::to_sql(&self.0, out) } } } @@ -237,7 +283,7 @@ pub struct EvmEtherTransferLimit { pub struct EvmBasicGrant { pub id: i32, pub wallet_access_id: i32, // references evm_wallet_access.id - pub chain_id: i32, + pub chain_id: ChainId, pub valid_from: Option, pub valid_until: Option, pub max_gas_fee_per_gas: Option>, @@ -260,7 +306,7 @@ pub struct EvmTransactionLog { pub id: i32, pub grant_id: i32, pub wallet_access_id: i32, - pub chain_id: i32, + pub chain_id: ChainId, pub eth_value: Vec, pub signed_at: SqliteTimestamp, } @@ -335,7 +381,7 @@ pub struct EvmTokenTransferLog { pub id: i32, pub grant_id: i32, pub log_id: i32, - pub chain_id: i32, + pub chain_id: ChainId, pub token_contract: Vec, pub recipient_address: Vec, pub value: Vec, diff --git a/server/crates/arbiter-server/src/evm/abi.rs b/server/crates/arbiter-server/src/evm/abi.rs index cef8d9f..561ce34 100644 --- a/server/crates/arbiter-server/src/evm/abi.rs +++ b/server/crates/arbiter-server/src/evm/abi.rs @@ -45,7 +45,7 @@ sol! { sol! { /// Permit2 — Uniswap's canonical token approval manager. - /// Replaces per-contract ERC-20 approve() with a single approval hub. + /// Replaces per-contract ERC-20 `approve()` with a single approval hub. #[derive(Debug)] interface IPermit2 { struct TokenPermissions { diff --git a/server/crates/arbiter-server/src/evm/mod.rs b/server/crates/arbiter-server/src/evm/mod.rs index 15ac999..c68cdea 100644 --- a/server/crates/arbiter-server/src/evm/mod.rs +++ b/server/crates/arbiter-server/src/evm/mod.rs @@ -34,14 +34,14 @@ mod utils; #[derive(Debug, thiserror::Error)] pub enum PolicyError { #[error("Database error")] - Database(#[from] crate::db::DatabaseError), + Database(#[from] DatabaseError), #[error("Transaction violates policy: {0:?}")] Violations(Vec), #[error("No matching grant found")] NoMatchingGrant, #[error("Integrity error: {0}")] - Integrity(#[from] integrity::Error), + Integrity(#[from] integrity::IntegrityError), } #[derive(Debug, thiserror::Error)] @@ -66,10 +66,10 @@ pub enum AnalyzeError { #[derive(Debug, thiserror::Error)] pub enum ListError { #[error("Database error")] - Database(#[from] crate::db::DatabaseError), + Database(#[from] DatabaseError), #[error("Integrity verification failed for grant")] - Integrity(#[from] integrity::Error), + Integrity(#[from] integrity::IntegrityError), } /// Controls whether a transaction should be executed or only validated @@ -127,7 +127,7 @@ async fn check_shared_constraints( .get_result(conn) .await?; - if count >= rate_limit.count as i64 { + if count >= rate_limit.count.into() { violations.push(EvalViolation::RateLimitExceeded); } } @@ -185,7 +185,7 @@ impl Engine { .values(&NewEvmTransactionLog { grant_id: grant.common_settings_id, wallet_access_id: context.target.id, - chain_id: context.chain as i32, + chain_id: context.chain.into(), eth_value: utils::u256_to_bytes(context.value).to_vec(), signed_at: Utc::now().into(), }) @@ -207,7 +207,7 @@ impl Engine { } impl Engine { - pub fn new(db: db::DatabasePool, keyholder: ActorRef) -> Self { + pub const fn new(db: db::DatabasePool, keyholder: ActorRef) -> Self { Self { db, keyholder } } @@ -226,9 +226,15 @@ impl Engine { Box::pin(async move { use schema::evm_basic_grant; + #[expect( + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::as_conversions, + reason = "fixme! #86" + )] let basic_grant: EvmBasicGrant = insert_into(evm_basic_grant::table) .values(&NewEvmBasicGrant { - chain_id: full_grant.shared.chain as i32, + chain_id: full_grant.shared.chain.into(), wallet_access_id: full_grant.shared.wallet_access_id, valid_from: full_grant.shared.valid_from.map(SqliteTimestamp), valid_until: full_grant.shared.valid_until.map(SqliteTimestamp), @@ -313,7 +319,7 @@ impl Engine { let TxKind::Call(to) = transaction.to else { return Err(VetError::ContractCreationNotSupported); }; - let context = policies::EvalContext { + let context = EvalContext { target, chain: transaction.chain_id, to, @@ -404,10 +410,16 @@ mod tests { conn: &mut DatabaseConnection, shared: &SharedGrantSettings, ) -> EvmBasicGrant { + #[expect( + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::as_conversions, + reason = "fixme! #86" + )] insert_into(evm_basic_grant::table) .values(NewEvmBasicGrant { wallet_access_id: shared.wallet_access_id, - chain_id: shared.chain as i32, + chain_id: shared.chain.into(), valid_from: shared.valid_from.map(SqliteTimestamp), valid_until: shared.valid_until.map(SqliteTimestamp), max_gas_fee_per_gas: shared @@ -571,7 +583,7 @@ mod tests { .values(NewEvmTransactionLog { grant_id: basic_grant.id, wallet_access_id: WALLET_ACCESS_ID, - chain_id: CHAIN_ID as i32, + chain_id: CHAIN_ID.into(), eth_value: super::utils::u256_to_bytes(U256::ZERO).to_vec(), signed_at: SqliteTimestamp(Utc::now()), }) diff --git a/server/crates/arbiter-server/src/evm/policies.rs b/server/crates/arbiter-server/src/evm/policies.rs index 828c52e..e64a368 100644 --- a/server/crates/arbiter-server/src/evm/policies.rs +++ b/server/crates/arbiter-server/src/evm/policies.rs @@ -11,7 +11,7 @@ use thiserror::Error; use crate::{ crypto::integrity::v1::Integrable, - db::models::{self, EvmBasicGrant, EvmWalletAccess}, + db::models::{EvmBasicGrant, EvmWalletAccess}, evm::utils, }; @@ -87,10 +87,10 @@ pub trait Policy: Sized { // Create a new grant in the database based on the provided grant details, and return its ID fn create_grant( - basic: &models::EvmBasicGrant, + basic: &EvmBasicGrant, grant: &Self::Settings, conn: &mut impl AsyncConnection, - ) -> impl std::future::Future> + Send; + ) -> impl Future> + Send; // Try to find an existing grant that matches the transaction context, and return its details if found // Additionally, return ID of basic grant for shared-logic checks like rate limits and validity periods @@ -157,7 +157,7 @@ impl SharedGrantSettings { pub(crate) fn try_from_model(model: EvmBasicGrant) -> QueryResult { Ok(Self { wallet_access_id: model.wallet_access_id, - chain: model.chain_id as u64, // safe because chain_id is stored as i32 but is guaranteed to be a valid ChainId by the API when creating grants + chain: model.chain_id.into(), valid_from: model.valid_from.map(Into::into), valid_until: model.valid_until.map(Into::into), max_gas_fee_per_gas: model @@ -168,10 +168,11 @@ impl SharedGrantSettings { .max_priority_fee_per_gas .map(|b| utils::try_bytes_to_u256(&b)) .transpose()?, + #[expect(clippy::cast_sign_loss, clippy::as_conversions, reason = "fixme! #86")] rate_limit: match (model.rate_limit_count, model.rate_limit_window_secs) { (Some(count), Some(window_secs)) => Some(TransactionRateLimit { count: count as u32, - window: Duration::seconds(window_secs as i64), + window: Duration::seconds(window_secs.into()), }), _ => None, }, @@ -181,7 +182,7 @@ impl SharedGrantSettings { pub async fn query_by_id( conn: &mut impl AsyncConnection, id: i32, - ) -> diesel::result::QueryResult { + ) -> QueryResult { use crate::db::schema::evm_basic_grant; let basic_grant: EvmBasicGrant = evm_basic_grant::table diff --git a/server/crates/arbiter-server/src/evm/policies/ether_transfer/mod.rs b/server/crates/arbiter-server/src/evm/policies/ether_transfer/mod.rs index 3fa507b..8d23c06 100644 --- a/server/crates/arbiter-server/src/evm/policies/ether_transfer/mod.rs +++ b/server/crates/arbiter-server/src/evm/policies/ether_transfer/mod.rs @@ -4,8 +4,8 @@ use std::fmt::Display; use alloy::primitives::{Address, U256}; use chrono::{DateTime, Duration, Utc}; use diesel::dsl::{auto_type, insert_into}; +use diesel::prelude::*; use diesel::sqlite::Sqlite; -use diesel::{ExpressionMethods, JoinOnDsl, prelude::*}; use diesel_async::{AsyncConnection, RunQueryDsl}; use crate::crypto::integrity::v1::Integrable; @@ -19,7 +19,7 @@ use crate::evm::policies::{ }; use crate::{ db::{ - models::{self, NewEvmEtherTransferGrant, NewEvmEtherTransferGrantTarget}, + models::{NewEvmEtherTransferGrant, NewEvmEtherTransferGrantTarget}, schema::{evm_ether_transfer_grant, evm_ether_transfer_grant_target}, }, evm::{policies::Policy, utils}, @@ -46,8 +46,8 @@ impl Display for Meaning { } } impl From for SpecificMeaning { - fn from(val: Meaning) -> SpecificMeaning { - SpecificMeaning::EtherTransfer(val) + fn from(val: Meaning) -> Self { + Self::EtherTransfer(val) } } @@ -62,8 +62,8 @@ impl Integrable for Settings { } impl From for SpecificGrant { - fn from(val: Settings) -> SpecificGrant { - SpecificGrant::EtherTransfer(val) + fn from(val: Settings) -> Self { + Self::EtherTransfer(val) } } @@ -74,9 +74,7 @@ async fn query_relevant_past_transaction( ) -> QueryResult)>> { let past_transactions: Vec<(Vec, SqliteTimestamp)> = evm_transaction_log::table .filter(evm_transaction_log::grant_id.eq(grant_id)) - .filter( - evm_transaction_log::signed_at.ge(SqliteTimestamp(chrono::Utc::now() - longest_window)), - ) + .filter(evm_transaction_log::signed_at.ge(SqliteTimestamp(Utc::now() - longest_window))) .select(( evm_transaction_log::eth_value, evm_transaction_log::signed_at, @@ -103,7 +101,7 @@ async fn check_rate_limits( let past_transaction = query_relevant_past_transaction(grant.id, window, db).await?; - let window_start = chrono::Utc::now() - grant.settings.specific.limit.window; + let window_start = Utc::now() - grant.settings.specific.limit.window; let prospective_cumulative_volume: U256 = past_transaction .iter() .filter(|(_, timestamp)| timestamp >= &window_start) @@ -153,10 +151,15 @@ impl Policy for EtherTransfer { } async fn create_grant( - basic: &models::EvmBasicGrant, + basic: &EvmBasicGrant, grant: &Self::Settings, conn: &mut impl AsyncConnection, - ) -> diesel::result::QueryResult { + ) -> QueryResult { + #[expect( + clippy::cast_possible_truncation, + clippy::as_conversions, + reason = "fixme! #86" + )] let limit_id: i32 = insert_into(evm_ether_transfer_limit::table) .values(NewEvmEtherTransferLimit { window_secs: grant.limit.window.num_seconds() as i32, @@ -191,7 +194,7 @@ impl Policy for EtherTransfer { async fn try_find_grant( context: &EvalContext, conn: &mut impl AsyncConnection, - ) -> diesel::result::QueryResult>> { + ) -> QueryResult>> { let target_bytes = context.to.to_vec(); // Find a grant where: @@ -245,7 +248,7 @@ impl Policy for EtherTransfer { limit: VolumeRateLimit { max_volume: utils::try_bytes_to_u256(&limit.max_volume) .map_err(|err| diesel::result::Error::DeserializationError(Box::new(err)))?, - window: chrono::Duration::seconds(limit.window_secs as i64), + window: Duration::seconds(limit.window_secs.into()), }, }; @@ -265,7 +268,7 @@ impl Policy for EtherTransfer { _log_id: i32, _grant: &Grant, _conn: &mut impl AsyncConnection, - ) -> diesel::result::QueryResult<()> { + ) -> QueryResult<()> { // Basic log is sufficient Ok(()) @@ -318,7 +321,7 @@ impl Policy for EtherTransfer { .map(|(basic, specific)| { let targets: Vec
= targets_by_grant .get(&specific.id) - .map(|v| v.as_slice()) + .map(Vec::as_slice) .unwrap_or_default() .iter() .filter_map(|t| { @@ -342,7 +345,7 @@ impl Policy for EtherTransfer { max_volume: utils::try_bytes_to_u256(&limit.max_volume).map_err( |e| diesel::result::Error::DeserializationError(Box::new(e)), )?, - window: Duration::seconds(limit.window_secs as i64), + window: Duration::seconds(limit.window_secs.into()), }, }, }, diff --git a/server/crates/arbiter-server/src/evm/policies/ether_transfer/tests.rs b/server/crates/arbiter-server/src/evm/policies/ether_transfer/tests.rs index 5253a25..9f3e603 100644 --- a/server/crates/arbiter-server/src/evm/policies/ether_transfer/tests.rs +++ b/server/crates/arbiter-server/src/evm/policies/ether_transfer/tests.rs @@ -21,7 +21,7 @@ use crate::evm::{ use super::{EtherTransfer, Settings}; const WALLET_ACCESS_ID: i32 = 1; -const CHAIN_ID: u64 = 1; +const CHAIN_ID: alloy::primitives::ChainId = 1; const ALLOWED: Address = address!("1111111111111111111111111111111111111111"); const OTHER: Address = address!("2222222222222222222222222222222222222222"); @@ -47,7 +47,7 @@ async fn insert_basic(conn: &mut DatabaseConnection, revoked: bool) -> EvmBasicG insert_into(evm_basic_grant::table) .values(NewEvmBasicGrant { wallet_access_id: WALLET_ACCESS_ID, - chain_id: CHAIN_ID as i32, + chain_id: CHAIN_ID.into(), valid_from: None, valid_until: None, max_gas_fee_per_gas: None, @@ -160,7 +160,7 @@ async fn evaluate_passes_when_volume_within_limit() { .values(NewEvmTransactionLog { grant_id, wallet_access_id: WALLET_ACCESS_ID, - chain_id: CHAIN_ID as i32, + chain_id: CHAIN_ID.into(), eth_value: utils::u256_to_bytes(U256::from(500u64)).to_vec(), signed_at: SqliteTimestamp(Utc::now()), }) @@ -202,7 +202,7 @@ async fn evaluate_rejects_volume_over_limit() { .values(NewEvmTransactionLog { grant_id, wallet_access_id: WALLET_ACCESS_ID, - chain_id: CHAIN_ID as i32, + chain_id: CHAIN_ID.into(), eth_value: utils::u256_to_bytes(U256::from(1_000u64)).to_vec(), signed_at: SqliteTimestamp(Utc::now()), }) @@ -245,7 +245,7 @@ async fn evaluate_passes_at_exactly_volume_limit() { .values(NewEvmTransactionLog { grant_id, wallet_access_id: WALLET_ACCESS_ID, - chain_id: CHAIN_ID as i32, + chain_id: CHAIN_ID.into(), eth_value: utils::u256_to_bytes(U256::from(900u64)).to_vec(), signed_at: SqliteTimestamp(Utc::now()), }) diff --git a/server/crates/arbiter-server/src/evm/policies/token_transfers/mod.rs b/server/crates/arbiter-server/src/evm/policies/token_transfers/mod.rs index f540c82..7318a50 100644 --- a/server/crates/arbiter-server/src/evm/policies/token_transfers/mod.rs +++ b/server/crates/arbiter-server/src/evm/policies/token_transfers/mod.rs @@ -27,8 +27,8 @@ use alloy::{ use arbiter_tokens_registry::evm::nonfungible::{self, TokenInfo}; use chrono::{DateTime, Duration, Utc}; use diesel::dsl::{auto_type, insert_into}; +use diesel::prelude::*; use diesel::sqlite::Sqlite; -use diesel::{ExpressionMethods, prelude::*}; use diesel_async::{AsyncConnection, RunQueryDsl}; use super::{DatabaseID, EvalContext, EvalViolation}; @@ -56,8 +56,8 @@ impl std::fmt::Display for Meaning { } } impl From for SpecificMeaning { - fn from(val: Meaning) -> SpecificMeaning { - SpecificMeaning::TokenTransfer(val) + fn from(val: Meaning) -> Self { + Self::TokenTransfer(val) } } @@ -73,8 +73,8 @@ impl Integrable for Settings { } impl From for SpecificGrant { - fn from(val: Settings) -> SpecificGrant { - SpecificGrant::TokenTransfer(val) + fn from(val: Settings) -> Self { + Self::TokenTransfer(val) } } @@ -85,10 +85,7 @@ async fn query_relevant_past_transfers( ) -> QueryResult)>> { let past_logs: Vec<(Vec, SqliteTimestamp)> = evm_token_transfer_log::table .filter(evm_token_transfer_log::grant_id.eq(grant_id)) - .filter( - evm_token_transfer_log::created_at - .ge(SqliteTimestamp(chrono::Utc::now() - longest_window)), - ) + .filter(evm_token_transfer_log::created_at.ge(SqliteTimestamp(Utc::now() - longest_window))) .select(( evm_token_transfer_log::value, evm_token_transfer_log::created_at, @@ -128,7 +125,7 @@ async fn check_volume_rate_limits( let past_transfers = query_relevant_past_transfers(grant.id, longest_window, db).await?; for limit in &grant.settings.specific.volume_limits { - let window_start = chrono::Utc::now() - limit.window; + let window_start = Utc::now() - limit.window; let prospective_cumulative_volume: U256 = past_transfers .iter() .filter(|(_, timestamp)| timestamp >= &window_start) @@ -204,6 +201,11 @@ impl Policy for TokenTransfer { .await?; for limit in &grant.volume_limits { + #[expect( + clippy::cast_possible_truncation, + clippy::as_conversions, + reason = "fixme! #86" + )] insert_into(evm_token_transfer_volume_limit::table) .values(NewEvmTokenTransferVolumeLimit { grant_id, @@ -253,7 +255,7 @@ impl Policy for TokenTransfer { max_volume: utils::try_bytes_to_u256(&row.max_volume).map_err(|err| { diesel::result::Error::DeserializationError(Box::new(err)) })?, - window: Duration::seconds(row.window_secs as i64), + window: Duration::seconds(row.window_secs.into()), }) }) .collect::>>()?; @@ -303,7 +305,7 @@ impl Policy for TokenTransfer { .values(NewEvmTokenTransferLog { grant_id: grant.id, log_id, - chain_id: context.chain as i32, + chain_id: context.chain.into(), token_contract: context.to.to_vec(), recipient_address: meaning.to.to_vec(), value: utils::u256_to_bytes(meaning.value).to_vec(), @@ -352,7 +354,7 @@ impl Policy for TokenTransfer { .map(|(basic, specific)| { let volume_limits: Vec = limits_by_grant .get(&specific.id) - .map(|v| v.as_slice()) + .map(Vec::as_slice) .unwrap_or_default() .iter() .map(|row| { @@ -360,7 +362,7 @@ impl Policy for TokenTransfer { max_volume: utils::try_bytes_to_u256(&row.max_volume).map_err(|e| { diesel::result::Error::DeserializationError(Box::new(e)) })?, - window: Duration::seconds(row.window_secs as i64), + window: Duration::seconds(row.window_secs.into()), }) }) .collect::>>()?; diff --git a/server/crates/arbiter-server/src/evm/policies/token_transfers/tests.rs b/server/crates/arbiter-server/src/evm/policies/token_transfers/tests.rs index c059b0b..ad6ad71 100644 --- a/server/crates/arbiter-server/src/evm/policies/token_transfers/tests.rs +++ b/server/crates/arbiter-server/src/evm/policies/token_transfers/tests.rs @@ -59,7 +59,7 @@ async fn insert_basic(conn: &mut DatabaseConnection, revoked: bool) -> EvmBasicG insert_into(evm_basic_grant::table) .values(NewEvmBasicGrant { wallet_access_id: WALLET_ACCESS_ID, - chain_id: CHAIN_ID as i32, + chain_id: CHAIN_ID.into(), valid_from: None, valid_until: None, max_gas_fee_per_gas: None, @@ -238,12 +238,11 @@ async fn evaluate_passes_volume_at_exact_limit() { .unwrap(); // Record a past transfer of 900, with current transfer 100 => exactly 1000 limit - use crate::db::{models::NewEvmTokenTransferLog, schema::evm_token_transfer_log}; - insert_into(evm_token_transfer_log::table) - .values(NewEvmTokenTransferLog { + insert_into(db::schema::evm_token_transfer_log::table) + .values(db::models::NewEvmTokenTransferLog { grant_id, log_id: 0, - chain_id: CHAIN_ID as i32, + chain_id: CHAIN_ID.into(), token_contract: DAI.to_vec(), recipient_address: RECIPIENT.to_vec(), value: utils::u256_to_bytes(U256::from(900u64)).to_vec(), @@ -283,12 +282,11 @@ async fn evaluate_rejects_volume_over_limit() { .await .unwrap(); - use crate::db::{models::NewEvmTokenTransferLog, schema::evm_token_transfer_log}; - insert_into(evm_token_transfer_log::table) - .values(NewEvmTokenTransferLog { + insert_into(db::schema::evm_token_transfer_log::table) + .values(db::models::NewEvmTokenTransferLog { grant_id, log_id: 0, - chain_id: CHAIN_ID as i32, + chain_id: CHAIN_ID.into(), token_contract: DAI.to_vec(), recipient_address: RECIPIENT.to_vec(), value: utils::u256_to_bytes(U256::from(1_000u64)).to_vec(), diff --git a/server/crates/arbiter-server/src/evm/safe_signer.rs b/server/crates/arbiter-server/src/evm/safe_signer.rs index e2f8100..230ef97 100644 --- a/server/crates/arbiter-server/src/evm/safe_signer.rs +++ b/server/crates/arbiter-server/src/evm/safe_signer.rs @@ -82,8 +82,8 @@ impl SafeSigner { }) } + #[expect(clippy::significant_drop_tightening, reason = "false positive")] fn sign_hash_inner(&self, hash: &B256) -> Result { - #[allow(clippy::expect_used)] let mut cell = self.key.lock().expect("SafeSigner mutex poisoned"); let reader = cell.read(); let sig: (ecdsa::Signature, RecoveryId) = reader.sign_prehash(hash.as_ref())?; @@ -96,7 +96,6 @@ impl SafeSigner { { return Err(Error::TransactionChainIdMismatch { signer: chain_id, - #[allow(clippy::expect_used)] tx: tx.chain_id().expect("Chain ID is guaranteed to be set"), }); } diff --git a/server/crates/arbiter-server/src/evm/utils.rs b/server/crates/arbiter-server/src/evm/utils.rs index 7358173..ae00f88 100644 --- a/server/crates/arbiter-server/src/evm/utils.rs +++ b/server/crates/arbiter-server/src/evm/utils.rs @@ -7,7 +7,7 @@ pub struct LengthError { pub actual: usize, } -pub fn u256_to_bytes(value: U256) -> [u8; 32] { +pub const fn u256_to_bytes(value: U256) -> [u8; 32] { value.to_le_bytes() } pub fn bytes_to_u256(bytes: &[u8]) -> Option { diff --git a/server/crates/arbiter-server/src/grpc/client.rs b/server/crates/arbiter-server/src/grpc/client.rs index 52fa1ea..a83e432 100644 --- a/server/crates/arbiter-server/src/grpc/client.rs +++ b/server/crates/arbiter-server/src/grpc/client.rs @@ -98,8 +98,7 @@ pub async fn start(mut conn: ClientConnection, mut bi: GrpcBi { let _ = bi .send(Err(Status::unauthenticated(format!( - "Authentication failed: {}", - err + "Authentication failed: {err}", )))) .await; warn!(error = ?err, "Client authentication failed"); diff --git a/server/crates/arbiter-server/src/grpc/client/auth.rs b/server/crates/arbiter-server/src/grpc/client/auth.rs index 4a7b944..285e869 100644 --- a/server/crates/arbiter-server/src/grpc/client/auth.rs +++ b/server/crates/arbiter-server/src/grpc/client/auth.rs @@ -5,8 +5,7 @@ use arbiter_proto::{ client::{ ClientRequest, ClientResponse, auth::{ - self as proto_auth, AuthChallenge as ProtoAuthChallenge, - AuthChallengeRequest as ProtoAuthChallengeRequest, + self as proto_auth, AuthChallengeRequest as ProtoAuthChallengeRequest, AuthChallengeSolution as ProtoAuthChallengeSolution, AuthResult as ProtoAuthResult, request::Payload as AuthRequestPayload, response::Payload as AuthResponsePayload, }, @@ -22,7 +21,7 @@ use tonic::Status; use tracing::warn; use crate::{ - actors::client::{self, ClientConnection, auth}, + actors::client::{ClientConnection, auth}, grpc::request_tracker::RequestTracker, }; @@ -32,7 +31,7 @@ pub struct AuthTransportAdapter<'a> { } impl<'a> AuthTransportAdapter<'a> { - pub fn new( + pub const fn new( bi: &'a mut GrpcBi, request_tracker: &'a mut RequestTracker, ) -> Self { @@ -42,40 +41,6 @@ impl<'a> AuthTransportAdapter<'a> { } } - fn response_to_proto(response: auth::Outbound) -> AuthResponsePayload { - match response { - auth::Outbound::AuthChallenge { pubkey, nonce } => { - AuthResponsePayload::Challenge(ProtoAuthChallenge { - pubkey: pubkey.to_bytes(), - nonce, - }) - } - auth::Outbound::AuthSuccess => { - AuthResponsePayload::Result(ProtoAuthResult::Success.into()) - } - } - } - - fn error_to_proto(error: auth::Error) -> AuthResponsePayload { - AuthResponsePayload::Result( - match error { - auth::Error::InvalidChallengeSolution => ProtoAuthResult::InvalidSignature, - auth::Error::ApproveError(auth::ApproveError::Denied) => { - ProtoAuthResult::ApprovalDenied - } - auth::Error::ApproveError(auth::ApproveError::Upstream( - crate::actors::flow_coordinator::ApprovalError::NoUserAgentsConnected, - )) => ProtoAuthResult::NoUserAgentsOnline, - auth::Error::ApproveError(auth::ApproveError::Internal) - | auth::Error::DatabasePoolUnavailable - | auth::Error::DatabaseOperationFailed - | auth::Error::IntegrityCheckFailed - | auth::Error::Transport => ProtoAuthResult::Internal, - } - .into(), - ) - } - async fn send_client_response( &mut self, payload: AuthResponsePayload, @@ -97,14 +62,14 @@ impl<'a> AuthTransportAdapter<'a> { } #[async_trait] -impl Sender> for AuthTransportAdapter<'_> { +impl Sender> for AuthTransportAdapter<'_> { async fn send( &mut self, - item: Result, + item: Result, ) -> Result<(), TransportError> { let payload = match item { - Ok(message) => AuthTransportAdapter::response_to_proto(message), - Err(err) => AuthTransportAdapter::error_to_proto(err), + Ok(message) => message.into(), + Err(err) => AuthResponsePayload::Result(ProtoAuthResult::from(err).into()), }; self.send_client_response(payload).await @@ -183,7 +148,7 @@ impl Receiver for AuthTransportAdapter<'_> { } } -impl Bi> for AuthTransportAdapter<'_> {} +impl Bi> for AuthTransportAdapter<'_> {} fn client_metadata_from_proto(metadata: ProtoClientInfo) -> ClientMetadata { ClientMetadata { @@ -197,7 +162,7 @@ pub async fn start( conn: &mut ClientConnection, bi: &mut GrpcBi, request_tracker: &mut RequestTracker, -) -> Result { +) -> Result { let mut transport = AuthTransportAdapter::new(bi, request_tracker); - client::auth::authenticate(conn, &mut transport).await + auth::authenticate(conn, &mut transport).await } diff --git a/server/crates/arbiter-server/src/grpc/client/evm.rs b/server/crates/arbiter-server/src/grpc/client/evm.rs index 5b5ba2e..7b152b7 100644 --- a/server/crates/arbiter-server/src/grpc/client/evm.rs +++ b/server/crates/arbiter-server/src/grpc/client/evm.rs @@ -23,7 +23,7 @@ use crate::{ }, }; -fn wrap_response(payload: EvmResponsePayload) -> ClientResponsePayload { +const fn wrap_response(payload: EvmResponsePayload) -> ClientResponsePayload { ClientResponsePayload::Evm(proto_evm::Response { payload: Some(payload), }) diff --git a/server/crates/arbiter-server/src/grpc/client/vault.rs b/server/crates/arbiter-server/src/grpc/client/vault.rs index b5e98e7..650ad3a 100644 --- a/server/crates/arbiter-server/src/grpc/client/vault.rs +++ b/server/crates/arbiter-server/src/grpc/client/vault.rs @@ -13,7 +13,7 @@ use tonic::Status; use tracing::warn; use crate::actors::{ - client::session::{ClientSession, Error, HandleQueryVaultState}, + client::session::{ClientSession, ClientSessionError, HandleQueryVaultState}, keyholder::KeyHolderState, }; @@ -28,12 +28,14 @@ pub(super) async fn dispatch( }; match payload { - VaultRequestPayload::QueryState(_) => { + VaultRequestPayload::QueryState(()) => { let state = match actor.ask(HandleQueryVaultState {}).await { Ok(KeyHolderState::Unbootstrapped) => ProtoVaultState::Unbootstrapped, Ok(KeyHolderState::Sealed) => ProtoVaultState::Sealed, Ok(KeyHolderState::Unsealed) => ProtoVaultState::Unsealed, - Err(SendError::HandlerError(Error::Internal)) => ProtoVaultState::Error, + Err(SendError::HandlerError(ClientSessionError::Internal)) => { + ProtoVaultState::Error + } Err(err) => { warn!(error = ?err, "Failed to query vault state"); ProtoVaultState::Error diff --git a/server/crates/arbiter-server/src/grpc/common/outbound.rs b/server/crates/arbiter-server/src/grpc/common/outbound.rs index c2a2045..7e60ca1 100644 --- a/server/crates/arbiter-server/src/grpc/common/outbound.rs +++ b/server/crates/arbiter-server/src/grpc/common/outbound.rs @@ -31,16 +31,16 @@ impl Convert for SpecificMeaning { fn convert(self) -> Self::Output { let kind = match self { - SpecificMeaning::EtherTransfer(meaning) => ProtoSpecificMeaningKind::EtherTransfer( + Self::EtherTransfer(meaning) => ProtoSpecificMeaningKind::EtherTransfer( arbiter_proto::proto::shared::evm::EtherTransferMeaning { to: meaning.to.to_vec(), value: u256_to_proto_bytes(meaning.value), }, ), - SpecificMeaning::TokenTransfer(meaning) => ProtoSpecificMeaningKind::TokenTransfer( + Self::TokenTransfer(meaning) => ProtoSpecificMeaningKind::TokenTransfer( arbiter_proto::proto::shared::evm::TokenTransferMeaning { token: Some(ProtoTokenInfo { - symbol: meaning.token.symbol.to_string(), + symbol: meaning.token.symbol.to_owned(), address: meaning.token.contract.to_vec(), chain_id: meaning.token.chain, }), @@ -61,25 +61,21 @@ impl Convert for EvalViolation { fn convert(self) -> Self::Output { let kind = match self { - EvalViolation::InvalidTarget { target } => { + Self::InvalidTarget { target } => { ProtoEvalViolationKind::InvalidTarget(target.to_vec()) } - EvalViolation::GasLimitExceeded { + Self::GasLimitExceeded { max_gas_fee_per_gas, max_priority_fee_per_gas, } => ProtoEvalViolationKind::GasLimitExceeded(GasLimitExceededViolation { max_gas_fee_per_gas: max_gas_fee_per_gas.map(u256_to_proto_bytes), max_priority_fee_per_gas: max_priority_fee_per_gas.map(u256_to_proto_bytes), }), - EvalViolation::RateLimitExceeded => ProtoEvalViolationKind::RateLimitExceeded(()), - EvalViolation::VolumetricLimitExceeded => { - ProtoEvalViolationKind::VolumetricLimitExceeded(()) - } - EvalViolation::InvalidTime => ProtoEvalViolationKind::InvalidTime(()), - EvalViolation::InvalidTransactionType => { - ProtoEvalViolationKind::InvalidTransactionType(()) - } - EvalViolation::MismatchingChainId { expected, actual } => { + Self::RateLimitExceeded => ProtoEvalViolationKind::RateLimitExceeded(()), + Self::VolumetricLimitExceeded => ProtoEvalViolationKind::VolumetricLimitExceeded(()), + Self::InvalidTime => ProtoEvalViolationKind::InvalidTime(()), + Self::InvalidTransactionType => ProtoEvalViolationKind::InvalidTransactionType(()), + Self::MismatchingChainId { expected, actual } => { ProtoEvalViolationKind::ChainIdMismatch(proto_eval_violation::ChainIdMismatch { expected, actual, @@ -96,13 +92,13 @@ impl Convert for VetError { fn convert(self) -> Self::Output { let kind = match self { - VetError::ContractCreationNotSupported => { + Self::ContractCreationNotSupported => { ProtoTransactionEvalErrorKind::ContractCreationNotSupported(()) } - VetError::UnsupportedTransactionType => { + Self::UnsupportedTransactionType => { ProtoTransactionEvalErrorKind::UnsupportedTransactionType(()) } - VetError::Evaluated(meaning, policy_error) => match policy_error { + Self::Evaluated(meaning, policy_error) => match policy_error { PolicyError::NoMatchingGrant => { ProtoTransactionEvalErrorKind::NoMatchingGrant(NoMatchingGrantError { meaning: Some(meaning.convert()), diff --git a/server/crates/arbiter-server/src/grpc/request_tracker.rs b/server/crates/arbiter-server/src/grpc/request_tracker.rs index 7ab6254..bd4a860 100644 --- a/server/crates/arbiter-server/src/grpc/request_tracker.rs +++ b/server/crates/arbiter-server/src/grpc/request_tracker.rs @@ -20,7 +20,7 @@ impl RequestTracker { // This is used to set the response id for auth responses, which need to match the request id of the auth challenge request. // -1 offset is needed because request() increments the next_request_id after returning the current request id. - pub fn current_request_id(&self) -> i32 { + pub const fn current_request_id(&self) -> i32 { self.next_request_id - 1 } } diff --git a/server/crates/arbiter-server/src/grpc/user_agent/auth.rs b/server/crates/arbiter-server/src/grpc/user_agent/auth.rs index e2625e0..c4be267 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/auth.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/auth.rs @@ -28,7 +28,7 @@ pub struct AuthTransportAdapter<'a> { } impl<'a> AuthTransportAdapter<'a> { - pub fn new( + pub const fn new( bi: &'a mut GrpcBi, request_tracker: &'a mut RequestTracker, ) -> Self { @@ -140,7 +140,7 @@ impl Receiver for AuthTransportAdapter<'_> { AuthRequestPayload::ChallengeRequest(ProtoAuthChallengeRequest { pubkey, bootstrap_token, - key_type: _, + .. }) => { let Ok(pubkey) = authn::PublicKey::try_from(pubkey.as_slice()) else { warn!( diff --git a/server/crates/arbiter-server/src/grpc/user_agent/evm.rs b/server/crates/arbiter-server/src/grpc/user_agent/evm.rs index 28725c2..0609881 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/evm.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/evm.rs @@ -37,7 +37,7 @@ use crate::{ }, }; -fn wrap_evm_response(payload: EvmResponsePayload) -> UserAgentResponsePayload { +const fn wrap_evm_response(payload: EvmResponsePayload) -> UserAgentResponsePayload { UserAgentResponsePayload::Evm(proto_evm::Response { payload: Some(payload), }) @@ -52,8 +52,8 @@ pub(super) async fn dispatch( }; match payload { - EvmRequestPayload::WalletCreate(_) => handle_wallet_create(actor).await, - EvmRequestPayload::WalletList(_) => handle_wallet_list(actor).await, + EvmRequestPayload::WalletCreate(()) => handle_wallet_create(actor).await, + EvmRequestPayload::WalletList(()) => handle_wallet_list(actor).await, EvmRequestPayload::GrantCreate(req) => handle_grant_create(actor, req).await, EvmRequestPayload::GrantDelete(req) => handle_grant_delete(actor, req).await, EvmRequestPayload::GrantList(_) => handle_grant_list(actor).await, diff --git a/server/crates/arbiter-server/src/grpc/user_agent/inbound.rs b/server/crates/arbiter-server/src/grpc/user_agent/inbound.rs index 6cfb2e5..0948a61 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/inbound.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/inbound.rs @@ -22,11 +22,11 @@ use crate::{ grpc::TryConvert, }; -fn address_from_bytes(bytes: Vec) -> Result { +fn address_from_bytes(bytes: &[u8]) -> Result { if bytes.len() != 20 { return Err(Status::invalid_argument("Invalid EVM address")); } - Ok(Address::from_slice(&bytes)) + Ok(Address::from_slice(bytes)) } fn u256_from_proto_bytes(bytes: &[u8]) -> Result { @@ -41,7 +41,7 @@ impl TryConvert for ProtoTimestamp { type Error = Status; fn try_convert(self) -> Result, Status> { - Utc.timestamp_opt(self.seconds, self.nanos as u32) + Utc.timestamp_opt(self.seconds, self.nanos.try_into().unwrap_or_default()) .single() .ok_or_else(|| Status::invalid_argument("Invalid timestamp")) } @@ -116,7 +116,8 @@ impl TryConvert for ProtoSpecificGrant { limit, })) => Ok(SpecificGrant::EtherTransfer(ether_transfer::Settings { target: targets - .into_iter() + .iter() + .map(Vec::as_slice) .map(address_from_bytes) .collect::>()?, limit: limit @@ -130,8 +131,10 @@ impl TryConvert for ProtoSpecificGrant { target, volume_limits, })) => Ok(SpecificGrant::TokenTransfer(token_transfers::Settings { - token_contract: address_from_bytes(token_contract)?, - target: target.map(address_from_bytes).transpose()?, + token_contract: address_from_bytes(&token_contract)?, + target: target + .map(|target| address_from_bytes(&target)) + .transpose()?, volume_limits: volume_limits .into_iter() .map(ProtoVolumeRateLimit::try_convert) diff --git a/server/crates/arbiter-server/src/grpc/user_agent/outbound.rs b/server/crates/arbiter-server/src/grpc/user_agent/outbound.rs index 805386e..d799750 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/outbound.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/outbound.rs @@ -22,7 +22,7 @@ impl Convert for DateTime { fn convert(self) -> ProtoTimestamp { ProtoTimestamp { seconds: self.timestamp(), - nanos: self.timestamp_subsec_nanos() as i32, + nanos: self.timestamp_subsec_nanos().try_into().unwrap_or(i32::MAX), } } } @@ -74,13 +74,13 @@ impl Convert for SpecificGrant { fn convert(self) -> ProtoSpecificGrant { let grant = match self { - SpecificGrant::EtherTransfer(s) => { + Self::EtherTransfer(s) => { ProtoSpecificGrantType::EtherTransfer(ProtoEtherTransferSettings { targets: s.target.into_iter().map(|a| a.to_vec()).collect(), limit: Some(s.limit.convert()), }) } - SpecificGrant::TokenTransfer(s) => { + Self::TokenTransfer(s) => { ProtoSpecificGrantType::TokenTransfer(ProtoTokenTransferSettings { token_contract: s.token_contract.to_vec(), target: s.target.map(|a| a.to_vec()), diff --git a/server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs b/server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs index b0d832f..f0445c9 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs @@ -32,7 +32,7 @@ use crate::{ grpc::Convert, }; -fn wrap_sdk_client_response(payload: SdkClientResponsePayload) -> UserAgentResponsePayload { +const fn wrap_sdk_client_response(payload: SdkClientResponsePayload) -> UserAgentResponsePayload { UserAgentResponsePayload::SdkClient(proto_sdk_client::Response { payload: Some(payload), }) @@ -75,14 +75,14 @@ pub(super) async fn dispatch( SdkClientRequestPayload::Revoke(_) => Err(Status::unimplemented( "SdkClientRevoke is not yet implemented", )), - SdkClientRequestPayload::List(_) => handle_list(actor).await, + SdkClientRequestPayload::List(()) => handle_list(actor).await, SdkClientRequestPayload::GrantWalletAccess(req) => { handle_grant_wallet_access(actor, req).await } SdkClientRequestPayload::RevokeWalletAccess(req) => { handle_revoke_wallet_access(actor, req).await } - SdkClientRequestPayload::ListWalletAccess(_) => handle_list_wallet_access(actor).await, + SdkClientRequestPayload::ListWalletAccess(()) => handle_list_wallet_access(actor).await, } } @@ -91,7 +91,7 @@ async fn handle_connection_response( resp: ProtoSdkClientConnectionResponse, ) -> Result, Status> { let pubkey = authn::PublicKey::try_from(resp.pubkey.as_slice()) - .map_err(|_| Status::invalid_argument("Invalid ML-DSA public key"))?; + .map_err(|()| Status::invalid_argument("Invalid ML-DSA public key"))?; actor .ask(HandleNewClientApprove { @@ -116,12 +116,17 @@ async fn handle_list( .into_iter() .map(|(client, metadata)| ProtoSdkClientEntry { id: client.id, - pubkey: client.public_key.to_vec(), + pubkey: client.public_key.clone(), info: Some(ProtoClientMetadata { name: metadata.name, description: metadata.description, version: metadata.version, }), + #[expect( + clippy::cast_possible_truncation, + clippy::as_conversions, + reason = "fixme! #84" + )] created_at: client.created_at.0.timestamp() as i32, }) .collect(), @@ -142,7 +147,7 @@ async fn handle_grant_wallet_access( actor: &ActorRef, req: ProtoSdkClientGrantWalletAccess, ) -> Result, Status> { - let entries: Vec = req.accesses.into_iter().map(|a| a.convert()).collect(); + let entries: Vec = req.accesses.into_iter().map(Convert::convert).collect(); match actor.ask(HandleGrantEvmWalletAccess { entries }).await { Ok(()) => { info!("Successfully granted wallet access"); @@ -182,7 +187,7 @@ async fn handle_list_wallet_access( match actor.ask(HandleListWalletAccess {}).await { Ok(accesses) => Ok(Some(wrap_sdk_client_response( SdkClientResponsePayload::ListWalletAccess(ListWalletAccessResponse { - accesses: accesses.into_iter().map(|a| a.convert()).collect(), + accesses: accesses.into_iter().map(Convert::convert).collect(), }), ))), Err(err) => { diff --git a/server/crates/arbiter-server/src/grpc/user_agent/vault.rs b/server/crates/arbiter-server/src/grpc/user_agent/vault.rs index 8a2940f..11e65df 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/vault.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/vault.rs @@ -31,13 +31,13 @@ use crate::actors::{ }, }; -fn wrap_vault_response(payload: VaultResponsePayload) -> UserAgentResponsePayload { +const fn wrap_vault_response(payload: VaultResponsePayload) -> UserAgentResponsePayload { UserAgentResponsePayload::Vault(proto_vault::Response { payload: Some(payload), }) } -fn wrap_unseal_response(payload: UnsealResponsePayload) -> UserAgentResponsePayload { +const fn wrap_unseal_response(payload: UnsealResponsePayload) -> UserAgentResponsePayload { wrap_vault_response(VaultResponsePayload::Unseal(proto_unseal::Response { payload: Some(payload), })) @@ -58,7 +58,7 @@ pub(super) async fn dispatch( }; match payload { - VaultRequestPayload::QueryState(_) => handle_query_vault_state(actor).await, + VaultRequestPayload::QueryState(()) => handle_query_vault_state(actor).await, VaultRequestPayload::Unseal(req) => dispatch_unseal_request(actor, req).await, VaultRequestPayload::Bootstrap(req) => handle_bootstrap_request(actor, req).await, } diff --git a/server/crates/arbiter-server/src/lib.rs b/server/crates/arbiter-server/src/lib.rs index 8bb07c8..8ad5f47 100644 --- a/server/crates/arbiter-server/src/lib.rs +++ b/server/crates/arbiter-server/src/lib.rs @@ -1,4 +1,3 @@ -#![forbid(unsafe_code)] use crate::context::ServerContext; pub mod actors; @@ -14,7 +13,7 @@ pub struct Server { } impl Server { - pub fn new(context: ServerContext) -> Self { + pub const fn new(context: ServerContext) -> Self { Self { context } } } diff --git a/server/crates/arbiter-server/tests/client/auth.rs b/server/crates/arbiter-server/tests/client/auth.rs index a7137e3..54b6390 100644 --- a/server/crates/arbiter-server/tests/client/auth.rs +++ b/server/crates/arbiter-server/tests/client/auth.rs @@ -15,7 +15,7 @@ use arbiter_server::{ }; use diesel::{ExpressionMethods as _, NullableExpressionMethods as _, QueryDsl as _, insert_into}; use diesel_async::RunQueryDsl; -use ml_dsa::{KeyGen, MlDsa87, SigningKey, VerifyingKey, signature::Keypair as _}; +use ml_dsa::{KeyGen, MlDsa87, SigningKey, VerifyingKey, signature::Keypair}; use super::common::ChannelTransport; @@ -27,6 +27,10 @@ fn metadata(name: &str, description: Option<&str>, version: Option<&str>) -> Cli } } +fn verifying_key(key: &SigningKey) -> VerifyingKey { + as Keypair>::verifying_key(key) +} + async fn insert_registered_client( db: &db::DatabasePool, actors: &GlobalActors, @@ -48,7 +52,7 @@ async fn insert_registered_client( .unwrap(); let client_id: i32 = insert_into(program_client::table) .values(( - program_client::public_key.eq(pubkey.encode().to_vec()), + program_client::public_key.eq(pubkey.encode().0.to_vec()), program_client::metadata_id.eq(metadata_id), )) .returning(program_client::id) @@ -83,9 +87,9 @@ fn sign_client_challenge( async fn insert_bootstrap_sentinel_useragent(db: &db::DatabasePool) { let mut conn = db.get().await.unwrap(); - let sentinel_key = MlDsa87::key_gen(&mut rand::rng()) - .verifying_key() + let sentinel_key = verifying_key(&MlDsa87::key_gen(&mut rand::rng())) .encode() + .0 .to_vec(); insert_into(schema::useragent_client::table) @@ -114,7 +118,7 @@ async fn spawn_test_actors(db: &db::DatabasePool) -> GlobalActors { #[tokio::test] #[test_log::test] -pub async fn test_unregistered_pubkey_rejected() { +pub async fn unregistered_pubkey_rejected() { let db = db::create_test_pool().await; let (server_transport, mut test_transport) = ChannelTransport::new(); @@ -129,7 +133,7 @@ pub async fn test_unregistered_pubkey_rejected() { test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), metadata: metadata("client", Some("desc"), Some("1.0.0")), }) .await @@ -141,18 +145,18 @@ pub async fn test_unregistered_pubkey_rejected() { #[tokio::test] #[test_log::test] -pub async fn test_challenge_auth() { +pub async fn challenge_auth() { let db = db::create_test_pool().await; let actors = spawn_test_actors(&db).await; let new_key = MlDsa87::key_gen(&mut rand::rng()); - insert_registered_client( + Box::pin(insert_registered_client( &db, &actors, - new_key.verifying_key(), + verifying_key(&new_key), &metadata("client", Some("desc"), Some("1.0.0")), - ) + )) .await; let (server_transport, mut test_transport) = ChannelTransport::new(); @@ -165,7 +169,7 @@ pub async fn test_challenge_auth() { // Send challenge request test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), metadata: metadata("client", Some("desc"), Some("1.0.0")), }) .await @@ -179,7 +183,7 @@ pub async fn test_challenge_auth() { let challenge = match response { Ok(resp) => match resp { auth::Outbound::AuthChallenge { pubkey, nonce } => (pubkey, nonce), - other => panic!("Expected AuthChallenge, got {other:?}"), + other @ auth::Outbound::AuthSuccess => panic!("Expected AuthChallenge, got {other:?}"), }, Err(err) => panic!("Expected Ok response, got Err({err:?})"), }; @@ -208,13 +212,19 @@ pub async fn test_challenge_auth() { #[tokio::test] #[test_log::test] -pub async fn test_metadata_unchanged_does_not_append_history() { +pub async fn metadata_unchanged_does_not_append_history() { let db = db::create_test_pool().await; let actors = spawn_test_actors(&db).await; let new_key = MlDsa87::key_gen(&mut rand::rng()); let requested = metadata("client", Some("desc"), Some("1.0.0")); - insert_registered_client(&db, &actors, new_key.verifying_key(), &requested).await; + Box::pin(insert_registered_client( + &db, + &actors, + verifying_key(&new_key), + &requested, + )) + .await; let props = ClientConnection::new(db.clone(), actors); @@ -226,7 +236,7 @@ pub async fn test_metadata_unchanged_does_not_append_history() { test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), metadata: requested, }) .await @@ -235,7 +245,7 @@ pub async fn test_metadata_unchanged_does_not_append_history() { let response = test_transport.recv().await.unwrap().unwrap(); let (pubkey, nonce) = match response { auth::Outbound::AuthChallenge { pubkey, nonce } => (pubkey, nonce), - other => panic!("Expected AuthChallenge, got {other:?}"), + auth::Outbound::AuthSuccess => panic!("Expected AuthChallenge, got AuthSuccess"), }; let signature = sign_client_challenge(&new_key, nonce, &pubkey); test_transport @@ -265,17 +275,17 @@ pub async fn test_metadata_unchanged_does_not_append_history() { #[tokio::test] #[test_log::test] -pub async fn test_metadata_change_appends_history_and_repoints_binding() { +pub async fn metadata_change_appends_history_and_repoints_binding() { let db = db::create_test_pool().await; let actors = spawn_test_actors(&db).await; let new_key = MlDsa87::key_gen(&mut rand::rng()); - insert_registered_client( + Box::pin(insert_registered_client( &db, &actors, - new_key.verifying_key(), + verifying_key(&new_key), &metadata("client", Some("old"), Some("1.0.0")), - ) + )) .await; let props = ClientConnection::new(db.clone(), actors); @@ -288,7 +298,7 @@ pub async fn test_metadata_change_appends_history_and_repoints_binding() { test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), metadata: metadata("client", Some("new"), Some("2.0.0")), }) .await @@ -297,14 +307,14 @@ pub async fn test_metadata_change_appends_history_and_repoints_binding() { let response = test_transport.recv().await.unwrap().unwrap(); let (pubkey, nonce) = match response { auth::Outbound::AuthChallenge { pubkey, nonce } => (pubkey, nonce), - other => panic!("Expected AuthChallenge, got {other:?}"), + auth::Outbound::AuthSuccess => panic!("Expected AuthChallenge, got AuthSuccess"), }; let signature = sign_client_challenge(&new_key, nonce, &pubkey); test_transport .send(auth::Inbound::AuthChallengeSolution { signature }) .await .unwrap(); - let _ = test_transport.recv().await.unwrap(); + drop(test_transport.recv().await.unwrap()); task.await.unwrap(); { @@ -352,7 +362,7 @@ pub async fn test_metadata_change_appends_history_and_repoints_binding() { #[tokio::test] #[test_log::test] -pub async fn test_challenge_auth_rejects_integrity_tag_mismatch() { +pub async fn challenge_auth_rejects_integrity_tag_mismatch() { let db = db::create_test_pool().await; let actors = spawn_test_actors(&db).await; @@ -374,7 +384,7 @@ pub async fn test_challenge_auth_rejects_integrity_tag_mismatch() { .unwrap(); insert_into(program_client::table) .values(( - program_client::public_key.eq(new_key.verifying_key().encode().to_vec()), + program_client::public_key.eq(verifying_key(&new_key).encode().0.to_vec()), program_client::metadata_id.eq(metadata_id), )) .execute(&mut conn) @@ -391,7 +401,7 @@ pub async fn test_challenge_auth_rejects_integrity_tag_mismatch() { test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), metadata: requested, }) .await @@ -401,7 +411,10 @@ pub async fn test_challenge_auth_rejects_integrity_tag_mismatch() { .recv() .await .expect("should receive auth rejection"); - assert!(matches!(response, Err(auth::Error::IntegrityCheckFailed))); + assert!(matches!( + response, + Err(auth::ClientAuthError::IntegrityCheckFailed) + )); task.await.unwrap(); } diff --git a/server/crates/arbiter-server/tests/common/mod.rs b/server/crates/arbiter-server/tests/common/mod.rs index c4e6878..2e6e91b 100644 --- a/server/crates/arbiter-server/tests/common/mod.rs +++ b/server/crates/arbiter-server/tests/common/mod.rs @@ -1,3 +1,4 @@ +#![allow(dead_code, reason = "Common test utilities that may not be used in every test")] use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_proto::transport::{Bi, Error, Receiver, Sender}; use arbiter_server::{ @@ -10,7 +11,6 @@ use diesel::QueryDsl; use diesel_async::RunQueryDsl; use tokio::sync::mpsc; -#[allow(dead_code)] pub async fn bootstrapped_keyholder(db: &db::DatabasePool) -> KeyHolder { let mut actor = KeyHolder::new(db.clone()).await.unwrap(); actor @@ -20,7 +20,6 @@ pub async fn bootstrapped_keyholder(db: &db::DatabasePool) -> KeyHolder { actor } -#[allow(dead_code)] pub async fn root_key_history_id(db: &db::DatabasePool) -> i32 { let mut conn = db.get().await.unwrap(); let id = schema::arbiter_settings::table @@ -31,14 +30,12 @@ pub async fn root_key_history_id(db: &db::DatabasePool) -> i32 { id.expect("root_key_id should be set after bootstrap") } -#[allow(dead_code)] pub struct ChannelTransport { receiver: mpsc::Receiver, sender: mpsc::Sender, } impl ChannelTransport { - #[allow(dead_code)] pub fn new() -> (Self, ChannelTransport) { let (tx1, rx1) = mpsc::channel(10); let (tx2, rx2) = mpsc::channel(10); diff --git a/server/crates/arbiter-server/tests/keyholder/concurrency.rs b/server/crates/arbiter-server/tests/keyholder/concurrency.rs index f128beb..4f685d2 100644 --- a/server/crates/arbiter-server/tests/keyholder/concurrency.rs +++ b/server/crates/arbiter-server/tests/keyholder/concurrency.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_server::{ - actors::keyholder::{CreateNew, Error, KeyHolder}, + actors::keyholder::{CreateNew, KeyHolder, KeyHolderError}, db::{self, models, schema}, }; @@ -122,7 +122,7 @@ async fn insert_failure_does_not_create_partial_row() { .create_new(SafeCell::new(b"should fail".to_vec())) .await .unwrap_err(); - assert!(matches!(err, Error::DatabaseTransaction(_))); + assert!(matches!(err, KeyHolderError::DatabaseTransaction(_))); let mut conn = db.get().await.unwrap(); sql_query("DROP TRIGGER fail_aead_insert;") diff --git a/server/crates/arbiter-server/tests/keyholder/lifecycle.rs b/server/crates/arbiter-server/tests/keyholder/lifecycle.rs index bd50b6f..10dc9c2 100644 --- a/server/crates/arbiter-server/tests/keyholder/lifecycle.rs +++ b/server/crates/arbiter-server/tests/keyholder/lifecycle.rs @@ -1,6 +1,6 @@ use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_server::{ - actors::keyholder::{Error, KeyHolder}, + actors::keyholder::{KeyHolder, KeyHolderError}, crypto::encryption::v1::{Nonce, ROOT_KEY_TAG}, db::{self, models, schema}, }; @@ -12,7 +12,7 @@ use crate::common; #[tokio::test] #[test_log::test] -async fn test_bootstrap() { +async fn bootstrap() { let db = db::create_test_pool().await; let mut actor = KeyHolder::new(db.clone()).await.unwrap(); @@ -35,18 +35,18 @@ async fn test_bootstrap() { #[tokio::test] #[test_log::test] -async fn test_bootstrap_rejects_double() { +async fn bootstrap_rejects_double() { let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; let seal_key2 = SafeCell::new(b"test-seal-key".to_vec()); let err = actor.bootstrap(seal_key2).await.unwrap_err(); - assert!(matches!(err, Error::AlreadyBootstrapped)); + assert!(matches!(err, KeyHolderError::AlreadyBootstrapped)); } #[tokio::test] #[test_log::test] -async fn test_create_new_before_bootstrap_fails() { +async fn create_new_before_bootstrap_fails() { let db = db::create_test_pool().await; let mut actor = KeyHolder::new(db).await.unwrap(); @@ -54,34 +54,34 @@ async fn test_create_new_before_bootstrap_fails() { .create_new(SafeCell::new(b"data".to_vec())) .await .unwrap_err(); - assert!(matches!(err, Error::NotBootstrapped)); + assert!(matches!(err, KeyHolderError::NotBootstrapped)); } #[tokio::test] #[test_log::test] -async fn test_decrypt_before_bootstrap_fails() { +async fn decrypt_before_bootstrap_fails() { let db = db::create_test_pool().await; let mut actor = KeyHolder::new(db).await.unwrap(); let err = actor.decrypt(1).await.unwrap_err(); - assert!(matches!(err, Error::NotBootstrapped)); + assert!(matches!(err, KeyHolderError::NotBootstrapped)); } #[tokio::test] #[test_log::test] -async fn test_new_restores_sealed_state() { +async fn new_restores_sealed_state() { let db = db::create_test_pool().await; let actor = common::bootstrapped_keyholder(&db).await; drop(actor); let mut actor2 = KeyHolder::new(db).await.unwrap(); let err = actor2.decrypt(1).await.unwrap_err(); - assert!(matches!(err, Error::NotBootstrapped)); + assert!(matches!(err, KeyHolderError::NotBootstrapped)); } #[tokio::test] #[test_log::test] -async fn test_unseal_correct_password() { +async fn unseal_correct_password() { let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; @@ -102,7 +102,7 @@ async fn test_unseal_correct_password() { #[tokio::test] #[test_log::test] -async fn test_unseal_wrong_then_correct_password() { +async fn unseal_wrong_then_correct_password() { let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; @@ -117,7 +117,7 @@ async fn test_unseal_wrong_then_correct_password() { let bad_key = SafeCell::new(b"wrong-password".to_vec()); let err = actor.try_unseal(bad_key).await.unwrap_err(); - assert!(matches!(err, Error::InvalidKey)); + assert!(matches!(err, KeyHolderError::InvalidKey)); let good_key = SafeCell::new(b"test-seal-key".to_vec()); actor.try_unseal(good_key).await.unwrap(); diff --git a/server/crates/arbiter-server/tests/keyholder/storage.rs b/server/crates/arbiter-server/tests/keyholder/storage.rs index 71ebccf..173c99c 100644 --- a/server/crates/arbiter-server/tests/keyholder/storage.rs +++ b/server/crates/arbiter-server/tests/keyholder/storage.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_server::{ - actors::keyholder::Error, + actors::keyholder::KeyHolderError, crypto::encryption::v1::Nonce, db::{self, models, schema}, }; @@ -14,7 +14,7 @@ use crate::common; #[tokio::test] #[test_log::test] -async fn test_create_decrypt_roundtrip() { +async fn create_decrypt_roundtrip() { let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; @@ -30,17 +30,17 @@ async fn test_create_decrypt_roundtrip() { #[tokio::test] #[test_log::test] -async fn test_decrypt_nonexistent_returns_not_found() { +async fn decrypt_nonexistent_returns_not_found() { let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; let err = actor.decrypt(9999).await.unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, KeyHolderError::NotFound)); } #[tokio::test] #[test_log::test] -async fn test_ciphertext_differs_across_entries() { +async fn ciphertext_differs_across_entries() { let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; @@ -78,7 +78,7 @@ async fn test_ciphertext_differs_across_entries() { #[tokio::test] #[test_log::test] -async fn test_nonce_never_reused() { +async fn nonce_never_reused() { let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; @@ -142,7 +142,7 @@ async fn broken_db_nonce_format_fails_closed() { .create_new(SafeCell::new(b"must fail".to_vec())) .await .unwrap_err(); - assert!(matches!(err, Error::BrokenDatabase)); + assert!(matches!(err, KeyHolderError::BrokenDatabase)); let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; @@ -159,5 +159,5 @@ async fn broken_db_nonce_format_fails_closed() { drop(conn); let err = actor.decrypt(id).await.unwrap_err(); - assert!(matches!(err, Error::BrokenDatabase)); + assert!(matches!(err, KeyHolderError::BrokenDatabase)); } diff --git a/server/crates/arbiter-server/tests/user_agent/auth.rs b/server/crates/arbiter-server/tests/user_agent/auth.rs index aeccc8a..8e8bf80 100644 --- a/server/crates/arbiter-server/tests/user_agent/auth.rs +++ b/server/crates/arbiter-server/tests/user_agent/auth.rs @@ -16,10 +16,14 @@ use arbiter_server::{ }; use diesel::{ExpressionMethods as _, QueryDsl, insert_into}; use diesel_async::RunQueryDsl; -use ml_dsa::{KeyGen, MlDsa87, SigningKey, signature::Keypair as _}; +use ml_dsa::{KeyGen, MlDsa87, SigningKey, VerifyingKey, signature::Keypair}; use super::common::ChannelTransport; +fn verifying_key(key: &SigningKey) -> VerifyingKey { + as Keypair>::verifying_key(key) +} + fn sign_useragent_challenge( key: &SigningKey, nonce: i32, @@ -34,7 +38,7 @@ fn sign_useragent_challenge( #[tokio::test] #[test_log::test] -pub async fn test_bootstrap_token_auth() { +pub async fn bootstrap_token_auth() { let db = db::create_test_pool().await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors @@ -56,7 +60,7 @@ pub async fn test_bootstrap_token_auth() { let new_key = MlDsa87::key_gen(&mut rand::rng()); test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), bootstrap_token: Some(token), }) .await @@ -79,12 +83,12 @@ pub async fn test_bootstrap_token_auth() { .first::>(&mut conn) .await .unwrap(); - assert_eq!(stored_pubkey, new_key.verifying_key().encode().to_vec()); + assert_eq!(stored_pubkey, verifying_key(&new_key).encode().0.to_vec()); } #[tokio::test] #[test_log::test] -pub async fn test_bootstrap_invalid_token_auth() { +pub async fn bootstrap_invalid_token_auth() { let db = db::create_test_pool().await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); @@ -98,8 +102,8 @@ pub async fn test_bootstrap_invalid_token_auth() { let new_key = MlDsa87::key_gen(&mut rand::rng()); test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), - bootstrap_token: Some("invalid_token".to_string()), + pubkey: verifying_key(&new_key).into(), + bootstrap_token: Some("invalid_token".to_owned()), }) .await .unwrap(); @@ -120,7 +124,7 @@ pub async fn test_bootstrap_invalid_token_auth() { #[tokio::test] #[test_log::test] -pub async fn test_challenge_auth() { +pub async fn challenge_auth() { let db = db::create_test_pool().await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors @@ -132,7 +136,7 @@ pub async fn test_challenge_auth() { .unwrap(); let new_key = MlDsa87::key_gen(&mut rand::rng()); - let pubkey_bytes = new_key.verifying_key().encode().to_vec(); + let pubkey_bytes = authn::PublicKey::from(verifying_key(&new_key)).to_bytes(); { let mut conn = db.get().await.unwrap(); @@ -149,7 +153,7 @@ pub async fn test_challenge_auth() { &mut conn, &actors.key_holder, &UserAgentCredentials { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), nonce: 1, }, id, @@ -167,7 +171,7 @@ pub async fn test_challenge_auth() { test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), bootstrap_token: None, }) .await @@ -180,7 +184,7 @@ pub async fn test_challenge_auth() { let challenge = match response { Ok(resp) => match resp { auth::Outbound::AuthChallenge { nonce } => nonce, - other => panic!("Expected AuthChallenge, got {other:?}"), + auth::Outbound::AuthSuccess => panic!("Expected AuthChallenge, got AuthSuccess"), }, Err(err) => panic!("Expected Ok response, got Err({err:?})"), }; @@ -208,7 +212,7 @@ pub async fn test_challenge_auth() { #[tokio::test] #[test_log::test] -pub async fn test_challenge_auth_rejects_integrity_tag_mismatch_when_unsealed() { +pub async fn challenge_auth_rejects_integrity_tag_mismatch_when_unsealed() { let db = db::create_test_pool().await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); @@ -221,7 +225,7 @@ pub async fn test_challenge_auth_rejects_integrity_tag_mismatch_when_unsealed() .unwrap(); let new_key = MlDsa87::key_gen(&mut rand::rng()); - let pubkey_bytes = new_key.verifying_key().encode().to_vec(); + let pubkey_bytes = authn::PublicKey::from(verifying_key(&new_key)).to_bytes(); { let mut conn = db.get().await.unwrap(); @@ -244,7 +248,7 @@ pub async fn test_challenge_auth_rejects_integrity_tag_mismatch_when_unsealed() test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), bootstrap_token: None, }) .await @@ -258,7 +262,7 @@ pub async fn test_challenge_auth_rejects_integrity_tag_mismatch_when_unsealed() #[tokio::test] #[test_log::test] -pub async fn test_challenge_auth_rejects_invalid_signature() { +pub async fn challenge_auth_rejects_invalid_signature() { let db = db::create_test_pool().await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors @@ -270,7 +274,7 @@ pub async fn test_challenge_auth_rejects_invalid_signature() { .unwrap(); let new_key = MlDsa87::key_gen(&mut rand::rng()); - let pubkey_bytes = new_key.verifying_key().encode().to_vec(); + let pubkey_bytes = authn::PublicKey::from(verifying_key(&new_key)).to_bytes(); { let mut conn = db.get().await.unwrap(); @@ -287,7 +291,7 @@ pub async fn test_challenge_auth_rejects_invalid_signature() { &mut conn, &actors.key_holder, &UserAgentCredentials { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), nonce: 1, }, id, @@ -305,7 +309,7 @@ pub async fn test_challenge_auth_rejects_invalid_signature() { test_transport .send(auth::Inbound::AuthChallengeRequest { - pubkey: new_key.verifying_key().into(), + pubkey: verifying_key(&new_key).into(), bootstrap_token: None, }) .await @@ -318,7 +322,7 @@ pub async fn test_challenge_auth_rejects_invalid_signature() { let challenge = match response { Ok(resp) => match resp { auth::Outbound::AuthChallenge { nonce } => nonce, - other => panic!("Expected AuthChallenge, got {other:?}"), + auth::Outbound::AuthSuccess => panic!("Expected AuthChallenge, got AuthSuccess"), }, Err(err) => panic!("Expected Ok response, got Err({err:?})"), }; diff --git a/server/crates/arbiter-server/tests/user_agent/unseal.rs b/server/crates/arbiter-server/tests/user_agent/unseal.rs index 15cf475..9dafb10 100644 --- a/server/crates/arbiter-server/tests/user_agent/unseal.rs +++ b/server/crates/arbiter-server/tests/user_agent/unseal.rs @@ -69,7 +69,7 @@ async fn client_dh_encrypt( #[tokio::test] #[test_log::test] -pub async fn test_unseal_success() { +pub async fn unseal_success() { let seal_key = b"test-seal-key"; let (_db, user_agent) = setup_sealed_user_agent(seal_key).await; @@ -81,7 +81,7 @@ pub async fn test_unseal_success() { #[tokio::test] #[test_log::test] -pub async fn test_unseal_wrong_seal_key() { +pub async fn unseal_wrong_seal_key() { let (_db, user_agent) = setup_sealed_user_agent(b"correct-key").await; let encrypted_key = client_dh_encrypt(&user_agent, b"wrong-key").await; @@ -97,7 +97,7 @@ pub async fn test_unseal_wrong_seal_key() { #[tokio::test] #[test_log::test] -pub async fn test_unseal_corrupted_ciphertext() { +pub async fn unseal_corrupted_ciphertext() { let (_db, user_agent) = setup_sealed_user_agent(b"test-key").await; let client_secret = EphemeralSecret::random(); @@ -128,7 +128,7 @@ pub async fn test_unseal_corrupted_ciphertext() { #[tokio::test] #[test_log::test] -pub async fn test_unseal_retry_after_invalid_key() { +pub async fn unseal_retry_after_invalid_key() { let seal_key = b"real-seal-key"; let (_db, user_agent) = setup_sealed_user_agent(seal_key).await; -- 2.49.1 From 41b3fc5d398f5ca5e4ff73a46e4959b1b17e961d Mon Sep 17 00:00:00 2001 From: CleverWild Date: Fri, 10 Apr 2026 01:00:21 +0200 Subject: [PATCH 2/2] fix(lints): remove unstable ones --- server/Cargo.toml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/server/Cargo.toml b/server/Cargo.toml index a58b31a..e56de12 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -44,10 +44,7 @@ unstable_features = "deny" deprecated_safe_2024 = "warn" ffi_unwind_calls = "warn" -fuzzy_provenance_casts = "warn" linker_messages = "warn" -lossy_provenance_casts = "warn" -must_not_suspend = "warn" elided_lifetimes_in_paths = "warn" explicit_outlives_requirements = "warn" @@ -59,7 +56,6 @@ unused_lifetimes = "warn" macro_use_extern_crate = "warn" redundant_imports = "warn" -unqualified_local_imports = "warn" unused_import_braces = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" @@ -128,8 +124,6 @@ mutex_integer = "warn" needless_raw_strings = "warn" non_ascii_literal = "warn" non_zero_suggestions = "warn" -panic = "warn" -panic_in_result_fn = "warn" pathbuf_init_then_push = "warn" pointer_format = "warn" precedence_bits = "warn" @@ -157,13 +151,22 @@ unnecessary_self_imports = "warn" unneeded_field_pattern = "warn" unused_result_ok = "warn" verbose_file_reads = "warn" + +# cargo lints +negative_feature_names = "warn" +redundant_feature_names = "warn" wildcard_dependencies = "warn" -# prod panicking macro. ENABLE BY THE FIRST MAJOR VERSION!! + +# ENABLE BY THE FIRST MAJOR VERSION!! # todo = "warn" # unimplemented = "warn" +# panic = "warn" +# panic_in_result_fn = "warn" +# +# cargo_common_metadata = "warn" +# multiple_crate_versions = "warn" # a controversial option since it's really difficult to maintain disallowed_methods = "deny" -cargo = { level = "warn", priority = -1 } nursery = { level = "warn", priority = -1 } pedantic = { level = "warn", priority = -1 } -- 2.49.1