【源码阅读】evmⅠ

代码位置如下:
代码
参考link
以太坊中有一个很重要的用途是智能合约,而其中evm模块是实现了执行智能合约的虚拟机。evm可以逐条解析执行智能合约的指令。
evm中的核心对象是EVM,代表一个以太坊虚拟机。其内部主要依赖:解释器Interoreter(循环解释执行给定的合约指令,直接遇到退出指令)、配置和状态数据库StateDB(用来提供数据的永久存储和查询)

1、创建EVM

前提是在core/state_processor.go中使用ApplyTransaction函数来处理交易。在这个函数里面,需要创建一个EVM来执行交易中的数据。
使用NewEVM函数来实现创建。

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

主要包括:

  1. ctx: 提供访问当前区块链数据和挖矿环境的函数和数据
  2. statedb: 以太坊状态数据库对象
  3. chainConfig: 当前节点的区块链配置信息
  4. vmConfig: 虚拟机配置信息
  5. 解释器
    而解释器的创建是通过函数NewEVMInterpreter实现的。该函数首先要根据以太坊的版本不同,为jumpTable字段进行不同的填充。

2、创建合约

使用EVM.Create函数来创建合约。通过当前合约的创建者地址和其账户中的 Nonce 值,计算出来一个地址值,作为合约的地址。然后将这个地址和其它参数传给 EVM.create 方法。同一份合约代码,每次创建合约时得到的合约地址都是不一样的,因为合约是通过发送交易创建,而每发送一次交易 Nonce 值都会改变。

func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}

而EVM.create函数才是主要内容。

func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error)

在函数的第一部分,需要进行判断,包括合约的递归调用次数depth是否在限制之内、以及创建者是否有足够的以太币。每次创建完成就需要将创建者的nonce值加一,这样每一次创建合约的地址才会不一样。

	if evm.depth > int(params.CallCreateDepth) {return nil, common.Address{}, gas, ErrDepth}if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {return nil, common.Address{}, gas, ErrInsufficientBalance}nonce := evm.StateDB.GetNonce(caller.Address())if nonce+1 < nonce {return nil, common.Address{}, gas, ErrNonceUintOverflow}evm.StateDB.SetNonce(caller.Address(), nonce+1)

在完成一系列的判断之后,需要创建合约账户,将交易中规定的金额转到账户中,如果账户已经存在就返回错误。

	if evm.chainRules.IsBerlin {evm.StateDB.AddAddressToAccessList(address)}// Ensure there's no existing contract already at the designated addresscontractHash := evm.StateDB.GetCodeHash(address)if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {return nil, common.Address{}, 0, ErrContractAddressCollision}// Create a new account on the statesnapshot := evm.StateDB.Snapshot()evm.StateDB.CreateAccount(address)if evm.chainRules.IsEIP158 {evm.StateDB.SetNonce(address, 1)}evm.Context.Transfer(evm.StateDB, caller.Address(), address, value)

在第三部分中需要创建一个contract对象,其中包括合约在执行过程中的必要信息,比如合约的创建者、合约的地址等。具体函数在contract.go文件里面。在合约创建之后,需要调用run函数来运行合约。如果合约运行成功,并且代码长度没有超过限制MaxCodeSize,就会将合约代码存储到stateDB中的合约账户中并且消耗一定的gas。因为在编译器阶段,会向源代码中插入其它代码,所以存储的是运行后的返回码。

//判断长度
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {err = ErrMaxCodeSizeExceeded}// Reject code starting with 0xEF if EIP-3541 is enabled.if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {err = ErrInvalidCode}//没有错误就写入数据库if err == nil {createDataGas := uint64(len(ret)) * params.CreateDataGasif contract.UseGas(createDataGas) {evm.StateDB.SetCode(address, ret)} else {err = ErrCodeStoreOutOfGas}}

2.1执行合约创建

主要实现在interpreter中的Run函数。首先迭代次数depth的增加和减少是在解释器中实现的,使用到了defer延迟函数。
之后我们判断readOnly和in.readOnly,只有当readOnly为真(即我们需要设置为只读)并且in.readOnly为假(即当前不处于只读状态)时,才会执行大括号内的代码。

	if readOnly && !in.readOnly {in.readOnly = truedefer func() { in.readOnly = false }()}

以太坊虚拟机的工作流程:
由solidity语言编写的智能合约,通过编译器编译成bytecode,之后发到以太坊上,以太坊底层通过evm模块支持合约的执行和调用,调用时根据合约获取代码,即合约的字节码,生成环境后载入到 EVM 执行。
所以需要opcode操作码和mem存储以及为了应对高并发情况下的栈资源问题,代码中创建了stack来保存一些被创造但未使用的栈空间。
主要函数内容以及解释如下:

	for {//如果处于调试模式,那么会记录执行前的状态信息,包括程序计数器(pc)、剩余的gas和操作的成本。if debug {// Capture pre-execution values for tracing.logged, pcCopy, gasCopy = false, pc, contract.Gas}// Get the operation from the jump table and validate the stack to ensure there are// enough stack items available to perform the operation.//从合约中获取当前程序计数器指向的操作op = contract.GetOp(pc)//从操作表中获取对应的操作operation := in.table[op]//获取操作的固定gas成本cost = operation.constantGas // For tracing// Validate stack 对stack进行验证if sLen := stack.len(); sLen < operation.minStack {return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}} else if sLen > operation.maxStack {return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}}//如果剩余的gas不足以执行这个操作,那么返回错误if !contract.UseGas(cost) {return nil, ErrOutOfGas}//如果操作有动态的gas成本,那么计算新的内存大小并扩展内存以适应操作if operation.dynamicGas != nil {// All ops with a dynamic memory usage also has a dynamic gas cost.var memorySize uint64if operation.memorySize != nil {memSize, overflow := operation.memorySize(stack)if overflow {return nil, ErrGasUintOverflow}if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {return nil, ErrGasUintOverflow}}var dynamicCost uint64//计算动态的gas成本dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)cost += dynamicCost // for tracing//如果计算过程中出现错误或者剩余的gas不足以支付动态的gas成本,那么返回错误if err != nil || !contract.UseGas(dynamicCost) {return nil, ErrOutOfGas}//如果处于调试模式,那么记录执行后的状态信息if debug {in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)logged = true}if memorySize > 0 {mem.Resize(memorySize)}} else if debug {in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)logged = true}// 执行操作res, err = operation.execute(&pc, in, callContext)if err != nil {break}//将程序计数器加一,准备执行下一个操作pc++}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/755157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯历年真题Java b组 省赛 2018年第九届 第几天

一、题目一 第几天 2000年的1月1日&#xff0c;是那一年的第1天。 那么&#xff0c;2000年的5月4日&#xff0c;是那一年的第几天&#xff1f; 注意&#xff1a;需要提交的是一个整数&#xff0c;不要填写任何多余内容。 分析&#xff1a; 将每个月的天数加起来&#xff0c…

2024年敏捷产品负责人CSPO认证培训

课程名称&#xff1a;Scrum Product Owner CSPO产品负责人认证 课程类型&#xff1a;经理级 课程简介&#xff1a; Scrum Product Owner产品负责人在Scrum产品开发当中扮演“舵手”的角色&#xff0c;他决定产品的愿景、路线图以及投资回报&#xff0c;他需要回答为什么做&am…

Python——字典

一、字典特性介绍 字典在 Python 中极为重要&#xff0c;是属于映射类型的数据结构。 字典有⼀对⼉⼤括号组成 {} , 字典内的元素都是成对⼉出现的 {"a":1} , 他们⽤英⽂的冒号( : )隔开, 左边叫做键(key),右边的叫值(value), 通常叫做键值对⼉。 每个元素⽤英⽂的逗…

【Python循环4/5】跳出循环的办法

目录 导入 break 具体用法 在for循环中的运用 在while循环中的运用 continue 具体用法 区别 总结 导入 前几天的博文里&#xff0c;我们学习了for循环和while循环。 无论是for循环还是while循环&#xff0c;默认的终止条件都是边界条件。在触发边界条件之前&am…

生成微信小程序二维码

首页 -> 统计 可以通过上面二个地方配置&#xff0c;生成小程序的二维码&#xff0c;并且在推广分析里&#xff0c;有详细的分析数据&#xff0c;

国务院大动作!2024年深入推广「项目管理国标」,行业将迎新风向!

职场竞争激烈&#xff0c;项目管理专业人才在各个行业中的作用越来越凸显出来。在23年之前&#xff0c;我国关于通用项目管理人才的培养更多依赖于国外的PMP认证&#xff0c;缺少自主的认证评价标准和体系。 为了弥补这一空缺&#xff0c;基于国内的项目管理发展需求&#xff…

基于单片机的智能台灯设计1.42

摘 要 社会在发展&#xff0c;时代在进步&#xff0c;人们对生活质量需求更加膨胀&#xff0c;是否拥有高科技技术也最终决定着产品是否可以满足人们的欲望&#xff0c;只有性价比更高&#xff0c;才可以得到更好的青睐。现在的电子产品愈来愈多&#xff0c;龙蛇混杂&#xff…

2.1(TCP)

TCP—传输控制协议 是一种面向连接的可靠传输协议。可靠、有序、无丢弃和不重复。 特点&#xff1a; TCP是面向连接&#xff08;虚连接&#xff09;的传输层协议每一条TCP连接有且只能有两个端点。可靠、有序、无丢弃和不重复。TCP协议提供全双工通讯。 发送缓存 存放发送方…

达科为生物领航,国产高端试剂“达优®”蓄势待发

在生物科技领域&#xff0c;每一次技术的突破都意味着行业前进的一大步。在即将盛大开幕的双博会上&#xff0c;深圳市达科为生物工程有限公司将携其国产高端试剂品牌“达优”闪亮登场&#xff0c;向世界展示中国生物科技的最新成果。而在这背后&#xff0c;是达科为生物多年来…

Docker【docker使用】

文章目录 前言一、概念二、常用方法1.镜像2.容器 三、镜像与镜像的关系&#xff0c;以及镜像构建和管理 前言 上一篇文章讲了docker的安装&#xff0c;本片文章我们来聊聊docker的一些常用操作。以及镜像、容器之间的关系 一、概念 docker三大核心概念&#xff1a;镜像 Imag…

力扣大厂热门面试算法题 43-45

43. 字符串相乘&#xff0c;44. 通配符匹配&#xff0c;45. 跳跃游戏 II&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.18 可通过leetcode所有测试用例。 目录 43. 字符串相乘 解题思路 完整代码 Python Java 44. 通配符…

基于Spring Boot的中医学习服务管理系统

摘 要 随着世界经济信息化、全球化的到来和互联网的飞速发展&#xff0c;推动了各行业的改革。若想达到安全&#xff0c;快捷的目的&#xff0c;就需要拥有信息化的组织和管理模式&#xff0c;建立一套合理、动态的、交互友好的、高效的中医学习服务管理系统。当前的信息管理存…

汽车制造业供应商管理会面临哪些问题?要如何解决?

汽车行业的供应链是及其复杂的&#xff0c;并且呈全球化分布&#xff0c;企业在知识产权方面的优势很可能是阶段性的。企业需要持续保持领先&#xff0c;将面临巨大的挑战&#xff0c;尽快地将产品推向市场是保持领先的唯一途径。然而&#xff0c;如果没有正确的方式去实现安全…

matlab 最小二乘拟合圆柱

目录 一、算法原理1、算法简介2、参考文献二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 1、算法简介 圆柱拟合步骤主要包括两步: 一是确定柱面模型参数初始值; 二是…

线性电压调节器的使用---MC7915BTG

MC7915BTG是一款固定输出负线性电压调节器。还提供了MECL系统中常用的一种额外电压选项。MC7915BTG线性电压调节器15V的固定输出电压&#xff0c;采用限流、热关闭和安全区域补偿&#xff0c;使其在大多数操作条件下都非常坚固。有了足够的散热&#xff0c;它们可以提供超过1.0…

关系数据库标准语言SQL

1.SQL概述 1.1基本表&#xff08;Base table&#xff09; 实际存储在数据库中的表SQL中一个关系就对应一个基本表基本表可以有若干个索引基本表的集合组成关系模式&#xff0c;即全局概念模式&#xff08;数据的整体逻辑结构&#xff09; 1.2 存储文件 存储文件和相关索引组…

LeetCode 热题 100 | 回溯(三)

目录 1 131. 分割回文串 2 51. N 皇后 菜鸟做题&#xff0c;语言是 C&#xff0c;感冒好了 ver. 1 131. 分割回文串 题眼&#xff1a;给你一个字符串 s&#xff0c;请你将 s 分割 成一些子串。 根据题眼可知&#xff0c;我们需要做的是将字符串 s 连续分割 为几段&#…

二、C#选择排序算法

简介 选择排序算法的基本思想是每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&#xff08;大&#xff09;元素&#xff0c;然后放到已排序序列…

Redis各场景应用集合

应用场景 1、缓存&#xff08;Cache&#xff09;,分布式缓存 有一些存储于数据库中的数据会被频繁访问&#xff0c;如果频繁的访问数据库&#xff0c;数据库负载会升高&#xff0c;同时由于数据库IO比较慢&#xff0c;应用程序的响应会比较差。此时&#xff0c;如果引入Redis来…

面试笔记——Redis(使用场景、面临问题、缓存穿透)

Redis的使用场景 Redis&#xff08;Remote Dictionary Server&#xff09;是一个内存数据结构存储系统&#xff0c;它以快速、高效的特性闻名&#xff0c;并且它支持多种数据结构&#xff0c;包括字符串、哈希表、列表、集合、有序集合等。它主要用于以下场景&#xff1a; 缓…