feat(server::evm::engine): return meaning on error path

This commit is contained in:
hdbg
2026-03-09 20:58:04 +01:00
parent 23f60cc98e
commit 1c7fe46595
2 changed files with 32 additions and 14 deletions

View File

@@ -24,28 +24,37 @@ use crate::{
pub mod policies; pub mod policies;
mod utils; mod utils;
/// Errors that can only occur once the transaction meaning is known (during policy evaluation)
#[derive(Debug, thiserror::Error, miette::Diagnostic)] #[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum VetError { pub enum PolicyError {
#[error("Database connection pool error")] #[error("Database connection pool error")]
#[diagnostic(code(arbiter_server::evm::vet_error::database_error))] #[diagnostic(code(arbiter_server::evm::policy_error::pool))]
Pool(#[from] db::PoolError), Pool(#[from] db::PoolError),
#[error("Database returned error")] #[error("Database returned error")]
#[diagnostic(code(arbiter_server::evm::vet_error::database_error))] #[diagnostic(code(arbiter_server::evm::policy_error::database))]
Database(#[from] diesel::result::Error), Database(#[from] diesel::result::Error),
#[error("Transaction violates policy: {0:?}")] #[error("Transaction violates policy: {0:?}")]
#[diagnostic(code(arbiter_server::evm::vet_error::policy_violation))] #[diagnostic(code(arbiter_server::evm::policy_error::violation))]
Violations(Vec<EvalViolation>), Violations(Vec<EvalViolation>),
#[error("No matching grant found")] #[error("No matching grant found")]
#[diagnostic(code(arbiter_server::evm::vet_error::no_matching_grant))] #[diagnostic(code(arbiter_server::evm::policy_error::no_matching_grant))]
NoMatchingGrant, NoMatchingGrant,
}
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum VetError {
#[error("Contract creation transactions are not supported")] #[error("Contract creation transactions are not supported")]
#[diagnostic(code(arbiter_server::evm::vet_error::contract_creation_unsupported))] #[diagnostic(code(arbiter_server::evm::vet_error::contract_creation_unsupported))]
ContractCreationNotSupported, ContractCreationNotSupported,
#[error("Engine can't classify this transaction")] #[error("Engine can't classify this transaction")]
#[diagnostic(code(arbiter_server::evm::vet_error::unsupported))] #[diagnostic(code(arbiter_server::evm::vet_error::unsupported))]
UnsupportedTransactionType, UnsupportedTransactionType,
#[error("Policy evaluation failed: {1}")]
#[diagnostic(code(arbiter_server::evm::vet_error::evaluated))]
Evaluated(SpecificMeaning, #[source] PolicyError),
} }
#[derive(Debug, thiserror::Error, miette::Diagnostic)] #[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum SignError { pub enum SignError {
#[error("Database connection pool error")] #[error("Database connection pool error")]
@@ -89,16 +98,16 @@ impl Engine {
context: EvalContext, context: EvalContext,
meaning: &P::Meaning, meaning: &P::Meaning,
dry_run: bool, dry_run: bool,
) -> Result<(), VetError> { ) -> Result<(), PolicyError> {
let mut conn = self.db.get().await?; let mut conn = self.db.get().await?;
let grant = P::try_find_grant(&context, &mut conn) let grant = P::try_find_grant(&context, &mut conn)
.await? .await?
.ok_or(VetError::NoMatchingGrant)?; .ok_or(PolicyError::NoMatchingGrant)?;
let violations = P::evaluate(&context, meaning, &grant, &mut conn).await?; let violations = P::evaluate(&context, meaning, &grant, &mut conn).await?;
if !violations.is_empty() { if !violations.is_empty() {
return Err(VetError::Violations(violations)); return Err(PolicyError::Violations(violations));
} else if !dry_run { } else if !dry_run {
conn.transaction(|conn| { conn.transaction(|conn| {
Box::pin(async move { Box::pin(async move {
@@ -190,7 +199,7 @@ impl Engine {
transaction: TxEip1559, transaction: TxEip1559,
// don't log // don't log
dry_run: bool, dry_run: bool,
) -> Result<(), VetError> { ) -> Result<SpecificMeaning, VetError> {
let TxKind::Call(to) = transaction.to else { let TxKind::Call(to) = transaction.to else {
return Err(VetError::ContractCreationNotSupported); return Err(VetError::ContractCreationNotSupported);
}; };
@@ -204,14 +213,22 @@ impl Engine {
}; };
if let Some(meaning) = EtherTransfer::analyze(&context) { if let Some(meaning) = EtherTransfer::analyze(&context) {
return self return match self
.vet_transaction::<EtherTransfer>(context, &meaning, dry_run) .vet_transaction::<EtherTransfer>(context, &meaning, dry_run)
.await; .await
{
Ok(()) => Ok(meaning.into()),
Err(e) => Err(VetError::Evaluated(meaning.into(), e)),
};
} }
if let Some(meaning) = TokenTransfer::analyze(&context) { if let Some(meaning) = TokenTransfer::analyze(&context) {
return self return match self
.vet_transaction::<TokenTransfer>(context, &meaning, dry_run) .vet_transaction::<TokenTransfer>(context, &meaning, dry_run)
.await; .await
{
Ok(()) => Ok(meaning.into()),
Err(e) => Err(VetError::Evaluated(meaning.into(), e)),
};
} }
Err(VetError::UnsupportedTransactionType) Err(VetError::UnsupportedTransactionType)

View File

@@ -70,7 +70,7 @@ pub struct Grant<PolicySettings> {
pub trait Policy: Sized { pub trait Policy: Sized {
type Settings: Send + Sync + 'static + Into<SpecificGrant>; type Settings: Send + Sync + 'static + Into<SpecificGrant>;
type Meaning: Display + Send + Sync + 'static + Into<SpecificMeaning>; type Meaning: Display + std::fmt::Debug + Send + Sync + 'static + Into<SpecificMeaning>;
fn analyze(context: &EvalContext) -> Option<Self::Meaning>; fn analyze(context: &EvalContext) -> Option<Self::Meaning>;
@@ -114,6 +114,7 @@ pub enum ReceiverTarget {
} }
// Classification of what transaction does // Classification of what transaction does
#[derive(Debug)]
pub enum SpecificMeaning { pub enum SpecificMeaning {
EtherTransfer(ether_transfer::Meaning), EtherTransfer(ether_transfer::Meaning),
TokenTransfer(token_transfers::Meaning), TokenTransfer(token_transfers::Meaning),