From e4dfaf47def87e234f0394ed3d87b0259731676f Mon Sep 17 00:00:00 2001 From: Builddddder Date: Mon, 23 Oct 2023 14:26:38 +0800 Subject: [PATCH] Add erc 1271 module implementation --- .../src/examples/modules/ERC1271Module.sol | 87 +++++++++++++++++++ contracts/test/example/ERC1271Module.t.sol | 52 +++++++++++ 2 files changed, 139 insertions(+) create mode 100644 contracts/src/examples/modules/ERC1271Module.sol create mode 100644 contracts/test/example/ERC1271Module.t.sol diff --git a/contracts/src/examples/modules/ERC1271Module.sol b/contracts/src/examples/modules/ERC1271Module.sol new file mode 100644 index 00000000..55e987d5 --- /dev/null +++ b/contracts/src/examples/modules/ERC1271Module.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.21; + +import { ECDSA } from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol"; +import { Strings } from "openzeppelin-contracts/contracts/utils/Strings.sol"; + +import { AbstractModule } from "../interface/AbstractModule.sol"; +import { AttestationPayload } from "../types/Structs.sol"; + +import "forge-std/console.sol"; + +contract ERC1271Module is AbstractModule { + address public owner; + constructor(address _owner) { + owner = _owner; + } + + /** + * @notice Recover the signer of hash, assuming it's an EOA account + * @dev Only for EthSign signatures + * @param _hash Hash of message that was signed + * @param _signature Signature encoded as (bytes32 r, bytes32 s, uint8 v) + */ + function recoverSigner( + bytes32 _hash, + bytes memory _signature + ) internal pure returns (address signer) { + require(_signature.length == 65, "Invalid signature length"); + bytes32 r; + bytes32 s; + uint8 v; + assembly { + r := mload(add(_signature, 0x20)) + s := mload(add(_signature, 0x40)) + v := byte(0, mload(add(_signature, 0x60))) + } + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { + revert("SignatureValidator#recoverSigner: invalid signature 's' value"); + } + if (v != 27 && v != 28) { + revert("SignatureValidator#recoverSigner: invalid signature 'v' value"); + } + + //address signee = abi.decode(attestationPayload.subject, (address)); + //uint256 nonce = abi.decode(attestationPayload.attestationData, (uint256)); + //bytes memory message = abi.encodePacked(signee, nonce); + //bytes32 digest = keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message)); + + signer = ecrecover(_hash, v, r, s); + // Prevent signer from being 0x0 + require( + signer != address(0x0), + "SignatureValidator#recoverSigner: INVALID_SIGNER" + ); + return signer; + } + + /** + * @notice Verifies that the signer is the owner of the signing contract. + */ + function isValidSignature( + bytes32 _hash, + bytes memory _signature + ) internal view returns (bytes4) { + // Validate signatures + address signer = recoverSigner(_hash, _signature); + if (signer == owner) { + return 0x1626ba7e; + } else { + return 0xffffffff; + } + } + + function run( + AttestationPayload memory attestationPayload, + bytes memory validationPayload, + address txSender, + uint256 /*value*/ + ) public view override { + require(txSender == owner, "Invalid sender"); + address signee = abi.decode(attestationPayload.subject, (address)); + uint256 nonce = abi.decode(attestationPayload.attestationData, (uint256)); + bytes memory message = abi.encodePacked(signee, nonce); + bytes32 digest = keccak256(bytes.concat("\x19Ethereum Signed Message:\n", message)); + require(isValidSignature(digest, validationPayload) == 0x1626ba7e, "SignatureValidator#isValidSignature: INVALID_SIGNER"); + } +} diff --git a/contracts/test/example/ERC1271Module.t.sol b/contracts/test/example/ERC1271Module.t.sol new file mode 100644 index 00000000..a0b9d24c --- /dev/null +++ b/contracts/test/example/ERC1271Module.t.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.21; + +import { Test } from "forge-std/Test.sol"; +import { ECDSA } from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol"; +import { AbstractModule } from "../../src/interface/AbstractModule.sol"; +import { ERC1271Module } from "../../src/example/ERC1271Module.sol"; +import { AttestationPayload } from "../../src/types/Structs.sol"; +import "forge-std/console.sol"; + +contract ERC1271ModuleTest is Test { + ERC1271Module private erc1271Module; + uint256 internal signerPrivateKey; + address internal signerAddress; + + event ModuleRegistered(string name, string description, address moduleAddress); + + function setUp() public { + (signerAddress, signerPrivateKey) = makeAddrAndKey("veraxUser"); + erc1271Module = new ERC1271Module(signerAddress); + vm.deal(signerAddress, 1 ether); + } + + function test_ERC1271Module_verifySignature() public { + address user = makeAddr("user"); + uint256 nonce = 1234567; + AttestationPayload memory attestationPayload = AttestationPayload( + bytes32(uint256(1234)), + 0, + abi.encode(user), + abi.encode(nonce) + ); + + bytes32 hash = keccak256(bytes.concat("\x19Ethereum Signed Message:\n", abi.encodePacked(user, nonce))); + /*console.log(message.length); + assembly { + mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash + mstore(0x1c, message) // 0x1c (28) is the length of the prefix + hash := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20) + }*/ + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, hash); + + bytes memory signature = abi.encodePacked(r, s, v); + + erc1271Module.run(attestationPayload, signature, signerAddress, 0); + } + + function test_EcRecoverModule_supportsInterface() public { + bool isAbstractModuleSupported = erc1271Module.supportsInterface(type(AbstractModule).interfaceId); + assertEq(isAbstractModuleSupported, true); + } +}