refactor(arbiter-client): rewrite errors to terrros

This commit is contained in:
CleverWild
2026-03-24 17:25:45 +01:00
parent 2148faa376
commit efb11d2271
10 changed files with 235 additions and 132 deletions

6
server/Cargo.lock generated
View File

@@ -686,6 +686,7 @@ dependencies = [
"http", "http",
"rand 0.10.0", "rand 0.10.0",
"rustls-webpki", "rustls-webpki",
"terrors",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
@@ -4908,6 +4909,11 @@ dependencies = [
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]
[[package]]
name = "terrors"
version = "0.5.1"
source = "git+https://github.com/CleverWild/terrors#a0867fd9ca3fbb44c32e92113a917f1577b5716a"
[[package]] [[package]]
name = "test-log" name = "test-log"
version = "0.2.19" version = "0.2.19"

View File

@@ -1,7 +1,5 @@
[workspace] [workspace]
members = [ members = ["crates/*"]
"crates/*",
]
resolver = "3" resolver = "3"
[workspace.lints.clippy] [workspace.lints.clippy]
@@ -43,3 +41,4 @@ k256 = { version = "0.13.4", features = ["ecdsa", "pkcs8"] }
rsa = { version = "0.9", features = ["sha2"] } rsa = { version = "0.9", features = ["sha2"] }
sha2 = "0.10" sha2 = "0.10"
spki = "0.7" spki = "0.7"
terrors = { version = "0.5", git = "https://github.com/CleverWild/terrors" }

View File

@@ -24,3 +24,4 @@ http = "1.4.0"
rustls-webpki = { version = "0.103.10", features = ["aws-lc-rs"] } rustls-webpki = { version = "0.103.10", features = ["aws-lc-rs"] }
async-trait.workspace = true async-trait.workspace = true
rand.workspace = true rand.workspace = true
terrors.workspace = true

View File

@@ -7,54 +7,15 @@ use arbiter_proto::{
}, },
}; };
use ed25519_dalek::Signer as _; use ed25519_dalek::Signer as _;
use terrors::OneOf;
use crate::{ use crate::{
storage::StorageError, errors::{
ConnectError, MissingAuthChallengeError, UnexpectedAuthResponseError, map_auth_code_error,
},
transport::{ClientTransport, next_request_id}, 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( async fn send_auth_challenge_request(
transport: &mut ClientTransport, transport: &mut ClientTransport,
key: &ed25519_dalek::SigningKey, key: &ed25519_dalek::SigningKey,
@@ -69,7 +30,7 @@ async fn send_auth_challenge_request(
)), )),
}) })
.await .await
.map_err(|_| ConnectError::UnexpectedAuthResponse) .map_err(|_| OneOf::new(UnexpectedAuthResponseError))
} }
async fn receive_auth_challenge( async fn receive_auth_challenge(
@@ -78,13 +39,15 @@ async fn receive_auth_challenge(
let response = transport let response = transport
.recv() .recv()
.await .await
.map_err(|_| ConnectError::MissingAuthChallenge)?; .map_err(|_| OneOf::new(MissingAuthChallengeError))?;
let payload = response.payload.ok_or(ConnectError::MissingAuthChallenge)?; let payload = response
.payload
.ok_or_else(|| OneOf::new(MissingAuthChallengeError))?;
match payload { match payload {
ClientResponsePayload::AuthChallenge(challenge) => Ok(challenge), ClientResponsePayload::AuthChallenge(challenge) => Ok(challenge),
ClientResponsePayload::AuthResult(result) => Err(map_auth_result(result)), ClientResponsePayload::AuthResult(result) => Err(map_auth_code_error(result)),
_ => Err(ConnectError::UnexpectedAuthResponse), _ => Err(OneOf::new(UnexpectedAuthResponseError)),
} }
} }
@@ -104,7 +67,7 @@ async fn send_auth_challenge_solution(
)), )),
}) })
.await .await
.map_err(|_| ConnectError::UnexpectedAuthResponse) .map_err(|_| OneOf::new(UnexpectedAuthResponseError))
} }
async fn receive_auth_confirmation( async fn receive_auth_confirmation(
@@ -113,19 +76,19 @@ async fn receive_auth_confirmation(
let response = transport let response = transport
.recv() .recv()
.await .await
.map_err(|_| ConnectError::UnexpectedAuthResponse)?; .map_err(|_| OneOf::new(UnexpectedAuthResponseError))?;
let payload = response let payload = response
.payload .payload
.ok_or(ConnectError::UnexpectedAuthResponse)?; .ok_or_else(|| OneOf::new(UnexpectedAuthResponseError))?;
match payload { match payload {
ClientResponsePayload::AuthResult(result) ClientResponsePayload::AuthResult(result)
if AuthResult::try_from(result).ok() == Some(AuthResult::Success) => if AuthResult::try_from(result).ok() == Some(AuthResult::Success) =>
{ {
Ok(()) Ok(())
} }
ClientResponsePayload::AuthResult(result) => Err(map_auth_result(result)), ClientResponsePayload::AuthResult(result) => Err(map_auth_code_error(result)),
_ => Err(ConnectError::UnexpectedAuthResponse), _ => Err(OneOf::new(UnexpectedAuthResponseError)),
} }
} }

View File

@@ -1,27 +1,23 @@
use arbiter_proto::{proto::arbiter_service_client::ArbiterServiceClient, url::ArbiterUrl}; use arbiter_proto::{proto::arbiter_service_client::ArbiterServiceClient, url::ArbiterUrl};
use std::sync::Arc; use std::sync::Arc;
use terrors::{Broaden as _, OneOf};
use tokio::sync::{Mutex, mpsc}; use tokio::sync::{Mutex, mpsc};
use tokio_stream::wrappers::ReceiverStream; use tokio_stream::wrappers::ReceiverStream;
use tonic::transport::ClientTlsConfig; use tonic::transport::ClientTlsConfig;
use crate::{ use crate::{
auth::{ConnectError, authenticate}, auth::authenticate,
errors::ConnectError,
storage::{FileSigningKeyStorage, SigningKeyStorage}, storage::{FileSigningKeyStorage, SigningKeyStorage},
transport::{BUFFER_LENGTH, ClientTransport}, transport::{BUFFER_LENGTH, ClientTransport},
}; };
#[cfg(feature = "evm")]
use crate::errors::{ClientConnectionClosedError, ClientError};
#[cfg(feature = "evm")] #[cfg(feature = "evm")]
use crate::wallets::evm::ArbiterEvmWallet; 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 { pub struct ArbiterClient {
#[allow(dead_code)] #[allow(dead_code)]
transport: Arc<Mutex<ClientTransport>>, transport: Arc<Mutex<ClientTransport>>,
@@ -29,7 +25,7 @@ pub struct ArbiterClient {
impl ArbiterClient { impl ArbiterClient {
pub async fn connect(url: ArbiterUrl) -> Result<Self, ConnectError> { pub async fn connect(url: ArbiterUrl) -> Result<Self, ConnectError> {
let storage = FileSigningKeyStorage::from_default_location()?; let storage = FileSigningKeyStorage::from_default_location().broaden()?;
Self::connect_with_storage(url, &storage).await Self::connect_with_storage(url, &storage).await
} }
@@ -37,7 +33,7 @@ impl ArbiterClient {
url: ArbiterUrl, url: ArbiterUrl,
storage: &S, storage: &S,
) -> Result<Self, ConnectError> { ) -> Result<Self, ConnectError> {
let key = storage.load_or_create()?; let key = storage.load_or_create().broaden()?;
Self::connect_with_key(url, key).await Self::connect_with_key(url, key).await
} }
@@ -45,17 +41,26 @@ impl ArbiterClient {
url: ArbiterUrl, url: ArbiterUrl,
key: ed25519_dalek::SigningKey, key: ed25519_dalek::SigningKey,
) -> Result<Self, ConnectError> { ) -> Result<Self, ConnectError> {
let anchor = webpki::anchor_from_trusted_cert(&url.ca_cert)?.to_owned(); let anchor = webpki::anchor_from_trusted_cert(&url.ca_cert)
.map_err(OneOf::new)?
.to_owned();
let tls = ClientTlsConfig::new().trust_anchor(anchor); let tls = ClientTlsConfig::new().trust_anchor(anchor);
let channel = tonic::transport::Channel::from_shared(format!("{}:{}", url.host, url.port))? let channel = tonic::transport::Channel::from_shared(format!("{}:{}", url.host, url.port))
.tls_config(tls)? .map_err(OneOf::new)?
.tls_config(tls)
.map_err(OneOf::new)?
.connect() .connect()
.await?; .await
.map_err(OneOf::new)?;
let mut client = ArbiterServiceClient::new(channel); let mut client = ArbiterServiceClient::new(channel);
let (tx, rx) = mpsc::channel(BUFFER_LENGTH); let (tx, rx) = mpsc::channel(BUFFER_LENGTH);
let response_stream = client.client(ReceiverStream::new(rx)).await?.into_inner(); let response_stream = client
.client(ReceiverStream::new(rx))
.await
.map_err(OneOf::new)?
.into_inner();
let mut transport = ClientTransport { let mut transport = ClientTransport {
sender: tx, sender: tx,
@@ -71,6 +76,7 @@ impl ArbiterClient {
#[cfg(feature = "evm")] #[cfg(feature = "evm")]
pub async fn evm_wallets(&self) -> Result<Vec<ArbiterEvmWallet>, ClientError> { pub async fn evm_wallets(&self) -> Result<Vec<ArbiterEvmWallet>, ClientError> {
todo!("fetch EVM wallet list from server") let _ = &self.transport;
Err(OneOf::new(ClientConnectionClosedError))
} }
} }

View File

@@ -0,0 +1,127 @@
use terrors::OneOf;
use thiserror::Error;
#[cfg(feature = "evm")]
use alloy::{primitives::ChainId, signers::Error as AlloySignerError};
pub type StorageError = OneOf<(std::io::Error, InvalidKeyLengthError)>;
pub type ConnectError = OneOf<(
tonic::transport::Error,
http::uri::InvalidUri,
webpki::Error,
tonic::Status,
MissingAuthChallengeError,
ApprovalDeniedError,
NoUserAgentsOnlineError,
UnexpectedAuthResponseError,
std::io::Error,
InvalidKeyLengthError,
)>;
pub type ClientError = OneOf<(tonic::Status, ClientConnectionClosedError)>;
pub(crate) type ClientTransportError =
OneOf<(TransportChannelClosedError, TransportConnectionClosedError)>;
#[cfg(feature = "evm")]
pub(crate) type EvmWalletError = OneOf<(
EvmChainIdMismatchError,
EvmHashSigningUnsupportedError,
EvmTransactionSigningUnsupportedError,
)>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("Invalid signing key length in storage: expected {expected} bytes, got {actual} bytes")]
pub struct InvalidKeyLengthError {
pub expected: usize,
pub actual: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("Auth challenge was not returned by server")]
pub struct MissingAuthChallengeError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("Client approval denied by User Agent")]
pub struct ApprovalDeniedError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("No User Agents online to approve client")]
pub struct NoUserAgentsOnlineError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("Unexpected auth response payload")]
pub struct UnexpectedAuthResponseError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("Connection closed by server")]
pub struct ClientConnectionClosedError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("Transport channel closed")]
pub struct TransportChannelClosedError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("Connection closed by server")]
pub struct TransportConnectionClosedError;
#[cfg(feature = "evm")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("Transaction chain id mismatch: signer {signer}, tx {tx}")]
pub struct EvmChainIdMismatchError {
pub signer: ChainId,
pub tx: ChainId,
}
#[cfg(feature = "evm")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("hash-only signing is not supported for ArbiterEvmWallet; use transaction signing")]
pub struct EvmHashSigningUnsupportedError;
#[cfg(feature = "evm")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("transaction signing is not supported by current arbiter.client protocol")]
pub struct EvmTransactionSigningUnsupportedError;
pub(crate) fn map_auth_code_error(code: i32) -> ConnectError {
use arbiter_proto::proto::client::AuthResult;
match AuthResult::try_from(code).unwrap_or(AuthResult::Unspecified) {
AuthResult::ApprovalDenied => OneOf::new(ApprovalDeniedError),
AuthResult::NoUserAgentsOnline => OneOf::new(NoUserAgentsOnlineError),
AuthResult::Unspecified
| AuthResult::Success
| AuthResult::InvalidKey
| AuthResult::InvalidSignature
| AuthResult::Internal => OneOf::new(UnexpectedAuthResponseError),
}
}
#[cfg(feature = "evm")]
impl From<EvmChainIdMismatchError> for AlloySignerError {
fn from(value: EvmChainIdMismatchError) -> Self {
AlloySignerError::TransactionChainIdMismatch {
signer: value.signer,
tx: value.tx,
}
}
}
#[cfg(feature = "evm")]
impl From<EvmHashSigningUnsupportedError> for AlloySignerError {
fn from(_value: EvmHashSigningUnsupportedError) -> Self {
AlloySignerError::other(
"hash-only signing is not supported for ArbiterEvmWallet; use transaction signing",
)
}
}
#[cfg(feature = "evm")]
impl From<EvmTransactionSigningUnsupportedError> for AlloySignerError {
fn from(_value: EvmTransactionSigningUnsupportedError) -> Self {
AlloySignerError::other(
"transaction signing is not supported by current arbiter.client protocol",
)
}
}

View File

@@ -1,12 +1,13 @@
mod auth; mod auth;
mod client; mod client;
mod errors;
mod storage; mod storage;
mod transport; mod transport;
pub mod wallets; pub mod wallets;
pub use auth::ConnectError; pub use client::ArbiterClient;
pub use client::{ArbiterClient, ClientError}; pub use errors::{ClientError, ConnectError, StorageError};
pub use storage::{FileSigningKeyStorage, SigningKeyStorage, StorageError}; pub use storage::{FileSigningKeyStorage, SigningKeyStorage};
#[cfg(feature = "evm")] #[cfg(feature = "evm")]
pub use wallets::evm::ArbiterEvmWallet; pub use wallets::evm::ArbiterEvmWallet;

View File

@@ -1,14 +1,8 @@
use arbiter_proto::home_path; use arbiter_proto::home_path;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use terrors::OneOf;
#[derive(Debug, thiserror::Error)] use crate::errors::{InvalidKeyLengthError, StorageError};
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 { pub trait SigningKeyStorage {
fn load_or_create(&self) -> std::result::Result<ed25519_dalek::SigningKey, StorageError>; fn load_or_create(&self) -> std::result::Result<ed25519_dalek::SigningKey, StorageError>;
@@ -27,18 +21,21 @@ impl FileSigningKeyStorage {
} }
pub fn from_default_location() -> std::result::Result<Self, StorageError> { pub fn from_default_location() -> std::result::Result<Self, StorageError> {
Ok(Self::new(home_path()?.join(Self::DEFAULT_FILE_NAME))) Ok(Self::new(
home_path()
.map_err(OneOf::new)?
.join(Self::DEFAULT_FILE_NAME),
))
} }
fn read_key(path: &Path) -> std::result::Result<ed25519_dalek::SigningKey, StorageError> { fn read_key(path: &Path) -> std::result::Result<ed25519_dalek::SigningKey, StorageError> {
let bytes = std::fs::read(path)?; let bytes = std::fs::read(path).map_err(OneOf::new)?;
let raw: [u8; 32] = let raw: [u8; 32] = bytes.try_into().map_err(|v: Vec<u8>| {
bytes OneOf::new(InvalidKeyLengthError {
.try_into() expected: 32,
.map_err(|v: Vec<u8>| StorageError::InvalidKeyLength { actual: v.len(),
expected: 32, })
actual: v.len(), })?;
})?;
Ok(ed25519_dalek::SigningKey::from_bytes(&raw)) Ok(ed25519_dalek::SigningKey::from_bytes(&raw))
} }
} }
@@ -46,7 +43,7 @@ impl FileSigningKeyStorage {
impl SigningKeyStorage for FileSigningKeyStorage { impl SigningKeyStorage for FileSigningKeyStorage {
fn load_or_create(&self) -> std::result::Result<ed25519_dalek::SigningKey, StorageError> { fn load_or_create(&self) -> std::result::Result<ed25519_dalek::SigningKey, StorageError> {
if let Some(parent) = self.path.parent() { if let Some(parent) = self.path.parent() {
std::fs::create_dir_all(parent)?; std::fs::create_dir_all(parent).map_err(OneOf::new)?;
} }
if self.path.exists() { if self.path.exists() {
@@ -64,20 +61,21 @@ impl SigningKeyStorage for FileSigningKeyStorage {
{ {
Ok(mut file) => { Ok(mut file) => {
use std::io::Write as _; use std::io::Write as _;
file.write_all(&raw_key)?; file.write_all(&raw_key).map_err(OneOf::new)?;
Ok(key) Ok(key)
} }
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => { Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {
Self::read_key(&self.path) Self::read_key(&self.path)
} }
Err(err) => Err(StorageError::Io(err)), Err(err) => Err(OneOf::new(err)),
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{FileSigningKeyStorage, SigningKeyStorage, StorageError}; use super::{FileSigningKeyStorage, SigningKeyStorage};
use crate::errors::InvalidKeyLengthError;
fn unique_temp_key_path() -> std::path::PathBuf { fn unique_temp_key_path() -> std::path::PathBuf {
let nanos = std::time::SystemTime::now() let nanos = std::time::SystemTime::now()
@@ -119,12 +117,12 @@ mod tests {
.load_or_create() .load_or_create()
.expect_err("storage should reject non-32-byte key file"); .expect_err("storage should reject non-32-byte key file");
match err { match err.narrow::<InvalidKeyLengthError, _>() {
StorageError::InvalidKeyLength { expected, actual } => { Ok(invalid_len) => {
assert_eq!(expected, 32); assert_eq!(invalid_len.expected, 32);
assert_eq!(actual, 31); assert_eq!(invalid_len.actual, 31);
} }
other => panic!("unexpected error: {other:?}"), Err(other) => panic!("unexpected io error: {other:?}"),
} }
std::fs::remove_file(path).expect("temp key file should be removable"); std::fs::remove_file(path).expect("temp key file should be removable");

View File

@@ -1,9 +1,12 @@
use arbiter_proto::proto::{ use arbiter_proto::proto::client::{ClientRequest, ClientResponse};
client::{ClientRequest, ClientResponse},
};
use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::atomic::{AtomicI32, Ordering};
use terrors::OneOf;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use crate::errors::{
ClientTransportError, TransportChannelClosedError, TransportConnectionClosedError,
};
pub(crate) const BUFFER_LENGTH: usize = 16; pub(crate) const BUFFER_LENGTH: usize = 16;
static NEXT_REQUEST_ID: AtomicI32 = AtomicI32::new(1); static NEXT_REQUEST_ID: AtomicI32 = AtomicI32::new(1);
@@ -11,15 +14,6 @@ pub(crate) fn next_request_id() -> i32 {
NEXT_REQUEST_ID.fetch_add(1, Ordering::Relaxed) 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) struct ClientTransport {
pub(crate) sender: mpsc::Sender<ClientRequest>, pub(crate) sender: mpsc::Sender<ClientRequest>,
pub(crate) receiver: tonic::Streaming<ClientResponse>, pub(crate) receiver: tonic::Streaming<ClientResponse>,
@@ -29,20 +23,20 @@ impl ClientTransport {
pub(crate) async fn send( pub(crate) async fn send(
&mut self, &mut self,
request: ClientRequest, request: ClientRequest,
) -> std::result::Result<(), ClientSignError> { ) -> std::result::Result<(), ClientTransportError> {
self.sender self.sender
.send(request) .send(request)
.await .await
.map_err(|_| ClientSignError::ChannelClosed) .map_err(|_| OneOf::new(TransportChannelClosedError))
} }
pub(crate) async fn recv( pub(crate) async fn recv(
&mut self, &mut self,
) -> std::result::Result<ClientResponse, ClientSignError> { ) -> std::result::Result<ClientResponse, ClientTransportError> {
match self.receiver.message().await { match self.receiver.message().await {
Ok(Some(resp)) => Ok(resp), Ok(Some(resp)) => Ok(resp),
Ok(None) => Err(ClientSignError::ConnectionClosed), Ok(None) => Err(OneOf::new(TransportConnectionClosedError)),
Err(_) => Err(ClientSignError::ConnectionClosed), Err(_) => Err(OneOf::new(TransportConnectionClosedError)),
} }
} }
} }

View File

@@ -2,13 +2,20 @@ use alloy::{
consensus::SignableTransaction, consensus::SignableTransaction,
network::TxSigner, network::TxSigner,
primitives::{Address, B256, ChainId, Signature}, primitives::{Address, B256, ChainId, Signature},
signers::{Error, Result, Signer}, signers::{Result, Signer},
}; };
use async_trait::async_trait; use async_trait::async_trait;
use std::sync::Arc; use std::sync::Arc;
use terrors::OneOf;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::transport::ClientTransport; use crate::{
errors::{
EvmChainIdMismatchError, EvmHashSigningUnsupportedError,
EvmTransactionSigningUnsupportedError, EvmWalletError,
},
transport::ClientTransport,
};
pub struct ArbiterEvmWallet { pub struct ArbiterEvmWallet {
transport: Arc<Mutex<ClientTransport>>, transport: Arc<Mutex<ClientTransport>>,
@@ -17,6 +24,7 @@ pub struct ArbiterEvmWallet {
} }
impl ArbiterEvmWallet { impl ArbiterEvmWallet {
#[allow(dead_code)]
pub(crate) fn new(transport: Arc<Mutex<ClientTransport>>, address: Address) -> Self { pub(crate) fn new(transport: Arc<Mutex<ClientTransport>>, address: Address) -> Self {
Self { Self {
transport, transport,
@@ -34,14 +42,17 @@ impl ArbiterEvmWallet {
self self
} }
fn validate_chain_id(&self, tx: &mut dyn SignableTransaction<Signature>) -> Result<()> { fn validate_chain_id(
&self,
tx: &mut dyn SignableTransaction<Signature>,
) -> std::result::Result<(), EvmWalletError> {
if let Some(chain_id) = self.chain_id if let Some(chain_id) = self.chain_id
&& !tx.set_chain_id_checked(chain_id) && !tx.set_chain_id_checked(chain_id)
{ {
return Err(Error::TransactionChainIdMismatch { return Err(OneOf::new(EvmChainIdMismatchError {
signer: chain_id, signer: chain_id,
tx: tx.chain_id().unwrap(), tx: tx.chain_id().unwrap(),
}); }));
} }
Ok(()) Ok(())
@@ -51,9 +62,7 @@ impl ArbiterEvmWallet {
#[async_trait] #[async_trait]
impl Signer for ArbiterEvmWallet { impl Signer for ArbiterEvmWallet {
async fn sign_hash(&self, _hash: &B256) -> Result<Signature> { async fn sign_hash(&self, _hash: &B256) -> Result<Signature> {
Err(Error::other( Err(EvmWalletError::new(EvmHashSigningUnsupportedError).into())
"hash-only signing is not supported for ArbiterEvmWallet; use transaction signing",
))
} }
fn address(&self) -> Address { fn address(&self) -> Address {
@@ -80,10 +89,9 @@ impl TxSigner<Signature> for ArbiterEvmWallet {
tx: &mut dyn SignableTransaction<Signature>, tx: &mut dyn SignableTransaction<Signature>,
) -> Result<Signature> { ) -> Result<Signature> {
let _transport = self.transport.lock().await; let _transport = self.transport.lock().await;
self.validate_chain_id(tx)?; self.validate_chain_id(tx)
.map_err(OneOf::into::<alloy::signers::Error>)?;
Err(Error::other( Err(EvmWalletError::new(EvmTransactionSigningUnsupportedError).into())
"transaction signing is not supported by current arbiter.client protocol",
))
} }
} }