Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions gas/ecdsa/report.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
No files changed, compilation skipped

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: 453373)
[PASS] test_initialize_twice() (gas: 20885)
[PASS] test_set_default_validator() (gas: 361374)
[PASS] test_set_execution() (gas: 411708)
[PASS] test_validate_signature() (gas: 163680)
[PASS] test_validate_userOp() (gas: 1727059)
Test result: ok. 8 passed; 0 failed; 0 skipped; finished in 2.90ms
[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_validate_signature() (gas: 163702)
[PASS] test_validate_userOp() (gas: 1727980)
Test result: ok. 8 passed; 0 failed; 0 skipped; finished in 2.81ms
| src/Kernel.sol:Kernel contract | | | | | |
|--------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1584184 | 8321 | | | | |
| 1585184 | 8326 | | | | |
| Function Name | min | avg | median | max | # calls |
| disableMode | 3765 | 3765 | 3765 | 3765 | 1 |
| getDefaultValidator | 341 | 341 | 341 | 341 | 1 |
Expand Down
20 changes: 20 additions & 0 deletions src/test/TestERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "solady/tokens/ERC20.sol";

contract TestERC20 is ERC20 {
constructor() ERC20() {}

function name() public pure override returns (string memory) {
return "TestERC20";
}

function symbol() public pure override returns (string memory) {
return "TST";
}

function mint(address _to, uint256 _amount) external {
_mint(_to, _amount);
}
}
16 changes: 14 additions & 2 deletions src/test/TestERC721.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";
import "solady/tokens/ERC721.sol";

contract TestERC721 is ERC721 {
constructor() ERC721("TestERC721", "TEST") {}
constructor() ERC721() {}

function name() public pure override returns (string memory) {
return "TestERC721";
}

function symbol() public pure override returns (string memory) {
return "TEST";
}

function tokenURI(uint256 _id) public pure override returns (string memory) {
return "";
}

function mint(address _to, uint256 _id) external {
_mint(_to, _id);
Expand Down
50 changes: 30 additions & 20 deletions src/validator/SessionKeyValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,23 @@ contract ExecuteSessionKeyValidator is IKernelValidator {

// TODO : gas spending limit
struct SessionData {
bool enabled;
uint48 validUntil;
uint48 validAfter;
bytes32 merkleRoot;
uint48 validAfter;
uint48 validUntil;
address paymaster;
bool enabled;
}

mapping(address sessionKey => mapping(address kernel => SessionData)) public sessionData;

function enable(bytes calldata _data) external payable {
address sessionKey = address(bytes20(_data[0:20]));
bytes32 merkleRoot = bytes32(_data[20:52]);
uint48 validUntil = uint48(bytes6(_data[52:58]));
uint48 validAfter = uint48(bytes6(_data[58:64]));
uint48 validAfter = uint48(bytes6(_data[52:58]));
uint48 validUntil = uint48(bytes6(_data[58:64]));
address paymaster = address(bytes20(_data[64:84]));

sessionData[sessionKey][msg.sender] = SessionData(true, validUntil, validAfter, merkleRoot);
sessionData[sessionKey][msg.sender] = SessionData(merkleRoot, validAfter, validUntil, paymaster, true);
}

function disable(bytes calldata _data) external payable {
Expand All @@ -58,27 +60,35 @@ contract ExecuteSessionKeyValidator is IKernelValidator {
payable
returns (uint256)
{
// userOp.signature = signature + permission + merkleProof
bytes calldata signature = userOp.signature[0:65]; // this may be problematic with stackup
address sessionKey = ECDSA.recover(userOpHash, signature);
// userOp.signature = signer + signature + permission + merkleProof
address sessionKey = address(bytes20(userOp.signature[0:20]));
bytes calldata signature = userOp.signature[20:85]; // this may be problematic with stackup
SessionData storage session = sessionData[sessionKey][msg.sender];
require(session.enabled, "SessionKeyValidator: session key not enabled");
if (session.merkleRoot == bytes32(0)) {
// sessionKey allowed to execute any tx
return _packValidationData(false, session.validUntil, session.validAfter);
}
if (session.paymaster == address(1)) {
require(userOp.paymasterAndData.length != 0, "SessionKeyValidator: paymaster not set");
} else if (session.paymaster != address(0)) {
require(
address(bytes20(userOp.paymasterAndData[0:20])) == session.paymaster,
"SessionKeyValidator: paymaster mismatch"
);
}

(Permission memory permission, bytes32[] memory merkleProof) =
abi.decode(userOp.signature[65:], (Permission, bytes32[]));
address target = address(bytes20(userOp.callData[16:36]));
require(target == permission.target, "SessionKeyValidator: target mismatch");
uint256 value = uint256(bytes32(userOp.callData[36:68]));
require(value <= permission.valueLimit, "SessionKeyValidator: value limit exceeded");
uint256 dataOffset = uint256(bytes32(userOp.callData[68:100]));
abi.decode(userOp.signature[85:], (Permission, bytes32[]));
require(address(bytes20(userOp.callData[16:36])) == permission.target, "SessionKeyValidator: target mismatch");
require(
uint256(bytes32(userOp.callData[36:68])) <= permission.valueLimit,
"SessionKeyValidator: value limit exceeded"
);
uint256 dataOffset = uint256(bytes32(userOp.callData[68:100])) + 4; // adding 4 for msg.sig
uint256 dataLength = uint256(bytes32(userOp.callData[dataOffset:dataOffset + 32]));
bytes calldata data = userOp.callData[dataOffset + 32:dataOffset + 32 + dataLength];
bytes4 sig = bytes4(data[0:4]);
require(sig == permission.sig, "SessionKeyValidator: sig mismatch");
require(bytes4(data[0:4]) == permission.sig, "SessionKeyValidator: sig mismatch");
for (uint256 i = 0; i < permission.rules.length; i++) {
ParamRule memory rule = permission.rules[i];
bytes32 param = bytes32(data[4 + rule.index * 32:4 + rule.index * 32 + 32]);
Expand All @@ -96,9 +106,9 @@ contract ExecuteSessionKeyValidator is IKernelValidator {
require(param != rule.param, "SessionKeyValidator: param mismatch");
}
}
bytes32 leaf = keccak256(abi.encodePacked(target, value, data));
bool result = MerkleProofLib.verify(merkleProof, session.merkleRoot, leaf);
return _packValidationData(result, session.validUntil, session.validAfter);
bool result = MerkleProofLib.verify(merkleProof, session.merkleRoot, keccak256(abi.encode(permission)))
&& (sessionKey == ECDSA.recover(ECDSA.toEthSignedMessageHash(userOpHash), signature));
return _packValidationData(!result, session.validUntil, session.validAfter);
}

function validCaller(address caller, bytes calldata _data) external view returns (bool) {
Expand Down
33 changes: 0 additions & 33 deletions test/foundry/ERC4337Utils.sol

This file was deleted.

14 changes: 2 additions & 12 deletions test/foundry/Kernel.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,11 @@ import "src/test/TestERC721.sol";
import "src/test/TestKernel.sol";
// test utils
import "forge-std/Test.sol";
import {ERC4337Utils} from "./ERC4337Utils.sol";
import {ERC4337Utils, KernelTestBase} from "./utils/ERC4337Utils.sol";

using ERC4337Utils for EntryPoint;

contract KernelTest is Test {
Kernel kernel;
Kernel kernelImpl;
KernelFactory factory;
EntryPoint entryPoint;
ECDSAValidator validator;
address owner;
uint256 ownerKey;
address payable beneficiary;
address factoryOwner;

contract KernelTest is KernelTestBase {
function setUp() public {
(owner, ownerKey) = makeAddrAndKey("owner");
(factoryOwner,) = makeAddrAndKey("factoryOwner");
Expand Down
81 changes: 2 additions & 79 deletions test/foundry/KernelExecution.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,14 @@ import "src/test/TestExecutor.sol";
import "src/test/TestERC721.sol";
// test utils
import "forge-std/Test.sol";
import {ERC4337Utils} from "./ERC4337Utils.sol";
import "./utils/ERC4337Utils.sol";
// test actions/validators
import "src/validator/ERC165SessionKeyValidator.sol";
import "src/executor/TokenActions.sol";

using ERC4337Utils for EntryPoint;

contract KernelExecutionTest is Test {
Kernel kernel;
Kernel kernelImpl;
KernelFactory factory;
EntryPoint entryPoint;
ECDSAValidator validator;
address owner;
uint256 ownerKey;
address payable beneficiary;
address factoryOwner;

contract KernelExecutionTest is KernelTestBase {
function setUp() public {
(owner, ownerKey) = makeAddrAndKey("owner");
(factoryOwner,) = makeAddrAndKey("factoryOwner");
Expand Down Expand Up @@ -225,71 +215,4 @@ contract KernelExecutionTest is Test {

assertEq(erc721.ownerOf(0), address(0xdead));
}

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 validAfter,
uint48 validUntil,
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) << 208) | (uint256(validUntil) << 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, validAfter, validUntil, 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)));
}
1 change: 0 additions & 1 deletion test/foundry/KernelMultiOwned.t.sol

This file was deleted.

Loading