Merge pull request 'feat(evm): implement EVM sign transaction handling in client and user agent' (#38) from feat--self-signed-transactions into main
Some checks failed
Some checks failed
Reviewed-on: #38 Reviewed-by: Stas <business@jexter.tech>
This commit was merged in pull request #38.
This commit is contained in:
@@ -49,6 +49,7 @@ message ClientRequest {
|
|||||||
AuthChallengeRequest auth_challenge_request = 1;
|
AuthChallengeRequest auth_challenge_request = 1;
|
||||||
AuthChallengeSolution auth_challenge_solution = 2;
|
AuthChallengeSolution auth_challenge_solution = 2;
|
||||||
google.protobuf.Empty query_vault_state = 3;
|
google.protobuf.Empty query_vault_state = 3;
|
||||||
|
arbiter.evm.EvmSignTransactionRequest evm_sign_transaction = 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,11 @@ message ListWalletAccessResponse {
|
|||||||
repeated SdkClientWalletAccess accesses = 1;
|
repeated SdkClientWalletAccess accesses = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message UserAgentEvmSignTransactionRequest {
|
||||||
|
int32 client_id = 1;
|
||||||
|
arbiter.evm.EvmSignTransactionRequest request = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message UserAgentRequest {
|
message UserAgentRequest {
|
||||||
int32 id = 16;
|
int32 id = 16;
|
||||||
oneof payload {
|
oneof payload {
|
||||||
@@ -174,6 +179,7 @@ message UserAgentRequest {
|
|||||||
SdkClientGrantWalletAccess grant_wallet_access = 15;
|
SdkClientGrantWalletAccess grant_wallet_access = 15;
|
||||||
SdkClientRevokeWalletAccess revoke_wallet_access = 17;
|
SdkClientRevokeWalletAccess revoke_wallet_access = 17;
|
||||||
google.protobuf.Empty list_wallet_access = 18;
|
google.protobuf.Empty list_wallet_access = 18;
|
||||||
|
UserAgentEvmSignTransactionRequest evm_sign_transaction = 19;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
message UserAgentResponse {
|
message UserAgentResponse {
|
||||||
@@ -195,5 +201,6 @@ message UserAgentResponse {
|
|||||||
SdkClientListResponse sdk_client_list_response = 14;
|
SdkClientListResponse sdk_client_list_response = 14;
|
||||||
BootstrapResult bootstrap_result = 15;
|
BootstrapResult bootstrap_result = 15;
|
||||||
ListWalletAccessResponse list_wallet_access_response = 17;
|
ListWalletAccessResponse list_wallet_access_response = 17;
|
||||||
|
arbiter.evm.EvmSignTransactionResponse evm_sign_transaction = 18;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
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 tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
@@ -36,9 +34,7 @@ impl ClientTransport {
|
|||||||
.map_err(|_| ClientSignError::ChannelClosed)
|
.map_err(|_| ClientSignError::ChannelClosed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn recv(
|
pub(crate) async fn recv(&mut self) -> std::result::Result<ClientResponse, ClientSignError> {
|
||||||
&mut self,
|
|
||||||
) -> std::result::Result<ClientResponse, ClientSignError> {
|
|
||||||
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(ClientSignError::ConnectionClosed),
|
||||||
|
|||||||
@@ -8,7 +8,15 @@ use async_trait::async_trait;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::transport::ClientTransport;
|
use arbiter_proto::proto::{
|
||||||
|
client::{
|
||||||
|
ClientRequest, client_request::Payload as ClientRequestPayload,
|
||||||
|
client_response::Payload as ClientResponsePayload,
|
||||||
|
},
|
||||||
|
evm::evm_sign_transaction_response::Result as EvmSignTransactionResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::transport::{ClientTransport, next_request_id};
|
||||||
|
|
||||||
pub struct ArbiterEvmWallet {
|
pub struct ArbiterEvmWallet {
|
||||||
transport: Arc<Mutex<ClientTransport>>,
|
transport: Arc<Mutex<ClientTransport>>,
|
||||||
@@ -79,11 +87,61 @@ impl TxSigner<Signature> for ArbiterEvmWallet {
|
|||||||
&self,
|
&self,
|
||||||
tx: &mut dyn SignableTransaction<Signature>,
|
tx: &mut dyn SignableTransaction<Signature>,
|
||||||
) -> Result<Signature> {
|
) -> Result<Signature> {
|
||||||
let _transport = self.transport.lock().await;
|
|
||||||
self.validate_chain_id(tx)?;
|
self.validate_chain_id(tx)?;
|
||||||
|
|
||||||
Err(Error::other(
|
let mut transport = self.transport.lock().await;
|
||||||
"transaction signing is not supported by current arbiter.client protocol",
|
let request_id = next_request_id();
|
||||||
))
|
let rlp_transaction = tx.encoded_for_signing();
|
||||||
|
|
||||||
|
transport
|
||||||
|
.send(ClientRequest {
|
||||||
|
request_id,
|
||||||
|
payload: Some(ClientRequestPayload::EvmSignTransaction(
|
||||||
|
arbiter_proto::proto::evm::EvmSignTransactionRequest {
|
||||||
|
wallet_address: self.address.to_vec(),
|
||||||
|
rlp_transaction,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::other("failed to send evm sign transaction request"))?;
|
||||||
|
|
||||||
|
let response = transport
|
||||||
|
.recv()
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::other("failed to receive evm sign transaction response"))?;
|
||||||
|
|
||||||
|
if response.request_id != Some(request_id) {
|
||||||
|
return Err(Error::other(
|
||||||
|
"received mismatched response id for evm sign transaction",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = response
|
||||||
|
.payload
|
||||||
|
.ok_or_else(|| Error::other("missing evm sign transaction response payload"))?;
|
||||||
|
|
||||||
|
let ClientResponsePayload::EvmSignTransaction(response) = payload else {
|
||||||
|
return Err(Error::other(
|
||||||
|
"unexpected response payload for evm sign transaction request",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = response
|
||||||
|
.result
|
||||||
|
.ok_or_else(|| Error::other("missing evm sign transaction result"))?;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
EvmSignTransactionResult::Signature(signature) => {
|
||||||
|
Signature::try_from(signature.as_slice())
|
||||||
|
.map_err(|_| Error::other("invalid signature returned by server"))
|
||||||
|
}
|
||||||
|
EvmSignTransactionResult::EvalError(eval_error) => Err(Error::other(format!(
|
||||||
|
"transaction rejected by policy: {eval_error:?}"
|
||||||
|
))),
|
||||||
|
EvmSignTransactionResult::Error(code) => Err(Error::other(format!(
|
||||||
|
"server failed to sign transaction with error code {code}"
|
||||||
|
))),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use arbiter_proto::{
|
use arbiter_proto::{
|
||||||
ClientMetadata, format_challenge, transport::{Bi, expect_message}
|
ClientMetadata, format_challenge,
|
||||||
|
transport::{Bi, expect_message},
|
||||||
};
|
};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
@@ -83,7 +84,6 @@ async fn get_client_and_nonce(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
conn.exclusive_transaction(|conn| {
|
conn.exclusive_transaction(|conn| {
|
||||||
let pubkey_bytes = pubkey_bytes.clone();
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let Some((client_id, 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))
|
||||||
@@ -290,7 +290,7 @@ where
|
|||||||
pub async fn authenticate<T>(
|
pub async fn authenticate<T>(
|
||||||
props: &mut ClientConnection,
|
props: &mut ClientConnection,
|
||||||
transport: &mut T,
|
transport: &mut T,
|
||||||
) -> Result<VerifyingKey, Error>
|
) -> Result<i32, Error>
|
||||||
where
|
where
|
||||||
T: Bi<Inbound, Result<Outbound, Error>> + Send + ?Sized,
|
T: Bi<Inbound, Result<Outbound, Error>> + Send + ?Sized,
|
||||||
{
|
{
|
||||||
@@ -318,7 +318,6 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
sync_client_metadata(&props.db, info.id, &metadata).await?;
|
sync_client_metadata(&props.db, info.id, &metadata).await?;
|
||||||
|
|
||||||
challenge_client(transport, pubkey, info.current_nonce).await?;
|
challenge_client(transport, pubkey, info.current_nonce).await?;
|
||||||
|
|
||||||
transport
|
transport
|
||||||
@@ -329,5 +328,5 @@ where
|
|||||||
Error::Transport
|
Error::Transport
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(pubkey)
|
Ok(info.id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use kameo::actor::Spawn;
|
|||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actors::{GlobalActors, client::{ session::ClientSession}},
|
actors::{GlobalActors, client::session::ClientSession},
|
||||||
db,
|
db,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,7 +20,10 @@ pub struct ClientConnection {
|
|||||||
|
|
||||||
impl ClientConnection {
|
impl ClientConnection {
|
||||||
pub fn new(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
pub fn new(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
||||||
Self { db, actors }
|
Self {
|
||||||
|
db,
|
||||||
|
actors,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,8 +35,8 @@ where
|
|||||||
T: Bi<auth::Inbound, Result<auth::Outbound, auth::Error>> + Send + ?Sized,
|
T: Bi<auth::Inbound, Result<auth::Outbound, auth::Error>> + Send + ?Sized,
|
||||||
{
|
{
|
||||||
match auth::authenticate(&mut props, transport).await {
|
match auth::authenticate(&mut props, transport).await {
|
||||||
Ok(_pubkey) => {
|
Ok(client_id) => {
|
||||||
ClientSession::spawn(ClientSession::new(props));
|
ClientSession::spawn(ClientSession::new(props, client_id));
|
||||||
info!("Client authenticated, session started");
|
info!("Client authenticated, session started");
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|||||||
@@ -1,21 +1,30 @@
|
|||||||
|
use ed25519_dalek::VerifyingKey;
|
||||||
use kameo::{Actor, messages};
|
use kameo::{Actor, messages};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
|
use alloy::{consensus::TxEip1559, primitives::Address, signers::Signature};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actors::{
|
actors::{
|
||||||
GlobalActors, client::ClientConnection, flow_coordinator::RegisterClient,
|
GlobalActors,
|
||||||
|
client::ClientConnection, flow_coordinator::RegisterClient,
|
||||||
|
|
||||||
|
evm::{ClientSignTransaction, SignTransactionError},
|
||||||
keyholder::KeyHolderState,
|
keyholder::KeyHolderState,
|
||||||
|
|
||||||
},
|
},
|
||||||
db,
|
db,
|
||||||
|
evm::VetError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ClientSession {
|
pub struct ClientSession {
|
||||||
props: ClientConnection,
|
props: ClientConnection,
|
||||||
|
client_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientSession {
|
impl ClientSession {
|
||||||
pub(crate) fn new(props: ClientConnection) -> Self {
|
pub(crate) fn new(props: ClientConnection, client_id: i32) -> Self {
|
||||||
Self { props }
|
Self { props, client_id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +44,34 @@ impl ClientSession {
|
|||||||
|
|
||||||
Ok(vault_state)
|
Ok(vault_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[message]
|
||||||
|
pub(crate) async fn handle_sign_transaction(
|
||||||
|
&mut self,
|
||||||
|
wallet_address: Address,
|
||||||
|
transaction: TxEip1559,
|
||||||
|
) -> Result<Signature, SignTransactionRpcError> {
|
||||||
|
match self
|
||||||
|
.props
|
||||||
|
.actors
|
||||||
|
.evm
|
||||||
|
.ask(ClientSignTransaction {
|
||||||
|
client_id: self.client_id,
|
||||||
|
wallet_address,
|
||||||
|
transaction,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(signature) => Ok(signature),
|
||||||
|
Err(kameo::error::SendError::HandlerError(SignTransactionError::Vet(vet_error))) => {
|
||||||
|
Err(SignTransactionRpcError::Vet(vet_error))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(?err, "Failed to sign EVM transaction in client session");
|
||||||
|
Err(SignTransactionRpcError::Internal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for ClientSession {
|
impl Actor for ClientSession {
|
||||||
@@ -59,7 +96,7 @@ impl Actor for ClientSession {
|
|||||||
impl ClientSession {
|
impl ClientSession {
|
||||||
pub fn new_test(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
pub fn new_test(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
||||||
let props = ClientConnection::new(db, actors);
|
let props = ClientConnection::new(db, actors);
|
||||||
Self { props }
|
Self { props, client_id: 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,3 +107,12 @@ pub enum Error {
|
|||||||
#[error("Internal error")]
|
#[error("Internal error")]
|
||||||
Internal,
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum SignTransactionRpcError {
|
||||||
|
#[error("Policy evaluation failed")]
|
||||||
|
Vet(#[from] VetError),
|
||||||
|
|
||||||
|
#[error("Internal error")]
|
||||||
|
Internal,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use alloy::primitives::Address;
|
use alloy::{consensus::TxEip1559, primitives::Address, signers::Signature};
|
||||||
use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit};
|
use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit};
|
||||||
use diesel::sql_types::ops::Add;
|
use diesel::sql_types::ops::Add;
|
||||||
use diesel::{BoolExpressionMethods as _, ExpressionMethods as _, QueryDsl as _, SelectableHelper};
|
use diesel::{BoolExpressionMethods as _, ExpressionMethods as _, QueryDsl as _, SelectableHelper};
|
||||||
@@ -23,7 +23,8 @@ use crate::safe_cell::SafeCell;
|
|||||||
use crate::{
|
use crate::{
|
||||||
actors::{
|
actors::{
|
||||||
evm::{
|
evm::{
|
||||||
Generate, ListWallets, UseragentCreateGrant, UseragentDeleteGrant, UseragentListGrants,
|
ClientSignTransaction, Generate, ListWallets, SignTransactionError as EvmSignError,
|
||||||
|
UseragentCreateGrant, UseragentDeleteGrant, UseragentListGrants,
|
||||||
},
|
},
|
||||||
keyholder::{self, Bootstrap, TryUnseal},
|
keyholder::{self, Bootstrap, TryUnseal},
|
||||||
user_agent::session::{
|
user_agent::session::{
|
||||||
@@ -112,6 +113,15 @@ pub enum BootstrapError {
|
|||||||
General(#[from] super::Error),
|
General(#[from] super::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum SignTransactionError {
|
||||||
|
#[error("Policy evaluation failed")]
|
||||||
|
Vet(#[from] crate::evm::VetError),
|
||||||
|
|
||||||
|
#[error("Internal signing error")]
|
||||||
|
Internal,
|
||||||
|
}
|
||||||
|
|
||||||
#[messages]
|
#[messages]
|
||||||
impl UserAgentSession {
|
impl UserAgentSession {
|
||||||
#[message]
|
#[message]
|
||||||
@@ -356,6 +366,35 @@ impl UserAgentSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[message]
|
||||||
|
pub(crate) async fn handle_sign_transaction(
|
||||||
|
&mut self,
|
||||||
|
client_id: i32,
|
||||||
|
wallet_address: Address,
|
||||||
|
transaction: TxEip1559,
|
||||||
|
) -> Result<Signature, SignTransactionError> {
|
||||||
|
match self
|
||||||
|
.props
|
||||||
|
.actors
|
||||||
|
.evm
|
||||||
|
.ask(ClientSignTransaction {
|
||||||
|
client_id,
|
||||||
|
wallet_address,
|
||||||
|
transaction,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(signature) => Ok(signature),
|
||||||
|
Err(SendError::HandlerError(EvmSignError::Vet(vet_error))) => {
|
||||||
|
Err(SignTransactionError::Vet(vet_error))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(?err, "EVM sign transaction failed in user-agent session");
|
||||||
|
Err(SignTransactionError::Internal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[message]
|
#[message]
|
||||||
pub(crate) async fn handle_grant_evm_wallet_access(
|
pub(crate) async fn handle_grant_evm_wallet_access(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ mod utils;
|
|||||||
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||||
pub enum PolicyError {
|
pub enum PolicyError {
|
||||||
#[error("Database error")]
|
#[error("Database error")]
|
||||||
Error(#[from] crate::db::DatabaseError),
|
Database(#[from] crate::db::DatabaseError),
|
||||||
#[error("Transaction violates policy: {0:?}")]
|
#[error("Transaction violates policy: {0:?}")]
|
||||||
#[diagnostic(code(arbiter_server::evm::policy_error::violation))]
|
#[diagnostic(code(arbiter_server::evm::policy_error::violation))]
|
||||||
Violations(Vec<EvalViolation>),
|
Violations(Vec<EvalViolation>),
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ use super::{DatabaseID, EvalContext, EvalViolation};
|
|||||||
// Plain ether transfer
|
// Plain ether transfer
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Meaning {
|
pub struct Meaning {
|
||||||
to: Address,
|
pub(crate) to: Address,
|
||||||
value: U256,
|
pub(crate) value: U256,
|
||||||
}
|
}
|
||||||
impl Display for Meaning {
|
impl Display for Meaning {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ fn grant_join() -> _ {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Meaning {
|
pub struct Meaning {
|
||||||
token: &'static TokenInfo,
|
pub(crate) token: &'static TokenInfo,
|
||||||
to: Address,
|
pub(crate) to: Address,
|
||||||
value: U256,
|
pub(crate) value: U256,
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for Meaning {
|
impl std::fmt::Display for Meaning {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
|
use alloy::primitives::Address;
|
||||||
use arbiter_proto::{
|
use arbiter_proto::{
|
||||||
proto::client::{
|
proto::{
|
||||||
ClientRequest, ClientResponse, VaultState as ProtoVaultState,
|
client::{
|
||||||
client_request::Payload as ClientRequestPayload,
|
ClientRequest, ClientResponse, VaultState as ProtoVaultState,
|
||||||
client_response::Payload as ClientResponsePayload,
|
client_request::Payload as ClientRequestPayload,
|
||||||
|
client_response::Payload as ClientResponsePayload,
|
||||||
|
},
|
||||||
|
evm::{
|
||||||
|
EvmError as ProtoEvmError, EvmSignTransactionResponse,
|
||||||
|
evm_sign_transaction_response::Result as EvmSignTransactionResult,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
transport::{Receiver, Sender, grpc::GrpcBi},
|
transport::{Receiver, Sender, grpc::GrpcBi},
|
||||||
};
|
};
|
||||||
@@ -17,11 +24,18 @@ use crate::{
|
|||||||
actors::{
|
actors::{
|
||||||
client::{
|
client::{
|
||||||
self, ClientConnection,
|
self, ClientConnection,
|
||||||
session::{ClientSession, Error, HandleQueryVaultState},
|
session::{
|
||||||
|
ClientSession, Error, HandleQueryVaultState, HandleSignTransaction,
|
||||||
|
SignTransactionRpcError,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
keyholder::KeyHolderState,
|
keyholder::KeyHolderState,
|
||||||
},
|
},
|
||||||
grpc::request_tracker::RequestTracker,
|
grpc::{
|
||||||
|
Convert, TryConvert,
|
||||||
|
common::inbound::{RawEvmAddress, RawEvmTransaction},
|
||||||
|
request_tracker::RequestTracker,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod auth;
|
mod auth;
|
||||||
@@ -34,7 +48,9 @@ async fn dispatch_loop(
|
|||||||
mut request_tracker: RequestTracker,
|
mut request_tracker: RequestTracker,
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
let Some(message) = bi.recv().await else { return };
|
let Some(message) = bi.recv().await else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let conn = match message {
|
let conn = match message {
|
||||||
Ok(conn) => conn,
|
Ok(conn) => conn,
|
||||||
@@ -53,16 +69,24 @@ async fn dispatch_loop(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Some(payload) = conn.payload else {
|
let Some(payload) = conn.payload else {
|
||||||
let _ = bi.send(Err(Status::invalid_argument("Missing client request payload"))).await;
|
let _ = bi
|
||||||
|
.send(Err(Status::invalid_argument(
|
||||||
|
"Missing client request payload",
|
||||||
|
)))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match dispatch_inner(&actor, payload).await {
|
match dispatch_inner(&actor, payload).await {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
if bi.send(Ok(ClientResponse {
|
if bi
|
||||||
request_id: Some(request_id),
|
.send(Ok(ClientResponse {
|
||||||
payload: Some(response),
|
request_id: Some(request_id),
|
||||||
})).await.is_err() {
|
payload: Some(response),
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,6 +116,47 @@ async fn dispatch_inner(
|
|||||||
};
|
};
|
||||||
Ok(ClientResponsePayload::VaultState(state.into()))
|
Ok(ClientResponsePayload::VaultState(state.into()))
|
||||||
}
|
}
|
||||||
|
ClientRequestPayload::EvmSignTransaction(request) => {
|
||||||
|
let address: Address = RawEvmAddress(request.wallet_address).try_convert()?;
|
||||||
|
let transaction = RawEvmTransaction(request.rlp_transaction).try_convert()?;
|
||||||
|
|
||||||
|
let response = match actor
|
||||||
|
.ask(HandleSignTransaction {
|
||||||
|
wallet_address: address,
|
||||||
|
transaction,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(signature) => EvmSignTransactionResponse {
|
||||||
|
result: Some(EvmSignTransactionResult::Signature(
|
||||||
|
signature.as_bytes().to_vec(),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
Err(kameo::error::SendError::HandlerError(SignTransactionRpcError::Vet(
|
||||||
|
vet_error,
|
||||||
|
))) => EvmSignTransactionResponse {
|
||||||
|
result: Some(vet_error.convert()),
|
||||||
|
},
|
||||||
|
|
||||||
|
Err(kameo::error::SendError::HandlerError(SignTransactionRpcError::Internal)) => {
|
||||||
|
EvmSignTransactionResponse {
|
||||||
|
result: Some(EvmSignTransactionResult::Error(
|
||||||
|
ProtoEvmError::Internal.into(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to sign EVM transaction");
|
||||||
|
EvmSignTransactionResponse {
|
||||||
|
result: Some(EvmSignTransactionResult::Error(
|
||||||
|
ProtoEvmError::Internal.into(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ClientResponsePayload::EvmSignTransaction(response))
|
||||||
|
}
|
||||||
payload => {
|
payload => {
|
||||||
warn!(?payload, "Unsupported post-auth client request");
|
warn!(?payload, "Unsupported post-auth client request");
|
||||||
Err(Status::invalid_argument("Unsupported client request"))
|
Err(Status::invalid_argument("Unsupported client request"))
|
||||||
@@ -102,14 +167,21 @@ async fn dispatch_inner(
|
|||||||
pub async fn start(mut conn: ClientConnection, mut bi: GrpcBi<ClientRequest, ClientResponse>) {
|
pub async fn start(mut conn: ClientConnection, mut bi: GrpcBi<ClientRequest, ClientResponse>) {
|
||||||
let mut request_tracker = RequestTracker::default();
|
let mut request_tracker = RequestTracker::default();
|
||||||
|
|
||||||
if let Err(e) = auth::start(&mut conn, &mut bi, &mut request_tracker).await {
|
let client_id = match auth::start(&mut conn, &mut bi, &mut request_tracker).await {
|
||||||
let mut transport = auth::AuthTransportAdapter::new(&mut bi, &mut request_tracker);
|
Ok(id) => id,
|
||||||
let _ = transport.send(Err(e.clone())).await;
|
Err(err) => {
|
||||||
warn!(error = ?e, "Client authentication failed");
|
let _ = bi
|
||||||
return;
|
.send(Err(Status::unauthenticated(format!(
|
||||||
|
"Authentication failed: {}",
|
||||||
|
err
|
||||||
|
))))
|
||||||
|
.await;
|
||||||
|
warn!(error = ?err, "Client authentication failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let actor = client::session::ClientSession::spawn(client::session::ClientSession::new(conn));
|
let actor = ClientSession::spawn(ClientSession::new(conn, client_id));
|
||||||
let actor_for_cleanup = actor.clone();
|
let actor_for_cleanup = actor.clone();
|
||||||
|
|
||||||
info!("Client authenticated successfully");
|
info!("Client authenticated successfully");
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
use arbiter_proto::{
|
use arbiter_proto::{
|
||||||
ClientMetadata, 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;
|
||||||
@@ -181,8 +183,7 @@ pub async fn start(
|
|||||||
conn: &mut ClientConnection,
|
conn: &mut ClientConnection,
|
||||||
bi: &mut GrpcBi<ClientRequest, ClientResponse>,
|
bi: &mut GrpcBi<ClientRequest, ClientResponse>,
|
||||||
request_tracker: &mut RequestTracker,
|
request_tracker: &mut RequestTracker,
|
||||||
) -> Result<(), auth::Error> {
|
) -> Result<i32, auth::Error> {
|
||||||
let mut transport = AuthTransportAdapter::new(bi, request_tracker);
|
let mut transport = AuthTransportAdapter::new(bi, request_tracker);
|
||||||
client::auth::authenticate(conn, &mut transport).await?;
|
client::auth::authenticate(conn, &mut transport).await
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
2
server/crates/arbiter-server/src/grpc/common.rs
Normal file
2
server/crates/arbiter-server/src/grpc/common.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod inbound;
|
||||||
|
pub mod outbound;
|
||||||
36
server/crates/arbiter-server/src/grpc/common/inbound.rs
Normal file
36
server/crates/arbiter-server/src/grpc/common/inbound.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use alloy::{consensus::TxEip1559, primitives::Address, rlp::Decodable as _};
|
||||||
|
|
||||||
|
use crate::grpc::TryConvert;
|
||||||
|
|
||||||
|
pub struct RawEvmAddress(pub Vec<u8>);
|
||||||
|
impl TryConvert for RawEvmAddress {
|
||||||
|
type Output = Address;
|
||||||
|
|
||||||
|
type Error = tonic::Status;
|
||||||
|
|
||||||
|
fn try_convert(self) -> Result<Self::Output, Self::Error> {
|
||||||
|
let wallet_address = match <[u8; 20]>::try_from(self.0.as_slice()) {
|
||||||
|
Ok(address) => Address::from(address),
|
||||||
|
Err(_) => {
|
||||||
|
return Err(tonic::Status::invalid_argument(
|
||||||
|
"Invalid EVM wallet address",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(wallet_address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RawEvmTransaction(pub Vec<u8>);
|
||||||
|
impl TryConvert for RawEvmTransaction {
|
||||||
|
type Output = TxEip1559;
|
||||||
|
|
||||||
|
type Error = tonic::Status;
|
||||||
|
|
||||||
|
fn try_convert(mut self) -> Result<Self::Output, Self::Error> {
|
||||||
|
let tx = TxEip1559::decode(&mut self.0.as_slice()).map_err(|_| {
|
||||||
|
tonic::Status::invalid_argument("Invalid EVM transaction format")
|
||||||
|
})?;
|
||||||
|
Ok(tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
114
server/crates/arbiter-server/src/grpc/common/outbound.rs
Normal file
114
server/crates/arbiter-server/src/grpc/common/outbound.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
use alloy::primitives::U256;
|
||||||
|
use arbiter_proto::proto::evm::{
|
||||||
|
EvalViolation as ProtoEvalViolation, EvmError as ProtoEvmError, GasLimitExceededViolation,
|
||||||
|
NoMatchingGrantError, PolicyViolationsError, SpecificMeaning as ProtoSpecificMeaning,
|
||||||
|
TokenInfo as ProtoTokenInfo, TransactionEvalError as ProtoTransactionEvalError,
|
||||||
|
eval_violation::Kind as ProtoEvalViolationKind,
|
||||||
|
evm_sign_transaction_response::Result as EvmSignTransactionResult,
|
||||||
|
specific_meaning::Meaning as ProtoSpecificMeaningKind,
|
||||||
|
transaction_eval_error::Kind as ProtoTransactionEvalErrorKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
evm::{
|
||||||
|
PolicyError, VetError,
|
||||||
|
policies::{EvalViolation, SpecificMeaning},
|
||||||
|
},
|
||||||
|
grpc::Convert,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn u256_to_proto_bytes(value: U256) -> Vec<u8> {
|
||||||
|
value.to_be_bytes::<32>().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert for SpecificMeaning {
|
||||||
|
type Output = ProtoSpecificMeaning;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Output {
|
||||||
|
let kind = match self {
|
||||||
|
SpecificMeaning::EtherTransfer(meaning) => ProtoSpecificMeaningKind::EtherTransfer(
|
||||||
|
arbiter_proto::proto::evm::EtherTransferMeaning {
|
||||||
|
to: meaning.to.to_vec(),
|
||||||
|
value: u256_to_proto_bytes(meaning.value),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SpecificMeaning::TokenTransfer(meaning) => ProtoSpecificMeaningKind::TokenTransfer(
|
||||||
|
arbiter_proto::proto::evm::TokenTransferMeaning {
|
||||||
|
token: Some(ProtoTokenInfo {
|
||||||
|
symbol: meaning.token.symbol.to_string(),
|
||||||
|
address: meaning.token.contract.to_vec(),
|
||||||
|
chain_id: meaning.token.chain,
|
||||||
|
}),
|
||||||
|
to: meaning.to.to_vec(),
|
||||||
|
value: u256_to_proto_bytes(meaning.value),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
ProtoSpecificMeaning {
|
||||||
|
meaning: Some(kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert for EvalViolation {
|
||||||
|
type Output = ProtoEvalViolation;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Output {
|
||||||
|
let kind = match self {
|
||||||
|
EvalViolation::InvalidTarget { target } => {
|
||||||
|
ProtoEvalViolationKind::InvalidTarget(target.to_vec())
|
||||||
|
}
|
||||||
|
EvalViolation::GasLimitExceeded {
|
||||||
|
max_gas_fee_per_gas,
|
||||||
|
max_priority_fee_per_gas,
|
||||||
|
} => ProtoEvalViolationKind::GasLimitExceeded(GasLimitExceededViolation {
|
||||||
|
max_gas_fee_per_gas: max_gas_fee_per_gas.map(u256_to_proto_bytes),
|
||||||
|
max_priority_fee_per_gas: max_priority_fee_per_gas.map(u256_to_proto_bytes),
|
||||||
|
}),
|
||||||
|
EvalViolation::RateLimitExceeded => ProtoEvalViolationKind::RateLimitExceeded(()),
|
||||||
|
EvalViolation::VolumetricLimitExceeded => {
|
||||||
|
ProtoEvalViolationKind::VolumetricLimitExceeded(())
|
||||||
|
}
|
||||||
|
EvalViolation::InvalidTime => ProtoEvalViolationKind::InvalidTime(()),
|
||||||
|
EvalViolation::InvalidTransactionType => {
|
||||||
|
ProtoEvalViolationKind::InvalidTransactionType(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ProtoEvalViolation { kind: Some(kind) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert for VetError {
|
||||||
|
type Output = EvmSignTransactionResult;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Output {
|
||||||
|
let kind = match self {
|
||||||
|
VetError::ContractCreationNotSupported => {
|
||||||
|
ProtoTransactionEvalErrorKind::ContractCreationNotSupported(())
|
||||||
|
}
|
||||||
|
VetError::UnsupportedTransactionType => {
|
||||||
|
ProtoTransactionEvalErrorKind::UnsupportedTransactionType(())
|
||||||
|
}
|
||||||
|
VetError::Evaluated(meaning, policy_error) => match policy_error {
|
||||||
|
PolicyError::NoMatchingGrant => {
|
||||||
|
ProtoTransactionEvalErrorKind::NoMatchingGrant(NoMatchingGrantError {
|
||||||
|
meaning: Some(meaning.convert()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
PolicyError::Violations(violations) => {
|
||||||
|
ProtoTransactionEvalErrorKind::PolicyViolations(PolicyViolationsError {
|
||||||
|
meaning: Some(meaning.convert()),
|
||||||
|
violations: violations.into_iter().map(Convert::convert).collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
PolicyError::Database(_) => {
|
||||||
|
return EvmSignTransactionResult::Error(ProtoEvmError::Internal.into());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
EvmSignTransactionResult::EvalError(ProtoTransactionEvalError { kind: Some(kind) }.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,10 +14,13 @@ use crate::{
|
|||||||
grpc::user_agent::start,
|
grpc::user_agent::start,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod client;
|
|
||||||
mod request_tracker;
|
mod request_tracker;
|
||||||
|
|
||||||
|
pub mod client;
|
||||||
pub mod user_agent;
|
pub mod user_agent;
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
pub trait Convert {
|
pub trait Convert {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ use arbiter_proto::{
|
|||||||
evm::{
|
evm::{
|
||||||
EvmError as ProtoEvmError, EvmGrantCreateRequest, EvmGrantCreateResponse,
|
EvmError as ProtoEvmError, EvmGrantCreateRequest, EvmGrantCreateResponse,
|
||||||
EvmGrantDeleteRequest, EvmGrantDeleteResponse, EvmGrantList, EvmGrantListResponse,
|
EvmGrantDeleteRequest, EvmGrantDeleteResponse, EvmGrantList, EvmGrantListResponse,
|
||||||
GrantEntry, WalletCreateResponse, WalletEntry, WalletList, WalletListResponse,
|
EvmSignTransactionResponse, GrantEntry, WalletCreateResponse, WalletEntry, WalletList,
|
||||||
evm_grant_create_response::Result as EvmGrantCreateResult,
|
WalletListResponse, evm_grant_create_response::Result as EvmGrantCreateResult,
|
||||||
evm_grant_delete_response::Result as EvmGrantDeleteResult,
|
evm_grant_delete_response::Result as EvmGrantDeleteResult,
|
||||||
evm_grant_list_response::Result as EvmGrantListResult,
|
evm_grant_list_response::Result as EvmGrantListResult,
|
||||||
|
evm_sign_transaction_response::Result as EvmSignTransactionResult,
|
||||||
wallet_create_response::Result as WalletCreateResult,
|
wallet_create_response::Result as WalletCreateResult,
|
||||||
wallet_list_response::Result as WalletListResult,
|
wallet_list_response::Result as WalletListResult,
|
||||||
},
|
},
|
||||||
@@ -22,8 +23,8 @@ use arbiter_proto::{
|
|||||||
SdkClientGrantWalletAccess, SdkClientList as ProtoSdkClientList,
|
SdkClientGrantWalletAccess, SdkClientList as ProtoSdkClientList,
|
||||||
SdkClientListResponse as ProtoSdkClientListResponse, SdkClientRevokeWalletAccess,
|
SdkClientListResponse as ProtoSdkClientListResponse, SdkClientRevokeWalletAccess,
|
||||||
SdkClientWalletAccess, UnsealEncryptedKey as ProtoUnsealEncryptedKey,
|
SdkClientWalletAccess, UnsealEncryptedKey as ProtoUnsealEncryptedKey,
|
||||||
UnsealResult as ProtoUnsealResult, UnsealStart, UserAgentRequest, UserAgentResponse,
|
UnsealResult as ProtoUnsealResult, UnsealStart, UserAgentEvmSignTransactionRequest,
|
||||||
VaultState as ProtoVaultState,
|
UserAgentRequest, UserAgentResponse, VaultState as ProtoVaultState,
|
||||||
sdk_client_list_response::Result as ProtoSdkClientListResult,
|
sdk_client_list_response::Result as ProtoSdkClientListResult,
|
||||||
user_agent_request::Payload as UserAgentRequestPayload,
|
user_agent_request::Payload as UserAgentRequestPayload,
|
||||||
user_agent_response::Payload as UserAgentResponsePayload,
|
user_agent_response::Payload as UserAgentResponsePayload,
|
||||||
@@ -49,12 +50,24 @@ use crate::{
|
|||||||
HandleEvmWalletList, HandleGrantCreate, HandleGrantDelete,
|
HandleEvmWalletList, HandleGrantCreate, HandleGrantDelete,
|
||||||
HandleGrantEvmWalletAccess, HandleGrantList, HandleListWalletAccess,
|
HandleGrantEvmWalletAccess, HandleGrantList, HandleListWalletAccess,
|
||||||
HandleNewClientApprove, HandleQueryVaultState, HandleRevokeEvmWalletAccess,
|
HandleNewClientApprove, HandleQueryVaultState, HandleRevokeEvmWalletAccess,
|
||||||
HandleSdkClientList, HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError,
|
HandleSdkClientList, HandleSignTransaction, HandleUnsealEncryptedKey,
|
||||||
|
HandleUnsealRequest, SignTransactionError as SessionSignTransactionError,
|
||||||
|
UnsealError,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db::models::{CoreEvmWalletAccess, NewEvmWalletAccess},
|
db::models::{CoreEvmWalletAccess, NewEvmWalletAccess},
|
||||||
grpc::{Convert, TryConvert, request_tracker::RequestTracker},
|
evm::{PolicyError, VetError, policies::EvalViolation},
|
||||||
|
grpc::{
|
||||||
|
Convert, TryConvert,
|
||||||
|
common::inbound::{RawEvmAddress, RawEvmTransaction},
|
||||||
|
request_tracker::RequestTracker,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use alloy::{
|
||||||
|
consensus::TxEip1559,
|
||||||
|
primitives::{Address, U256},
|
||||||
|
rlp::Decodable,
|
||||||
};
|
};
|
||||||
mod auth;
|
mod auth;
|
||||||
mod inbound;
|
mod inbound;
|
||||||
@@ -178,7 +191,6 @@ async fn dispatch_inner(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::UnsealEncryptedKey(ProtoUnsealEncryptedKey {
|
UserAgentRequestPayload::UnsealEncryptedKey(ProtoUnsealEncryptedKey {
|
||||||
nonce,
|
nonce,
|
||||||
ciphertext,
|
ciphertext,
|
||||||
@@ -203,7 +215,6 @@ async fn dispatch_inner(
|
|||||||
};
|
};
|
||||||
UserAgentResponsePayload::UnsealResult(result.into())
|
UserAgentResponsePayload::UnsealResult(result.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::BootstrapEncryptedKey(ProtoBootstrapEncryptedKey {
|
UserAgentRequestPayload::BootstrapEncryptedKey(ProtoBootstrapEncryptedKey {
|
||||||
nonce,
|
nonce,
|
||||||
ciphertext,
|
ciphertext,
|
||||||
@@ -231,7 +242,6 @@ async fn dispatch_inner(
|
|||||||
};
|
};
|
||||||
UserAgentResponsePayload::BootstrapResult(result.into())
|
UserAgentResponsePayload::BootstrapResult(result.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::QueryVaultState(_) => {
|
UserAgentRequestPayload::QueryVaultState(_) => {
|
||||||
let state = match actor.ask(HandleQueryVaultState {}).await {
|
let state = match actor.ask(HandleQueryVaultState {}).await {
|
||||||
Ok(KeyHolderState::Unbootstrapped) => ProtoVaultState::Unbootstrapped,
|
Ok(KeyHolderState::Unbootstrapped) => ProtoVaultState::Unbootstrapped,
|
||||||
@@ -244,7 +254,6 @@ async fn dispatch_inner(
|
|||||||
};
|
};
|
||||||
UserAgentResponsePayload::VaultState(state.into())
|
UserAgentResponsePayload::VaultState(state.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmWalletCreate(_) => {
|
UserAgentRequestPayload::EvmWalletCreate(_) => {
|
||||||
let result = match actor.ask(HandleEvmWalletCreate {}).await {
|
let result = match actor.ask(HandleEvmWalletCreate {}).await {
|
||||||
Ok((wallet_id, address)) => WalletCreateResult::Wallet(WalletEntry {
|
Ok((wallet_id, address)) => WalletCreateResult::Wallet(WalletEntry {
|
||||||
@@ -260,7 +269,6 @@ async fn dispatch_inner(
|
|||||||
result: Some(result),
|
result: Some(result),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmWalletList(_) => {
|
UserAgentRequestPayload::EvmWalletList(_) => {
|
||||||
let result = match actor.ask(HandleEvmWalletList {}).await {
|
let result = match actor.ask(HandleEvmWalletList {}).await {
|
||||||
Ok(wallets) => WalletListResult::Wallets(WalletList {
|
Ok(wallets) => WalletListResult::Wallets(WalletList {
|
||||||
@@ -281,7 +289,6 @@ async fn dispatch_inner(
|
|||||||
result: Some(result),
|
result: Some(result),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmGrantList(_) => {
|
UserAgentRequestPayload::EvmGrantList(_) => {
|
||||||
let result = match actor.ask(HandleGrantList {}).await {
|
let result = match actor.ask(HandleGrantList {}).await {
|
||||||
Ok(grants) => EvmGrantListResult::Grants(EvmGrantList {
|
Ok(grants) => EvmGrantListResult::Grants(EvmGrantList {
|
||||||
@@ -304,7 +311,6 @@ async fn dispatch_inner(
|
|||||||
result: Some(result),
|
result: Some(result),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmGrantCreate(EvmGrantCreateRequest { shared, specific }) => {
|
UserAgentRequestPayload::EvmGrantCreate(EvmGrantCreateRequest { shared, specific }) => {
|
||||||
let basic = shared
|
let basic = shared
|
||||||
.ok_or_else(|| Status::invalid_argument("Missing shared grant settings"))?
|
.ok_or_else(|| Status::invalid_argument("Missing shared grant settings"))?
|
||||||
@@ -324,7 +330,6 @@ async fn dispatch_inner(
|
|||||||
result: Some(result),
|
result: Some(result),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmGrantDelete(EvmGrantDeleteRequest { grant_id }) => {
|
UserAgentRequestPayload::EvmGrantDelete(EvmGrantDeleteRequest { grant_id }) => {
|
||||||
let result = match actor.ask(HandleGrantDelete { grant_id }).await {
|
let result = match actor.ask(HandleGrantDelete { grant_id }).await {
|
||||||
Ok(()) => EvmGrantDeleteResult::Ok(()),
|
Ok(()) => EvmGrantDeleteResult::Ok(()),
|
||||||
@@ -337,7 +342,6 @@ async fn dispatch_inner(
|
|||||||
result: Some(result),
|
result: Some(result),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::SdkClientConnectionResponse(resp) => {
|
UserAgentRequestPayload::SdkClientConnectionResponse(resp) => {
|
||||||
let pubkey_bytes = <[u8; 32]>::try_from(resp.pubkey)
|
let pubkey_bytes = <[u8; 32]>::try_from(resp.pubkey)
|
||||||
.map_err(|_| Status::invalid_argument("Invalid Ed25519 public key length"))?;
|
.map_err(|_| Status::invalid_argument("Invalid Ed25519 public key length"))?;
|
||||||
@@ -357,9 +361,7 @@ async fn dispatch_inner(
|
|||||||
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::SdkClientRevoke(_) => todo!(),
|
UserAgentRequestPayload::SdkClientRevoke(_) => todo!(),
|
||||||
|
|
||||||
UserAgentRequestPayload::SdkClientList(_) => {
|
UserAgentRequestPayload::SdkClientList(_) => {
|
||||||
let result = match actor.ask(HandleSdkClientList {}).await {
|
let result = match actor.ask(HandleSdkClientList {}).await {
|
||||||
Ok(clients) => ProtoSdkClientListResult::Clients(ProtoSdkClientList {
|
Ok(clients) => ProtoSdkClientListResult::Clients(ProtoSdkClientList {
|
||||||
@@ -386,7 +388,6 @@ async fn dispatch_inner(
|
|||||||
result: Some(result),
|
result: Some(result),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::GrantWalletAccess(SdkClientGrantWalletAccess { accesses }) => {
|
UserAgentRequestPayload::GrantWalletAccess(SdkClientGrantWalletAccess { accesses }) => {
|
||||||
let entries: Vec<NewEvmWalletAccess> =
|
let entries: Vec<NewEvmWalletAccess> =
|
||||||
accesses.into_iter().map(|a| a.convert()).collect();
|
accesses.into_iter().map(|a| a.convert()).collect();
|
||||||
@@ -402,9 +403,11 @@ async fn dispatch_inner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::RevokeWalletAccess(SdkClientRevokeWalletAccess { accesses }) => {
|
UserAgentRequestPayload::RevokeWalletAccess(SdkClientRevokeWalletAccess { accesses }) => {
|
||||||
match actor.ask(HandleRevokeEvmWalletAccess { entries: accesses }).await {
|
match actor
|
||||||
|
.ask(HandleRevokeEvmWalletAccess { entries: accesses })
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
info!("Successfully revoked wallet access");
|
info!("Successfully revoked wallet access");
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@@ -415,7 +418,6 @@ async fn dispatch_inner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::ListWalletAccess(_) => {
|
UserAgentRequestPayload::ListWalletAccess(_) => {
|
||||||
let result = match actor.ask(HandleListWalletAccess {}).await {
|
let result = match actor.ask(HandleListWalletAccess {}).await {
|
||||||
Ok(accesses) => ListWalletAccessResponse {
|
Ok(accesses) => ListWalletAccessResponse {
|
||||||
@@ -428,12 +430,59 @@ async fn dispatch_inner(
|
|||||||
};
|
};
|
||||||
UserAgentResponsePayload::ListWalletAccessResponse(result)
|
UserAgentResponsePayload::ListWalletAccessResponse(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::AuthChallengeRequest(..)
|
UserAgentRequestPayload::AuthChallengeRequest(..)
|
||||||
| UserAgentRequestPayload::AuthChallengeSolution(..) => {
|
| UserAgentRequestPayload::AuthChallengeSolution(..) => {
|
||||||
warn!(?payload, "Unsupported post-auth user agent request");
|
warn!(?payload, "Unsupported post-auth user agent request");
|
||||||
return Err(Status::invalid_argument("Unsupported user-agent request"));
|
return Err(Status::invalid_argument("Unsupported user-agent request"));
|
||||||
}
|
}
|
||||||
|
UserAgentRequestPayload::EvmSignTransaction(UserAgentEvmSignTransactionRequest {
|
||||||
|
client_id,
|
||||||
|
request,
|
||||||
|
}) => {
|
||||||
|
let Some(request) = request else {
|
||||||
|
warn!("Missing transaction signing request");
|
||||||
|
return Err(Status::invalid_argument(
|
||||||
|
"Missing transaction signing request",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let address: Address = RawEvmAddress(request.wallet_address).try_convert()?;
|
||||||
|
let transaction = RawEvmTransaction(request.rlp_transaction).try_convert()?;
|
||||||
|
|
||||||
|
let response = match actor
|
||||||
|
.ask(HandleSignTransaction {
|
||||||
|
client_id,
|
||||||
|
wallet_address: address,
|
||||||
|
transaction,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(signature) => EvmSignTransactionResponse {
|
||||||
|
result: Some(EvmSignTransactionResult::Signature(
|
||||||
|
signature.as_bytes().to_vec(),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
Err(SendError::HandlerError(SessionSignTransactionError::Vet(vet_error))) => {
|
||||||
|
EvmSignTransactionResponse { result: Some(vet_error.convert()) }
|
||||||
|
}
|
||||||
|
Err(SendError::HandlerError(SessionSignTransactionError::Internal)) => {
|
||||||
|
EvmSignTransactionResponse {
|
||||||
|
result: Some(EvmSignTransactionResult::Error(
|
||||||
|
ProtoEvmError::Internal.into(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to sign EVM transaction via user-agent");
|
||||||
|
EvmSignTransactionResponse {
|
||||||
|
result: Some(EvmSignTransactionResult::Error(
|
||||||
|
ProtoEvmError::Internal.into(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
UserAgentResponsePayload::EvmSignTransaction(response)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(response))
|
Ok(Some(response))
|
||||||
|
|||||||
Reference in New Issue
Block a user