Ethereum has emerged as one of the most influential blockchain platforms, not only for its native cryptocurrency Ether but also for enabling decentralized applications (dApps) and smart contracts. While high-level overviews of Ethereum are widely available, this article dives deep into the Go implementation of the Ethereum protocol — specifically go-ethereum — to uncover the core architectural decisions, data structures, and execution mechanisms that power the network.
We’ll explore how blocks and transactions are structured, how gas governs computation, how digital signatures secure transactions, and how the Ethereum Virtual Machine (EVM) executes smart contracts. Whether you're a developer, researcher, or blockchain enthusiast, this technical walkthrough aims to deliver meaningful insights grounded in actual source code.
Core Concepts in Ethereum’s Architecture
Before analyzing transaction execution and contract logic, it's essential to understand the foundational components used throughout the Ethereum codebase.
SHA-3 Hashing and RLP Encoding
At the heart of Ethereum’s data integrity lies cryptography, primarily through SHA-3 (Keccak-256) hashing. Unlike SHA-1 and SHA-2, SHA-3 uses a different internal structure called the sponge construction, offering resilience against known cryptographic attacks. In Ethereum, every object — from transactions to blocks — is hashed using SHA-3 to produce a unique 32-byte identifier.
However, before hashing, objects must be serialized. This is where RLP (Recursive Length Prefix) encoding comes in. RLP serializes nested arrays of bytes into a flat byte sequence, making complex data structures suitable for hashing and storage.
The combination of RLP + SHA-3 forms what’s known as an RLP hash, which serves as a globally unique key for storing data in Ethereum’s underlying key-value database (typically LevelDB).
For example:
// Pseudocode: RLP hash of a transaction
encodedTx := rlp.Encode(transaction)
hash := sha3.Sum256(encodedTx)This design ensures immutability: any change in the original data alters the RLP encoding and thus the final hash.
👉 Discover how developers interact with Ethereum’s execution layer using modern tools.
Key Data Types: Hash, Address, and Big Integers
Ethereum defines several custom types to ensure consistency across the system:
// common/types.go
const (
HashLength = 32 // 256 bits
AddressLength = 20 // 160 bits
)
type Hash [HashLength]byte
type Address [AddressLength]bytecommon.Hash: A 32-byte value used for identifying blocks, transactions, and state roots.common.Address: A 20-byte identifier derived from public keys, representing user accounts or smart contracts.
Additionally, Ethereum uses big.Int from Go’s standard library to handle large integers — especially important when dealing with Ether (measured in wei, where 1 Ether = 10¹⁸ wei) and gas calculations.
Example usage:
value := new(big.Int).SetUint64(1000000)These types are used consistently across all core operations, ensuring precision and preventing overflow issues.
Understanding Gas and Ether
Two fundamental concepts in Ethereum are Gas and Ether, often confused but serving distinct purposes.
| Concept | Purpose |
|---|---|
| Gas | Unit measuring computational effort required to execute operations (e.g., memory allocation, arithmetic). Each operation has a predefined gas cost. |
| Ether (ETH) | Native cryptocurrency used to pay for gas. Users specify a gas price (in wei per unit of gas), determining transaction priority. |
Every transaction includes:
GasLimit: Maximum amount of gas the sender is willing to consume.GasPrice: Amount of Ether paid per unit of gas.
Total fee = GasUsed × GasPriceThis mechanism prevents infinite loops and resource abuse while incentivizing miners (or validators in PoS) to process transactions.
Blocks as Ordered Containers of Transactions
In Ethereum, a block is more than just a container — it's a unit of consensus and execution.
The block structure in go-ethereum looks like this:
// core/types/block.go
type Block struct {
header *Header
transactions Transactions
// ...
}
type Header struct {
ParentHash common.Hash
Number *big.Int
// ...
}Key points:
- Blocks form a chain via
ParentHash, linking each block to its predecessor. - The
Numberfield indicates position in the chain, starting from Genesis Block (number 0). - Transactions are executed sequentially within a block during state transitions.
Each transaction (Transaction) contains critical fields:
type txdata struct {
AccountNonce uint64
Price *big.Int // Gas price
GasLimit *big.Int // Max gas allowed
Recipient *common.Address // To address (nil for contract creation)
Amount *big.Int // Value transferred
Payload []byte // Data or init code
V, R, S *big.Int // Signature values
}Notably, the sender address is not stored directly — it's derived cryptographically from the signature during validation.
How Transactions Are Executed
Transaction execution is orchestrated by the StateProcessor, which processes all transactions in a block and updates the global state accordingly.
The Execution Flow: From Block to Receipts
Execution begins in Process():
func (p *StateProcessor) Process(block *Block, statedb *StateDB, cfg vm.Config) {
for _, tx := range block.Transactions() {
receipt, _, err := ApplyTransaction(/*...*/)
receipts = append(receipts, receipt)
}
return receipts, logs, totalGasUsed, nil
}Each transaction results in a Receipt, which records:
PostState: RLP hash of the world state after execution.Logs: Events emitted during execution (e.g., token transfers).Bloom: Bloom filter of logs for efficient querying.GasUsed: Actual gas consumed.
These receipts are crucial for light clients and event indexing.
Gas Mechanics: Consumption, Refunds, and Miner Rewards
The actual work happens inside ApplyTransaction() → TransitionDb().
Here’s a step-by-step breakdown:
- Buy Gas: Deduct
GasLimit × GasPricefrom sender’s balance. - Intrinsic Gas Cost: Calculate base cost based on transaction size and payload (non-zero vs zero bytes).
- Execute via EVM: Run contract code or transfer funds.
- Refund Unused Gas: Return unused gas plus bonuses for storage cleanup.
- Reward Miner: Pay
GasUsed × GasPriceto the block proposer.
💡 Why refunds? They encourage efficient use of storage by rewarding users who free up space (e.g., deleting data).
Miner rewards create economic incentives for securing the network — a cornerstone of Ethereum’s decentralization model.
Digital Signatures: Securing Transaction Origin
Since sender addresses aren’t explicitly declared, they’re recovered from digital signatures using ECDSA (Elliptic Curve Digital Signature Algorithm).
A signature consists of three parts:
R,S: Components of the elliptic curve signature.V: Recovery ID indicating which public key variant was used.
When processing a transaction:
- Concatenate
R,S,Vinto a 65-byte signature. - Use ECDSA recovery to derive the public key.
- Hash the public key (Keccak-256) and take last 20 bytes → sender address.
This process is encapsulated in the Signer interface:
type Signer interface {
Sender(tx *Transaction) (common.Address, error)
Hash(tx *Transaction) common.Hash
}Once recovered, the sender is cached to avoid recomputation.
👉 Learn how wallets validate Ethereum transactions securely.
Inside the Ethereum Virtual Machine (EVM)
The EVM is a stack-based virtual machine responsible for executing smart contracts. It runs in isolation, ensuring deterministic behavior across nodes.
Execution Context and State Management
The EVM operates within a context containing:
- Block metadata (
Number,Difficulty) - Transaction details (
GasPrice,Origin) - State database (
StateDB) — tracks account balances, storage, and code
Account state is managed via stateObject, cached in memory and committed only upon block finalization.
Contract Creation vs. Call
Two primary entrypoints:
Call(): Invokes existing contract at a given address.Create(): Deploys new contract; generates new address via sender + nonce.
Both functions:
- Transfer value (if applicable).
- Initialize a
Contractstruct. - Execute code via
run().
Difference:
- In
Call(), code is fetched from existing account (GetCode(addr)). - In
Create(),Payloadcontains initialization code that returns runtime bytecode.
After deployment, runtime code is stored under the new contract’s address using SetCode().
Precompiled Contracts for Efficiency
Certain cryptographic operations are implemented as precompiled contracts, bypassing interpreter overhead.
Located at specific addresses (e.g., 0x01 for ECDSA recovery), they offer fixed logic with predictable gas costs:
type PrecompiledContract interface {
RequiredGas(input []byte) uint64
Run(input []byte) ([]byte, error)
}Examples include:
- SHA256 hashing
- RIPEMD160
- Elliptic curve operations (e.g.,
ecrecover) - BLAKE2 compression
Developers can extend this set for performance-critical applications.
Interpreter: Executing Smart Contract Instructions
Non-precompiled contracts are interpreted byte-by-byte using an opcode table (JumpTable). Each opcode maps to an operation struct defining:
execute: Core logicgasCost: Dynamic or static costvalidateStack: Ensures operand availabilitymemorySize: Required memory expansion
Common opcodes:
- Arithmetic:
ADD,MUL,EXP - Logic:
AND,OR,ISZERO - State access:
BALANCE,CALLER,GASPRICE - Logging:
LOG1toLOG4
Logs generated by LOGn instructions become part of the receipt and enable dApps to listen for events like token transfers.
Summary: Ethereum’s Design Philosophy
Ethereum builds upon Bitcoin’s foundation but introduces programmability at scale. Key takeaways:
- ✅ Gas is the economic engine regulating computation.
- ✅ Blocks execute transactions sequentially, updating global state.
- ✅ Transactions are signed messages with embedded logic (via payload).
- ✅ EVM enables Turing-complete smart contracts with sandboxed execution.
- ✅ RLP + SHA-3 + ECDSA provide secure serialization, identification, and authentication.
By combining cryptographic rigor with flexible execution semantics, Ethereum supports everything from DeFi protocols to NFT marketplaces — all built on open-source primitives.
Frequently Asked Questions
Q: Why does Ethereum use RLP instead of JSON or Protocol Buffers?
A: RLP ensures canonical encoding — there's exactly one way to encode any given data structure. This prevents ambiguity in hashing and consensus-critical operations.
Q: Can a transaction run out of gas mid-execution?
A: Yes. If gas is exhausted, execution halts immediately, changes are reverted (except gas payment), and the transaction fails — though miners still receive fees.
Q: How is the sender address recovered without being stored?
A: Using ECDSA’s public key recovery feature. From (R,S,V) and the signed message hash, we can reconstruct the public key and derive the address.
Q: What happens to unused gas?
A: It’s refunded to the sender after execution. This encourages accurate gas estimation without penalizing overestimation.
Q: Are all EVM operations equally expensive?
A: No. Opcodes are priced according to computational intensity. For example, SHA3 costs more than ADD. Prices are updated via EIPs to reflect real-world costs.
Q: How do precompiled contracts improve performance?
A: They skip interpretation overhead and execute native Go code directly, making them significantly faster for common cryptographic tasks.
👉 Explore real-time Ethereum network metrics and contract interactions.