以太坊虚拟机 (Ethereum Virtual Machine, EVM) 负责执行交易和更新区块链状态。
EVM 是一个状态执行的机器,输入是 solidity 编译后的二进制指令和节点的状态数据,输出是节点状态的改变。
像 VirtualBox 或 QEMU 是计算机的虚拟,KVM 是整个操作系统实例的虚拟,他们分别提供了硬件、系统调用和其它内核功能的软件抽象。EVM 则只是一个计算引擎,提供了计算和存储的抽象。EVM 没有调度能力,因为执行顺序是外部组织的。以太坊客户端通过验证的区块交易来确定哪些智能合约需要执行以及执行顺序。从这个意义上讲,以太坊世界计算机是单线程的,就像 JavaScript 一样。EVM 也没有任何“系统接口”处理或“硬件支持”——没有与之交互的物理机器。
EVM 可以访问账户信息(比如地址和余额)和区块链信息(比如 block number 和 gas 费用)。
EVM 的位置:
EVM 架构:
EVM 由程序计数器 (Program Counter)、堆栈 (Stack)、内存 (Memory) 和外部存储(Storage)组成。
EVM 存储:
EVM 中数据可以在三个地方进行存储,分别是栈,临时存储,永久存储。
EVM 运行:
当 EVM 运行时,它的状态可被定义为 (block_state, transaction, message, code, memory, stack, pc, gas),block_state 包含所有帐户的全局状态,包括余额和存储。每一轮执行开始时,通过 code 的第 pc 个字节获取当前指令,每条指令都有自己的定义,并且影响着状态。例如,ADD 从栈中弹出两项并将它们的总和压栈,将 gas 减 1 并将 pc 加 1。SSTORE 将栈中的前两项压栈,并将第二项按照第一项的索引插入合约存储中。
code 是 solidity 编译后的二进制指令/字节码,EVM 会将 code 分解为 Opcode。理论上共有 256 个长度为 1 字节的 Opcode,但 EVM 只使用了 140 种。code 示例如下:
60003560e01c80632e64cec11461003b5780636057361d1461005957
上面示例中的每个字节都指向不同的 Opcode。例如,第一字节(例如 60)是 PUSH1 操作码,下一字节(例如 00)是正被 push 的数据,第三字节(60)是 PUSH2 操作码,而下一字节是其输入(例如 e0)。
启动的每个 EVM 实例都是为了运行一段字节码。因此,字节码就像是 EVM 实例的 ROM,是不能修改的。
一个合约可以调用另一个合约,每次调用都会导致一个新的 EVM 实例化,如下图所示。
Gas:
每次执行指令时,内部计数器会记录需要向用户收取的佣金。当用户发起交易时,钱包中需要留有少量资金来支付这些佣金。
gas 有两个功能:
源码:SputnikVM
benches/loop.rs
SputnikVM 组成:
let backend = MemoryBackend::new(&vicinity, state);
let metadata = StackSubstateMetadata::new(u64::MAX, &config);
let state = MemoryStackState::new(metadata, &backend);
let precompiles = BTreeMap::new();
let mut executor = StackExecutor::new_with_precompiles(state, &config, &precompiles);
pub struct MemoryStackSubstate<'config> {
metadata: StackSubstateMetadata<'config>,
parent: Option<Box<MemoryStackSubstate<'config>>>,
storages: BTreeMap<(H160, H256), H256>,
logs: Vec<Log>, // 日志
accounts: BTreeMap<H160, MemoryStackAccount>, // 交易所接触过的账户集合
deletes: BTreeSet<H160>, // 自毁集合
}
通过给定的 caller,target address,value,data 和 gas limit 执行 CALL 指令。最后一个参数 access_list 是 Ethereum Berlin hard fork 后引入的。
let (exit_reason, data) = executor.transact_call(
H160::from_str("0xf000000000000000000000000000000000000000").unwrap(),
H160::from_str("0x1000000000000000000000000000000000000000").unwrap(),
U256::zero(),
hex::decode("0f14a4060000000000000000000000000000000000000000000000000000000000b71b00")
.unwrap(),
u64::MAX,
Vec::new(),
);
源码:REVM
bins/revm-test/src/bin/snailtracer.rs
REVM 组成:
.3.1 创建 EVM
let mut evm = revm::new();
// BenchmarkDB is dummy state that implements Database trait.
let bytecode = to_analysed::<BerlinSpec>(Bytecode::new_raw(contract_data));
evm.database(BenchmarkDB::new_bytecode(bytecode.clone()));
EVM 输入:
// execution globals block hash/gas_limit/coinbase/timestamp..
evm.env.tx.caller = "0x1000000000000000000000000000000000000000"
.parse()
.unwrap();
evm.env.tx.transact_to = TransactTo::Call(
"0x0000000000000000000000000000000000000000"
.parse()
.unwrap(),
);
evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap());
EVM 运行:
evm.transact().unwrap();
不同之处:
本人不确定 REVM 是否支持不同的以太坊区块链,是否兼容物联网;不确定 SputnikVM 是否可以用作 wasm-lib。 两者的逻辑相近,个人感觉 REVM 的结构会简单一些。
SputnikVM | REVM | |
---|---|---|
star | 837 | 546 |
used by | 5.1k | 219 |
first release |
What is the Ethereum Virtual Machine (EVM)?
The Ethereum Virtual Machine (EVM) - What Is It and How to Make Business on It?
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。