feat(evm): add wallet access grant/revoke functionality
Some checks failed
Some checks failed
This commit is contained in:
@@ -130,7 +130,7 @@ impl EvmActor {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub async fn list_wallets(&self) -> Result<Vec<Address>, Error> {
|
||||
pub async fn list_wallets(&self) -> Result<Vec<(i32, Address)>, Error> {
|
||||
let mut conn = self.db.get().await?;
|
||||
let rows: Vec<models::EvmWallet> = schema::evm_wallet::table
|
||||
.select(models::EvmWallet::as_select())
|
||||
@@ -139,7 +139,7 @@ impl EvmActor {
|
||||
|
||||
Ok(rows
|
||||
.into_iter()
|
||||
.map(|w| Address::from_slice(&w.address))
|
||||
.map(|w| (w.id, Address::from_slice(&w.address)))
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,11 @@ use crate::{
|
||||
db::{self, models::KeyType},
|
||||
};
|
||||
|
||||
pub struct EvmAccessEntry {
|
||||
pub wallet_id: i32,
|
||||
pub sdk_client_id: i32,
|
||||
}
|
||||
|
||||
/// Abstraction over Ed25519 / ECDSA-secp256k1 / RSA public keys used during the auth handshake.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AuthPublicKey {
|
||||
|
||||
@@ -58,13 +58,7 @@ pub struct UserAgentSession {
|
||||
pending_client_approvals: HashMap<VerifyingKey, PendingClientApproval>,
|
||||
}
|
||||
|
||||
mod connection;
|
||||
pub(crate) use connection::{
|
||||
BootstrapError, HandleBootstrapEncryptedKey, HandleEvmWalletCreate, HandleEvmWalletList,
|
||||
HandleGrantCreate, HandleGrantDelete, HandleGrantList, HandleNewClientApprove,
|
||||
HandleQueryVaultState, HandleSdkClientList,
|
||||
};
|
||||
pub use connection::{HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError};
|
||||
pub mod connection;
|
||||
|
||||
impl UserAgentSession {
|
||||
pub(crate) fn new(props: UserAgentConnection, sender: Box<dyn Sender<OutOfBand>>) -> Self {
|
||||
|
||||
@@ -2,8 +2,9 @@ use std::sync::Mutex;
|
||||
|
||||
use alloy::primitives::Address;
|
||||
use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit};
|
||||
use diesel::{QueryDsl as _, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use diesel::sql_types::ops::Add;
|
||||
use diesel::{BoolExpressionMethods as _, ExpressionMethods as _, QueryDsl as _, SelectableHelper};
|
||||
use diesel_async::{AsyncConnection, RunQueryDsl};
|
||||
use kameo::error::SendError;
|
||||
use kameo::prelude::Context;
|
||||
use kameo::{message, messages};
|
||||
@@ -12,8 +13,10 @@ use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||
|
||||
use crate::actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer;
|
||||
use crate::actors::keyholder::KeyHolderState;
|
||||
use crate::actors::user_agent::EvmAccessEntry;
|
||||
use crate::actors::user_agent::session::Error;
|
||||
use crate::db::models::{ProgramClient, ProgramClientMetadata};
|
||||
use crate::db::schema::evm_wallet_access;
|
||||
use crate::evm::policies::{Grant, SpecificGrant};
|
||||
use crate::safe_cell::SafeCell;
|
||||
use crate::{
|
||||
@@ -290,7 +293,7 @@ impl UserAgentSession {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub(crate) async fn handle_evm_wallet_list(&mut self) -> Result<Vec<Address>, Error> {
|
||||
pub(crate) async fn handle_evm_wallet_list(&mut self) -> Result<Vec<(i32, Address)>, Error> {
|
||||
match self.props.actors.evm.ask(ListWallets {}).await {
|
||||
Ok(wallets) => Ok(wallets),
|
||||
Err(err) => {
|
||||
@@ -301,6 +304,8 @@ impl UserAgentSession {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[messages]
|
||||
impl UserAgentSession {
|
||||
#[message]
|
||||
@@ -351,6 +356,79 @@ impl UserAgentSession {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub(crate) async fn handle_grant_evm_wallet_access(
|
||||
&mut self,
|
||||
entries: Vec<EvmAccessEntry>,
|
||||
) -> Result<(), Error> {
|
||||
let mut conn = self.props.db.get().await?;
|
||||
conn.transaction(|conn| {
|
||||
Box::pin(async move {
|
||||
use crate::db::models::NewEvmWalletAccess;
|
||||
use crate::db::schema::evm_wallet_access;
|
||||
|
||||
for entry in entries {
|
||||
diesel::insert_into(evm_wallet_access::table)
|
||||
.values(&NewEvmWalletAccess {
|
||||
wallet_id: entry.wallet_id,
|
||||
client_id: entry.sdk_client_id,
|
||||
})
|
||||
.on_conflict_do_nothing()
|
||||
.execute(conn)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Result::<_, Error>::Ok(())
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub(crate) async fn handle_revoke_evm_wallet_access(
|
||||
&mut self,
|
||||
entries: Vec<EvmAccessEntry>,
|
||||
) -> Result<(), Error> {
|
||||
let mut conn = self.props.db.get().await?;
|
||||
conn.transaction(|conn| {
|
||||
Box::pin(async move {
|
||||
use crate::db::schema::evm_wallet_access;
|
||||
for entry in entries {
|
||||
diesel::delete(evm_wallet_access::table)
|
||||
.filter(
|
||||
evm_wallet_access::wallet_id
|
||||
.eq(entry.wallet_id)
|
||||
.and(evm_wallet_access::client_id.eq(entry.sdk_client_id)),
|
||||
)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Result::<_, Error>::Ok(())
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub(crate) async fn handle_list_wallet_access(&mut self) -> Result<Vec<EvmAccessEntry>, Error> {
|
||||
let mut conn = self.props.db.get().await?;
|
||||
use crate::db::schema::evm_wallet_access;
|
||||
let access_entries = evm_wallet_access::table
|
||||
.select((evm_wallet_access::wallet_id, evm_wallet_access::client_id))
|
||||
.load::<(i32, i32)>(&mut conn)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|(wallet_id, sdk_client_id)| EvmAccessEntry {
|
||||
wallet_id,
|
||||
sdk_client_id,
|
||||
})
|
||||
.collect();
|
||||
Ok(access_entries)
|
||||
}
|
||||
}
|
||||
|
||||
#[messages]
|
||||
@@ -391,15 +469,18 @@ impl UserAgentSession {
|
||||
pub(crate) async fn handle_sdk_client_list(
|
||||
&mut self,
|
||||
) -> Result<Vec<(ProgramClient, ProgramClientMetadata)>, Error> {
|
||||
use crate::db::schema::{program_client, client_metadata};
|
||||
use crate::db::schema::{client_metadata, program_client};
|
||||
let mut conn = self.props.db.get().await?;
|
||||
|
||||
let clients = program_client::table
|
||||
.inner_join(client_metadata::table)
|
||||
.select((ProgramClient::as_select(), ProgramClientMetadata::as_select()))
|
||||
.select((
|
||||
ProgramClient::as_select(),
|
||||
ProgramClientMetadata::as_select(),
|
||||
))
|
||||
.load::<(ProgramClient, ProgramClientMetadata)>(&mut conn)
|
||||
.await?;
|
||||
|
||||
Ok(clients)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +187,12 @@ pub struct EvmWallet {
|
||||
|
||||
#[derive(Models, Queryable, Debug, Insertable, Selectable, Clone)]
|
||||
#[diesel(table_name = schema::evm_wallet_access, check_for_backend(Sqlite))]
|
||||
#[view(
|
||||
NewEvmWalletAccess,
|
||||
derive(Insertable),
|
||||
omit(id, created_at),
|
||||
attributes_with = "deriveless"
|
||||
)]
|
||||
pub struct EvmWalletAccess {
|
||||
pub id: i32,
|
||||
pub wallet_id: i32,
|
||||
|
||||
@@ -15,14 +15,15 @@ use arbiter_proto::{
|
||||
},
|
||||
user_agent::{
|
||||
BootstrapEncryptedKey as ProtoBootstrapEncryptedKey,
|
||||
BootstrapResult as ProtoBootstrapResult,
|
||||
BootstrapResult as ProtoBootstrapResult, ListWalletAccessResponse,
|
||||
SdkClientConnectionCancel as ProtoSdkClientConnectionCancel,
|
||||
SdkClientConnectionRequest as ProtoSdkClientConnectionRequest,
|
||||
SdkClientEntry as ProtoSdkClientEntry, SdkClientError as ProtoSdkClientError,
|
||||
SdkClientList as ProtoSdkClientList,
|
||||
SdkClientListResponse as ProtoSdkClientListResponse,
|
||||
UnsealEncryptedKey as ProtoUnsealEncryptedKey, UnsealResult as ProtoUnsealResult,
|
||||
UnsealStart, UserAgentRequest, UserAgentResponse, VaultState as ProtoVaultState,
|
||||
SdkClientGrantWalletAccess, SdkClientList as ProtoSdkClientList,
|
||||
SdkClientListResponse as ProtoSdkClientListResponse, SdkClientRevokeWalletAccess,
|
||||
SdkClientWalletAccess, UnsealEncryptedKey as ProtoUnsealEncryptedKey,
|
||||
UnsealResult as ProtoUnsealResult, UnsealStart, UserAgentRequest, UserAgentResponse,
|
||||
VaultState as ProtoVaultState,
|
||||
sdk_client_list_response::Result as ProtoSdkClientListResult,
|
||||
user_agent_request::Payload as UserAgentRequestPayload,
|
||||
user_agent_response::Payload as UserAgentResponsePayload,
|
||||
@@ -43,11 +44,8 @@ use crate::{
|
||||
keyholder::KeyHolderState,
|
||||
user_agent::{
|
||||
OutOfBand, UserAgentConnection, UserAgentSession,
|
||||
session::{
|
||||
BootstrapError, HandleBootstrapEncryptedKey, HandleEvmWalletCreate,
|
||||
HandleEvmWalletList, HandleGrantCreate, HandleGrantDelete, HandleGrantList,
|
||||
HandleNewClientApprove, HandleQueryVaultState, HandleSdkClientList,
|
||||
HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError,
|
||||
session::connection::{
|
||||
BootstrapError, HandleBootstrapEncryptedKey, HandleEvmWalletCreate, HandleEvmWalletList, HandleGrantCreate, HandleGrantDelete, HandleGrantEvmWalletAccess, HandleGrantList, HandleListWalletAccess, HandleNewClientApprove, HandleQueryVaultState, HandleRevokeEvmWalletAccess, HandleSdkClientList, HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -263,9 +261,9 @@ async fn dispatch_inner(
|
||||
Ok(wallets) => WalletListResult::Wallets(WalletList {
|
||||
wallets: wallets
|
||||
.into_iter()
|
||||
.map(|w| WalletEntry {
|
||||
address: w.to_vec(),
|
||||
id: todo!(),
|
||||
.map(|(id, address)| WalletEntry {
|
||||
address: address.to_vec(),
|
||||
id,
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
@@ -384,8 +382,48 @@ async fn dispatch_inner(
|
||||
})
|
||||
}
|
||||
|
||||
UserAgentRequestPayload::GrantWalletAccessList(_)
|
||||
| UserAgentRequestPayload::RevokeWalletAccessList(_) => todo!(),
|
||||
UserAgentRequestPayload::GrantWalletAccess(SdkClientGrantWalletAccess { accesses }) => {
|
||||
let entries = accesses.try_convert()?;
|
||||
|
||||
match actor.ask(HandleGrantEvmWalletAccess { entries }).await {
|
||||
Ok(()) => {
|
||||
info!("Successfully granted wallet access");
|
||||
return Ok(None);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(error = ?err, "Failed to grant wallet access");
|
||||
return Err(Status::internal("Failed to grant wallet access"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UserAgentRequestPayload::RevokeWalletAccess(SdkClientRevokeWalletAccess { accesses }) => {
|
||||
let entries = accesses.try_convert()?;
|
||||
|
||||
match actor.ask(HandleRevokeEvmWalletAccess { entries }).await {
|
||||
Ok(()) => {
|
||||
info!("Successfully revoked wallet access");
|
||||
return Ok(None);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(error = ?err, "Failed to revoke wallet access");
|
||||
return Err(Status::internal("Failed to revoke wallet access"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UserAgentRequestPayload::ListWalletAccess(_) => {
|
||||
let result = match actor.ask(HandleListWalletAccess {}).await {
|
||||
Ok(accesses) => ListWalletAccessResponse {
|
||||
accesses: accesses.into_iter().map(|a| a.convert()).collect(),
|
||||
},
|
||||
Err(err) => {
|
||||
warn!(error = ?err, "Failed to list wallet access");
|
||||
return Err(Status::internal("Failed to list wallet access"));
|
||||
}
|
||||
};
|
||||
UserAgentResponsePayload::ListWalletAccessResponse(result)
|
||||
}
|
||||
|
||||
UserAgentRequestPayload::AuthChallengeRequest(..)
|
||||
| UserAgentRequestPayload::AuthChallengeSolution(..) => {
|
||||
|
||||
@@ -7,11 +7,13 @@ use arbiter_proto::proto::evm::{
|
||||
VolumeRateLimit as ProtoVolumeRateLimit,
|
||||
specific_grant::Grant as ProtoSpecificGrantType,
|
||||
};
|
||||
use arbiter_proto::proto::user_agent::SdkClientWalletAccess;
|
||||
use alloy::primitives::{Address, U256};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use prost_types::Timestamp as ProtoTimestamp;
|
||||
use tonic::Status;
|
||||
|
||||
use crate::actors::user_agent::EvmAccessEntry;
|
||||
use crate::{
|
||||
evm::policies::{
|
||||
SharedGrantSettings, SpecificGrant, TransactionRateLimit, VolumeRateLimit,
|
||||
@@ -133,3 +135,18 @@ impl TryConvert for ProtoSpecificGrant {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryConvert for Vec<SdkClientWalletAccess> {
|
||||
type Output = Vec<EvmAccessEntry>;
|
||||
type Error = Status;
|
||||
|
||||
fn try_convert(self) -> Result<Vec<EvmAccessEntry>, Status> {
|
||||
Ok(self
|
||||
.into_iter()
|
||||
.map(|SdkClientWalletAccess { client_id, wallet_id }| EvmAccessEntry {
|
||||
wallet_id,
|
||||
sdk_client_id: client_id,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
use arbiter_proto::proto::evm::{
|
||||
EtherTransferSettings as ProtoEtherTransferSettings,
|
||||
SharedSettings as ProtoSharedSettings,
|
||||
SpecificGrant as ProtoSpecificGrant,
|
||||
TokenTransferSettings as ProtoTokenTransferSettings,
|
||||
TransactionRateLimit as ProtoTransactionRateLimit,
|
||||
VolumeRateLimit as ProtoVolumeRateLimit,
|
||||
specific_grant::Grant as ProtoSpecificGrantType,
|
||||
use arbiter_proto::proto::{
|
||||
evm::{
|
||||
EtherTransferSettings as ProtoEtherTransferSettings, SharedSettings as ProtoSharedSettings,
|
||||
SpecificGrant as ProtoSpecificGrant, TokenTransferSettings as ProtoTokenTransferSettings,
|
||||
TransactionRateLimit as ProtoTransactionRateLimit, VolumeRateLimit as ProtoVolumeRateLimit,
|
||||
specific_grant::Grant as ProtoSpecificGrantType,
|
||||
},
|
||||
user_agent::SdkClientWalletAccess as ProtoSdkClientWalletAccess,
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use prost_types::Timestamp as ProtoTimestamp;
|
||||
|
||||
use crate::{
|
||||
actors::user_agent::EvmAccessEntry,
|
||||
evm::policies::{SharedGrantSettings, SpecificGrant, TransactionRateLimit, VolumeRateLimit},
|
||||
grpc::Convert,
|
||||
};
|
||||
@@ -83,10 +84,25 @@ impl Convert for SpecificGrant {
|
||||
ProtoSpecificGrantType::TokenTransfer(ProtoTokenTransferSettings {
|
||||
token_contract: s.token_contract.to_vec(),
|
||||
target: s.target.map(|a| a.to_vec()),
|
||||
volume_limits: s.volume_limits.into_iter().map(VolumeRateLimit::convert).collect(),
|
||||
volume_limits: s
|
||||
.volume_limits
|
||||
.into_iter()
|
||||
.map(VolumeRateLimit::convert)
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
};
|
||||
ProtoSpecificGrant { grant: Some(grant) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Convert for EvmAccessEntry {
|
||||
type Output = ProtoSdkClientWalletAccess;
|
||||
|
||||
fn convert(self) -> Self::Output {
|
||||
ProtoSdkClientWalletAccess {
|
||||
client_id: self.sdk_client_id,
|
||||
wallet_id: self.wallet_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ use arbiter_server::{
|
||||
actors::{
|
||||
GlobalActors,
|
||||
keyholder::{Bootstrap, Seal},
|
||||
user_agent::session::{
|
||||
HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError, UserAgentSession,
|
||||
},
|
||||
user_agent::{UserAgentSession, session::connection::{
|
||||
HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError,
|
||||
}},
|
||||
},
|
||||
db,
|
||||
safe_cell::{SafeCell, SafeCellHandle as _},
|
||||
|
||||
Reference in New Issue
Block a user