Skip to content

Commit

Permalink
feat: [v0.7.0] Cut permitted call hooks & injection (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-alchemy authored Jan 17, 2024
1 parent 8265ca0 commit 7846d38
Show file tree
Hide file tree
Showing 22 changed files with 770 additions and 2,402 deletions.
18 changes: 1 addition & 17 deletions src/account/AccountLoupe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeab
import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {
AccountStorage,
getAccountStorage,
getPermittedCallKey,
HookGroup,
toFunctionReferenceArray
} from "./AccountStorage.sol";
import {AccountStorage, getAccountStorage, HookGroup, toFunctionReferenceArray} from "./AccountStorage.sol";
import {FunctionReference} from "../helpers/FunctionReferenceLib.sol";
import {IAccountLoupe} from "../interfaces/IAccountLoupe.sol";
import {IPluginManager} from "../interfaces/IPluginManager.sol";
Expand Down Expand Up @@ -53,16 +47,6 @@ abstract contract AccountLoupe is IAccountLoupe {
execHooks = _getHooks(getAccountStorage().selectorData[selector].executionHooks);
}

/// @inheritdoc IAccountLoupe
function getPermittedCallHooks(address callingPlugin, bytes4 selector)
external
view
returns (ExecutionHooks[] memory execHooks)
{
bytes24 key = getPermittedCallKey(callingPlugin, selector);
execHooks = _getHooks(getAccountStorage().permittedCalls[key].permittedCallHooks);
}

/// @inheritdoc IAccountLoupe
function getPreValidationHooks(bytes4 selector)
external
Expand Down
17 changes: 1 addition & 16 deletions src/account/AccountStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,12 @@ struct PluginData {
FunctionReference[] dependencies;
// Tracks the number of times this plugin has been used as a dependency function
uint256 dependentCount;
StoredInjectedHook[] injectedHooks;
}

// A version of IPluginManager.InjectedHook used to track injected hooks in storage.
// Omits the hookApplyData field, which is not needed for storage, and flattens the struct.
struct StoredInjectedHook {
// The plugin that provides the hook
address providingPlugin;
// Either a plugin-defined execution function, or the native function executeFromPluginExternal
bytes4 selector;
// Contents of the InjectedHooksInfo struct
uint8 preExecHookFunctionId;
bool isPostHookUsed;
uint8 postExecHookFunctionId;
}

// Represents data associated with a plugin's permission to use `executeFromPlugin`
// to interact with another plugin installed on the account.
struct PermittedCallData {
bool callPermitted;
HookGroup permittedCallHooks;
}

// Represents data associated with a plugin's permission to use `executeFromPluginExternal`
Expand All @@ -52,7 +37,7 @@ struct PermittedExternalCallData {
mapping(bytes4 => bool) permittedSelectors;
}

// Represets a set of pre- and post- hooks. Used to store both execution hooks and permitted call hooks.
// Represets a set of pre- and post- hooks.
struct HookGroup {
EnumerableMap.Bytes32ToUintMap preHooks;
// bytes21 key = pre hook function reference
Expand Down
197 changes: 6 additions & 191 deletions src/account/PluginManagerInternals.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ import {
AccountStorage,
getAccountStorage,
SelectorData,
PermittedCallData,
getPermittedCallKey,
HookGroup,
PermittedExternalCallData,
StoredInjectedHook
PermittedExternalCallData
} from "./AccountStorage.sol";
import {FunctionReference, FunctionReferenceLib} from "../helpers/FunctionReferenceLib.sol";
import {IPluginManager} from "../interfaces/IPluginManager.sol";
Expand Down Expand Up @@ -45,8 +43,6 @@ abstract contract PluginManagerInternals is IPluginManager {
error PluginNotInstalled(address plugin);
error RuntimeValidationFunctionAlreadySet(bytes4 selector, FunctionReference validationFunction);
error UserOpValidationFunctionAlreadySet(bytes4 selector, FunctionReference validationFunction);
error PluginApplyHookCallbackFailed(address providingPlugin, bytes revertReason);
error PluginUnapplyHookCallbackFailed(address providingPlugin, bytes revertReason);

modifier notNullFunction(FunctionReference functionReference) {
if (functionReference.isEmpty()) {
Expand Down Expand Up @@ -157,30 +153,6 @@ abstract contract PluginManagerInternals is IPluginManager {
accountStorage.permittedCalls[key].callPermitted = false;
}

function _addPermittedCallHooks(
bytes4 selector,
address plugin,
FunctionReference preExecHook,
FunctionReference postExecHook
) internal notNullPlugin(plugin) {
bytes24 permittedCallKey = getPermittedCallKey(plugin, selector);
PermittedCallData storage _permittedCalldata = getAccountStorage().permittedCalls[permittedCallKey];

_addHooks(_permittedCalldata.permittedCallHooks, preExecHook, postExecHook);
}

function _removePermittedCallHooks(
bytes4 selector,
address plugin,
FunctionReference preExecHook,
FunctionReference postExecHook
) internal notNullPlugin(plugin) {
bytes24 permittedCallKey = getPermittedCallKey(plugin, selector);
PermittedCallData storage _permittedCallData = getAccountStorage().permittedCalls[permittedCallKey];

_removeHooks(_permittedCallData.permittedCallHooks, preExecHook, postExecHook);
}

function _addHooks(HookGroup storage hooks, FunctionReference preExecHook, FunctionReference postExecHook)
internal
{
Expand Down Expand Up @@ -263,8 +235,7 @@ abstract contract PluginManagerInternals is IPluginManager {
address plugin,
bytes32 manifestHash,
bytes memory pluginInitData,
FunctionReference[] memory dependencies,
InjectedHook[] memory injectedHooks
FunctionReference[] memory dependencies
) internal {
AccountStorage storage _storage = getAccountStorage();

Expand Down Expand Up @@ -376,44 +347,6 @@ abstract contract PluginManagerInternals is IPluginManager {
}
}

length = injectedHooks.length;
// manually set arr length
StoredInjectedHook[] storage optionalHooksLengthArr = _storage.pluginData[plugin].injectedHooks;
assembly ("memory-safe") {
sstore(optionalHooksLengthArr.slot, length)
}

for (uint256 i = 0; i < length;) {
InjectedHook memory hook = injectedHooks[i];
_storage.pluginData[plugin].injectedHooks[i] = StoredInjectedHook({
providingPlugin: hook.providingPlugin,
selector: hook.selector,
preExecHookFunctionId: hook.injectedHooksInfo.preExecHookFunctionId,
isPostHookUsed: hook.injectedHooksInfo.isPostHookUsed,
postExecHookFunctionId: hook.injectedHooksInfo.postExecHookFunctionId
});

// Increment the dependent count for the plugin providing the hook.
_storage.pluginData[hook.providingPlugin].dependentCount += 1;

if (!_storage.plugins.contains(hook.providingPlugin)) {
revert MissingPluginDependency(hook.providingPlugin);
}

_addPermittedCallHooks(
hook.selector,
plugin,
FunctionReferenceLib.pack(hook.providingPlugin, hook.injectedHooksInfo.preExecHookFunctionId),
hook.injectedHooksInfo.isPostHookUsed
? FunctionReferenceLib.pack(hook.providingPlugin, hook.injectedHooksInfo.postExecHookFunctionId)
: FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE
);

unchecked {
++i;
}
}

length = manifest.userOpValidationFunctions.length;
for (uint256 i = 0; i < length;) {
ManifestAssociatedFunction memory mv = manifest.userOpValidationFunctions[i];
Expand Down Expand Up @@ -500,30 +433,6 @@ abstract contract PluginManagerInternals is IPluginManager {
}
}

length = manifest.permittedCallHooks.length;
for (uint256 i = 0; i < length;) {
_addPermittedCallHooks(
manifest.permittedCallHooks[i].executionSelector,
plugin,
_resolveManifestFunction(
manifest.permittedCallHooks[i].preExecHook,
plugin,
dependencies,
ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY
),
_resolveManifestFunction(
manifest.permittedCallHooks[i].postExecHook,
plugin,
dependencies,
ManifestAssociatedFunctionType.NONE
)
);

unchecked {
++i;
}
}

length = manifest.interfaceIds.length;
for (uint256 i = 0; i < length;) {
_storage.supportedIfaces[manifest.interfaceIds[i]] += 1;
Expand All @@ -532,40 +441,19 @@ abstract contract PluginManagerInternals is IPluginManager {
}
}

// call onHookApply after all setup, but before calling plugin onInstall
length = injectedHooks.length;

for (uint256 i = 0; i < length;) {
InjectedHook memory hook = injectedHooks[i];
// not inlined in function call to avoid stack too deep error
bytes memory onHookApplyData = injectedHooks[i].hookApplyData;
/* solhint-disable no-empty-blocks */
try IPlugin(hook.providingPlugin).onHookApply(plugin, hook.injectedHooksInfo, onHookApplyData) {}
catch (bytes memory revertReason) {
revert PluginApplyHookCallbackFailed(hook.providingPlugin, revertReason);
}
/* solhint-enable no-empty-blocks */
unchecked {
++i;
}
}

// Initialize the plugin storage for the account.
// solhint-disable-next-line no-empty-blocks
try IPlugin(plugin).onInstall(pluginInitData) {}
catch (bytes memory revertReason) {
revert PluginInstallCallbackFailed(plugin, revertReason);
}

emit PluginInstalled(plugin, manifestHash, dependencies, injectedHooks);
emit PluginInstalled(plugin, manifestHash, dependencies);
}

function _uninstallPlugin(
address plugin,
PluginManifest memory manifest,
bytes memory uninstallData,
bytes[] calldata hookUnapplyData
) internal {
function _uninstallPlugin(address plugin, PluginManifest memory manifest, bytes memory uninstallData)
internal
{
AccountStorage storage _storage = getAccountStorage();

// Check if the plugin exists.
Expand Down Expand Up @@ -602,30 +490,6 @@ abstract contract PluginManagerInternals is IPluginManager {
// Remove components according to the manifest, in reverse order (by component type) of their installation.
// If any expected components are missing, revert.

length = manifest.permittedCallHooks.length;
for (uint256 i = 0; i < length;) {
_removePermittedCallHooks(
manifest.permittedCallHooks[i].executionSelector,
plugin,
_resolveManifestFunction(
manifest.permittedCallHooks[i].preExecHook,
plugin,
dependencies,
ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY
),
_resolveManifestFunction(
manifest.permittedCallHooks[i].postExecHook,
plugin,
dependencies,
ManifestAssociatedFunctionType.NONE
)
);

unchecked {
++i;
}
}

length = manifest.executionHooks.length;
for (uint256 i = 0; i < length;) {
ManifestExecutionHook memory mh = manifest.executionHooks[i];
Expand Down Expand Up @@ -749,27 +613,6 @@ abstract contract PluginManagerInternals is IPluginManager {
}
}

length = _storage.pluginData[plugin].injectedHooks.length;
for (uint256 i = 0; i < length;) {
StoredInjectedHook memory hook = _storage.pluginData[plugin].injectedHooks[i];

// Decrement the dependent count for the plugin providing the hook.
_storage.pluginData[hook.providingPlugin].dependentCount -= 1;

_removePermittedCallHooks(
hook.selector,
plugin,
FunctionReferenceLib.pack(hook.providingPlugin, hook.preExecHookFunctionId),
hook.isPostHookUsed
? FunctionReferenceLib.pack(hook.providingPlugin, hook.postExecHookFunctionId)
: FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE
);

unchecked {
++i;
}
}

length = manifest.permittedExecutionSelectors.length;
for (uint256 i = 0; i < length;) {
_disableExecFromPlugin(manifest.permittedExecutionSelectors[i], plugin, _storage);
Expand All @@ -796,34 +639,6 @@ abstract contract PluginManagerInternals is IPluginManager {
}
}

length = _storage.pluginData[plugin].injectedHooks.length;
bool hasUnapplyHookData = hookUnapplyData.length != 0;
if (hasUnapplyHookData && hookUnapplyData.length != length) {
revert ArrayLengthMismatch();
}

for (uint256 i = 0; i < length;) {
StoredInjectedHook memory hook = _storage.pluginData[plugin].injectedHooks[i];

/* solhint-disable no-empty-blocks */
try IPlugin(hook.providingPlugin).onHookUnapply(
plugin,
InjectedHooksInfo({
preExecHookFunctionId: hook.preExecHookFunctionId,
isPostHookUsed: hook.isPostHookUsed,
postExecHookFunctionId: hook.postExecHookFunctionId
}),
hasUnapplyHookData ? hookUnapplyData[i] : bytes("")
) {} catch (bytes memory revertReason) {
revert PluginUnapplyHookCallbackFailed(hook.providingPlugin, revertReason);
}
/* solhint-enable no-empty-blocks */

unchecked {
++i;
}
}

// Remove the plugin metadata from the account.
delete _storage.pluginData[plugin];

Expand Down
Loading

0 comments on commit 7846d38

Please sign in to comment.