2

Below is a simple signature verification flow I wrote, along with a test case with Buidler (all mainly to learn how to verify signatures in a Solidity smart contract). The problem is that the address I recover from the signature and the associated hash is different from the address of the signer that signed the hash. Moreover, (weirdly) the recovered address changes if the hash being signed is changed. I'm sure that shouldn't happen. I'd really appreciate if you could point out what I'm doing wrong!

The contract:

//SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.6.0;

import "@openzeppelin/contracts/cryptography/ECDSA.sol";
import "@nomiclabs/buidler/console.sol";

contract Verification {

    bytes32 public hash;
    bytes public signature;

    constructor(string memory toHash) public {
        bytes32 temp = keccak256(abi.encode(toHash));
        hash = ECDSA.toEthSignedMessageHash(temp);
    }

    function setSignature(bytes calldata _signature) external {
        signature = _signature;
    }

    function verify(address signer) external view returns(bool) {
        
        address recoveredAddr = ECDSA.recover(hash, signature);
        //console.log(recoveredAddr);
        return recoveredAddr == signer;
    }

}

The test script. The first test passes, the second one fails:

const { expect } = require("chai");
const { ethers } = require("@nomiclabs/buidler");

describe("Verification", function() {

    let verify;
    let signer;

    before(async () => {
        const Verification = await ethers.getContractFactory("Verification");
        verify = await Verification.deploy("Hash this message");
        await verify.deployed();

        const addresses = await ethers.getSigners();
        signer = addresses[1];
    })

    // Test objective: To check that signature generated here in the script is stored, as is, in the contract.
    it("Should verify that signature in the contract and the ethers script are identical", async function() {

        const _hash = await verify.hash();
        const _signature = await signer.signMessage(_hash);

        await verify.setSignature(_signature);

        expect(await verify.signature()).to.equal(_signature);
    });

    it("Should verify that the signer signed the hash the contract was deployed with.", async function() {

        const _hash = await verify.hash();
        const _signature = await signer.signMessage(_hash);

        await verify.setSignature(_signature);
        
        signerAddr = await signer.getAddress();
        //console.log(`Signer address: ${signerAddr}`);

        expect(await verify.verify(signerAddr)).to.equal(true);
    })
})

Appreciate the help. Thanks!

1

A not in ethers documentation seems to apply in your case:

A common case is to sign a hash. In this case, if the hash is a string, it must be converted to an array first, using the arrayify utility function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.