Obtener una dirección de ethereumjs-utils ecrecover

Estoy tratando de firmar una cadena usando web3.eth.sign() y luego obtener la clave pública con la que firmé de la firma. Para hacer esto, estoy usando ecrecover() de ethereumjs-utils, que devuelve un búfer. Cuando uso bufferToHex() en el búfer, da una cadena hexadecimal que es demasiado larga para ser una dirección: 130 caracteres, incluido '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)

Tomé la mayor parte del código de la respuesta al flujo de trabajo al firmar una cadena con clave privada, seguido de la verificación de firma con clave pública, pero tuve que convertir 'msg' en un búfer ya que ecrecover arrojó un error de tipo de lo contrario.

¿Qué debo hacer para convertir el búfer de ecrecover a una dirección?

¿En qué sistema operativo estás? También usaste Meteor?
Estoy en macOS Sierra y sí, esto está usando meteorito

Respuestas (2)

ecrecoverdevuelve la clave pública , debe convertirla en una dirección con pubToAddress.

pub     = ethJsUtil.ecrecover(msg, v, r, s);
addrBuf = ethJsUtil.pubToAddress(pub);
addr    = ethJsUtil.bufferToHex(addrBuf);

Además, puede utilizar fromRpcSigpara obtenerv, r, s

sig = web3.eth.sign(myAccount,msg)
res = ethJsUtil.fromRpcSig(sig)
pub = ethJsUtil.ecrecover(msg, res.v, res.r, res.s);

Tenga en cuenta que web3.eth.signagrega un prefijo al mensaje antes de firmarlo (consulte la especificación JSON-RPC ).

Aquí se explica cómo agregarlo manualmente:

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);

Por otro lado, testrpc (al menos la versión 3.0.5) no agrega dicho prefijo.

Ejemplo node.js + sesión testrpc:

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);
Gracias por responder, pubToAddress es definitivamente lo que estaba buscando, pero no da la misma dirección con la que firmé. ¿Tiene alguna idea de por qué podría ser esto o debería marcar esta respuesta como correcta y hacer una nueva pregunta?
Todo debería estar bien. Actualicé mi respuesta con una sesión de ejemplo. Puede haber un problema con la v, r, srecuperación, intente usar fromRpcSig.
Copié y pegué su código de demostración y sigo recibiendo direcciones diferentes cuando lo uso.
No veo por qué esto haría alguna diferencia, pero tuve que usar la línea var web3 = new Web3(new Web3.providers.HttpProvider(" localhost:8545" )) en lugar de sus líneas 2-4 ya que no pudo encontrar el módulo
¿Qué versiones de web3.js y ethereumjs-util está utilizando? Por cierto, creo que ahora esto merece una pregunta aparte.

Ejemplo node.js + ethereumjs-util otro método para validar la firma

const ethUtil = require("ethereumjs-util");

const inSignature = "0x...."; //user signed message
const message = "hello!";
const msgHex = ethUtil.bufferToHex(Buffer.from(message));
const msgBuffer = ethUtil.toBuffer(msgHex);
const msgHash = ethUtil.hashPersonalMessage(msgBuffer);

const signature = ethUtil.toBuffer(inSignature);

const sigParams = ethUtil.fromRpcSig(signature);
const publicKey = ethUtil.ecrecover(
            msgHash,
            sigParams.v,
            sigParams.r,
            sigParams.s
);

const sender = ethUtil.publicToAddress(publicKey);
const addr = ethUtil.bufferToHex(sender);

//now compare addr with user wallet address
if("0x<userWaller>" === addr) console.log("valid");