Skip to content

Commit

Permalink
feat: rename executeWithAuthorization to executeWithRuntimeValidation (
Browse files Browse the repository at this point in the history
  • Loading branch information
jaypaik authored Sep 20, 2024
1 parent 5ba3568 commit 0bb2bfa
Show file tree
Hide file tree
Showing 17 changed files with 50 additions and 45 deletions.
4 changes: 2 additions & 2 deletions src/account/ReferenceModularAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ contract ReferenceModularAccount is
}

/// @inheritdoc IModularAccount
function executeWithAuthorization(bytes calldata data, bytes calldata authorization)
function executeWithRuntimeValidation(bytes calldata data, bytes calldata authorization)
external
payable
returns (bytes memory)
Expand Down Expand Up @@ -704,7 +704,7 @@ contract ReferenceModularAccount is
// To prevent arbitrarily-deep recursive checking, we limit the depth of self-calls to one
// for the purposes of batching.
// This means that all self-calls must occur at the top level of the batch.
// Note that modules of other contracts using `executeWithAuthorization` may still
// Note that modules of other contracts using `executeWithRuntimeValidation` may still
// independently call into this account with a different validation function, allowing
// composition of multiple batches.
revert SelfCallRecursionDepthExceeded();
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/KnownSelectors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ library KnownSelectors {
|| selector == IModularAccount.installValidation.selector
|| selector == IModularAccount.uninstallValidation.selector || selector == IModularAccount.execute.selector
|| selector == IModularAccount.executeBatch.selector
|| selector == IModularAccount.executeWithAuthorization.selector
|| selector == IModularAccount.executeWithRuntimeValidation.selector
|| selector == IModularAccount.accountId.selector
// check against IERC165 methods
|| selector == IERC165.supportsInterface.selector
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IModularAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ interface IModularAccount {
/// @param data The calldata to send to the account.
/// @param authorization The authorization data to use for the call. The first 24 bytes specifies which runtime
/// validation to use, and the rest is sent as a parameter to runtime validation.
function executeWithAuthorization(bytes calldata data, bytes calldata authorization)
function executeWithRuntimeValidation(bytes calldata data, bytes calldata authorization)
external
payable
returns (bytes memory);
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/IValidationHookModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface IValidationHookModule is IModule {
/// @param sender The caller address.
/// @param value The call value.
/// @param data The calldata sent.
/// @param authorization Additional data for the hook to use.
function preRuntimeValidationHook(
uint32 entityId,
address sender,
Expand Down
14 changes: 8 additions & 6 deletions standard/ERCs/erc-6900.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ interface IModularAccount {
/// @param data The calldata to send to the account.
/// @param authorization The authorization data to use for the call. The first 24 bytes specifies which runtime
/// validation to use, and the rest is sent as a parameter to runtime validation.
function executeWithAuthorization(bytes calldata data, bytes calldata authorization)
function executeWithRuntimeValidation(bytes calldata data, bytes calldata authorization)
external
payable
returns (bytes memory);
Expand Down Expand Up @@ -372,6 +372,7 @@ interface IValidationHookModule is IModule {
/// @param sender The caller address.
/// @param value The call value.
/// @param data The calldata sent.
/// @param authorization Additional data for the hook to use.
function preRuntimeValidationHook(
uint32 entityId,
address sender,
Expand Down Expand Up @@ -517,18 +518,19 @@ During execution uninstallation, the account MUST correctly clear flags and othe
#### Execution Hooks Data Format

For accounts that implement execution hooks, accounts MUST conform to these execution hook formats:

1. For `executeUserOp` calls, for execution hooks associated with a validation function, accounts MUST send the full `msg.data`, including the `executeUserOp` selector.
2. For `executeUserOp` calls, for execution hooks associated with a selector, accounts MUST send `PackedUserOperation.callData` for `executeUserOp` calls, excluding `executeUserOp.selector` and the rest of the `PackedUserOperation`.
3. For `executeWithAuthorization` calls, for all execution hooks, accounts MUST send the inner `data`.
4. For all other calls, for execution hooks associated with a selector, accounts MUST send over `msg.data`.
3. For `executeWithRuntimeValidation` calls, for all execution hooks, accounts MUST send the inner `data`.
4. For all other calls, for execution hooks associated with a selector, accounts MUST send over `msg.data`.

#### Hook Execution Order

It is RECOMMENDED that an account implementer runs hooks in first installed first executed order. However, an account MAY implement a different execution order.

### Validation Call Flow

Modular accounts support three different calls flows for validation: user op validation, runtime validation, and signature validation. User op validation happens within the account's implementation of the function `validateUserOp`, defined in the ERC-4337 interface `IAccount`. Runtime validation happens through the dispatcher function `executeWithAuthorization`, or when using direct call validation. Signature validation happens within the account's implementation of the function `isValidSignature`, defined in ERC-1271.
Modular accounts support three different calls flows for validation: user op validation, runtime validation, and signature validation. User op validation happens within the account's implementation of the function `validateUserOp`, defined in the ERC-4337 interface `IAccount`. Runtime validation happens through the dispatcher function `executeWithRuntimeValidation`, or when using direct call validation. Signature validation happens within the account's implementation of the function `isValidSignature`, defined in ERC-1271.

For each of these validation types, an account implementation MAY specify its own format for selecting which validation function to use, as well as any per-hook data for validation hooks.

Expand All @@ -552,13 +554,13 @@ Installed validations have two flag variables indicating what they may be used f

#### Direct Call Validation

If a validation function is installed with the entity ID of `0xffffffff`, it may be used as direct call validation. This occurs when a module or other address calls a function on the modular account, without wrapping its call in the dispatcher function `executeWithAuthorization` to use as a selection mechanism for a runtime validation function.
If a validation function is installed with the entity ID of `0xffffffff`, it may be used as direct call validation. This occurs when a module or other address calls a function on the modular account, without wrapping its call in the dispatcher function `executeWithRuntimeValidation` to use as a selection mechanism for a runtime validation function.

To implement direct call validation, the modular account MUST treat direct function calls that are not from the modular account itself or the EntryPoint as an attempt to validate using the caller's address and the entity ID of `0xffffffff`. If such a validation function is installed, and applies to the function intended to be called, the modular account MUST allow it to continue, without performing runtime validation. Any pre validation hooks and execution hooks installed to this validation function MUST still run.

### Execution Call Flow

For all non-view functions within `IModularAccount` except `executeWithAuthorization`, all module-defined execution functions, and any additional native functions that the modular account MAY wish to include, the modular account MUST adhere to these steps during execution:
For all non-view functions within `IModularAccount` except `executeWithRuntimeValidation`, all module-defined execution functions, and any additional native functions that the modular account MAY wish to include, the modular account MUST adhere to these steps during execution:

If the caller is not the EntryPoint or the account, the account MUST check access control for direct call validation.

Expand Down
6 changes: 3 additions & 3 deletions test/account/AccountReturnData.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ contract AccountReturnDataTest is AccountTestBase {

// Tests the ability to read the results of contracts called via IModularAccount.execute
function test_returnData_singular_execute() public {
bytes memory returnData = account1.executeWithAuthorization(
bytes memory returnData = account1.executeWithRuntimeValidation(
abi.encodeCall(
account1.execute,
(address(regularResultContract), 0, abi.encodeCall(RegularResultContract.foo, ()))
Expand All @@ -89,7 +89,7 @@ contract AccountReturnDataTest is AccountTestBase {
data: abi.encodeCall(RegularResultContract.bar, ())
});

bytes memory retData = account1.executeWithAuthorization(
bytes memory retData = account1.executeWithRuntimeValidation(
abi.encodeCall(account1.executeBatch, (calls)),
_encodeSignature(_signerValidation, GLOBAL_VALIDATION, "")
);
Expand All @@ -110,7 +110,7 @@ contract AccountReturnDataTest is AccountTestBase {
assertTrue(result);
}

// Tests the ability to read data via executeWithAuthorization
// Tests the ability to read data via executeWithRuntimeValidation
function test_returnData_authorized_exec() public {
bool result = ResultConsumerModule(address(account1)).checkResultExecuteWithAuthorization(
address(regularResultContract), keccak256("bar")
Expand Down
2 changes: 1 addition & 1 deletion test/account/GlobalValidationTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ contract GlobalValidationTest is AccountTestBase {
factory.createAccount(owner2, 0);

vm.prank(owner2);
account2.executeWithAuthorization(
account2.executeWithRuntimeValidation(
abi.encodeCall(ReferenceModularAccount.execute, (ethRecipient, 1 wei, "")),
_encodeSignature(_signerValidation, GLOBAL_VALIDATION, "")
);
Expand Down
4 changes: 2 additions & 2 deletions test/account/MultiValidation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@ contract MultiValidationTest is AccountTestBase {
abi.encodeWithSignature("NotAuthorized()")
)
);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(IModularAccount.execute, (address(0), 0, "")),
_encodeSignature(
ModuleEntityLib.pack(address(validator2), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, ""
)
);

vm.prank(owner2);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(IModularAccount.execute, (address(0), 0, "")),
_encodeSignature(
ModuleEntityLib.pack(address(validator2), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, ""
Expand Down
18 changes: 9 additions & 9 deletions test/account/PerHookData.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ contract PerHookDataTest is CustomValidationTestBase {
preValidationHookData[0] = PreValidationHookData({index: 0, validationData: abi.encodePacked(_counter)});

vm.prank(owner1);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(
ReferenceModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ()))
),
Expand All @@ -307,7 +307,7 @@ contract PerHookDataTest is CustomValidationTestBase {
abi.encodeWithSignature("Error(string)", "Proof doesn't match target")
)
);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(
ReferenceModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ()))
),
Expand All @@ -325,7 +325,7 @@ contract PerHookDataTest is CustomValidationTestBase {
abi.encodeWithSignature("Error(string)", "Proof doesn't match target")
)
);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(
ReferenceModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ()))
),
Expand All @@ -342,7 +342,7 @@ contract PerHookDataTest is CustomValidationTestBase {
vm.expectRevert(
abi.encodeWithSelector(SparseCalldataSegmentLib.ValidationSignatureSegmentMissing.selector)
);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(
ReferenceModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ()))
),
Expand All @@ -360,7 +360,7 @@ contract PerHookDataTest is CustomValidationTestBase {
preValidationHookData[1] = PreValidationHookData({index: 1, validationData: abi.encodePacked(_counter)});

vm.prank(owner1);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(
ReferenceModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ()))
),
Expand All @@ -379,7 +379,7 @@ contract PerHookDataTest is CustomValidationTestBase {

vm.prank(owner1);
vm.expectRevert(abi.encodeWithSelector(SparseCalldataSegmentLib.SegmentOutOfOrder.selector));
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(
ReferenceModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ()))
),
Expand All @@ -400,7 +400,7 @@ contract PerHookDataTest is CustomValidationTestBase {
abi.encodeWithSignature("Error(string)", "Target not allowed")
)
);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(ReferenceModularAccount.execute, (beneficiary, 1 wei, "")),
_encodeSignature(_signerValidation, GLOBAL_VALIDATION, preValidationHookData, "")
);
Expand All @@ -412,7 +412,7 @@ contract PerHookDataTest is CustomValidationTestBase {

vm.prank(owner1);
vm.expectRevert(abi.encodeWithSelector(SparseCalldataSegmentLib.NonCanonicalEncoding.selector));
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(
ReferenceModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ()))
),
Expand All @@ -426,7 +426,7 @@ contract PerHookDataTest is CustomValidationTestBase {

vm.prank(owner1);
vm.expectRevert(abi.encodeWithSelector(SparseCalldataSegmentLib.NonCanonicalEncoding.selector));
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(
ReferenceModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ()))
),
Expand Down
2 changes: 1 addition & 1 deletion test/account/ReferenceModularAccount.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ contract ReferenceModularAccountTest is AccountTestBase {

//show working rt validation
vm.startPrank(address(owner1));
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(ReferenceModularAccount.execute, (ethRecipient, 1 wei, "")),
_encodeSignature(
ModuleEntityLib.pack(address(singleSignerValidationModule), newEntityId), GLOBAL_VALIDATION, ""
Expand Down
10 changes: 5 additions & 5 deletions test/account/ReplaceModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ contract UpgradeModuleTest is AccountTestBase {
IModularAccount.installExecution, (address(moduleV2), moduleV2.executionManifest(), "")
)
});
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(account1.executeBatch, (calls)),
_encodeSignature(_signerValidation, GLOBAL_VALIDATION, "")
);
Expand Down Expand Up @@ -147,7 +147,7 @@ contract UpgradeModuleTest is AccountTestBase {
),
0
);
account1.executeWithAuthorization(callData, _encodeSignature(currModuleEntity, GLOBAL_VALIDATION, ""));
account1.executeWithRuntimeValidation(callData, _encodeSignature(currModuleEntity, GLOBAL_VALIDATION, ""));
assertEq(target.balance, sendAmount);

// upgrade module by batching uninstall + install calls
Expand Down Expand Up @@ -181,7 +181,7 @@ contract UpgradeModuleTest is AccountTestBase {
)
)
});
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(account1.executeBatch, (calls)),
_encodeSignature(_signerValidation, GLOBAL_VALIDATION, "")
);
Expand All @@ -193,7 +193,7 @@ contract UpgradeModuleTest is AccountTestBase {
abi.encode(IModularAccount.execute.selector)
)
);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(IModularAccount.execute, (target, sendAmount, "")),
_encodeSignature(currModuleEntity, GLOBAL_VALIDATION, "")
);
Expand All @@ -213,7 +213,7 @@ contract UpgradeModuleTest is AccountTestBase {
),
0
);
account1.executeWithAuthorization(callData, _encodeSignature(newModuleEntity, GLOBAL_VALIDATION, ""));
account1.executeWithRuntimeValidation(callData, _encodeSignature(newModuleEntity, GLOBAL_VALIDATION, ""));
assertEq(target.balance, 2 * sendAmount);
}
}
6 changes: 3 additions & 3 deletions test/account/SelfCallAuthorization.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ contract SelfCallAuthorizationTest is AccountTestBase {
calls[1] = Call(address(account1), 0, abi.encodeCall(ComprehensiveModule.foo, ()));

vm.expectCall(address(comprehensiveModule), abi.encodeCall(ComprehensiveModule.foo, ()), 2);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(IModularAccount.executeBatch, (calls)),
_encodeSignature(comprehensiveModuleValidation, SELECTOR_ASSOCIATED_VALIDATION, "")
);
Expand Down Expand Up @@ -286,7 +286,7 @@ contract SelfCallAuthorizationTest is AccountTestBase {
outerCalls[0] = Call(address(account1), 0, abi.encodeCall(IModularAccount.executeBatch, (innerCalls)));

vm.expectRevert(abi.encodeWithSelector(ReferenceModularAccount.SelfCallRecursionDepthExceeded.selector));
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(IModularAccount.executeBatch, (outerCalls)),
_encodeSignature(comprehensiveModuleValidation, SELECTOR_ASSOCIATED_VALIDATION, "")
);
Expand All @@ -300,7 +300,7 @@ contract SelfCallAuthorizationTest is AccountTestBase {
selectors[0] = IModularAccount.executeBatch.selector;

vm.prank(owner1);
account1.executeWithAuthorization(
account1.executeWithRuntimeValidation(
abi.encodeCall(
ReferenceModularAccount.installValidation,
(
Expand Down
2 changes: 1 addition & 1 deletion test/mocks/modules/ReturnDataModuleMocks.sol
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ contract ResultConsumerModule is IExecutionModule, BaseModule, IValidationModule
// Check the return data through the execute with authorization case
function checkResultExecuteWithAuthorization(address target, bytes32 expected) external returns (bool) {
// This result should be allowed based on the manifest permission request
bytes memory returnData = IModularAccount(msg.sender).executeWithAuthorization(
bytes memory returnData = IModularAccount(msg.sender).executeWithRuntimeValidation(
abi.encodeCall(IModularAccount.execute, (target, 0, abi.encodeCall(RegularResultContract.foo, ()))),
abi.encodePacked(this, DIRECT_CALL_VALIDATION_ENTITYID, uint8(0), uint32(1), uint8(255)) // Validation
// function of self,
Expand Down
Loading

0 comments on commit 0bb2bfa

Please sign in to comment.