以太坊EVM源码分析学习记录

这篇具有很好参考价值的文章主要介绍了以太坊EVM源码分析学习记录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

待办清单

  • analysis.go
  • common.go
  • contract.go
  • contracts.go
  • doc.go
  • eips.go
  • errors.go
  • evm.go
  • gas.go
  • gas_table.go
  • instructions.go
  • interface.go
  • interpreter.go
  • jump_table.go
  • logger.go
  • memory.go
  • memory_table.go
  • opcodes.go
  • operations_acl.go
  • stack.go
  • stack_table.go

结构与流程

2020年版本的evm结构

evm源码,区块链学习,学习,区块链

大致流程

编写合约 > 生成abi > 解析abi得出指令集 > 指令通过opcode来映射成操作码集 > 生成一个operation[256]

以太坊虚拟机的工作流程:
由solidity语言编写的智能合约,通过编译器编译成bytecode,之后发到以太坊上,以太坊底层通过evm模块支持合约的执行和调用,调用时根据合约获取代码,即合约的字节码,生成环境后载入到 EVM 执行。


opcodes.go

OptionCode(操作码)
OpCode
文件opcodes.go中定义了所有的OpCode,该值是一个byte,合约编译出来的bytecode中,一个OpCode就是上面的一位。opcodes按功能分为9组,以第一位十六进制数来分类,例如0x1x,0x2x。
例如第一组为 算术 操作

// 0x0 range - arithmetic ops.
const (
	STOP       OpCode = 0x0
	ADD        OpCode = 0x1
	MUL        OpCode = 0x2
	SUB        OpCode = 0x3
	DIV        OpCode = 0x4
	SDIV       OpCode = 0x5
	MOD        OpCode = 0x6
	SMOD       OpCode = 0x7
	ADDMOD     OpCode = 0x8
	MULMOD     OpCode = 0x9
	EXP        OpCode = 0xa
	SIGNEXTEND OpCode = 0xb
)

可以使用表格来总结

opCodeRange 对应操作
0x0 算术操作
0x10 比较操作
0x20 加密操作
0x30 状态闭包
0x40 区块操作
0x50 存储和执行操作
0x60 压栈操作
0x80 克隆操作
0x90 交换操作
0xa0 日志操作
0xf0 闭包

实现了判断能否压栈、操作码的byte类型和string类型互相转换的函数或接口。
func StringToOp(str string) OpCode
func (op OpCode) String() string
func (op OpCode) IsPush() bool


AddressLength = 20
HashLength = 32
type Address [AddressLength]byte
type bitvec [ ]byte
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
type Hash [HashLength]byte

contract.go

该文件中包含了饭回合约的调用者信息和value、判断gas值是否足够运行合约执行、

合约的结构

type Contract struct {
	// CallerAddress is the result of the caller which initialised this
	// contract. However when the "call method" is delegated this value
	// needs to be initialised to that of the caller's caller.
	CallerAddress common.Address
	caller        ContractRef
	self          ContractRef

	jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
	analysis  bitvec                 // Locally cached result of JUMPDEST analysis

	Code     []byte
	CodeHash common.Hash
	CodeAddr *common.Address
	Input    []byte

	Gas   uint64
	value *big.Int
}
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract

该函数构造了新的合约,且如果是被合约调用,则复用该合约的 jumpdests

func (c *Contract) validJumpdest(dest *uint256.Int) bool
func (c *Contract) isCode(udest uint64) bool

存在两段校验的函数检验代码跳转是否合法以及


  • int类型的大小为 8 字节
  • int8类型大小为 1 字节
  • int16类型大小为 2 字节
  • int32类型大小为 4 字节
  • int64类型大小为 8 字节

analysis.go

func (c *Contract) AsDelegate() *Contract

AsDelegate将合约设置为委托调用并返回当前合同(用于链式调用)


stack.go

为了应对高并发情况下的栈资源问题,代码中创建了 栈池 来保存一些被创造但未使用的栈空间。

var stackPool = sync.Pool{
	New: func() interface{} {
		return &Stack{data: make([]uint256.Int, 0, 16)}
	},
}

除了一些栈该有的基础操作以外,还有:

func (st *Stack) swap(n int) 将从栈顶开始数的第 n 个和栈顶元素交换

func (st *Stack) dup(n int) 复制栈顶元素,并将其压栈

func (st *Stack) Back(n int) *uint256.Int 返回栈底元素


stack_table.go

一些栈的辅助函数


Memory.go

type Memory struct {
	store       []byte
	lastGasCost uint64
}

为以太坊虚拟机提供一个简单存储的模型

func (m *Memory) Set(offset, size uint64, value []byte) 
func (m *Memory) Set32(offset uint64, val *uint256.Int) 
func (m *Memory) Resize(size uint64)
func (m *Memory) GetCopy(offset, size int64) (cpy []byte)  // 截取切片中的一段 (offset,offset+size)
func (m *Memory) GetPtr(offset, size int64)  // 返回切片中的一段的指针
func (m *Memory) Len() int
func (m *Memory) Data() []byte


Memory_table.go

衡量一些操作所消耗的内存大小同时判断是否会发生栈溢出,如keccak256、callDataCopy、MStore等


EVM.go

EVM机器位宽为256位,即32个字节,256位机器字宽不同于我们经常见到主流的64位的机器字宽

设计256位宽的原因:

  • 时间,智能合约是否能执行得更快
  • 空间,这样是否整体字节码的大小会有所减少gas成本

区块上下文

这里的Random same as difficulty(具体是什么还不知道)

前三个为函数类型,依次作用为 查询转账者账户是否有充足ether支持转账操作转账操作获取第n个区块的hash

其余为一些基础的区块信息,如币基交易地址、Gaslimit、区块高、时间戳、难度值和基础费用

区块一旦创建,区块信息不可以被修改

type BlockContext struct {
	// CanTransfer returns whether the account contains
	// sufficient ether to transfer the value
	CanTransfer CanTransferFunc
	// Transfer transfers ether from one account to the other
	Transfer TransferFunc
	// GetHash returns the hash corresponding to n
	GetHash GetHashFunc

	// Block information
	Coinbase    common.Address // Provides information for COINBASE
	GasLimit    uint64         // Provides information for GASLIMIT
	BlockNumber *big.Int       // Provides information for NUMBER
	Time        *big.Int       // Provides information for TIME
	Difficulty  *big.Int       // Provides information for DIFFICULTY
	BaseFee     *big.Int       // Provides information for BASEFEE
	Random      *common.Hash   // Provides information for PREVRANDAO
}

交易上下文

Origin是什么,就是第一个交易

type TxContext struct {
	// Message information
	Origin   common.Address // Provides information for ORIGIN
	GasPrice *big.Int       // Provides information for GASPRICE
}

EVM结构

evm是以太坊虚拟机基础对象,提供工具处理对应上下文中的交易。运行过程中一旦发生错误,状态会回滚并且不退还gas费用,运行中产生的任务错误都会被归结为代码错误。

type EVM struct 
	// Context provides auxiliary blockchain related information
	Context BlockContext
	TxContext
	// StateDB gives access to the underlying state
	StateDB StateDB
	// Depth is the current call stack
	depth int

  // chainconfig是决定区块链设置的核心配置。
  //chainconfig以块为单位存储在数据库中。这意味着
  //任何一个网络,通过它的起源块来识别,都可以有它自己的
  //一组配置选项。
	// 包含了chainId,该链什么时候发生硬分叉,该链难度总和到多少的时候发生更新等信息
	chainConfig *params.ChainConfig
	// chain rules contains the chain rules for the current epoch
	// rules包装了config信息,属于语法糖,是一次性接口,不应
	chainRules params.Rules
	// virtual machine configuration options used to initialise the
	// evm.
	// 解释器的配置信息
	Config Config
	// global (to this context) ethereum virtual machine
	// used throughout the execution of the tx.
	interpreter *EVMInterpreter
	// abort is used to abort the EVM calling operations
	// NOTE: must be set atomically
	// 能够终止evm调用操作
	abort int32
	// callGasTemp holds the gas available for the current call. This is needed because the
	// available gas is calculated in gasCall* according to the 63/64 rule and later
	// applied in opCall*.
	callGasTemp uint64
}

创建evm,只能用一次

func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM 

reset EVM的交易上下文和状态数据库

func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) 

能够通过原子的修改abort使得取消任何evm操作

func (evm *EVM) Cancel()
func (evm *EVM) Cancelled() bool 

合约预编译的作用

预编译合约是 EVM 中用于提供更复杂库函数(通常用于加密、散列等复杂操作)的一种折衷方法,这些函数不适合编写操作码。 它们适用于简单但经常调用的合约,或逻辑上固定但计算量很大的合约。 预编译合约是在使用节点客户端代码实现的,因为它们不需要 EVM,所以运行速度很快。 与使用直接在 EVM 中运行的函数相比,它对开发人员来说成本也更低。

evm调用深度 <= 1024

evm调用contract的步骤

  • 判断调用深度是否大于1024
  • 判断是否有充足的余额支持调用
  • 进行快照和预编译
  • 检查该地址是否在状态数据库中存在
  • 若不存在,调用一个不存在的帐户,不要做任何事情,只需ping跟踪程序,检查是否是debug模式,若不是则会创建账户
  • 判断是否预编译,若是调用节点客户端代码实现;反之,创建合约对象并加载被调用地址和地址的hash以及代码信息,后用解释器来运行
  • 若运行过程中有任何错误,则状态将会回滚到操作前快照处,并消耗gas

以太坊中的调用call、callcode和delegatecall

调用方式 修改的storage 调用者的msg.sender 被调用者的msg.sender 执行的上下文
call 被调用者的storage 交易发起者的地址 调用者的地址 被调用者
callcode 调用者的storage 调用者的地址 调用者的地址 调用者
delegatecall 调用者的storage 交易发起者的地址 调用者的地址 调用者

还有staticCall调用过程中不允许进行任何修改操作,可以用view来修饰,因此在函数实现中会给解释器的运行函数中的read-only参数传入true值。

创建合约

nonce值指定交易数,每发起一笔交易确认后nonce值+1


interpreter.go

解释器中会有一个配置结构体,能够选择debug模式,包含追踪操作码的evm日志,一些eip提议的配置,evm跳表

type Config struct {
	Debug                   bool      // Enables debugging
	Tracer                  EVMLogger // Opcode logger
	NoBaseFee               bool      // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
	EnablePreimageRecording bool      // Enables recording of SHA3/keccak preimages

	JumpTable *JumpTable // EVM instruction table, automatically populated if unset

	ExtraEips []int // Additional EIPS that are to be enabled
}

范围上下文

解释器结构,包含evm指针,配置信息,hasher??,是否只读,返回数据信息

type EVMInterpreter struct {
	evm *EVM
	cfg Config

	hasher    crypto.KeccakState // Keccak256 hasher instance shared across opcodes
	hasherBuf common.Hash        // Keccak256 hasher result array shared aross opcodes

	readOnly   bool   // Whether to throw on stateful modifications
	returnData []byte // Last CALL's return data for subsequent reuse
}
func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter

传入evm和配置信息构建新的解释器,根据配置信息设置该链的规则,如遵循eip158、eip150提议。

func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error)

会控制解释器堆栈调用的深度的加减,同时会用传入的合约、和栈池中调用一个栈来创建一个全新的上下文,并为它新建一个memory模型。

run函数中主要部分是处理一个死循环,只会在停止、返回、自毁和出错的时候停止。

通过循环一直执行合约中的操作,并且每次执行之前都要验证堆栈是否在限制范围之中,还要计算由于动态使用空间导致的动态gas费用,检查完这些之后才会由operation来执行操作。


jump_table.go

operation结构体

type operation struct {
	execute     executionFunc
	constantGas uint64
	dynamicGas  gasFunc				
	minStack int							 // 堆栈中需要已有多少项
	maxStack int							 //	堆栈中最多能有多少项(否则执行这个操作的时候会溢出)
	memorySize memorySizeFunc  // 返回该操作需要的内存大小
}

其中 executionFunc 有四处实现

func makeLog(size int) executionFunc
func makePush(size uint64, pushByteSize int) executionFunc
func makeDup(size int64) executionFunc
func makeSwap(size int64) executionFunc

memorySizeFunc 的实现在memory_table.go文件中

// memory_table.go
func memoryMLoad(stack *Stack) (uint64, bool) {
	return calcMemSize64WithUint(stack.Back(0), 32)
}

func memoryMStore8(stack *Stack) (uint64, bool) {
	return calcMemSize64WithUint(stack.Back(0), 1)
}

func memoryMStore(stack *Stack) (uint64, bool) {
	return calcMemSize64WithUint(stack.Back(0), 32)
}

// common.go
func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) 

我们可以看到这几个比较熟悉的操作,MLoad、MStore、MStore8 从栈中拿出 偏移量offset地址 + length

查看是否溢出 uint64

Solidity的内存布局将前4个32字节的插槽保留

  • 0x00 - 0x3f (64bytes): 暂存空间(Scratch space)
  • 0x40 - 0x5f (32bytes): 空闲内存指针
  • 0x60 - 0x7f (32bytes): 0 插槽值

他们的作用分别是

  • 用来给hash方法和内联汇编使用
  • 记录当前已经分配的内存大小,空闲内存的起始值为0x80
  • 用作动态内存的初始值,不会被使用

jumpTable包含指向操作的指针

type JumpTable [256]*operation
func validate(jt JumpTable) JumpTable

检查jumpTable中的操作是否为空

我们知道 opsCode是代码的解释器,这里的operation就是opsCode的解释器,interpreter中有一个jumptable,它包含了指向操作的指针,jumptable中的操作就是对应opscode的操作,但是在不同的config配置下,操作集合也会遵循不同的规则。

例如我们可以看看部分代码

// jump_table.go
func newFrontierInstructionSet() JumpTable {
	tbl := JumpTable{
		STOP: {
			execute:     opStop,
			constantGas: 0,
			minStack:    minStack(0, 0),
			maxStack:    maxStack(0, 0),
		},
		ADD: {
			execute:     opAdd,
			constantGas: GasFastestStep,
			minStack:    minStack(2, 1),
			maxStack:    maxStack(2, 1),
		},
    ......
    RETURN: {
			execute:    opReturn,
			dynamicGas: gasReturn,
			minStack:   minStack(2, 0),
			maxStack:   maxStack(2, 0),
			memorySize: memoryReturn,
		},
		SELFDESTRUCT: {
			execute:    opSelfdestruct,
			dynamicGas: gasSelfdestruct,
			minStack:   minStack(1, 0),
			maxStack:   maxStack(1, 0),
		},
	}
	// Fill all unassigned slots with opUndefined.
  // 将所有没有指定的插槽填充为 未定义操作
	for i, entry := range tbl {
		if entry == nil {
			tbl[i] = &operation{execute: opUndefined, maxStack: maxStack(0, 0)}
		}
	}
	return validate(tbl)
}

// opscode.go
// 0x0 range - arithmetic ops.
const (
	STOP       OpCode = 0x0
	ADD        OpCode = 0x1
	......
	EXP        OpCode = 0xa
	SIGNEXTEND OpCode = 0xb
)


instructions.go

指令集合,封装了操作指定过程中的堆栈操作。


gas.go

const (
   GasQuickStep   uint64 = 2
   GasFastestStep uint64 = 3
   GasFastStep    uint64 = 5
   GasMidStep     uint64 = 8
   GasSlowStep    uint64 = 10
   GasExtStep     uint64 = 20
)

根据是否遵循EIP150,返回实际的调用产生的费用


gas_table.go

func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error)

evm源码,区块链学习,学习,区块链

一些操作的gas值计算,如自毁、Call、Callcode、delegateCall、staticCall、内存存储等。


logger.go

EVM的日志接口,由不同的tracer或者logger实现。用于从EVM交易执行中收集执行时的信息,能够追踪交易级、调用级、opcode操作码级的信息。


contracts.go

存放预编译好的合约


common.go

存放常用工具方法

func calcMemSize64(off, l *uint256.Int) (uint64, bool) 
func getData(data []byte, start uint64, size uint64) []byte 
func toWordSize(size uint64) uint64
func allZero(b []byte) bool

计算内存空间是否溢出、根据给的参数返回数据切片、 返回内存扩展所需的字的大小、判断是否全0


eips.go

实现了许多eip协议的配置函数,可以通过函数的方式使能跳转表,使其能够遵循某个eip规则。


interface.go

包含stateDBCallContext两种接口,


在evm上,evm字节码是可执行的代码,合约abi是与EVM字节码交互的接口。

ppt中首先介绍web3js , contract abi – json format , evm

contract function 用来支持外部调用,使得应用-合约能够交互,使得合约-合约之间可以联系。

evm bytecode 对应EVM中的一系列的opcode指令

前4个byte是函数名的keccak256的前4个byte 后32byte是十六进制参数 左边用0补齐

所以这个bytecode是4 + 32 = 36 byte文章来源地址https://www.toymoban.com/news/detail-790198.html


到了这里,关于以太坊EVM源码分析学习记录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 区块链知识系列 - 系统学习EVM(一)-架构与执行流程

    EVM有一个基于栈的架构,在一个栈中保存了所有内存数值。EVM的数据处理单位被定义为256位的“字”( 这主要是为了方便处理哈希运算和椭圆曲线运算操作 ) 这里所说的内存数值是指那些EVM字节码运行所需要的输入、输出参数数据和智能合约程序运行中所需要的局部变量等

    2024年02月02日
    浏览(40)
  • 了解以太坊虚拟机(EVM)

    以太坊虚拟机(Ethereum Virtual Machine,简称EVM) 是以太坊网络的核心组件之一,它承担着 智能合约执行 的重要任务 智能合约执行环境 :EVM提供了一个安全的环境,用于 执行智能合约 。这些合约以 Solidity 等高级编程语言编写,并在以太坊区块链上部署和执行 不可篡改性 :

    2024年04月26日
    浏览(30)
  • 以太坊虚拟机 EVM 的原理

    作者:禅与计算机程序设计艺术 以太坊虚拟机(Ethereum Virtual Machine,简称EVM)是一个运行在以太坊区块链网络上执行智能合约、存储数据的软件。它负责部署、验证、执行智能合约的代码,并维护所有账户信息。用户可以通过发送交易数据到网络中请求执行智能合约。EVM是一

    2024年02月08日
    浏览(46)
  • 以太坊虚拟机EVM介绍,智能合约详解

    指的是“一套以数字形式指定的承诺,包括合约参与方可以在上面执行这些承诺的协议”。早在1994年,美国计算机科学家尼克·萨博(Nick Szabo)就提出了智能合约的概念。 智能合约是一种基于区块链技术的自动执行合约的计算机程序。它们是一组以代码形式编写的、在区块

    2024年02月04日
    浏览(43)
  • 以太坊EVM智能合约交易信息中Input和Logs解码

    以太坊交易获取一般通过两种方式:getTransaction、getTransactionReceipt web3.eth.getTransaction: web3.eth.getTransactionReceipt: abi-parser 是一个用于解析ethereum/web3 日志、事件和方法的 NodeJS/JS 库。

    2024年02月15日
    浏览(43)
  • Web3中文|什么是以太坊虚拟机(EVM),它是如何工作的?

    来源 | cointelegraph 编译 | Dali@iNFTnews.com 以太坊已成为仅次于比特币的第二重要区块链。以太坊能发展得这么好,它的原生Solidity编程语言和以太坊虚拟机(EVM)发挥了重要的作用。 以太坊区块链凭借自身拥有的灵活性、大量可用的开发工具和庞大的用户基础,继续吸引着去中

    2024年01月18日
    浏览(44)
  • AWR1642 + DCA100EVM 学习笔记

       ~~     自己在学习TI的AWR1642与DCA1000EVM时候的一些笔记,前面大体上是按照TI论坛上的各种教程来进行的,后面会加自己具体工程的笔记和代码。 因为自己也是零基础,所以想尽可能的写详细一些,方便之后的同学能够快速上手,我也把自己碰到的各种BUG给总结了一下,希

    2023年04月17日
    浏览(63)
  • Ethereum EVM简介

    首先需了解以下基本概念: 1)区块链 2)世界状态 3)账号 4)交易 5)消息 6)去中心化账本 7)原子性和顺序性 以太坊可看成是基于交易的状态机,交易代表了2个状态间的valid arc: 将多笔交易整理打包为区块,区块为package of data: 从状态的角度来看,可将以太坊看成是状

    2023年04月09日
    浏览(39)
  • 揭秘EVM Opcodes

    本文主要源自Macro团队的Gilbert在ETHNewYork 2022分享 Demystifying EVM Opcodes,同时结合evm.codes来理解。 下图摘自Evolution of the EVM Pt. 1: 一个很赞的理解Opcodes的资料见EVM opcodes谜题: Learning Ethereum Virtual Machine Opcodes With EVM Puzzles(https://github.com/fvictorio/evm-puzzles javascript) 学习EVM Opcode

    2024年02月11日
    浏览(41)
  • TI毫米波级联雷达评估板 MMWCAS-DSP-EVM 和MMWCAS-RF-EVM

    本文主要是TI的MMWCAS-DSP-EVM 和MMWCAS-RF-EVM 两块评估板的一些使用心得和毫米波雷达的学习总结。 毫米波(mmWave)是一类使用短波长电磁波的特殊雷达技术。通过捕捉反射的信号,雷达系统可以确定物体的距离、速度和角度。毫米波雷达可发射波长为毫米量级的信号,短波长让所需

    2024年01月16日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包