ecrecover
returns public key, you need to convert it to address with pubToAddress
.
pub = ethJsUtil.ecrecover(msg, v, r, s);
addrBuf = ethJsUtil.pubToAddress(pub);
addr = ethJsUtil.bufferToHex(addrBuf);
Also, you can use fromRpcSig
to get v, r, s
sig = web3.eth.sign(myAccount,msg)
res = ethJsUtil.fromRpcSig(sig)
pub = ethJsUtil.ecrecover(msg, res.v, res.r, res.s);
Please note that web3.eth.sign
adds prefix to the message before signing it (see JSON-RPC spec).
Here is how to add it manually:
const util = require('ethereumjs-util')
const msg = new Buffer('hello');
const sig = web3.eth.sign(web3.eth.accounts[0], '0x' + msg.toString('hex'));
const res = util.fromRpcSig(sig);
const prefix = new Buffer("\x19Ethereum Signed Message:\n");
const prefixedMsg = util.sha3(
Buffer.concat([prefix, new Buffer(String(msg.length)), msg])
);
const pubKey = util.ecrecover(prefixedMsg, res.v, res.r, res.s);
const addrBuf = util.pubToAddress(pubKey);
const addr = util.bufferToHex(addrBuf);
console.log(web3.eth.accounts[0], addr);
On the other hand, testrpc (at least version 3.0.5) does not add such prefix.
Example node.js + testrpc session:
const util = require('ethereumjs-util')
const msg = web3.sha3('hello!');
const sig = web3.eth.sign(web3.eth.accounts[0], msg);
const {v, r, s} = util.fromRpcSig(sig);
const pubKey = util.ecrecover(util.toBuffer(msg), v, r, s);
const addrBuf = util.pubToAddress(pubKey);
const addr = util.bufferToHex(addrBuf);
console.log(web3.eth.accounts[0], addr);
Geth adds prefix to the message before siginig it in web3.eth.sign
(see JSON-PRC spec).
Without this it can be possible to trick user to sign transaction (more here).
So, the correct code to sign message with web3.eth.sign
and recover address with ethereumjs-util.ecrecover
or (Solidity's ecrecover
) should add the prefix explicitly.
const util = require('ethereumjs-util')
const msg = new Buffer('hello');
const sig = web3.eth.sign(web3.eth.accounts[0], '0x' + msg.toString('hex'));
const res = util.fromRpcSig(sig);
const prefix = new Buffer("\x19Ethereum Signed Message:\n");
const prefixedMsg = util.sha3(
Buffer.concat([prefix, new Buffer(String(msg.length)), msg])
);
const pubKey = util.ecrecover(prefixedMsg, res.v, res.r, res.s);
const addrBuf = util.pubToAddress(pubKey);
const addr = util.bufferToHex(addrBuf);
console.log(web3.eth.accounts[0], addr);
There is misleading inconsistency with ethereumjs-testrpc
as it does not prefix message before signing it in web3.eth.sign
.
Best Answer
If you use
web3
to sign a message and then try to verify that message in a contract, you need to prepend the following string to the message before runningecrecover
in a contract:\x19Ethereum Signed Message:\n<length of message>
There are the two places where I found this.
https://github.com/ethereum/go-ethereum/issues/3731
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
The Ethereum wiki page has a link after the Example in the
eth_sign
documentation, that links to an example of using solidityecrecover
to verify signature calculated withweb3.eth.sign
. Pasting it here for completeness.https://gist.github.com/bas-vk/d46d83da2b2b4721efb0907aecdb7ebd