-
Notifications
You must be signed in to change notification settings - Fork 624
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
AA-161 validate Nonce in EntryPoint #247
Changes from 9 commits
75f0245
931df7e
c1290e7
b2068fa
33a8e54
de441b0
98ee474
60e869c
d0a9b11
85371d1
bb8dc1f
33cbda9
ac89c73
beba6a7
5fed963
b646697
4618401
69e19f8
8bc2f63
dab35aa
e0ee20b
98d6888
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,9 @@ import "../utils/Exec.sol"; | |
import "./StakeManager.sol"; | ||
import "./SenderCreator.sol"; | ||
import "./Helpers.sol"; | ||
import "./NonceManager.sol"; | ||
|
||
contract EntryPoint is IEntryPoint, StakeManager { | ||
contract EntryPoint is IEntryPoint, StakeManager, NonceManager { | ||
|
||
using UserOperationLib for UserOperation; | ||
|
||
|
@@ -345,11 +346,13 @@ contract EntryPoint is IEntryPoint, StakeManager { | |
/** | ||
* Get counterfactual sender address. | ||
* Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation. | ||
* this method always revert, and returns the address in SenderAddressResult error | ||
* this method always revert, and returns the address (and the nonce) in SenderAddressResult error | ||
* @param initCode the constructor code to be passed into the UserOperation. | ||
*/ | ||
function getSenderAddress(bytes calldata initCode) public { | ||
drortirosh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
revert SenderAddressResult(senderCreator.createSender(initCode)); | ||
address sender = senderCreator.createSender(initCode); | ||
uint256 nonce = getNonce(sender, 0); | ||
revert SenderAddressResult(sender, nonce); | ||
} | ||
|
||
function _simulationOnlyValidations(UserOperation calldata userOp) internal view { | ||
|
@@ -511,6 +514,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)) { | ||
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. I noticed some pattern on old sample accounts that it skip nonce increment when initCode.length > 0. 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. It was an internal optimization of SimpleWallet. |
||
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,37 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.12; | ||
|
||
import "../interfaces/IEntryPoint.sol"; | ||
|
||
/** | ||
* nonce management functionality | ||
*/ | ||
contract NonceManager is INonceManager { | ||
|
||
mapping(address => mapping(uint192 => uint256)) public nonces; | ||
|
||
function getNonce(address sender, uint192 key) | ||
public view override returns (uint256 nonce) { | ||
return nonces[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 { | ||
drortirosh marked this conversation as resolved.
Show resolved
Hide resolved
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. Wallet can call 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. I agree that in make sense to modify the nonce before calling a user-provided method. |
||
nonces[msg.sender][key]++; | ||
drortirosh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* 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 nonces[sender][key]++ == seq; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,8 +12,9 @@ pragma solidity ^0.8.12; | |
import "./UserOperation.sol"; | ||
import "./IStakeManager.sol"; | ||
import "./IAggregator.sol"; | ||
import "./INonceManager.sol"; | ||
|
||
interface IEntryPoint is IStakeManager { | ||
interface IEntryPoint is IStakeManager, INonceManager { | ||
|
||
/*** | ||
* An event emitted after each successful request | ||
|
@@ -93,7 +94,7 @@ interface IEntryPoint is IStakeManager { | |
/** | ||
* return value of getSenderAddress | ||
*/ | ||
error SenderAddressResult(address sender); | ||
error SenderAddressResult(address sender, uint nonce); | ||
|
||
/** | ||
* return value of simulateHandleOp | ||
|
@@ -174,7 +175,7 @@ interface IEntryPoint is IStakeManager { | |
/** | ||
* Get counterfactual sender address. | ||
* Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation. | ||
* this method always revert, and returns the address in SenderAddressResult error | ||
* this method always revert, and returns the address (and nonce) in SenderAddressResult error | ||
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. Doesn't return nonce |
||
* @param initCode the constructor code to be passed into the UserOperation. | ||
*/ | ||
function getSenderAddress(bytes memory initCode) external; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.12; | ||
|
||
interface INonceManager { | ||
|
||
/** | ||
* return the next nonce for this sender | ||
* @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); | ||
|
||
function incrementNonce(uint192 key) external; | ||
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. Maybe add documentation, explaining that this function doesn't need to be called by the account? 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. actually, it is not needed at all. I just wonder if we should keep it for completeness, or remove it altogether.. 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. I thought it was needed for the "bypass" case where an account wants to support non-EntryPoint interactions and still use the same nonce. Is that no longer the case? 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. I think one side-effect of this function is breaking continuity of nonce, that lose the 'one nonce corresponding to one OP' property. For example, wallet can call Also, wallet can call 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. The match of "one nonce to one OP" exists, but in any case, it can't be trusted as a counter: e.g. "current nonce is # of past transactions" is not true, because:
In short, at the system level, nonces are nothing but replay protection.. |
||
} |
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.
Not returning nonce anymore