merge: main
This commit is contained in:
@@ -6,13 +6,16 @@ use alloy::{
|
||||
primitives::{TxKind, U256},
|
||||
};
|
||||
use chrono::Utc;
|
||||
use diesel::{ExpressionMethods as _, QueryDsl, QueryResult, insert_into, sqlite::Sqlite};
|
||||
use diesel::{ExpressionMethods as _, QueryDsl as _, QueryResult, insert_into, sqlite::Sqlite};
|
||||
use diesel_async::{AsyncConnection, RunQueryDsl};
|
||||
use tracing_subscriber::registry::Data;
|
||||
|
||||
use crate::{
|
||||
db::{
|
||||
self,
|
||||
models::{EvmBasicGrant, NewEvmBasicGrant, NewEvmTransactionLog, SqliteTimestamp},
|
||||
self, DatabaseError,
|
||||
models::{
|
||||
EvmBasicGrant, EvmWalletAccess, NewEvmBasicGrant, NewEvmTransactionLog, SqliteTimestamp,
|
||||
},
|
||||
schema::{self, evm_transaction_log},
|
||||
},
|
||||
evm::policies::{
|
||||
@@ -28,12 +31,8 @@ mod utils;
|
||||
/// Errors that can only occur once the transaction meaning is known (during policy evaluation)
|
||||
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||
pub enum PolicyError {
|
||||
#[error("Database connection pool error")]
|
||||
#[diagnostic(code(arbiter_server::evm::policy_error::pool))]
|
||||
Pool(#[from] db::PoolError),
|
||||
#[error("Database returned error")]
|
||||
#[diagnostic(code(arbiter_server::evm::policy_error::database))]
|
||||
Database(#[from] diesel::result::Error),
|
||||
#[error("Database error")]
|
||||
Database(#[from] crate::db::DatabaseError),
|
||||
#[error("Transaction violates policy: {0:?}")]
|
||||
#[diagnostic(code(arbiter_server::evm::policy_error::violation))]
|
||||
Violations(Vec<EvalViolation>),
|
||||
@@ -55,16 +54,6 @@ pub enum VetError {
|
||||
Evaluated(SpecificMeaning, #[source] PolicyError),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||
pub enum SignError {
|
||||
#[error("Database connection pool error")]
|
||||
#[diagnostic(code(arbiter_server::evm::database_error))]
|
||||
Pool(#[from] db::PoolError),
|
||||
#[error("Database returned error")]
|
||||
#[diagnostic(code(arbiter_server::evm::database_error))]
|
||||
Database(#[from] diesel::result::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||
pub enum AnalyzeError {
|
||||
#[error("Engine doesn't support granting permissions for contract creation")]
|
||||
@@ -76,28 +65,6 @@ pub enum AnalyzeError {
|
||||
UnsupportedTransactionType,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||
pub enum CreationError {
|
||||
#[error("Database connection pool error")]
|
||||
#[diagnostic(code(arbiter_server::evm::creation_error::database_error))]
|
||||
Pool(#[from] db::PoolError),
|
||||
|
||||
#[error("Database returned error")]
|
||||
#[diagnostic(code(arbiter_server::evm::creation_error::database_error))]
|
||||
Database(#[from] diesel::result::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||
pub enum ListGrantsError {
|
||||
#[error("Database connection pool error")]
|
||||
#[diagnostic(code(arbiter_server::evm::list_grants_error::pool))]
|
||||
Pool(#[from] db::PoolError),
|
||||
|
||||
#[error("Database returned error")]
|
||||
#[diagnostic(code(arbiter_server::evm::list_grants_error::database))]
|
||||
Database(#[from] diesel::result::Error),
|
||||
}
|
||||
|
||||
/// Controls whether a transaction should be executed or only validated
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RunKind {
|
||||
@@ -165,16 +132,22 @@ impl Engine {
|
||||
meaning: &P::Meaning,
|
||||
run_kind: RunKind,
|
||||
) -> Result<(), PolicyError> {
|
||||
let mut conn = self.db.get().await?;
|
||||
let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
||||
|
||||
let grant = P::try_find_grant(&context, &mut conn)
|
||||
.await?
|
||||
.await
|
||||
.map_err(DatabaseError::from)?
|
||||
.ok_or(PolicyError::NoMatchingGrant)?;
|
||||
|
||||
let mut violations =
|
||||
check_shared_constraints(&context, &grant.shared, grant.shared_grant_id, &mut conn)
|
||||
.await?;
|
||||
violations.extend(P::evaluate(&context, meaning, &grant, &mut conn).await?);
|
||||
.await
|
||||
.map_err(DatabaseError::from)?;
|
||||
violations.extend(
|
||||
P::evaluate(&context, meaning, &grant, &mut conn)
|
||||
.await
|
||||
.map_err(DatabaseError::from)?,
|
||||
);
|
||||
|
||||
if !violations.is_empty() {
|
||||
return Err(PolicyError::Violations(violations));
|
||||
@@ -184,8 +157,7 @@ impl Engine {
|
||||
let log_id: i32 = insert_into(evm_transaction_log::table)
|
||||
.values(&NewEvmTransactionLog {
|
||||
grant_id: grant.shared_grant_id,
|
||||
client_id: context.client_id,
|
||||
wallet_id: context.wallet_id,
|
||||
wallet_access_id: context.target.id,
|
||||
chain_id: context.chain as i32,
|
||||
eth_value: utils::u256_to_bytes(context.value).to_vec(),
|
||||
signed_at: Utc::now().into(),
|
||||
@@ -199,7 +171,8 @@ impl Engine {
|
||||
QueryResult::Ok(())
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
.await
|
||||
.map_err(DatabaseError::from)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -213,9 +186,8 @@ impl Engine {
|
||||
|
||||
pub async fn create_grant<P: Policy>(
|
||||
&self,
|
||||
client_id: i32,
|
||||
full_grant: FullGrant<P::Settings>,
|
||||
) -> Result<i32, CreationError> {
|
||||
) -> Result<i32, DatabaseError> {
|
||||
let mut conn = self.db.get().await?;
|
||||
|
||||
let id = conn
|
||||
@@ -225,9 +197,8 @@ impl Engine {
|
||||
|
||||
let basic_grant: EvmBasicGrant = insert_into(evm_basic_grant::table)
|
||||
.values(&NewEvmBasicGrant {
|
||||
wallet_id: full_grant.basic.wallet_id,
|
||||
chain_id: full_grant.basic.chain as i32,
|
||||
client_id,
|
||||
wallet_access_id: full_grant.basic.wallet_access_id,
|
||||
valid_from: full_grant.basic.valid_from.map(SqliteTimestamp),
|
||||
valid_until: full_grant.basic.valid_until.map(SqliteTimestamp),
|
||||
max_gas_fee_per_gas: full_grant
|
||||
@@ -262,7 +233,7 @@ impl Engine {
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub async fn list_all_grants(&self) -> Result<Vec<Grant<SpecificGrant>>, ListGrantsError> {
|
||||
pub async fn list_all_grants(&self) -> Result<Vec<Grant<SpecificGrant>>, DatabaseError> {
|
||||
let mut conn = self.db.get().await?;
|
||||
|
||||
let mut grants: Vec<Grant<SpecificGrant>> = Vec::new();
|
||||
@@ -295,8 +266,7 @@ impl Engine {
|
||||
|
||||
pub async fn evaluate_transaction(
|
||||
&self,
|
||||
wallet_id: i32,
|
||||
client_id: i32,
|
||||
target: EvmWalletAccess,
|
||||
transaction: TxEip1559,
|
||||
run_kind: RunKind,
|
||||
) -> Result<SpecificMeaning, VetError> {
|
||||
@@ -304,8 +274,7 @@ impl Engine {
|
||||
return Err(VetError::ContractCreationNotSupported);
|
||||
};
|
||||
let context = policies::EvalContext {
|
||||
wallet_id,
|
||||
client_id,
|
||||
target,
|
||||
chain: transaction.chain_id,
|
||||
to,
|
||||
value: transaction.value,
|
||||
|
||||
@@ -10,7 +10,7 @@ use miette::Diagnostic;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
db::models::{self, EvmBasicGrant},
|
||||
db::models::{self, EvmBasicGrant, EvmWalletAccess},
|
||||
evm::utils,
|
||||
};
|
||||
|
||||
@@ -19,9 +19,8 @@ pub mod token_transfers;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EvalContext {
|
||||
// Which wallet is this transaction for
|
||||
pub client_id: i32,
|
||||
pub wallet_id: i32,
|
||||
// Which wallet is this transaction for and who requested it
|
||||
pub target: EvmWalletAccess,
|
||||
|
||||
// The transaction data
|
||||
pub chain: ChainId,
|
||||
@@ -145,8 +144,7 @@ pub struct VolumeRateLimit {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct SharedGrantSettings {
|
||||
pub wallet_id: i32,
|
||||
pub client_id: i32,
|
||||
pub wallet_access_id: i32,
|
||||
pub chain: ChainId,
|
||||
|
||||
pub valid_from: Option<DateTime<Utc>>,
|
||||
@@ -161,8 +159,7 @@ pub struct SharedGrantSettings {
|
||||
impl SharedGrantSettings {
|
||||
fn try_from_model(model: EvmBasicGrant) -> QueryResult<Self> {
|
||||
Ok(Self {
|
||||
wallet_id: model.wallet_id,
|
||||
client_id: model.client_id,
|
||||
wallet_access_id: model.wallet_access_id,
|
||||
chain: model.chain_id as u64, // safe because chain_id is stored as i32 but is guaranteed to be a valid ChainId by the API when creating grants
|
||||
valid_from: model.valid_from.map(Into::into),
|
||||
valid_until: model.valid_until.map(Into::into),
|
||||
|
||||
@@ -196,9 +196,8 @@ impl Policy for EtherTransfer {
|
||||
.inner_join(evm_basic_grant::table)
|
||||
.inner_join(evm_ether_transfer_grant_target::table)
|
||||
.filter(
|
||||
evm_basic_grant::wallet_id
|
||||
.eq(context.wallet_id)
|
||||
.and(evm_basic_grant::client_id.eq(context.client_id))
|
||||
evm_basic_grant::wallet_access_id
|
||||
.eq(context.target.id)
|
||||
.and(evm_basic_grant::revoked_at.is_null())
|
||||
.and(evm_ether_transfer_grant_target::address.eq(&target_bytes)),
|
||||
)
|
||||
|
||||
@@ -5,7 +5,9 @@ use diesel_async::RunQueryDsl;
|
||||
|
||||
use crate::db::{
|
||||
self, DatabaseConnection,
|
||||
models::{EvmBasicGrant, NewEvmBasicGrant, NewEvmTransactionLog, SqliteTimestamp},
|
||||
models::{
|
||||
EvmBasicGrant, EvmWalletAccess, NewEvmBasicGrant, NewEvmTransactionLog, SqliteTimestamp,
|
||||
},
|
||||
schema::{evm_basic_grant, evm_transaction_log},
|
||||
};
|
||||
use crate::evm::{
|
||||
@@ -15,8 +17,7 @@ use crate::evm::{
|
||||
|
||||
use super::{EtherTransfer, Settings};
|
||||
|
||||
const WALLET_ID: i32 = 1;
|
||||
const CLIENT_ID: i32 = 2;
|
||||
const WALLET_ACCESS_ID: i32 = 1;
|
||||
const CHAIN_ID: u64 = 1;
|
||||
|
||||
const ALLOWED: Address = address!("1111111111111111111111111111111111111111");
|
||||
@@ -24,8 +25,12 @@ const OTHER: Address = address!("2222222222222222222222222222222222222222");
|
||||
|
||||
fn ctx(to: Address, value: U256) -> EvalContext {
|
||||
EvalContext {
|
||||
wallet_id: WALLET_ID,
|
||||
client_id: CLIENT_ID,
|
||||
target: EvmWalletAccess {
|
||||
id: WALLET_ACCESS_ID,
|
||||
wallet_id: 10,
|
||||
client_id: 20,
|
||||
created_at: SqliteTimestamp(Utc::now()),
|
||||
},
|
||||
chain: CHAIN_ID,
|
||||
to,
|
||||
value,
|
||||
@@ -38,8 +43,7 @@ fn ctx(to: Address, value: U256) -> EvalContext {
|
||||
async fn insert_basic(conn: &mut DatabaseConnection, revoked: bool) -> EvmBasicGrant {
|
||||
insert_into(evm_basic_grant::table)
|
||||
.values(NewEvmBasicGrant {
|
||||
wallet_id: WALLET_ID,
|
||||
client_id: CLIENT_ID,
|
||||
wallet_access_id: WALLET_ACCESS_ID,
|
||||
chain_id: CHAIN_ID as i32,
|
||||
valid_from: None,
|
||||
valid_until: None,
|
||||
@@ -67,14 +71,13 @@ fn make_settings(targets: Vec<Address>, max_volume: u64) -> Settings {
|
||||
|
||||
fn shared() -> SharedGrantSettings {
|
||||
SharedGrantSettings {
|
||||
wallet_id: WALLET_ID,
|
||||
wallet_access_id: WALLET_ACCESS_ID,
|
||||
chain: CHAIN_ID,
|
||||
valid_from: None,
|
||||
valid_until: None,
|
||||
max_gas_fee_per_gas: None,
|
||||
max_priority_fee_per_gas: None,
|
||||
rate_limit: None,
|
||||
client_id: CLIENT_ID,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,8 +156,7 @@ async fn evaluate_passes_when_volume_within_limit() {
|
||||
insert_into(evm_transaction_log::table)
|
||||
.values(NewEvmTransactionLog {
|
||||
grant_id,
|
||||
client_id: CLIENT_ID,
|
||||
wallet_id: WALLET_ID,
|
||||
wallet_access_id: WALLET_ACCESS_ID,
|
||||
chain_id: CHAIN_ID as i32,
|
||||
eth_value: utils::u256_to_bytes(U256::from(500u64)).to_vec(),
|
||||
signed_at: SqliteTimestamp(Utc::now()),
|
||||
@@ -194,8 +196,7 @@ async fn evaluate_rejects_volume_over_limit() {
|
||||
insert_into(evm_transaction_log::table)
|
||||
.values(NewEvmTransactionLog {
|
||||
grant_id,
|
||||
client_id: CLIENT_ID,
|
||||
wallet_id: WALLET_ID,
|
||||
wallet_access_id: WALLET_ACCESS_ID,
|
||||
chain_id: CHAIN_ID as i32,
|
||||
eth_value: utils::u256_to_bytes(U256::from(1_001u64)).to_vec(),
|
||||
signed_at: SqliteTimestamp(Utc::now()),
|
||||
@@ -236,8 +237,7 @@ async fn evaluate_passes_at_exactly_volume_limit() {
|
||||
insert_into(evm_transaction_log::table)
|
||||
.values(NewEvmTransactionLog {
|
||||
grant_id,
|
||||
client_id: CLIENT_ID,
|
||||
wallet_id: WALLET_ID,
|
||||
wallet_access_id: WALLET_ACCESS_ID,
|
||||
chain_id: CHAIN_ID as i32,
|
||||
eth_value: utils::u256_to_bytes(U256::from(1_000u64)).to_vec(),
|
||||
signed_at: SqliteTimestamp(Utc::now()),
|
||||
|
||||
@@ -209,8 +209,7 @@ impl Policy for TokenTransfer {
|
||||
|
||||
let grant: Option<(EvmBasicGrant, EvmTokenTransferGrant)> = grant_join()
|
||||
.filter(evm_basic_grant::revoked_at.is_null())
|
||||
.filter(evm_basic_grant::wallet_id.eq(context.wallet_id))
|
||||
.filter(evm_basic_grant::client_id.eq(context.client_id))
|
||||
.filter(evm_basic_grant::wallet_access_id.eq(context.target.id))
|
||||
.filter(evm_token_transfer_grant::token_contract.eq(&token_contract_bytes))
|
||||
.select((
|
||||
EvmBasicGrant::as_select(),
|
||||
|
||||
@@ -6,7 +6,7 @@ use diesel_async::RunQueryDsl;
|
||||
|
||||
use crate::db::{
|
||||
self, DatabaseConnection,
|
||||
models::{EvmBasicGrant, NewEvmBasicGrant, SqliteTimestamp},
|
||||
models::{EvmBasicGrant, EvmWalletAccess, NewEvmBasicGrant, SqliteTimestamp},
|
||||
schema::evm_basic_grant,
|
||||
};
|
||||
use crate::evm::{
|
||||
@@ -21,8 +21,7 @@ use super::{Settings, TokenTransfer};
|
||||
const CHAIN_ID: u64 = 1;
|
||||
const DAI: Address = address!("6B175474E89094C44Da98b954EedeAC495271d0F");
|
||||
|
||||
const WALLET_ID: i32 = 1;
|
||||
const CLIENT_ID: i32 = 2;
|
||||
const WALLET_ACCESS_ID: i32 = 1;
|
||||
|
||||
const RECIPIENT: Address = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
const OTHER: Address = address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
|
||||
@@ -38,8 +37,12 @@ fn transfer_calldata(to: Address, value: U256) -> Bytes {
|
||||
|
||||
fn ctx(to: Address, calldata: Bytes) -> EvalContext {
|
||||
EvalContext {
|
||||
wallet_id: WALLET_ID,
|
||||
client_id: CLIENT_ID,
|
||||
target: EvmWalletAccess {
|
||||
id: WALLET_ACCESS_ID,
|
||||
wallet_id: 10,
|
||||
client_id: 20,
|
||||
created_at: SqliteTimestamp(Utc::now()),
|
||||
},
|
||||
chain: CHAIN_ID,
|
||||
to,
|
||||
value: U256::ZERO,
|
||||
@@ -52,8 +55,7 @@ fn ctx(to: Address, calldata: Bytes) -> EvalContext {
|
||||
async fn insert_basic(conn: &mut DatabaseConnection, revoked: bool) -> EvmBasicGrant {
|
||||
insert_into(evm_basic_grant::table)
|
||||
.values(NewEvmBasicGrant {
|
||||
wallet_id: WALLET_ID,
|
||||
client_id: CLIENT_ID,
|
||||
wallet_access_id: WALLET_ACCESS_ID,
|
||||
chain_id: CHAIN_ID as i32,
|
||||
valid_from: None,
|
||||
valid_until: None,
|
||||
@@ -86,14 +88,13 @@ fn make_settings(target: Option<Address>, max_volume: Option<u64>) -> Settings {
|
||||
|
||||
fn shared() -> SharedGrantSettings {
|
||||
SharedGrantSettings {
|
||||
wallet_id: WALLET_ID,
|
||||
wallet_access_id: WALLET_ACCESS_ID,
|
||||
chain: CHAIN_ID,
|
||||
valid_from: None,
|
||||
valid_until: None,
|
||||
max_gas_fee_per_gas: None,
|
||||
max_priority_fee_per_gas: None,
|
||||
rate_limit: None,
|
||||
client_id: CLIENT_ID,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user