merge: new flow into main

This commit is contained in:
hdbg
2026-03-22 12:23:07 +01:00
24 changed files with 995 additions and 206 deletions

View File

@@ -1,16 +1,37 @@
# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html
[[tools.ast-grep]] [[tools.ast-grep]]
version = "0.42.0" version = "0.42.0"
backend = "aqua:ast-grep/ast-grep" backend = "aqua:ast-grep/ast-grep"
"platforms.linux-arm64" = { checksum = "sha256:5c830eae8456569e2f7212434ed9c238f58dca412d76045418ed6d394a755836", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-aarch64-unknown-linux-gnu.zip"}
"platforms.linux-x64" = { checksum = "sha256:e825a05603f0bcc4cd9076c4cc8c9abd6d008b7cd07d9aa3cc323ba4b8606651", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-x86_64-unknown-linux-gnu.zip"} [tools.ast-grep."platforms.linux-arm64"]
"platforms.macos-arm64" = { checksum = "sha256:fc300d5293b1c770a5aece03a8a193b92e71e87cec726c28096990691a582620", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-aarch64-apple-darwin.zip"} checksum = "sha256:5c830eae8456569e2f7212434ed9c238f58dca412d76045418ed6d394a755836"
"platforms.macos-x64" = { checksum = "sha256:979ffe611327056f4730a1ae71b0209b3b830f58b22c6ed194cda34f55400db2", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-x86_64-apple-darwin.zip"} url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-aarch64-unknown-linux-gnu.zip"
"platforms.windows-x64" = { checksum = "sha256:55836fa1b2c65dc7d61615a4d9368622a0d2371a76d28b9a165e5a3ab6ae32a4", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-x86_64-pc-windows-msvc.zip"}
[tools.ast-grep."platforms.linux-x64"]
checksum = "sha256:e825a05603f0bcc4cd9076c4cc8c9abd6d008b7cd07d9aa3cc323ba4b8606651"
url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-x86_64-unknown-linux-gnu.zip"
[tools.ast-grep."platforms.macos-arm64"]
checksum = "sha256:fc300d5293b1c770a5aece03a8a193b92e71e87cec726c28096990691a582620"
url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-aarch64-apple-darwin.zip"
[tools.ast-grep."platforms.macos-x64"]
checksum = "sha256:979ffe611327056f4730a1ae71b0209b3b830f58b22c6ed194cda34f55400db2"
url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-x86_64-apple-darwin.zip"
[tools.ast-grep."platforms.windows-x64"]
checksum = "sha256:55836fa1b2c65dc7d61615a4d9368622a0d2371a76d28b9a165e5a3ab6ae32a4"
url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-x86_64-pc-windows-msvc.zip"
[[tools."cargo:cargo-audit"]] [[tools."cargo:cargo-audit"]]
version = "0.22.1" version = "0.22.1"
backend = "cargo:cargo-audit" backend = "cargo:cargo-audit"
[[tools."cargo:cargo-edit"]]
version = "0.13.9"
backend = "cargo:cargo-edit"
[[tools."cargo:cargo-features"]] [[tools."cargo:cargo-features"]]
version = "1.0.0" version = "1.0.0"
backend = "cargo:cargo-features" backend = "cargo:cargo-features"
@@ -62,20 +83,50 @@ backend = "asdf:flutter"
[[tools.protoc]] [[tools.protoc]]
version = "29.6" version = "29.6"
backend = "aqua:protocolbuffers/protobuf/protoc" backend = "aqua:protocolbuffers/protobuf/protoc"
"platforms.linux-arm64" = { checksum = "sha256:2594ff4fcae8cb57310d394d0961b236190ad9c5efbfdf1f597ea471d424fe79", url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-linux-aarch_64.zip"}
"platforms.linux-x64" = { checksum = "sha256:48785a926e73ffa3f68e2f22b14e7b849620c7a1d36809ac9249a5495e280323", url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-linux-x86_64.zip"} [tools.protoc."platforms.linux-arm64"]
"platforms.macos-arm64" = { checksum = "sha256:b9576b5fa1a1ef3fe13a8c91d9d8204b46545759bea5ae155cd6ba2ea4cdaeed", url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-osx-aarch_64.zip"} checksum = "sha256:2594ff4fcae8cb57310d394d0961b236190ad9c5efbfdf1f597ea471d424fe79"
"platforms.macos-x64" = { checksum = "sha256:312f04713946921cc0187ef34df80241ddca1bab6f564c636885fd2cc90d3f88", url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-osx-x86_64.zip"} url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-linux-aarch_64.zip"
"platforms.windows-x64" = { checksum = "sha256:1ebd7c87baffb9f1c47169b640872bf5fb1e4408079c691af527be9561d8f6f7", url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-win64.zip"}
[tools.protoc."platforms.linux-x64"]
checksum = "sha256:48785a926e73ffa3f68e2f22b14e7b849620c7a1d36809ac9249a5495e280323"
url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-linux-x86_64.zip"
[tools.protoc."platforms.macos-arm64"]
checksum = "sha256:b9576b5fa1a1ef3fe13a8c91d9d8204b46545759bea5ae155cd6ba2ea4cdaeed"
url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-osx-aarch_64.zip"
[tools.protoc."platforms.macos-x64"]
checksum = "sha256:312f04713946921cc0187ef34df80241ddca1bab6f564c636885fd2cc90d3f88"
url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-osx-x86_64.zip"
[tools.protoc."platforms.windows-x64"]
checksum = "sha256:1ebd7c87baffb9f1c47169b640872bf5fb1e4408079c691af527be9561d8f6f7"
url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.6/protoc-29.6-win64.zip"
[[tools.python]] [[tools.python]]
version = "3.14.3" version = "3.14.3"
backend = "core:python" backend = "core:python"
"platforms.linux-arm64" = { checksum = "sha256:be0f4dc2932f762292b27d46ea7d3e8e66ddf3969a5eb0254a229015ed402625", url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz"}
"platforms.linux-x64" = { checksum = "sha256:0a73413f89efd417871876c9accaab28a9d1e3cd6358fbfff171a38ec99302f0", url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz"} [tools.python."platforms.linux-arm64"]
"platforms.macos-arm64" = { checksum = "sha256:4703cdf18b26798fde7b49b6b66149674c25f97127be6a10dbcf29309bdcdcdb", url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-aarch64-apple-darwin-install_only_stripped.tar.gz"} checksum = "sha256:be0f4dc2932f762292b27d46ea7d3e8e66ddf3969a5eb0254a229015ed402625"
"platforms.macos-x64" = { checksum = "sha256:76f1cc26e3d262eae8ca546a93e8bded10cf0323613f7e246fea2e10a8115eb7", url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-x86_64-apple-darwin-install_only_stripped.tar.gz"} url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz"
"platforms.windows-x64" = { checksum = "sha256:950c5f21a015c1bdd1337f233456df2470fab71e4d794407d27a84cb8b9909a0", url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"}
[tools.python."platforms.linux-x64"]
checksum = "sha256:0a73413f89efd417871876c9accaab28a9d1e3cd6358fbfff171a38ec99302f0"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz"
[tools.python."platforms.macos-arm64"]
checksum = "sha256:4703cdf18b26798fde7b49b6b66149674c25f97127be6a10dbcf29309bdcdcdb"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-aarch64-apple-darwin-install_only_stripped.tar.gz"
[tools.python."platforms.macos-x64"]
checksum = "sha256:76f1cc26e3d262eae8ca546a93e8bded10cf0323613f7e246fea2e10a8115eb7"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-x86_64-apple-darwin-install_only_stripped.tar.gz"
[tools.python."platforms.windows-x64"]
checksum = "sha256:950c5f21a015c1bdd1337f233456df2470fab71e4d794407d27a84cb8b9909a0"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260303/cpython-3.14.3+20260303-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"
[[tools.rust]] [[tools.rust]]
version = "1.93.0" version = "1.93.0"

View File

@@ -11,6 +11,7 @@ protoc = "29.6"
"cargo:cargo-insta" = "1.46.3" "cargo:cargo-insta" = "1.46.3"
python = "3.14.3" python = "3.14.3"
ast-grep = "0.42.0" ast-grep = "0.42.0"
"cargo:cargo-edit" = "0.13.9"
[tasks.codegen] [tasks.codegen]
sources = ['protobufs/*.proto'] sources = ['protobufs/*.proto']

View File

@@ -13,6 +13,44 @@ enum KeyType {
KEY_TYPE_RSA = 3; KEY_TYPE_RSA = 3;
} }
// --- SDK client management ---
enum SdkClientError {
SDK_CLIENT_ERROR_UNSPECIFIED = 0;
SDK_CLIENT_ERROR_ALREADY_EXISTS = 1;
SDK_CLIENT_ERROR_NOT_FOUND = 2;
SDK_CLIENT_ERROR_HAS_RELATED_DATA = 3; // hard-delete blocked by FK (client has grants or transaction logs)
SDK_CLIENT_ERROR_INTERNAL = 4;
}
message SdkClientRevokeRequest {
int32 client_id = 1;
}
message SdkClientEntry {
int32 id = 1;
bytes pubkey = 2;
int32 created_at = 3;
}
message SdkClientList {
repeated SdkClientEntry clients = 1;
}
message SdkClientRevokeResponse {
oneof result {
google.protobuf.Empty ok = 1;
SdkClientError error = 2;
}
}
message SdkClientListResponse {
oneof result {
SdkClientList clients = 1;
SdkClientError error = 2;
}
}
message AuthChallengeRequest { message AuthChallengeRequest {
bytes pubkey = 1; bytes pubkey = 1;
optional string bootstrap_token = 2; optional string bootstrap_token = 2;
@@ -79,22 +117,22 @@ enum VaultState {
VAULT_STATE_ERROR = 4; VAULT_STATE_ERROR = 4;
} }
message ClientConnectionRequest { message SdkClientConnectionRequest {
bytes pubkey = 1; bytes pubkey = 1;
arbiter.client.ClientInfo info = 2; arbiter.client.ClientInfo info = 2;
} }
message ClientConnectionResponse { message SdkClientConnectionResponse {
bool approved = 1; bool approved = 1;
bytes pubkey = 2; bytes pubkey = 2;
} }
message ClientConnectionCancel { message SdkClientConnectionCancel {
bytes pubkey = 1; bytes pubkey = 1;
} }
message UserAgentRequest { message UserAgentRequest {
int32 id = 14; int32 id = 16;
oneof payload { oneof payload {
AuthChallengeRequest auth_challenge_request = 1; AuthChallengeRequest auth_challenge_request = 1;
AuthChallengeSolution auth_challenge_solution = 2; AuthChallengeSolution auth_challenge_solution = 2;
@@ -106,12 +144,14 @@ message UserAgentRequest {
arbiter.evm.EvmGrantCreateRequest evm_grant_create = 8; arbiter.evm.EvmGrantCreateRequest evm_grant_create = 8;
arbiter.evm.EvmGrantDeleteRequest evm_grant_delete = 9; arbiter.evm.EvmGrantDeleteRequest evm_grant_delete = 9;
arbiter.evm.EvmGrantListRequest evm_grant_list = 10; arbiter.evm.EvmGrantListRequest evm_grant_list = 10;
ClientConnectionResponse client_connection_response = 11; SdkClientConnectionResponse sdk_client_connection_response = 11;
BootstrapEncryptedKey bootstrap_encrypted_key = 12; SdkClientRevokeRequest sdk_client_revoke = 13;
google.protobuf.Empty sdk_client_list = 14;
BootstrapEncryptedKey bootstrap_encrypted_key = 15;
} }
} }
message UserAgentResponse { message UserAgentResponse {
optional int32 id = 14; optional int32 id = 16;
oneof payload { oneof payload {
AuthChallenge auth_challenge = 1; AuthChallenge auth_challenge = 1;
AuthResult auth_result = 2; AuthResult auth_result = 2;
@@ -123,8 +163,10 @@ message UserAgentResponse {
arbiter.evm.EvmGrantCreateResponse evm_grant_create = 8; arbiter.evm.EvmGrantCreateResponse evm_grant_create = 8;
arbiter.evm.EvmGrantDeleteResponse evm_grant_delete = 9; arbiter.evm.EvmGrantDeleteResponse evm_grant_delete = 9;
arbiter.evm.EvmGrantListResponse evm_grant_list = 10; arbiter.evm.EvmGrantListResponse evm_grant_list = 10;
ClientConnectionRequest client_connection_request = 11; SdkClientConnectionRequest sdk_client_connection_request = 11;
ClientConnectionCancel client_connection_cancel = 12; SdkClientConnectionCancel sdk_client_connection_cancel = 12;
BootstrapResult bootstrap_result = 13; SdkClientRevokeResponse sdk_client_revoke_response = 13;
SdkClientListResponse sdk_client_list_response = 14;
BootstrapResult bootstrap_result = 15;
} }
} }

342
server/Cargo.lock generated
View File

@@ -67,13 +67,13 @@ dependencies = [
[[package]] [[package]]
name = "alloy-chains" name = "alloy-chains"
version = "0.2.31" version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9d22005bf31b018f31ef9ecadb5d2c39cf4f6acc8db0456f72c815f3d7f757" checksum = "9247f0a399ef71aeb68f497b2b8fb348014f742b50d3b83b1e00dfe1b7d64b3d"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"num_enum", "num_enum",
"strum", "strum 0.27.2",
] ]
[[package]] [[package]]
@@ -100,7 +100,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_with", "serde_with",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -136,7 +136,7 @@ dependencies = [
"futures", "futures",
"futures-util", "futures-util",
"serde_json", "serde_json",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -165,7 +165,7 @@ dependencies = [
"itoa", "itoa",
"serde", "serde",
"serde_json", "serde_json",
"winnow", "winnow 0.7.15",
] ]
[[package]] [[package]]
@@ -178,7 +178,7 @@ dependencies = [
"alloy-rlp", "alloy-rlp",
"crc", "crc",
"serde", "serde",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -203,7 +203,7 @@ dependencies = [
"alloy-rlp", "alloy-rlp",
"borsh", "borsh",
"serde", "serde",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -239,7 +239,7 @@ dependencies = [
"serde", "serde",
"serde_with", "serde_with",
"sha2 0.10.9", "sha2 0.10.9",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -280,7 +280,7 @@ dependencies = [
"http", "http",
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror 2.0.18",
"tracing", "tracing",
] ]
@@ -307,7 +307,7 @@ dependencies = [
"futures-utils-wasm", "futures-utils-wasm",
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -382,7 +382,7 @@ dependencies = [
"reqwest", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror 2.0.18",
"tokio", "tokio",
"tracing", "tracing",
"url", "url",
@@ -471,11 +471,11 @@ dependencies = [
"alloy-rlp", "alloy-rlp",
"alloy-serde", "alloy-serde",
"alloy-sol-types", "alloy-sol-types",
"itertools 0.14.0", "itertools 0.13.0",
"serde", "serde",
"serde_json", "serde_json",
"serde_with", "serde_with",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -501,7 +501,7 @@ dependencies = [
"either", "either",
"elliptic-curve", "elliptic-curve",
"k256", "k256",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -517,7 +517,7 @@ dependencies = [
"async-trait", "async-trait",
"k256", "k256",
"rand 0.8.5", "rand 0.8.5",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -578,7 +578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249"
dependencies = [ dependencies = [
"serde", "serde",
"winnow", "winnow 0.7.15",
] ]
[[package]] [[package]]
@@ -608,7 +608,7 @@ dependencies = [
"parking_lot", "parking_lot",
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror 2.0.18",
"tokio", "tokio",
"tower", "tower",
"tracing", "tracing",
@@ -624,7 +624,7 @@ checksum = "aa501ad58dd20acddbfebc65b52e60f05ebf97c52fa40d1b35e91f5e2da0ad0e"
dependencies = [ dependencies = [
"alloy-json-rpc", "alloy-json-rpc",
"alloy-transport", "alloy-transport",
"itertools 0.14.0", "itertools 0.13.0",
"reqwest", "reqwest",
"serde_json", "serde_json",
"tower", "tower",
@@ -644,7 +644,7 @@ dependencies = [
"nybbles", "nybbles",
"serde", "serde",
"smallvec", "smallvec",
"thiserror", "thiserror 2.0.18",
"tracing", "tracing",
] ]
@@ -678,6 +678,19 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]] [[package]]
name = "arbiter-client" name = "arbiter-client"
version = "0.1.0" version = "0.1.0"
dependencies = [
"alloy",
"arbiter-proto",
"async-trait",
"ed25519-dalek",
"http",
"rand 0.10.0",
"rustls-webpki",
"thiserror 2.0.18",
"tokio",
"tokio-stream",
"tonic",
]
[[package]] [[package]]
name = "arbiter-proto" name = "arbiter-proto"
@@ -691,11 +704,12 @@ dependencies = [
"miette", "miette",
"prost", "prost",
"prost-types", "prost-types",
"protoc-bin-vendored",
"rand 0.10.0", "rand 0.10.0",
"rcgen", "rcgen",
"rstest", "rstest",
"rustls-pki-types", "rustls-pki-types",
"thiserror", "thiserror 2.0.18",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tonic", "tonic",
@@ -721,6 +735,7 @@ dependencies = [
"diesel-async", "diesel-async",
"diesel_migrations", "diesel_migrations",
"ed25519-dalek", "ed25519-dalek",
"fatality",
"futures", "futures",
"insta", "insta",
"k256", "k256",
@@ -738,9 +753,9 @@ dependencies = [
"sha2 0.10.9", "sha2 0.10.9",
"smlang", "smlang",
"spki", "spki",
"strum", "strum 0.28.0",
"test-log", "test-log",
"thiserror", "thiserror 2.0.18",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tonic", "tonic",
@@ -977,7 +992,7 @@ dependencies = [
"nom", "nom",
"num-traits", "num-traits",
"rusticata-macros", "rusticata-macros",
"thiserror", "thiserror 2.0.18",
"time", "time",
] ]
@@ -1062,9 +1077,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "aws-lc-rs" name = "aws-lc-rs"
version = "1.16.1" version = "1.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc"
dependencies = [ dependencies = [
"aws-lc-sys", "aws-lc-sys",
"untrusted 0.7.1", "untrusted 0.7.1",
@@ -1073,9 +1088,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-lc-sys" name = "aws-lc-sys"
version = "0.38.0" version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a"
dependencies = [ dependencies = [
"cc", "cc",
"cmake", "cmake",
@@ -1270,19 +1285,20 @@ dependencies = [
[[package]] [[package]]
name = "borsh" name = "borsh"
version = "1.6.0" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a"
dependencies = [ dependencies = [
"borsh-derive", "borsh-derive",
"bytes",
"cfg_aliases", "cfg_aliases",
] ]
[[package]] [[package]]
name = "borsh-derive" name = "borsh-derive"
version = "1.6.0" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"proc-macro-crate", "proc-macro-crate",
@@ -1796,15 +1812,16 @@ dependencies = [
[[package]] [[package]]
name = "diesel-async" name = "diesel-async"
version = "0.7.4" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13096fb8dae53f2d411c4b523bec85f45552ed3044a2ab4d85fb2092d9cb4f34" checksum = "b95864e58597509106f1fddfe0600de7e589e1fddddd87f54eee0a49fd111bbc"
dependencies = [ dependencies = [
"bb8", "bb8",
"diesel", "diesel",
"diesel_migrations", "diesel_migrations",
"futures-core", "futures-core",
"futures-util", "futures-util",
"pin-project-lite",
"scoped-futures", "scoped-futures",
"tokio", "tokio",
] ]
@@ -2034,7 +2051,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.52.0",
]
[[package]]
name = "expander"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2c470c71d91ecbd179935b24170459e926382eaaa86b590b78814e180d8a8e2"
dependencies = [
"blake2",
"file-guard",
"fs-err",
"prettyplease",
"proc-macro2",
"quote",
"syn 2.0.117",
] ]
[[package]] [[package]]
@@ -2065,6 +2097,30 @@ dependencies = [
"bytes", "bytes",
] ]
[[package]]
name = "fatality"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec6f82451ff7f0568c6181287189126d492b5654e30a788add08027b6363d019"
dependencies = [
"fatality-proc-macro",
"thiserror 1.0.69",
]
[[package]]
name = "fatality-proc-macro"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb42427514b063d97ce21d5199f36c0c307d981434a6be32582bc79fe5bd2303"
dependencies = [
"expander",
"indexmap 2.13.0",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]] [[package]]
name = "ff" name = "ff"
version = "0.13.1" version = "0.13.1"
@@ -2087,6 +2143,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24"
[[package]]
name = "file-guard"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21ef72acf95ec3d7dbf61275be556299490a245f017cf084bd23b4f68cf9407c"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.9" version = "0.1.9"
@@ -2148,6 +2214,15 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "fs-err"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "fs_extra" name = "fs_extra"
version = "1.3.0" version = "1.3.0"
@@ -2796,20 +2871,11 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.17" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]] [[package]]
name = "jobserver" name = "jobserver"
@@ -3120,7 +3186,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -3197,9 +3263,9 @@ dependencies = [
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.7.5" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
dependencies = [ dependencies = [
"num_enum_derive", "num_enum_derive",
"rustversion", "rustversion",
@@ -3207,9 +3273,9 @@ dependencies = [
[[package]] [[package]]
name = "num_enum_derive" name = "num_enum_derive"
version = "0.7.5" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -3601,7 +3667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7"
dependencies = [ dependencies = [
"heck", "heck",
"itertools 0.14.0", "itertools 0.13.0",
"log", "log",
"multimap", "multimap",
"petgraph", "petgraph",
@@ -3622,7 +3688,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"itertools 0.14.0", "itertools 0.13.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.117", "syn 2.0.117",
@@ -3639,10 +3705,74 @@ dependencies = [
] ]
[[package]] [[package]]
name = "pulldown-cmark" name = "protoc-bin-vendored"
version = "0.13.1" version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83c41efbf8f90ac44de7f3a868f0867851d261b56291732d0cbf7cceaaeb55a6" checksum = "d1c381df33c98266b5f08186583660090a4ffa0889e76c7e9a5e175f645a67fa"
dependencies = [
"protoc-bin-vendored-linux-aarch_64",
"protoc-bin-vendored-linux-ppcle_64",
"protoc-bin-vendored-linux-s390_64",
"protoc-bin-vendored-linux-x86_32",
"protoc-bin-vendored-linux-x86_64",
"protoc-bin-vendored-macos-aarch_64",
"protoc-bin-vendored-macos-x86_64",
"protoc-bin-vendored-win32",
]
[[package]]
name = "protoc-bin-vendored-linux-aarch_64"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c350df4d49b5b9e3ca79f7e646fde2377b199e13cfa87320308397e1f37e1a4c"
[[package]]
name = "protoc-bin-vendored-linux-ppcle_64"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a55a63e6c7244f19b5c6393f025017eb5d793fd5467823a099740a7a4222440c"
[[package]]
name = "protoc-bin-vendored-linux-s390_64"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dba5565db4288e935d5330a07c264a4ee8e4a5b4a4e6f4e83fad824cc32f3b0"
[[package]]
name = "protoc-bin-vendored-linux-x86_32"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8854774b24ee28b7868cd71dccaae8e02a2365e67a4a87a6cd11ee6cdbdf9cf5"
[[package]]
name = "protoc-bin-vendored-linux-x86_64"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b38b07546580df720fa464ce124c4b03630a6fb83e05c336fea2a241df7e5d78"
[[package]]
name = "protoc-bin-vendored-macos-aarch_64"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89278a9926ce312e51f1d999fee8825d324d603213344a9a706daa009f1d8092"
[[package]]
name = "protoc-bin-vendored-macos-x86_64"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81745feda7ccfb9471d7a4de888f0652e806d5795b61480605d4943176299756"
[[package]]
name = "protoc-bin-vendored-win32"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95067976aca6421a523e491fce939a3e65249bac4b977adee0ee9771568e8aa3"
[[package]]
name = "pulldown-cmark"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14104c5a24d9bcf7eb2c24753e0f49fe14555d8bd565ea3d38e4b4303267259d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"memchr", "memchr",
@@ -3678,7 +3808,7 @@ dependencies = [
"rustc-hash", "rustc-hash",
"rustls", "rustls",
"socket2", "socket2",
"thiserror", "thiserror 2.0.18",
"tokio", "tokio",
"tracing", "tracing",
"web-time", "web-time",
@@ -3699,7 +3829,7 @@ dependencies = [
"rustls", "rustls",
"rustls-pki-types", "rustls-pki-types",
"slab", "slab",
"thiserror", "thiserror 2.0.18",
"tinyvec", "tinyvec",
"tracing", "tracing",
"web-time", "web-time",
@@ -4034,7 +4164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a1f2315036ef6b1fbacd1972e8ee7688030b0a2121edfc2a6550febd41574d" checksum = "a8a1f2315036ef6b1fbacd1972e8ee7688030b0a2121edfc2a6550febd41574d"
dependencies = [ dependencies = [
"hashbrown 0.16.1", "hashbrown 0.16.1",
"thiserror", "thiserror 2.0.18",
] ]
[[package]] [[package]]
@@ -4155,7 +4285,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -4186,9 +4316,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.9" version = "0.103.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"ring", "ring",
@@ -4571,7 +4701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -4632,7 +4762,16 @@ version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [ dependencies = [
"strum_macros", "strum_macros 0.27.2",
]
[[package]]
name = "strum"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd"
dependencies = [
"strum_macros 0.28.0",
] ]
[[package]] [[package]]
@@ -4647,6 +4786,18 @@ dependencies = [
"syn 2.0.117", "syn 2.0.117",
] ]
[[package]]
name = "strum_macros"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
@@ -4744,7 +4895,7 @@ dependencies = [
"getrandom 0.4.2", "getrandom 0.4.2",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -4788,13 +4939,33 @@ dependencies = [
"unicode-width 0.2.2", "unicode-width 0.2.2",
] ]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.18" version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl 2.0.18",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
] ]
[[package]] [[package]]
@@ -4956,7 +5127,7 @@ dependencies = [
"serde_spanned", "serde_spanned",
"toml_datetime 0.7.5+spec-1.1.0", "toml_datetime 0.7.5+spec-1.1.0",
"toml_parser", "toml_parser",
"winnow", "winnow 0.7.15",
] ]
[[package]] [[package]]
@@ -4970,32 +5141,32 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "1.0.0+spec-1.1.0" version = "1.0.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
dependencies = [ dependencies = [
"serde_core", "serde_core",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.25.4+spec-1.1.0" version = "0.25.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1"
dependencies = [ dependencies = [
"indexmap 2.13.0", "indexmap 2.13.0",
"toml_datetime 1.0.0+spec-1.1.0", "toml_datetime 1.0.1+spec-1.1.0",
"toml_parser", "toml_parser",
"winnow", "winnow 1.0.0",
] ]
[[package]] [[package]]
name = "toml_parser" name = "toml_parser"
version = "1.0.9+spec-1.1.0" version = "1.0.10+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
dependencies = [ dependencies = [
"winnow", "winnow 1.0.0",
] ]
[[package]] [[package]]
@@ -5759,6 +5930,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winnow"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.51.0" version = "0.51.0"
@@ -5888,7 +6068,7 @@ dependencies = [
"nom", "nom",
"oid-registry", "oid-registry",
"rusticata-macros", "rusticata-macros",
"thiserror", "thiserror 2.0.18",
"time", "time",
] ]
@@ -5926,18 +6106,18 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.42" version = "0.8.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.42" version = "0.8.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@@ -9,23 +9,23 @@ disallowed-methods = "deny"
[workspace.dependencies] [workspace.dependencies]
tonic = { version = "0.14.3", features = [ tonic = { version = "0.14.5", features = [
"deflate", "deflate",
"gzip", "gzip",
"tls-connect-info", "tls-connect-info",
"zstd", "zstd",
] } ] }
tracing = "0.1.44" tracing = "0.1.44"
tokio = { version = "1.49.0", features = ["full"] } tokio = { version = "1.50.0", features = ["full"] }
ed25519-dalek = { version = "3.0.0-pre.6", features = ["rand_core"] } ed25519-dalek = { version = "3.0.0-pre.6", features = ["rand_core"] }
chrono = { version = "0.4.43", features = ["serde"] } chrono = { version = "0.4.44", features = ["serde"] }
rand = "0.10.0" rand = "0.10.0"
rustls = { version = "0.23.36", features = ["aws-lc-rs"] } rustls = { version = "0.23.37", features = ["aws-lc-rs"] }
smlang = "0.8.0" smlang = "0.8.0"
miette = { version = "7.6.0", features = ["fancy", "serde"] } miette = { version = "7.6.0", features = ["fancy", "serde"] }
thiserror = "2.0.18" thiserror = "2.0.18"
async-trait = "0.1.89" async-trait = "0.1.89"
futures = "0.3.31" futures = "0.3.32"
tokio-stream = { version = "0.1.18", features = ["full"] } tokio-stream = { version = "0.1.18", features = ["full"] }
kameo = "0.19.2" kameo = "0.19.2"
prost-types = { version = "0.14.3", features = ["chrono"] } prost-types = { version = "0.14.3", features = ["chrono"] }

View File

@@ -5,4 +5,22 @@ edition = "2024"
repository = "https://git.markettakers.org/MarketTakers/arbiter" repository = "https://git.markettakers.org/MarketTakers/arbiter"
license = "Apache-2.0" license = "Apache-2.0"
[lints]
workspace = true
[features]
evm = ["dep:alloy"]
[dependencies] [dependencies]
arbiter-proto.path = "../arbiter-proto"
alloy = { workspace = true, optional = true }
tonic.workspace = true
tonic.features = ["tls-aws-lc"]
tokio.workspace = true
tokio-stream.workspace = true
ed25519-dalek.workspace = true
thiserror.workspace = true
http = "1.4.0"
rustls-webpki = { version = "0.103.10", features = ["aws-lc-rs"] }
async-trait.workspace = true
rand.workspace = true

View File

@@ -0,0 +1,147 @@
use arbiter_proto::{
ClientMetadata, format_challenge,
proto::client::{
AuthChallengeRequest, AuthChallengeSolution, AuthResult, ClientInfo as ProtoClientInfo,
ClientRequest, client_request::Payload as ClientRequestPayload,
client_response::Payload as ClientResponsePayload,
},
};
use ed25519_dalek::Signer as _;
use crate::{
storage::StorageError,
transport::{ClientTransport, next_request_id},
};
#[derive(Debug, thiserror::Error)]
pub enum ConnectError {
#[error("Could not establish connection")]
Connection(#[from] tonic::transport::Error),
#[error("Invalid server URI")]
InvalidUri(#[from] http::uri::InvalidUri),
#[error("Invalid CA certificate")]
InvalidCaCert(#[from] webpki::Error),
#[error("gRPC error")]
Grpc(#[from] tonic::Status),
#[error("Auth challenge was not returned by server")]
MissingAuthChallenge,
#[error("Client approval denied by User Agent")]
ApprovalDenied,
#[error("No User Agents online to approve client")]
NoUserAgentsOnline,
#[error("Unexpected auth response payload")]
UnexpectedAuthResponse,
#[error("Signing key storage error")]
Storage(#[from] StorageError),
}
fn map_auth_result(code: i32) -> ConnectError {
match AuthResult::try_from(code).unwrap_or(AuthResult::Unspecified) {
AuthResult::ApprovalDenied => ConnectError::ApprovalDenied,
AuthResult::NoUserAgentsOnline => ConnectError::NoUserAgentsOnline,
AuthResult::Unspecified
| AuthResult::Success
| AuthResult::InvalidKey
| AuthResult::InvalidSignature
| AuthResult::Internal => ConnectError::UnexpectedAuthResponse,
}
}
async fn send_auth_challenge_request(
transport: &mut ClientTransport,
metadata: ClientMetadata,
key: &ed25519_dalek::SigningKey,
) -> std::result::Result<(), ConnectError> {
transport
.send(ClientRequest {
request_id: next_request_id(),
payload: Some(ClientRequestPayload::AuthChallengeRequest(
AuthChallengeRequest {
pubkey: key.verifying_key().to_bytes().to_vec(),
client_info: Some(ProtoClientInfo {
name: metadata.name,
description: metadata.description,
version: metadata.version,
}),
},
)),
})
.await
.map_err(|_| ConnectError::UnexpectedAuthResponse)
}
async fn receive_auth_challenge(
transport: &mut ClientTransport,
) -> std::result::Result<arbiter_proto::proto::client::AuthChallenge, ConnectError> {
let response = transport
.recv()
.await
.map_err(|_| ConnectError::MissingAuthChallenge)?;
let payload = response.payload.ok_or(ConnectError::MissingAuthChallenge)?;
match payload {
ClientResponsePayload::AuthChallenge(challenge) => Ok(challenge),
ClientResponsePayload::AuthResult(result) => Err(map_auth_result(result)),
_ => Err(ConnectError::UnexpectedAuthResponse),
}
}
async fn send_auth_challenge_solution(
transport: &mut ClientTransport,
key: &ed25519_dalek::SigningKey,
challenge: arbiter_proto::proto::client::AuthChallenge,
) -> std::result::Result<(), ConnectError> {
let challenge_payload = format_challenge(challenge.nonce, &challenge.pubkey);
let signature = key.sign(&challenge_payload).to_bytes().to_vec();
transport
.send(ClientRequest {
request_id: next_request_id(),
payload: Some(ClientRequestPayload::AuthChallengeSolution(
AuthChallengeSolution { signature },
)),
})
.await
.map_err(|_| ConnectError::UnexpectedAuthResponse)
}
async fn receive_auth_confirmation(
transport: &mut ClientTransport,
) -> std::result::Result<(), ConnectError> {
let response = transport
.recv()
.await
.map_err(|_| ConnectError::UnexpectedAuthResponse)?;
let payload = response
.payload
.ok_or(ConnectError::UnexpectedAuthResponse)?;
match payload {
ClientResponsePayload::AuthResult(result)
if AuthResult::try_from(result).ok() == Some(AuthResult::Success) =>
{
Ok(())
}
ClientResponsePayload::AuthResult(result) => Err(map_auth_result(result)),
_ => Err(ConnectError::UnexpectedAuthResponse),
}
}
pub(crate) async fn authenticate(
transport: &mut ClientTransport,
metadata: ClientMetadata,
key: &ed25519_dalek::SigningKey,
) -> std::result::Result<(), ConnectError> {
send_auth_challenge_request(transport, metadata, key).await?;
let challenge = receive_auth_challenge(transport).await?;
send_auth_challenge_solution(transport, key, challenge).await?;
receive_auth_confirmation(transport).await
}

View File

@@ -0,0 +1,78 @@
use arbiter_proto::{ClientMetadata, proto::arbiter_service_client::ArbiterServiceClient, url::ArbiterUrl};
use std::sync::Arc;
use tokio::sync::{Mutex, mpsc};
use tokio_stream::wrappers::ReceiverStream;
use tonic::transport::ClientTlsConfig;
use crate::{
auth::{ConnectError, authenticate},
storage::{FileSigningKeyStorage, SigningKeyStorage},
transport::{BUFFER_LENGTH, ClientTransport},
};
#[cfg(feature = "evm")]
use crate::wallets::evm::ArbiterEvmWallet;
#[derive(Debug, thiserror::Error)]
pub enum ClientError {
#[error("gRPC error")]
Grpc(#[from] tonic::Status),
#[error("Connection closed by server")]
ConnectionClosed,
}
pub struct ArbiterClient {
#[allow(dead_code)]
transport: Arc<Mutex<ClientTransport>>,
}
impl ArbiterClient {
pub async fn connect(url: ArbiterUrl, metadata: ClientMetadata) -> Result<Self, ConnectError> {
let storage = FileSigningKeyStorage::from_default_location()?;
Self::connect_with_storage(url, metadata, &storage).await
}
pub async fn connect_with_storage<S: SigningKeyStorage>(
url: ArbiterUrl,
metadata: ClientMetadata,
storage: &S,
) -> Result<Self, ConnectError> {
let key = storage.load_or_create()?;
Self::connect_with_key(url, metadata, key).await
}
pub async fn connect_with_key(
url: ArbiterUrl,
metadata: ClientMetadata,
key: ed25519_dalek::SigningKey,
) -> Result<Self, ConnectError> {
let anchor = webpki::anchor_from_trusted_cert(&url.ca_cert)?.to_owned();
let tls = ClientTlsConfig::new().trust_anchor(anchor);
let channel = tonic::transport::Channel::from_shared(format!("{}:{}", url.host, url.port))?
.tls_config(tls)?
.connect()
.await?;
let mut client = ArbiterServiceClient::new(channel);
let (tx, rx) = mpsc::channel(BUFFER_LENGTH);
let response_stream = client.client(ReceiverStream::new(rx)).await?.into_inner();
let mut transport = ClientTransport {
sender: tx,
receiver: response_stream,
};
authenticate(&mut transport, metadata, &key).await?;
Ok(Self {
transport: Arc::new(Mutex::new(transport)),
})
}
#[cfg(feature = "evm")]
pub async fn evm_wallets(&self) -> Result<Vec<ArbiterEvmWallet>, ClientError> {
todo!("fetch EVM wallet list from server")
}
}

View File

@@ -1,14 +1,12 @@
pub fn add(left: u64, right: u64) -> u64 { mod auth;
left + right mod client;
} mod storage;
mod transport;
pub mod wallets;
#[cfg(test)] pub use auth::ConnectError;
mod tests { pub use client::{ArbiterClient, ClientError};
use super::*; pub use storage::{FileSigningKeyStorage, SigningKeyStorage, StorageError};
#[test] #[cfg(feature = "evm")]
fn it_works() { pub use wallets::evm::ArbiterEvmWallet;
let result = add(2, 2);
assert_eq!(result, 4);
}
}

View File

@@ -0,0 +1,132 @@
use arbiter_proto::home_path;
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 },
}
pub trait SigningKeyStorage {
fn load_or_create(&self) -> std::result::Result<ed25519_dalek::SigningKey, StorageError>;
}
#[derive(Debug, Clone)]
pub struct FileSigningKeyStorage {
path: PathBuf,
}
impl FileSigningKeyStorage {
pub const DEFAULT_FILE_NAME: &str = "sdk_client_ed25519.key";
pub fn new(path: impl Into<PathBuf>) -> Self {
Self { path: path.into() }
}
pub fn from_default_location() -> std::result::Result<Self, StorageError> {
Ok(Self::new(home_path()?.join(Self::DEFAULT_FILE_NAME)))
}
fn read_key(path: &Path) -> std::result::Result<ed25519_dalek::SigningKey, StorageError> {
let bytes = std::fs::read(path)?;
let raw: [u8; 32] =
bytes
.try_into()
.map_err(|v: Vec<u8>| StorageError::InvalidKeyLength {
expected: 32,
actual: v.len(),
})?;
Ok(ed25519_dalek::SigningKey::from_bytes(&raw))
}
}
impl SigningKeyStorage for FileSigningKeyStorage {
fn load_or_create(&self) -> std::result::Result<ed25519_dalek::SigningKey, StorageError> {
if let Some(parent) = self.path.parent() {
std::fs::create_dir_all(parent)?;
}
if self.path.exists() {
return Self::read_key(&self.path);
}
let key = ed25519_dalek::SigningKey::generate(&mut rand::rng());
let raw_key = key.to_bytes();
// Use create_new to prevent accidental overwrite if another process creates the key first.
match std::fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&self.path)
{
Ok(mut file) => {
use std::io::Write as _;
file.write_all(&raw_key)?;
Ok(key)
}
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {
Self::read_key(&self.path)
}
Err(err) => Err(StorageError::Io(err)),
}
}
}
#[cfg(test)]
mod tests {
use super::{FileSigningKeyStorage, SigningKeyStorage, StorageError};
fn unique_temp_key_path() -> std::path::PathBuf {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("clock should be after unix epoch")
.as_nanos();
std::env::temp_dir().join(format!(
"arbiter-client-key-{}-{}.bin",
std::process::id(),
nanos
))
}
#[test]
fn file_storage_creates_and_reuses_key() {
let path = unique_temp_key_path();
let storage = FileSigningKeyStorage::new(path.clone());
let key_a = storage
.load_or_create()
.expect("first load_or_create should create key");
let key_b = storage
.load_or_create()
.expect("second load_or_create should read same key");
assert_eq!(key_a.to_bytes(), key_b.to_bytes());
assert!(path.exists());
std::fs::remove_file(path).expect("temp key file should be removable");
}
#[test]
fn file_storage_rejects_invalid_key_length() {
let path = unique_temp_key_path();
std::fs::write(&path, [42u8; 31]).expect("should write invalid key file");
let storage = FileSigningKeyStorage::new(path.clone());
let err = storage
.load_or_create()
.expect_err("storage should reject non-32-byte key file");
match err {
StorageError::InvalidKeyLength { expected, actual } => {
assert_eq!(expected, 32);
assert_eq!(actual, 31);
}
other => panic!("unexpected error: {other:?}"),
}
std::fs::remove_file(path).expect("temp key file should be removable");
}
}

View File

@@ -0,0 +1,48 @@
use arbiter_proto::proto::{
client::{ClientRequest, ClientResponse},
};
use std::sync::atomic::{AtomicI32, Ordering};
use tokio::sync::mpsc;
pub(crate) const BUFFER_LENGTH: usize = 16;
static NEXT_REQUEST_ID: AtomicI32 = AtomicI32::new(1);
pub(crate) fn next_request_id() -> i32 {
NEXT_REQUEST_ID.fetch_add(1, Ordering::Relaxed)
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum ClientSignError {
#[error("Transport channel closed")]
ChannelClosed,
#[error("Connection closed by server")]
ConnectionClosed,
}
pub(crate) struct ClientTransport {
pub(crate) sender: mpsc::Sender<ClientRequest>,
pub(crate) receiver: tonic::Streaming<ClientResponse>,
}
impl ClientTransport {
pub(crate) async fn send(
&mut self,
request: ClientRequest,
) -> std::result::Result<(), ClientSignError> {
self.sender
.send(request)
.await
.map_err(|_| ClientSignError::ChannelClosed)
}
pub(crate) async fn recv(
&mut self,
) -> std::result::Result<ClientResponse, ClientSignError> {
match self.receiver.message().await {
Ok(Some(resp)) => Ok(resp),
Ok(None) => Err(ClientSignError::ConnectionClosed),
Err(_) => Err(ClientSignError::ConnectionClosed),
}
}
}

View File

@@ -0,0 +1,89 @@
use alloy::{
consensus::SignableTransaction,
network::TxSigner,
primitives::{Address, B256, ChainId, Signature},
signers::{Error, Result, Signer},
};
use async_trait::async_trait;
use std::sync::Arc;
use tokio::sync::Mutex;
use crate::transport::ClientTransport;
pub struct ArbiterEvmWallet {
transport: Arc<Mutex<ClientTransport>>,
address: Address,
chain_id: Option<ChainId>,
}
impl ArbiterEvmWallet {
pub(crate) fn new(transport: Arc<Mutex<ClientTransport>>, address: Address) -> Self {
Self {
transport,
address,
chain_id: None,
}
}
pub fn address(&self) -> Address {
self.address
}
pub fn with_chain_id(mut self, chain_id: ChainId) -> Self {
self.chain_id = Some(chain_id);
self
}
fn validate_chain_id(&self, tx: &mut dyn SignableTransaction<Signature>) -> Result<()> {
if let Some(chain_id) = self.chain_id
&& !tx.set_chain_id_checked(chain_id)
{
return Err(Error::TransactionChainIdMismatch {
signer: chain_id,
tx: tx.chain_id().unwrap(),
});
}
Ok(())
}
}
#[async_trait]
impl Signer for ArbiterEvmWallet {
async fn sign_hash(&self, _hash: &B256) -> Result<Signature> {
Err(Error::other(
"hash-only signing is not supported for ArbiterEvmWallet; use transaction signing",
))
}
fn address(&self) -> Address {
self.address
}
fn chain_id(&self) -> Option<ChainId> {
self.chain_id
}
fn set_chain_id(&mut self, chain_id: Option<ChainId>) {
self.chain_id = chain_id;
}
}
#[async_trait]
impl TxSigner<Signature> for ArbiterEvmWallet {
fn address(&self) -> Address {
self.address
}
async fn sign_transaction(
&self,
tx: &mut dyn SignableTransaction<Signature>,
) -> Result<Signature> {
let _transport = self.transport.lock().await;
self.validate_chain_id(tx)?;
Err(Error::other(
"transaction signing is not supported by current arbiter.client protocol",
))
}
}

View File

@@ -0,0 +1,2 @@
#[cfg(feature = "evm")]
pub mod evm;

View File

@@ -10,7 +10,7 @@ tonic.workspace = true
tokio.workspace = true tokio.workspace = true
futures.workspace = true futures.workspace = true
hex = "0.4.3" hex = "0.4.3"
tonic-prost = "0.14.3" tonic-prost = "0.14.5"
prost = "0.14.3" prost = "0.14.3"
kameo.workspace = true kameo.workspace = true
url = "2.5.8" url = "2.5.8"
@@ -24,7 +24,8 @@ async-trait.workspace = true
tokio-stream.workspace = true tokio-stream.workspace = true
[build-dependencies] [build-dependencies]
tonic-prost-build = "0.14.3" tonic-prost-build = "0.14.5"
protoc-bin-vendored = "3"
[dev-dependencies] [dev-dependencies]
rstest.workspace = true rstest.workspace = true
@@ -33,5 +34,3 @@ rcgen.workspace = true
[package.metadata.cargo-shear] [package.metadata.cargo-shear]
ignored = ["tonic-prost", "prost", "kameo"] ignored = ["tonic-prost", "prost", "kameo"]

View File

@@ -19,6 +19,13 @@ pub mod proto {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClientMetadata {
pub name: String,
pub description: Option<String>,
pub version: Option<String>,
}
pub static BOOTSTRAP_PATH: &str = "bootstrap_token"; pub static BOOTSTRAP_PATH: &str = "bootstrap_token";
pub fn home_path() -> Result<std::path::PathBuf, std::io::Error> { pub fn home_path() -> Result<std::path::PathBuf, std::io::Error> {

View File

@@ -9,8 +9,8 @@ license = "Apache-2.0"
workspace = true workspace = true
[dependencies] [dependencies]
diesel = { version = "2.3.6", features = ["chrono", "returning_clauses_for_sqlite_3_35", "serde_json", "time", "uuid"] } diesel = { version = "2.3.7", features = ["chrono", "returning_clauses_for_sqlite_3_35", "serde_json", "time", "uuid"] }
diesel-async = { version = "0.7.4", features = [ diesel-async = { version = "0.8.0", features = [
"bb8", "bb8",
"migrations", "migrations",
"sqlite", "sqlite",
@@ -27,6 +27,7 @@ rustls.workspace = true
smlang.workspace = true smlang.workspace = true
miette.workspace = true miette.workspace = true
thiserror.workspace = true thiserror.workspace = true
fatality = "0.1.1"
diesel_migrations = { version = "2.3.1", features = ["sqlite"] } diesel_migrations = { version = "2.3.1", features = ["sqlite"] }
async-trait.workspace = true async-trait.workspace = true
secrecy = "0.10.3" secrecy = "0.10.3"
@@ -43,7 +44,7 @@ x25519-dalek.workspace = true
chacha20poly1305 = { version = "0.10.1", features = ["std"] } chacha20poly1305 = { version = "0.10.1", features = ["std"] }
argon2 = { version = "0.5.3", features = ["zeroize"] } argon2 = { version = "0.5.3", features = ["zeroize"] }
restructed = "0.2.2" restructed = "0.2.2"
strum = { version = "0.27.2", features = ["derive"] } strum = { version = "0.28.0", features = ["derive"] }
pem = "3.0.6" pem = "3.0.6"
k256.workspace = true k256.workspace = true
rsa.workspace = true rsa.workspace = true

View File

@@ -80,6 +80,9 @@ create table if not exists program_client (
updated_at integer not null default(unixepoch ('now')) updated_at integer not null default(unixepoch ('now'))
) STRICT; ) STRICT;
create unique index if not exists program_client_public_key_unique
on program_client (public_key);
create unique index if not exists uniq_program_client_public_key on program_client (public_key); create unique index if not exists uniq_program_client_public_key on program_client (public_key);
create table if not exists evm_wallet ( create table if not exists evm_wallet (

View File

@@ -1,6 +1,5 @@
use arbiter_proto::{ use arbiter_proto::{
format_challenge, ClientMetadata, format_challenge, transport::{Bi, expect_message}
transport::{Bi, expect_message},
}; };
use chrono::Utc; use chrono::Utc;
use diesel::{ use diesel::{
@@ -24,13 +23,6 @@ use crate::{
}, },
}; };
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClientMetadata {
pub name: String,
pub description: Option<String>,
pub version: Option<String>,
}
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum Error { pub enum Error {
#[error("Database pool unavailable")] #[error("Database pool unavailable")]
@@ -72,9 +64,17 @@ pub enum Outbound {
AuthSuccess, AuthSuccess,
} }
pub struct ClientInfo {
pub id: i32,
pub current_nonce: i32,
}
/// Atomically reads and increments the nonce for a known client. /// Atomically reads and increments the nonce for a known client.
/// Returns `None` if the pubkey is not registered. /// Returns `None` if the pubkey is not registered.
async fn get_nonce(db: &db::DatabasePool, pubkey: &VerifyingKey) -> Result<Option<i32>, Error> { async fn get_client_and_nonce(
db: &db::DatabasePool,
pubkey: &VerifyingKey,
) -> Result<Option<ClientInfo>, Error> {
let pubkey_bytes = pubkey.as_bytes().to_vec(); let pubkey_bytes = pubkey.as_bytes().to_vec();
let mut conn = db.get().await.map_err(|e| { let mut conn = db.get().await.map_err(|e| {
@@ -85,10 +85,10 @@ async fn get_nonce(db: &db::DatabasePool, pubkey: &VerifyingKey) -> Result<Optio
conn.exclusive_transaction(|conn| { conn.exclusive_transaction(|conn| {
let pubkey_bytes = pubkey_bytes.clone(); let pubkey_bytes = pubkey_bytes.clone();
Box::pin(async move { Box::pin(async move {
let Some(current_nonce) = program_client::table let Some((client_id, current_nonce)) = program_client::table
.filter(program_client::public_key.eq(&pubkey_bytes)) .filter(program_client::public_key.eq(&pubkey_bytes))
.select(program_client::nonce) .select((program_client::id, program_client::nonce))
.first::<i32>(conn) .first::<(i32, i32)>(conn)
.await .await
.optional()? .optional()?
else { else {
@@ -101,7 +101,10 @@ async fn get_nonce(db: &db::DatabasePool, pubkey: &VerifyingKey) -> Result<Optio
.execute(conn) .execute(conn)
.await?; .await?;
Ok(Some(current_nonce)) Ok(Some(ClientInfo {
id: client_id,
current_nonce,
}))
}) })
}) })
.await .await
@@ -138,9 +141,8 @@ async fn insert_client(
db: &db::DatabasePool, db: &db::DatabasePool,
pubkey: &VerifyingKey, pubkey: &VerifyingKey,
metadata: &ClientMetadata, metadata: &ClientMetadata,
) -> Result<(), Error> { ) -> Result<i32, Error> {
use crate::db::schema::client_metadata; use crate::db::schema::{client_metadata, program_client};
let mut conn = db.get().await.map_err(|e| { let mut conn = db.get().await.map_err(|e| {
error!(error = ?e, "Database pool error"); error!(error = ?e, "Database pool error");
Error::DatabasePoolUnavailable Error::DatabasePoolUnavailable
@@ -160,38 +162,22 @@ async fn insert_client(
Error::DatabaseOperationFailed Error::DatabaseOperationFailed
})?; })?;
insert_into(program_client::table) let client_id = insert_into(program_client::table)
.values(( .values((
program_client::public_key.eq(pubkey.as_bytes().to_vec()), program_client::public_key.eq(pubkey.as_bytes().to_vec()),
program_client::metadata_id.eq(metadata_id), program_client::metadata_id.eq(metadata_id),
program_client::nonce.eq(1), // pre-incremented; challenge uses 0 program_client::nonce.eq(1), // pre-incremented; challenge uses 0
)) ))
.execute(&mut conn) .on_conflict_do_nothing()
.returning(program_client::id)
.get_result::<i32>(&mut conn)
.await .await
.map_err(|e| { .map_err(|e| {
error!(error = ?e, "Failed to insert new client"); error!(error = ?e, "Failed to insert client metadata");
Error::DatabaseOperationFailed Error::DatabaseOperationFailed
})?; })?;
Ok(()) Ok(client_id)
}
async fn get_client_id(db: &db::DatabasePool, pubkey: &VerifyingKey) -> Result<Option<i32>, Error> {
let mut conn = db.get().await.map_err(|e| {
error!(error = ?e, "Database pool error");
Error::DatabasePoolUnavailable
})?;
program_client::table
.filter(program_client::public_key.eq(pubkey.as_bytes().to_vec()))
.select(program_client::id)
.first::<i32>(&mut conn)
.await
.optional()
.map_err(|e| {
error!(error = ?e, "Database error");
Error::DatabaseOperationFailed
})
} }
async fn sync_client_metadata( async fn sync_client_metadata(
@@ -312,7 +298,7 @@ where
return Err(Error::Transport); return Err(Error::Transport);
}; };
let nonce = match get_nonce(&props.db, &pubkey).await? { let info = match get_client_and_nonce(&props.db, &pubkey).await? {
Some(nonce) => nonce, Some(nonce) => nonce,
None => { None => {
approve_new_client( approve_new_client(
@@ -323,17 +309,18 @@ where
}, },
) )
.await?; .await?;
insert_client(&props.db, &pubkey, &metadata).await?; let client_id = insert_client(&props.db, &pubkey, &metadata).await?;
0 ClientInfo {
id: client_id,
current_nonce: 0,
}
} }
}; };
let client_id = get_client_id(&props.db, &pubkey) sync_client_metadata(&props.db, info.id, &metadata).await?;
.await?
.ok_or(Error::DatabaseOperationFailed)?;
sync_client_metadata(&props.db, client_id, &metadata).await?;
challenge_client(transport, pubkey, nonce).await?; challenge_client(transport, pubkey, info.current_nonce).await?;
transport transport
.send(Ok(Outbound::AuthSuccess)) .send(Ok(Outbound::AuthSuccess))
.await .await

View File

@@ -1,9 +1,9 @@
use arbiter_proto::transport::Bi; use arbiter_proto::{ClientMetadata, transport::Bi};
use kameo::actor::Spawn; use kameo::actor::Spawn;
use tracing::{error, info}; use tracing::{error, info};
use crate::{ use crate::{
actors::{GlobalActors, client::{auth::ClientMetadata, session::ClientSession}}, actors::{GlobalActors, client::{ session::ClientSession}},
db, db,
}; };

View File

@@ -3,7 +3,7 @@ use std::{borrow::Cow, collections::HashMap};
use arbiter_proto::transport::Sender; use arbiter_proto::transport::Sender;
use async_trait::async_trait; use async_trait::async_trait;
use ed25519_dalek::VerifyingKey; use ed25519_dalek::VerifyingKey;
use kameo::{Actor, actor::ActorRef, messages, prelude::Context}; use kameo::{Actor, actor::ActorRef, messages};
use thiserror::Error; use thiserror::Error;
use tracing::error; use tracing::error;

View File

@@ -1,12 +1,11 @@
use arbiter_proto::{ use arbiter_proto::{
proto::client::{ ClientMetadata, proto::client::{
AuthChallenge as ProtoAuthChallenge, AuthChallengeRequest as ProtoAuthChallengeRequest, AuthChallenge as ProtoAuthChallenge, AuthChallengeRequest as ProtoAuthChallengeRequest,
AuthChallengeSolution as ProtoAuthChallengeSolution, AuthResult as ProtoAuthResult, AuthChallengeSolution as ProtoAuthChallengeSolution, AuthResult as ProtoAuthResult,
ClientInfo as ProtoClientInfo, ClientRequest, ClientResponse, ClientInfo as ProtoClientInfo, ClientRequest, ClientResponse,
client_request::Payload as ClientRequestPayload, client_request::Payload as ClientRequestPayload,
client_response::Payload as ClientResponsePayload, client_response::Payload as ClientResponsePayload,
}, }, transport::{Bi, Error as TransportError, Receiver, Sender, grpc::GrpcBi}
transport::{Bi, Error as TransportError, Receiver, Sender, grpc::GrpcBi},
}; };
use async_trait::async_trait; use async_trait::async_trait;
use tonic::Status; use tonic::Status;
@@ -170,8 +169,8 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
impl Bi<auth::Inbound, Result<auth::Outbound, auth::Error>> for AuthTransportAdapter<'_> {} impl Bi<auth::Inbound, Result<auth::Outbound, auth::Error>> for AuthTransportAdapter<'_> {}
fn client_metadata_from_proto(metadata: ProtoClientInfo) -> auth::ClientMetadata { fn client_metadata_from_proto(metadata: ProtoClientInfo) -> ClientMetadata {
auth::ClientMetadata { ClientMetadata {
name: metadata.name, name: metadata.name,
description: metadata.description, description: metadata.description,
version: metadata.version, version: metadata.version,

View File

@@ -20,15 +20,18 @@ use arbiter_proto::{
}, },
user_agent::{ user_agent::{
BootstrapEncryptedKey as ProtoBootstrapEncryptedKey, BootstrapEncryptedKey as ProtoBootstrapEncryptedKey,
BootstrapResult as ProtoBootstrapResult, ClientConnectionCancel, BootstrapResult as ProtoBootstrapResult,
ClientConnectionRequest, UnsealEncryptedKey as ProtoUnsealEncryptedKey, SdkClientConnectionCancel as ProtoSdkClientConnectionCancel,
UnsealResult as ProtoUnsealResult, UnsealStart, UserAgentRequest, UserAgentResponse, SdkClientConnectionRequest as ProtoSdkClientConnectionRequest,
VaultState as ProtoVaultState, user_agent_request::Payload as UserAgentRequestPayload, UnsealEncryptedKey as ProtoUnsealEncryptedKey, UnsealResult as ProtoUnsealResult,
UnsealStart, UserAgentRequest, UserAgentResponse, VaultState as ProtoVaultState,
user_agent_request::Payload as UserAgentRequestPayload,
user_agent_response::Payload as UserAgentResponsePayload, user_agent_response::Payload as UserAgentResponsePayload,
}, },
}, },
transport::{Error as TransportError, Receiver, Sender, grpc::GrpcBi}, transport::{Error as TransportError, Receiver, Sender, grpc::GrpcBi},
}; };
use prost_types::{Timestamp as ProtoTimestamp, };
use async_trait::async_trait; use async_trait::async_trait;
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use kameo::{ use kameo::{
@@ -261,12 +264,14 @@ async fn dispatch_conn_message(
actor.ask(HandleGrantDelete { grant_id }).await, actor.ask(HandleGrantDelete { grant_id }).await,
)) ))
} }
UserAgentRequestPayload::ClientConnectionResponse(resp) => { UserAgentRequestPayload::SdkClientConnectionResponse(resp) => {
let pubkey_bytes: [u8; 32] = match resp.pubkey.try_into() { let pubkey_bytes: [u8; 32] = match resp.pubkey.try_into() {
Ok(bytes) => bytes, Ok(bytes) => bytes,
Err(_) => { Err(_) => {
let _ = bi let _ = bi
.send(Err(Status::invalid_argument("Invalid Ed25519 public key length"))) .send(Err(Status::invalid_argument(
"Invalid Ed25519 public key length",
)))
.await; .await;
return Err(()); return Err(());
} }
@@ -289,13 +294,18 @@ async fn dispatch_conn_message(
.await .await
{ {
warn!(?err, "Failed to process client connection response"); warn!(?err, "Failed to process client connection response");
let _ = bi.send(Err(Status::internal("Failed to process response"))).await; let _ = bi
.send(Err(Status::internal("Failed to process response")))
.await;
return Err(()); return Err(());
} }
return Ok(()); return Ok(());
} }
UserAgentRequestPayload::AuthChallengeRequest(..) | UserAgentRequestPayload::AuthChallengeSolution(..) => { UserAgentRequestPayload::SdkClientRevoke(_sdk_client_revoke_request) => todo!(),
UserAgentRequestPayload::SdkClientList(_) => todo!(),
UserAgentRequestPayload::AuthChallengeRequest(..)
| UserAgentRequestPayload::AuthChallengeSolution(..) => {
warn!(?payload, "Unsupported post-auth user agent request"); warn!(?payload, "Unsupported post-auth user agent request");
let _ = bi let _ = bi
.send(Err(Status::invalid_argument( .send(Err(Status::invalid_argument(
@@ -304,7 +314,7 @@ async fn dispatch_conn_message(
.await; .await;
return Err(()); return Err(());
} }
}; };
bi.send(Ok(UserAgentResponse { bi.send(Ok(UserAgentResponse {
@@ -321,7 +331,7 @@ async fn send_out_of_band(
) -> Result<(), ()> { ) -> Result<(), ()> {
let payload = match oob { let payload = match oob {
OutOfBand::ClientConnectionRequest { profile } => { OutOfBand::ClientConnectionRequest { profile } => {
UserAgentResponsePayload::ClientConnectionRequest(ClientConnectionRequest { UserAgentResponsePayload::SdkClientConnectionRequest(ProtoSdkClientConnectionRequest {
pubkey: profile.pubkey.to_bytes().to_vec(), pubkey: profile.pubkey.to_bytes().to_vec(),
info: Some(ProtoClientMetadata { info: Some(ProtoClientMetadata {
name: profile.metadata.name, name: profile.metadata.name,
@@ -331,7 +341,7 @@ async fn send_out_of_band(
}) })
} }
OutOfBand::ClientConnectionCancel { pubkey } => { OutOfBand::ClientConnectionCancel { pubkey } => {
UserAgentResponsePayload::ClientConnectionCancel(ClientConnectionCancel { UserAgentResponsePayload::SdkClientConnectionCancel(ProtoSdkClientConnectionCancel {
pubkey: pubkey.to_bytes().to_vec(), pubkey: pubkey.to_bytes().to_vec(),
}) })
} }
@@ -435,9 +445,7 @@ fn u256_from_proto_bytes(bytes: &[u8]) -> Result<U256, Status> {
Ok(U256::from_be_slice(bytes)) Ok(U256::from_be_slice(bytes))
} }
fn proto_timestamp_to_utc( fn proto_timestamp_to_utc(timestamp: ProtoTimestamp) -> Result<chrono::DateTime<Utc>, Status> {
timestamp: prost_types::Timestamp,
) -> Result<chrono::DateTime<Utc>, Status> {
Utc.timestamp_opt(timestamp.seconds, timestamp.nanos as u32) Utc.timestamp_opt(timestamp.seconds, timestamp.nanos as u32)
.single() .single()
.ok_or_else(|| Status::invalid_argument("Invalid timestamp")) .ok_or_else(|| Status::invalid_argument("Invalid timestamp"))
@@ -447,11 +455,11 @@ fn shared_settings_to_proto(shared: SharedGrantSettings) -> ProtoSharedSettings
ProtoSharedSettings { ProtoSharedSettings {
wallet_access_id: shared.wallet_access_id, wallet_access_id: shared.wallet_access_id,
chain_id: shared.chain, chain_id: shared.chain,
valid_from: shared.valid_from.map(|time| prost_types::Timestamp { valid_from: shared.valid_from.map(|time| ProtoTimestamp {
seconds: time.timestamp(), seconds: time.timestamp(),
nanos: time.timestamp_subsec_nanos() as i32, nanos: time.timestamp_subsec_nanos() as i32,
}), }),
valid_until: shared.valid_until.map(|time| prost_types::Timestamp { valid_until: shared.valid_until.map(|time| ProtoTimestamp {
seconds: time.timestamp(), seconds: time.timestamp(),
nanos: time.timestamp_subsec_nanos() as i32, nanos: time.timestamp_subsec_nanos() as i32,
}), }),

View File

@@ -1,6 +1,4 @@
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use crate::context::ServerContext; use crate::context::ServerContext;
pub mod actors; pub mod actors;

View File

@@ -1,3 +1,4 @@
use arbiter_proto::ClientMetadata;
use arbiter_proto::transport::{Receiver, Sender}; use arbiter_proto::transport::{Receiver, Sender};
use arbiter_server::actors::GlobalActors; use arbiter_server::actors::GlobalActors;
use arbiter_server::{ use arbiter_server::{
@@ -10,8 +11,8 @@ use ed25519_dalek::Signer as _;
use super::common::ChannelTransport; use super::common::ChannelTransport;
fn metadata(name: &str, description: Option<&str>, version: Option<&str>) -> auth::ClientMetadata { fn metadata(name: &str, description: Option<&str>, version: Option<&str>) -> ClientMetadata {
auth::ClientMetadata { ClientMetadata {
name: name.to_owned(), name: name.to_owned(),
description: description.map(str::to_owned), description: description.map(str::to_owned),
version: version.map(str::to_owned), version: version.map(str::to_owned),
@@ -21,7 +22,7 @@ fn metadata(name: &str, description: Option<&str>, version: Option<&str>) -> aut
async fn insert_registered_client( async fn insert_registered_client(
db: &db::DatabasePool, db: &db::DatabasePool,
pubkey: Vec<u8>, pubkey: Vec<u8>,
metadata: &auth::ClientMetadata, metadata: &ClientMetadata,
) { ) {
use arbiter_server::db::schema::{client_metadata, program_client}; use arbiter_server::db::schema::{client_metadata, program_client};