1f754bdb78
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.
108 lines
3.0 KiB
Go
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
|
|
}
|