想象你走进一座没有围墙的数字城市——以太坊。这里没有市长,没有物业,任何人都能落户、摆摊、做生意。你在街上看到的“市民”分为两种:一种揣着钥匙,能自己走路办事,叫外部账户;另一种是立在路边的自动售货机,投币就自动出货,没有人的性格,只有铁的规则,叫合约账户。这座城市的每一笔买卖,都被记录在一本人人持有的公共档案里;而每一份“办事单”要真正生效,都得先加满油、再送进城市中央那一台名叫EVM的引擎里,由它按部就班地执行。这篇文章不堆术语,只带你“逛城”:先认账户,再看交易,然后付Gas、查档案,最后钻进引擎内部,看懂字节码是怎么被一步步消化掉的。逛完你就明白,所谓智能合约,不过是一台永远按规则运行的自动售货机。

岗位关联

对AI从业者来说,理解以太坊与EVM是进入Web3、链上数据分析和去中心化AI应用开发的第一块入门砖,能帮你读懂链上执行的底层逻辑与费用构成。

目录
  1. 可视化演示
  2. 账户模型:城市里的“人”与“自动售货机”
  3. 交易与消息:办事单的流转
  4. Gas机制:燃油经济学
  5. 状态存储:档案馆的索引系统
  6. EVM执行引擎:城市的心脏
  7. 字节码与编译:从图纸到机器指令
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

动态演示拆解以太坊的核心机制,通过基础/核心/应用/扩展四种模式展示关键概念与交互流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

拆解以太坊:以太坊账 以太坊账户模型与状态结构 Gas机制与交易费用计算 核心机制可视化 拆解以太坊整体架构与关键组件

账户模型:城市里的“人”与“自动售货机”

想象一座向全世界开放的数字城市——以太坊。街上走着两类“居民”:一类是揣着钥匙的市民,能自己走路、主动办事、签名转账;另一类是立在街角的自动售货机,没有腿、没有意志,但只要投币符合条件,就会自动吐出商品。你没法让售货机“主动”来帮你搬东西,却可以把钱和指令塞进去,等它默默按规则执行。这种“有主动能力的居民”与“只会按程序反应的机器”之间的对比,正是理解以太坊账户模型的钥匙。

在以太坊里,“市民”和“售货机”统称为账户(Account)。外部账户(EOA)由一对公私钥控制,谁持有私钥谁就能支配它;合约账户则由一段字节码控制,一旦部署就按代码运行,任何人都无法篡改它的行为。两者都有余额(balance)和nonce(交易计数),合约账户额外保存代码(code)与存储(storage)。当你要转账或调用合约时,必须用私钥对交易签名,因为私钥是唯一能证明“你是这个账户所有者”的凭证。

你可能会好奇:地址是怎么来的?它不是随机生成的,而是由公钥经哈希运算推导而出。地址 = 公钥做Keccak256哈希后,取最后20字节。这个过程是单向的:从地址无法反推公钥,更无法反推私钥,但任何人都可以用公钥验证签名。

市政厅不需要记住你的长相,只需拿你的钥匙指纹现场算出门牌号,对上了就放行。这就是地址生成的巧妙之处——公钥可以公开,私钥必须藏好,谁有钥匙谁就是主人。

address=keccak256⁡(pubKey)[12:]\text{address} = \operatorname{keccak256}(\text{pubKey})[12:]
地址不是“用户名”,而是你钥匙的“指纹门牌号”——别人知道门牌能给你寄信,但打不开你的门。
  1. 哈希公钥对公钥进行Keccak256单向哈希,得到64字节的摘要,把“长钥匙”压缩成“短指纹”。
    hash=keccak256⁡(pubKey)\text{hash} = \operatorname{keccak256}(\text{pubKey})
  2. 截取后20字节从哈希第12字节开始,截取到末尾共20字节,就是通用的0x地址。
    address=hash[12:]\text{address} = \text{hash}[12:]

交易与消息:办事单的流转

市民要办事,得先填一张“办事单”。单子上写明:这是第几号申请(nonce)、每升油愿意出多少钱(gasPrice)、最多加多少油(gasLimit)、办什么事(to、value、data),最后签上自己的名字。单子填好后投进窗口,由城市系统广播给全网,等待被装进一个“包裹”(区块)。

这个“办事单”在以太坊里就叫交易(Transaction)。交易只能由外部账户发起,合约账户无法主动“发单”。交易的结构包含六个核心字段:nonce、gasPrice、gasLimit、to、value、data。其中to字段为目标地址;如果to为空,则表示这是一笔“创建合约”的交易。合约之间也可以相互沟通,这种内部调用叫消息调用(message call),它不产生独立的Gas字段,而是消耗父交易剩余的Gas——就像一台售货机内部机械臂,不能自己出门加油,只能花掉顾客投币产生的“额度”。

一笔交易的完整生命周期可以概括为四步:构造与签名、广播进内存池、矿工打包进区块、全节点在EVM中执行并更新状态。有趣的是,部署合约时,新合约的地址并非由你指定,而是由“发起者地址 + 发起者交易计数nonce”经RLP编码和Keccak256哈希后,取后20字节得到。

自动售货机的“出厂编号”由“申请人地址+申请序号”唯一确定。谁在第几号窗口申请,机器编号就是对应的哈希,绝不会撞号。

contractAddress=keccak256⁡(rlp⁡([sender,nonce]))[12:]\text{contractAddress} = \operatorname{keccak256}(\operatorname{rlp}([\text{sender}, \text{nonce}]))[12:]
合约地址是“申请人和序号”的数学指纹,只要知道谁在哪个序号部署,就能预先算出它的门牌号——这也是CREATE2能提前预测合约地址的原理。
  1. RLP编码把发送者地址和nonce按RLP规则编码成一段唯一的字节串。
    bytes=rlp⁡([sender,nonce])\text{bytes} = \operatorname{rlp}([\text{sender}, \text{nonce}])
  2. Keccak256哈希对这段字节串求哈希,打乱并压缩成64字节摘要。
    hash=keccak256⁡(bytes)\text{hash} = \operatorname{keccak256}(\text{bytes})
  3. 截取后20字节取哈希第12字节到末尾,得到合约地址。
    contractAddress=hash[12:]\text{contractAddress} = \text{hash}[12:]

Gas机制:燃油经济学

城市里跑车要加油。烧油不只是为了驱动车辆,更是为了防止有人开着车满城乱撞、刷存在感把道路堵死。你跑得越远、载得越重,油耗就越高。以太坊设计了同样的机制:每一笔操作都要消耗“燃油”——Gas。算术运算只需3个单位,读写临时内存只需几到几十个单位,而往持久存储里写一个数据(SSTORE)要花约20000个单位,几乎是算术的几千倍。

为什么存储这么贵?因为链上数据需要被全世界每个节点永久保存,存得越多,整座城市的“档案负担”越大。所以以太坊用价格杠杆鼓励大家少写存储。当你发起交易时,需要设定两个关键参数:gasLimit(这趟最多加多少油)和gasPrice(每升油愿意出多少钱)。矿工按gasPrice排序优先打包高油价的交易,就像加油站高峰期,愿意出更高价的车先进站。

交易结束后,实际费用按如下方式结算:

就像打出租车:路程决定耗油量,单价决定每升油多少钱。半路抛锚或出事故(交易失败),已经跑过的路程照常计费,绝不会因为没到达目的地就免单。

fee=gasUsed×gasPrice\text{fee} = \text{gasUsed} \times \text{gasPrice}
总费用 = 实际耗油量 × 每单位油价。没烧完的油会退还到你的账户,但如果油在半路耗尽,整笔交易回滚,已烧掉的油不会退还。
  1. 耗油量(gasUsed)交易执行过程中,每个操作码消耗的Gas累加之和。
    gasUsed=∑opcost(op)\text{gasUsed} = \sum_{\text{op}} \text{cost}(\text{op})
  2. 油价(gasPrice)你为每个Gas单位支付的单价,由市场供需决定。
    gasPrice=你设定的单价\text{gasPrice} = \text{你设定的单价}
  3. 总费用两者相乘得到最终从账户扣除的金额。
    fee=gasUsed×gasPrice\text{fee} = \text{gasUsed} \times \text{gasPrice}

状态存储:档案馆的索引系统

城市的档案馆每天新增海量档案,管理员不能每次确认“某人现在有多少钱”都翻遍整栋楼。于是她设计了一棵神奇的目录树:把每一份档案都做指纹哈希,两两配对、层层向上,最终汇总成一个“总指纹”——根哈希。任何人只要知道这个总指纹,就能快速验证任意一份档案是否被篡改,而无需下载全部档案。

以太坊的“世界状态”正是这样一棵树。它是一棵全局唯一的Patricia Merkle树,把所有账户的余额、nonce、代码哈希和存储根都挂在这棵树上。每个区块头里只保存一个stateRoot字段,相当于整个档案馆的“总印章”。当一个节点同步到新区块时,它会重新执行交易、重算状态树,如果算出的根与区块头不一致,说明数据被篡改或执行出错,这个区块会被拒绝。

合约账户内部的数据也存在一棵独立的存储树中。每棵存储树由无数个256位的“存储槽”(slot)组成,每个槽位都可以存一个256位数字。你可以把存储槽想象成售货机里一个个货道:编号固定,每个货道里放什么由合约代码决定。读存储很便宜,写存储极贵——因为一旦写入,就意味着全世界的档案馆都要为这个货道永久腾出空间。

所谓“防篡改”,靠的不是管理员盯得紧,而是数学上的蝴蝶效应——任何一粒灰尘落在哪片叶子上,都会让总印章彻底变脸。

root=H(H(leaf1)∥H(leaf2))\text{root} = H(H(\text{leaf}_1) \parallel H(\text{leaf}_2))
Merkle树的根哈希相当于整棵树的“数字指纹”,任何一个叶子节点哪怕改一个比特,根都会彻底变样——这就是链上数据难以篡改的数学根基。
  1. 叶子哈希每个账户数据先做一次哈希,得到固定长度的“指纹”。
    hashi=H(accounti)\text{hash}_i = H(\text{account}_i)
  2. 配对拼接相邻两个指纹首尾相连成一个更长的字节串。
    pair=hash1∥hash2\text{pair} = \text{hash}_1 \parallel \text{hash}_2
  3. 递归哈希对拼接结果再次哈希,得到父节点;层层向上直到根节点。
    root=H(pair)\text{root} = H(\text{pair})

EVM执行引擎:城市的心脏

所有办事单最终会被送进市政大厅中央的一台精密机器——EVM(以太坊虚拟机)。它不认得城市风景,不关心天气和心情,只认一条条由字节码翻译成的机械指令。这台机器不是摆设,它运行在全世界每一个以太坊节点中。无论你在北京、东京还是纽约执行同一笔交易,得到的最终状态都完全一致——这种“确定性”是区块链共识的基础。

EVM是一个基于堆栈的虚拟机(Stack Machine)。它有三块截然不同的工作区:堆栈(Stack)是临时操作台,深度上限1024,所有算术都在这里进行,先进后出,像一摞盘子;内存(Memory)是临时草稿纸,按字节寻址,只存在于当前交易执行期间,交易结束后即被清空;存储(Storage)是永久账本,数据会写到链上,由全世界共同保存,因此也最贵。

EVM的执行流程可以理解为一个循环:取指令(PC计数器指向当前指令)→ 解码操作码 → 扣减Gas → 执行操作 → 更新PC并进入下一条。每次扣Gas前,EVM都会检查剩余Gas是否足够:

这台机器每走一步都先看油箱,油不够就立即停车并把一切恢复原状。正是这种“油尽回滚”的规则,保证了全世界数万个节点在各自机器上跑出完全一致的最终状态。

gas′=gas−cost,if gas′<0 then revert\text{gas}' = \text{gas} - \text{cost}, \quad \text{if} \ \text{gas}' < 0 \ \text{then revert}
每当一个操作码要扣钱,EVM先问“油箱还够不够”;不够就立刻停车回滚,把所有状态恢复成交易执行前,仿佛什么都没发生过。
  1. 本步成本每个操作码都有明码标价,如PUSH是3,SSTORE是20000。
    cost=operationPrice\text{cost} = \text{operationPrice}
  2. 扣减余额用当前剩余Gas减去本步成本。
    gas′=gas−cost\text{gas}' = \text{gas} - \text{cost}
  3. 回滚判断如果结果为负,说明油不够,触发整笔交易回滚。
    if gas′<0 then revert\text{if} \ \text{gas}' < 0 \ \text{then} \ \text{revert}

字节码与编译:从图纸到机器指令

售货机出厂前,工程师先画图纸(Solidity源码),再翻译成机器能执行的内部动作序列(字节码),最后装到街上通电试运行(部署)。以太坊的智能合约开发也遵循同样的流程。用Solidity写好的合约,交给solc编译器,会生成两段字节码:初始化代码(init code)和运行时代码(runtime code)。部署交易执行的是init code,它的职责是“装修”——把runtime code作为返回值写到链上,从此永久保存。

字节码由一系列单字节操作码(opcode)组成。比如0x60代表PUSH1(压入一个1字节数值)、0x01代表ADD(加法)、0x55代表SSTORE(写入存储)。整个字节码就是一张“机械动作清单”,EVM逐条读取、逐条执行。由于操作码种类有限、行为确定,合约能做什么、不能做什么,其实在你部署的那一刻就被这张清单锁死了。

当用户调用合约函数时,调用数据(calldata)的前4个字节叫“函数选择器”(function selector),它告诉EVM“你该执行清单里的哪一段动作”。选择器的计算方式是对函数签名(如 transfer(address,uint256))做Keccak256,取前4字节:

你发给售货机的每一枚代币,机器看一眼“编号前缀”就知道该掉出哪种饮料。而合约一旦部署,这道“编号规则”就永远刻在链上,谁也不能偷偷改装机器。

selector=keccak256⁡(signature)[0:4]\text{selector} = \operatorname{keccak256}(\text{signature})[0:4]
调用合约时,EVM只看前4字节就知道你要“转账”还是“查询余额”,就像老练的邮差只看信封上的几个数字就能分拣信件。
  1. 构造签名把函数名和参数类型写成规范字符串,如 transfer(address,uint256)。
    signature="transfer(address,uint256)"\text{signature} = \text{"transfer(address,uint256)"}
  2. Keccak256哈希对签名串求哈希,得到64字节摘要。
    hash=keccak256⁡(signature)\text{hash} = \operatorname{keccak256}(\text{signature})
  3. 截取前4字节取哈希前4字节作为函数选择器。
    selector=hash[0:4]\text{selector} = \text{hash}[0:4]

AI 应用场景

场景一:智能合约安全审计助手

阶段:代码分析与特征提取(模型推理)

解决的问题:人工审计一份合约往往需要数天,容易漏掉隐蔽的漏洞。AI可以扫描源码和字节码特征(如SSTORE调用位置、CALL的深度),快速标记重入漏洞、整数溢出、Gas陷阱等风险点,把审计时间从几天压缩到几小时。

具体案例:某安全公司用大语言模型读取Solidity源码,再结合EVM字节码反汇编结果,自动生成审计报告并给出高危函数定位。分析师只需复核AI标记的少数位置,效率提升近10倍。

场景二:链上交易行为分析

阶段:数据工程与特征工程

解决的问题:公链上每时每刻产生海量交易,传统SQL无法高效处理非结构化的事件日志和嵌套调用关系。AI工程师需要理解交易结构、Gas消耗和消息调用链,才能把原始数据清洗成可训练的特征。

具体案例:一个量化团队在做MEV(矿工可提取价值)策略时,用聚类算法分析Gas Price分布与交易排队模式,预测抢跑交易的黄金时机。他们先按“外部账户→合约账户→内部消息”的结构解析交易,再训练预测模型。

场景三:去中心化AI推理与Gas预估

阶段:推理上链与成本预估

解决的问题:AI模型跑在链上成本极高,工程师需要精确计算每个操作的Gas才能决策“哪部分上链、哪部分留链下”。理解EVM的存储成本是优化的关键。

具体案例:一个去中心化AI项目要把轻量逻辑回归模型部署到链上,他们通过改写合约、用事件日志代替存储、合并SSTORE操作,把单次推理的Gas费从几百万单位降到几十万单位。

Python 演示

python57 行
"""迷你EVM风格栈执行器: 演示指令执行与Gas消耗。"""  class StackMachine:    """用栈模拟EVM的核心执行,记录每步Gas变化。"""     def __init__(self, initial_gas=30000):        self.stack = []        self.gas_left = initial_gas        self.storage = {}        self.logs = []     def _consume(self, cost):        """扣除Gas,不足则回滚。"""        if self.gas_left < cost:            raise RuntimeError("out of gas, transaction reverted!")        self.gas_left -= cost     def push(self, value):        """模拟PUSH指令,消耗3 Gas。"""        self._consume(3)        self.stack.append(value)        self.logs.append(            f"PUSH {value}  stack={self.stack}  gas={self.gas_left}"        )     def add(self):        """模拟ADD指令,消耗3 Gas。"""        self._consume(3)        b = self.stack.pop()        a = self.stack.pop()        self.stack.append(a + b)        self.logs.append(            f"ADD {a}+{b}={a + b}  stack={self.stack}  gas={self.gas_left}"        )     def sstore(self):        """模拟SSTORE指令,消耗20000 Gas,写入持久存储。"""        self._consume(20000)        slot = self.stack.pop()        value = self.stack.pop()        self.storage[slot] = value        self.logs.append(            f"SSTORE storage[{slot}]={value}  gas={self.gas_left}"        )  if __name__ == "__main__":    machine = StackMachine()    machine.push(1)    machine.push(2)    machine.add()    machine.push(0)    machine.sstore()    for line in machine.logs:        print(line)    print("final storage:", machine.storage)
目的:以不到70行的Python模拟EVM的堆栈执行和Gas扣减,直观展示“算术便宜、存储昂贵”的核心规律。
  1. def _consume(self, cost):定义扣Gas方法,所有指令执行前都要先查油表。
  2. raise RuntimeError("out of gas, transaction reverted!")油量不足时抛异常,模拟EVM整笔回滚。
  3. self.stack.append(value)模拟PUSH:把数字压入栈顶。
  4. self.stack.pop()弹出栈顶操作数,模拟算术指令取数。
  5. self.storage[slot] = value写持久存储,对应EVM最贵的SSTORE指令。
  6. if __name__ == "__main__":保证脚本可直接运行并输出日志。

常见误区

误区我一直以为GasPrice就是交易手续费,填1 Gwei就感觉特别便宜,结果转账时发现扣的钱比想象中多得多。

正解GasPrice只是每单位燃料的单价,总费用=耗油量×单价。真正决定手续费高低的,是你到底执行了多少操作。普通转账约21000 Gas,而一次SSTORE就可能烧掉20000 Gas。所以GasPrice低并不等于便宜,还要看操作复杂度。

为何常见:主流钱包界面只展示一个“矿工费”总额,很少拆解GasUsed和GasPrice,用户自然把单价误认为总价。

误区我以为部署合约时能像注册用户名一样,给自己的合约挑一个好看的地址,结果发现地址完全不由我决定。

正解合约地址是“发送者地址+nonce”的RLP编码经Keccak256哈希后取后20字节,是可预测但不可挑选的。只有通过CREATE2操作码,才能把盐值(salt)纳入计算,提前算出并锁定地址。

为何常见:用户只见过部署完成后的地址,从未接触生成过程,容易按现实世界的“注册”直觉去理解。

误区EVM和JVM、V8差不多吧?为什么不能直接在链上打断点、单步调试、查看变量?它应该有完整的调试器。

正解EVM是确定性的执行环境:不访问系统时钟、不做文件IO、没有随机数。所有节点必须对同一交易产出完全一致的结果,所以链上不存在“调试模式”。开发者只能通过Hardhat、Tenderly等模拟器预演执行过程,部署后再也不能修改。

为何常见:把EVM类比成普通编程语言的运行时,忽略了区块链“全网一致”的共识约束。

误区既然没用完的Gas会退回来,那我GasLimit随便设个几百万也没关系,反正不会多扣钱。

正解交易成功后,未消耗的Gas确实会退还。但如果Gas耗尽导致交易回滚,已消耗的Gas照扣不误,而且失败的交易同样要付钱给矿工。此外,GasLimit过高会占用区块资源,钱包通常不允许无限提高。

为何常见:只记住了“退还”两个字,没理解“退还的前提是交易执行到某一步且余额充足”。

误区以太坊应该就是把所有账户余额存进一张超大SQL表,查余额就SELECT一下。

正解账户状态被组织成一棵Patricia Merkle树,每个区块头只存一个根哈希。好处是轻节点无需下载全量数据也能验证某账户余额;任何篡改都会导致根哈希变化,被其他节点拒绝。这是传统数据库没有的密码学可验证性。

为何常见:传统后端开发的思维惯性,把链上状态等同于MySQL里的表,忽略了Merkle树在“可验证”上的独特价值。

面试题

Q1. 以太坊中由私钥控制、能主动发起交易的账户类型是?

单选题 · easy

  • A.合约账户类型
  • B.外部账户类型← 正确答案
  • C.多签账户类型
  • D.系统账户类型

答案:B。外部账户(EOA)由私钥控制,能主动签名发起交易;合约账户只能由代码触发执行,无法主动发起交易。多签和系统账户不是以太坊的基础账户分类。

Q2. 以太坊地址本质上是什么?

单选题 · easy

  • A.公钥的本身形式
  • B.公钥哈希截取段← 正确答案
  • C.私钥哈希的数值
  • D.随机数种子生成

答案:B。地址 = Keccak256(公钥) 的后20字节。它由公钥单向哈希而来,无法反推出私钥。选项A错在“本身”,C错在“私钥”,D错在“随机”。

Q3. 账户nonce字段的主要作用是什么?

单选题 · mid

  • A.记录交易的笔数← 正确答案
  • B.记录账户的余额
  • C.计算交易的费用
  • D.更新存储的键值

答案:A。nonce是外部账户已发出的交易计数,每笔交易递增一次,可防止同一笔签名交易被重复广播(重放攻击)。余额、费用、存储键值都与nonce无关。

Q4. 合约地址的正确生成方式是什么?

单选题 · mid

  • A.部署者自行指定
  • B.随机算法随机生成
  • C.哈希发送者与nonce← 正确答案
  • D.矿工集体投票确定

答案:C。合约地址 = Keccak256(RLP([发送者地址,nonce])) 的后20字节,因此可以预先推算。但普通用户不能“指定”地址,矿工也不参与地址生成。

Q5. GasLimit设高但交易成功后,未用Gas会怎样?

单选题 · mid

  • A.全部被矿工扣走
  • B.按比例退还一半
  • C.退还到发送账户← 正确答案
  • D.自动转入合约账户

答案:C。交易按实际耗油量(gasUsed)结算,未消耗的Gas退回发送者账户,不会转给合约或矿工。矿工只获得实际消耗的部分。

Q6. 以下哪个操作消耗的Gas最多?

单选题 · easy

  • A.ADD 加法运算
  • B.SSTORE 写存储← 正确答案
  • C.PUSH1 压栈操作
  • D.POP 弹出栈顶

答案:B。SSTORE写存储约20000 Gas,因为数据要被全网永久保存;而ADD、PUSH1、POP等算术与栈操作只需3 Gas。存储比计算贵几千倍。

Q7. 交易总费用的正确计算公式是?

单选题 · hard

  • A.耗油量乘以油价← 正确答案
  • B.上限乘以油价再加值
  • C.耗油量加上交易值
  • D.油价乘以转账金额

答案:A。交易费 = gasUsed × gasPrice。gasLimit只是上限,不是实际消耗;转账额(value)另算,不参与手续费计算。失败交易也按已消耗的Gas扣费。

Q8. 以下属于EVM三种数据区域的有?(选两项)

多选题 · mid

  • A.堆栈Stack存储← 正确答案
  • B.内存Memory区域← 正确答案
  • C.磁盘Disk存储区
  • D.寄存器Reg区域

答案:AB。EVM的三种数据区域是堆栈(Stack)、内存(Memory)和存储(Storage)。磁盘与寄存器是传统计算机的概念,EVM中没有这两种区域。

Q9. 关于失败的链上交易,哪些说法正确?(选两项)

多选题 · mid

  • A.失败交易同样会扣费← 正确答案
  • B.失败后状态不会回滚
  • C.失败后所有状态回滚← 正确答案
  • D.失败交易不消耗任何Gas

答案:AC。交易失败时,已消耗的Gas不退还(所以A对、D错);所有状态变更会回滚,像没发生过一样(所以C对、B错)。失败交易的费用付给了打包它的矿工。

Q10. 关于EVM字节码,哪两项正确?(选两项)

多选题 · hard

  • A.字节码由CPU直接执行
  • B.部署后代码可以修改
  • C.函数选择器是哈希前段← 正确答案
  • D.字节码由EVM执行← 正确答案

答案:CD。函数选择器是函数签名Keccak256哈希的前4字节,由EVM解释执行而非CPU直接执行。合约部署后代码不可变,这是区块链的共识规则。