mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 17:33:47 +00:00
params: change blob count types to int
{Latest,}MaxBlobsPerBlock returns a length, which is traditionally an int in Go. When
comparing with blob gas (uint64) a conversion is required, but I think it's actually
helpful. Kind of makes it clearer that you're working with different units.
When comparing against a slice length, no conversion is needed.
I'm also adding MaxBlobGasPerBlock to simplify the most common expression involving
MaxBlobsPerBlock.
This commit is contained in:
parent
86be309281
commit
28496b0180
11 changed files with 64 additions and 42 deletions
|
|
@ -242,7 +242,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
txBlobGas := uint64(0)
|
txBlobGas := uint64(0)
|
||||||
if tx.Type() == types.BlobTxType {
|
if tx.Type() == types.BlobTxType {
|
||||||
txBlobGas = uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
|
txBlobGas = uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
|
||||||
if used, max := blobGasUsed+txBlobGas, chainConfig.MaxBlobsPerBlock(pre.Env.Timestamp)*params.BlobTxBlobGasPerBlob; used > max {
|
max := chainConfig.MaxBlobGasPerBlock(pre.Env.Timestamp)
|
||||||
|
if used := blobGasUsed + txBlobGas; used > max {
|
||||||
err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max)
|
err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max)
|
||||||
log.Warn("rejected tx", "index", i, "err", err)
|
log.Warn("rejected tx", "index", i, "err", err)
|
||||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,9 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
|
||||||
return errors.New("header is missing blobGasUsed")
|
return errors.New("header is missing blobGasUsed")
|
||||||
}
|
}
|
||||||
// Verify that the blob gas used remains within reasonable limits.
|
// Verify that the blob gas used remains within reasonable limits.
|
||||||
if max := config.MaxBlobsPerBlock(header.Time) * params.BlobTxBlobGasPerBlob; *header.BlobGasUsed > max {
|
maxBlobGas := config.MaxBlobGasPerBlock(header.Time)
|
||||||
return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, max)
|
if *header.BlobGasUsed > maxBlobGas {
|
||||||
|
return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, maxBlobGas)
|
||||||
}
|
}
|
||||||
if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 {
|
if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 {
|
||||||
return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob)
|
return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob)
|
||||||
|
|
@ -60,7 +61,7 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
|
||||||
// blobs on top of the excess blob gas.
|
// blobs on top of the excess blob gas.
|
||||||
func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header) uint64 {
|
func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header) uint64 {
|
||||||
var (
|
var (
|
||||||
target = config.TargetBlobsPerBlock(parent.Time) * params.BlobTxBlobGasPerBlob
|
targetGas = uint64(config.TargetBlobsPerBlock(parent.Time)) * params.BlobTxBlobGasPerBlob
|
||||||
parentExcessBlobGas uint64
|
parentExcessBlobGas uint64
|
||||||
parentBlobGasUsed uint64
|
parentBlobGasUsed uint64
|
||||||
)
|
)
|
||||||
|
|
@ -69,10 +70,10 @@ func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header) uint64
|
||||||
parentBlobGasUsed = *parent.BlobGasUsed
|
parentBlobGasUsed = *parent.BlobGasUsed
|
||||||
}
|
}
|
||||||
excessBlobGas := parentExcessBlobGas + parentBlobGasUsed
|
excessBlobGas := parentExcessBlobGas + parentBlobGasUsed
|
||||||
if excessBlobGas < target {
|
if excessBlobGas < targetGas {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return excessBlobGas - target
|
return excessBlobGas - targetGas
|
||||||
}
|
}
|
||||||
|
|
||||||
// CalcBlobFee calculates the blobfee from the header's excess blob gas field.
|
// CalcBlobFee calculates the blobfee from the header's excess blob gas field.
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@ func TestCalcExcessBlobGas(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
config = params.MainnetChainConfig
|
config = params.MainnetChainConfig
|
||||||
targetBlobs = config.TargetBlobsPerBlock(*config.CancunTime)
|
targetBlobs = config.TargetBlobsPerBlock(*config.CancunTime)
|
||||||
targetBlobGas = targetBlobs * params.BlobTxBlobGasPerBlob
|
targetBlobGas = uint64(targetBlobs) * params.BlobTxBlobGasPerBlob
|
||||||
)
|
)
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
excess uint64
|
excess uint64
|
||||||
blobs uint64
|
blobs int
|
||||||
want uint64
|
want uint64
|
||||||
}{
|
}{
|
||||||
// The excess blob gas should not increase from zero if the used blob
|
// The excess blob gas should not increase from zero if the used blob
|
||||||
|
|
@ -56,7 +56,7 @@ func TestCalcExcessBlobGas(t *testing.T) {
|
||||||
{params.BlobTxBlobGasPerBlob - 1, targetBlobs - 1, 0},
|
{params.BlobTxBlobGasPerBlob - 1, targetBlobs - 1, 0},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
blobGasUsed := tt.blobs * params.BlobTxBlobGasPerBlob
|
blobGasUsed := uint64(tt.blobs) * params.BlobTxBlobGasPerBlob
|
||||||
parent := &types.Header{
|
parent := &types.Header{
|
||||||
Time: *config.CancunTime,
|
Time: *config.CancunTime,
|
||||||
ExcessBlobGas: &tt.excess,
|
ExcessBlobGas: &tt.excess,
|
||||||
|
|
|
||||||
|
|
@ -1073,7 +1073,7 @@ func TestChangingSlotterSize(t *testing.T) {
|
||||||
store.Close()
|
store.Close()
|
||||||
|
|
||||||
// Mimic a blobpool with max blob count of 6 upgrading to a max blob count of 24.
|
// Mimic a blobpool with max blob count of 6 upgrading to a max blob count of 24.
|
||||||
for _, maxBlobs := range []uint64{6, 24} {
|
for _, maxBlobs := range []int{6, 24} {
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
|
statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
|
||||||
statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
|
statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ type limbo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newLimbo opens and indexes a set of limboed blob transactions.
|
// newLimbo opens and indexes a set of limboed blob transactions.
|
||||||
func newLimbo(datadir string, maxBlobsPerTransaction uint64) (*limbo, error) {
|
func newLimbo(datadir string, maxBlobsPerTransaction int) (*limbo, error) {
|
||||||
l := &limbo{
|
l := &limbo{
|
||||||
index: make(map[common.Hash]uint64),
|
index: make(map[common.Hash]uint64),
|
||||||
groups: make(map[uint64]map[uint64]common.Hash),
|
groups: make(map[uint64]map[uint64]common.Hash),
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ package blobpool
|
||||||
// The slotter also creates a shelf for 0-blob transactions. Whilst those are not
|
// The slotter also creates a shelf for 0-blob transactions. Whilst those are not
|
||||||
// allowed in the current protocol, having an empty shelf is not a relevant use
|
// allowed in the current protocol, having an empty shelf is not a relevant use
|
||||||
// of resources, but it makes stress testing with junk transactions simpler.
|
// of resources, but it makes stress testing with junk transactions simpler.
|
||||||
func newSlotter(maxBlobsPerTransaction uint64) func() (uint32, bool) {
|
func newSlotter(maxBlobsPerTransaction int) func() (uint32, bool) {
|
||||||
slotsize := uint32(txAvgSize)
|
slotsize := uint32(txAvgSize)
|
||||||
slotsize -= uint32(blobSize) // underflows, it's ok, will overflow back in the first return
|
slotsize -= uint32(blobSize) // underflows, it's ok, will overflow back in the first return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
if len(hashes) == 0 {
|
if len(hashes) == 0 {
|
||||||
return errors.New("blobless blob transaction")
|
return errors.New("blobless blob transaction")
|
||||||
}
|
}
|
||||||
if max := opts.Config.MaxBlobsPerBlock(head.Time); uint64(len(hashes)) > max {
|
if max := opts.Config.MaxBlobsPerBlock(head.Time); len(hashes) > max {
|
||||||
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), max)
|
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), max)
|
||||||
}
|
}
|
||||||
// Ensure commitments, proofs and hashes are valid
|
// Ensure commitments, proofs and hashes are valid
|
||||||
|
|
|
||||||
|
|
@ -121,9 +121,9 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas
|
||||||
if args.BlobHashes != nil && len(args.BlobHashes) == 0 {
|
if args.BlobHashes != nil && len(args.BlobHashes) == 0 {
|
||||||
return errors.New(`need at least 1 blob for a blob transaction`)
|
return errors.New(`need at least 1 blob for a blob transaction`)
|
||||||
}
|
}
|
||||||
maxBlobsPerTransaction := b.ChainConfig().MaxBlobsPerBlock(b.CurrentHeader().Time)
|
maxBlobs := b.ChainConfig().MaxBlobsPerBlock(b.CurrentHeader().Time)
|
||||||
if args.BlobHashes != nil && uint64(len(args.BlobHashes)) > maxBlobsPerTransaction {
|
if args.BlobHashes != nil && len(args.BlobHashes) > maxBlobs {
|
||||||
return fmt.Errorf(`too many blobs in transaction (have=%d, max=%d)`, len(args.BlobHashes), maxBlobsPerTransaction)
|
return fmt.Errorf(`too many blobs in transaction (have=%d, max=%d)`, len(args.BlobHashes), maxBlobs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create check
|
// create check
|
||||||
|
|
|
||||||
|
|
@ -281,7 +281,8 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio
|
||||||
// isn't really a better place right now. The blob gas limit is checked at block validation time
|
// isn't really a better place right now. The blob gas limit is checked at block validation time
|
||||||
// and not during execution. This means core.ApplyTransaction will not return an error if the
|
// and not during execution. This means core.ApplyTransaction will not return an error if the
|
||||||
// tx has too many blobs. So we have to explicitly check it here.
|
// tx has too many blobs. So we have to explicitly check it here.
|
||||||
if env.blobs+len(sc.Blobs) > int(miner.chainConfig.MaxBlobsPerBlock(env.header.Time)) {
|
maxBlobs := miner.chainConfig.MaxBlobsPerBlock(env.header.Time)
|
||||||
|
if env.blobs+len(sc.Blobs) > maxBlobs {
|
||||||
return errors.New("max data blobs reached")
|
return errors.New("max data blobs reached")
|
||||||
}
|
}
|
||||||
receipt, err := miner.applyTransaction(env, tx)
|
receipt, err := miner.applyTransaction(env, tx)
|
||||||
|
|
@ -330,7 +331,7 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran
|
||||||
}
|
}
|
||||||
// If we don't have enough blob space for any further blob transactions,
|
// If we don't have enough blob space for any further blob transactions,
|
||||||
// skip that list altogether
|
// skip that list altogether
|
||||||
if !blobTxs.Empty() && env.blobs >= int(miner.chainConfig.MaxBlobsPerBlock(env.header.Time)) {
|
if !blobTxs.Empty() && env.blobs >= miner.chainConfig.MaxBlobsPerBlock(env.header.Time) {
|
||||||
log.Trace("Not enough blob space for further blob transactions")
|
log.Trace("Not enough blob space for further blob transactions")
|
||||||
blobTxs.Clear()
|
blobTxs.Clear()
|
||||||
// Fall though to pick up any plain txs
|
// Fall though to pick up any plain txs
|
||||||
|
|
@ -369,7 +370,8 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran
|
||||||
// blobs or not, however the max check panics when called on a chain without
|
// blobs or not, however the max check panics when called on a chain without
|
||||||
// a defined schedule, so we need to verify it's safe to call.
|
// a defined schedule, so we need to verify it's safe to call.
|
||||||
if miner.chainConfig.IsCancun(env.header.Number, env.header.Time) {
|
if miner.chainConfig.IsCancun(env.header.Number, env.header.Time) {
|
||||||
if left := miner.chainConfig.MaxBlobsPerBlock(env.header.Time) - uint64(env.blobs); left < (ltx.BlobGas / params.BlobTxBlobGasPerBlob) {
|
left := miner.chainConfig.MaxBlobsPerBlock(env.header.Time) - env.blobs
|
||||||
|
if left < int(ltx.BlobGas/params.BlobTxBlobGasPerBlob) {
|
||||||
log.Trace("Not enough blob space left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas/params.BlobTxBlobGasPerBlob)
|
log.Trace("Not enough blob space left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas/params.BlobTxBlobGasPerBlob)
|
||||||
txs.Pop()
|
txs.Pop()
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package params
|
package params
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -476,11 +477,10 @@ func (c *ChainConfig) Description() string {
|
||||||
return banner
|
return banner
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlobConfig specifies the target and max blobs per block for the associated
|
// BlobConfig specifies the target and max blobs per block for the associated fork.
|
||||||
// fork.
|
|
||||||
type BlobConfig struct {
|
type BlobConfig struct {
|
||||||
Target uint64 `json:"target"`
|
Target int `json:"target"`
|
||||||
Max uint64 `json:"max"`
|
Max int `json:"max"`
|
||||||
UpdateFraction uint64 `json:"baseFeeUpdateFraction"`
|
UpdateFraction uint64 `json:"baseFeeUpdateFraction"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,7 +493,7 @@ type BlobScheduleConfig struct {
|
||||||
|
|
||||||
// TargetBlobsPerBlock returns the target blobs per block associated with
|
// TargetBlobsPerBlock returns the target blobs per block associated with
|
||||||
// requested time.
|
// requested time.
|
||||||
func (c *ChainConfig) TargetBlobsPerBlock(time uint64) uint64 {
|
func (c *ChainConfig) TargetBlobsPerBlock(time uint64) int {
|
||||||
if c.BlobScheduleConfig == nil {
|
if c.BlobScheduleConfig == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
@ -511,9 +511,8 @@ func (c *ChainConfig) TargetBlobsPerBlock(time uint64) uint64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxBlobsPerBlock returns the max blobs per block associated with
|
// MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp.
|
||||||
// requested time.
|
func (c *ChainConfig) MaxBlobsPerBlock(time uint64) int {
|
||||||
func (c *ChainConfig) MaxBlobsPerBlock(time uint64) uint64 {
|
|
||||||
if c.BlobScheduleConfig == nil {
|
if c.BlobScheduleConfig == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
@ -531,9 +530,14 @@ func (c *ChainConfig) MaxBlobsPerBlock(time uint64) uint64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaxBlobsPerBlock returns the maximum blob gas that can be spent in a block at the given timestamp.
|
||||||
|
func (c *ChainConfig) MaxBlobGasPerBlock(time uint64) uint64 {
|
||||||
|
return uint64(c.MaxBlobsPerBlock(time)) * BlobTxBlobGasPerBlob
|
||||||
|
}
|
||||||
|
|
||||||
// LatestMaxBlobsPerBlock returns the latest max blobs per block defined by the
|
// LatestMaxBlobsPerBlock returns the latest max blobs per block defined by the
|
||||||
// configuration, regardless of the currently active fork.
|
// configuration, regardless of the currently active fork.
|
||||||
func (c *ChainConfig) LatestMaxBlobsPerBlock() uint64 {
|
func (c *ChainConfig) LatestMaxBlobsPerBlock() int {
|
||||||
s := c.BlobScheduleConfig
|
s := c.BlobScheduleConfig
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -758,11 +762,10 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that all forks with blobs explicitly define the blob schedule
|
// Check that all forks with blobs explicitly define the blob schedule configuration.
|
||||||
// configuration.
|
|
||||||
bsc := c.BlobScheduleConfig
|
bsc := c.BlobScheduleConfig
|
||||||
if bsc == nil {
|
if bsc == nil {
|
||||||
bsc = &BlobScheduleConfig{}
|
bsc = new(BlobScheduleConfig)
|
||||||
}
|
}
|
||||||
for _, cur := range []struct {
|
for _, cur := range []struct {
|
||||||
name string
|
name string
|
||||||
|
|
@ -773,17 +776,31 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
|
||||||
{name: "prague", timestamp: c.PragueTime, config: bsc.Prague},
|
{name: "prague", timestamp: c.PragueTime, config: bsc.Prague},
|
||||||
{name: "verkle", timestamp: c.VerkleTime, config: bsc.Verkle},
|
{name: "verkle", timestamp: c.VerkleTime, config: bsc.Verkle},
|
||||||
} {
|
} {
|
||||||
|
if cur.config != nil {
|
||||||
|
if err := cur.config.validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid blob configuration for fork %s: %v", cur.name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
if cur.timestamp != nil {
|
if cur.timestamp != nil {
|
||||||
// If the fork is configured, verify the blob schedule is valid.
|
// If the fork is configured, a blob schedule must be defined for it.
|
||||||
if cur.config == nil {
|
if cur.config == nil {
|
||||||
return fmt.Errorf("unsupported fork configuration: missing blob configuration entry for %v in schedule", cur.name)
|
return fmt.Errorf("unsupported fork configuration: missing blob configuration entry for %v in schedule", cur.name)
|
||||||
}
|
}
|
||||||
if cur.config.UpdateFraction == 0 {
|
|
||||||
return fmt.Errorf("unsupported fork configuration: %v update fraction must be defined and non-zero", cur.name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc *BlobConfig) validate() error {
|
||||||
|
if bc.Max < 0 {
|
||||||
|
return errors.New("max < 0")
|
||||||
|
}
|
||||||
|
if bc.Target < 0 {
|
||||||
|
return errors.New("target < 0")
|
||||||
|
}
|
||||||
|
if bc.UpdateFraction == 0 {
|
||||||
|
return errors.New("update fraction must be defined and non-zero")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -276,13 +276,14 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
|
||||||
return st, common.Hash{}, 0, err
|
return st, common.Hash{}, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Blob transactions may be present after the Cancun fork.
|
// Blob transactions may be present after the Cancun fork.
|
||||||
// In production,
|
// In production,
|
||||||
// - the header is verified against the max in eip4844.go:VerifyEIP4844Header
|
// - the header is verified against the max in eip4844.go:VerifyEIP4844Header
|
||||||
// - the block body is verified against the header in block_validator.go:ValidateBody
|
// - the block body is verified against the header in block_validator.go:ValidateBody
|
||||||
// Here, we just do this shortcut smaller fix, since state tests do not
|
// Here, we just do this shortcut smaller fix, since state tests do not
|
||||||
// utilize those codepaths
|
// utilize those codepaths.
|
||||||
if config.IsCancun(new(big.Int), block.Time()) && uint64(len(msg.BlobHashes)) > config.MaxBlobsPerBlock(block.Time()) {
|
if config.IsCancun(new(big.Int), block.Time()) {
|
||||||
|
if len(msg.BlobHashes) > config.MaxBlobsPerBlock(block.Time()) {
|
||||||
return st, common.Hash{}, 0, errors.New("blob gas exceeds maximum")
|
return st, common.Hash{}, 0, errors.New("blob gas exceeds maximum")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue