I would like to recover the public key (or address) from the r,s and v values of a signed transaction without using a private key. The idea of course being, that I want to use Nick's method to deploy a contract registry at a deterministic address.
This forum post (or rather the answer) uses ethers-js which seems to automatically try to comply with EIP-155 (adding a chainId to the transaction). Instead I tried using the ecrecover
method. The ethereumtx-util
library has a ecrecover implementation which uses the arguments v
, s
, r
and a 32-byte message. So immediately I thought this must be the transaction hash but I got the notification that the public key could not be recovered. What should I put here as the message? Are there better ways to recover the public key?
For those interested here is my code (The variable DeploymentByteCode
holds a string with valid deployment code)
import {ethers} from "hardhat";
import {Transaction} from 'ethereumjs-tx'
import {bufferToHex, pubToAddress, ecrecover, sha256} from 'ethereumjs-util'
let GasPrice = ethers.utils.formatUnits("100","gwei")
let GasLimit = ethers.utils.formatUnits("100000","wei") // in abuse of (wei-)notation
async function recover(){
const expandedSig = {
r: '0xa0de33f4f398c1f5f9bce68825cbc1afd8078ee95bba17f5ba9e8ed046ef7de4',
s: '0x34290374099df8f0b7f1702de210094fef777428563814560202ec8aff531016',
v: 28
}
let txData = {
gasPrice: GasPrice.toHexString(),
gasLimit: GasLimit.toHexString(),
to: undefined,
value: ZeroBigNumber.toHexString(),
nonce: 0,
data: DeploymentByteCode
}
let signedTxData = {
...txData,
...expandedSig
}
const tx = new Transaction(signedTxData, {hardfork: 'homestead' });
const serializedTx = tx.serialize()
let serializedTxToHexNoPrefix = tx.serialize().toString('hex')
let bufferedtxNoPrefix = Buffer.from(serializedTxToHexNoPrefix, 'hex')
let txHashComputedNoPrefix = ethers.utils.keccak256(bufferedtxNoPrefix)
let bufferedMsg = Buffer.from(txHashComputedNoPrefix.slice(2,), 'hex')
const pubKey = ecrecover(
bufferedMsg,
expandedSig.v,
Buffer.from(expandedSig.r, 'hex'),
Buffer.from(expandedSig.s, 'hex'),
0);
const addrBuf = pubToAddress(pubKey);
const recoveredAddress = bufferToHex(addrBuf);
console.log("recoveredAddress", recoveredAddress)
}
recover()
The error I get is (note also the error recover => recovered
in the error statement):
Error: Public key could not be recover
Note: I have another post that deals with the first part of this question regarding the correct form of the message that is required to be passed into the ecrecover function. Answering that would make a little bit of progress on this question, too:
ecrecover
should be the unsigned message and the signature (ie r,s & v). However, if I use the unsignedtxData
and calculate the RLP, serialized and hash it, I should recover r,s and v values. But I dontconst unsignedTx = new Transaction(txData, {hardfork: 'homestead' }); const serializedTx = tx.serialize(); const unsignedTxHash = ethers.utils.keccak256(Buffer.from(unsignedTx.serialize().toString('hex'),'hex')); let ethersSignature = await ALICE.signMessage(unsignedTxHash)
Guess I gotta go through the exercise of rlp-ing it by myself.