EVM之源码分析
ethereum的虚拟机源码所有部分在core/vm下。 去除测试总共有24个源码文件。 整个vm调用的入口在go-ethereum/core/state_transaction.go中。 我们主要是为了分析虚拟机源码,所以关于以太坊是如何进行交易转账忽略过去。
1 | func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) { |
从上面的截图我们可以看出, 当以太坊的交易中to地址为nil时, 意味着部署合约, 那么就会调用evm.Create方法。 否则调用了evm.Call方法。 也就是说分析以太坊虚拟机源码时, 只要从这两个函数作为入口即可。
首先我们先看一下EVM数据结构:
1 | type EVM struct { |
先看一看创建EVM的方法
1 | func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { |
接下来我们先分析部署合约的入口, 看一看整个合约部署的流程。
1 | // Create creates a new contract using code as deployment code. |
所以我们下面就开始主要分析run函数
1 | func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) { |
到了这里整个部署合约流程就完成了, 部署合约时是从evm.Create->run->interper.run 然后在执行codeCopy指令后把runtime的内容返回出来。 在evm.Create函数中我们也看到了当run执行完成后会把runtime的合约代码最终设置到合约地址名下。 整个合约部署就算完成了。
分析完合约创建接着就该分析合约调用代码了。 调用智能合约和部署在以太坊交易上看来就是to的地址不在是nil而是一个具体的合约地址了。 同时input的内容不再是整个合约编译后的字节码了而是调用函数和对应的实参组合的内容。 这里就涉及到另一个东西那就是abi的概念。此处我不打算详细说明, abi描述了整个接口的详细信息, 根据abi可以解包和打包input调用的数据。
1 | // 忽略一些隐藏了主线的内容 |
应该说到了这里只能合约流程就完事了, 可是也许你会好奇命名evm里面有那么多的内容没有分析到。 但是整个流程确实就是这些。 其他的比如栈对象是如何模拟的, 内存是如何模拟的。 操作码对应的操作函数,及其相关gas花费怎么计算的都没有说明。 可是我觉得首先知道整个流程和原理。阅读这些就比较容易了, 因为我们知道目的和原理, 就会明白它的那些代码的作用了。 如果我上去就说那些东西, 整个主线就会被淹没了。
最后还有一个比较重要的接口要说明一下, 它是我们接下来移植中要重点修改的内容。
1 | type StateDB interface { |
到此整个evm分析就结束了,