Skip to content

Commit

Permalink
feat: [v0.8-develop] update SingleOwnerPlugin to use installValidatio…
Browse files Browse the repository at this point in the history
…n [1/2] (#91)
  • Loading branch information
adamegyed committed Jul 26, 2024
1 parent de140d0 commit b3f0092
Show file tree
Hide file tree
Showing 20 changed files with 368 additions and 350 deletions.
67 changes: 27 additions & 40 deletions src/account/PluginManager2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,35 @@ pragma solidity ^0.8.25;
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {IPlugin} from "../interfaces/IPlugin.sol";
import {FunctionReference} from "../interfaces/IPluginManager.sol";
import {FunctionReference, ValidationConfig} from "../interfaces/IPluginManager.sol";
import {FunctionReferenceLib} from "../helpers/FunctionReferenceLib.sol";
import {AccountStorage, getAccountStorage, toSetValue, toFunctionReference} from "./AccountStorage.sol";
import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol";
import {ValidationData, getAccountStorage, toSetValue, toFunctionReference} from "./AccountStorage.sol";
import {ExecutionHook} from "../interfaces/IAccountLoupe.sol";

// Temporary additional functions for a user-controlled install flow for validation functions.
abstract contract PluginManager2 {
using EnumerableSet for EnumerableSet.Bytes32Set;
using ValidationConfigLib for ValidationConfig;

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

error GlobalValidationAlreadySet(FunctionReference validationFunction);
error PreValidationAlreadySet(FunctionReference validationFunction, FunctionReference preValidationFunction);
error ValidationAlreadySet(bytes4 selector, FunctionReference validationFunction);
error ValidationNotSet(bytes4 selector, FunctionReference validationFunction);
error PermissionAlreadySet(FunctionReference validationFunction, ExecutionHook hook);
error PreValidationHookLimitExceeded();

function _installValidation(
FunctionReference validationFunction,
bool isGlobal,
ValidationConfig validationConfig,
bytes4[] memory selectors,
bytes calldata installData,
bytes memory preValidationHooks,
bytes memory permissionHooks
)
// TODO: flag for signature validation
internal
{
AccountStorage storage _storage = getAccountStorage();
) internal {
ValidationData storage _validationData =
getAccountStorage().validationData[validationConfig.functionReference()];

if (preValidationHooks.length > 0) {
(FunctionReference[] memory preValidationFunctions, bytes[] memory initDatas) =
Expand All @@ -43,7 +41,7 @@ abstract contract PluginManager2 {
for (uint256 i = 0; i < preValidationFunctions.length; ++i) {
FunctionReference preValidationFunction = preValidationFunctions[i];

_storage.validationData[validationFunction].preValidationHooks.push(preValidationFunction);
_validationData.preValidationHooks.push(preValidationFunction);

if (initDatas[i].length > 0) {
(address preValidationPlugin,) = FunctionReferenceLib.unpack(preValidationFunction);
Expand All @@ -52,10 +50,7 @@ abstract contract PluginManager2 {
}

// Avoid collision between reserved index and actual indices
if (
_storage.validationData[validationFunction].preValidationHooks.length
> _RESERVED_VALIDATION_DATA_INDEX
) {
if (_validationData.preValidationHooks.length > _RESERVED_VALIDATION_DATA_INDEX) {
revert PreValidationHookLimitExceeded();
}
}
Expand All @@ -67,10 +62,8 @@ abstract contract PluginManager2 {
for (uint256 i = 0; i < permissionFunctions.length; ++i) {
ExecutionHook memory permissionFunction = permissionFunctions[i];

if (
!_storage.validationData[validationFunction].permissionHooks.add(toSetValue(permissionFunction))
) {
revert PermissionAlreadySet(validationFunction, permissionFunction);
if (!_validationData.permissionHooks.add(toSetValue(permissionFunction))) {
revert PermissionAlreadySet(validationConfig.functionReference(), permissionFunction);
}

if (initDatas[i].length > 0) {
Expand All @@ -80,22 +73,18 @@ abstract contract PluginManager2 {
}
}

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

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

if (installData.length > 0) {
(address plugin,) = FunctionReferenceLib.unpack(validationFunction);
address plugin = validationConfig.plugin();
IPlugin(plugin).onInstall(installData);
}
}
Expand All @@ -106,33 +95,31 @@ abstract contract PluginManager2 {
bytes calldata preValidationHookUninstallData,
bytes calldata permissionHookUninstallData
) internal {
AccountStorage storage _storage = getAccountStorage();
ValidationData storage _validationData = getAccountStorage().validationData[validationFunction];

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

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

// Clear pre validation hooks
FunctionReference[] storage preValidationHooks =
_storage.validationData[validationFunction].preValidationHooks;
FunctionReference[] storage preValidationHooks = _validationData.preValidationHooks;
for (uint256 i = 0; i < preValidationHooks.length; ++i) {
FunctionReference preValidationFunction = preValidationHooks[i];
if (preValidationHookUninstallDatas[0].length > 0) {
(address preValidationPlugin,) = FunctionReferenceLib.unpack(preValidationFunction);
IPlugin(preValidationPlugin).onUninstall(preValidationHookUninstallDatas[0]);
}
}
delete _storage.validationData[validationFunction].preValidationHooks;
delete _validationData.preValidationHooks;
}

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

// Clear permission hooks
EnumerableSet.Bytes32Set storage permissionHooks =
_storage.validationData[validationFunction].permissionHooks;
EnumerableSet.Bytes32Set storage permissionHooks = _validationData.permissionHooks;
uint256 i = 0;
while (permissionHooks.length() > 0) {
FunctionReference permissionHook = toFunctionReference(permissionHooks.at(0));
Expand All @@ -141,12 +128,12 @@ abstract contract PluginManager2 {
IPlugin(permissionHookPlugin).onUninstall(permissionHookUninstallDatas[i++]);
}
}
delete _storage.validationData[validationFunction].preValidationHooks;
delete _validationData.preValidationHooks;

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

if (uninstallData.length > 0) {
Expand Down
18 changes: 7 additions & 11 deletions src/account/UpgradeableModularAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {FunctionReferenceLib} from "../helpers/FunctionReferenceLib.sol";
import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol";
import {SparseCalldataSegmentLib} from "../helpers/SparseCalldataSegmentLib.sol";
import {_coalescePreValidation, _coalesceValidation} from "../helpers/ValidationDataHelpers.sol";
import {IPlugin, PluginManifest} from "../interfaces/IPlugin.sol";
import {IValidation} from "../interfaces/IValidation.sol";
import {IValidationHook} from "../interfaces/IValidationHook.sol";
import {IExecutionHook} from "../interfaces/IExecutionHook.sol";
import {FunctionReference, IPluginManager} from "../interfaces/IPluginManager.sol";
import {FunctionReference, IPluginManager, ValidationConfig} from "../interfaces/IPluginManager.sol";
import {IStandardExecutor, Call} from "../interfaces/IStandardExecutor.sol";
import {AccountExecutor} from "./AccountExecutor.sol";
import {AccountLoupe} from "./AccountLoupe.sol";
Expand All @@ -41,6 +42,7 @@ contract UpgradeableModularAccount is
{
using EnumerableSet for EnumerableSet.Bytes32Set;
using FunctionReferenceLib for FunctionReference;
using ValidationConfigLib for ValidationConfig;
using SparseCalldataSegmentLib for bytes;

struct PostExecToRun {
Expand Down Expand Up @@ -274,32 +276,26 @@ contract UpgradeableModularAccount is
/// with user install configs.
/// @dev This function is only callable once, and only by the EntryPoint.
function initializeWithValidation(
FunctionReference validationFunction,
bool isGlobal,
ValidationConfig validationConfig,
bytes4[] memory selectors,
bytes calldata installData,
bytes calldata preValidationHooks,
bytes calldata permissionHooks
) external initializer {
_installValidation(
validationFunction, isGlobal, selectors, installData, preValidationHooks, permissionHooks
);
_installValidation(validationConfig, selectors, installData, preValidationHooks, permissionHooks);
emit ModularAccountInitialized(_ENTRY_POINT);
}

/// @inheritdoc IPluginManager
/// @notice May be validated by a global validation.
function installValidation(
FunctionReference validationFunction,
bool isGlobal,
ValidationConfig validationConfig,
bytes4[] memory selectors,
bytes calldata installData,
bytes calldata preValidationHooks,
bytes calldata permissionHooks
) external wrapNativeFunction {
_installValidation(
validationFunction, isGlobal, selectors, installData, preValidationHooks, permissionHooks
);
_installValidation(validationConfig, selectors, installData, preValidationHooks, permissionHooks);
}

/// @inheritdoc IPluginManager
Expand Down
92 changes: 92 additions & 0 deletions src/helpers/ValidationConfigLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

import {FunctionReference, ValidationConfig} from "../interfaces/IPluginManager.sol";

// Validation config is a packed representation of a validation function and flags for its configuration.
// Layout:
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________________ // Address
// 0x________________________________________BB______________________ // Function ID
// 0x__________________________________________CC____________________ // isGlobal
// 0x____________________________________________DD__________________ // isSignatureValidation
// 0x______________________________________________000000000000000000 // unused

library ValidationConfigLib {
function pack(FunctionReference _validationFunction, bool _isGlobal, bool _isSignatureValidation)
internal
pure
returns (ValidationConfig)
{
return ValidationConfig.wrap(
bytes23(
bytes23(FunctionReference.unwrap(_validationFunction))
// isGlobal flag stored in the 22nd byte
| bytes23(bytes32(_isGlobal ? uint256(1) << 80 : 0))
// isSignatureValidation flag stored in the 23rd byte
| bytes23(bytes32(_isSignatureValidation ? uint256(1) << 72 : 0))
)
);
}

function pack(address _plugin, uint8 _functionId, bool _isGlobal, bool _isSignatureValidation)
internal
pure
returns (ValidationConfig)
{
return ValidationConfig.wrap(
bytes23(
// plugin address stored in the first 20 bytes
bytes23(bytes20(_plugin))
// functionId stored in the 21st byte
| bytes23(bytes32(uint256(_functionId) << 168))
// isGlobal flag stored in the 22nd byte
| bytes23(bytes32(_isGlobal ? uint256(1) << 80 : 0))
// isSignatureValidation flag stored in the 23rd byte
| bytes23(bytes32(_isSignatureValidation ? uint256(1) << 72 : 0))
)
);
}

function unpackUnderlying(ValidationConfig config)
internal
pure
returns (address _plugin, uint8 _functionId, bool _isGlobal, bool _isSignatureValidation)
{
bytes23 configBytes = ValidationConfig.unwrap(config);
_plugin = address(bytes20(configBytes));
_functionId = uint8(configBytes[20]);
_isGlobal = uint8(configBytes[21]) == 1;
_isSignatureValidation = uint8(configBytes[22]) == 1;
}

function unpack(ValidationConfig config)
internal
pure
returns (FunctionReference _validationFunction, bool _isGlobal, bool _isSignatureValidation)
{
bytes23 configBytes = ValidationConfig.unwrap(config);
_validationFunction = FunctionReference.wrap(bytes21(configBytes));
_isGlobal = uint8(configBytes[21]) == 1;
_isSignatureValidation = uint8(configBytes[22]) == 1;
}

function plugin(ValidationConfig config) internal pure returns (address) {
return address(bytes20(ValidationConfig.unwrap(config)));
}

function functionId(ValidationConfig config) internal pure returns (uint8) {
return uint8(ValidationConfig.unwrap(config)[20]);
}

function functionReference(ValidationConfig config) internal pure returns (FunctionReference) {
return FunctionReference.wrap(bytes21(ValidationConfig.unwrap(config)));
}

function isGlobal(ValidationConfig config) internal pure returns (bool) {
return uint8(ValidationConfig.unwrap(config)[21]) == 1;
}

function isSignatureValidation(ValidationConfig config) internal pure returns (bool) {
return uint8(ValidationConfig.unwrap(config)[22]) == 1;
}
}
8 changes: 4 additions & 4 deletions src/interfaces/IPluginManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ pragma solidity ^0.8.25;

type FunctionReference is bytes21;

type ValidationConfig is bytes23;

interface IPluginManager {
event PluginInstalled(address indexed plugin, bytes32 manifestHash);

Expand All @@ -21,15 +23,13 @@ interface IPluginManager {
/// validation.
/// TODO: remove or update.
/// @dev This does not validate anything against the manifest - the caller must ensure validity.
/// @param validationFunction The validation function to install.
/// @param isGlobal Whether the validation function applies for all selectors in the global pool.
/// @param validationConfig The validation function to install, along with configuration flags.
/// @param selectors The selectors to install the validation function for.
/// @param installData Optional data to be decoded and used by the plugin to setup initial plugin state.
/// @param preValidationHooks Optional pre-validation hooks to install for the validation function.
/// @param permissionHooks Optional permission hooks to install for the validation function.
function installValidation(
FunctionReference validationFunction,
bool isGlobal,
ValidationConfig validationConfig,
bytes4[] memory selectors,
bytes calldata installData,
bytes calldata preValidationHooks,
Expand Down
36 changes: 0 additions & 36 deletions src/plugins/owner/ISingleOwnerPlugin.sol

This file was deleted.

Loading

0 comments on commit b3f0092

Please sign in to comment.