I'm trying to verify a message, however the ecrecover function is returning the incorrect address: 0x828c50fDeE9160C76EE160C9d8e9D4bc4Cc3D591

function verify(bytes32 message, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
    bytes memory prefix = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, message));
    address signer = ecrecover(prefixedHash, v, r, s);
    return signer;

Message = "test"

Message hash = 0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658

v = 28

r = 0x1e97a4e505996ba54c0003a79281e5c714afe5b93aa867d87a50e6d0c1bb5c81

s = 0x68cf3bbc7afb18e412d68d50c444ba046eea8adc28fa72cda5c12a4199e1b843

Address/Expected result = 0x70b224FF8089D40e4d91331f8284Bb8Ab9877E4B

I am using Ganache-cli. I have tested in Remix and it works however when I run using ganache and metamask I do not get correct answer. Not sure what is going wrong??? Many Thanks in advance.

EDIT (front-end)

var payload = "test"
// hash of message
var testData = web3.utils.sha3("test")

// ethereum address 
var address = accounts[0]

var signature = web3.eth.sign(testData,address)

signature = signature.slice(2)
var r = `0x${signature.slice(0, 64)}`
var s = `0x${signature.slice(64, 128)}`
var v = web3.utils.toDecimal(signature.slice(128, 130)) 

const response = await contract.methods.verify(testData, v, r, s).call()

The error is that your message was signed without the prefix. The message hash that you wrote is the hash of 'test' without the prefix, so use only message without the prefix in the contract. It works in remix.

if message is the hash of 'test' use this:

function verify(bytes32 message, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
    address signer = ecrecover(message, v, r, s);
    return signer;

if message is the string 'test' use this

function verify(string message, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
    bytes32 msgHash = keccak256(abi.encodePacked(message));
    address signer = ecrecover(msgHash, v, r, s);
    return signer;
  • Removed the prefix in the contract and still not getting the same address. Not sure what I'm doing wrong. Any suggestions? Thanks for your help Oct 31 '18 at 11:57
  • I edited the answer to show how I did it in remix. Let me know.
    – Jaime
    Oct 31 '18 at 12:02
  • Note that if message is 'test' then you should use 'string' instead of 'bytes32'
    – Jaime
    Oct 31 '18 at 12:09
  • Still not working. I am using your first answer, passing in web3.utils.sha3("test") as my first parameter. Do I have to cast this to bytes32 first? Oct 31 '18 at 12:22
  • A comment here: github.com/ethereum/web3.js/issues/392 says that test rpc needs the prefix I originally put in. Maybe remix gives behaves like geth? Oct 31 '18 at 12:28

Your issue is that you need to first hash the message payload before rehashing it again with the signature prefix.

Here is a fully functional example for you get to get all intricacies related to signature operations. It runs with web3 1.0 and should be executed with nodejs.

const Web3 = require('web3');
const utils = require('ethereumjs-util');

// The actual message to sign
const payload = "test";
// First hash the payload (equivalent to Solidity's keccack256)
const hashedPayload = Web3.utils.soliditySha3(payload);
// Then add the prefix and rehash (equivalent to Solidity's keccack256)
const paddedMessage = Web3.utils.soliditySha3("\x19Ethereum Signed Message:\n32", hashedPayload);
// Remove the '0x' hex prefix
const message = hashedPayload.substring(2, hashedPayload.length);
// Reeconde the message as a buffer
const messageBuffer = Buffer.from(message, 'hex');
console.log('message: ', message);

// Load signature parameters
const v = 28;
const r = '0x1e97a4e505996ba54c0003a79281e5c714afe5b93aa867d87a50e6d0c1bb5c81';
const s = '0x68cf3bbc7afb18e412d68d50c444ba046eea8adc28fa72cda5c12a4199e1b843';

// Expected signer address
const expected = '0x70b224FF8089D40e4d91331f8284Bb8Ab9877E4B'.toLowerCase();

// Same as Solidity's ecrecover function
const pub = utils.ecrecover(messageBuffer, v, r, s);
// Convert the output public key to address
const recoveredAddress = '0x' + utils.pubToAddress(pub).toString('hex');

// Print the result and show it works
console.log('recovered address: ', recoveredAddress);
console.log('success: ', recoveredAddress === expected);

Note that the Buffer/Hex part could still be simplified, but this should already get you going.

  • You say my issue is that I first hash the message payload before rehashing it again with the signature prefix however don't you do the same thing in your example? Oct 31 '18 at 12:31
  • You indeed need to hash the message once, then add the prefix and rehash this concatenated output. I did this in the code provided, assigning the output to paddedMessage. Is this more clear now? Oct 31 '18 at 12:34
  • I realise that I made a typo in the initial answer, just fixed it and it should be clear now. Oct 31 '18 at 12:35
  • diagram linked above says that for testrpc you do not need to prepend that prefix Oct 31 '18 at 12:57

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.