Skip to content
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

multisignature ecdsa validator #95

Merged
merged 1 commit into from
Apr 17, 2024
Merged
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
133 changes: 133 additions & 0 deletions src/validator/MultiSignatureECDSAValidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {ECDSA} from "solady/utils/ECDSA.sol";
import {MerkleProofLib} from "solady/utils/MerkleProofLib.sol";
import {IValidator, IHook} from "../interfaces/IERC7579Modules.sol";
import {PackedUserOperation} from "../interfaces/PackedUserOperation.sol";
import {
SIG_VALIDATION_SUCCESS_UINT,
SIG_VALIDATION_FAILED_UINT,
MODULE_TYPE_VALIDATOR,
MODULE_TYPE_HOOK,
ERC1271_MAGICVALUE,
ERC1271_INVALID
} from "../types/Constants.sol";

struct ECDSAValidatorStorage {
address owner;
}

contract MultiSignatureECDSAValidator is IValidator, IHook {
event OwnerRegistered(address indexed kernel, address indexed owner);

mapping(address => ECDSAValidatorStorage) public ecdsaValidatorStorage;

function onInstall(bytes calldata _data) external payable override {
if (_isInitialized(msg.sender)) revert AlreadyInitialized(msg.sender);
address owner = address(bytes20(_data[0:20]));
ecdsaValidatorStorage[msg.sender].owner = owner;
emit OwnerRegistered(msg.sender, owner);
}

function onUninstall(bytes calldata) external payable override {
if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
delete ecdsaValidatorStorage[msg.sender];
}

function isModuleType(uint256 typeID) external pure override returns (bool) {
return typeID == MODULE_TYPE_VALIDATOR || typeID == MODULE_TYPE_HOOK;
}

function isInitialized(address smartAccount) external view override returns (bool) {
return _isInitialized(smartAccount);
}

function _isInitialized(address smartAccount) internal view returns (bool) {
return ecdsaValidatorStorage[smartAccount].owner != address(0);
}

function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
external
payable
override
returns (uint256)
{
bytes calldata sig = userOp.signature;
address owner = ecdsaValidatorStorage[msg.sender].owner;
if (sig.length == 65) {
// simple ecdsa verification
if (owner == ECDSA.recover(userOpHash, sig)) {
return SIG_VALIDATION_SUCCESS_UINT;
}
bytes32 ethHash = ECDSA.toEthSignedMessageHash(userOpHash);
address recovered = ECDSA.recover(ethHash, sig);
if (owner != recovered) {
return SIG_VALIDATION_FAILED_UINT;
}
return SIG_VALIDATION_SUCCESS_UINT;
}
bytes memory ecdsaSig = sig[0:65];
bytes32 merkleRoot = bytes32(sig[65:97]);
bytes32[] memory proof = abi.decode(sig[97:], (bytes32[]));
require(MerkleProofLib.verify(proof, merkleRoot, userOpHash), "hash is not in proof");
// simple ecdsa verification
if (owner == ECDSA.recover(merkleRoot, ecdsaSig)) {
return SIG_VALIDATION_SUCCESS_UINT;
}
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
address recovered = ECDSA.recover(ethRoot, ecdsaSig);
if (owner != recovered) {
return SIG_VALIDATION_FAILED_UINT;
}
return SIG_VALIDATION_SUCCESS_UINT;
}

function isValidSignatureWithSender(address, bytes32 hash, bytes calldata sig)
external
view
override
returns (bytes4)
{
address owner = ecdsaValidatorStorage[msg.sender].owner;
if (sig.length == 65) {
// simple ecdsa verification
if (owner == ECDSA.recover(hash, sig)) {
return ERC1271_MAGICVALUE;
}
bytes32 ethHash = ECDSA.toEthSignedMessageHash(hash);
address recovered = ECDSA.recover(ethHash, sig);
if (owner != recovered) {
return ERC1271_INVALID;
}
return ERC1271_MAGICVALUE;
}
bytes memory ecdsaSig = sig[0:65];
bytes32 merkleRoot = bytes32(sig[65:97]);
bytes32[] memory proof = abi.decode(sig[97:], (bytes32[]));
require(MerkleProofLib.verify(proof, merkleRoot, hash), "hash is not in proof");
// simple ecdsa verification
if (owner == ECDSA.recover(merkleRoot, ecdsaSig)) {
return ERC1271_MAGICVALUE;
}
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
address recovered = ECDSA.recover(ethRoot, ecdsaSig);
if (owner != recovered) {
return ERC1271_INVALID;
}
return ERC1271_MAGICVALUE;
}

function preCheck(address msgSender, uint256 value, bytes calldata)
external
payable
override
returns (bytes memory)
{
require(msgSender == ecdsaValidatorStorage[msg.sender].owner, "ECDSAValidator: sender is not owner");
return hex"";
}

function postCheck(bytes calldata hookData, bool success, bytes calldata res) external payable override {}
}
Loading