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

_callValidateUserOp to wrap sender.validateUserOp #533

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from

Conversation

drortirosh
Copy link
Contributor

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.

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...)
Comment on lines 527 to 533
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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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) {

Copy link
Collaborator

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?

Copy link
Contributor Author

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

@@ -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")
Copy link
Collaborator

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.

Comment on lines 520 to 522
assembly ("memory-safe") {
saveFreePtr := mload(0x40)
}
Copy link
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants