private UserOperationSimulationResult SimulateValidation( Transaction transaction, UserOperation userOperation, BlockHeader parent, ITransactionProcessor transactionProcessor) { bool paymasterWhitelisted = _whitelistedPaymasters.Contains(userOperation.Paymaster); UserOperationTxTracer txTracer = new( transaction, paymasterWhitelisted, userOperation.InitCode != Bytes.Empty, userOperation.Sender, userOperation.Paymaster, _entryPointContractAddress, _logger ); transactionProcessor.Trace(transaction, parent, txTracer); FailedOp?failedOp = _userOperationTxBuilder.DecodeEntryPointOutputError(txTracer.Output); string?error = null; if (!txTracer.Success) { if (failedOp is not null) { error = failedOp.ToString() !; } else { error = txTracer.Error; } return(UserOperationSimulationResult.Failed(error)); } UserOperationAccessList userOperationAccessList = new(txTracer.AccessedStorage); IDictionary <Address, Keccak> addressesToCodeHashes = new Dictionary <Address, Keccak>(); foreach (Address accessedAddress in txTracer.AccessedAddresses) { addressesToCodeHashes[accessedAddress] = _stateProvider.GetCodeHash(accessedAddress); } return(new UserOperationSimulationResult() { Success = true, AccessList = userOperationAccessList, AddressesToCodeHashes = addressesToCodeHashes, Error = error }); }
public ResultWrapper <Keccak> Simulate(UserOperation userOperation, BlockHeader parent, UInt256?timestamp = null, CancellationToken cancellationToken = default) { if (userOperation.AlreadySimulated) { // codehash of all accessed addresses should not change between calls foreach (KeyValuePair <Address, Keccak> kv in userOperation.AddressesToCodeHashes) { (Address address, Keccak expectedCodeHash) = kv; Keccak codeHash = _stateProvider.GetCodeHash(address); if (codeHash != expectedCodeHash) { return(ResultWrapper <Keccak> .Fail($"codehash of address {address} changed since initial simulation")); } } } IReleaseSpec currentSpec = _specProvider.GetSpec(parent.Number + 1); ReadOnlyTxProcessingEnv txProcessingEnv = _readOnlyTxProcessingEnvFactory.Create(); ITransactionProcessor transactionProcessor = txProcessingEnv.Build(_stateProvider.StateRoot); // wrap userOp into a tx calling the simulateWallet function off-chain from zero-address (look at EntryPoint.sol for more context) Transaction simulateValidationTransaction = BuildSimulateValidationTransaction(userOperation, parent, currentSpec); UserOperationSimulationResult simulationResult = SimulateValidation(simulateValidationTransaction, userOperation, parent, transactionProcessor); if (!simulationResult.Success) { return(ResultWrapper <Keccak> .Fail(simulationResult.Error ?? "unknown simulation failure")); } if (userOperation.AlreadySimulated) { // if previously simulated we must make sure it doesn't access any more than it did on the first round if (!userOperation.AccessList.AccessListContains(simulationResult.AccessList.Data)) { return(ResultWrapper <Keccak> .Fail("access list exceeded")); } } else { userOperation.AccessList = simulationResult.AccessList; userOperation.AddressesToCodeHashes = simulationResult.AddressesToCodeHashes; userOperation.AlreadySimulated = true; } return(ResultWrapper <Keccak> .Success(userOperation.RequestId !)); }