学习以太坊Gas机制

2024-02-04 01:59
文章标签 学习 机制 以太 gas

本文主要是介绍学习以太坊Gas机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Gas

基本概念

为了避免网络滥用及回避由于图灵完备而带来的一些不可避免的问题(the halting problem),在以太坊中所有的程序执行都收费。Gas是基本的工作量成本单位,用于计量在以太坊区块链上执行操作所需的计算、存储资源和带宽,其目的是限制执行交易所需的工作量。各种操作的费用以gas为单位计算。任意的程序片段(包括合约创建、消息调用、分配资源以及访问账户storage、在虚拟机上执行操作等)都有一个普遍认同的gas成本。[1] Gas有两个作用[5]:

  • 以太坊(不稳定的)价格和矿工工作报酬之间的缓冲
  • 对拒绝服务(DoS)攻击的防御.

每一个交易都要指定一个 gas 上限:gasLimit。发送者通过在交易中指定gas price来购买gas,系统预先从发送者的账户余额中扣除gasLimit * gasPrice的交易费,即采用预付费机制。Gas price是指当你将交易发送到以太坊网络时,愿意支付的每单位gas的价格。[5]如果账户余额不足,交易会被视为无效交易。[1]之所以将其命名为 gasLimit,是因为剩余的 gas会在交易完成后被返还(与购买时同样价格)到发送者账户。每个矿工自己选择他们想要接受和拒绝的gas价格。交易者们则需要在降低 gas 价格和使交易能尽快被矿工打包间进行权衡。
通常来说,以太币(Ether)是用来购买 gas 的,未返还的部分就会移交到 beneficiary 的地址(即一般由矿工所控制的一个账户地址)。以太币最小的单位是 Wei(伟),所有货币值都以 Wei 的整数倍来记录。[1]
ether-wei
注意:Gas只存在于EVM中,用来给计算的工作量计数。发送方用ether支付交易费,然后将其转换为gas用于EVM核算,最后将剩余的gas转换为ether返还给发送方,未返还的同样转换为ether作为交易费付给矿工[5]。

block gas limit

block gas limit是一个块中所有交易可以消耗的最大gas量,并且限制了一个块中可以容纳多少个交易。如果矿工试图包含一个需要比block gas limit更多gas的交易,则该块将被网络拒绝。[5]
以太坊采用投票系统来设定block gas limit。网络上的矿工共同决定block gas limit。以太坊协议有一个内置的机制,矿工可以对block gas limit进行投票,从而增加或减少后续区块的容量。矿工有权将当前区块的gas限定值设定在最后区块的gas限定值的0.0975% (1/1024)内[3]。所以最终的gas限定值应该是矿工们设置的中间值。

Gas成本的确定

EVM可执行的各种操作的相对gas cost经过精心设计,以最好地保护以太坊区块链不受攻击。操作进行的计算越多,gas成本越高[5]。
2016年,一名攻击者发现并利用了gas成本与实际资源成本不匹配的问题,证明了将gas成本与实际资源成本相匹配的重要性。 这个问题通过一个硬分叉(代号为“橘子口哨”,EIP 150)解决,它通过改变IO重型操作长期的gas费率来抵抗垃圾交易攻击,并增加了63/64规则。

Gas收费情况

三种情况下会收取执行费用(以gas来结算)[1]:

  1. 最普遍的情况就是计算操作费用。
  2. 执行一个低级别的消息调用或者合约创建可能需要扣除 gas,这也就是执行 CREATE,CALL和CALLCODE 的费用的一部分。
  3. 内存使用的增加也会消耗一定的 gas。
    gas cost

gas消耗计算还有以下特点[3]:

  • 对于任何交易,都先收取21000 gas的基本费用(base fee)。这些费用可用于支付运行椭圆曲线算法(该算法旨在从签名中恢复发送者的地址)以及存储交易所花费的硬盘空间和带宽所需的费用。

  • 交易可以包括无限量的“数据”。虚拟机中的某些操作码,可以让合约允许交易对这些数据的访问。数据的固定费用(intrinsic gas)计算:每个零字节4 gas,非零字节68 gas。

  • 合约提供的消息数据是没有成本的。因为在消息调用期间不需要实际复制任何数据,调用数据可以简单地视为指向父合约内存的指针,该指针在子进程执行时不会改变。

  • 某些操作码的计算时间极度依赖参数,gas成本是动态变化的。例如,EXP的的开销是指数级别的(ie. x^0 = 1 gas, x^1 … x^255 = 2 gas, x^256 … x^65535 = 3 gas, etc)。

  • 如果操作码CALL(以及CALLCODE)的值不是零,会额外消耗9000 gas。这是因为任何值传输都会引起归档节点的历史存储显著增大。请注意,实际消耗是6700,在此基础上,以太坊强制增加了一个自动给予接收方的gas值,这个值最小是2300。这样做是为了让接受交易的钱包至少有足够的gas来记录交易。

对于一个账户的执行,内存的总费用和其内存索引(无论是读还是写)的范围成正比;这个内存范围是32字节的倍数,不足32字节以32字节计。这是实时(just-in-time)结算的;也就是说,任何对超出先前已索引的内存区域的访问,都会实时地结算为额外的内存使用费。
存储费用则有一个细微差别——激励存储的最小化使用。清除一个存储中的记录项或账户不仅不收费,而且还会返还一定gas作为奖励。[1] EVM中有两种操作会出现这种情况,具体可以参考SSTORE操作码的gas计算函数(core/vm/gas_table.go/gasSStore)[5]:

  1. 删除一份合约(自毁)将会得到奖励。
  2. 将一个存储地址的非零值改为零(SSTORE[x] = 0)可以获得退款。

为了避免退款机制被利用,每笔交易的最高退款额被设定为gas总用量的50%(向下取整)[5]。这种退款机制会激励人们清理存储器。正因为缺乏这样的激励,许多合约并未有效使用存储空间,从而导致存储快速膨胀。这样既获得了存储收费的大部分好处,又不会失去合约一旦确立就可以永久存在的保证。延迟退款机制是必要的,因为可以防止拒绝服务攻击。攻击者发送一笔含有少量gas的交易,循环清理大量的存储,直到用光gas,这样消耗了大量的验证算力,但实际并没有真正清理存储也没有花费大量gas。50%的上限是为了确保:给定一个具有一定数量gas的交易,矿工依然可以根据gasLimit确定用于执行此交易的计算时间上限。[3]

gas在执行过程中的使用

当EVM需要完成一个交易时,它首先被给予一个等于交易中gas limit所指定数量的gas supply。执行的每个操作码都有一个gas成本,因此EVM的gas supply会随着程序向前一步步执行而逐渐减少。在每个操作之前,EVM检查是否有足够的gas来支付操作的执行费用。以太坊在操作执行前收取费用。如果没有足够的gas,EVM就会停止执行并将本次交易修改的状态回滚。[5]
gas and fee

  • 如果EVM成功完成了执行,并且没有耗尽gas,则使用的gas将作为交易费支付给矿工,并根据交易中指定的gas价格转换为ether,即交易费= gas used * gas price。gas supply中剩余的gas将退还给发送方,同样是根据交易中指定的gas价格转换为ether。

  • 如果交易在执行期间“耗尽gas”,操作将立即终止,抛出“out of gas(OOG)”异常。交易被恢复,对状态的所有更改都回滚。

虽然交易没成功执行,但发送方仍需支付交易费,因为到那时为止,矿工已经执行了计算工作,必须为此进行补偿。[5] 收取的交易费为发送方提供的全部gas,即gas limit * gas price。当一个合约发送消息给另一个合约,可以对这个消息引起的子执行设置一个gas限制。如果子执行耗尽了gas,则子执行被恢复,但gas仍然消耗。[3]

gas相关源代码(geth)

gas成本定义和指令gas成本的计算代码集中在core/vm/gas.gocore/vm/gas_table.go两个文件。
core/vm/gas.go

// Gas costs
const (GasQuickStep   uint64 = 2GasFastestStep uint64 = 3GasFastStep    uint64 = 5GasMidStep     uint64 = 8GasSlowStep    uint64 = 10GasExtStep     uint64 = 20
)// calcGas returns the actual gas cost of the call.
// calcGas返回实际用于调用的gas成本。。
//
// The cost of gas was changed during the homestead price change HF.
// As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
// 在homestead价格变动中,gas成本发生了变化。 HF ??
// 作为 EIP 150 (TangerineWhistle 橘子口哨硬分叉)的一部分,返回的gas = (gas - base) * 63 / 64.
//
// EIP150是通过重新调整gas价格彻底解决DoS问题的硬分叉主要备选方案。
// “针对IO重型操作长期的gas费率改变以抵抗垃圾交易攻击” https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
// 添加规则:一个调用的子调用不能消耗超过父调用剩余gas的63/64。也就是说,如果调用者最初投入了数量为 a 的 gas, 在 10 层递归调用后,最内层的函数最多只有 (63/64)^10*a 的 gas.
// 有两个目的:(Rationale 片段)
// 1. 用一个更软的基于gas的限制("softer" gas-based restriction)取代最大调用栈深度的“硬限制”,这将使得深度调用需要的gas数量呈指数增长。
//    这将堆栈深度限制攻击这一个类别的攻击彻底从合约开发者需要担心的问题清单中剔除,从而提高了合约编程的安全性。
// 2. 把事实上的最大堆栈调用深度从1024减少到约300,在一定程度上缓解未来客户端受二次Dos攻击的可能。
func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint64, error) {if isEip150 {availableGas = availableGas - basegas := availableGas - availableGas/64 // gas = (availableGas - base) * 63 / 64// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150// is smaller than the requested amount. Therefor we return the new gas instead// of returning an error.// 如果位长超过64位,我们知道新计算的EIP150的“gas”要比请求的量小。// 因此,我们返回新的gas,而不是返回一个错误。// 若callCost超过64位,条件直接成立,不用比较即返回gas;// 若callCost不超过64位,新计算的gas肯定小于CallCost,仍返回gas。if !callCost.IsUint64() || gas < callCost.Uint64() {return gas, nil}}if !callCost.IsUint64() {return 0, errGasUintOverflow}return callCost

这篇关于学习以太坊Gas机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/676071

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

《offer来了》第二章学习笔记

1.集合 Java四种集合:List、Queue、Set和Map 1.1.List:可重复 有序的Collection ArrayList: 基于数组实现,增删慢,查询快,线程不安全 Vector: 基于数组实现,增删慢,查询快,线程安全 LinkedList: 基于双向链实现,增删快,查询慢,线程不安全 1.2.Queue:队列 ArrayBlockingQueue:

硬件基础知识——自学习梳理

计算机存储分为闪存和永久性存储。 硬盘(永久存储)主要分为机械磁盘和固态硬盘。 机械磁盘主要靠磁颗粒的正负极方向来存储0或1,且机械磁盘没有使用寿命。 固态硬盘就有使用寿命了,大概支持30w次的读写操作。 闪存使用的是电容进行存储,断电数据就没了。 器件之间传输bit数据在总线上是一个一个传输的,因为通过电压传输(电流不稳定),但是电压属于电势能,所以可以叠加互相干扰,这也就是硬盘,U盘

人工智能机器学习算法总结神经网络算法(前向及反向传播)

1.定义,意义和优缺点 定义: 神经网络算法是一种模仿人类大脑神经元之间连接方式的机器学习算法。通过多层神经元的组合和激活函数的非线性转换,神经网络能够学习数据的特征和模式,实现对复杂数据的建模和预测。(我们可以借助人类的神经元模型来更好的帮助我们理解该算法的本质,不过这里需要说明的是,虽然名字是神经网络,并且结构等等也是借鉴了神经网络,但其原型以及算法本质上还和生物层面的神经网络运行原理存在

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa