8

I am trying to sign a string using web3.eth.sign() and then get the public key I signed with from the signature. To do this I am using ecrecover() from ethereumjs-utils, which returns a buffer. When I use bufferToHex() on the buffer it gives a hex string that is much too long to be an address - 130 characters including '0x'

web3.personal.unlockAccount(myAccount,pass)
msg = web3.sha3(aString)
sig = web3.eth.sign(myAccount,msg)
r = sig.slice(0, 66)
s = '0x' + sig.slice(66, 130)
v = '0x' + sig.slice(130, 132)
v = web3.toDecimal(v)
msg = ethJsUtil.toBuffer(msg)
addr = ethJsUtil.ecrecover(msg,v,r,s)
addr = ethJsUtil.bufferToHex(addr)

I took most of the code from the answer to workflow on signing a string with private key, followed by signature verification with public key but had to convert 'msg' to a buffer as ecrecover threw a type error otherwise.

What do I need to do to convert the buffer from ecrecover to an address?

  • Which OS your on? also you used Meteor ? – DappDev Feb 28 '17 at 13:44
  • I am on macOS Sierra and yes this is using meteor – Joe Feb 28 '17 at 13:47
8

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);
  • Thanks for answering the pubToAddress is definitely what I was looking for but it is not giving the same address as the one I signed with. Do you have any idea why this might be or should I mark this answer as correct and make a new question? – Joe Feb 28 '17 at 15:52
  • Everything should be ok. I updated my answer with example session. There might be a problem with v, r, s recovery, try to use fromRpcSig. – max taldykin Feb 28 '17 at 16:11
  • 1
    I copy pasted your demo code and still get different addresses when I use it. – Joe Feb 28 '17 at 16:21
  • I cant see why this would make any difference but I had to use the line var web3 = new Web3(new Web3.providers.HttpProvider("localhost:8545")) instead of your lines 2- 4 as it couldn't find the module – Joe Feb 28 '17 at 16:24
  • 1

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.