GSoC 2018 Stories 03: Recover sender from ethereum transaction

Umesh Prabushitha Jayasinghe
2 min readJun 14, 2018

--

As I explained in the last article, ethereum blockchain doesn’t store the sender address of an transaction. But you can use signature related values to recover the sender public key and then the address.

Generating Sender Ethereum Address from a Transaction

Here’s how we can get the sender public key, then the sender address out from a transaction.

  1. Take v, r and s by rlp decoding the transaction
  2. Get the transaction hash
  3. Use ECDSA with secp256k1’s curve to recover the public key from tx hash and v, r, s values
  4. Take the Keccak-256 hash of the public key
  5. Take the last 40 characters / 20 bytes of this public key (Keccak-256). Or, in other words, drop the first 24 characters / 12 bytes. These 40 characters / 20 bytes are the address. When prefixed with 0x it becomes 42 characters long.

Now let’s see how we can implement this using a psuedo code. Inorder to understand the lines, I’ve explained brief in comments.

recoverTxSender() {
chainId = v[0]; // 1st byte of v
if (chainId > 0) {
chainId = (v[0]-35)/2;
}
// RLP encode selected transaction attributes
encoded_tx;
if (chainId > 0) {
encoded_tx = RLP::encode(nonce, gasPrice, gasLimit, to, value, init, v, s, r);
} else {
encoded_tx = RLP::encode(nonce, gasPrice, gasLimit, to, value, init);
}
// get the keccak256 hash of the encoded transaction
txKeccak = keccak_256(encoded_tx);

// calculate the new v value
new_v = v[0];
if (chainId > 0) {
new_v -= (chainId * 2 + 8);
}
// recover the public key from keccak256 hash and signature values
public_key = recover(new_v, r_bytes, s_bytes, txKeccak);
if (public_key.empty()) {
return {};
}

// extract address from public key
address = public_key[12:40];
return '0x'+address;
}

Here’s the underlying mechanism of recovery function


recover( v_new, r, s, txKeccak)
{
//correct r and s with 32 in size (leading 0s if size mismatch)
if (r.size() < 32) {
r = left_pad_with_0s(r, 32-r.size());
}
if (s.size() < 32) {
r = left_pad_with_0s(r, 32-s.size());
}
// reserve 65 byte memory for the signature
signature;
signature.reserve( 65 );

signature[0:32] = r; // 32 bytes
signature[32:64] = s; // 32 bytes
signature[64] = v_new; // 1 byte
int v = v_new-27;
if (v > 3 || v < 0){
// invalid
return {};
}
// get recoverable signature
rawSig = secp256k1_ecdsa_recoverable_signature_parse_compact(signature, v);
if(!rawSig) return error;
// recover raw public key
rawPubkey = secp256k1_ecdsa_recover(rawSig, _message);
if(!rawPubkey) return error;
// encode the raw public key
hashPubkey = secp256k1_ec_pubkey_serialize( rawPubkey, SECP256K1_EC_UNCOMPRESSED);
// Create the Public key skipping the header.
return hashPubkey[1:];
}

You can find the C++ implementation for this in my GSoC 2018 project repository

During the 1st evaluation period, I could able to develop the basic blockchain parser from leveldb files of geth synced blockchain and write unit tests alongwith. I’ve planned to develop the builder which structure and store data to facilitate ethereum analysis.

Click here for the Previous Article

--

--