Filecoin – Gas计算

Filecoin开始Space Race了。矿工的实力都开始展现。官方的代码也有不少问题,几天时间,版本从0.5.1升级到0.5.6。好久不看go语言相关的逻辑,最近看了看FilecoinGas计算相关的逻辑,分享一下吧。本文分析的是0.5.6的逻辑,Lotus代码的最后一个提交信息如下:

commit 606a58bc6bc035ec0b90c6b50488e29e90f4238f
Author: Aayush Rajasekaran <arajasek94@gmail.com>
Date:   Sat Aug 29 00:56:24 2020 -0400

    Lotus version 0.5.6

Lotus代码的CHANGELOG清晰记录了Gas费用模型的变化:从原来的limit/price,改成了limit/premium/feecap。新的Gas模型参考了EIP-1559:发送交易,交易费用不超过”feecap*limit”。矿工赚取的交易费是”premium*limit”。简单的说,feecap*limit是Gas费用的上限,矿工能赚取的费用是premium*limit。(feecap-premium)*limit的Gas费用会被燃烧。

feecap是如何设置的?limit是不是设置的越大越好?

Gas费用计算的相关逻辑实现在node/impl/full/gas.go中的GasEstimateMessageGas函数。接下来详细介绍Base/Limit/Premium/FeeCap。

1. Base Fee

每个区块都会设置一个baseFee。在该区块中的交易都需要燃烧相应的baseFee。注意baseFee,虽然名字看上去是fee,其实是price,具体燃烧的费用是baseFee*limit。baseFee相关的设置定义在build/params_shared_vals.go中:

 const BlockGasLimit = 10_000_000_000
 const BlockGasTarget = BlockGasLimit / 2
 const BaseFeeMaxChangeDenom = 8 // 12.5%
 const InitialBaseFee = 100e6
 const MinimumBaseFee = 100
 const PackingEfficiencyNum = 4
 const PackingEfficiencyDenom = 5

在初始区块,baseFee设置为InitialBaseFee(10^8)。从当前的区块,生成下一个区块时,需要根据当前的区块的limit的总量确定,具体的逻辑请查看chain/store/basefee.go的ComputeBaseFee和computeNextBaseFee函数。

  • 最小的baseFee – MinimumBaseFee (100)

  • 区块Gas Limit – 区块中所有交易的Gas Limit的总和,在计算baseFee的时候,打了九折(PackingEfficiencyNum/PackingEfficiencyDenom)

  • 区块Gas Limit的“超出”部分 – 每个区块存在Gas Limit的目标大小 – BlockGasTarget。超过BlockGasTarget的部分,视为超出部分。注意超出部分,可正可负。

  • 更新的baseFee – 下一个区块的baseFee,在当前区块的baseFee的基础上增加超出部分的12.5%(BaseFeeMaxChangeDenom)。相关计算逻辑如下:

     change := big.Mul(baseFee, big.NewInt(delta))
     change = big.Div(change, big.NewInt(build.BlockGasTarget))
     change = big.Div(change, big.NewInt(build.BaseFeeMaxChangeDenom))

简单的说,当前区块中的Gas Limit消耗超出了BlockGasTarget,则base Fee增加超出部分的12.5%。在这样的逻辑下,你会发现在交易多的情况下,base Fee会增加迅速增加和降低。

最新24小时的base Fee可以在飞狐浏览器查看(https://filfox.info/zh):

Filecoin - Gas计算

2. GasLimit

Gas Limit是指一个交易,愿意为交易执行支付的“油量”。一个交易消耗的Gas Limit几乎是固定的,计算过程请查看GasEstimateGasLimit函数。简单的说,当一个交易需要获取Gas Limit时,在目前区块高度上,“执行”该交易:

res, err := a.Stmgr.CallWithGas(ctx, &msg, priorMsgs, ts)

CallWithGas,只是为了获取执行过程消耗的Gas,并不真正改变当前的状态。

3. GasPremium

Gas Premium是指一个交易,愿意为交易执行支付的“油价”。GAS费用是油量乘以油价的结果。油量和交易本身有关,也几乎是固定的。显然的是,油价高,GAS费用高,矿工的收入就高,更愿意将交易优先打包。从交易发送者的角度,油价越低越好。Lotus代码给出一个计算Gas Premium的方法,请查看GasEstimateGasPremium函数,分为几步:

  • 查看之前区块(4个 = 2*2)中所有交易,并按照Gas Premium从高到低排序

  • 计算所有交易的“平均”Gas Premium。平均的意思是,找出一半油量消耗的Gas Premium:

         at := build.BlockGasTarget * int64(blocks) / 2
         prev1, prev2 := big.Zero(), big.Zero()
         for _, price := range prices {
             prev1, prev2 = price.price, prev1
             at -= price.limit
             if at > 0 {
                 continue
             }
         }
  • 加上千分之五的随机性

         // mean 1, stddev 0.005 => 95% within +-1%
         noise := 1 + rand.NormFloat64()*0.005
         premium = types.BigMul(premium, types.NewInt(uint64(noise*(1<<precision))+1))
         premium = types.BigDiv(premium, types.NewInt(1<<precision))

4. GasFeeCap

除了Gas Premium,交易还需要支付Base Fee。也就是说,一般情况下,交易需要支付的费用是: (Gas Premium + Base Fee) * Gas Limit。问题是,Base Fee是变化的,有可能太大,交易发送者,不愿意支付。Gas Fee Cap(上限),就是设置支付费用的上限。相关的计算逻辑请看GasEstimateFeeCap函数。

  • 交易非常可能不是下一个区块立即被打包,那需要考虑在接下来的多个区块(10个)才被打包的情况下,Base Fee的变化:

         parentBaseFee := ts.Blocks()[0].ParentBaseFee
         increaseFactor := math.Pow(1.+1./float64(build.BaseFeeMaxChangeDenom), float64(maxqueueblks))
     
         feeInFuture := types.BigMul(parentBaseFee, types.NewInt(uint64(increaseFactor*(1<<8))))
         feeInFuture = types.BigDiv(feeInFuture, types.NewInt(1<<8))

    每个区块Base Fee按照12.5%的涨幅计算。

  • 当前账户余额的百分之一,作为支付费用的上限:

    maxAccepted := types.BigDiv(act.Balance, types.NewInt(MaxSpendOnFeeDenom))
  • 上述两种情况下的最少值,作为Gas Fee Cap

5. GasLimit设置惩罚

众所周知,以太坊中的Gas Limit可以设置的非常大。一般情况下,多余的Gas费用会全数返还。特别注意的是,Filecoin并不完全是这样。因为Gas Limit参与了Base Fee和Gas Premium的计算,真实的Gas Limit是非常重要的。如果一个交易,设置了不合理的Gas Limit,Filecoin采取了一种惩罚机制。惩罚的Gas费用也被燃烧,计算逻辑看ComputeGasOverestimationBurn函数。

  • 允许一定的误差情况下,计算Gas limit的超出部分

     const (
         gasOveruseNum   = 11
         gasOveruseDenom = 10
     )
     
    over := gasLimit - (gasOveruseNum*gasUsed)/gasOveruseDenom

    Gas消耗的1.1倍以内认为是合理设置。

    if over > gasUsed {
         over = gasUsed
    }

    超出部分的上限是Gas的使用量。

  • 按照超出(over)的比例,确定惩罚的油量:

    gasToBurn := big.NewInt(gasLimit - gasUsed)
    gasToBurn = big.Mul(gasToBurn, big.NewInt(over))
    gasToBurn = big.Div(gasToBurn, big.NewInt(gasUsed))

    简单的说,惩罚的是over/gasUsed的比例。如果over超过了gasUsed,就是所有罚光。

总结:

Filecoin的Gas模型,引入了BaseFee,用来调节交易的拥堵情况。BaseFee,在区块拥堵或者区块交易不够的情况下,都会按照12.5%进行相应的调节。每笔交易的费用计算公式:(Gas Premium + Base Fee) * Gas Limit。其中BaseFee的部分会被燃烧掉,Gas Premium作为矿工的手续费。特别注意的是,GasLimit不要随意设置,多余的Gas Limit会被燃烧。

该内容来自于互联网公开内容,非区块链原创内容,如若转载,请注明出处:https://htzkw.com/archives/25474

联系我们

aliyinhang@gmail.com