Hello World - EVM
type: channel
link: https://blog.jerichos.site/index.php/category/EVM/
items: About the EVM Part III
items: About the EVM Part II
items: About the EVM Part I

About the EVM Part III
type: item
date: 2025-05-24T16:21:00+08:00
description: 13.Return指令🚀 1. RETURN 指令✅ 功能终止执行，并返回内存中一段数据作为调用结果。🔍 实现源码路径：go-ethereum/core/vm/instructions.gofunc opReturn(pc *uint64, evm *EVM, contract *Contract, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() ret := contract.GetMem(int64(offset.Uint64()), int64(size.Uint64())) return ret, nil }📦 Gas 消耗指令本身消耗：0（RETURN 没有额外指令成本）访问 memory 的成本：根据 offset+size 的访问范围来收取内存扩展费用（memoryGasCost）func memoryGasCost(memSize uint64) uint64 { // linear + quadratic cost function // linear: memSize * 3 // quadratic: (memSize^2) / 512 }💡 特点调用成功返回数据（用于 CALL, DELEGATECALL 等）常用于函数正常返回🚨 2. REVERT 指令✅ 功能回滚状态，同时返回错误数据（不会消耗所有 gas）。🔍 实现func opRevert(pc *uint64, evm *EVM, contract *Contract, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() ret := contract.GetMem(int64(offset.Uint64()), int64(size.Uint64())) evm.interpreter.returnData = ret return ret, ErrExecutionReverted }📦 Gas 消耗指令本身：0内存访问费用：同样受 memoryGasCost 限制保留剩余 gas，不销毁💡 特点引入自 Byzantium（EIP-140）与 INVALID、ASSERT 不同，它会“温柔地失败”，gas 不全耗尽，便于合约进行 try/catch💣 3. SELFDESTRUCT 指令（旧称 SUICIDE）✅ 功能销毁当前合约，并将余额发送给指定地址。🔍 实现func opSelfDestruct(pc *uint64, evm *EVM, contract *Contract, stack *Stack) ([]byte, error) { beneficiary := common.Address(stack.pop().Bytes20()) // handle refund and deletion evm.StateDB.SelfDestruct(contract.Address()) // transfer balance to beneficiary evm.Transfer(contract.Gas, contract.Address(), beneficiary, contract.Balance()) contract.SelfDestruct = true return nil, nil }📦 Gas 消耗固定成本：5000（EIP-150 后）若首次销毁：+24000（删除合约 refund）若 beneficiary 是新地址：再加 25000// In gas_table.go (EIP150): SelfDestruct: 5000, SelfDestructRefund: 24000,💡 特点合约立即从状态树中移除（实际上是标记为删除，block 结束后才真正移除）balance 强制转移，但无法返回自定义数据（不是直接返回 data）🔁 总结对比表指令名功能描述返回数据Gas 行为出现版本RETURN正常返回执行结果✅memory + 0初始版本REVERT回滚并返回错误信息✅memory + 0Byzantium (EIP-140)SELFDESTRUCT销毁合约，发送余额❌高消耗+可能 refund初始版本（EIP-6 优化）14.CALL、DELEGATECALL、STATICCALL📘 总览指令名是否改变调用者是否转移 msg.value是否可变更 storageCALL✅ 改变 msg.sender✅ 支持转账✅ 支持状态更改DELEGATECALL❌ 保持调用上下文❌ 不支持转账✅ 支持状态更改STAT…

About the EVM Part II
type: item
date: 2025-05-22T11:17:00+08:00
description: 8.控制流（Control Flow）指令以太坊虚拟机（EVM）中的控制流指令用于控制程序执行的流程。这类指令不涉及算术计算或内存操作，而是改变程序计数器（Program Counter，pc）的值，从而跳转到新的位置继续执行。以下是常用的 5 个控制流指令及其详细说明（包含源码级别原理和 Gas 消耗）：🛑 1. STOP（0x00）功能：终止当前执行，正常返回。源码实现：func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { return nil, errStopToken }行为：不消耗任何栈、内存等资源。直接停止程序执行。Gas 消耗：0 Gas🔁 2. JUMP（0x56）功能：跳转到某个位置执行。前提：跳转目标必须是 JUMPDEST 指令的地址，否则异常。栈行为：栈顶：跳转地址源码实现：func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { if interpreter.evm.abort.Load() { return nil, errStopToken } pos := scope.Stack.pop() if !scope.Contract.validJumpdest(&pos) { return nil, ErrInvalidJump } *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop return nil, nil }validJumpdest 会检查该地址是否是 JUMPDEST（0x5b）字节。由于 pc 在每条指令之后自动加 1，所以此处设置为 dest - 1。Gas 消耗：8 Gas❓ 3. JUMPI（0x57）功能：条件跳转，只有条件非零才跳转。栈行为：栈顶：condition 次栈顶：destination源码实现：func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { dest, cond := scope.Stack.pop(), scope.Stack.pop() if cond.IsZero() { return nil, nil // 不跳转 } if !interpreter.validJumpdest(dest.Uint64(), scope.Contract.Code) { return nil, ErrInvalidJump } *pc = dest.Uint64() - 1 return nil, nil }行为说明：如果 cond == 0，什么也不做。如果 cond != 0，执行跳转到 dest。Gas 消耗：10 Gas🎯 4. JUMPDEST（0x5b）功能：跳转目标的标记，标志合法的 JUMP/JUMPI 可跳转的位置。源码实现：func opJumpdest(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { return nil, nil }行为：什么都不做，就是个占位符。编译器在生成代码时会在跳转目标位置插入 JUMPDEST。Gas 消耗：1 Gas📍 5. PC（0x58）功能：将当前指令的程序计数器 pc 压入栈。栈行为：push 当前 PC源码实现：func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.push(new(uint256.Int).SetUint64(*pc)) return nil, nil }用途：常用于合约内部的程序构造（如 inline jump）。与 JUMP 配合使用时可构造动态跳转。Gas 消耗：2 Gas🔍 总结对比表：指令含义栈操作Gas 消耗说明STOP停止执行无0不返回值，正常终止JUMP跳转弹出目标地址8目标地址必须是 JUMPDESTJUMPI条件跳转弹出地址和条件10条件非零跳转JUMPDEST跳转目标标记无1合法跳转的目标PC获取当前指令地址压入当前 pc2可用于构造 jump 表9.查询区块信息以太坊虚拟机（EVM）中用于查询区块信息的 9 个指令的详细说明，包括其功能、…

About the EVM Part I
type: item
date: 2025-05-21T11:03:00+08:00
description: 一、EVM 工作原理概述EVM 是一个 基于栈的图灵完备虚拟机，每次交易或合约调用都会在 EVM 中创建一个执行上下文，用于处理指令（opcode）。基本流程：接收交易：用户发起交易或合约调用，EVM 收到包含输入数据和 gas 的执行请求。读取字节码：合约代码会被编译成字节码（bytecode），并由 EVM 逐条解释执行。执行字节码：字节码指令在 EVM 中执行，通过操作栈、内存、存储、程序计数器（PC）等来完成逻辑。消耗 gas：每执行一条指令都会消耗 gas，gas 不足会导致回滚。更新状态：执行结束后，将结果写入状态树（State Trie）或回滚。二、EVM 中的三种关键存储结构1. 堆栈（Stack）大小固定：最大深度为 1024。操作方式：后进先出（LIFO），类似于汇编里的寄存器。所有算术逻辑操作都必须通过堆栈完成，如 ADD, MUL, CALLDATALOAD 等。只能操作栈顶元素（通过 PUSH, POP, DUP, SWAP 控制）。示例：PUSH1 0x02 PUSH1 0x03 ADD执行后：堆栈中留下 0x05。2. 内存（Memory）临时存储空间：生命周期只在当前合约调用过程中，调用结束就释放。是一个线性字节数组，按需扩展，初始化全为 0。可读写，适合处理中间数据，如函数参数、返回值、字符串、数组等。操作指令包括：MLOAD, MSTORE, MSTORE8。成本： 访问越高地址，gas 越贵（因为内存要扩容）。3. 存储（Storage）永久存储空间：存储在区块链状态中，合约变量的最终值都保存在这里。结构类似一个巨大的 key-value 映射（256-bit 到 256-bit）。极其昂贵（读比内存贵很多，写更贵），所以要谨慎使用。操作指令包括：SLOAD, SSTORE。示例： Solidity 中的 uint a; 在 EVM 中会映射到 storage[0]，a = 5 相当于 SSTORE 0 5。三、EVM 执行模型简图：四、执行环境（Execution Context）Program Counter（PC）：指向当前执行的字节码位置。Gas：限制执行资源，防止死循环。Call Data：外部调用传入的数据，只读。Code：当前执行的合约字节码。Logs：用于触发事件。返回值区域：放置 RETURN 指令返回的数据。五、与 Solidity 的映射关系Solidity 结构EVM 存储位置局部变量Stack / Memory状态变量Storage函数参数Memory / Stack数组 / 映射Storage / Memory（视情况而定）六、Opcodes1.分类Opcodes可以根据功能分为以下几类:堆栈（Stack）指令: 这些指令直接操作EVM堆栈。这包括将元素压入堆栈（如PUSH1）和从堆栈中弹出元素（如POP）。算术（Arithmetic）指令: 这些指令用于在EVM中执行基本的数学运算，如加法（ADD）、减法（SUB）、乘法（MUL）和除法（DIV）。比较（Comparison）指令: 这些指令用于比较堆栈顶部的两个元素。例如，大于（GT）和小于（LT）。位运算（Bitwise）指令: 这些指令用于在位级别上操作数据。例如，按位与（AND）和按位或（OR）。内存（Memory）指令: 这些指令用于操作EVM的内存。例如，将内存中的数据读取到堆栈（MLOAD）和将堆栈中的数据存储到内存（MSTORE）。存储（Storage）指令: 这些指令用于操作EVM的账户存储。例如，将存储中的数据读取到堆栈（SLOAD）和将堆栈中的数据保存到存储（SSTORE）。这类指令的gas消耗比内存指令要大。控制流（Control Flow）指令: 这些指令用于EVM的控制流操作，比如跳转JUMP和跳转目标JUMPDEST。上下文（Context）指令: 这些指令用于获取交易和区块的上下文信息。例如，获取msg.sender（CALLER）和当前可用的gas（GAS）。2.堆栈（Stack）指令栈结构的定义路径：go-ethereum/core/vm/stack.go//栈结构是一个uint256的切片 type Stack struct { data []uint256.Int }push确保了切片的长度最多1024func (st *Stack) push(d *uint256.Int) { // NOTE push limit (1024) is checked in baseCheck st.data = append(st.data, *d) } func (st *Stack) pop() (ret uint256.Int) { ret = st.data[len(st.da…