Slides for the Nethermind Workshop from the EVM Bootcamp (Week 26).
Size: 807.85 KB
Language: en
Added: Sep 18, 2024
Slides: 47 pages
Slide Content
EVM Bootcamp
Yehia
Introductions
●I’m Yehia
●Blockchain Engineer at Nethermind
●Specialize in Dapps Development
●I build DeFi solutions
●Why EVM
●What is EVM
●Instructions Executions
●Ethereum State: Account,trie
transaction and receipt State
●Blocks
What we will cover
●Gas Optimization
●Solidity will make sense only if you
understand EVM
●Advanced smart contract design
patterns
Why Learn EVM
What is EVM
What is EVM
CPU Machines
Over the decades, CPUs have been highly, highly optimized for
speed of execution. They can execute instructions fast –
trillions per second fast. That's a lot of instructions.
Ethereum Virtual Machine
A virtual machine is a software program designed to behave like a
physical machine. That is, it's software that – like a CPU – reads,
interprets, and executes instructions.
The EVM is no different. It reads, interprets, and executes instructions
encoded in a smart contract's bytecode. The tangible result from
executing these instructions is updating contract state and returning
data.
EVM Instructions
EVM Instructions - Program Counter
-The PC is a 256-bit integer that points to
the current instruction in the EVM code.
-It starts at 0 and increments by 1 after
each instruction (unless modified by a
JUMP instruction).
-The PC determines which opcode in the
EVM code will be executed next.
EVM Instructions - EVM Code
-This is the bytecode of the smart
contract or the data payload of a
transaction.
-It's a series of opcodes and their
arguments.
-Each opcode is one byte (8 bits) long,
represented by a hexadecimal value (e.g.,
0x01 for ADD).
EVM Instructions - EVM Code (examples)
-Arithmetic Opcodes: ADD, MUL, DIV (and
more) allow for the computation of
arithmetic operations
-Memory and Storage Management
Opcodes MLOAD, SLOAD, SSTORE (and
more): There are different opcodes
depending on the instruction, such as
SSTORE, SLOAD, MSTORE, MLOAD and
several more
EVM Instructions - EVM Code (examples)
-New opcodes that are specific to the
Ethereum environment itself:
-CREATE: This opcode creates a new
smart contract based on the code
provided. This uses a larger gas amount
and is variable depending on the
bytecode being deployed.
-BLOCKHASH: This opcode is the hash of
one of the 256 most recent complete
blocks
-BALANCE: Returns the balance of a given
account
-CALL: A system operation which consists
of a message-call (read operation) into
an account
EVM Instructions - Operations (Gas)
-The EVM reads the opcode at the current
PC.
-Each opcode has a predefined gas cost.
-Before execution, the EVM checks if
there's enough gas. If not, it throws an
"Out of Gas" exception.
-The gas cost is deducted from the
remaining gas.
-The operation is then executed, which
may involve:
-> Manipulating the stack
-> Reading from or writing to memory
-> Accessing account storage
-> Performing arithmetic or logical
operations
EVM Instructions - Message Call (Gas)
If the opcode is a CALL, DELEGATECALL, or
STATICCALL:
-Additional gas is required for the call
itself and for any value transfer.
-A new execution context is created (with
its own PC, memory, and stack).
-The called contract's code is loaded and
executed.
-Any remaining gas is returned to the
caller after the call completes.
EVM Instructions- The Stack
Stack
- Most operations involve the stack (e.g.,
PUSH, POP, SWAP, DUP).
- Stack operations are generally cheap in
terms of gas. All opcode cost 3 gas
- The stack is limited to 1024 elements.
EVM Instructions- Memory
Memory
- Operations like MLOAD, MSTORE involve
memory.
- Memory expansion incurs additional gas
costs.
- MLOAD and MSTORE cost is 3 gas
- Gas cost increases quadratically with
memory size. (complex operation sha3, size or
unused memory)
EVM Instructions - Storage
Storage
-Storage operations are the most
expensive in terms of gas.
- SLOAD and SSTORE opcodes access
account storage.
-SSTORE can cost 20,000 gas for a new
storage slot, or 5,000 gas for updating
an existing slot.
-SLOAD costs 2100 gas to initially access
a value during a transaction and costs
100 gas for each subsequent access
EVM Instructions - Storage (example)
contract MyContract {
uint32 a; // First slot
uint64 b; // First slot
uint128 c; // First slot
uint64 d; // Second slot
uint256 e; // Third slot
}
EVM Instructions- The Stack Too Deep Issue
Stack Too Deep Issue
-Break function into smaller functions
-Use storage instead of memory for large
arrays/structs(rare case)
-Optimize local variable usage
-Reduce function parameters
-Use external instead of public for
functions
-Use libraries for complex operations
EVM Instructions- The Stack Too Deep Issue
Stack Too Deep Issue
-Use events to store non-critical data
-Use assembly for gas-intensive
operations
-Implement batching for multiple
operations
-Use mapping instead of arrays where
possible
-Use short-circuiting in logical operations
EVM Instruction - Flow Part One
1.The EVM reads the opcode at the current
PC.
2.It checks if there's enough gas for the
operation.
3.If sufficient gas, it deducts the base cost
of the operation.
4.The operation is executed:
-For stack operations: Elements are
pushed, popped, or manipulated.
-For memory operations: Data is read
from or written to memory.
-For storage operations: The contract's
storage is read or updated.
EVM Instructions - Flow Part Two
1.If the operation involves memory
expansion or storage modification,
additional gas is consumed.
2.For CALL operations, a new execution
context is created, and the process
resources.
3.The PC is incremented (unless it's a JUMP
operation).
4.This process repeats until:
- The code execution completes
successfully.
-An error occurs (e.g., out of gas,
stack overflow).
-A STOP, RETURN, or REVERT opcode
is encountered.
Ethereum State
●Account State
●Transaction
●Transaction Receipt
Account State
Ethereum State Trie Architecture Explained
Merkle Particia Trie Ethereum Foundation
Transaction And Transaction Receipt Tries
Transaction Tries: records transaction request
Transaction Receipt Tries: records the transaction
outcome
Transaction Lifecycle
1.The transaction is created and signed by the sender.
2.The transaction is sent to a node that does the following:
i) Checks if the transaction is valid.
ii) Spins up a EVM instance.
iii) Load state from the database.(global variables, persistent storage)
iv) Executes the transaction:
a. Executes all the opcodes in the transaction.
b. Updates the state of the EVM.
c. Reduces the gas left for the transaction.
3.The transaction is added to the blockchain and the state is saved to the database, while the stack
and memory are wiped.
Transaction Components
to: DATA, 20 Bytes - address of the receiver. null when it's a contract creation transaction.
transactionIndex: QUANTITY - integer of the transactions index position in the block. null when it's
pending.
value: QUANTITY - value transferred in Wei.
v: QUANTITY - ECDSA recovery id
r: DATA, 32 Bytes - ECDSA signature r
s: DATA, 32 Bytes - ECDSA signature s
Transaction Receipt
The Transaction Trie is linked to the World State Trie and other tries through block hashes,
ensuring data integrity and immutability. Receipts are a data structure in which details regarding
the execution of every transaction within a specific block are recorded.
Transaction Receipt
The Transaction Trie is linked to the World State Trie and other tries through block hashes,
ensuring data integrity and immutability. Receipts are a data structure in which details regarding
the execution of every transaction within a specific block are recorded.
Transaction Receipt
blockHash: String, 32 Bytes - hash of the block where this transaction was in.
blockNumber: Number - block number where this transaction was in.
transactionHash: String, 32 Bytes - hash of the transaction.
transactionIndex: Number - integer of the transactions index position in the block.
from: String, 20 Bytes - address of the sender.
to: String, 20 Bytes - address of the receiver. null when its a contract creation transaction.
cumulativeGasUsed: Number - The total amount of gas used when this transaction was executed
in the block.
gasUsed: Number - The amount of gas used by this specific transaction alone.
Transaction Receipt
status: String - '0x0' indicates transaction failure , '0x1' indicates transaction succeeded.
contractAddress: String - 20 Bytes - The contract address created, if the transaction was a
contract creation, otherwise null.
logs: Array - Array of log objects, which this transaction generated.
Blocks
Blocks
The Block Fields
Field Description
slot the slot the block belongs to
proposer_indexthe ID of the validator proposing the block
parent_root the hash of the preceding block
state_root the root hash of the state object
body an object containing several fields, as defined below
There is a lot of information contained within a block. At the highest level a block
contains the following fields:
The Block Fields - The Body Field
Field Description
randao_reveal a value used to select the next block proposer
eth1_data information about the deposit contract(Deposit
root, deposit count and blockhash)
graffiti arbitrary data used to tag blocks
proposer_slash
ings
list of validators to be slashed
attester_slash
ings
list of attesters to be slashed
The Block Fields - The Body Field
receipts_root hash of the transaction receipts trie
logs_bloom data structure containing event logs
prev_randao value used in random validator selection
block_number the number of the current block
gas_limit maximum gas allowed in this block
gas_used the actual amount of gas used in this block
timestamp the block time
extra_data arbitrary additional data as raw bytes
base_fee_per_gasthe base fee value
block_hash Hash of execution block
Field Description
deposits list of new deposits to the deposit contract
voluntary_exits list of validators exiting the network
sync_aggregate subset of validators used to serve light clients
execution_payloadtransactions passed from the execution client
The Block Fields - The Body Field
attestations list of attestations in favor of the current block
The Block Fields
The attestations field contains a list of all the attestations in the block. Attestations have their own
data type that contains several pieces of data. Each attestation contains:
Field Description
aggregation_bitsa list of which validators participated in this attestation
data a container with multiple subfields
signature aggregate signature of all attesting validators
The Block Fields
The data field in the attestation contains the following:
Field Description
slot the slot the attestation relates to
index indices for attesting validators
beacon_block_rootthe root hash of the Beacon block containing this object
source the last justified checkpoint
target the latest epoch boundary block
The Block Fields
Executing the transactions in the execution_payload updates the global state. All clients
re-execute the transactions in the execution_payload to ensure the new state matches that in
the new block state_root field
Field Description
parent_hash hash of the parent block
fee_recipientaccount address for paying transaction fees to
state_root root hash for the global state after applying changes in this block
The Block Fields
transactions_root root hash of the transactions in the payload
withdrawal_root root hash of the withdrawals in the payload
The execution_payload itself contains the following (notice this is identical to the header
except that instead of the root hash of the transactions it includes the actual list of
transactions and withdrawal information) :
Field Description
parent_hash hash of the parent block
fee_recipientaccount address for paying transaction fees to
The Block Fields
Field Description
state_root root hash for the global state after applying changes in this block
receipts_root hash of the transaction receipts trie
logs_bloom data structure containing event logs
prev_randao value used in random validator selection
block_number the number of the current block
gas_limit maximum gas allowed in this block
The Block Fields
Field Description
gas_used the actual amount of gas used in this block
timestamp the block time
extra_data arbitrary additional data as raw bytes
base_fee_per_gasthe base fee value
block_hash Hash of execution block
transactions list of transactions to be executed
withdrawals list of withdrawal objects
The Block Fields
The withdrawals list contains withdrawal objects structured in the following way:
Field Description
address account address that has withdrawn
amount withdrawal amount
index withdrawal index value
validatorIndex validator index value
The Block Time
Block time refers to the time separating blocks. In Ethereum, time is divided up into twelve
second units called 'slots'. In each slot a single validator is selected to propose a block.
Assuming all validators are online and fully functional there will be a block in every slot,
meaning the block time is 12s. However, occasionally validators might be offline when called
to propose a block, meaning slots can sometimes go empty.
This implementation differs from proof-of-work based systems where block times are
probabilistic and tuned by the protocol's target mining difficulty. Ethereum's average block
time(opens in a new tab) is a perfect example of this whereby the transition from
proof-of-work to proof-of-stake can be clearly inferred based on the consistency of the new
12s block time.
The Block Size
Blocks themselves are bounded in size. Each block has a target size of 15 million gas but the
size of blocks will increase or decrease in accordance with network demands, up until the
block limit of 30 million gas (2x target block size). The block gas limit can be adjusted upwards
or downwards by a factor of 1/1024 from the previous block's gas limit. As a result, validators
can change the block gas limit through consensus. The total amount of gas expended by all
transactions in the block must be less than the block gas limit. This is important because it
ensures that blocks can’t be arbitrarily large. If blocks could be arbitrarily large, then less
performant full nodes would gradually stop being able to keep up with the network due to
space and speed requirements. The larger the block, the greater the computing power
required to process them in time for the next slot. This is a centralizing force, which is resisted
by capping block sizes.