From b8442f2b138b7832b2353b564bcdebb269446a46 Mon Sep 17 00:00:00 2001 From: taek Date: Wed, 17 Apr 2024 21:36:38 +0900 Subject: [PATCH] multisignature signer (#96) --- src/signer/MultiSignatureSigner.sol | 119 ++++++++++++++++++ .../MultiSignatureECDSAValidator.sol | 8 +- 2 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 src/signer/MultiSignatureSigner.sol diff --git a/src/signer/MultiSignatureSigner.sol b/src/signer/MultiSignatureSigner.sol new file mode 100644 index 0000000..4e4f3e9 --- /dev/null +++ b/src/signer/MultiSignatureSigner.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {ECDSA} from "solady/utils/ECDSA.sol"; +import {MerkleProofLib} from "solady/utils/MerkleProofLib.sol"; +import {ISigner} from "../interfaces/IERC7579Modules.sol"; +import {SignerBase} from "../sdk/moduleBase/SignerBase.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 MultiSignatureECDSASigner is SignerBase { + mapping(address => uint256) public usedIds; + mapping(bytes32 id => mapping(address wallet => address)) public signer; + event SignerRegistered(address indexed kernel, bytes32 indexed id, address indexed owner); + error NoSignerRegistered(); + + function isInitialized(address wallet) external view override returns (bool) { + return usedIds[wallet] > 0; + } + + function _signerOninstall(bytes32 id, bytes calldata _data) internal override { + if(signer[id][msg.sender] == address(0)) { + usedIds[msg.sender]++; + } + signer[id][msg.sender] = address(bytes20(_data[0:20])); + emit SignerRegistered(msg.sender, id, address(bytes20(_data[0:20]))); + } + + function _signerOnUninstall(bytes32 id, bytes calldata) internal override { + if(signer[id][msg.sender] == address(0)) { + revert NoSignerRegistered(); + } + delete signer[id][msg.sender]; + usedIds[msg.sender]--; + } + + function checkUserOpSignature(bytes32 id, PackedUserOperation calldata userOp, bytes32 userOpHash) + external + payable + override + returns (uint256) + { + bytes calldata sig = userOp.signature; + address owner = signer[id][msg.sender]; + 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 merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig); + if (owner != merkleRecovered) { + return SIG_VALIDATION_FAILED_UINT; + } + return SIG_VALIDATION_SUCCESS_UINT; + } + + function checkSignature(bytes32 id, address sender, bytes32 hash, bytes calldata sig) + external + view + override + returns (bytes4) + { + address owner = signer[id][msg.sender]; + 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 merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig); + if (owner != merkleRecovered) { + return ERC1271_INVALID; + } + return ERC1271_MAGICVALUE; + } +} diff --git a/src/validator/MultiSignatureECDSAValidator.sol b/src/validator/MultiSignatureECDSAValidator.sol index 6ad9004..e9084de 100644 --- a/src/validator/MultiSignatureECDSAValidator.sol +++ b/src/validator/MultiSignatureECDSAValidator.sol @@ -77,8 +77,8 @@ contract MultiSignatureECDSAValidator is IValidator, IHook { return SIG_VALIDATION_SUCCESS_UINT; } bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot); - address recovered = ECDSA.recover(ethRoot, ecdsaSig); - if (owner != recovered) { + address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig); + if (owner != merkleRecovered) { return SIG_VALIDATION_FAILED_UINT; } return SIG_VALIDATION_SUCCESS_UINT; @@ -112,8 +112,8 @@ contract MultiSignatureECDSAValidator is IValidator, IHook { return ERC1271_MAGICVALUE; } bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot); - address recovered = ECDSA.recover(ethRoot, ecdsaSig); - if (owner != recovered) { + address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig); + if (owner != merkleRecovered) { return ERC1271_INVALID; } return ERC1271_MAGICVALUE;