Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ERC-6900 account extension #80

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

jeremyjams
Copy link

@jeremyjams jeremyjams commented Feb 10, 2025

In progress works:

  • installValidation
  • uninstallValidation
  • installExecution
  • uninstallExecution
  • validateUserOp
  • rawSignatureValidation
  • execute
  • executeBatch
  • executeWithRuntimeValidation
  • fallback

@jeremyjams jeremyjams requested a review from a team as a code owner February 10, 2025 17:34
@Amxx Amxx requested review from arr00 and Amxx February 10, 2025 17:34
package.json Outdated Show resolved Hide resolved
* @dev Extracts the validator from the user operation.
*
*/
function _extractUserOpValidator(PackedUserOperation calldata userOp) internal pure virtual returns (ModuleEntity) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_extractSignatureValidator already does that. Maybe we can drop this function, and only use _extractSignatureValidator

const hooks = [ethers.hexlify(ethers.randomBytes(32))];
await expect(this.mockFromEntrypoint.installValidation(validationConfig, selectors, installData, hooks))
.to.be.revertedWithCustomError(this.mock, 'ERC6900ModuleInterfaceNotSupported')
.withArgs(moduleId, '0x46c0c1b4');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this value should be defined as a constant

Comment on lines 176 to 194
if (_validationFlags.isGlobalValidation()) {
Execution storage execution = _executions[executionSelector];
// The account MUST consider the validation applicable to any module
// execution function with the allowGlobalValidation flag set to true
require(
!execution.allowGlobalValidation,
ERC6900Utils.ERC6900ExecutionSelectorNotAllowedForGlobalValidation(
validationModuleEntity,
executionSelector
)
);
} else {
// validation functions have a configurable range of applicability.
// This can be configured with selectors installed to a validation
require(
!validation.selectors.contains(executionSelector),
ERC6900Utils.ERC6900MissingValidationForSelector(executionSelector)
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the ERC is somewhat ambiguous to me. Should a validation that has the isGlobal flag be able to also specify selectors for which it is valid? Which approach do you think is better?

Note: if we do go forward with this interpretation, maybe we should disallow adding validation selectors when the isGlobal flag is true? https://github.com/jeremyjams/openzeppelin-community-contracts/blob/a0c58324195ab1bff1f4b8d8d9c4e655c6a8ec8d/contracts/account/extensions/AccountERC6900.sol#L280-L285

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, about your question "Should a validation that has the isGlobal flag be able to also specify selectors for which it is valid?"

We can see from the ERC:

  1. The standard supports a isGlobalValidation flag for validation functions, which means that this function is added to a global validation pool and can validate any of the execution functions that expose themselves to global validation via allowGlobalValidation flag.

  2. validation functions [...] can be configured with selectors installed to a validation

From than I understand that:

  1. Any execution function being declared as allowed for global validation can be validated by a validation set as global
  2. and Any execution function can be validated by a validation having the execution selector installed

From that I understand that a same validation could validate both rules, then I feel we should not "disallow adding validation selectors when the isGlobal flag is true". What do think, do you still stick to your first impression?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could go either way. The reference impl actually does something similar to what we have here already (either global or selector based, not both). Go with whatever you think makes more sense for how validation functions will be utilized.

Comment on lines +155 to +156
* if the module specified by the first 20 bytes of the nonce key is installed. Falls back to
* {AccountCore-_validateUserOp} otherwise.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the fallback currently implemented?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, indeed fallback was not implemented at all, please see Init fallback now.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I was referring to the fallback to super mentioned in _validateUserOp. Sorry for the confusion.

Comment on lines +219 to +222
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given #80 (comment) it seems like super is not being called, as such, when is this function utilized?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AccountERC6900 being an ERC7739, this function is at least called from there, what do you think?

function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4 result) {

https://eips.ethereum.org/EIPS/eip-6900#validation-call-flow

Signature validation happens within the account’s implementation of the function isValidSignature, defined in ERC-1271.

Copy link
Collaborator

@Amxx Amxx Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arr00 mentioned to me that ERC-6900 requires 1271 to be implemented using validation modules, and that having ERC-7739 + _rawSignatureValidation is not the right approach.

However, having a quick look at ERC-6900 and I am not seeing that. @arr00, could you please direct me to the relevant part ?

Edit: possible missunderstanding

Copy link
Collaborator

@Amxx Amxx Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: considering the signature validation mechanism (calling validateSignature on a module with address(this) as one of the arguments), I'm not convinced we want to use 7739 in the case of an ERC6900 account.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the paragraph on the validation call flow. https://eips.ethereum.org/EIPS/eip-6900#validation-call-flow

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I was a little confused here. I think the callflow is sound. That said, do you think it might make more sense to implement ERC7739 on the module side as opposed to the account side?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"not convinced we want to use 7739 in the case of an ERC6900 account"
I think I get your point, preventing signature replays should be the responsibility of the validator module. What you suggest is aligned with related reference validator module.

Please see new "Expose ERC-1271 signature validation" commit.

Comment on lines 274 to 277
require(
ERC165Checker.supportsInterface(module, type(IERC6900Module).interfaceId),
ERC6900Utils.ERC6900ModuleInterfaceNotSupported(module, type(IERC6900Module).interfaceId)
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't always call supportsInterface since the module may be an EOA (or other non-module) used for direct validation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to understand, can a module be an EOA?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the paragraph on direct call validation in the ERC. Yes, you can install a validation function with the module being an EOA.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey all, I wanted to mention here that the ERC-6900 spec does not require that this check happens onchain, and we realized that having this as non-standard behavior in the reference implementation is probably confusing. So, we've decided to remove it from the RI here: erc6900/reference-implementation#214

Per our discussion with @Amxx and @ernestognw, we're still requiring this to be implemented by modules, and moving the responsibility of checking to be off-chain (i.e. by wallets) prior to installing a module.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @adamegyed

bytes4[] calldata selectors,
bytes calldata installData,
bytes[] calldata hooks
) public virtual onlyEntryPointOrSelf {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onlyEntryPointOrSelf doesn't appear to be the right modifier to use here, at least according to the reference implementation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants