-
Notifications
You must be signed in to change notification settings - Fork 62
[AA]: fix inconsistent userOpHash #757
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
Changes from all commits
d97ee5d
81794d3
2ab9a53
0bc894c
be7b45b
5342377
7fa4832
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,8 +16,10 @@ import "../utils/Exec.sol"; | |
| import "./StakeManager.sol"; | ||
| import "./SenderCreator.sol"; | ||
| import "./Helpers.sol"; | ||
| import "./NonceManager.sol"; | ||
| import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; | ||
|
|
||
| contract EntryPoint is IEntryPoint, StakeManager { | ||
| contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard { | ||
|
|
||
| using UserOperationLib for UserOperation; | ||
|
|
||
|
|
@@ -87,7 +89,7 @@ contract EntryPoint is IEntryPoint, StakeManager { | |
| * @param ops the operations to execute | ||
| * @param beneficiary the address to receive the fees | ||
| */ | ||
| function handleOps(UserOperation[] calldata ops, address payable beneficiary) public { | ||
| function handleOps(UserOperation[] calldata ops, address payable beneficiary) public nonReentrant { | ||
|
|
||
| uint256 opslen = ops.length; | ||
| UserOpInfo[] memory opInfos = new UserOpInfo[](opslen); | ||
|
|
@@ -100,6 +102,7 @@ contract EntryPoint is IEntryPoint, StakeManager { | |
| } | ||
|
|
||
| uint256 collected = 0; | ||
| emit BeforeExecution(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is this? :D
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh since there can be recursive calls to handleOps() (and the handleAggregatedOps()), understanding the order of emitted events and parsing is difficult. This event emitted before every execution is like a separator for each execution (if recursive)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sweet! |
||
|
|
||
| for (uint256 i = 0; i < opslen; i++) { | ||
| collected += _executeUserOp(i, ops[i], opInfos[i]); | ||
|
|
@@ -117,7 +120,7 @@ contract EntryPoint is IEntryPoint, StakeManager { | |
| function handleAggregatedOps( | ||
| UserOpsPerAggregator[] calldata opsPerAggregator, | ||
| address payable beneficiary | ||
| ) public { | ||
| ) public nonReentrant { | ||
|
|
||
| uint256 opasLen = opsPerAggregator.length; | ||
| uint256 totalOps = 0; | ||
|
|
@@ -142,6 +145,8 @@ contract EntryPoint is IEntryPoint, StakeManager { | |
|
|
||
| UserOpInfo[] memory opInfos = new UserOpInfo[](totalOps); | ||
|
|
||
| emit BeforeExecution(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same ^^ |
||
|
|
||
| uint256 opIndex = 0; | ||
| for (uint256 a = 0; a < opasLen; a++) { | ||
| UserOpsPerAggregator calldata opa = opsPerAggregator[a]; | ||
|
|
@@ -349,7 +354,8 @@ contract EntryPoint is IEntryPoint, StakeManager { | |
| * @param initCode the constructor code to be passed into the UserOperation. | ||
| */ | ||
| function getSenderAddress(bytes calldata initCode) public { | ||
| revert SenderAddressResult(senderCreator.createSender(initCode)); | ||
| address sender = senderCreator.createSender(initCode); | ||
| revert SenderAddressResult(sender); | ||
| } | ||
|
|
||
| function _simulationOnlyValidations(UserOperation calldata userOp) internal view { | ||
|
|
@@ -511,6 +517,11 @@ contract EntryPoint is IEntryPoint, StakeManager { | |
| uint256 gasUsedByValidateAccountPrepayment; | ||
| (uint256 requiredPreFund) = _getRequiredPrefund(mUserOp); | ||
| (gasUsedByValidateAccountPrepayment, validationData) = _validateAccountPrepayment(opIndex, userOp, outOpInfo, requiredPreFund); | ||
|
|
||
| if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) { | ||
| revert FailedOp(opIndex, "AA25 invalid account nonce"); | ||
| } | ||
|
|
||
| //a "marker" where account opcode validation is done and paymaster opcode validation is about to start | ||
| // (used only by off-chain simulateValidation) | ||
| numberMarker(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
| pragma solidity ^0.8.12; | ||
|
|
||
| import "../interfaces/IEntryPoint.sol"; | ||
|
|
||
| /** | ||
| * nonce management functionality | ||
| */ | ||
| contract NonceManager is INonceManager { | ||
|
|
||
| /** | ||
| * The next valid sequence number for a given nonce key. | ||
| */ | ||
| mapping(address => mapping(uint192 => uint256)) public nonceSequenceNumber; | ||
|
|
||
| function getNonce(address sender, uint192 key) | ||
| public view override returns (uint256 nonce) { | ||
| return nonceSequenceNumber[sender][key] | (uint256(key) << 64); | ||
| } | ||
|
|
||
| // allow an account to manually increment its own nonce. | ||
| // (mainly so that during construction nonce can be made non-zero, | ||
| // to "absorb" the gas cost of first nonce increment to 1st transaction (construction), | ||
| // not to 2nd transaction) | ||
| function incrementNonce(uint192 key) public override { | ||
| nonceSequenceNumber[msg.sender][key]++; | ||
| } | ||
|
|
||
| /** | ||
| * validate nonce uniqueness for this account. | ||
| * called just after validateUserOp() | ||
| */ | ||
| function _validateAndUpdateNonce(address sender, uint256 nonce) internal returns (bool) { | ||
|
|
||
| uint192 key = uint192(nonce >> 64); | ||
| uint64 seq = uint64(nonce); | ||
| return nonceSequenceNumber[sender][key]++ == seq; | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
| pragma solidity ^0.8.12; | ||
|
|
||
| interface INonceManager { | ||
|
|
||
| /** | ||
| * Return the next nonce for this sender. | ||
| * Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop) | ||
| * But UserOp with different keys can come with arbitrary order. | ||
| * | ||
| * @param sender the account address | ||
| * @param key the high 192 bit of the nonce | ||
| * @return nonce a full nonce to pass for next UserOp with this sender. | ||
| */ | ||
| function getNonce(address sender, uint192 key) | ||
| external view returns (uint256 nonce); | ||
|
|
||
| /** | ||
| * Manually increment the nonce of the sender. | ||
| * This method is exposed just for completeness.. | ||
| * Account does NOT need to call it, neither during validation, nor elsewhere, | ||
| * as the EntryPoint will update the nonce regardless. | ||
| * Possible use-case is call it with various keys to "initialize" their nonces to one, so that future | ||
| * UserOperations will not pay extra for the first transaction with a given key. | ||
| */ | ||
| function incrementNonce(uint192 key) external; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
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.
was just discussing this on twitter lol, why is nonce handling handled on the entrypoint contract?
I guess it makes sense since the bundler submitter nonce actually gets bumped not the userop sender
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.
yeah! if user account contracts are allowed to control nonces - (or not have nonces) - the userOpHash might not be a unique value and that might confuse wallets/explorers. Because of that they moved the validation to the EntryPoint. found more details here :)
https://docs.google.com/document/d/1MywdH_TCkyEjD3QusLZ_kUZg4ZEI00qp97mBze9JI4k/edit#heading=h.gyhqxhuyd59n