I'm trying to find out how to derive a public key (not the address) of an Ethereum account by using ethereumjs-util's ecrecover
method.
Basically I get a transaction by hash using Web3, convert r
, s
and v
like in this answer, and pass them to ecrecover
. It does yield a public key, but that key does not correspond to the address of the sender of the transaction.
What am I doing wrong?
Code below:
const ethJsUtil = require('ethereumjs-util');
class EthPubKeyReg {
constructor(web3, chainId) {
this.chainId = chainId;
this.web3 = web3;
}
async extractPubKey(txHash, chainId) {
chainId = (chainId || this.chainId);
let tx = await this.web3.eth.getTransaction(txHash);
let v = parseInt(tx.v);
let msgHash = ethJsUtil.toBuffer(tx.hash);
let r = ethJsUtil.toBuffer(tx.r);
let s = ethJsUtil.toBuffer(tx.s);
let pubKey = ethJsUtil.ecrecover(msgHash, v, r, s);
console.log("address", ethJsUtil.bufferToHex(ethJsUtil.pubToAddress(pubKey)));
return pubKey;
}
async extractPubKeyString(txHash, chainId) {
return ethJsUtil.bufferToHex(await this.extractPubKey(txHash, chainId));
}
}
===== Update =====
I've updated my code the way @libertylocked suggested in his answer below:
const Web3 = require('web3');
const ethJsUtil = require('ethereumjs-util');
const Transaction = require('ethereumjs-tx');
const HDWalletProvider = require('truffle-hdwallet-provider');
let provider = new HDWalletProvider(
'some words to instantiate this truffe hdwallet provider',
"https://mainnet.infura.io/[ACCCESS TOKEN]"
);
let web3 = new Web3(provider);
/*
* Extracts the public key of the sender of a
* signed and mined transaction as Buffer
*/
async function extractPubKey(txHash, web3) {
const tx = await web3.eth.getTransaction(txHash) // insert txhash here
console.log("tx.from", tx.from);
const txDetails = new Transaction({
nonce: tx.nonce,
gasPrice: `0x${tx.gasPrice.toString(16)}`,
gasLimit: tx.gas,
to: tx.to,
value: `0x${tx.value.toString(16)}`,
data: tx.input,
chainId: web3.version.network,
r: tx.r,
s: tx.s,
v: tx.v,
});
const pubkey = txDetails.getSenderPublicKey()
console.log("txDetails.getSenderAddress", txDetails.getSenderAddress().toString('hex'));
console.log("Pubkey to address", ethJsUtil.bufferToHex(ethJsUtil.pubToAddress(pubkey)));
return pubkey;
}
/*
* Extracts the public key of the sender of a
* signed and mined transaction as hex string
*/
async function extractPubKeyAsString(txHash, web3) {
return ethJsUtil.bufferToHex(await extractPubKey(txHash, web3));
}
// link to tx on Etherscan: https://etherscan.io/tx/0xbee6f2a54e1c4921dec2d6834334acd3177ce42229e1d1c4d2490fb60839634e
extractPubKeyAsString('0xbee6f2a54e1c4921dec2d6834334acd3177ce42229e1d1c4d2490fb60839634e', web3)
.then(pubkey => {
console.log("pubkeystring", pubkey);
})
.finally(() => process.exit());
However, this still yields an incorrect sender address:
tx.from 0xBa34A08bcAe1AB74b5e52b6eE06b108945A387f0
txDetails.getSenderAddress efc92668485027e2d41bd229d66111ad0aaa8a16
Pubkey to address 0xefc92668485027e2d41bd229d66111ad0aaa8a16
pubkeystring 0x6d5441c4e53bb8c4dd69b16e79d5ca4f51a84eebaded3b6dfc5b51dd63ee7b5c5707ca1e788653b07872dd8c13293be840c93aeae582f2c080ff8a3aef79a24f
ethereumjs-tx
can help.