Comments (8)
Thanks @jochem-brouwer! That's a great answer.
from ethereumjs-util.
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.
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.
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.
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.
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.
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.
Looks good @kss-espeo.
from ethereumjs-util.
Related Issues (20)
- Split tests from index.js into separate files HOT 3
- Update TypeDoc and typedoc-plugin-markdown dev dependencies
- Examples in README for different categories
- Add basic API tests for re-exports
- Browser compatible elliptic alternative to secp256k1 HOT 2
- [Util v7| Tracking Issue for BN.js v4 and v5 interoperability issues HOT 2
- Buffer error with keccak256 related to abi.encodePacked HOT 8
- Crashes on node:12-alpine HOT 3
- [bug] hash.ts not work on alpine linux image HOT 2
- BN re-export failure HOT 1
- Error: Expected private key to be an Uint8Array HOT 8
- Node crashes when require ethereumjs-util in worker threads HOT 5
- Extend Address class to handle BN HOT 7
- Cannot read property 'fromRpcSig' of undefined HOT 2
- Memory leak HOT 2
- bnToRlp: misleading name HOT 1
- Upgrade ethereumjs-config to v2, switch from Coveralls to CodeCov
- ethereumjs-util v8 Release Planning HOT 1
- housekeeping: remove use of deprecated assert methods HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ethereumjs-util.