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
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/solady"]
path = lib/solady
url = https://github.com/vectorized/solady
[submodule "lib/I4337"]
path = lib/I4337
url = https://github.com/leekt/I4337
[submodule "lib/FreshCryptoLib"]
path = lib/FreshCryptoLib
url = https://github.com/rdubois-crypto/FreshCryptoLib
[submodule "lib/solady"]
path = lib/solady
url = https://github.com/vectorized/solady
2 changes: 1 addition & 1 deletion lib/forge-std
2 changes: 1 addition & 1 deletion lib/solady
Submodule solady updated 53 files
+575 −531 .gas-snapshot
+3 −2 .github/workflows/ci-all-via-ir.yml
+1 −1 .github/workflows/ci-wake.yml
+7 −6 .github/workflows/ci.yml
+0 −4 .gitmodules
+3 −1 README.md
+1 −1 foundry.toml
+7 −7 js/solady.js
+0 −1 lib/ds-test
+1 −1 package.json
+2 −0 src/Milady.sol
+26 −1 src/accounts/ERC4337.sol
+1 −1 src/accounts/ERC4337Factory.sol
+1 −1 src/accounts/ERC6551.sol
+1 −1 src/accounts/ERC6551Proxy.sol
+4 −1 src/tokens/ERC4626.sol
+4 −4 src/tokens/ERC6909.sol
+55 −33 src/tokens/ERC721.sol
+7 −11 src/utils/DateTimeLib.sol
+0 −1 src/utils/ERC1967Factory.sol
+138 −88 src/utils/FixedPointMathLib.sol
+185 −0 src/utils/Initializable.sol
+3 −3 src/utils/JSONParserLib.sol
+17 −22 src/utils/LibBit.sol
+107 −6 src/utils/LibClone.sol
+25 −0 src/utils/LibPRNG.sol
+18 −9 src/utils/LibString.sol
+19 −20 src/utils/LibZip.sol
+82 −9 src/utils/MetadataReaderLib.sol
+87 −28 src/utils/MinHeapLib.sol
+71 −58 src/utils/RedBlackTreeLib.sol
+55 −0 src/utils/ReentrancyGuard.sol
+7 −0 test/ERC4337.t.sol
+3 −3 test/ERC6909.t.sol
+29 −0 test/ERC721.t.sol
+99 −16 test/FixedPointMathLib.t.sol
+159 −0 test/Initializable.t.sol
+29 −1 test/JSONParserLib.t.sol
+22 −0 test/LibBit.t.sol
+50 −1 test/LibClone.t.sol
+33 −0 test/LibPRNG.t.sol
+5 −0 test/LibString.t.sol
+61 −28 test/MetadataReaderLib.t.sol
+157 −8 test/MinHeapLib.t.sol
+88 −32 test/RedBlackTree.t.sol
+86 −0 test/ReentrancyGuard.t.sol
+29 −1 test/utils/TestPlus.sol
+1 −1 test/utils/forge-std/Test.sol
+517 −0 test/utils/forge-std/ds-test/test.sol
+2 −2 test/utils/mocks/MockERC6909.sol
+11 −0 test/utils/mocks/MockERC721.sol
+80 −0 test/utils/mocks/MockInitializable.sol
+136 −0 test/utils/mocks/MockReentrancyGuard.sol
32 changes: 21 additions & 11 deletions src/validator/WeightedECDSAValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
proposal.status = ProposalStatus.Rejected;
}

function validateUserOp(UserOperation calldata userOp, bytes32, uint256)
function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256)
external
payable
returns (ValidationData validationData)
Expand All @@ -186,10 +186,10 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
bytes calldata sig = userOp.signature;
// parse sig with 65 bytes
uint256 sigCount = sig.length / 65;
require(sigCount > 0, "No sig");
address signer;
VoteStorage storage vote;
for (uint256 i = 0; i < sigCount && !passed; i++) {
// last sig is for userOpHash verification
for (uint256 i = 0; i < sigCount - 1 && !passed; i++) {
signer = ECDSA.recover(
_hashTypedData(
keccak256(abi.encode(keccak256("Approve(bytes32 callDataAndNonceHash)"), callDataAndNonceHash))
Expand All @@ -206,18 +206,28 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
passed = true;
}
}
if (passed) {
validationData = packValidationData(ValidAfter.wrap(0), ValidUntil.wrap(0));
// userOpHash verification for the last sig
signer = ECDSA.recover(ECDSA.toEthSignedMessageHash(userOpHash), sig[sig.length - 65:]);
vote = voteStatus[callDataAndNonceHash][signer][msg.sender];
if (vote.status == VoteStatus.NA) {
vote.status = VoteStatus.Approved;
totalWeight += guardian[signer][msg.sender].weight;
if (totalWeight >= threshold) {
passed = true;
}
}
if (passed && guardian[signer][msg.sender].weight != 0) {
proposal.status = ProposalStatus.Executed;
} else {
validationData = SIG_VALIDATION_FAILED;
return packValidationData(ValidAfter.wrap(0), ValidUntil.wrap(0));
}
} else if (proposal.status == ProposalStatus.Approved || passed) {
validationData = packValidationData(proposal.validAfter, ValidUntil.wrap(0));
proposal.status = ProposalStatus.Executed;
} else {
return SIG_VALIDATION_FAILED;
address signer = ECDSA.recover(ECDSA.toEthSignedMessageHash(userOpHash), userOp.signature);
if (guardian[signer][msg.sender].weight != 0) {
proposal.status = ProposalStatus.Executed;
return packValidationData(ValidAfter.wrap(0), ValidUntil.wrap(0));
}
}
return SIG_VALIDATION_FAILED;
}

function getApproval(address kernel, bytes32 hash) public view returns (uint256 approvals, bool passed) {
Expand Down
158 changes: 158 additions & 0 deletions test/foundry/validator/WeightedECDSAValidator.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
pragma solidity ^0.8.0;

import {IEntryPoint} from "I4337/interfaces/IEntryPoint.sol";
import "src/Kernel.sol";
import "src/validator/WeightedECDSAValidator.sol";
// test artifacts
// test utils
import "forge-std/Test.sol";
import {ERC4337Utils} from "src/utils/ERC4337Utils.sol";
import {KernelTestBase} from "src/utils/KernelTestBase.sol";
import {TestExecutor} from "src/mock/TestExecutor.sol";
import {TestValidator} from "src/mock/TestValidator.sol";
import {IKernel} from "src/interfaces/IKernel.sol";

using ERC4337Utils for IEntryPoint;

contract KernelWeightedECDSATest is KernelTestBase {
address[] public owners;
uint256[] public ownerKeys;
uint24[] public weights;
uint24 public threshold;
uint48 public delay;

function setUp() public virtual {
_initialize();
defaultValidator = new WeightedECDSAValidator();
owners = new address[](3);
ownerKeys = new uint256[](3);
(owners[0], ownerKeys[0]) = makeAddrAndKey("owner0");
(owners[1], ownerKeys[1]) = makeAddrAndKey("owner1");
(owners[2], ownerKeys[2]) = makeAddrAndKey("owner2");
weights = [uint24(1), uint24(2), uint24(3)];
threshold = 3;
delay = 0;
_setAddress();
_setExecutionDetail();
}

function test_ignore() external {}

function _setExecutionDetail() internal virtual override {
executionDetail.executor = address(new TestExecutor());
executionSig = TestExecutor.doNothing.selector;
executionDetail.validator = new TestValidator();
}

function getEnableData() internal view virtual override returns (bytes memory) {
return "";
}

function getValidatorSignature(UserOperation memory) internal view virtual override returns (bytes memory) {
return "";
}

function getOwners() internal view override returns (address[] memory) {
return owners;
}

function getInitializeData() internal view override returns (bytes memory) {
bytes memory data = abi.encode(owners, weights, threshold, delay);
return abi.encodeWithSelector(KernelStorage.initialize.selector, defaultValidator, data);
}

function test_external_call_execute_success() external override {
vm.skip(true);
}

function test_external_call_default() external override {
vm.skip(true);
}

function test_external_call_execute_delegatecall_success() external override {
vm.skip(true);
}

function test_external_call_batch_execute_success() external override {
vm.skip(true);
}

function signUserOp(UserOperation memory op) internal view override returns (bytes memory) {
bytes32 calldataAndNonceHash = keccak256(abi.encode(op.sender, op.callData, op.nonce));

bytes32 digest = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256("WeightedECDSAValidator"),
keccak256("0.0.2"),
block.chainid,
address(defaultValidator)
)
);

bytes32 structHash =
keccak256(abi.encode(keccak256("Approve(bytes32 callDataAndNonceHash)"), calldataAndNonceHash));
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, digest) // Store the domain separator.
mstore(0x3a, structHash) // Store the struct hash.
digest := keccak256(0x18, 0x42)
// Restore the part of the free memory slot that was overwritten.
mstore(0x3a, 0)
}

(uint8 v0, bytes32 r0, bytes32 s0) = vm.sign(ownerKeys[0], digest);
(uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(ownerKeys[1], digest);
bytes memory opSig = entryPoint.signUserOpHash(vm, ownerKeys[2], op);
return abi.encodePacked(bytes4(0x00000000), r0, s0, v0, r1, s1, v1, opSig);
}

function getWrongSignature(UserOperation memory op) internal view override returns (bytes memory) {
return abi.encodePacked(bytes4(0x00000000), entryPoint.signUserOpHash(vm, ownerKeys[0], op));
}

function signHash(bytes32 hash) internal view override returns (bytes memory) {
(uint8 v0, bytes32 r0, bytes32 s0) = vm.sign(ownerKeys[0], hash);
(uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(ownerKeys[1], hash);
(uint8 v2, bytes32 r2, bytes32 s2) = vm.sign(ownerKeys[2], hash);
return abi.encodePacked(r0, s0, v0, r1, s1, v1, r2, s2, v2);
}

function getWrongSignature(bytes32 hash) internal view override returns (bytes memory) {
(uint8 v0, bytes32 r0, bytes32 s0) = vm.sign(ownerKeys[0], hash);
(uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(ownerKeys[1] + 1, hash);
(uint8 v2, bytes32 r2, bytes32 s2) = vm.sign(ownerKeys[2] + 1, hash);
return abi.encodePacked(r0, s0, v0, r1, s1, v1, r2, s2, v2);
}

function test_default_validator_enable() external override {
//UserOperation memory op = buildUserOperation(
// abi.encodeWithSelector(
// IKernel.execute.selector,
// address(defaultValidator),
// 0,
// abi.encodeWithSelector(ECDSAValidator.enable.selector, abi.encodePacked(address(0xdeadbeef))),
// Operation.Call
// )
//);
//performUserOperationWithSig(op);
//(address owner) = ECDSAValidator(address(defaultValidator)).ecdsaValidatorStorage(address(kernel));
//assertEq(owner, address(0xdeadbeef), "owner should be 0xdeadbeef");
}

function test_default_validator_disable() external override {
//UserOperation memory op = buildUserOperation(
// abi.encodeWithSelector(
// IKernel.execute.selector,
// address(defaultValidator),
// 0,
// abi.encodeWithSelector(ECDSAValidator.disable.selector, ""),
// Operation.Call
// )
//);
//performUserOperationWithSig(op);
//(address owner) = ECDSAValidator(address(defaultValidator)).ecdsaValidatorStorage(address(kernel));
//assertEq(owner, address(0), "owner should be 0");
}
}