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


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.


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
  • 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 4 '18 at 2:45

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})
recoveredAddress = w3.eth.account.recover(msgHash,vrs=vrs)

The testRecovery solidity function I used:

function testRecovery(
    bytes32 _msgHash, 
    uint8 _v, 
    bytes32 _r,
    bytes32 _s)
    returns (bool)
    returned = ecrecover(_msgHash, _v, _r, _s);
    if (returned == expected) {
        return true;
    } else {
        return false;
  • 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. Apr 13 '19 at 5:51

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.