diff --git a/docs/gcp-kms-signer.md b/docs/gcp-kms-signer.md new file mode 100644 index 0000000..9d5e2f7 --- /dev/null +++ b/docs/gcp-kms-signer.md @@ -0,0 +1,180 @@ +# Configure Rebalancer with GCP KMS Signer + +This guide shows how to configure the rebalancer to use Google Cloud KMS with: +- `type = "gcp_kms"` +- `project_id` +- `location` +- `keyring` +- `key_name` +- `key_version` + +It matches the current implementation in `rebalancer/src/signer.rs` and uses ADC (Application Default Credentials). + +## Prerequisites + +- `gcloud` CLI authenticated to the target project. +- A GCP project with Cloud KMS API enabled. +- A secp256k1 asymmetric signing key version in Cloud KMS. + +Enable API if needed: + +```bash +gcloud services enable cloudkms.googleapis.com --project +``` + +## 1) Create key ring and secp256k1 key (if you do not already have one) + +```bash +export PROJECT_ID= +export LOCATION=us-central1 +export KEYRING=solver-keyring +export KEY_NAME=solver-key + +gcloud kms keyrings create "$KEYRING" \ + --location "$LOCATION" \ + --project "$PROJECT_ID" + +gcloud kms keys create "$KEY_NAME" \ + --location "$LOCATION" \ + --keyring "$KEYRING" \ + --purpose "asymmetric-signing" \ + --default-algorithm "ec-sign-secp256k1-sha256" \ + --protection-level "hsm" \ + --project "$PROJECT_ID" +``` + +List versions (pick an enabled version, usually `1` initially): + +```bash +gcloud kms keys versions list \ + --location "$LOCATION" \ + --keyring "$KEYRING" \ + --key "$KEY_NAME" \ + --project "$PROJECT_ID" +``` + +## 2) Set up runtime credentials (ADC) + +The service uses Google ADC. Common options: + +1. Local user OAuth ADC (recommended for local dev): + +```bash +gcloud auth application-default login +gcloud auth application-default set-quota-project "$PROJECT_ID" +gcloud auth application-default print-access-token +``` + +2. Service account JSON ADC: + +```bash +export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json +gcloud auth application-default print-access-token +``` + +3. Workload Identity / GCE / GKE metadata credentials: +- no env var needed, ADC resolves from environment metadata. + +Important: +- `gcloud auth login` is not sufficient by itself for ADC. +- You need `gcloud auth application-default login` (or a service account / metadata identity). + +## 3) Grant minimal IAM permissions + +Grant the runtime identity permissions on the target key version/key: + +- `cloudkms.cryptoKeyVersions.useToSign` +- `cloudkms.cryptoKeyVersions.viewPublicKey` + +These are included by roles such as `roles/cloudkms.signerVerifier`. + +Example binding: + +```bash +gcloud kms keys add-iam-policy-binding "$KEY_NAME" \ + --location "$LOCATION" \ + --keyring "$KEYRING" \ + --member "serviceAccount:" \ + --role "roles/cloudkms.signerVerifier" \ + --project "$PROJECT_ID" +``` + +## 4) Configure `rebalancer.toml` + +```toml +[[chains]] +name = "sepolia" +chain_id = 11155111 +domain_id = 11155111 +rpc_url = "https://ethereum-sepolia-rpc.publicnode.com" +account = "0x" + + [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = "us-central1" + keyring = "solver-keyring" + key_name = "solver-key" + key_version = 1 +``` + +Important runtime behavior: +- Startup enforces signer/account match. +- If `chains.account` does not match the KMS-derived address, startup fails with `Signer/account mismatch`. + +## 5) Optional key checks + +Fetch the KMS public key for inspection: + +```bash +gcloud kms keys versions get-public-key 1 \ + --location "$LOCATION" \ + --keyring "$KEYRING" \ + --key "$KEY_NAME" \ + --project "$PROJECT_ID" +``` + +## 6) Troubleshooting + +- `None of the possible sources detected for Google OAuth token` + - the process cannot find ADC credentials. + - fix with one of: + - `gcloud auth application-default login` + - `export GOOGLE_APPLICATION_CREDENTIALS=/abs/path/service-account.json` + - run on GCE/GKE/Cloud Run with attached workload identity. + +- `token source error: not found token source` + - same root cause as above: no ADC token source available to the process. + - verify in the same shell/session used to start rebalancer: + +```bash +echo "$GOOGLE_APPLICATION_CREDENTIALS" +ls -l ~/.config/gcloud/application_default_credentials.json +gcloud auth application-default print-access-token +``` + +- `make rebalancer-start` still cannot see credentials + - `make` does not automatically load `.env`; export credentials in the active shell first. + - example: + +```bash +export GOOGLE_APPLICATION_CREDENTIALS=/abs/path/service-account.json +make rebalancer-start +``` + +- `Failed to initialize gcp_kms signer for chain ...` + - ADC credentials not available + - wrong `project_id` / `location` / `keyring` / `key_name` / `key_version` + - missing KMS IAM permissions + +- `Signer/account mismatch ...` + - configured `account` does not match KMS key address used by signer + +- permission denied from KMS + - ensure the runtime identity has signing and public key permissions on that key + +## Security notes + +- Prefer a dedicated KMS key per environment. +- Restrict IAM bindings to least privilege and only required key resources. +- Do not commit service-account credential files. diff --git a/rebalancer/Cargo.lock b/rebalancer/Cargo.lock index ef2e395..164cb16 100644 --- a/rebalancer/Cargo.lock +++ b/rebalancer/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" version = "1.1.4" @@ -37,6 +43,7 @@ dependencies = [ "alloy-serde", "alloy-signer", "alloy-signer-aws", + "alloy-signer-gcp", "alloy-signer-local", "alloy-transport", "alloy-transport-http", @@ -367,7 +374,7 @@ dependencies = [ "futures-utils-wasm", "lru", "parking_lot", - "pin-project", + "pin-project 1.1.10", "reqwest", "serde", "serde_json", @@ -436,7 +443,7 @@ dependencies = [ "alloy-transport-ipc", "alloy-transport-ws", "futures", - "pin-project", + "pin-project 1.1.10", "reqwest", "serde", "serde_json", @@ -609,6 +616,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-signer-gcp" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "485d0c73c53a36580be4d882a5c6c9a069759088de88ff759e59342a793adb16" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "gcloud-sdk", + "k256", + "spki", + "thiserror", + "tracing", +] + [[package]] name = "alloy-signer-local" version = "1.7.3" @@ -749,7 +774,7 @@ dependencies = [ "bytes", "futures", "interprocess", - "pin-project", + "pin-project 1.1.10", "serde", "serde_json", "tokio", @@ -1066,6 +1091,18 @@ dependencies = [ "serde", ] +[[package]] +name = "async-compression" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" +dependencies = [ + "compression-codecs", + "compression-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -1543,6 +1580,49 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core", + "bytes", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -1819,6 +1899,23 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "compression-codecs" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +dependencies = [ + "compression-core", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + [[package]] name = "const-hex" version = "1.17.0" @@ -1916,6 +2013,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -2269,6 +2375,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2417,6 +2533,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" +[[package]] +name = "gcloud-sdk" +version = "0.27.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8458d2ad7741b6a16981b84e66b7e4d8026423096da721894769c6980d06ecc" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures", + "hyper", + "jsonwebtoken", + "once_cell", + "prost 0.13.5", + "prost-types 0.13.5", + "reqwest", + "secret-vault-value", + "serde", + "serde_json", + "tokio", + "tonic", + "tower", + "tower-layer", + "tower-util", + "tracing", + "url", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2635,6 +2779,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.8.1" @@ -2649,6 +2799,7 @@ dependencies = [ "http 1.4.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "pin-utils", @@ -2675,6 +2826,19 @@ dependencies = [ "webpki-roots 1.0.6", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -2708,7 +2872,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.2", "system-configuration", "tokio", "tower-service", @@ -2987,6 +3151,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "k256" version = "0.13.4" @@ -3106,6 +3285,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "memchr" version = "2.8.0" @@ -3118,6 +3303,26 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.1.1" @@ -3372,6 +3577,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -3407,13 +3622,33 @@ dependencies = [ "rustc_version 0.4.1", ] +[[package]] +name = "pin-project" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +dependencies = [ + "pin-project-internal 0.4.30", +] + [[package]] name = "pin-project" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ - "pin-project-internal", + "pin-project-internal 1.1.10", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -3568,6 +3803,70 @@ dependencies = [ "unarray", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive 0.13.5", +] + +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive 0.14.3", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost 0.13.5", +] + +[[package]] +name = "prost-types" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +dependencies = [ + "prost 0.14.3", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3587,7 +3886,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", + "socket2 0.6.2", "thiserror", "tokio", "tracing", @@ -3624,7 +3923,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.6.2", "tracing", "windows-sys 0.60.2", ] @@ -3741,6 +4040,7 @@ dependencies = [ "clap", "hex", "reqwest", + "rustls", "serde", "sha2", "tokio", @@ -3817,6 +4117,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-core", + "futures-util", "h2", "http 1.4.0", "http-body 1.0.1", @@ -3828,11 +4129,13 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "percent-encoding", "pin-project-lite", "quinn", "rustls", + "rustls-native-certs", "rustls-pki-types", "serde", "serde_json", @@ -3841,12 +4144,14 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 1.0.6", ] @@ -3969,6 +4274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "aws-lc-rs", + "log", "once_cell", "ring", "rustls-pki-types", @@ -4110,6 +4416,19 @@ dependencies = [ "cc", ] +[[package]] +name = "secret-vault-value" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662c7f8e99d46c9d3a87561d771a970c29efaccbab4bbdc6ab65d099d2358077" +dependencies = [ + "prost 0.14.3", + "prost-types 0.14.3", + "serde", + "serde_json", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -4358,6 +4677,24 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "simple_asn1" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + [[package]] name = "slab" version = "0.4.12" @@ -4373,6 +4710,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.2" @@ -4638,7 +4985,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.2", "tokio-macros", "windows-sys 0.61.2", ] @@ -4766,6 +5113,37 @@ version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +[[package]] +name = "tonic" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project 1.1.10", + "prost 0.13.5", + "rustls-native-certs", + "socket2 0.5.10", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.5.3" @@ -4774,11 +5152,15 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", + "indexmap 2.13.0", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -4787,13 +5169,18 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ + "async-compression", "bitflags", "bytes", + "futures-core", "futures-util", "http 1.4.0", "http-body 1.0.1", + "http-body-util", "iri-string", "pin-project-lite", + "tokio", + "tokio-util", "tower", "tower-layer", "tower-service", @@ -4811,6 +5198,18 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +[[package]] +name = "tower-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" +dependencies = [ + "futures-core", + "futures-util", + "pin-project 0.4.30", + "tower-service", +] + [[package]] name = "tracing" version = "0.1.44" @@ -4927,6 +5326,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -5146,6 +5551,19 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" diff --git a/rebalancer/Cargo.toml b/rebalancer/Cargo.toml index 922d1af..0503ab4 100644 --- a/rebalancer/Cargo.toml +++ b/rebalancer/Cargo.toml @@ -18,6 +18,7 @@ alloy = { version = "1.0", features = [ "json-rpc", "rpc-types", "signer-aws", + "signer-gcp", ] } anyhow = "1.0" aws-config = { version = "=1.8.13", features = ["rt-tokio", "credentials-login"] } @@ -25,6 +26,7 @@ bech32 = "0.11" clap = { version = "4.4", features = ["derive", "env"] } hex = "0.4" reqwest = { version = "0.12", features = ["json"] } +rustls = { version = "0.23", default-features = false, features = ["std", "aws_lc_rs"] } serde = { version = "1.0", features = ["derive"] } sha2 = "0.10" tokio = { version = "1.34", features = ["full"] } diff --git a/rebalancer/SPEC.md b/rebalancer/SPEC.md index 5c83ba4..257420e 100644 --- a/rebalancer/SPEC.md +++ b/rebalancer/SPEC.md @@ -84,7 +84,7 @@ domain_id = 11155111 # optional; defaults to chain_id rpc_url = "https://ethereum-sepolia-rpc.publicnode.com" account = "rebalancer" [chains.signer] - type = "env" # env | file | aws_kms + type = "env" # env | file | aws_kms | gcp_kms [[chains]] name = "eden" @@ -152,6 +152,12 @@ Signer rules: 1. `type = "env"` supports only `type`. 2. `type = "file"` requires `key`. 3. `type = "aws_kms"` requires `key_id` and `region`. +4. `type = "gcp_kms"` requires: + - `project_id` + - `location` + - `keyring` + - `key_name` + - `key_version` (`> 0`) `env` signer runtime key lookup order: 1. `REBALANCER__PK` diff --git a/rebalancer/src/config.rs b/rebalancer/src/config.rs index 5a6ad6b..efcafac 100644 --- a/rebalancer/src/config.rs +++ b/rebalancer/src/config.rs @@ -45,6 +45,13 @@ pub enum SignerConfig { Env, File { key: String }, AwsKms { key_id: String, region: String }, + GcpKms { + project_id: String, + location: String, + keyring: String, + key_name: String, + key_version: u32, + }, } #[derive(Debug, Clone)] @@ -148,6 +155,7 @@ enum RawSignerType { Env, File, AwsKms, + GcpKms, } #[derive(Debug, Deserialize)] @@ -158,6 +166,11 @@ struct RawSignerConfig { key: Option, key_id: Option, region: Option, + project_id: Option, + location: Option, + keyring: Option, + key_name: Option, + key_version: Option, env_var: Option, } @@ -533,13 +546,29 @@ fn parse_signer_config(value: Option) -> Result { if value.env_var.is_some() { bail!("chains.signer.env_var is not supported for type = \"env\""); } - if value.key.is_some() || value.key_id.is_some() || value.region.is_some() { + if value.key.is_some() + || value.key_id.is_some() + || value.region.is_some() + || value.project_id.is_some() + || value.location.is_some() + || value.keyring.is_some() + || value.key_name.is_some() + || value.key_version.is_some() + { bail!("chains.signer type = \"env\" only accepts field \"type\""); } SignerConfig::Env } RawSignerType::File => { - if value.env_var.is_some() || value.key_id.is_some() || value.region.is_some() { + if value.env_var.is_some() + || value.key_id.is_some() + || value.region.is_some() + || value.project_id.is_some() + || value.location.is_some() + || value.keyring.is_some() + || value.key_name.is_some() + || value.key_version.is_some() + { bail!("chains.signer type = \"file\" only accepts fields \"type\" and \"key\""); } let key = value.key.ok_or_else(|| { @@ -551,7 +580,14 @@ fn parse_signer_config(value: Option) -> Result { SignerConfig::File { key } } RawSignerType::AwsKms => { - if value.env_var.is_some() || value.key.is_some() { + if value.env_var.is_some() + || value.key.is_some() + || value.project_id.is_some() + || value.location.is_some() + || value.keyring.is_some() + || value.key_name.is_some() + || value.key_version.is_some() + { bail!( "chains.signer type = \"aws_kms\" only accepts fields \"type\", \"key_id\", and \"region\"" ); @@ -570,6 +606,54 @@ fn parse_signer_config(value: Option) -> Result { } SignerConfig::AwsKms { key_id, region } } + RawSignerType::GcpKms => { + if value.env_var.is_some() + || value.key.is_some() + || value.key_id.is_some() + || value.region.is_some() + { + bail!( + "chains.signer type = \"gcp_kms\" only accepts fields \"type\", \"project_id\", \"location\", \"keyring\", \"key_name\", and \"key_version\"" + ); + } + let project_id = value.project_id.ok_or_else(|| { + anyhow::anyhow!("chains.signer.project_id is required for type = \"gcp_kms\"") + })?; + let location = value.location.ok_or_else(|| { + anyhow::anyhow!("chains.signer.location is required for type = \"gcp_kms\"") + })?; + let keyring = value.keyring.ok_or_else(|| { + anyhow::anyhow!("chains.signer.keyring is required for type = \"gcp_kms\"") + })?; + let key_name = value.key_name.ok_or_else(|| { + anyhow::anyhow!("chains.signer.key_name is required for type = \"gcp_kms\"") + })?; + let key_version = value.key_version.ok_or_else(|| { + anyhow::anyhow!("chains.signer.key_version is required for type = \"gcp_kms\"") + })?; + if project_id.trim().is_empty() { + bail!("chains.signer.project_id cannot be empty for type = \"gcp_kms\""); + } + if location.trim().is_empty() { + bail!("chains.signer.location cannot be empty for type = \"gcp_kms\""); + } + if keyring.trim().is_empty() { + bail!("chains.signer.keyring cannot be empty for type = \"gcp_kms\""); + } + if key_name.trim().is_empty() { + bail!("chains.signer.key_name cannot be empty for type = \"gcp_kms\""); + } + if key_version == 0 { + bail!("chains.signer.key_version must be > 0 for type = \"gcp_kms\""); + } + SignerConfig::GcpKms { + project_id, + location, + keyring, + key_name, + key_version, + } + } }; Ok(signer) @@ -633,6 +717,56 @@ fn normalize_env_key(value: &str) -> String { mod tests { use super::*; + fn config_with_first_chain_signer_block(signer_block: &str) -> String { + format!( + r#" +dry_run = true + +[forwarding] +domain_id = 69420 +service_url = "http://127.0.0.1:8080" + +[accounts] +rebalancer = "0x000000000000000000000000000000000000dEaD" + +[[chains]] +name = "evolve" +chain_id = 1234 +rpc_url = "http://127.0.0.1:8545" +account = "rebalancer" +{signer_block} + +[[chains]] +name = "sepolia" +chain_id = 11155111 +rpc_url = "https://rpc.sepolia.org" +account = "rebalancer" + [chains.signer] + type = "env" + +[[assets]] +symbol = "USDC" +decimals = 6 + + [[assets.tokens]] + chain_id = 1234 + address = "0x0000000000000000000000000000000000000001" + + [[assets.tokens]] + chain_id = 11155111 + address = "0x0000000000000000000000000000000000000002" + + [assets.weights] + "1234" = 0.50 + "11155111" = 0.50 + + [assets.min_weights] + "1234" = 0.40 + "11155111" = 0.40 +"# + ) + } + #[test] fn accepts_valid_config() { let toml = r#" @@ -965,6 +1099,263 @@ decimals = 6 assert!(err.to_string().contains("Invalid signer config for chain")); } + #[test] + fn accepts_valid_gcp_kms_signer_config() { + let toml = config_with_first_chain_signer_block( + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = "us-central1" + keyring = "solver-keyring" + key_name = "solver-key" + key_version = 1 +"#, + ); + + let raw: RawRebalancerConfig = toml::from_str(&toml).expect("valid TOML"); + let config = RebalancerConfig::from_raw(raw).expect("valid config"); + let chain = config + .chains + .iter() + .find(|chain| chain.name == "evolve") + .expect("missing evolve chain"); + + match &chain.signer { + SignerConfig::GcpKms { + project_id, + location, + keyring, + key_name, + key_version, + } => { + assert_eq!(project_id, "my-gcp-project"); + assert_eq!(location, "us-central1"); + assert_eq!(keyring, "solver-keyring"); + assert_eq!(key_name, "solver-key"); + assert_eq!(*key_version, 1); + } + signer => panic!("expected gcp_kms signer, got {:?}", signer), + } + } + + #[test] + fn rejects_missing_gcp_kms_project_id() { + let toml = config_with_first_chain_signer_block( + r#" [chains.signer] + type = "gcp_kms" + location = "us-central1" + keyring = "solver-keyring" + key_name = "solver-key" + key_version = 1 +"#, + ); + + let raw: RawRebalancerConfig = toml::from_str(&toml).expect("valid TOML"); + let err = RebalancerConfig::from_raw(raw).expect_err("should fail"); + let details = format!("{err:#}"); + assert!( + details.contains("chains.signer.project_id is required for type = \"gcp_kms\""), + "unexpected error: {details}" + ); + } + + #[test] + fn rejects_missing_gcp_kms_location() { + let toml = config_with_first_chain_signer_block( + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + keyring = "solver-keyring" + key_name = "solver-key" + key_version = 1 +"#, + ); + + let raw: RawRebalancerConfig = toml::from_str(&toml).expect("valid TOML"); + let err = RebalancerConfig::from_raw(raw).expect_err("should fail"); + let details = format!("{err:#}"); + assert!( + details.contains("chains.signer.location is required for type = \"gcp_kms\""), + "unexpected error: {details}" + ); + } + + #[test] + fn rejects_missing_gcp_kms_keyring() { + let toml = config_with_first_chain_signer_block( + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = "us-central1" + key_name = "solver-key" + key_version = 1 +"#, + ); + + let raw: RawRebalancerConfig = toml::from_str(&toml).expect("valid TOML"); + let err = RebalancerConfig::from_raw(raw).expect_err("should fail"); + let details = format!("{err:#}"); + assert!( + details.contains("chains.signer.keyring is required for type = \"gcp_kms\""), + "unexpected error: {details}" + ); + } + + #[test] + fn rejects_missing_gcp_kms_key_name() { + let toml = config_with_first_chain_signer_block( + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = "us-central1" + keyring = "solver-keyring" + key_version = 1 +"#, + ); + + let raw: RawRebalancerConfig = toml::from_str(&toml).expect("valid TOML"); + let err = RebalancerConfig::from_raw(raw).expect_err("should fail"); + let details = format!("{err:#}"); + assert!( + details.contains("chains.signer.key_name is required for type = \"gcp_kms\""), + "unexpected error: {details}" + ); + } + + #[test] + fn rejects_missing_gcp_kms_key_version() { + let toml = config_with_first_chain_signer_block( + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = "us-central1" + keyring = "solver-keyring" + key_name = "solver-key" +"#, + ); + + let raw: RawRebalancerConfig = toml::from_str(&toml).expect("valid TOML"); + let err = RebalancerConfig::from_raw(raw).expect_err("should fail"); + let details = format!("{err:#}"); + assert!( + details.contains("chains.signer.key_version is required for type = \"gcp_kms\""), + "unexpected error: {details}" + ); + } + + #[test] + fn rejects_empty_gcp_kms_string_fields() { + let cases = [ + ( + "project_id", + r#" [chains.signer] + type = "gcp_kms" + project_id = " " + location = "us-central1" + keyring = "solver-keyring" + key_name = "solver-key" + key_version = 1 +"#, + "chains.signer.project_id cannot be empty for type = \"gcp_kms\"", + ), + ( + "location", + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = " " + keyring = "solver-keyring" + key_name = "solver-key" + key_version = 1 +"#, + "chains.signer.location cannot be empty for type = \"gcp_kms\"", + ), + ( + "keyring", + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = "us-central1" + keyring = " " + key_name = "solver-key" + key_version = 1 +"#, + "chains.signer.keyring cannot be empty for type = \"gcp_kms\"", + ), + ( + "key_name", + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = "us-central1" + keyring = "solver-keyring" + key_name = " " + key_version = 1 +"#, + "chains.signer.key_name cannot be empty for type = \"gcp_kms\"", + ), + ]; + + for (field, signer_block, expected) in cases { + let toml = config_with_first_chain_signer_block(signer_block); + let raw: RawRebalancerConfig = toml::from_str(&toml).expect("valid TOML"); + let err = RebalancerConfig::from_raw(raw).expect_err("should fail"); + let details = format!("{err:#}"); + assert!( + details.contains(expected), + "expected error for field {}", + field + ); + } + } + + #[test] + fn rejects_zero_gcp_kms_key_version() { + let toml = config_with_first_chain_signer_block( + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = "us-central1" + keyring = "solver-keyring" + key_name = "solver-key" + key_version = 0 +"#, + ); + + let raw: RawRebalancerConfig = toml::from_str(&toml).expect("valid TOML"); + let err = RebalancerConfig::from_raw(raw).expect_err("should fail"); + let details = format!("{err:#}"); + assert!( + details.contains("chains.signer.key_version must be > 0 for type = \"gcp_kms\""), + "unexpected error: {details}" + ); + } + + #[test] + fn rejects_non_gcp_fields_for_gcp_kms_signer_type() { + let toml = config_with_first_chain_signer_block( + r#" [chains.signer] + type = "gcp_kms" + project_id = "my-gcp-project" + location = "us-central1" + keyring = "solver-keyring" + key_name = "solver-key" + key_version = 1 + key = "0x1234" +"#, + ); + + let raw: RawRebalancerConfig = toml::from_str(&toml).expect("valid TOML"); + let err = RebalancerConfig::from_raw(raw).expect_err("should fail"); + let details = format!("{err:#}"); + assert!( + details.contains( + "chains.signer type = \"gcp_kms\" only accepts fields \"type\", \"project_id\", \"location\", \"keyring\", \"key_name\", and \"key_version\"" + ), + "unexpected error: {details}" + ); + } + #[test] fn supports_optional_collateral_token_with_fallback_to_address() { let toml = r#" diff --git a/rebalancer/src/lib.rs b/rebalancer/src/lib.rs index 2b64d93..c7d5b27 100644 --- a/rebalancer/src/lib.rs +++ b/rebalancer/src/lib.rs @@ -7,11 +7,23 @@ pub mod signer; use anyhow::Result; use std::path::Path; +use std::sync::Once; use config::RebalancerConfig; use service::RebalancerService; +static RUSTLS_PROVIDER_INIT: Once = Once::new(); + +fn install_rustls_crypto_provider() { + RUSTLS_PROVIDER_INIT.call_once(|| { + // This crate can pull both `ring` and `aws-lc-rs`; pick one explicitly. + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + }); +} + pub async fn run_from_config(config_path: &Path, once: bool) -> Result<()> { + install_rustls_crypto_provider(); + let config = RebalancerConfig::load(config_path)?; let mut service = RebalancerService::new(config).await?; diff --git a/rebalancer/src/signer.rs b/rebalancer/src/signer.rs index ad10ac6..a4ecba6 100644 --- a/rebalancer/src/signer.rs +++ b/rebalancer/src/signer.rs @@ -6,6 +6,13 @@ use alloy::{ aws_sdk_kms::{self, config::Region}, AwsSigner, }, + signers::gcp::{ + gcloud_sdk::{ + google::cloud::kms::v1::key_management_service_client::KeyManagementServiceClient, + GoogleApi, + }, + GcpKeyRingRef, GcpSigner, KeySpecifier, + }, signers::{local::PrivateKeySigner, Signer}, }; use anyhow::{bail, Context, Result}; @@ -32,6 +39,22 @@ impl TxSigner { }; backend.load_signer(chain).await } + SignerConfig::GcpKms { + project_id, + location, + keyring, + key_name, + key_version, + } => { + let backend = GcpKmsRemoteSignerBackend { + project_id: project_id.clone(), + location: location.clone(), + keyring: keyring.clone(), + key_name: key_name.clone(), + key_version: *key_version, + }; + backend.load_signer(chain).await + } } } @@ -92,6 +115,53 @@ impl AwsKmsRemoteSignerBackend { } } +#[derive(Debug, Clone)] +pub struct GcpKmsRemoteSignerBackend { + project_id: String, + location: String, + keyring: String, + key_name: String, + key_version: u32, +} + +impl GcpKmsRemoteSignerBackend { + async fn load_signer(&self, chain: &ChainConfig) -> Result { + let keyring = GcpKeyRingRef { + google_project_id: self.project_id.clone(), + location: self.location.clone(), + name: self.keyring.clone(), + }; + let key_specifier = KeySpecifier::new(keyring, &self.key_name, self.key_version.into()); + + let kms_client = GoogleApi::from_function( + KeyManagementServiceClient::new, + "https://cloudkms.googleapis.com", + None, + ) + .await + .with_context(|| { + format!( + "Failed to initialize gcp_kms signer for chain {}", + chain.name + ) + })?; + let signer = GcpSigner::new(kms_client, key_specifier, None) + .await + .with_context(|| { + format!( + "Failed to initialize gcp_kms signer for chain {}", + chain.name + ) + })?; + let address = signer.address(); + + Ok(TxSigner { + address, + wallet: EthereumWallet::from(signer), + }) + } +} + fn normalize_env_key(name: &str) -> String { name.chars() .map(|c| { diff --git a/solver-cli/Cargo.lock b/solver-cli/Cargo.lock index a2abcc9..5405191 100644 --- a/solver-cli/Cargo.lock +++ b/solver-cli/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "ahash" version = "0.7.8" @@ -48,6 +54,7 @@ dependencies = [ "alloy-serde", "alloy-signer", "alloy-signer-aws", + "alloy-signer-gcp", "alloy-signer-local", "alloy-transport", "alloy-transport-http", @@ -378,7 +385,7 @@ dependencies = [ "futures-utils-wasm", "lru", "parking_lot", - "pin-project", + "pin-project 1.1.10", "reqwest", "serde", "serde_json", @@ -447,7 +454,7 @@ dependencies = [ "alloy-transport-ipc", "alloy-transport-ws", "futures", - "pin-project", + "pin-project 1.1.10", "reqwest", "serde", "serde_json", @@ -621,6 +628,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-signer-gcp" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2115c08b1ba42a4110085bdd6f3d97db0d0c83fe85f4f0d1849341c16d8ce083" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "gcloud-sdk", + "k256", + "spki", + "thiserror 2.0.18", + "tracing", +] + [[package]] name = "alloy-signer-local" version = "1.6.1" @@ -763,7 +788,7 @@ dependencies = [ "bytes", "futures", "interprocess", - "pin-project", + "pin-project 1.1.10", "serde", "serde_json", "tokio", @@ -1104,6 +1129,18 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "async-compression" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" +dependencies = [ + "compression-codecs", + "compression-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -1982,6 +2019,23 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "compression-codecs" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +dependencies = [ + "compression-core", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + [[package]] name = "console" version = "0.15.11" @@ -2092,6 +2146,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -2520,6 +2583,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "float-cmp" version = "0.10.0" @@ -2682,6 +2755,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" +[[package]] +name = "gcloud-sdk" +version = "0.27.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8458d2ad7741b6a16981b84e66b7e4d8026423096da721894769c6980d06ecc" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures", + "hyper", + "jsonwebtoken", + "once_cell", + "prost 0.13.5", + "prost-types 0.13.5", + "reqwest", + "secret-vault-value", + "serde", + "serde_json", + "tokio", + "tonic", + "tower", + "tower-layer", + "tower-util", + "tracing", + "url", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2928,6 +3029,19 @@ dependencies = [ "webpki-roots 1.0.6", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -2961,7 +3075,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.2", "system-configuration", "tokio", "tower-service", @@ -3427,6 +3541,26 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.1.1" @@ -3782,13 +3916,33 @@ dependencies = [ "rustc_version 0.4.1", ] +[[package]] +name = "pin-project" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +dependencies = [ + "pin-project-internal 0.4.30", +] + [[package]] name = "pin-project" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ - "pin-project-internal", + "pin-project-internal 1.1.10", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -3978,6 +4132,70 @@ dependencies = [ "unarray", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive 0.13.5", +] + +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive 0.14.3", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost 0.13.5", +] + +[[package]] +name = "prost-types" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +dependencies = [ + "prost 0.14.3", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -4017,7 +4235,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", + "socket2 0.6.2", "thiserror 2.0.18", "tokio", "tracing", @@ -4054,7 +4272,7 @@ dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2", + "socket2 0.6.2", "tracing", "windows-sys 0.60.2", ] @@ -4171,6 +4389,7 @@ dependencies = [ "clap", "hex", "reqwest", + "rustls", "serde", "sha2", "tokio", @@ -4204,7 +4423,7 @@ dependencies = [ "pin-project-lite", "ryu", "sha1_smol", - "socket2", + "socket2 0.6.2", "tokio", "tokio-util", "url", @@ -4304,6 +4523,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-core", + "futures-util", "h2", "http 1.4.0", "http-body 1.0.1", @@ -4315,11 +4535,13 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "percent-encoding", "pin-project-lite", "quinn", "rustls", + "rustls-native-certs", "rustls-pki-types", "serde", "serde_json", @@ -4328,12 +4550,14 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 1.0.6", ] @@ -4501,6 +4725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "aws-lc-rs", + "log", "once_cell", "ring", "rustls-pki-types", @@ -4668,6 +4893,19 @@ dependencies = [ "cc", ] +[[package]] +name = "secret-vault-value" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662c7f8e99d46c9d3a87561d771a970c29efaccbab4bbdc6ab65d099d2358077" +dependencies = [ + "prost 0.14.3", + "prost-types 0.14.3", + "serde", + "serde_json", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -4952,6 +5190,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "simdutf8" version = "0.1.5" @@ -4985,6 +5229,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.2" @@ -5569,7 +5823,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.2", "tokio-macros", "windows-sys 0.61.2", ] @@ -5697,6 +5951,37 @@ version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +[[package]] +name = "tonic" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project 1.1.10", + "prost 0.13.5", + "rustls-native-certs", + "socket2 0.5.10", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.5.3" @@ -5705,9 +5990,12 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", + "indexmap 2.13.0", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -5719,13 +6007,18 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ + "async-compression", "bitflags", "bytes", + "futures-core", "futures-util", "http 1.4.0", "http-body 1.0.1", + "http-body-util", "iri-string", "pin-project-lite", + "tokio", + "tokio-util", "tower", "tower-layer", "tower-service", @@ -5743,6 +6036,18 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +[[package]] +name = "tower-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" +dependencies = [ + "futures-core", + "futures-util", + "pin-project 0.4.30", + "tower-service", +] + [[package]] name = "tracing" version = "0.1.44" @@ -5889,6 +6194,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -6090,6 +6401,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmtimer" version = "0.4.3"