-
Notifications
You must be signed in to change notification settings - Fork 376
[not for merge] (heavily wip) EIP4337 & EIP7702 #1438
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
base: draft-v28
Are you sure you want to change the base?
Changes from all commits
c6d283e
6b40b7a
7fbb437
5a256d9
94f5a9b
cb98f1b
a7890d8
1eee592
eb9855c
55b1891
a204c5f
2f6735e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,10 +3,18 @@ | |
pragma solidity 0.8.28; | ||
|
||
import {IAccountCodeStorage} from "./interfaces/IAccountCodeStorage.sol"; | ||
import {SystemContractBase} from "./abstract/SystemContractBase.sol"; | ||
import {SystemContractsCaller} from "./libraries/SystemContractsCaller.sol"; | ||
Check failure on line 7 in system-contracts/contracts/AccountCodeStorage.sol
|
||
import {EfficientCall} from "./libraries/EfficientCall.sol"; | ||
import {Transaction, AuthorizationListItem} from "./libraries/TransactionHelper.sol"; | ||
Check failure on line 9 in system-contracts/contracts/AccountCodeStorage.sol
|
||
import {RLPEncoder} from "./libraries/RLPEncoder.sol"; | ||
import {Utils} from "./libraries/Utils.sol"; | ||
import {DEPLOYER_SYSTEM_CONTRACT, NONCE_HOLDER_SYSTEM_CONTRACT, CURRENT_MAX_PRECOMPILE_ADDRESS, EVM_HASHES_STORAGE} from "./Constants.sol"; | ||
import {DEPLOYER_SYSTEM_CONTRACT, NONCE_HOLDER_SYSTEM_CONTRACT, CURRENT_MAX_PRECOMPILE_ADDRESS, EVM_HASHES_STORAGE, INonceHolder} from "./Constants.sol"; | ||
import {Unauthorized, InvalidCodeHash, CodeHashReason} from "./SystemContractErrors.sol"; | ||
|
||
event AccountDelegated(address indexed authority, address indexed delegationAddress); | ||
event AccountDelegationRemoved(address indexed authority); | ||
|
||
/** | ||
* @author Matter Labs | ||
* @custom:security-contact [email protected] | ||
|
@@ -20,9 +28,13 @@ | |
* were published on L1 as calldata. This contract trusts the ContractDeployer and the KnownCodesStorage | ||
* system contracts to enforce the invariants mentioned above. | ||
*/ | ||
contract AccountCodeStorage is IAccountCodeStorage { | ||
contract AccountCodeStorage is IAccountCodeStorage, SystemContractBase { | ||
bytes32 private constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; | ||
|
||
/// @notice Information about EIP-7702 delegated EOAs. | ||
/// @dev Delegated EOAs. | ||
mapping(address => address) private delegatedEOAs; | ||
|
||
modifier onlyDeployer() { | ||
if (msg.sender != address(DEPLOYER_SYSTEM_CONTRACT)) { | ||
revert Unauthorized(msg.sender); | ||
|
@@ -153,4 +165,104 @@ | |
bytes32 bytecodeHash = getRawCodeHash(_addr); | ||
return Utils.isCodeHashEVM(bytecodeHash); | ||
} | ||
|
||
/// @notice Returns the address of the account that is delegated to execute transactions on behalf of the given | ||
/// address. | ||
/// @notice Returns the zero address if no delegation is set. | ||
function getAccountDelegation(address _addr) external view override returns (address) { | ||
return delegatedEOAs[_addr]; | ||
} | ||
|
||
/// @notice Allows the bootloader to override bytecode hash of account. | ||
/// TODO: can we avoid it and do it in bootloader? Having it as a public interface feels very unsafe. | ||
function setRawCodeHash(address addr, bytes32 rawBytecodeHash) external onlyCallFromBootloader { | ||
_storeCodeHash(addr, rawBytecodeHash); | ||
} | ||
|
||
function processDelegations(AuthorizationListItem[] calldata authorizationList) external onlyCallFromBootloader { | ||
for (uint256 i = 0; i < authorizationList.length; i++) { | ||
Check failure on line 183 in system-contracts/contracts/AccountCodeStorage.sol
|
||
// Per EIP7702 rules, if any check for the tuple item fails, | ||
// we must move on to the next item in the list. | ||
AuthorizationListItem calldata item = authorizationList[i]; | ||
|
||
// Verify the chain ID is 0 or the ID of the current chain. | ||
if (item.chainId != 0 && item.chainId != block.chainid) { | ||
continue; | ||
} | ||
|
||
// Verify the nonce is less than 2**64 - 1. | ||
if (item.nonce >= 0xFFFFFFFFFFFFFFFF) { | ||
continue; | ||
} | ||
|
||
// Calculate EIP7702 magic: | ||
// msg = keccak(MAGIC || rlp([chain_id, address, nonce])) | ||
bytes memory chainIdEncoded = RLPEncoder.encodeUint256(item.chainId); | ||
bytes memory addressEncoded = RLPEncoder.encodeAddress(item.addr); | ||
bytes memory nonceEncoded = RLPEncoder.encodeUint256(item.nonce); | ||
bytes memory listLenEncoded = RLPEncoder.encodeListLen( | ||
uint64(chainIdEncoded.length + addressEncoded.length + nonceEncoded.length) | ||
); | ||
bytes32 message = keccak256( | ||
bytes.concat(bytes1(0x05), listLenEncoded, chainIdEncoded, addressEncoded, nonceEncoded) | ||
Check failure on line 207 in system-contracts/contracts/AccountCodeStorage.sol
|
||
); | ||
|
||
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature | ||
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines | ||
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most | ||
// signatures from current libraries generate a unique signature with an s-value in the lower half order. | ||
// | ||
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value | ||
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or | ||
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept | ||
// these malleable signatures as well. | ||
if (uint256(item.s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { | ||
continue; | ||
} | ||
Comment on lines
+210
to
+221
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. This part was copy-pasted from some other place, needs to be checked. |
||
|
||
address authority = ecrecover(message, uint8(item.yParity + 27), bytes32(item.r), bytes32(item.s)); | ||
|
||
// ZKsync has native account abstraction, so we only allow delegation for EOAs. | ||
if (this.getRawCodeHash(authority) != 0x00 && this.getAccountDelegation(authority) == address(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.
|
||
continue; | ||
} | ||
|
||
bool nonceIncremented = this._performRawMimicCall( | ||
Check failure on line 230 in system-contracts/contracts/AccountCodeStorage.sol
|
||
uint32(gasleft()), | ||
authority, | ||
address(NONCE_HOLDER_SYSTEM_CONTRACT), | ||
abi.encodeCall(INonceHolder.incrementMinNonceIfEquals, (item.nonce)), | ||
true | ||
); | ||
if (!nonceIncremented) { | ||
continue; | ||
} | ||
if (item.addr == address(0)) { | ||
// If the delegation address is 0, we need to remove the delegation. | ||
delete delegatedEOAs[authority]; | ||
_storeCodeHash(authority, 0x00); | ||
emit AccountDelegationRemoved(authority); | ||
} else { | ||
// Otherwise, store the delegation. | ||
// TODO: Do we need any security checks here, e.g. non-default code hash or non-system contract? | ||
delegatedEOAs[authority] = item.addr; | ||
|
||
bytes32 codeHash = getRawCodeHash(item.addr); | ||
_storeCodeHash(authority, codeHash); // TODO: Do we need additional checks here? | ||
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. Here the semantics that you imply is not "I delegate to this address", but more like "I use the same bytecode as another address". It has the following differences from EIP7702:
I would personally make it more like EIP7702: reset But my approach has drawbacks:
So yeah.. not sure |
||
emit AccountDelegated(authority, item.addr); | ||
} | ||
} | ||
} | ||
|
||
// Needed to convert `memory` to `calldata` | ||
// TODO: (partial) duplication with EntryPointV01; probably need to be moved somewhere. | ||
function _performRawMimicCall( | ||
uint32 _gas, | ||
address _whoToMimic, | ||
address _to, | ||
bytes calldata _data, | ||
bool isSystem | ||
) external onlyCallFrom(address(this)) returns (bool success) { | ||
return EfficientCall.rawMimicCall(_gas, _to, _data, _whoToMimic, false, isSystem); | ||
Check failure on line 266 in system-contracts/contracts/AccountCodeStorage.sol
|
||
} | ||
} |
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.
just in case, dont store anything in this contract except for code hashes for accounts, the role of code hash registry is used on the vm level and bad things will happen if unintended
value
is stored for some key