JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

EVM 与 Gas

以太坊上每一个节点都步调一致地运行着同一台世界计算机。Gas 就是那只计费表,让这台计算机的每一步都得花钱——于是代码必须付费,也永远不可能无休止地跑下去。

一台计算机,由所有人一起运行

想象一个巨大的厅堂,里面摆满了成千上万台一模一样的计算器,每一台前面都坐着一个身处不同大洲的人。有人念出一份步骤清单——把这个加上、把那个存下、把这两个比一比——然后每一个人都在同一时刻、敲入完全相同的步骤。因为计算器一模一样、步骤也一模一样,他们最终都会落到同一个答案上。没有哪一台计算器说了算;答案不过就是他们全体一致认可的那个。这个厅堂,就是 以太坊虚拟机,通常简称为 EVM。

一份 智能合约,就是这样一份步骤清单——只写下一次,然后永久存在链上。当你发出一笔调用某个合约的 交易,你就是在把它的步骤清单念给整个厅堂听。以太坊 上的每一个 全节点 都会让 EVM 重新走一遍这些步骤,并核对自己是否得出了相同的结果。这就是为什么人们把 EVM 叫做一台单一的全球计算机:它是一台机器,但它的运算被镜像复制到每一个节点上,于是没有任何单独一个节点能在结果上撒谎。

为什么计算需要一只计费表

但这里有个麻烦。成千上万个节点马上就要免费替你跑这份步骤清单,用的还是他们自己的机器。那么,是什么拦着一份粗心——甚或恶意——的合约,不让它叫所有节点数到一万亿,或是绕着圈子永远跑下去、永不停手呢?只要哪怕一份步骤清单能不受限制地跑,它就能把整台全球计算机冻住。解法妙得很简单:每一步都收费。给计算贴上的这个价签,就叫 gas

把 gas 想成出租车里的一只油表。EVM 执行的每一个操作,都有规则手册定好的一笔固定 gas 成本——像把两个数相加这种便宜活儿,花得少;像把一个值写进永久存储这种昂贵活儿,花得多。当你的交易在跑,计费表一路往上跳,消耗着一个你出钱灌满的油箱。跑一份又短又简单的步骤清单,你只花一点点油;跑一份又长、又频繁写存储的,你就花掉一大笔。这只表把工作量变得可见、也可计费,就像出租车按里程而不是按一口价收费。

gas 上限、gas 价格,与最后的账单

当你发出一笔交易,你要设定两个各自独立的数字,而把它们在脑子里分清楚是很值得的。gas 上限是油箱的大小——在 EVM 把这笔交易硬生生叫停之前,你允许它最多烧掉多少 gas。它保护你不被一份有缺陷的合约把钱包抽干。gas 价格则是这一份份 gas,你每一单位愿意出多少钱,用以太币的极小零头来报价。前者说的是*我准备开多远*;后者说的是*每升油我愿意付多少*。

你真正要付的账单,大体上就是这两个数相乘:实际用掉的 gas × gas 价格。关键在于,你是按真正烧掉的 gas 来付费的,而不是按整个油箱——如果你的步骤清单在十万的上限里只需要四万 gas,那没用掉的六万会原封退还给你。上限是为了安全的天花板;真实的成本,跟着实际干完的活儿走。还有一处值得知道的细节:如今的 gas 价格分成两部分——一部分是网络为每一个 区块 自动定下、随后销毁掉的基础费,另一部分是你额外加在上面、用来把自己的交易往前推一推的一小笔小费。基础费不进任何人的口袋,而是被烧掉,从流通中永久抹除。

那么,为什么同样一个动作,在繁忙时段会更贵?因为每一个区块能容纳的 gas 总量是有上限的。当 内存池 里挤满了同时都想挤进去的人,区块被塞满,网络就会一个区块接一个区块地自动把基础费往上抬,直到需求降温——正是这一部分,让一个清闲日里的动作突然变贵。小费则是你插队往前的杠杆:给得更厚,区块构建者就更早把你排进去;链上清闲时给得少,你照样进得去。你那个动作所需的 gas 数量几乎不变——上下浮动的,是每单位的价格,由有多少人在抢同一块稀缺的区块空间所决定。

一段微型轨迹,一步一步看

我们来亲眼看一份微型合约在计费表上跑一遍。下面是一段 Solidity 代码——编写 EVM 合约最常用的语言。它做的不过是把两个数相加,再把结果存进永久存储里:

contract Tiny {
    uint256 total;

    function bump(uint256 a, uint256 b) public {
        uint256 sum = a + b;   // do the math in memory
        total = sum;           // save it to storage
    }
}
一份极简的 Solidity 合约:把两个数相加,再把结果存到链上。

EVM 从来看不到这段工整的文字。它跑的是这段代码编译之后落成的一串微型操作,并对每一个计费。看着 gas 油箱一路被抽干——留意那一笔单独的写存储是怎样把其余一切都比得微不足道的,因为让成千上万个节点永远记住一个值,是你能向它们提出的最昂贵的请求:

step  operation        gas    running total
----  ---------------  -----  -------------
 1    PUSH a               3              3
 2    PUSH b               3              6
 3    ADD  (a + b)         3              9
 4    PUSH slot            3             12
 5    SSTORE (save sum) 20000          20012
                                  ^^^^^
                          one storage write
                          dominates the bill
每一个 EVM 操作都有一笔固定的 gas 成本。算术几乎免费;写入永久存储则是远远最贵的一步。

把它当成一笔流水账来读。那三步算术几乎拨不动指针;而单单那一笔写存储,就贵了上千倍。这是你最该带走的一条直觉:在 EVM 上,做运算是便宜的,记住东西是昂贵的。优秀的合约作者,会把大量心思花在尽可能少地碰存储上,原因正在于,gas 就是花在那里的。

本篇要点

把它收拢到一处,整幅图就清清爽爽了。EVM 是一台全球计算机,每个节点都步调一致地重跑一遍它,于是它的结果是整个网络可以去验证、而不是只能去信任的东西。Gas 是给那场计算的每一步定价的计费表:它让工作必须付费,让任何合约都没法永远循环下去,并且——随着拥堵把每个区块的基础费推高又压低——决定你此刻要付多少。gas 上限给你的风险封顶;gas 价格(一笔被烧掉的基础费,加上你的小费)定下费率;账单就是这个费率,乘上你实际用掉的工作量。

现在你能切身体会到,为什么一条链可能只花几分钱,而另一条要花上几美元:归根结底,就看区块空间有多少、又有多少人在为它出价。引擎和它的计费表都弄明白之后,接下来的几篇会往上爬一层——去看那些让合约得以铸造与转移价值的 代币标准,以及驱动整台机器运转的钱包与应用。