diff --git a/foundry.toml b/foundry.toml index 003a12a6..2a88ba29 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,9 +7,9 @@ solc_version = '0.8.19' bytecode_hash = "none" cbor_metadata = false optimize = true -via-ir = false +via-ir = true runs = 1000000 -gas_reports=["Kernel","KernelFactory"] +gas_reports=["Kernel","KernelLiteECDSA","KernelFactory"] # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/gas/ecdsa/report-lite.txt b/gas/ecdsa/report-lite.txt new file mode 100644 index 00000000..bd7680bd --- /dev/null +++ b/gas/ecdsa/report-lite.txt @@ -0,0 +1,40 @@ +No files changed, compilation skipped + +Running 8 tests for test/foundry/KernelLite.t.sol:KernelTest +[PASS] test_disable_mode() (gas: 171425) +[PASS] test_external_call_default() (gas: 22953) +[PASS] test_external_call_execution() (gas: 439687) +[PASS] test_initialize_twice() (gas: 20968) +[PASS] test_set_default_validator() (gas: 369789) +[PASS] test_set_execution() (gas: 400852) +[PASS] test_validate_signature() (gas: 139666) +[PASS] test_validate_userOp() (gas: 1733019) +Test result: ok. 8 passed; 0 failed; 0 skipped; finished in 2.73ms +| src/factory/KernelFactory.sol:KernelFactory contract | | | | | | +|------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 594023 | 3050 | | | | | +| Function Name | min | avg | median | max | # calls | +| createAccount | 106913 | 107413 | 106913 | 111413 | 9 | +| deployDeterministicAndCall | 105350 | 105627 | 105350 | 107850 | 9 | +| setImplementation | 22862 | 22862 | 22862 | 22862 | 8 | + + +| src/lite/KernelLiteECDSA.sol:KernelLiteECDSA contract | | | | | | +|-------------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 1598398 | 8392 | | | | | +| Function Name | min | avg | median | max | # calls | +| disableMode | 22865 | 22865 | 22865 | 22865 | 1 | +| getDefaultValidator | 341 | 341 | 341 | 341 | 1 | +| getDisabledMode | 577 | 577 | 577 | 577 | 1 | +| getExecution | 1249 | 1249 | 1249 | 1249 | 2 | +| initialize | 3117 | 21073 | 23069 | 23069 | 10 | +| isValidSignature | 8587 | 8587 | 8587 | 8587 | 1 | +| setDefaultValidator | 26774 | 26774 | 26774 | 26774 | 1 | +| setExecution | 49853 | 49853 | 49853 | 49853 | 2 | +| validateUserOp | 36049 | 36049 | 36049 | 36049 | 4 | + + + +Ran 1 test suites: 8 tests passed, 0 failed, 0 skipped (8 total tests) diff --git a/gas/ecdsa/report.txt b/gas/ecdsa/report.txt index 0a64adca..c53e8856 100644 --- a/gas/ecdsa/report.txt +++ b/gas/ecdsa/report.txt @@ -1,29 +1,40 @@ No files changed, compilation skipped -Running 7 tests for test/foundry/Kernel.t.sol:KernelTest -[PASS] test_disable_mode() (gas: 162879) -[PASS] test_external_call_default() (gas: 28845) -[PASS] test_external_call_execution() (gas: 453354) -[PASS] test_initialize_twice() (gas: 20840) +Running 8 tests for test/foundry/Kernel.t.sol:KernelTest +[PASS] test_disable_mode() (gas: 162901) +[PASS] test_external_call_default() (gas: 28867) +[PASS] test_external_call_execution() (gas: 453376) +[PASS] test_initialize_twice() (gas: 20885) [PASS] test_set_default_validator() (gas: 361374) -[PASS] test_set_execution() (gas: 411689) -[PASS] test_validate_signature() (gas: 165811) -Test result: ok. 7 passed; 0 failed; 0 skipped; finished in 2.67ms +[PASS] test_set_execution() (gas: 411711) +[PASS] test_validate_signature() (gas: 164810) +[PASS] test_validate_userOp() (gas: 1729466) +Test result: ok. 8 passed; 0 failed; 0 skipped; finished in 2.77ms | src/Kernel.sol:Kernel contract | | | | | | |--------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 1590591 | 8353 | | | | | +| 1586591 | 8333 | | | | | | Function Name | min | avg | median | max | # calls | | disableMode | 3765 | 3765 | 3765 | 3765 | 1 | | getDefaultValidator | 341 | 341 | 341 | 341 | 1 | | getDisabledMode | 577 | 577 | 577 | 577 | 1 | | getExecution | 1249 | 1249 | 1249 | 1249 | 2 | -| initialize | 3083 | 44928 | 49847 | 52347 | 9 | +| initialize | 3046 | 43982 | 48253 | 50753 | 10 | | isValidSignature | 6047 | 6047 | 6047 | 6047 | 1 | | setDefaultValidator | 7870 | 7870 | 7870 | 7870 | 1 | | setExecution | 49877 | 49877 | 49877 | 49877 | 2 | | validateUserOp | 46040 | 46234 | 46256 | 46386 | 4 | +| src/factory/KernelFactory.sol:KernelFactory contract | | | | | | +|------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 594023 | 3050 | | | | | +| Function Name | min | avg | median | max | # calls | +| createAccount | 132097 | 132874 | 132097 | 139097 | 9 | +| deployDeterministicAndCall | 130534 | 131089 | 130534 | 135534 | 9 | +| setImplementation | 22862 | 22862 | 22862 | 22862 | 8 | -Ran 1 test suites: 7 tests passed, 0 failed, 0 skipped (7 total tests) + + +Ran 1 test suites: 8 tests passed, 0 failed, 0 skipped (8 total tests) diff --git a/gas/gas_report.sh b/gas/gas_report.sh index 26f8ee2b..e776d913 100755 --- a/gas/gas_report.sh +++ b/gas/gas_report.sh @@ -1 +1,3 @@ +forge build forge test --gas-report --match-path test/foundry/Kernel.t.sol > gas/ecdsa/report.txt +forge test --gas-report --match-path test/foundry/KernelLite.t.sol > gas/ecdsa/report-lite.txt diff --git a/lib/account-abstraction b/lib/account-abstraction index abff2aca..12be13e2 160000 --- a/lib/account-abstraction +++ b/lib/account-abstraction @@ -1 +1 @@ -Subproject commit abff2aca61a8f0934e533d0d352978055fddbd96 +Subproject commit 12be13e2e97b763e1ef294602b3f2072bc301443 diff --git a/src/Kernel.sol b/src/Kernel.sol index 4a827f2f..82b44ab2 100644 --- a/src/Kernel.sol +++ b/src/Kernel.sol @@ -60,14 +60,13 @@ contract Kernel is EIP712, Compatibility, KernelStorage { /// @param value The amount of Ether to send /// @param data The call data to be sent /// @param operation The type of operation (call or delegatecall) - function execute(address to, uint256 value, bytes calldata data, Operation operation) external payable { + function execute(address to, uint256 value, bytes memory data, Operation operation) external payable { if (msg.sender != address(entryPoint) && !_checkCaller()) { revert NotAuthorizedCaller(); } - bytes memory callData = data; if (operation == Operation.DelegateCall) { assembly { - let success := delegatecall(gas(), to, add(callData, 0x20), mload(callData), 0, 0) + let success := delegatecall(gas(), to, add(data, 0x20), mload(data), 0, 0) returndatacopy(0, 0, returndatasize()) switch success case 0 { revert(0, returndatasize()) } @@ -75,7 +74,7 @@ contract Kernel is EIP712, Compatibility, KernelStorage { } } else { assembly { - let success := call(gas(), to, value, add(callData, 0x20), mload(callData), 0, 0) + let success := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0) returndatacopy(0, 0, returndatasize()) switch success case 0 { revert(0, returndatasize()) } diff --git a/src/abstract/KernelStorage.sol b/src/abstract/KernelStorage.sol index f03fbc32..6e4c6dac 100644 --- a/src/abstract/KernelStorage.sol +++ b/src/abstract/KernelStorage.sol @@ -60,13 +60,7 @@ contract KernelStorage { // Function to initialize the wallet kernel function initialize(IKernelValidator _defaultValidator, bytes calldata _data) external payable { - WalletKernelStorage storage ws = getKernelStorage(); - if (address(ws.defaultValidator) != address(0)) { - revert AlreadyInitialized(); - } - ws.defaultValidator = _defaultValidator; - emit DefaultValidatorChanged(address(0), address(_defaultValidator)); - _defaultValidator.enable(_data); + _setInitialData(_defaultValidator, _data); } // Function to get the wallet kernel storage @@ -161,4 +155,16 @@ contract KernelStorage { getKernelStorage().disabledMode = _disableFlag; getKernelStorage().lastDisabledTime = uint48(block.timestamp); } + + function _setInitialData(IKernelValidator _defaultValidator, bytes calldata _data) internal virtual { + address validator; + assembly { + validator := shr(80, sload(KERNEL_STORAGE_SLOT_1)) + } + if (address(validator) != address(0)) { + revert AlreadyInitialized(); + } + getKernelStorage().defaultValidator = _defaultValidator; + _defaultValidator.enable(_data); + } } diff --git a/src/factory/ECDSAKernelFactory.sol b/src/factory/ECDSAKernelFactory.sol deleted file mode 100644 index 0ada474c..00000000 --- a/src/factory/ECDSAKernelFactory.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import "./KernelFactory.sol"; -import "src/validator/ECDSAValidator.sol"; - -contract ECDSAKernelFactory { - KernelFactory public immutable singletonFactory; - ECDSAValidator public immutable validator; - - constructor(KernelFactory _singletonFactory, ECDSAValidator _validator) { - singletonFactory = _singletonFactory; - validator = _validator; - } - - function createAccount(address _owner, uint256 _index) external returns (address proxy) { - bytes memory data = abi.encodePacked(_owner); - proxy = singletonFactory.createAccount(validator, data, _index); - } - - function getAccountAddress(address _owner, uint256 _index) public view returns (address) { - bytes memory data = abi.encodePacked(_owner); - return singletonFactory.getAccountAddress(validator, data, _index); - } -} diff --git a/src/factory/KernelFactory.sol b/src/factory/KernelFactory.sol index dd020f52..574c2d67 100644 --- a/src/factory/KernelFactory.sol +++ b/src/factory/KernelFactory.sol @@ -6,36 +6,36 @@ import "./AdminLessERC1967Factory.sol"; import "openzeppelin-contracts/contracts/utils/Create2.sol"; import "src/Kernel.sol"; import "src/validator/ECDSAValidator.sol"; +import "solady/auth/Ownable.sol"; -contract KernelFactory { - AdminLessERC1967Factory public immutable erc1967factory; - Kernel public immutable kernelTemplate; - IEntryPoint public immutable entryPoint; +contract KernelFactory is AdminLessERC1967Factory, Ownable{ - event AccountCreated(address indexed account, address indexed validator, bytes data, uint256 index); + mapping(address => bool) public isAllowedImplementation; - constructor(AdminLessERC1967Factory _erc1967factory, IEntryPoint _entryPoint) { - erc1967factory = _erc1967factory; - entryPoint = _entryPoint; - kernelTemplate = new Kernel(_entryPoint); + constructor(address _owner) { + _initializeOwner(_owner); } - function createAccount(IKernelValidator _validator, bytes calldata _data, uint256 _index) + function setImplementation(address _implementation, bool _allow) external onlyOwner { + isAllowedImplementation[_implementation] = _allow; + } + + function createAccount(address _implementation, bytes calldata _data, uint256 _index) external payable returns (address proxy) { - bytes memory initData = abi.encodeWithSelector(KernelStorage.initialize.selector, _validator, _data); - bytes32 salt = bytes32(uint256(keccak256(abi.encodePacked(_validator, _data, _index))) & type(uint96).max); - proxy = erc1967factory.deployDeterministicAndCall(address(kernelTemplate), salt, initData); + require(isAllowedImplementation[_implementation], "KernelFactory: implementation not allowed"); + bytes32 salt = bytes32(uint256(keccak256(abi.encodePacked(_data, _index))) & type(uint96).max); + proxy = this.deployDeterministicAndCall(_implementation, salt, _data); } - function getAccountAddress(IKernelValidator _validator, bytes calldata _data, uint256 _index) + function getAccountAddress(bytes calldata _data, uint256 _index) public view returns (address) { - bytes32 salt = bytes32(uint256(keccak256(abi.encodePacked(_validator, _data, _index))) & type(uint96).max); - return erc1967factory.predictDeterministicAddress(salt); + bytes32 salt = bytes32(uint256(keccak256(abi.encodePacked(_data, _index))) & type(uint96).max); + return predictDeterministicAddress(salt); } } diff --git a/src/factory/MultiECDSAKernelFactory.sol b/src/factory/MultiECDSAKernelFactory.sol deleted file mode 100644 index a7b9d6ce..00000000 --- a/src/factory/MultiECDSAKernelFactory.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import "./KernelFactory.sol"; -import "src/validator/MultiECDSAValidator.sol"; -import "src/interfaces/IAddressBook.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; - -contract MultiECDSAKernelFactory is IAddressBook, Ownable { - KernelFactory public immutable singletonFactory; - MultiECDSAValidator public immutable validator; - IEntryPoint public immutable entryPoint; - - address[] public owners; - - constructor(KernelFactory _singletonFactory, MultiECDSAValidator _validator, IEntryPoint _entryPoint) { - singletonFactory = _singletonFactory; - validator = _validator; - entryPoint = _entryPoint; - } - - function setOwners(address[] calldata _owners) external onlyOwner { - owners = _owners; - } - - function getOwners() external view override returns (address[] memory) { - return owners; - } - - function createAccount(uint256 _index) external returns (address proxy) { - bytes memory data = abi.encodePacked(address(this)); - proxy = singletonFactory.createAccount(validator, data, _index); - } - - function getAccountAddress(uint256 _index) public view returns (address) { - bytes memory data = abi.encodePacked(address(this)); - return singletonFactory.getAccountAddress(validator, data, _index); - } - - /** - * add a deposit for this factory, used for paying for transaction fees - */ - function deposit() public payable { - entryPoint.depositTo{value: msg.value}(address(this)); - } - - /** - * withdraw value from the deposit - * @param withdrawAddress target to send to - * @param amount to withdraw - */ - function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner { - entryPoint.withdrawTo(withdrawAddress, amount); - } - /** - * add stake for this factory. - * This method can also carry eth value to add to the current stake. - * @param unstakeDelaySec - the unstake delay for this factory. Can only be increased. - */ - - function addStake(uint32 unstakeDelaySec) external payable onlyOwner { - entryPoint.addStake{value: msg.value}(unstakeDelaySec); - } - - /** - * return current factory's deposit on the entryPoint. - */ - function getDeposit() public view returns (uint256) { - return entryPoint.balanceOf(address(this)); - } - - /** - * unlock the stake, in order to withdraw it. - * The factory can't serve requests once unlocked, until it calls addStake again - */ - function unlockStake() external onlyOwner { - entryPoint.unlockStake(); - } - - /** - * withdraw the entire factory's stake. - * stake must be unlocked first (and then wait for the unstakeDelay to be over) - * @param withdrawAddress the address to send withdrawn value. - */ - function withdrawStake(address payable withdrawAddress) external onlyOwner { - entryPoint.withdrawStake(withdrawAddress); - } -} diff --git a/test/foundry/Kernel.t.sol b/test/foundry/Kernel.t.sol index dd6e8781..217bc816 100644 --- a/test/foundry/Kernel.t.sol +++ b/test/foundry/Kernel.t.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; import "src/factory/AdminLessERC1967Factory.sol"; import "src/factory/KernelFactory.sol"; -import "src/factory/ECDSAKernelFactory.sol"; import "src/Kernel.sol"; import "src/validator/ECDSAValidator.sol"; // test artifacts @@ -18,25 +17,28 @@ using ERC4337Utils for EntryPoint; contract KernelTest is Test { Kernel kernel; - AdminLessERC1967Factory erc1967factory; + Kernel kernelImpl; KernelFactory factory; - ECDSAKernelFactory ecdsaFactory; EntryPoint entryPoint; ECDSAValidator validator; address owner; uint256 ownerKey; address payable beneficiary; + address factoryOwner; function setUp() public { (owner, ownerKey) = makeAddrAndKey("owner"); - erc1967factory = new AdminLessERC1967Factory(); + (factoryOwner, ) = makeAddrAndKey("factoryOwner"); entryPoint = new EntryPoint(); - factory = new KernelFactory(erc1967factory, entryPoint); + kernelImpl = new Kernel(entryPoint); + factory = new KernelFactory(factoryOwner); + vm.startPrank(factoryOwner); + factory.setImplementation(address(kernelImpl), true); + vm.stopPrank(); validator = new ECDSAValidator(); - ecdsaFactory = new ECDSAKernelFactory(factory, validator); - kernel = Kernel(payable(ecdsaFactory.createAccount(owner, 0))); + kernel = Kernel(payable(address(factory.createAccount(address(kernelImpl), abi.encodeWithSelector(KernelStorage.initialize.selector, validator, abi.encodePacked(owner)), 0)))); vm.deal(address(kernel), 1e30); beneficiary = payable(address(makeAddr("beneficiary"))); } @@ -53,7 +55,7 @@ contract KernelTest is Test { } function test_validate_signature() external { - Kernel kernel2 = Kernel(payable(address(ecdsaFactory.createAccount(owner, 1)))); + Kernel kernel2 = Kernel(payable(address(factory.createAccount(address(kernelImpl), abi.encodeWithSelector(KernelStorage.initialize.selector, validator, abi.encodePacked(owner)), 1)))); bytes32 hash = keccak256(abi.encodePacked("hello world")); (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerKey, hash); assertEq(kernel2.isValidSignature(hash, abi.encodePacked(r, s, v)), Kernel.isValidSignature.selector); diff --git a/test/foundry/KernelExecution.t.sol b/test/foundry/KernelExecution.t.sol index 15fc862b..1878aff8 100644 --- a/test/foundry/KernelExecution.t.sol +++ b/test/foundry/KernelExecution.t.sol @@ -5,7 +5,6 @@ import "src/factory/AdminLessERC1967Factory.sol"; import "src/Kernel.sol"; import "src/validator/ECDSAValidator.sol"; import "src/factory/KernelFactory.sol"; -import "src/factory/ECDSAKernelFactory.sol"; // test artifacts import "src/test/TestValidator.sol"; import "src/test/TestExecutor.sol"; @@ -21,25 +20,28 @@ using ERC4337Utils for EntryPoint; contract KernelExecutionTest is Test { Kernel kernel; - AdminLessERC1967Factory erc1967factory; + Kernel kernelImpl; KernelFactory factory; - ECDSAKernelFactory ecdsaFactory; EntryPoint entryPoint; ECDSAValidator validator; address owner; uint256 ownerKey; address payable beneficiary; + address factoryOwner; function setUp() public { (owner, ownerKey) = makeAddrAndKey("owner"); + (factoryOwner, ) = makeAddrAndKey("factoryOwner"); entryPoint = new EntryPoint(); - erc1967factory = new AdminLessERC1967Factory(); - factory = new KernelFactory(erc1967factory, entryPoint); + kernelImpl = new Kernel(entryPoint); + factory = new KernelFactory(factoryOwner); + vm.startPrank(factoryOwner); + factory.setImplementation(address(kernelImpl), true); + vm.stopPrank(); validator = new ECDSAValidator(); - ecdsaFactory = new ECDSAKernelFactory(factory, validator); - kernel = Kernel(payable(address(ecdsaFactory.createAccount(owner, 0)))); + kernel = Kernel(payable(address(factory.createAccount(address(kernelImpl), abi.encodeWithSelector(KernelStorage.initialize.selector, validator, abi.encodePacked(owner)), 0)))); vm.deal(address(kernel), 1e30); beneficiary = payable(address(makeAddr("beneficiary"))); } diff --git a/test/foundry/KernelMultiOwned.t.sol b/test/foundry/KernelMultiOwned.t.sol index 3841047e..e69de29b 100644 --- a/test/foundry/KernelMultiOwned.t.sol +++ b/test/foundry/KernelMultiOwned.t.sol @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "src/factory/AdminLessERC1967Factory.sol"; -import "src/factory/KernelFactory.sol"; -import "src/factory/MultiECDSAKernelFactory.sol"; -import "src/Kernel.sol"; -import "src/validator/MultiECDSAValidator.sol"; -// test artifacts -import "src/test/TestValidator.sol"; -// test utils -import "forge-std/Test.sol"; -import {ERC4337Utils} from "./ERC4337Utils.sol"; - -using ERC4337Utils for EntryPoint; - -contract KernelTest is Test { - Kernel kernel; - AdminLessERC1967Factory erc1967factory; - KernelFactory factory; - MultiECDSAKernelFactory ecdsaFactory; - EntryPoint entryPoint; - MultiECDSAValidator validator; - address owner; - uint256 ownerKey; - address payable beneficiary; - - function setUp() public { - (owner, ownerKey) = makeAddrAndKey("owner"); - erc1967factory = new AdminLessERC1967Factory(); - entryPoint = new EntryPoint(); - factory = new KernelFactory(erc1967factory, entryPoint); - - validator = new MultiECDSAValidator(); - ecdsaFactory = new MultiECDSAKernelFactory(factory, validator, entryPoint); - address[] memory owners = new address[](1); - owners[0] = owner; - ecdsaFactory.setOwners(owners); - - kernel = Kernel(payable(ecdsaFactory.createAccount(0))); - vm.deal(address(kernel), 1e30); - beneficiary = payable(address(makeAddr("beneficiary"))); - } - - function test_initialize_twice() external { - vm.expectRevert(); - kernel.initialize(validator, abi.encodePacked(owner)); - } - - function test_validate_signature() external { - Kernel kernel2 = Kernel(payable(address(ecdsaFactory.createAccount(1)))); - bytes32 hash = keccak256(abi.encodePacked("hello world")); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerKey, hash); - assertEq(kernel2.isValidSignature(hash, abi.encodePacked(r, s, v)), Kernel.isValidSignature.selector); - } - - function test_set_default_validator() external { - TestValidator newValidator = new TestValidator(); - bytes memory empty; - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), - abi.encodeWithSelector(KernelStorage.setDefaultValidator.selector, address(newValidator), empty) - ); - op.signature = abi.encodePacked(bytes4(0x00000000), entryPoint.signUserOpHash(vm, ownerKey, op)); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); - assertEq(address(KernelStorage(address(kernel)).getDefaultValidator()), address(newValidator)); - } - - function test_disable_mode() external { - bytes memory empty; - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), - abi.encodeWithSelector(KernelStorage.disableMode.selector, bytes4(0x00000001), address(0), empty) - ); - op.signature = abi.encodePacked(bytes4(0x00000000), entryPoint.signUserOpHash(vm, ownerKey, op)); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); - assertEq(uint256(bytes32(KernelStorage(address(kernel)).getDisabledMode())), 1 << 224); - } - - function test_set_execution() external { - console.log("owner", owner); - TestValidator newValidator = new TestValidator(); - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), - abi.encodeWithSelector( - KernelStorage.setExecution.selector, - bytes4(0xdeadbeef), - address(0xdead), - address(newValidator), - uint48(0), - uint48(0), - bytes("") - ) - ); - op.signature = abi.encodePacked(bytes4(0x00000000), entryPoint.signUserOpHash(vm, ownerKey, op)); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); - ExecutionDetail memory execution = KernelStorage(address(kernel)).getExecution(bytes4(0xdeadbeef)); - assertEq(execution.executor, address(0xdead)); - assertEq(address(execution.validator), address(newValidator)); - assertEq(uint256(execution.validUntil), uint256(0)); - assertEq(uint256(execution.validAfter), uint256(0)); - } -} diff --git a/test/foundry/KillSwitch.t.sol b/test/foundry/KillSwitch.t.sol deleted file mode 100644 index 44922b4e..00000000 --- a/test/foundry/KillSwitch.t.sol +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "src/factory/AdminLessERC1967Factory.sol"; -import "src/factory/KernelFactory.sol"; -import "src/validator/ECDSAValidator.sol"; -import "src/factory/ECDSAKernelFactory.sol"; -import "src/Kernel.sol"; -import "src/validator/KillSwitchValidator.sol"; -import "src/executor/KillSwitchAction.sol"; -// test utils -import "forge-std/Test.sol"; -import {ERC4337Utils} from "./ERC4337Utils.sol"; - -using ERC4337Utils for EntryPoint; - -contract KernelExecutionTest is Test { - Kernel kernel; - AdminLessERC1967Factory erc1967factory; - KernelFactory factory; - ECDSAKernelFactory ecdsaFactory; - EntryPoint entryPoint; - ECDSAValidator validator; - - KillSwitchValidator killSwitch; - KillSwitchAction action; - address owner; - uint256 ownerKey; - address payable beneficiary; - - function setUp() public { - (owner, ownerKey) = makeAddrAndKey("owner"); - erc1967factory = new AdminLessERC1967Factory(); - entryPoint = new EntryPoint(); - factory = new KernelFactory(erc1967factory, entryPoint); - - validator = new ECDSAValidator(); - ecdsaFactory = new ECDSAKernelFactory(factory, validator); - - kernel = Kernel(payable(address(ecdsaFactory.createAccount(owner, 0)))); - vm.deal(address(kernel), 1e30); - beneficiary = payable(address(makeAddr("beneficiary"))); - killSwitch = new KillSwitchValidator(); - action = new KillSwitchAction(killSwitch); - } - - function test_mode_2() external { - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), abi.encodeWithSelector(Kernel.execute.selector, owner, 0, "", Operation.Call) - ); - - op.signature = bytes.concat(bytes4(0), entryPoint.signUserOpHash(vm, ownerKey, op)); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); - - op = entryPoint.fillUserOp(address(kernel), abi.encodeWithSelector(KillSwitchAction.toggleKillSwitch.selector)); - address guardianKeyAddr; - uint256 guardianKeyPriv; - (guardianKeyAddr, guardianKeyPriv) = makeAddrAndKey("guardianKey"); - bytes memory enableData = abi.encodePacked(guardianKeyAddr); - { - bytes32 digest = getTypedDataHash( - address(kernel), - KillSwitchAction.toggleKillSwitch.selector, - 0, - 0, - address(killSwitch), - address(action), - enableData - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerKey, digest); - - op.signature = abi.encodePacked( - bytes4(0x00000002), - uint48(0), - uint48(0), - address(killSwitch), - address(action), - uint256(enableData.length), - enableData, - uint256(65), - r, - s, - v - ); - } - - uint256 pausedUntil = block.timestamp + 1000; - - bytes32 hash = entryPoint.getUserOpHash(op); - { - (uint8 v, bytes32 r, bytes32 s) = vm.sign( - guardianKeyPriv, - ECDSA.toEthSignedMessageHash(keccak256(bytes.concat(bytes6(uint48(pausedUntil)), hash))) - ); - bytes memory sig = abi.encodePacked(r, s, v); - - op.signature = bytes.concat(op.signature, bytes6(uint48(pausedUntil)), sig); - } - - ops[0] = op; - logGas(op); - entryPoint.handleOps(ops, beneficiary); - assertEq(address(kernel.getDefaultValidator()), address(killSwitch)); - op = entryPoint.fillUserOp( - address(kernel), abi.encodeWithSelector(Kernel.execute.selector, owner, 0, "", Operation.Call) - ); - - op.signature = bytes.concat(bytes4(0), entryPoint.signUserOpHash(vm, ownerKey, op)); - ops[0] = op; - vm.expectRevert(); - entryPoint.handleOps(ops, beneficiary); // should revert because kill switch is active - vm.warp(pausedUntil + 1); - entryPoint.handleOps(ops, beneficiary); // should not revert because pausedUntil has been passed - } - - function logGas(UserOperation memory op) internal returns (uint256 used) { - try this.consoleGasUsage(op) { - revert("should revert"); - } catch Error(string memory reason) { - used = abi.decode(bytes(reason), (uint256)); - console.log("validation gas usage :", used); - } - } - - function consoleGasUsage(UserOperation memory op) external { - uint256 gas = gasleft(); - vm.startPrank(address(entryPoint)); - kernel.validateUserOp(op, entryPoint.getUserOpHash(op), 0); - vm.stopPrank(); - revert(string(abi.encodePacked(gas - gasleft()))); - } -} - -// computes the hash of a permit -function getStructHash( - bytes4 sig, - uint48 validUntil, - uint48 validAfter, - address validator, - address executor, - bytes memory enableData -) pure returns (bytes32) { - return keccak256( - abi.encode( - keccak256("ValidatorApproved(bytes4 sig,uint256 validatorData,address executor,bytes enableData)"), - bytes4(sig), - uint256(uint256(uint160(validator)) | (uint256(validAfter) << 160) | (uint256(validUntil) << (48 + 160))), - executor, - keccak256(enableData) - ) - ); -} - -// computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer -function getTypedDataHash( - address sender, - bytes4 sig, - uint48 validUntil, - uint48 validAfter, - address validator, - address executor, - bytes memory enableData -) view returns (bytes32) { - return keccak256( - abi.encodePacked( - "\x19\x01", - _buildDomainSeparator("Kernel", "0.2.1", sender), - getStructHash(sig, validUntil, validAfter, validator, executor, enableData) - ) - ); -} - -function _buildDomainSeparator(string memory name, string memory version, address verifyingContract) - view - returns (bytes32) -{ - bytes32 hashedName = keccak256(bytes(name)); - bytes32 hashedVersion = keccak256(bytes(version)); - bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - - return keccak256(abi.encode(typeHash, hashedName, hashedVersion, block.chainid, address(verifyingContract))); -}