feat(useragent): initial impl

This commit is contained in:
hdbg
2026-03-04 15:27:27 +01:00
parent ccd657c9ec
commit 62c4bc5ade
78 changed files with 10635 additions and 223 deletions

View File

@@ -168,6 +168,9 @@ impl UserAgentSession {
UserAgentRequestPayload::UnsealEncryptedKey(unseal_encrypted_key) => {
self.handle_unseal_encrypted_key(unseal_encrypted_key).await
}
UserAgentRequestPayload::QueryVaultState(_) => {
self.handle_query_vault_state().await
}
UserAgentRequestPayload::EvmWalletCreate(_) => self.handle_evm_wallet_create().await,
UserAgentRequestPayload::EvmWalletList(_) => self.handle_evm_wallet_list().await,
_ => Err(TransportResponseError::UnexpectedRequestPayload),
@@ -290,6 +293,27 @@ impl UserAgentSession {
}
}
impl UserAgentSession {
async fn handle_query_vault_state(&mut self) -> Output {
use arbiter_proto::proto::user_agent::VaultState;
use crate::actors::keyholder::{GetState, StateDiscriminants};
let vault_state = match self.props.actors.key_holder.ask(GetState {}).await {
Ok(StateDiscriminants::Unbootstrapped) => VaultState::Unbootstrapped,
Ok(StateDiscriminants::Sealed) => VaultState::Sealed,
Ok(StateDiscriminants::Unsealed) => VaultState::Unsealed,
Err(err) => {
error!(?err, actor = "useragent", "keyholder.query.failed");
VaultState::Error
}
};
Ok(response(UserAgentResponsePayload::VaultState(
vault_state.into(),
)))
}
}
impl UserAgentSession {
async fn handle_evm_wallet_create(&mut self) -> Output {
use evm_proto::wallet_create_response::Result as CreateResult;

View File

@@ -254,4 +254,126 @@ where
}
mod grpc;
pub use grpc::{ConnectError, connect_grpc};
pub use grpc::{connect_grpc, ConnectError, UserAgentGrpc};
use arbiter_proto::proto::user_agent::{
UnsealEncryptedKey, UnsealStart,
user_agent_request::Payload as RequestPayload,
user_agent_response::Payload as ResponsePayload,
};
/// Send an `UnsealStart` request and await the server's `UnsealStartResponse`.
pub struct SendUnsealStart {
pub client_pubkey: Vec<u8>,
}
/// Send an `UnsealEncryptedKey` request and await the server's `UnsealResult`.
pub struct SendUnsealEncryptedKey {
pub nonce: Vec<u8>,
pub ciphertext: Vec<u8>,
pub associated_data: Vec<u8>,
}
/// Query the server for the current `VaultState`.
pub struct QueryVaultState;
/// Errors that can occur during post-authentication session operations.
#[derive(Debug, thiserror::Error)]
pub enum SessionError {
#[error("Transport send failed")]
TransportSendFailed,
#[error("Transport closed unexpectedly")]
TransportClosed,
#[error("Server sent an unexpected response payload")]
UnexpectedResponse,
}
impl<Transport> kameo::message::Message<SendUnsealStart> for UserAgentActor<Transport>
where
Transport: Bi<UserAgentResponse, UserAgentRequest>,
{
type Reply = Result<arbiter_proto::proto::user_agent::UnsealStartResponse, SessionError>;
async fn handle(
&mut self,
msg: SendUnsealStart,
_ctx: &mut kameo::message::Context<Self, Self::Reply>,
) -> Self::Reply {
self.transport
.send(UserAgentRequest {
payload: Some(RequestPayload::UnsealStart(UnsealStart {
client_pubkey: msg.client_pubkey,
})),
})
.await
.map_err(|_| SessionError::TransportSendFailed)?;
match self.transport.recv().await {
Some(resp) => match resp.payload {
Some(ResponsePayload::UnsealStartResponse(r)) => Ok(r),
_ => Err(SessionError::UnexpectedResponse),
},
None => Err(SessionError::TransportClosed),
}
}
}
impl<Transport> kameo::message::Message<SendUnsealEncryptedKey> for UserAgentActor<Transport>
where
Transport: Bi<UserAgentResponse, UserAgentRequest>,
{
type Reply = Result<i32, SessionError>;
async fn handle(
&mut self,
msg: SendUnsealEncryptedKey,
_ctx: &mut kameo::message::Context<Self, Self::Reply>,
) -> Self::Reply {
self.transport
.send(UserAgentRequest {
payload: Some(RequestPayload::UnsealEncryptedKey(UnsealEncryptedKey {
nonce: msg.nonce,
ciphertext: msg.ciphertext,
associated_data: msg.associated_data,
})),
})
.await
.map_err(|_| SessionError::TransportSendFailed)?;
match self.transport.recv().await {
Some(resp) => match resp.payload {
Some(ResponsePayload::UnsealResult(r)) => Ok(r),
_ => Err(SessionError::UnexpectedResponse),
},
None => Err(SessionError::TransportClosed),
}
}
}
impl<Transport> kameo::message::Message<QueryVaultState> for UserAgentActor<Transport>
where
Transport: Bi<UserAgentResponse, UserAgentRequest>,
{
type Reply = Result<i32, SessionError>;
async fn handle(
&mut self,
_msg: QueryVaultState,
_ctx: &mut kameo::message::Context<Self, Self::Reply>,
) -> Self::Reply {
self.transport
.send(UserAgentRequest {
payload: Some(RequestPayload::QueryVaultState(())),
})
.await
.map_err(|_| SessionError::TransportSendFailed)?;
match self.transport.recv().await {
Some(resp) => match resp.payload {
Some(ResponsePayload::VaultState(v)) => Ok(v),
_ => Err(SessionError::UnexpectedResponse),
},
None => Err(SessionError::TransportClosed),
}
}
}