16

I struggle heavy to actually recover the ethereum address from a hash and the {r, s, v} from a signature with the ecRecover-method in solidity.

What i want is to sign another ethereum-address. So i hash that address with sha3 and hex-encoding in web3, and sign it with my address. I then extract {r, s, v} from it and call my method. But i never get the correct result.

    function recover(bytes32 _h, uint8 _v, bytes32 _r, bytes32 _s){
    ecRecovery = ecrecover(_h, _v, _r, _s);
}

The address i want to sign is: 0x8CbaC5e4d803bE2A3A5cd3DbE7174504c6DD0c1C

The resulting hash for it for me is: 0x4d55361a8f362c8fc244dbd1e4968ca4b96d58e63a0f0c01a2cad1149106568a

After signing it, i get: 0x7709ac9c45926cc4d9791d868ad6c81883b624db44fbae1e62c0e6cc6c19784b74eb7f5ecdcf33f9c61946dc33b57964b1a0fee40ebf3b9c875424553cb865031b

From which i extract:

v = 27 r = 7709ac9c45926cc4d9791d868ad6c81883b624db44fbae1e62c0e6cc6c19784b s = 74eb7f5ecdcf33f9c61946dc33b57964b1a0fee40ebf3b9c875424553cb86503

feeding that into my function, i get: 0x9a5aAF102C82FaB302923bF8838678728A520103 whis is wrong. What i expect is: 0x1b7fBea9e926B1eAfC299b9Fab8f7174f466244e

My first instinct was to add the prefix of "\x19Ethereum Signed Message:\n", but that doesn't work either. Probably because i'm doing it wrong. So, is anybody able to help me with that one?

1

1 Answer 1

16

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

1
  • 1
    The web3.eth.sign function seems to take msg as first parameter and the address as second parameter now. See this answer if you are running into errors ->ethereum.stackexchange.com/a/40837/31604
    – Abe
    Feb 28, 2018 at 14:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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