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 }