diff --git a/ethers-middleware/src/policy.rs b/ethers-middleware/src/policy.rs index 2462ba54b..e1f1276ff 100644 --- a/ethers-middleware/src/policy.rs +++ b/ethers-middleware/src/policy.rs @@ -1,7 +1,9 @@ -use ethers_core::types::{transaction::eip2718::TypedTransaction, BlockId}; +use ethers_core::types::{transaction::eip2718::TypedTransaction, BlockId, Bytes, Selector, U256}; use ethers_providers::{FromErr, Middleware, PendingTransaction}; use async_trait::async_trait; +use ethers_core::abi::Address; +use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use thiserror::Error; @@ -17,34 +19,6 @@ pub trait Policy: Sync + Send + Debug { async fn ensure_can_send(&self, tx: TypedTransaction) -> Result; } -/// A policy that does not restrict anything. -#[derive(Debug, Clone, Copy)] -pub struct AllowEverything; - -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl Policy for AllowEverything { - type Error = (); - - async fn ensure_can_send(&self, tx: TypedTransaction) -> Result { - Ok(tx) - } -} - -/// A policy that rejects all transactions. -#[derive(Debug, Clone, Copy)] -pub struct RejectEverything; - -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl Policy for RejectEverything { - type Error = (); - - async fn ensure_can_send(&self, _: TypedTransaction) -> Result { - Err(()) - } -} - /// Middleware used to enforce certain policies for transactions. #[derive(Clone, Debug)] pub struct PolicyMiddleware { @@ -67,6 +41,14 @@ where pub fn new(inner: M, policy: P) -> Self { Self { inner, policy } } + + pub fn policy(&self) -> &P { + &self.policy + } + + pub fn policy_mut(&mut self) -> &mut P { + &mut self.policy + } } #[derive(Error, Debug)] @@ -113,3 +95,95 @@ where .map_err(PolicyMiddlewareError::MiddlewareError) } } + +/// A `Policy` that only lives in memory +#[derive(Debug)] +pub struct MemoryPolicy { + pub rules: Vec, +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl Policy for MemoryPolicy { + type Error = PolicyError; + + async fn ensure_can_send(&self, tx: TypedTransaction) -> Result { + self.rules + .iter() + .find_map(|rule| rule.ensure(&tx).err()) + .map_or(Ok(tx), Err) + } +} + +/// Different rules that can be configured. +#[derive(Debug)] +pub enum Rule { + ReceiverAllowList(HashSet
), + ReceiverBlockList(HashSet
), + SenderAllowList(HashSet
), + SenderBlockList(HashSet
), + AllowList(HashSet<(Address, Address)>), + BlockList(HashSet<(Address, Address)>), + ValueCap(U256), + SenderValueCap(HashMap), + ReceiverValueCap(HashMap), + InvalidSelector(HashSet), + InvalidReceiverSelector(HashMap), + // Other(Box Result<(), PolicyError> + Send + Sync>) +} + +impl Rule { + fn ensure(&self, tx: &TypedTransaction) -> Result<(), PolicyError> { + todo!() + } +} + +/// Reasons why a transaction was rejected. +#[derive(Error, Debug)] +pub enum PolicyError { + #[error("Invalid receiver address: `{0:?}`")] + InvalidReceiver(Address), + #[error("Invalid sender address: `{0:?}`")] + InvalidSender(Address), + #[error("Attempted to transfer `{value}` ETH, maximum allowed value `{max}`")] + ValueExceeded { + /// The requested value, which exceeds `max` + value: U256, + /// The maximum allowed value to be transferred + max: U256, + }, + /// The first 4 bytes of the hash of the method signature and. + #[error("Invalid function selector: `0x{0:?}`")] + InvalidMethod(Selector), + /// The first 4 bytes of the hash of the method signature and encoded parameters. + #[error("Invalid function payload")] + InvalidPayload(Bytes), +} + +/// A policy that does not restrict anything. +#[derive(Debug, Clone, Copy)] +pub struct AllowEverything; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl Policy for AllowEverything { + type Error = (); + + async fn ensure_can_send(&self, tx: TypedTransaction) -> Result { + Ok(tx) + } +} + +/// A policy that rejects all transactions. +#[derive(Debug, Clone, Copy)] +pub struct RejectEverything; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl Policy for RejectEverything { + type Error = (); + + async fn ensure_can_send(&self, _: TypedTransaction) -> Result { + Err(()) + } +}