← All weekly notes

11 min read

Under the hood of an Ethereum transaction

Following one 1 ETH transfer from the Send button to signing, sender recovery, EVM execution, receipts and block roots.

Ethereum · Cryptography · Blockchain

I open a wallet, enter Bob's address, type 1 ETH and press Send.

The wallet shows a transaction hash. A little later, a block explorer shows Success. That looks simple, but Ethereum has solved several problems in between:

  1. It represented my request in one exact format.
  2. It proved that the owner of my account authorized that request.
  3. It found the sender without receiving the private key.
  4. It executed the request and recorded the result.
  5. It committed the transaction and result into a block that other nodes can verify.

This note follows those steps in order. I introduce each cryptographic tool only when the transaction needs it.

1. The wallet first builds an unsigned transaction

Before signing anything, the wallet creates a transaction containing the user's request and the limits under which it may run.

For Alice's transfer, the important fields are:

  • Chain ID says which Ethereum-compatible network this transaction belongs to. Ethereum mainnet uses chain ID 1.
  • Nonce is Alice's next transaction sequence number. If her previous confirmed transaction used nonce 6, this one normally uses 7.
  • Recipient is Bob's 20-byte Ethereum address.
  • Value is the amount to transfer, represented in wei. One ETH is 10¹⁸ wei.
  • Gas limit is the maximum amount of execution work Alice permits.
  • Fee fields limit what Alice is willing to pay for that work.
  • Data is empty for the simplest ETH transfer. When calling a contract, it contains the function selector and encoded arguments.

At this point, the transaction is only a request in memory. It has not yet proved who created it.

2. The fields must become exact bytes

A signature does not understand fields such as “recipient” or “value.” It works with bytes. The wallet and every Ethereum node therefore need one agreed way to turn the transaction into bytes.

This is why encoding matters.

Imagine joining two values without their lengths. ab followed by c produces the same text as a followed by bc. The meanings differ, but the resulting bytes are ambiguous.

Ethereum avoids this by defining exact transaction formats. The execution layer commonly uses RLP, which means Recursive Length Prefix. RLP records whether a value is a byte string or a list and includes enough length information to separate values.

Modern Ethereum transactions also begin with a transaction-type byte. The simplified shape is:

transaction type → encoded transaction fields

The transaction type tells a node which fields and rules to use. The encoding then gives those fields one exact byte representation.

RLP does not sign or encrypt anything. It only answers:

What exact bytes represent this transaction?

If a wallet and a node encode the fields differently, they calculate different hashes and the signature cannot verify.

3. The account comes from a private key

Alice's wallet controls a private key. It is a secret number and must never leave the wallet.

From that private key, the wallet derives a public key. Ethereum uses the secp256k1 elliptic curve for this calculation. The operation is easy in the forward direction:

private key → public key

Going backward—from the public key to the private key—is intended to be computationally infeasible.

Ethereum then derives Alice's address from the public key:

  1. Take the uncompressed public-key coordinates.
  2. Hash them with Keccak-256.
  3. Keep the final 20 bytes.

Those 20 bytes are the Ethereum address we normally write with a 0x prefix.

The three values have different jobs:

  • The private key authorizes transactions and stays secret.
  • The public key lets others verify signatures.
  • The address is the shorter public account identifier used by Ethereum.

The address is not the public key, and neither one is the private key.

4. The wallet hashes and signs the transaction

The wallet now has exact transaction bytes. It hashes the protocol-defined signing payload with Keccak-256. The 32-byte result is called the signing hash.

Why hash first? A transaction can have different lengths, while the signature algorithm works with a fixed-size message digest. The hash also changes when any signed field changes. If an attacker changes Bob's address or changes 1 ETH to 10 ETH, the signing hash changes.

The wallet signs that hash using ECDSA and Alice's private key.

ECDSA and secp256k1 are related but not identical:

  • ECDSA defines how a signature is created and checked.
  • secp256k1 defines the curve on which that mathematics operates.

The easy distinction is: ECDSA is the signing method; secp256k1 is the curve it uses.

The signature contains r, s and recovery information. Modern typed transactions represent the recovery information as a parity value. Older material often calls it v.

The signed transaction can now be broadcast. The private key is not included. It never needs to be sent to another node.

Two small but important safety details

The chain ID is part of the signing context. It makes Alice's authorization specific to one chain, so the same signed request cannot simply be replayed on another Ethereum-compatible network.

Ethereum also requires the signature's s value to use its canonical lower-half form. ECDSA otherwise allows two related encodings of the same authorization. Choosing the low-s form prevents those two encodings from producing two different transaction identifiers.

5. A node recovers the sender

An Ethereum transaction does not need a trusted from field. A node derives the sender from the signature.

The node repeats the public part of the wallet's work:

  1. Read the transaction type and unsigned fields.
  2. Encode those fields using the same rules.
  3. Calculate the same Keccak-256 signing hash.
  4. Combine that hash with r, s and the recovery information.
  5. Recover the public key.
  6. Derive the final 20-byte address from that public key.

The resulting address is Alice's sender address.

If anyone changed a signed field, the node would calculate a different signing hash. Alice's old signature would no longer recover to the expected sender, so the transaction would be rejected.

A valid signature proves a narrow but important fact:

The private key controlling this sender authorized these exact transaction fields on this chain.

It does not prove that Alice has enough ETH, that the nonce is correct, or that execution will succeed. Those checks happen next.

Signing hash and transaction hash

These two hashes are easy to confuse.

The signing hash is calculated from the unsigned signing payload. Alice signs this hash.

The transaction hash is calculated from the final serialized transaction, including its signature. This is the identifier shown by wallets and block explorers.

The sender signs the first. The network uses the second to identify the finished transaction.

6. The node validates the transaction

After recovering Alice's address, the node checks the transaction against the current Ethereum state and protocol rules.

For this transfer, it checks that:

  • The signature is valid and canonical.
  • The transaction uses Alice's expected nonce.
  • Alice can cover the transferred value and the required gas cost.
  • The gas limit and fee fields are valid.
  • The transaction is valid for this chain and transaction type.

The nonce provides ordering for Alice's transactions. A confirmed transaction with nonce 7 moves her account nonce forward, so the same transaction cannot be executed successfully again.

If validation fails, execution does not begin.

7. The EVM executes the request

When the transaction is included in a block, the Ethereum Virtual Machine applies it to the current state.

For a simple ETH transfer, the important changes are:

  • Alice's nonce increases.
  • Alice's balance decreases by the transferred value and the actual gas cost.
  • Bob's balance increases by 1 ETH.

A contract transaction can do much more. It can read and write storage, call other contracts, emit logs, create a contract, return data or revert.

This is the key boundary:

The signature proves permission to try. EVM execution determines what actually happens.

A correctly signed contract call can still revert because a contract condition fails. The transaction remains part of the block and still consumes gas, but its state changes are reverted according to EVM rules.

8. Execution produces a receipt

After execution, the node produces a receipt. It records information about the result, including:

  • Success or failure status.
  • Cumulative gas used at that point in the block.
  • Logs emitted by contracts.
  • A bloom filter used to quickly search for potentially matching logs.

Alice does not sign the receipt. Every validating node independently calculates it by executing the same ordered transaction against the same starting state.

The three objects are now distinct:

  • The unsigned transaction is what Alice wants to authorize.
  • The signed transaction is the authorized request sent to Ethereum.
  • The receipt is the result produced by execution.

A logs bloom is only a search shortcut. A negative match means the log is absent. A positive match means it may be present, so the receipt still has to be checked.

9. The block commits to transactions, receipts and state

A block cannot place every account, transaction and receipt directly into its header. Instead, it stores compact root hashes.

The three execution roots to remember are:

  • stateRoot commits to the world state after the block is executed.
  • transactionsRoot commits to the ordered signed transactions in the block.
  • receiptsRoot commits to their ordered execution receipts.

Alice's new nonce and balance, and Bob's new balance, contribute to a new stateRoot. The signed transfer contributes to transactionsRoot. Its execution receipt contributes to receiptsRoot.

Ethereum's execution layer organizes these values with Merkle Patricia Tries. The internal trie rules are more detailed, but the beginner-level purpose is straightforward:

Many values are summarized by one root that changes if a committed value changes.

How an inclusion proof helps

Suppose a lightweight client wants to check Bob's account under a particular stateRoot without downloading the entire Ethereum state.

It receives Bob's encoded account value and the small set of trie nodes needed to reach the root. It hashes upward through that path and compares the calculated result with the trusted stateRoot.

If they match, Bob's value is included under that root. If Bob's value or one proof node is changed, the calculated root differs and verification fails.

The proof does not decide whether the root belongs to the canonical chain. The client still needs a trusted consensus view of the block containing that root.

10. Where SSZ fits

RLP is mainly associated with execution-layer transaction and trie data. Ethereum's consensus layer uses another format called SSZ, or Simple Serialize.

SSZ understands defined types such as containers, lists and vectors. It specifies both serialization and how consensus data is arranged into Merkle trees. This makes it practical to prove one field inside a large consensus object.

For this note, the distinction is enough:

  • RLP commonly encodes execution-layer transaction structures.
  • SSZ serializes and Merkleizes consensus-layer structures.

Neither one is a signature algorithm. Both create deterministic data for later hashing and verification.

11. Blob transactions add one separate data path

The standard transfer is complete. Blob transactions add another mechanism for rollups that need to publish a large batch of data.

The large blob is not placed directly inside the execution transaction. Instead:

  • The execution transaction contains a small versioned hash referring to the expected commitment.
  • A consensus-layer blob sidecar carries the blob, a KZG commitment and a KZG proof.

A node performs two checks:

  1. Derive the versioned hash from the sidecar's commitment and compare it with the reference inside the transaction.
  2. Verify that the blob and proof match that commitment.

The first check connects the sidecar to the transaction. The second connects the large blob to the commitment.

KZG means Kate–Zaverucha–Goldberg, the names associated with the commitment scheme. The deeper mathematics represents blob values using a polynomial, but that is not required to understand its role here.

The beginner mental model is:

  • The blob is a large package.
  • The commitment is its compact tamper-resistant seal.
  • The versioned hash is the seal's identifier written in the transaction.
  • The proof lets a node check that the supplied blob matches the seal.

KZG does not encrypt the blob and does not decide whether the rollup's transactions are correct. It authenticates the relationship between the blob and its commitment. The consensus network makes the blob available, and the rollup interprets its contents.

What I want to remember

One normal Ethereum transaction can be reconstructed in ten steps:

  1. The wallet builds transaction fields.
  2. The transaction type and RLP rules produce exact bytes.
  3. Keccak-256 produces the signing hash.
  4. ECDSA over secp256k1 signs it with Alice's private key.
  5. The signed transaction is broadcast; the private key stays in the wallet.
  6. A node rebuilds the signing hash and recovers Alice's public key and address.
  7. The node validates the nonce, balance, gas and fees.
  8. The EVM executes the request.
  9. Execution produces a receipt and a new state.
  10. Transaction, receipt and state roots commit the result into the block.

The shortest version is:

Encode the request → hash it → sign it → recover the sender → validate → execute → commit the result.

And the most important distinction is:

A signature proves authorization. Execution determines the result. A root makes that result verifiable later.

References