Получение адреса из ethereumjs-utils ecrecover

Я пытаюсь подписать строку с помощью web3.eth.sign(), а затем получить открытый ключ, которым я подписался, из подписи. Для этого я использую функцию ecrecover() из ethereumjs-utils, которая возвращает буфер. Когда я использую bufferToHex() для буфера, он дает шестнадцатеричную строку, которая слишком длинна для адреса — 130 символов, включая «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)

Я взял большую часть кода из ответа на рабочий процесс при подписании строки закрытым ключом с последующей проверкой подписи с помощью открытого ключа , но мне пришлось преобразовать «msg» в буфер, поскольку в противном случае ecrecover выдал ошибку типа.

Что мне нужно сделать, чтобы преобразовать буфер из ecrecover в адрес?

Какая у вас ОС? также вы использовали Метеор?
Я на macOS Sierra, и да, это использует метеор.

Ответы (2)

ecrecoverвозвращает открытый ключ , вам нужно преобразовать его в адрес с pubToAddress.

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

Также вы можете использовать fromRpcSigдля полученияv, r, s

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

Обратите внимание, что web3.eth.signдобавляет префикс к сообщению перед его подписью (см . спецификацию JSON-RPC ).

Вот как добавить его вручную:

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

С другой стороны, testrpc (по крайней мере, версия 3.0.5) не добавляет такой префикс.

Пример сеанса node.js + 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);
Спасибо за ответ, pubToAddress - это определенно то, что я искал, но он не дает тот же адрес, что и тот, с которым я подписался. У вас есть идеи, почему это может быть, или я должен отметить этот ответ как правильный и задать новый вопрос?
Все должно быть в порядке. Я обновил свой ответ примером сеанса. Может быть проблема с v, r, sвосстановлением, попробуйте использовать fromRpcSig.
Я скопировал ваш демонстрационный код и все еще получаю разные адреса, когда использую его.
Я не понимаю, почему это имеет какое-либо значение, но мне пришлось использовать строку var web3 = new Web3(new Web3.providers.HttpProvider(" localhost:8545" )) вместо ваших строк 2-4, так как он не смог найти модуль
Какие версии web3.js и ethereumjs-util вы используете? Кстати, я думаю, что теперь это заслуживает отдельного вопроса.

Пример node.js + ethereumjs-util другой метод проверки подписи

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