GithubHelp home page GithubHelp logo

Comments (8)

cgewecke avatar cgewecke commented on June 10, 2024 1

Thanks @jochem-brouwer! That's a great answer.

from ethereumjs-util.

cgewecke avatar cgewecke commented on June 10, 2024

@kss-espeo

I think you might need to begin with the "raw transaction hash" rather than the transaction id.

The raw tx for 0x26eed054f1ef51ae7a6c94841469c82a20f2deda84affde461b7d3887677bda8 can be found at this etherscan link:

The @ethereumjs/tx library executes the ecrecover logic you're looking for in the method getSenderPublicKey

Usage for your example looks like:

import { Transaction } from '@ethereumjs/tx';

const rawTx = toBuffer('0xf86b0b85250523760082520894eafaf9bb8f35235d0df61275e86fd65d9ef2c3f9870aaa0065c66b8b8026a05fd883bb01a10915ebc06621b925bd6d624cb6768976b73c0d468b31f657d15ba0121d855c539a23aadf6f06ac21165db1ad5efd261842e82a719c9863ca4ac04c');
const tx = Transaction.fromRlpSerializedTx(rawTx);
const pubKey = tx.getSenderPublicKey();

console.log(bufferToHex(pubKey))
> 0x54a1c393389571253d4095a973c59ce63a30e540c2931b80d1654248e3a7271ef16f92c5ec534213877fcd5e0be82b242a6b93bb9ada6dd432df76a10c3f71c9

from ethereumjs-util.

kss-espeo avatar kss-espeo commented on June 10, 2024

Thanks @cgewecke , this works. However I would like to better understand why is that, since I need to implement this in Java (which has a poorer toolset for eth and there is no "magical" method to do what you suggested AFAIK) .

My main question is : how is a "raw transaction hash" different from a keccak hash of a raw transaction? I looked inside Transaction.getSenderPublicKey and a "raw transaction hash" it's using is this: 0x623997462cb089e708c99c886c1920eeba2d7fa13faeb0511b7b31b2028cea42 .
However, a keccak hash of raw transaction (0xf86b0b85250523760082520894eafaf9bb8f35235d0df61275e86fd65d9ef2c3f9870aaa0065c66b8b8026a05fd883bb01a10915ebc06621b925bd6d624cb6768976b73c0d468b31f657d15ba0121d855c539a23aadf6f06ac21165db1ad5efd261842e82a719c9863ca4ac04c) is this: 0x26eed054f1ef51ae7a6c94841469c82a20f2deda84affde461b7d3887677bda8 .

If you could help me understand the difference between the two (perhaps direct me to something I can read to educate myself on that) , I could find some lib in Java that does that. I tried to read what happens inside Transaction.getMessageToVerifySignature , but it's a little too complex to understand.

from ethereumjs-util.

cgewecke avatar cgewecke commented on June 10, 2024

how is a "raw transaction hash" different from a keccak hash of a raw transaction

The raw transaction hash is a transaction object which has been

[Edit: please see jochem brower's account of this in comments below which explains this process more clearly.]

I think Java methods equivalent to those implemented in @ethereumjs/tx can be found in web3j's crypto package.

from ethereumjs-util.

jochem-brouwer avatar jochem-brouwer commented on June 10, 2024

Hi @kss-espeo, a really interesting question, sat down to figure this out.

The confusion (at least from my side) indeed comes from this "raw tx hash" and the tx hash which you look up on etherscan. Realize that in the etherscan tx hash (so also the hash recorded in the blockchain as the "Transaction Hash") includes also the v, r and s values - but you can of course not know these values if you want to sign the message.

So, in order to solve this problem, you need to take the original transaction and actually hash that but exclude the v, r and s values. However you need to hash them in a rather specific way to get the raw tx hash. For reference see this code from @ethereumjs/tx. This code specifies how to get the "raw transaction hash" which you need to sign.

Below is an example script how to correctly get the hash from your specific transaction:

import { bnToRlp, BN, rlphash, toBuffer, unpadBuffer, ecrecover, pubToAddress } from "ethereumjs-util"

const nonce = new BN("11")
const gasPrice = new BN("159000000000") 
const gasLimit = new BN("21000")
const to = Buffer.from("eafaf9bb8f35235d0df61275e86fd65d9ef2c3f9", 'hex')
const value = new BN("3001668451330955")
const data = Buffer.from('')
const chainId = 1

const values = [
    bnToRlp(nonce),
    bnToRlp(gasPrice),
    bnToRlp(gasLimit),
    to !== undefined ? to : Buffer.from([]),
    bnToRlp(value),
    data
]

values.push(toBuffer(chainId))
values.push(unpadBuffer(toBuffer(0)))
values.push(unpadBuffer(toBuffer(0)))

const rawHash = rlphash(values)

const r = toBuffer('0x5fd883bb01a10915ebc06621b925bd6d624cb6768976b73c0d468b31f657d15b');
const s = toBuffer('0x121d855c539a23aadf6f06ac21165db1ad5efd261842e82a719c9863ca4ac04c');
const v = 0x26

const pubKey = ecrecover(rawHash, v, r, s, chainId);
const addr = pubToAddress(pubKey);

console.log(addr.toString('hex')) // 53b3779f4833116fcb87ebdbdccb61141eed7f87

You can of course also use @ethereumjs/tx and then use the getMessageToSign method.

from ethereumjs-util.

jochem-brouwer avatar jochem-brouwer commented on June 10, 2024

For clarity here's also the simpler implementation using @ethereumjs/tx

import { toBuffer, ecrecover, pubToAddress } from "ethereumjs-util"

import { Transaction } from '@ethereumjs/tx'

const Tx = Transaction.fromRlpSerializedTx(Buffer.from("f86b0b85250523760082520894eafaf9bb8f35235d0df61275e86fd65d9ef2c3f9870aaa0065c66b8b8026a05fd883bb01a10915ebc06621b925bd6d624cb6768976b73c0d468b31f657d15ba0121d855c539a23aadf6f06ac21165db1ad5efd261842e82a719c9863ca4ac04c", 'hex'))
const hash = Tx.getMessageToSign()

const r = toBuffer('0x5fd883bb01a10915ebc06621b925bd6d624cb6768976b73c0d468b31f657d15b');
const s = toBuffer('0x121d855c539a23aadf6f06ac21165db1ad5efd261842e82a719c9863ca4ac04c');
const v = 0x26

const chainId = 1

const pubKey = ecrecover(hash, v, r, s, chainId);
const addr = pubToAddress(pubKey);

console.log(addr.toString('hex')) // 53b3779f4833116fcb87ebdbdccb61141eed7f87

Note that this huge hex string is the raw transaction from etherscan

(This is the RLP-encoded version of the transaction).

from ethereumjs-util.

kss-espeo avatar kss-espeo commented on June 10, 2024

Thank you guys for the detailed answers. As far as I am concerned, @jochem-brouwer should be called Jochem Bro-U-Are ^^

I think the source of all confusion is two "raw" states of a transaction - before sign and after sign. It seems these two states are sort of called the same way, even by ETH node interfaces.

Please find a working Java solution below, should anybody need it in the future:

BigInteger v = new BigInteger("26", 16);
BigInteger r = new BigInteger("5fd883bb01a10915ebc06621b925bd6d624cb6768976b73c0d468b31f657d15b", 16);
BigInteger s = new BigInteger("121d855c539a23aadf6f06ac21165db1ad5efd261842e82a719c9863ca4ac04c", 16);
BigInteger chainId = new BigInteger("1", 16);
v = v.subtract(chainId.multiply(BigInteger.valueOf(2)).add(BigInteger.valueOf(8)));
Sign.SignatureData signatureData = new Sign.SignatureData(v.toByteArray(), r.toByteArray(), s.toByteArray());
byte[] raw = DatatypeConverter.parseHexBinary("f86b0b85250523760082520894eafaf9bb8f35235d0df61275e86fd65d9ef2c3f9870aaa0065c66b8b8026a05fd883bb01a10915ebc06621b925bd6d624cb6768976b73c0d468b31f657d15ba0121d855c539a23aadf6f06ac21165db1ad5efd261842e82a719c9863ca4ac04c");

RawTransaction decoded = TransactionDecoder.decode(DatatypeConverter.printHexBinary(raw));
byte[] encoded = TransactionEncoder.encode(decoded, chainId.longValue());
byte[] rawTxHash = Hash.sha3(encoded);

System.out.println("Raw tx hash:                    " + DatatypeConverter.printHexBinary(rawTxHash));
System.out.println("Pub key from raw tx hash :      " + signedMessageHashToKey(rawTxHash, signatureData).toString(16));

All the utility classes used come from web3j.crypto , as suggested by @cgewecke

from ethereumjs-util.

cgewecke avatar cgewecke commented on June 10, 2024

Looks good @kss-espeo.

from ethereumjs-util.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.