Skip to content

Commit

Permalink
feat: As a user, I want to have an example ecrecover module (#311)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhanymkanov authored Oct 23, 2023
1 parent c877819 commit 01a15b5
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
52 changes: 52 additions & 0 deletions contracts/src/examples/modules/ECDSAModule.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { ECDSA } from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol";

import { AbstractModule } from "../../interface/AbstractModule.sol";
import { AttestationPayload } from "../../types/Structs.sol";

/**
* @title ECDSA Module
* @author Consensys x DappSheriff
* @notice This contract illustrates a valid Module that is used to verify ECDSA signatures of payload
*/
contract ECDSAModule is AbstractModule, Ownable {
address public signer;
mapping(uint256 => bool) public usedNonces; // nonce => used

constructor(address _signer) {
signer = _signer;
}

/// @dev This empty method prevents Foundry from counting this contract in code coverage
function test() public {}

/**
* @notice This method is used to run the module's validation logic
* @param attestationPayload - AttestationPayload containing the user address as `subject` and nonce as `attestationData`
* @param validationPayload - Payload encoded with abi.encode(uint256).toEthSignedMessageHash().sign(signer)
*/
function run(
AttestationPayload memory attestationPayload,
bytes memory validationPayload,
address /*txSender*/,
uint256 /*value*/
) public override {
address signee = abi.decode(attestationPayload.subject, (address));
uint256 nonce = abi.decode(attestationPayload.attestationData, (uint256));
if (usedNonces[nonce]) {
revert("Nonce already used");
}

bytes32 hash = ECDSA.toEthSignedMessageHash(abi.encodePacked(signee, nonce));
require(ECDSA.recover(hash, validationPayload) == signer, "Wrong signature");

usedNonces[nonce] = true;
}

function setSigner(address _signer) external onlyOwner {
signer = _signer;
}
}
55 changes: 55 additions & 0 deletions contracts/test/example/ECDSAModule.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 { ECDSAModule } from "../../src/examples/modules/ECDSAModule.sol";
import { AttestationPayload } from "../../src/types/Structs.sol";

contract ECDSAModuleTest is Test {
ECDSAModule private ecdsaModule;
address private signer;
uint256 private signerPk;
AttestationPayload private attestationPayload;

address private user = makeAddr("veraxUser");
uint256 private nonce = 1234567;

function setUp() public {
(signer, signerPk) = makeAddrAndKey("veraxSigner");
ecdsaModule = new ECDSAModule(signer);

attestationPayload = AttestationPayload(bytes32(uint256(1234)), 0, abi.encode(user), abi.encode(nonce));
}

function test_ECDSAModule_validSignature() public {
bytes memory signature = makeSignature(signerPk);

ecdsaModule.run(attestationPayload, signature, signer, 0);
}

function test_ECDSAModule_revertInvalidSignature() public {
Account memory fakeSigner = makeAccount("user");

bytes memory signature = makeSignature(fakeSigner.key);

vm.expectRevert("Wrong signature");
ecdsaModule.run(attestationPayload, signature, user, 0);
}

function test_ECDSAModule_revertNonceUsed() public {
bytes memory signature = makeSignature(signerPk);

ecdsaModule.run(attestationPayload, signature, user, 0);
vm.expectRevert("Nonce already used");
ecdsaModule.run(attestationPayload, signature, user, 0);
}

function makeSignature(uint256 _signer) private view returns (bytes memory) {
bytes32 hash = ECDSA.toEthSignedMessageHash(abi.encodePacked(user, nonce));

(uint8 v, bytes32 r, bytes32 s) = vm.sign(_signer, hash);
return abi.encodePacked(r, s, v);
}
}

0 comments on commit 01a15b5

Please sign in to comment.