Skip to content

Commit

Permalink
feat(simulation): check whether the op fits within a valid time range (
Browse files Browse the repository at this point in the history
  • Loading branch information
0xfourzerofour authored May 24, 2024
1 parent 332d241 commit 78f15c9
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 20 deletions.
7 changes: 3 additions & 4 deletions crates/builder/src/bundle_proposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use std::{
mem,
pin::Pin,
sync::Arc,
time::Duration,
};

use anyhow::Context;
Expand All @@ -41,16 +40,14 @@ use rundler_types::{
pool::{Pool, PoolOperation, SimulationViolation},
Entity, EntityInfo, EntityInfos, EntityType, EntityUpdate, EntityUpdateType, GasFees,
Timestamp, UserOperation, UserOperationVariant, UserOpsPerAggregator, BUNDLE_BYTE_OVERHEAD,
USER_OP_OFFSET_WORD_SIZE,
TIME_RANGE_BUFFER, USER_OP_OFFSET_WORD_SIZE,
};
use rundler_utils::{emit::WithEntryPoint, math};
use tokio::{sync::broadcast, try_join};
use tracing::{error, info, warn};

use crate::emit::{BuilderEvent, OpRejectionReason, SkipReason};

/// A user op must be valid for at least this long into the future to be included.
const TIME_RANGE_BUFFER: Duration = Duration::from_secs(60);
/// Extra buffer percent to add on the bundle transaction gas estimate to be sure it will be enough
const BUNDLE_TRANSACTION_GAS_OVERHEAD_PERCENT: u64 = 5;

Expand Down Expand Up @@ -1285,6 +1282,8 @@ impl<UO: UserOperation> ProposalContext<UO> {

#[cfg(test)]
mod tests {
use std::time::Duration;

use anyhow::anyhow;
use ethers::{
types::{H160, U64},
Expand Down
6 changes: 6 additions & 0 deletions crates/pool/proto/op_pool/op_pool.proto
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ message SimulationViolationError {
InvalidAccountSignature invalid_account_signature = 21;
InvalidPaymasterSignature invalid_paymaster_signature = 22;
AssociatedStorageDuringDeploy associated_storage_during_deploy = 23;
InvalidTimeRange invalid_time_range = 24;
}
}

Expand All @@ -661,6 +662,11 @@ message UnstakedAggregator {}

message UnstakedPaymasterContext {}

message InvalidTimeRange {
uint64 valid_until = 1;
uint64 valud_after = 2;
}

message UnintendedRevertWithMessage {
Entity entity = 1;
string reason = 2;
Expand Down
40 changes: 28 additions & 12 deletions crates/pool/src/server/remote/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use rundler_types::{
pool::{
MempoolError, NeedsStakeInformation, PoolError, PrecheckViolation, SimulationViolation,
},
Opcode, StorageSlot, ValidationRevert, ViolationOpCode,
Opcode, StorageSlot, Timestamp, ValidationRevert, ViolationOpCode,
};

use super::protos::{
Expand All @@ -27,17 +27,17 @@ use super::protos::{
CalledBannedEntryPointMethod, CodeHashChanged, DidNotRevert, DiscardedOnInsertError, Entity,
EntityThrottledError, EntityType, EntryPointRevert, ExistingSenderWithInitCode,
FactoryCalledCreate2Twice, FactoryIsNotContract, InvalidAccountSignature,
InvalidPaymasterSignature, InvalidSignature, InvalidStorageAccess, MaxFeePerGasTooLow,
MaxOperationsReachedError, MaxPriorityFeePerGasTooLow, MempoolError as ProtoMempoolError,
MultipleRolesViolation, NotStaked, OperationAlreadyKnownError, OperationDropTooSoon,
OperationRevert, OutOfGas, PaymasterBalanceTooLow, PaymasterDepositTooLow,
PaymasterIsNotContract, PreVerificationGasTooLow,
PrecheckViolationError as ProtoPrecheckViolationError, ReplacementUnderpricedError,
SenderAddressUsedAsAlternateEntity, SenderFundsTooLow, SenderIsNotContractAndNoInitCode,
SimulationViolationError as ProtoSimulationViolationError, TotalGasLimitTooHigh,
UnintendedRevert, UnintendedRevertWithMessage, UnknownEntryPointError, UnknownRevert,
UnstakedAggregator, UnstakedPaymasterContext, UnsupportedAggregatorError, UsedForbiddenOpcode,
UsedForbiddenPrecompile, ValidationRevert as ProtoValidationRevert,
InvalidPaymasterSignature, InvalidSignature, InvalidStorageAccess, InvalidTimeRange,
MaxFeePerGasTooLow, MaxOperationsReachedError, MaxPriorityFeePerGasTooLow,
MempoolError as ProtoMempoolError, MultipleRolesViolation, NotStaked,
OperationAlreadyKnownError, OperationDropTooSoon, OperationRevert, OutOfGas,
PaymasterBalanceTooLow, PaymasterDepositTooLow, PaymasterIsNotContract,
PreVerificationGasTooLow, PrecheckViolationError as ProtoPrecheckViolationError,
ReplacementUnderpricedError, SenderAddressUsedAsAlternateEntity, SenderFundsTooLow,
SenderIsNotContractAndNoInitCode, SimulationViolationError as ProtoSimulationViolationError,
TotalGasLimitTooHigh, UnintendedRevert, UnintendedRevertWithMessage, UnknownEntryPointError,
UnknownRevert, UnstakedAggregator, UnstakedPaymasterContext, UnsupportedAggregatorError,
UsedForbiddenOpcode, UsedForbiddenPrecompile, ValidationRevert as ProtoValidationRevert,
VerificationGasLimitBufferTooLow, VerificationGasLimitTooHigh, WrongNumberOfPhases,
};

Expand Down Expand Up @@ -611,6 +611,16 @@ impl From<SimulationViolation> for ProtoSimulationViolationError {
CodeHashChanged {},
)),
},
SimulationViolation::InvalidTimeRange(valid_until, valid_after) => {
ProtoSimulationViolationError {
violation: Some(simulation_violation_error::Violation::InvalidTimeRange(
InvalidTimeRange {
valid_until: valid_until.seconds_since_epoch(),
valud_after: valid_after.seconds_since_epoch(),
},
)),
}
}
SimulationViolation::AggregatorValidationFailed => ProtoSimulationViolationError {
violation: Some(
simulation_violation_error::Violation::AggregatorValidationFailed(
Expand Down Expand Up @@ -642,6 +652,12 @@ impl TryFrom<ProtoSimulationViolationError> for SimulationViolation {
Some(simulation_violation_error::Violation::InvalidSignature(_)) => {
SimulationViolation::InvalidSignature
}
Some(simulation_violation_error::Violation::InvalidTimeRange(e)) => {
SimulationViolation::InvalidTimeRange(
Timestamp::new(e.valid_until),
Timestamp::new(e.valud_after),
)
}
Some(simulation_violation_error::Violation::InvalidAccountSignature(_)) => {
SimulationViolation::InvalidAccountSignature
}
Expand Down
7 changes: 7 additions & 0 deletions crates/sim/src/simulation/simulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,13 @@ where
}
}

if !entry_point_out.return_info.is_valid_time_range() {
violations.push(SimulationViolation::InvalidTimeRange(
entry_point_out.return_info.valid_until,
entry_point_out.return_info.valid_after,
));
}

if let Some(aggregator_info) = entry_point_out.aggregator_info {
if !context::is_staked(aggregator_info.stake_info, self.sim_settings) {
// [EREP-040]
Expand Down
8 changes: 7 additions & 1 deletion crates/types/src/pool/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
use ethers::types::{Address, U256};

use crate::{
validation_results::ValidationRevert, Entity, EntityType, StorageSlot, ViolationOpCode,
validation_results::ValidationRevert, Entity, EntityType, StorageSlot, Timestamp,
ViolationOpCode,
};

/// Pool server error type
Expand Down Expand Up @@ -152,6 +153,11 @@ pub enum SimulationViolation {
/// The signature is invalid for the account
#[display("invalid account signature")]
InvalidAccountSignature,
/// The user operation has an invalid time range based on the `valid_until` and `valid_after` fields
#[display(
"User Operation expired or has an invalid time range. validUntil: {0}, validAfter: {1}"
)]
InvalidTimeRange(Timestamp, Timestamp),
/// The signature is invalid for the paymaster
#[display("invalid paymaster signature")]
InvalidPaymasterSignature,
Expand Down
5 changes: 4 additions & 1 deletion crates/types/src/user_operation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +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 std::fmt::Debug;
use std::{fmt::Debug, time::Duration};

use ethers::{
abi::AbiEncode,
Expand All @@ -25,6 +25,9 @@ pub mod v0_7;

use crate::Entity;

/// A user op must be valid for at least this long into the future to be included.
pub const TIME_RANGE_BUFFER: Duration = Duration::from_secs(60);

/// Overhead for bytes required for each bundle
/// 4 bytes for function signature
/// 32 bytes for user op array offset
Expand Down
12 changes: 11 additions & 1 deletion crates/types/src/validation_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// You should have received a copy of the GNU General Public License along with Rundler.
// If not, see https://www.gnu.org/licenses/.

use std::ops::Add;

use ethers::{
abi::{self, AbiDecode, AbiError},
types::{Address, Bytes, H160, U256},
Expand All @@ -29,7 +31,7 @@ use crate::{
StakeInfo as StakeInfoV0_7, ValidationResult as ValidationResultV0_7,
},
},
Timestamp, ValidTimeRange,
Timestamp, ValidTimeRange, TIME_RANGE_BUFFER,
};

/// Both v0.6 and v0.7 contracts use this aggregator address to indicate that the signature validation failed
Expand Down Expand Up @@ -259,6 +261,14 @@ pub struct ValidationReturnInfo {
pub paymaster_context: Bytes,
}

impl ValidationReturnInfo {
/// helper function to check if the returned time range is valid
pub fn is_valid_time_range(&self) -> bool {
let now = Timestamp::now();
self.valid_after <= now || self.valid_until > now.add(TIME_RANGE_BUFFER)
}
}

// Conversion for v0.6
impl From<(U256, U256, bool, u64, u64, Bytes)> for ValidationReturnInfo {
fn from(value: (U256, U256, bool, u64, u64, Bytes)) -> Self {
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/pool.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Upon each `add_operation` call the `Pool` will preforms a series of checks.

2. Simulate the UO via a `debug_traceCall` as per the [ERC-4337 spec](https://eips.ethereum.org/EIPS/eip-4337#simulation).

If violations are found, the UO is rejected. Else, the UO is added to the pool.
If violations are found, the UO is rejected. Else, the UO is added to the pool. We only accept User Operations into the pool if the `validUntil` field has over 60 seconds to expire from the time of entry or the `validAfter` field is before the time of entry.

### Tracer

Expand Down

0 comments on commit 78f15c9

Please sign in to comment.