Skip to content

Commit

Permalink
test_ProofSubmitterExecuteOnRewardsCoordinatorFromServiceViaEntryPoint
Browse files Browse the repository at this point in the history
  • Loading branch information
sanbir committed Sep 4, 2024
1 parent 63ce51f commit 9c00e3d
Show file tree
Hide file tree
Showing 7 changed files with 525 additions and 15 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Compiler files
cache/
out/
forge-cache/

# Ignores development broadcast logs
!/broadcast
Expand All @@ -14,3 +15,5 @@ docs/

# Dotenv file
.env
.DS_Store

36 changes: 36 additions & 0 deletions src/mocks/erc4337/IAggregator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../../lib/erc4337/UserOperation.sol";

/**
* Aggregated Signatures validator.
*/
interface IAggregator {

/**
* validate aggregated signature.
* revert if the aggregated signature does not match the given list of operations.
*/
function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) external view;

/**
* validate signature of a single userOp
* This method is should be called by bundler after EntryPoint.simulateValidation() returns (reverts) with ValidationResultWithAggregation
* First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
* @param userOp the userOperation received from the user.
* @return sigForUserOp the value to put into the signature field of the userOp when calling handleOps.
* (usually empty, unless account and aggregator support some kind of "multisig"
*/
function validateUserOpSignature(UserOperation calldata userOp)
external view returns (bytes memory sigForUserOp);

/**
* aggregate multiple signatures into a single value.
* This method is called off-chain to calculate the signature to pass with handleOps()
* bundler MAY use optimized custom code perform this aggregation
* @param userOps array of UserOperations to collect the signatures from.
* @return aggregatedSignature the aggregated signature
*/
function aggregateSignatures(UserOperation[] calldata userOps) external view returns (bytes memory aggregatedSignature);
}
205 changes: 205 additions & 0 deletions src/mocks/erc4337/IEntryPoint.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/**
** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
** Only one instance required on each chain.
**/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */

import "./IStakeManager.sol";
import "./IAggregator.sol";
import "./INonceManager.sol";
import "../../lib/erc4337/UserOperation.sol";

interface IEntryPoint is IStakeManager, INonceManager {

/***
* An event emitted after each successful request
* @param userOpHash - unique identifier for the request (hash its entire content, except signature).
* @param sender - the account that generates this request.
* @param paymaster - if non-null, the paymaster that pays for this request.
* @param nonce - the nonce value from the request.
* @param success - true if the sender transaction succeeded, false if reverted.
* @param actualGasCost - actual amount paid (by account or paymaster) for this UserOperation.
* @param actualGasUsed - total gas used by this UserOperation (including preVerification, creation, validation and execution).
*/
event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed);

/**
* account "sender" was deployed.
* @param userOpHash the userOp that deployed this account. UserOperationEvent will follow.
* @param sender the account that is deployed
* @param factory the factory used to deploy this account (in the initCode)
* @param paymaster the paymaster used by this UserOp
*/
event AccountDeployed(bytes32 indexed userOpHash, address indexed sender, address factory, address paymaster);

/**
* An event emitted if the UserOperation "callData" reverted with non-zero length
* @param userOpHash the request unique identifier.
* @param sender the sender of this request
* @param nonce the nonce used in the request
* @param revertReason - the return bytes from the (reverted) call to "callData".
*/
event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason);

/**
* an event emitted by handleOps(), before starting the execution loop.
* any event emitted before this event, is part of the validation.
*/
event BeforeExecution();

/**
* signature aggregator used by the following UserOperationEvents within this bundle.
*/
event SignatureAggregatorChanged(address indexed aggregator);

/**
* a custom revert error of handleOps, to identify the offending op.
* NOTE: if simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
* @param opIndex - index into the array of ops to the failed one (in simulateValidation, this is always zero)
* @param reason - revert reason
* The string starts with a unique code "AAmn", where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
* so a failure can be attributed to the correct entity.
* Should be caught in off-chain handleOps simulation and not happen on-chain.
* Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
*/
error FailedOp(uint256 opIndex, string reason);

/**
* error case when a signature aggregator fails to verify the aggregated signature it had created.
*/
error SignatureValidationFailed(address aggregator);

/**
* Successful result from simulateValidation.
* @param returnInfo gas and time-range returned values
* @param senderInfo stake information about the sender
* @param factoryInfo stake information about the factory (if any)
* @param paymasterInfo stake information about the paymaster (if any)
*/
error ValidationResult(ReturnInfo returnInfo,
StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo);

/**
* Successful result from simulateValidation, if the account returns a signature aggregator
* @param returnInfo gas and time-range returned values
* @param senderInfo stake information about the sender
* @param factoryInfo stake information about the factory (if any)
* @param paymasterInfo stake information about the paymaster (if any)
* @param aggregatorInfo signature aggregation info (if the account requires signature aggregator)
* bundler MUST use it to verify the signature, or reject the UserOperation
*/
error ValidationResultWithAggregation(ReturnInfo returnInfo,
StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo,
AggregatorStakeInfo aggregatorInfo);

/**
* return value of getSenderAddress
*/
error SenderAddressResult(address sender);

/**
* return value of simulateHandleOp
*/
error ExecutionResult(uint256 preOpGas, uint256 paid, uint48 validAfter, uint48 validUntil, bool targetSuccess, bytes targetResult);

//UserOps handled, per aggregator
struct UserOpsPerAggregator {
UserOperation[] userOps;

// aggregator address
IAggregator aggregator;
// aggregated signature
bytes signature;
}

/**
* Execute a batch of UserOperation.
* no signature aggregator is used.
* if any account requires an aggregator (that is, it returned an aggregator when
* performing simulateValidation), then handleAggregatedOps() must be used instead.
* @param ops the operations to execute
* @param beneficiary the address to receive the fees
*/
function handleOps(UserOperation[] calldata ops, address payable beneficiary) external;

/**
* Execute a batch of UserOperation with Aggregators
* @param opsPerAggregator the operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts)
* @param beneficiary the address to receive the fees
*/
function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
address payable beneficiary
) external;

/**
* generate a request Id - unique identifier for this request.
* the request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
*/
function getUserOpHash(UserOperation calldata userOp) external view returns (bytes32);

/**
* Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.
* @dev this method always revert. Successful result is ValidationResult error. other errors are failures.
* @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data.
* @param userOp the user operation to validate.
*/
function simulateValidation(UserOperation calldata userOp) external;

/**
* gas and return values during simulation
* @param preOpGas the gas used for validation (including preValidationGas)
* @param prefund the required prefund for this operation
* @param sigFailed validateUserOp's (or paymaster's) signature check failed
* @param validAfter - first timestamp this UserOp is valid (merging account and paymaster time-range)
* @param validUntil - last timestamp this UserOp is valid (merging account and paymaster time-range)
* @param paymasterContext returned by validatePaymasterUserOp (to be passed into postOp)
*/
struct ReturnInfo {
uint256 preOpGas;
uint256 prefund;
bool sigFailed;
uint48 validAfter;
uint48 validUntil;
bytes paymasterContext;
}

/**
* returned aggregated signature info.
* the aggregator returned by the account, and its current stake.
*/
struct AggregatorStakeInfo {
address aggregator;
StakeInfo stakeInfo;
}

/**
* Get counterfactual sender address.
* Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
* this method always revert, and returns the address in SenderAddressResult error
* @param initCode the constructor code to be passed into the UserOperation.
*/
function getSenderAddress(bytes memory initCode) external;


/**
* simulate full execution of a UserOperation (including both validation and target execution)
* this method will always revert with "ExecutionResult".
* it performs full validation of the UserOperation, but ignores signature error.
* an optional target address is called after the userop succeeds, and its value is returned
* (before the entire call is reverted)
* Note that in order to collect the the success/failure of the target call, it must be executed
* with trace enabled to track the emitted events.
* @param op the UserOperation to simulate
* @param target if nonzero, a target address to call after userop simulation. If called, the targetSuccess and targetResult
* are set to the return from that call.
* @param targetCallData callData to pass to target address
*/
function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external;
}

27 changes: 27 additions & 0 deletions src/mocks/erc4337/INonceManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

interface INonceManager {

/**
* Return the next nonce for this sender.
* Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop)
* But UserOp with different keys can come with arbitrary order.
*
* @param sender the account address
* @param key the high 192 bit of the nonce
* @return nonce a full nonce to pass for next UserOp with this sender.
*/
function getNonce(address sender, uint192 key)
external view returns (uint256 nonce);

/**
* Manually increment the nonce of the sender.
* This method is exposed just for completeness..
* Account does NOT need to call it, neither during validation, nor elsewhere,
* as the EntryPoint will update the nonce regardless.
* Possible use-case is call it with various keys to "initialize" their nonces to one, so that future
* UserOperations will not pay extra for the first transaction with a given key.
*/
function incrementNonce(uint192 key) external;
}
51 changes: 51 additions & 0 deletions src/mocks/erc4337/IPaymaster.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

import "../../lib/erc4337/UserOperation.sol";

/**
* the interface exposed by a paymaster contract, who agrees to pay the gas for user's operations.
* a paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction.
*/
interface IPaymaster {

enum PostOpMode {
opSucceeded, // user op succeeded
opReverted, // user op reverted. still has to pay for gas.
postOpReverted //user op succeeded, but caused postOp to revert. Now it's a 2nd call, after user's op was deliberately reverted.
}

/**
* payment validation: check if paymaster agrees to pay.
* Must verify sender is the entryPoint.
* Revert to reject this request.
* Note that bundlers will reject this method if it changes the state, unless the paymaster is trusted (whitelisted)
* The paymaster pre-pays using its deposit, and receive back a refund after the postOp method returns.
* @param userOp the user operation
* @param userOpHash hash of the user's request data.
* @param maxCost the maximum cost of this transaction (based on maximum gas and gas price from userOp)
* @return context value to send to a postOp
* zero length to signify postOp is not required.
* @return validationData signature and time-range of this operation, encoded the same as the return value of validateUserOperation
* <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
* otherwise, an address of an "authorizer" contract.
* <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
* <6-byte> validAfter - first timestamp this operation is valid
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)
external returns (bytes memory context, uint256 validationData);

/**
* post-operation handler.
* Must verify sender is the entryPoint
* @param mode enum with the following options:
* opSucceeded - user operation succeeded.
* opReverted - user op reverted. still has to pay for gas.
* postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert.
* Now this is the 2nd call, after user's op was deliberately reverted.
* @param context - the context value returned by validatePaymasterUserOp
* @param actualGasCost - actual gas used so far (without this postOp call).
*/
function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external;
}
Loading

0 comments on commit 9c00e3d

Please sign in to comment.