-
Notifications
You must be signed in to change notification settings - Fork 681
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
_callValidateUserOp to wrap sender.validateUserOp #533
base: develop
Are you sure you want to change the base?
Conversation
wrap validateUserOp with assembly call. This way, we don't rely on EntryPointSimulation to create FailedOp on sender failures with malformed output (e.g. account not deployed)
optimized callValidateUserOp (less parameters), but actually costs more (16 per useop, but still...)
contracts/core/EntryPoint.sol
Outdated
assembly ("memory-safe"){ | ||
let success := call(gasLimit, sender, 0, add(callData, 0x20), mload(callData), 0, 32) | ||
dataSize := mul(returndatasize(), success) | ||
validationData := mload(0) | ||
mstore(0x40, saveFreePtr) | ||
} | ||
if (dataSize != 32) { |
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.
assembly ("memory-safe"){ | |
let success := call(gasLimit, sender, 0, add(callData, 0x20), mload(callData), 0, 32) | |
dataSize := mul(returndatasize(), success) | |
validationData := mload(0) | |
mstore(0x40, saveFreePtr) | |
} | |
if (dataSize != 32) { | |
bool success; | |
assembly ("memory-safe"){ | |
success := call(gasLimit, sender, 0, add(callData, 0x20), mload(callData), 0, 32) | |
dataSize := returndatasize() | |
validationData := mload(0) | |
mstore(0x40, saveFreePtr) | |
} | |
if (!successs || dataSize != 32) { |
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.
Maybe I don't understand the idea behind the mul(returndatasize(), success)
but it seems like you could just check for success
, which is a lot more reasonable?
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 logic is "ignore datasize in case of revert". I don't think that double-negative is more readable than multiplying "success" code by datasize. the both have the same effect. I also mload the returned data optimistically, and ignore it if datasize is not 32
contracts/core/EntryPoint.sol
Outdated
@@ -227,7 +227,7 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuardT | |||
//address(1) is special marker of "signature error" | |||
require( | |||
address(aggregator) != address(1), | |||
"AA96 invalid aggregator" | |||
FailedOp(totalOps, "AA96 invalid aggregator") |
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.
Is totalOps
the same as opIndex
in this case? If it is not, any tool build around FailedOp
will blame the wrong UserOp for this revert.
contracts/core/EntryPoint.sol
Outdated
assembly ("memory-safe") { | ||
saveFreePtr := mload(0x40) | ||
} |
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 looked into it, and there is probably no way to have a clean generic function that wraps this 'save-restore free memory pointer' trick.
Maybe for simplicity we could create inline save()
and restore()
functions and reuse those instead?
These assembly blocks kind of pollute the code.
Use _callValidateUserOp instead of sender.validateUserOp
Wrap validateUserOp with assembly call.
the try/catch is unable to catch wrong return value size, which causes entire handleOps to revert.
Now we can catch FailedOp on broken (or missing) sender.