I have tried to use ecrecover() to verify the signature of a message.

I have looked at lots of references here and elsewhere, like:

and others.

But I still cannot get ecrecover() to return the signing address. So I am hoping that someone can point out some stupid mistake I am making.

Here is my code:

pragma solidity ^0.4.0;

contract test {

  function test() {
  }

  function verify(bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) constant returns (address) {
   address signer = ecrecover(_message, _v, _r, _s);
   return signer;
  }
}

Then in geth, I do:

var msg = web3.sha3("hello") "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"

eth.accounts[0] --> "0x7156526fbd7a3c72969b54f64e42c10fbb768c8a"

var sig = eth.sign(eth.accounts[0], msg) "0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac80388256084f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada1c"

var r = sig.substr(0,66) "0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608"

var s = "0x" + sig.substr(66,64) "0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada"

var v = 28 --> 28

test.verify(msg,v,r,s) --> "0x33692ee5cbf7ecdb8ca43ec9e815c47f3db8cd11"

...which of course, is NOT eth.accounts[0]

I am totally stumped. Is there anyone who can see what I am doing wrong?

  • Your first link has died... :-( – Greenonline Jun 28 at 17:34
up vote 33 down vote accepted

I was stuck on this issue as well for a very long time.

So the solution is: Add this prefix string to your Solidity smart contract.

function verify(bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(bool) {

    bytes memory prefix = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = keccak256(prefix, hash);
    return ecrecover(prefixedHash, v, r, s) == (Your Address);
}

So, with thanks to Adil (see above), here is the finished code that I used that definitely works with the process as outlined by me above:

pragma solidity ^0.4.0;

contract test {

  function test() {
  }

  function verify(bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) constant returns (address) {
    bytes memory prefix = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = sha3(prefix, _message);
    address signer = ecrecover(prefixedHash, _v, _r, _s);
    return signer;
  }
}
  • I am totally confused by this. In your example contract the _message is the hash you really sign on, right? I mean the _message is the message your sign on and generate the _v, _r and _s. Then in the contract you hash it the second time with the prefix and throw it into the ecrecover function, right? Why we need to do like this and it seems to me doesn't work.(I generate the _r, _s, _v in Truffle and test the contract in Remix) Can you tell me how to solve it? – Wang Jul 19 '17 at 6:49
  • yes, that is exactly how it works.. you hash a message and pass it into the contract. Then you hash it again with the prefix. Then it works. But I don't know why it works. I cannot help you further than that, except to say that if you follow they above steps you should get the right answer. If anyone could explain why this works, or why you even need that random prefix, that would be great! – Daniel Mermelstein Jul 20 '17 at 10:38

According to issue #3731:

Geth prepends the string \x19Ethereum Signed Message:\n<length of message> to all data before signing it (https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign). If you want to verify such a signature from Solidity, you'll have to prepend the same string in solidity before doing the ecrecovery.

Here's a working example I tested out using truffle:

Example.sol

pragma solidity ^0.4.0;

contract Example {
    function testRecovery(bytes32 h, uint8 v, bytes32 r, bytes32 s) returns (address) {
        bytes memory prefix = "\x19Ethereum Signed Message:\n32";
        bytes32 prefixedHash = sha3(prefix, h);
        address addr = ecrecover(prefixedHash, v, r, s);

        return addr;
    }
}

example.js (test)

var Example = artifacts.require('./Example.sol')

var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))

contract('Example', (accounts) => {
  var address = accounts[0]

  it('ecrecover result matches address', async function() {
    var instance = await Example.deployed()
    var msg = '0x8CbaC5e4d803bE2A3A5cd3DbE7174504c6DD0c1C'

    var h = web3.sha3(msg)
    var sig = web3.eth.sign(address, h).slice(2)
    var r = `0x${sig.slice(0, 64)}`
    var s = `0x${sig.slice(64, 128)}`
    var v = web3.toDecimal(sig.slice(128, 130)) + 27

    var result = await instance.testRecovery.call(h, v, r, s)
    assert.equal(result, address)
  })
})

Running test:

$ truffle test

Using network 'development'.

Compiling ./contracts/Example.sol...


  Contract: Example
    ✓ ecrecover result matches address (132ms)


  1 passing (147ms)

It's probably better to do the prefixing at the application level instead of in solidity contract since it'll be cheaper.

Related

Are you sure you're using the right data types? I've created a simple validation app using ecrecover a few weeks ago.

You can watch the code here: https://github.com/Shultzi/validator/blob/master/client/main.js

As you can see, I passed the h, r and s not as buffers but a hex. Also, make sure that in your smart contract the v value is uint8

  • BTW, I just checked your arguments in my code and I also received the address "0x3369..." I'm certain that it has something to do with the account you're signing. Which client you're using for signing? – shultz May 6 '17 at 18:41
  • hi thanks for your reply... I am signing via the geth console. Using version 1.6.1-stable-021c3c28 of geth and doing web3.eth.sign() . You think there is a problem there? – Daniel Mermelstein May 10 '17 at 17:06

The following article may explain the "\x19EthereumSignedMessage" part

https://blog.ricmoo.com/verifying-messages-in-solidity-50a94f82b2ca

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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