lotus/chain/vm/burn.go
Steven Allen 1f754bdb78 disable gas burning for window post messages
While over-estimation fees and miner tips are still paid, gas is no longer burnt
for direct, successful window PoSt messages.

Usually, gas is burnt to prevent an attacker from spamming the network and to
allow clients to "price" messages (using the base fee cap) based on how urgently
they need them to be processed. However:

1. Window PoSt is already a "proof of work".
2. Miners need to submit WindowedPoSts on-time so all window post messages are urgent.
3. Work is already under way to move window post verification off-chain (making
it effectively free). This change simply introduces the "free" part a bit earlier.
2020-12-16 00:10:25 -05:00

108 lines
3.0 KiB
Go

package vm
import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
)
const (
gasOveruseNum = 11
gasOveruseDenom = 10
)
type GasOutputs struct {
BaseFeeBurn abi.TokenAmount
OverEstimationBurn abi.TokenAmount
MinerPenalty abi.TokenAmount
MinerTip abi.TokenAmount
Refund abi.TokenAmount
GasRefund int64
GasBurned int64
}
// ZeroGasOutputs returns a logically zeroed GasOutputs.
func ZeroGasOutputs() GasOutputs {
return GasOutputs{
BaseFeeBurn: big.Zero(),
OverEstimationBurn: big.Zero(),
MinerPenalty: big.Zero(),
MinerTip: big.Zero(),
Refund: big.Zero(),
}
}
// ComputeGasOverestimationBurn computes amount of gas to be refunded and amount of gas to be burned
// Result is (refund, burn)
func ComputeGasOverestimationBurn(gasUsed, gasLimit int64) (int64, int64) {
if gasUsed == 0 {
return 0, gasLimit
}
// over = gasLimit/gasUsed - 1 - 0.1
// over = min(over, 1)
// gasToBurn = (gasLimit - gasUsed) * over
// so to factor out division from `over`
// over*gasUsed = min(gasLimit - (11*gasUsed)/10, gasUsed)
// gasToBurn = ((gasLimit - gasUsed)*over*gasUsed) / gasUsed
over := gasLimit - (gasOveruseNum*gasUsed)/gasOveruseDenom
if over < 0 {
return gasLimit - gasUsed, 0
}
// if we want sharper scaling it goes here:
// over *= 2
if over > gasUsed {
over = gasUsed
}
// needs bigint, as it overflows in pathological case gasLimit > 2^32 gasUsed = gasLimit / 2
gasToBurn := big.NewInt(gasLimit - gasUsed)
gasToBurn = big.Mul(gasToBurn, big.NewInt(over))
gasToBurn = big.Div(gasToBurn, big.NewInt(gasUsed))
return gasLimit - gasUsed - gasToBurn.Int64(), gasToBurn.Int64()
}
func ComputeGasOutputs(gasUsed, gasLimit int64, baseFee, feeCap, gasPremium abi.TokenAmount, chargeNetworkFee bool) GasOutputs {
gasUsedBig := big.NewInt(gasUsed)
out := ZeroGasOutputs()
baseFeeToPay := baseFee
if baseFee.Cmp(feeCap.Int) > 0 {
baseFeeToPay = feeCap
out.MinerPenalty = big.Mul(big.Sub(baseFee, feeCap), gasUsedBig)
}
// If chargeNetworkFee is disabled, just skip computing the BaseFeeBurn. However,
// we charge all the other fees regardless.
if chargeNetworkFee {
out.BaseFeeBurn = big.Mul(baseFeeToPay, gasUsedBig)
}
minerTip := gasPremium
if big.Cmp(big.Add(baseFeeToPay, minerTip), feeCap) > 0 {
minerTip = big.Sub(feeCap, baseFeeToPay)
}
out.MinerTip = big.Mul(minerTip, big.NewInt(gasLimit))
out.GasRefund, out.GasBurned = ComputeGasOverestimationBurn(gasUsed, gasLimit)
if out.GasBurned != 0 {
gasBurnedBig := big.NewInt(out.GasBurned)
out.OverEstimationBurn = big.Mul(baseFeeToPay, gasBurnedBig)
minerPenalty := big.Mul(big.Sub(baseFee, baseFeeToPay), gasBurnedBig)
out.MinerPenalty = big.Add(out.MinerPenalty, minerPenalty)
}
requiredFunds := big.Mul(big.NewInt(gasLimit), feeCap)
refund := big.Sub(requiredFunds, out.BaseFeeBurn)
refund = big.Sub(refund, out.MinerTip)
refund = big.Sub(refund, out.OverEstimationBurn)
out.Refund = refund
return out
}