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

feat: [v0.8-develop] merge plugin manager contracts 2/N #102

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 0 additions & 148 deletions src/account/ModuleManager2.sol

This file was deleted.

181 changes: 156 additions & 25 deletions src/account/ModuleManagerInternals.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,35 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet

import {KnownSelectors} from "../helpers/KnownSelectors.sol";
import {ModuleEntityLib} from "../helpers/ModuleEntityLib.sol";
import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol";
import {ExecutionHook} from "../interfaces/IAccountLoupe.sol";
import {IModule, ManifestExecutionHook, ManifestValidation, ModuleManifest} from "../interfaces/IModule.sol";
import {IModuleManager, ModuleEntity} from "../interfaces/IModuleManager.sol";
import {AccountStorage, SelectorData, getAccountStorage, toSetValue} from "./AccountStorage.sol";
import {IModuleManager, ModuleEntity, ValidationConfig} from "../interfaces/IModuleManager.sol";
import {AccountStorage, SelectorData, ValidationData, getAccountStorage, toSetValue} from "./AccountStorage.sol";

abstract contract ModuleManagerInternals is IModuleManager {
using EnumerableSet for EnumerableSet.Bytes32Set;
using ModuleEntityLib for ModuleEntity;
using ValidationConfigLib for ValidationConfig;

// Index marking the start of the data for the validation function.
uint8 internal constant _RESERVED_VALIDATION_DATA_INDEX = 255;

// Magic value for the Entity ID of direct call validation.
uint32 internal constant _SELF_PERMIT_VALIDATION_FUNCTIONID = type(uint32).max;

error ArrayLengthMismatch();
error Erc4337FunctionNotAllowed(bytes4 selector);
error ExecutionFunctionAlreadySet(bytes4 selector);
error IModuleFunctionNotAllowed(bytes4 selector);
error NativeFunctionNotAllowed(bytes4 selector);
error NullModule();
error PermissionAlreadySet(ModuleEntity validationFunction, ExecutionHook hook);
error ModuleInstallCallbackFailed(address module, bytes revertReason);
error ModuleInterfaceNotSupported(address module);
error ModuleNotInstalled(address module);
error ValidationFunctionAlreadySet(bytes4 selector, ModuleEntity validationFunction);
error PreValidationHookLimitExceeded();
error ValidationAlreadySet(bytes4 selector, ModuleEntity validationFunction);

// Storage update operations

Expand Down Expand Up @@ -71,39 +81,35 @@ abstract contract ModuleManagerInternals is IModuleManager {
_selectorData.allowGlobalValidation = false;
}

function _addValidationFunction(address module, ManifestValidation memory mv) internal {
AccountStorage storage _storage = getAccountStorage();
function _addValidationFunction(ValidationConfig validationConfig, bytes4[] memory selectors) internal {
ValidationData storage _validationData =
getAccountStorage().validationData[validationConfig.moduleEntity()];

ModuleEntity validationFunction = ModuleEntityLib.pack(module, mv.entityId);

if (mv.isDefault) {
_storage.validationData[validationFunction].isGlobal = true;
if (validationConfig.isGlobal()) {
_validationData.isGlobal = true;
}

if (mv.isSignatureValidation) {
_storage.validationData[validationFunction].isSignatureValidation = true;
if (validationConfig.isSignatureValidation()) {
_validationData.isSignatureValidation = true;
}

// Add the validation function to the selectors.
uint256 length = mv.selectors.length;
uint256 length = selectors.length;
for (uint256 i = 0; i < length; ++i) {
bytes4 selector = mv.selectors[i];
_storage.validationData[validationFunction].selectors.add(toSetValue(selector));
_validationData.selectors.add(toSetValue(selectors[i]));
}
}

function _removeValidationFunction(address module, ManifestValidation memory mv) internal {
AccountStorage storage _storage = getAccountStorage();

ModuleEntity validationFunction = ModuleEntityLib.pack(module, mv.entityId);
function _removeValidationFunction(ModuleEntity validationFunction) internal {
ValidationData storage _validationData = getAccountStorage().validationData[validationFunction];

_storage.validationData[validationFunction].isGlobal = false;
_storage.validationData[validationFunction].isSignatureValidation = false;
_validationData.isGlobal = false;
_validationData.isSignatureValidation = false;

// Clear the selectors
while (_storage.validationData[validationFunction].selectors.length() > 0) {
bytes32 selector = _storage.validationData[validationFunction].selectors.at(0);
_storage.validationData[validationFunction].selectors.remove(selector);
uint256 length = _validationData.selectors.length();
for (uint256 i = 0; i < length; ++i) {
_validationData.selectors.remove(_validationData.selectors.at(0));
}
}

Expand Down Expand Up @@ -161,7 +167,11 @@ abstract contract ModuleManagerInternals is IModuleManager {
for (uint256 i = 0; i < length; ++i) {
// Todo: limit this to only "direct runtime call" validation path (old EFP),
// and add a way for the user to specify permission/pre-val hooks here.
_addValidationFunction(module, manifest.validationFunctions[i]);
ManifestValidation memory mv = manifest.validationFunctions[i];

ValidationConfig validationConfig =
ValidationConfigLib.pack(module, mv.entityId, mv.isGlobal, mv.isSignatureValidation);
_addValidationFunction(validationConfig, mv.selectors);
}

length = manifest.executionHooks.length;
Expand Down Expand Up @@ -204,7 +214,9 @@ abstract contract ModuleManagerInternals is IModuleManager {

length = manifest.validationFunctions.length;
for (uint256 i = 0; i < length; ++i) {
_removeValidationFunction(module, manifest.validationFunctions[i]);
ModuleEntity validationFunction =
ModuleEntityLib.pack(module, manifest.validationFunctions[i].entityId);
_removeValidationFunction(validationFunction);
}

length = manifest.executionFunctions.length;
Expand All @@ -228,4 +240,123 @@ abstract contract ModuleManagerInternals is IModuleManager {

emit ModuleUninstalled(module, onUninstallSuccess);
}

function _installValidation(
ValidationConfig validationConfig,
bytes4[] memory selectors,
bytes calldata installData,
bytes memory preValidationHooks,
bytes memory permissionHooks
) internal {
ValidationData storage _validationData =
getAccountStorage().validationData[validationConfig.moduleEntity()];

if (preValidationHooks.length > 0) {
(ModuleEntity[] memory preValidationFunctions, bytes[] memory initDatas) =
abi.decode(preValidationHooks, (ModuleEntity[], bytes[]));

for (uint256 i = 0; i < preValidationFunctions.length; ++i) {
ModuleEntity preValidationFunction = preValidationFunctions[i];

_validationData.preValidationHooks.push(preValidationFunction);

if (initDatas[i].length > 0) {
(address preValidationPlugin,) = ModuleEntityLib.unpack(preValidationFunction);
IModule(preValidationPlugin).onInstall(initDatas[i]);
}
}

// Avoid collision between reserved index and actual indices
if (_validationData.preValidationHooks.length > _RESERVED_VALIDATION_DATA_INDEX) {
revert PreValidationHookLimitExceeded();
}
}

if (permissionHooks.length > 0) {
(ExecutionHook[] memory permissionFunctions, bytes[] memory initDatas) =
abi.decode(permissionHooks, (ExecutionHook[], bytes[]));

for (uint256 i = 0; i < permissionFunctions.length; ++i) {
ExecutionHook memory permissionFunction = permissionFunctions[i];

if (!_validationData.permissionHooks.add(toSetValue(permissionFunction))) {
revert PermissionAlreadySet(validationConfig.moduleEntity(), permissionFunction);
}

if (initDatas[i].length > 0) {
(address executionPlugin,) = ModuleEntityLib.unpack(permissionFunction.hookFunction);
IModule(executionPlugin).onInstall(initDatas[i]);
}
}
}

for (uint256 i = 0; i < selectors.length; ++i) {
bytes4 selector = selectors[i];
if (!_validationData.selectors.add(toSetValue(selector))) {
revert ValidationAlreadySet(selector, validationConfig.moduleEntity());
}
}

if (validationConfig.entityId() != _SELF_PERMIT_VALIDATION_FUNCTIONID) {
// Only allow global validations and signature validations if they're not direct-call validations.

_validationData.isGlobal = validationConfig.isGlobal();
_validationData.isSignatureValidation = validationConfig.isSignatureValidation();
if (installData.length > 0) {
IModule(validationConfig.module()).onInstall(installData);
}
}
}

function _uninstallValidation(
ModuleEntity validationFunction,
bytes calldata uninstallData,
bytes calldata preValidationHookUninstallData,
bytes calldata permissionHookUninstallData
) internal {
ValidationData storage _validationData = getAccountStorage().validationData[validationFunction];

_removeValidationFunction(validationFunction);

{
bytes[] memory preValidationHookUninstallDatas = abi.decode(preValidationHookUninstallData, (bytes[]));

// Clear pre validation hooks
ModuleEntity[] storage preValidationHooks = _validationData.preValidationHooks;
for (uint256 i = 0; i < preValidationHooks.length; ++i) {
ModuleEntity preValidationFunction = preValidationHooks[i];
if (preValidationHookUninstallDatas[0].length > 0) {
(address preValidationPlugin,) = ModuleEntityLib.unpack(preValidationFunction);
IModule(preValidationPlugin).onUninstall(preValidationHookUninstallDatas[0]);
}
}
delete _validationData.preValidationHooks;
}

{
bytes[] memory permissionHookUninstallDatas = abi.decode(permissionHookUninstallData, (bytes[]));

// Clear permission hooks
EnumerableSet.Bytes32Set storage permissionHooks = _validationData.permissionHooks;
uint256 permissionHookLen = permissionHooks.length();
for (uint256 i = 0; i < permissionHookLen; ++i) {
bytes32 permissionHook = permissionHooks.at(0);
permissionHooks.remove(permissionHook);
address permissionHookPlugin = address(uint160(bytes20(permissionHook)));
IModule(permissionHookPlugin).onUninstall(permissionHookUninstallDatas[i]);
}
}

// Clear selectors
uint256 selectorLen = _validationData.selectors.length();
for (uint256 i = 0; i < selectorLen; ++i) {
bytes32 selectorSetValue = _validationData.selectors.at(0);
_validationData.selectors.remove(selectorSetValue);
}

if (uninstallData.length > 0) {
(address plugin,) = ModuleEntityLib.unpack(validationFunction);
IModule(plugin).onUninstall(uninstallData);
}
}
}
Loading
Loading