github.com/ethereum/go-ethereum@v1.16.1/consensus/misc/eip4844/eip4844.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package eip4844
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"math/big"
    23  
    24  	"github.com/ethereum/go-ethereum/core/types"
    25  	"github.com/ethereum/go-ethereum/params"
    26  	"github.com/ethereum/go-ethereum/params/forks"
    27  )
    28  
    29  var (
    30  	minBlobGasPrice = big.NewInt(params.BlobTxMinBlobGasprice)
    31  )
    32  
    33  // VerifyEIP4844Header verifies the presence of the excessBlobGas field and that
    34  // if the current block contains no transactions, the excessBlobGas is updated
    35  // accordingly.
    36  func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Header) error {
    37  	if header.Number.Uint64() != parent.Number.Uint64()+1 {
    38  		panic("bad header pair")
    39  	}
    40  	// Verify the header is not malformed
    41  	if header.ExcessBlobGas == nil {
    42  		return errors.New("header is missing excessBlobGas")
    43  	}
    44  	if header.BlobGasUsed == nil {
    45  		return errors.New("header is missing blobGasUsed")
    46  	}
    47  	// Verify that the blob gas used remains within reasonable limits.
    48  	maxBlobGas := MaxBlobGasPerBlock(config, header.Time)
    49  	if *header.BlobGasUsed > maxBlobGas {
    50  		return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, maxBlobGas)
    51  	}
    52  	if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 {
    53  		return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob)
    54  	}
    55  	// Verify the excessBlobGas is correct based on the parent header
    56  	expectedExcessBlobGas := CalcExcessBlobGas(config, parent, header.Time)
    57  	if *header.ExcessBlobGas != expectedExcessBlobGas {
    58  		return fmt.Errorf("invalid excessBlobGas: have %d, want %d", *header.ExcessBlobGas, expectedExcessBlobGas)
    59  	}
    60  	return nil
    61  }
    62  
    63  // CalcExcessBlobGas calculates the excess blob gas after applying the set of
    64  // blobs on top of the excess blob gas.
    65  func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTimestamp uint64) uint64 {
    66  	var (
    67  		parentExcessBlobGas uint64
    68  		parentBlobGasUsed   uint64
    69  	)
    70  	if parent.ExcessBlobGas != nil {
    71  		parentExcessBlobGas = *parent.ExcessBlobGas
    72  		parentBlobGasUsed = *parent.BlobGasUsed
    73  	}
    74  	excessBlobGas := parentExcessBlobGas + parentBlobGasUsed
    75  	targetGas := uint64(targetBlobsPerBlock(config, headTimestamp)) * params.BlobTxBlobGasPerBlob
    76  	if excessBlobGas < targetGas {
    77  		return 0
    78  	}
    79  	return excessBlobGas - targetGas
    80  }
    81  
    82  // CalcBlobFee calculates the blobfee from the header's excess blob gas field.
    83  func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
    84  	var frac uint64
    85  	switch config.LatestFork(header.Time) {
    86  	case forks.Osaka:
    87  		frac = config.BlobScheduleConfig.Osaka.UpdateFraction
    88  	case forks.Prague:
    89  		frac = config.BlobScheduleConfig.Prague.UpdateFraction
    90  	case forks.Cancun:
    91  		frac = config.BlobScheduleConfig.Cancun.UpdateFraction
    92  	default:
    93  		panic("calculating blob fee on unsupported fork")
    94  	}
    95  	return fakeExponential(minBlobGasPrice, new(big.Int).SetUint64(*header.ExcessBlobGas), new(big.Int).SetUint64(frac))
    96  }
    97  
    98  // MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp.
    99  func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
   100  	if cfg.BlobScheduleConfig == nil {
   101  		return 0
   102  	}
   103  	var (
   104  		london = cfg.LondonBlock
   105  		s      = cfg.BlobScheduleConfig
   106  	)
   107  	switch {
   108  	case cfg.IsOsaka(london, time) && s.Osaka != nil:
   109  		return s.Osaka.Max
   110  	case cfg.IsPrague(london, time) && s.Prague != nil:
   111  		return s.Prague.Max
   112  	case cfg.IsCancun(london, time) && s.Cancun != nil:
   113  		return s.Cancun.Max
   114  	default:
   115  		return 0
   116  	}
   117  }
   118  
   119  // MaxBlobsPerBlock returns the maximum blob gas that can be spent in a block at the given timestamp.
   120  func MaxBlobGasPerBlock(cfg *params.ChainConfig, time uint64) uint64 {
   121  	return uint64(MaxBlobsPerBlock(cfg, time)) * params.BlobTxBlobGasPerBlob
   122  }
   123  
   124  // LatestMaxBlobsPerBlock returns the latest max blobs per block defined by the
   125  // configuration, regardless of the currently active fork.
   126  func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
   127  	s := cfg.BlobScheduleConfig
   128  	if s == nil {
   129  		return 0
   130  	}
   131  	switch {
   132  	case s.Osaka != nil:
   133  		return s.Osaka.Max
   134  	case s.Prague != nil:
   135  		return s.Prague.Max
   136  	case s.Cancun != nil:
   137  		return s.Cancun.Max
   138  	default:
   139  		return 0
   140  	}
   141  }
   142  
   143  // targetBlobsPerBlock returns the target number of blobs in a block at the given timestamp.
   144  func targetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
   145  	if cfg.BlobScheduleConfig == nil {
   146  		return 0
   147  	}
   148  	var (
   149  		london = cfg.LondonBlock
   150  		s      = cfg.BlobScheduleConfig
   151  	)
   152  	switch {
   153  	case cfg.IsOsaka(london, time) && s.Osaka != nil:
   154  		return s.Osaka.Target
   155  	case cfg.IsPrague(london, time) && s.Prague != nil:
   156  		return s.Prague.Target
   157  	case cfg.IsCancun(london, time) && s.Cancun != nil:
   158  		return s.Cancun.Target
   159  	default:
   160  		return 0
   161  	}
   162  }
   163  
   164  // fakeExponential approximates factor * e ** (numerator / denominator) using
   165  // Taylor expansion.
   166  func fakeExponential(factor, numerator, denominator *big.Int) *big.Int {
   167  	var (
   168  		output = new(big.Int)
   169  		accum  = new(big.Int).Mul(factor, denominator)
   170  	)
   171  	for i := 1; accum.Sign() > 0; i++ {
   172  		output.Add(output, accum)
   173  
   174  		accum.Mul(accum, numerator)
   175  		accum.Div(accum, denominator)
   176  		accum.Div(accum, big.NewInt(int64(i)))
   177  	}
   178  	return output.Div(output, denominator)
   179  }