Extrahieren des öffentlichen Schlüssels aus einer signierten Ethereum-Nachricht oder -Transaktion in JavaScript

Ich versuche herauszufinden, wie ich einen öffentlichen Schlüssel (nicht die Adresse) eines Ethereum-Kontos mithilfe der Methode von ethereumjs-util ableiten ecrecover kann.

Grundsätzlich erhalte ich eine Transaktion per Hash mit Web3, convert rund slike vin this answer und übergebe sie an ecrecover. Es ergibt zwar einen öffentlichen Schlüssel, aber dieser Schlüssel entspricht nicht der Adresse des Absenders der Transaktion.

Was mache ich falsch?

Code unten:

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

===== Aktualisierung =====

Ich habe meinen Code so aktualisiert, wie @libertylocked in seiner Antwort unten vorgeschlagen hat:

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

Dies ergibt jedoch immer noch eine falsche Absenderadresse:

tx.from 0xBa34A08bcAe1AB74b5e52b6eE06b108945A387f0
txDetails.getSenderAddress efc92668485027e2d41bd229d66111ad0aaa8a16
Pubkey to address 0xefc92668485027e2d41bd229d66111ad0aaa8a16
pubkeystring 0x6d5441c4e53bb8c4dd69b16e79d5ca4f51a84eebaded3b6dfc5b51dd63ee7b5c5707ca1e788653b07872dd8c13293be840c93aeae582f2c080ff8a3aef79a24f
Sehen Sie sich github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md an . Der Transaktions-Hash ist nicht genau das, was in aktuellen Transaktionen signiert wird. ethereumjs-txkann helfen.

Antworten (1)

Die einfachste Lösung ist die Verwendung von ethereumjs-txmodule.

Wenn Sie verwenden web3@0.20:

const Transaction = require('ethereumjs-tx')
const tx = web3.eth.getTransaction(txHash) // insert txhash here
const pubkey = 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: 1, // mainnet network ID is 1. or use web3.version.network to find out
  r: tx.r,
  s: tx.s,
  v: tx.v,
}).getSenderPublicKey()

console.log(pubkey.toString('hex'))

Wenn Sie web3 1.0 Beta verwenden (und Sie verwenden ethereumjs-util), müssen Sie einige Werte in Hex-Strings mit vorangestelltem 0x konvertieren

new Transaction({
    nonce: tx.nonce,
    gasPrice: ethJsUtil.bufferToHex(new ethJsUtil.BN(tx.gasPrice)),
    gasLimit: tx.gas,
    to: tx.to,
    value: ethJsUtil.bufferToHex(new ethJsUtil.BN(tx.value)),
    data: tx.input,
    chainId: 1,
    r: tx.r,
    s: tx.s,
    v: tx.v,
});

Um zu überprüfen, ob der Pub-Schlüssel von derselben Absenderadresse abgeleitet werden kann, können Sie entweder publicToAddress(pubkey)from ethereumjs-utiloder tx.getSenderAddress()from verwendenethereumjs-tx

Der so erhaltene öffentliche Schlüssel scheint nicht mit der Adresse des Transaktionssenders übereinzustimmen
Hast du es versucht getSenderAddress().toString('hex')? Es sollte passen tx.from.
Es tut mir leid, das ergibt die gleiche falsche Adresse
Hast du vorgesorgt 0x? Versuchen Sie es andernfalls verifySignature()mit dem ethereumjs tx-Objekt
Ich habe meine Frage aktualisiert, um zu zeigen, wie ich die Korrektheit teste
Aus irgendeinem sehr seltsamen Grund funktioniert dieser Code in web3@0.20, aber nicht in 1.0 Beta
Yay! Es klappt! Das Kopfgeld gehört dir, sobald ich es dir zusprechen kann