-
Notifications
You must be signed in to change notification settings - Fork 25
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] per validation hook data #66
Conversation
b680f75
to
7cb19a4
Compare
cab3262
to
62344f2
Compare
Just to reiterate some points from previous discussions, my main concern lies in this being part of the signature field. We've discussed that similar designs in the past have had some issues but it's difficult to find a better spot. What's the downside of including it in the beginning of the calldata field instead? |
We could include it in calldata, that would help us by ensuring the data is signed over, making it non-malleable. It would come with some additional considerations:
Do you know of any way we could encode it in calldata such that the account could parse it out? We're constrained by the fact that the account doesn't know the parameter format of plugin-defined execution functions. |
I do agree that using the signature is error prone. I just realized there's an issue with the current implementation, you can express zero-length hook data in two different ways: specifying the index and setting a length of zero, or skipping the encoding. I'll update the PR to enforce 1 canonical encoding format. |
07b307a
to
bfdf5b5
Compare
62344f2
to
0e67b55
Compare
bfdf5b5
to
67b7088
Compare
0e67b55
to
c2da4d0
Compare
Updated to enforce canonical encoding, it is no longer possible to add zero-length segments to the signature. Instead, zero length segments must be omitted. It is up to the validation function and hooks to enforce that only the expected amount of data is provided. |
67b7088
to
29055a9
Compare
64a9917
to
23cdc39
Compare
29055a9
to
0aefb0f
Compare
23cdc39
to
4599a2d
Compare
0aefb0f
to
c9fcabf
Compare
4599a2d
to
8a09d31
Compare
d402361
to
60916fa
Compare
Taking a look, overall thoughts so far:
|
} | ||
|
||
// Load the next per-hook data segment | ||
(authSegment, authorizationData) = authorizationData.getNextSegment(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The recursion implemented here is neat! This appears to be the the most efficient approach, aside from key-value structure, which is not feasible within internal functions.
@@ -430,9 +468,13 @@ contract UpgradeableModularAccount is | |||
} | |||
} | |||
|
|||
if (authSegment.getIndex() != _RESERVED_VALIDATION_DATA_INDEX) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably move _RESERVED_VALIDATION_DATA_INDEX
into the library and expose a helper function, something like hasNext()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would that look? The getIndex
function is used to determine if the next segment applies for the current function (pre-validation hook or validation function), or for a later function. I think a hasNext
function would also need to return the index, so it seems pretty similar to the existing setup.
for (uint256 i = 0; i < preUserOpValidationHooksLength; ++i) { | ||
bytes32 key = preUserOpValidationHooks.at(i); | ||
FunctionReference preUserOpValidationHook = toFunctionReference(key); | ||
for (uint256 i = 0; i < preUserOpValidationHooks.length; ++i) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mega smol but could we rename "i" to preValidationHookIndex
since we use it for a few comparisons?
for (uint256 i = 0; i < preRuntimeValidationHooksLength; ++i) { | ||
bytes32 key = preRuntimeValidationHooks.at(i); | ||
FunctionReference preRuntimeValidationHook = toFunctionReference(key); | ||
for (uint256 i = 0; i < preRuntimeValidationHooks.length; ++i) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Echoing my previous comment to potentially rename this to preValiationHookIndex
or something-- not a hard req by any means.
I overall agree with the change, but what abuses could this lead to (forgive me if we talked about this already)? Edit: followup wrt abuses of this being in the signature field-- if it's only used to provide actual signature and signature-like information for authorization, I think it should be fine. Correct me if I'm wrong. Developers will need to keep this in mind when building validation hooks though. |
Are we sure we only want to call (Edit: This is specifically for |
2afe76b
to
94b7592
Compare
e118421
to
94a3764
Compare
/// than inline as part of the tuple returned by `getNextSegment`. | ||
library SparseCalldataSegmentLib { | ||
/// @notice Splits out a segment of calldata, sparsely-packed | ||
/// @param source The calldata to extract the segment from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be useful to document the expected format of the source, e.g., [ len(segment0) - 1] [ segment0 ] ... [ len(segmentN) - 1 ][ segmentN ]
Also maybe could be easier to reason about if the length of the segment included the index byte?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there might have been a versioning issue - previously I didn't include the index in the segment length, but I've since updated it to include it. And sounds good on the comment, will add it in as a "spec" for the encoding.
currentValidationData = IValidationHook(plugin).preUserOpValidationHook(functionId, userOp, userOpHash); | ||
(address plugin, uint8 functionId) = preUserOpValidationHooks[i].unpack(); | ||
uint256 currentValidationData = | ||
IValidationHook(plugin).preUserOpValidationHook(functionId, userOp, userOpHash); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts on introducing an additional parameter here for both preUserOpValidationHook
and validateUserOp
similar to the runtime path? That would allow us to keep the signature the same. What are the downsides?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just adds to the gas cost, and I'm not sure we have a good use case for the entire sig at the moment. With the runtime path, there wasn't anywhere else we could put it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving to unblock. Mind making a note of the unresolved comments to come back to later (if they aren't addressed before merge)?
94a3764
to
e4526cc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
e4526cc
to
68a5847
Compare
68a5847
to
b579102
Compare
Motivation
erc6900/resources#32
To better compose plugins, it is useful to allow extra data to be passed to hooks, to prevent having to do non-standard calldata encoding or require re-defining execution functions.
This is necessary for certain types of permissions-enforcing hooks to be used in a composable way – for example, an access control plugin implemented via a merkle tree requires proofs to be passed along with each call.
Solution
This PR implements per-hook data for pre-validation hooks only.
userOp.signature
field to pass the extra data into pre user op validation hooks, and adds anauthorization
field to pre runtime validation hooks to pass in extra data.