From 87ace2ec8a2e886c8321058012edffe5ee01b9bd Mon Sep 17 00:00:00 2001 From: dancoombs Date: Tue, 25 Feb 2025 15:10:31 -0600 Subject: [PATCH] feat: add max uo per sender permission --- crates/builder/src/bundle_proposer.rs | 5 ++- crates/pool/proto/op_pool/op_pool.proto | 1 + crates/pool/src/mempool/uo_pool.rs | 44 +++++++++++++++++-- crates/pool/src/server/remote/protos.rs | 6 +++ crates/rpc/src/types/permissions.rs | 5 +++ .../types/src/user_operation/permissions.rs | 2 + 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/crates/builder/src/bundle_proposer.rs b/crates/builder/src/bundle_proposer.rs index 89389b4c8..9faa2eb3e 100644 --- a/crates/builder/src/bundle_proposer.rs +++ b/crates/builder/src/bundle_proposer.rs @@ -3328,7 +3328,10 @@ mod tests { aggregator: None, da_gas_data: Default::default(), filter_id: None, - perms: UserOperationPermissions { trusted: *trusted }, + perms: UserOperationPermissions { + trusted: *trusted, + ..Default::default() + }, }) .collect(); diff --git a/crates/pool/proto/op_pool/op_pool.proto b/crates/pool/proto/op_pool/op_pool.proto index c96e3cca6..1f649a83a 100644 --- a/crates/pool/proto/op_pool/op_pool.proto +++ b/crates/pool/proto/op_pool/op_pool.proto @@ -26,6 +26,7 @@ message UserOperation { message UserOperationPermissions { bool trusted = 1; + optional uint64 max_allowed_in_pool_for_sender = 2; } // Protocol Buffer representation of an 7702 authorization tuple. See the official diff --git a/crates/pool/src/mempool/uo_pool.rs b/crates/pool/src/mempool/uo_pool.rs index 5b01b3284..259fe115d 100644 --- a/crates/pool/src/mempool/uo_pool.rs +++ b/crates/pool/src/mempool/uo_pool.rs @@ -641,14 +641,18 @@ where // Check sender count in mempool. If sender has too many operations, must be staked { + let sender_allowed_count = pool_op + .perms + .max_allowed_in_pool_for_sender + .unwrap_or(self.config.same_sender_mempool_count); + let state = self.state.read(); if !pool_op.account_is_staked && to_replace.is_none() - && state.pool.address_count(&pool_op.uo.sender()) - >= self.config.same_sender_mempool_count + && state.pool.address_count(&pool_op.uo.sender()) >= sender_allowed_count { return Err(MempoolError::MaxOperationsReached( - self.config.same_sender_mempool_count, + sender_allowed_count, Entity::account(pool_op.uo.sender()), )); } @@ -2229,7 +2233,10 @@ mod tests { #[tokio::test] async fn test_trusted_uo() { let config = default_config(); - let perms = UserOperationPermissions { trusted: true }; + let perms = UserOperationPermissions { + trusted: true, + ..Default::default() + }; let op = create_trusted_op(Address::random(), 0, 0); let pool = create_pool_with_config(config, vec![op.clone()]); @@ -2238,6 +2245,35 @@ mod tests { .unwrap(); } + #[tokio::test] + async fn test_max_allowed_in_pool_for_sender() { + let config = default_config(); + let perms = UserOperationPermissions { + max_allowed_in_pool_for_sender: Some(2), + ..Default::default() + }; + let sender = Address::random(); + + let op1 = create_op(sender, 1, 100, None); + let op2 = create_op(sender, 2, 100, None); + let op3 = create_op(sender, 3, 100, None); + + let pool = create_pool_with_config(config, vec![op1.clone(), op2.clone(), op3.clone()]); + pool.add_operation(OperationOrigin::Local, op1.op, perms.clone()) + .await + .unwrap(); + pool.add_operation(OperationOrigin::Local, op2.op, perms.clone()) + .await + .unwrap(); + let err = pool + .add_operation(OperationOrigin::Local, op3.op, perms) + .await + .err() + .unwrap(); + + assert!(matches!(err, MempoolError::MaxOperationsReached(2, _))); + } + #[derive(Clone, Debug)] struct OpWithErrors { op: UserOperationVariant, diff --git a/crates/pool/src/server/remote/protos.rs b/crates/pool/src/server/remote/protos.rs index 48db22328..c3e67dcf3 100644 --- a/crates/pool/src/server/remote/protos.rs +++ b/crates/pool/src/server/remote/protos.rs @@ -576,6 +576,9 @@ impl From for RundlerUserOperationPermissions { fn from(permissions: UserOperationPermissions) -> Self { Self { trusted: permissions.trusted, + max_allowed_in_pool_for_sender: permissions + .max_allowed_in_pool_for_sender + .map(|c| c as usize), } } } @@ -584,6 +587,9 @@ impl From for UserOperationPermissions { fn from(permissions: RundlerUserOperationPermissions) -> Self { Self { trusted: permissions.trusted, + max_allowed_in_pool_for_sender: permissions + .max_allowed_in_pool_for_sender + .map(|c| c as u64), } } } diff --git a/crates/rpc/src/types/permissions.rs b/crates/rpc/src/types/permissions.rs index 2743da4cd..3cb62c83f 100644 --- a/crates/rpc/src/types/permissions.rs +++ b/crates/rpc/src/types/permissions.rs @@ -11,6 +11,7 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. +use alloy_primitives::U64; use rundler_types::{chain::ChainSpec, UserOperationPermissions}; use serde::{Deserialize, Serialize}; @@ -23,12 +24,16 @@ pub(crate) struct RpcUserOperationPermissions { /// Whether the user operation is trusted, allowing the bundler to skip untrusted simulation #[serde(default)] pub(crate) trusted: bool, + /// The maximum sender allowed in the pool + #[serde(default)] + pub(crate) max_allowed_in_pool_for_sender: Option, } impl FromRpc for UserOperationPermissions { fn from_rpc(rpc: RpcUserOperationPermissions, _chain_spec: &ChainSpec) -> Self { UserOperationPermissions { trusted: rpc.trusted, + max_allowed_in_pool_for_sender: rpc.max_allowed_in_pool_for_sender.map(|c| c.to()), } } } diff --git a/crates/types/src/user_operation/permissions.rs b/crates/types/src/user_operation/permissions.rs index e85b3fd4e..e83392217 100644 --- a/crates/types/src/user_operation/permissions.rs +++ b/crates/types/src/user_operation/permissions.rs @@ -16,4 +16,6 @@ pub struct UserOperationPermissions { /// Whether the user operation is trusted, allowing the bundler to skip untrusted simulation pub trusted: bool, + /// The maximum number of user operations allowed for a sender in the mempool + pub max_allowed_in_pool_for_sender: Option, }