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?

user2762996
  • 293
  • 2
  • 6
  • Maybe this will help you: https://ethereum.stackexchange.com/questions/1777/workflow-on-signing-a-string-with-private-key-followed-by-signature-verificatio – Michał Kłeczek Jul 07 '17 at 06:22

1 Answers1

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

Miguel Mota
  • 5,143
  • 29
  • 47
  • 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 ->https://ethereum.stackexchange.com/a/40837/31604 – Abe Feb 28 '18 at 14:43