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

feat: As a user, I want to have an example ecrecover module #311

Merged
merged 2 commits into from
Oct 23, 2023
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
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);
}
}
Loading