diff --git a/gas/ecdsa/report.txt b/gas/ecdsa/report.txt index f8521e58..4454d1c6 100644 --- a/gas/ecdsa/report.txt +++ b/gas/ecdsa/report.txt @@ -1,19 +1,19 @@ No files changed, compilation skipped Running 8 tests for test/foundry/Kernel.t.sol:KernelTest -[PASS] test_disable_mode() (gas: 162839) -[PASS] test_external_call_default() (gas: 28889) -[PASS] test_external_call_execution() (gas: 453341) -[PASS] test_initialize_twice() (gas: 20907) -[PASS] test_set_default_validator() (gas: 361312) -[PASS] test_set_execution() (gas: 411646) +[PASS] test_disable_mode() (gas: 162625) +[PASS] test_external_call_default() (gas: 28867) +[PASS] test_external_call_execution() (gas: 453097) +[PASS] test_initialize_twice() (gas: 20885) +[PASS] test_set_default_validator() (gas: 361098) +[PASS] test_set_execution() (gas: 411432) [PASS] test_validate_signature() (gas: 163702) -[PASS] test_validate_userOp() (gas: 1727980) -Test result: ok. 8 passed; 0 failed; 0 skipped; finished in 2.81ms +[PASS] test_validate_userOp() (gas: 1711945) +Test result: ok. 8 passed; 0 failed; 0 skipped; finished in 2.87ms | src/Kernel.sol:Kernel contract | | | | | | |--------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 1585184 | 8326 | | | | | +| 1569370 | 8247 | | | | | | Function Name | min | avg | median | max | # calls | | disableMode | 3765 | 3765 | 3765 | 3765 | 1 | | getDefaultValidator | 341 | 341 | 341 | 341 | 1 | @@ -23,16 +23,16 @@ Test result: ok. 8 passed; 0 failed; 0 skipped; finished in 2.81ms | isValidSignature | 6047 | 6047 | 6047 | 6047 | 1 | | setDefaultValidator | 7870 | 7870 | 7870 | 7870 | 1 | | setExecution | 49874 | 49874 | 49874 | 49874 | 2 | -| validateUserOp | 46040 | 46234 | 46256 | 46386 | 4 | +| validateUserOp | 45764 | 45958 | 45980 | 46110 | 4 | | src/factory/KernelFactory.sol:KernelFactory contract | | | | | | |------------------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 437471 | 2268 | | | | | +| 564969 | 2862 | | | | | | Function Name | min | avg | median | max | # calls | -| createAccount | 130967 | 131744 | 130967 | 137967 | 9 | -| setImplementation | 22796 | 22796 | 22796 | 22796 | 8 | +| createAccount | 130989 | 131766 | 130989 | 137989 | 9 | +| setImplementation | 22862 | 22862 | 22862 | 22862 | 8 | diff --git a/src/Kernel.sol b/src/Kernel.sol index e3ddcc8f..3049f2bc 100644 --- a/src/Kernel.sol +++ b/src/Kernel.sol @@ -11,6 +11,8 @@ import "./abstract/Compatibility.sol"; import "./abstract/KernelStorage.sol"; import "./utils/KernelHelper.sol"; +import "forge-std/console.sol"; + enum Operation { Call, DelegateCall @@ -89,11 +91,19 @@ contract Kernel is EIP712, Compatibility, KernelStorage { /// @param userOpHash The hash of the user operation /// @param missingAccountFunds The funds needed to be reimbursed /// @return validationData The data used for validation - function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) + function validateUserOp(UserOperation memory userOp, bytes32 userOpHash, uint256 missingAccountFunds) external payable returns (uint256 validationData) { + bytes calldata userOpSignature; + uint256 userOpEndOffset; + assembly { + userOpEndOffset := add(calldataload(0x04), 0x24) + userOpSignature.offset := add(calldataload(add(userOpEndOffset, 0x120)), userOpEndOffset) + userOpSignature.length := calldataload(sub(userOpSignature.offset, 0x20)) + } + if (msg.sender != address(entryPoint)) { revert NotEntryPoint(); } @@ -102,43 +112,49 @@ contract Kernel is EIP712, Compatibility, KernelStorage { storage_slot_1 := sload(KERNEL_STORAGE_SLOT_1) } // mode based signature - bytes4 mode = bytes4(userOp.signature[0:4]); // mode == 00..00 use validators + bytes4 mode = bytes4(userOpSignature[0:4]); // mode == 00..00 use validators // mode == 0x00000000 use sudo validator // mode == 0x00000001 use given validator // mode == 0x00000002 enable validator - UserOperation memory op = userOp; IKernelValidator validator; if (mode == 0x00000000) { // sudo mode (use default validator) - op.signature = userOp.signature[4:]; + userOpSignature = userOpSignature[4:]; assembly { validator := shr(80, storage_slot_1) } } else if (mode & (storage_slot_1 << 224) != 0x00000000) { revert DisabledMode(); } else if (mode == 0x00000001) { - bytes4 sig = bytes4(userOp.callData[0:4]); - ExecutionDetail storage detail = getKernelStorage().execution[sig]; + bytes calldata userOpCallData; + assembly { + userOpCallData.offset := add(calldataload(add(userOpEndOffset, 0x40)), userOpEndOffset) + userOpCallData.length := calldataload(sub(userOpCallData.offset, 0x20)) + } + ExecutionDetail storage detail = getKernelStorage().execution[bytes4(userOpCallData[0:4])]; validator = detail.validator; if (address(validator) == address(0)) { assembly { validator := shr(80, storage_slot_1) } } - op.signature = userOp.signature[4:]; + userOpSignature = userOpSignature[4:]; validationData = (uint256(detail.validAfter) << 208) | (uint256(detail.validUntil) << 160); } else if (mode == 0x00000002) { - bytes4 sig = bytes4(userOp.callData[0:4]); + bytes calldata userOpCallData; + assembly { + userOpCallData.offset := add(calldataload(add(userOpEndOffset, 0x40)), userOpEndOffset) + userOpCallData.length := calldataload(sub(userOpCallData.offset, 0x20)) + } // use given validator - // userOp.signature[4:10] = validAfter, - // userOp.signature[10:16] = validUntil, - // userOp.signature[16:36] = validator address, - validator = IKernelValidator(address(bytes20(userOp.signature[16:36]))); + // userOpSignature[4:10] = validAfter, + // userOpSignature[10:16] = validUntil, + // userOpSignature[16:36] = validator address, + validator = IKernelValidator(address(bytes20(userOpSignature[16:36]))); bytes calldata enableData; - bytes calldata remainSig; - (validationData, enableData, remainSig) = _approveValidator(sig, userOp.signature); + (validationData, enableData, userOpSignature) = + _approveValidator(bytes4(userOpCallData[0:4]), userOpSignature); validator.enable(enableData); - op.signature = remainSig; } else { return SIG_VALIDATION_FAILED; } @@ -148,8 +164,9 @@ contract Kernel is EIP712, Compatibility, KernelStorage { } //ignore failure (its EntryPoint's job to verify, not account.) } + userOp.signature = userOpSignature; validationData = - _intersectValidationData(validationData, validator.validateUserOp(op, userOpHash, missingAccountFunds)); + _intersectValidationData(validationData, validator.validateUserOp(userOp, userOpHash, missingAccountFunds)); return validationData; } diff --git a/src/factory/KernelFactory.sol b/src/factory/KernelFactory.sol index a35338ac..ba6b5ba4 100644 --- a/src/factory/KernelFactory.sol +++ b/src/factory/KernelFactory.sol @@ -9,16 +9,22 @@ import "src/validator/ECDSAValidator.sol"; import "solady/auth/Ownable.sol"; contract KernelFactory is AdminLessERC1967Factory, Ownable { + IEntryPoint public entryPoint; mapping(address => bool) public isAllowedImplementation; - constructor(address _owner) { + constructor(address _owner, IEntryPoint _entryPoint) { _initializeOwner(_owner); + entryPoint = _entryPoint; } function setImplementation(address _implementation, bool _allow) external onlyOwner { isAllowedImplementation[_implementation] = _allow; } + function setEntryPoint(IEntryPoint _entryPoint) external onlyOwner { + entryPoint = _entryPoint; + } + function createAccount(address _implementation, bytes calldata _data, uint256 _index) external payable @@ -33,4 +39,17 @@ contract KernelFactory is AdminLessERC1967Factory, Ownable { bytes32 salt = bytes32(uint256(keccak256(abi.encodePacked(_data, _index))) & type(uint96).max); return predictDeterministicAddress(salt); } + + // stake functions + function addStake(uint32 unstakeDelaySec) external payable onlyOwner { + entryPoint.addStake{value: msg.value}(unstakeDelaySec); + } + + function unlockStake() external onlyOwner { + entryPoint.unlockStake(); + } + + 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 26a73f1b..3ba682ef 100644 --- a/test/foundry/Kernel.t.sol +++ b/test/foundry/Kernel.t.sol @@ -21,7 +21,7 @@ contract KernelTest is KernelTestBase { (factoryOwner,) = makeAddrAndKey("factoryOwner"); entryPoint = new EntryPoint(); kernelImpl = new Kernel(entryPoint); - factory = new KernelFactory(factoryOwner); + factory = new KernelFactory(factoryOwner, entryPoint); vm.startPrank(factoryOwner); factory.setImplementation(address(kernelImpl), true); vm.stopPrank(); diff --git a/test/foundry/KernelExecution.t.sol b/test/foundry/KernelExecution.t.sol index b97e60eb..990f2c5d 100644 --- a/test/foundry/KernelExecution.t.sol +++ b/test/foundry/KernelExecution.t.sol @@ -24,7 +24,7 @@ contract KernelExecutionTest is KernelTestBase { (factoryOwner,) = makeAddrAndKey("factoryOwner"); entryPoint = new EntryPoint(); kernelImpl = new Kernel(entryPoint); - factory = new KernelFactory(factoryOwner); + factory = new KernelFactory(factoryOwner, entryPoint); vm.startPrank(factoryOwner); factory.setImplementation(address(kernelImpl), true); vm.stopPrank();