1

I do the following in geth:

var msgHash = web3.sha3("hello")
var signature = eth.sign(eth.accounts[0], msgHash)
var r = signature.slice(0,64)
var s = "0x" +signature.slice(64,128)
var v = signature.slice(128,130)

r = signature.slice(0,64) "0x6d49c891d29b33c292232f690c9972e17e0dbead7d4fc446bb4ce5892f0e55" s = signature.slice(64,128) "a22c9f3c20a7f0bc90666d1d1c6f269658a0ccd56b1db1812671e23331d8ad2c" v = signature.slice(128,130) "52"

I then call the following solidity code

function verify(bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) public {
    bytes memory prefix = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = keccak256(prefix, msgHash);
    a = ecrecover(prefixedHash, v, r, s);
}

Which returns the following: 0x0000000000000000000000000000000000000000

Absolutely stumped by this, I've taken a look at the following threads to no success:

ecrecover from Geth and web3.eth.sign

hextet
  • 1,583
  • 7
  • 28

3 Answers3

1

You have to take the 0x out before you do the slice - it's just to show that the string is hex, not part of the r value.

natewelch_
  • 12,021
  • 1
  • 29
  • 43
1

You need to get rid of the 0x prefix before you slice.

ethereumjs-util has a very handy function fromRpcSig, you can use it like

const eutil = require('ethereumjs-util')

const hash = web3.sha3('hello world')
const rpcSig = web3.eth.sign(web3.eth.coinbase, eutil.bufferToHex(hash))
const rsv = eutil.fromRpcSig(rpcSig)
// then call your ecrecover with 
// hash, eutil.bufferToHex(rsv.r), eutil.bufferToHex(rsv.s), rsv.v
libertylocked
  • 1,368
  • 8
  • 16
  • Thanks! Although I'm not very good at javascript I took a look at the library and was able to find a suitable python version. – hextet Feb 04 '18 at 02:45
1

Both flygoing and Liberty were correct in that I needed to remove the 0x prefix before slicing, but I needed to add the prefix back on after the slice which took me a little bit to figure out. I was able to use web3py to automatically prefix and do the slice, which still failed to recover the proper address. It wasn't until I re-added the 0x prefix to r and s that it worked. As a result I was able to come up with the following web3py code:

decryptedPrivateKey = w3.eth.account.decrypt(encrypted, password)
attribDict = w3.eth.account.sign(message_text="testmessage", private_key=decryptedPrivateKey)
msgHash = Web3.toHex(attribDict['messageHash'])
v = attribDict['v']
r = attribDict['r']
s = attribDict['s']
r = Web3.toHex(r)
s = Web3.toHex(s)
vrs = (v,r,s)
contract.functions.testRecovery(msgHash,v,r,s).transact({'from': w3.eth.accounts[0], 'gasPrice': 91000000000})
print("v:\t",v,"\nr:\t",r,"\ns:\t",s,"\nmsgHsh:\t",msgHash)
recoveredAddress = w3.eth.account.recover(msgHash,vrs=vrs)

The testRecovery solidity function I used:

function testRecovery(
    bytes32 _msgHash, 
    uint8 _v, 
    bytes32 _r,
    bytes32 _s)
    public
    returns (bool)
{
    returned = ecrecover(_msgHash, _v, _r, _s);
    if (returned == expected) {
        return true;
    } else {
        return false;
    }
}
hextet
  • 1,583
  • 7
  • 28
  • Here you are signing using web3.eth.account.sign(msg, private_key) but in your question you were signing using web3.eth.sign(msg, public_key).. i guess both work in different ways. web3.eth.account.sign() prepends the string "\x19Ethereum Signed Message:\n32" before signing the message on the other hand web3.eth.account.sign() dont prepend this string. so while decoding i guess web3.eth.sign() signature won't work. This is the case happening to me right now. – Sachin Tomar Apr 13 '19 at 05:51