Skip to content

Commit

Permalink
feat(pool): return already known if exact UO already in pool
Browse files Browse the repository at this point in the history
  • Loading branch information
dancoombs committed Sep 1, 2023
1 parent d3ad10f commit 38e6ced
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 9 deletions.
1 change: 1 addition & 0 deletions proto/op_pool/op_pool.proto
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ enum ErrorReason {
ERROR_REASON_OPERATION_REJECTED = 3;
ERROR_REASON_REPLACEMENT_UNDERPRICED = 4;
ERROR_REASON_OPERATION_DISCARDED_ON_INSERT = 5;
ERROR_REASON_OPERATION_ALREADY_KNOWN = 6;
}

enum ErrorMetadataKey {
Expand Down
3 changes: 3 additions & 0 deletions src/op_pool/mempool/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pub enum MempoolError {
/// Some other error occurred
#[error(transparent)]
Other(#[from] anyhow::Error),
/// Operation with the same hash already in pool
#[error("Operation already known")]
OperationAlreadyKnown,
/// Operation with same sender/nonce already in pool
/// and the replacement operation has lower gas price.
#[error("Replacement operation underpriced. Existing priority fee: {0}. Existing fee: {1}")]
Expand Down
36 changes: 30 additions & 6 deletions src/op_pool/mempool/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,14 @@ impl PoolInner {
op: Arc<PoolOperation>,
submission_id: Option<u64>,
) -> MempoolResult<H256> {
// Check if operation already known
if self
.by_hash
.contains_key(&op.uo.op_hash(self.config.entry_point, self.config.chain_id))
{
return Err(MempoolError::OperationAlreadyKnown);
}

// Check for replacement by ID
if let Some(pool_op) = self.by_id.get(&op.uo.id()) {
if op.uo.max_fee_per_gas > u128::MAX.into()
Expand Down Expand Up @@ -621,18 +629,18 @@ mod tests {
fn replace_op_underpriced() {
let mut pool = PoolInner::new(conf());
let sender = Address::random();
let mut po1 = create_op(sender, 0, 10);
po1.uo.max_priority_fee_per_gas = 10.into();
let mut po1 = create_op(sender, 0, 100);
po1.uo.max_priority_fee_per_gas = 100.into();
let _ = pool.add_operation(po1.clone()).unwrap();

let mut po2 = create_op(sender, 0, 10);
po2.uo.max_priority_fee_per_gas = 10.into();
let mut po2 = create_op(sender, 0, 101);
po2.uo.max_priority_fee_per_gas = 101.into();
let res = pool.add_operation(po2);
assert!(res.is_err());
match res.err().unwrap() {
MempoolError::ReplacementUnderpriced(a, b) => {
assert_eq!(a, 10.into());
assert_eq!(b, 10.into());
assert_eq!(a, 100.into());
assert_eq!(b, 100.into());
}
_ => panic!("wrong error"),
}
Expand Down Expand Up @@ -664,6 +672,22 @@ mod tests {
assert_eq!(pool.pool_size, po2.size());
}

#[test]
fn test_already_known() {
let mut pool = PoolInner::new(conf());
let sender = Address::random();
let mut po1 = create_op(sender, 0, 10);
po1.uo.max_priority_fee_per_gas = 10.into();
let _ = pool.add_operation(po1.clone()).unwrap();

let res = pool.add_operation(po1);
assert!(res.is_err());
match res.err().unwrap() {
MempoolError::OperationAlreadyKnown => (),
_ => panic!("wrong error"),
}
}

fn conf() -> PoolConfig {
PoolConfig {
entry_point: Address::random(),
Expand Down
4 changes: 4 additions & 0 deletions src/op_pool/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ where
impl From<MempoolError> for Status {
fn from(e: MempoolError) -> Self {
let ei = match &e {
MempoolError::OperationAlreadyKnown => ErrorInfo {
reason: ErrorReason::OperationAlreadyKnown.as_str_name().to_string(),
metadata: HashMap::new(),
},
MempoolError::EntityThrottled(et) => ErrorInfo {
reason: ErrorReason::EntityThrottled.as_str_name().to_string(),
metadata: HashMap::from([(et.kind.to_string(), to_checksum(&et.address, None))]),
Expand Down
10 changes: 7 additions & 3 deletions src/rpc/eth/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use ethers::types::{Address, Opcode, U256};
use jsonrpsee::{
core::Error as RpcError,
types::{
error::{CallError, INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE, INVALID_REQUEST_CODE},
error::{CallError, CALL_EXECUTION_FAILED_CODE, INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE},
ErrorObject,
},
};
Expand Down Expand Up @@ -67,6 +67,9 @@ pub enum EthRpcError {
/// Replacement underpriced
#[error("replacement underpriced")]
ReplacementUnderpriced(ReplacementUnderpricedData),
/// Operation already known
#[error("already known")]
OperationAlreadyKnown,
/// Other internal errors
#[error("Invalid UserOp signature or paymaster signature")]
SignatureCheckFailed,
Expand Down Expand Up @@ -162,9 +165,10 @@ impl From<EthRpcError> for RpcError {
EthRpcError::ReplacementUnderpriced(data) => {
rpc_err_with_data(INVALID_PARAMS_CODE, msg, data)
}
EthRpcError::OperationAlreadyKnown => rpc_err(INVALID_PARAMS_CODE, msg),
EthRpcError::SignatureCheckFailed => rpc_err(SIGNATURE_CHECK_FAILED_CODE, msg),
EthRpcError::PrecheckFailed(_) => rpc_err(INVALID_PARAMS_CODE, msg),
EthRpcError::SimulationFailed(_) => rpc_err(INVALID_REQUEST_CODE, msg),
EthRpcError::PrecheckFailed(_) => rpc_err(CALL_EXECUTION_FAILED_CODE, msg),
EthRpcError::SimulationFailed(_) => rpc_err(CALL_EXECUTION_FAILED_CODE, msg),
EthRpcError::Internal(_) => rpc_err(INTERNAL_ERROR_CODE, msg),
EthRpcError::ExecutionReverted(_) => rpc_err(EXECUTION_REVERTED, msg),
}
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,8 @@ impl From<ErrorInfo> for EthRpcError {
} else if reason == ErrorReason::OperationDiscardedOnInsert.as_str_name() {
return anyhow!("operation rejected: mempool full try again with higher gas price")
.into();
} else if reason == ErrorReason::OperationAlreadyKnown.as_str_name() {
return EthRpcError::OperationAlreadyKnown;
}

anyhow!("operation rejected").into()
Expand Down

0 comments on commit 38e6ced

Please sign in to comment.