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] update SingleOwnerPlugin to use installValidation [1/2] #91

Merged
merged 1 commit into from
Jul 11, 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
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
Loading