diff --git a/cmd/evm/internal/t8ntool/block.go b/cmd/evm/internal/t8ntool/block.go index 4a070b6c71b54..02a53920518f7 100644 --- a/cmd/evm/internal/t8ntool/block.go +++ b/cmd/evm/internal/t8ntool/block.go @@ -54,6 +54,7 @@ type header struct { MixDigest common.Hash `json:"mixHash"` Nonce *types.BlockNonce `json:"nonce"` BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` + ExcessBlobs uint64 `json:"excessBlobs" rlp:"optional"` } type headerMarshaling struct { @@ -129,6 +130,7 @@ func (i *bbInput) ToBlock() *types.Block { Extra: i.Header.Extra, MixDigest: i.Header.MixDigest, BaseFee: i.Header.BaseFee, + ExcessBlobs: i.Header.ExcessBlobs, } // Fill optional values. diff --git a/cmd/evm/internal/t8ntool/gen_header.go b/cmd/evm/internal/t8ntool/gen_header.go index 196e49dd716f8..a996801bfbe4d 100644 --- a/cmd/evm/internal/t8ntool/gen_header.go +++ b/cmd/evm/internal/t8ntool/gen_header.go @@ -34,6 +34,7 @@ func (h header) MarshalJSON() ([]byte, error) { MixDigest common.Hash `json:"mixHash"` Nonce *types.BlockNonce `json:"nonce"` BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"` + ExcessBlobs uint64 `json:"excessBlobs" rlp:"optional"` } var enc header enc.ParentHash = h.ParentHash @@ -52,6 +53,7 @@ func (h header) MarshalJSON() ([]byte, error) { enc.MixDigest = h.MixDigest enc.Nonce = h.Nonce enc.BaseFee = (*math.HexOrDecimal256)(h.BaseFee) + enc.ExcessBlobs = h.ExcessBlobs return json.Marshal(&enc) } @@ -74,6 +76,7 @@ func (h *header) UnmarshalJSON(input []byte) error { MixDigest *common.Hash `json:"mixHash"` Nonce *types.BlockNonce `json:"nonce"` BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"` + ExcessBlobs *uint64 `json:"excessBlobs" rlp:"optional"` } var dec header if err := json.Unmarshal(input, &dec); err != nil { @@ -131,5 +134,8 @@ func (h *header) UnmarshalJSON(input []byte) error { if dec.BaseFee != nil { h.BaseFee = (*big.Int)(dec.BaseFee) } + if dec.ExcessBlobs != nil { + h.ExcessBlobs = *dec.ExcessBlobs + } return nil } diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index 7c021f10798d8..7e023fc0da035 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -138,9 +138,16 @@ func Transaction(ctx *cli.Context) error { } else { r.Address = sender } - // Check intrinsic gas - if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), len(tx.BlobVersionedHashes()), tx.To() == nil, - chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int))); err != nil { + // Check intrinsic gas assuming no excess blobs + // NOTE: We set excess_blobs prestate to zero. So this may not accurately compute the + // intrinsic gas unless the tool is updated to take in an excess_blobs parameter. + + rules := core.IntrinsicGasChainRules{ + Homestead: chainConfig.IsHomestead(new(big.Int)), + EIP2028: chainConfig.IsIstanbul(new(big.Int)), + EIP4844: chainConfig.IsSharding(new(big.Int)), + } + if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), len(tx.DataHashes()), 0, tx.To() == nil, rules); err != nil { r.Error = err results = append(results, r) continue diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 0397b026f1f05..724a56fa74b1c 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -318,6 +318,9 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // The block reward is no longer handled here. It's done by the // external consensus engine. header.Root = state.IntermediateRoot(true) + if parent := chain.GetHeaderByHash(header.ParentHash); parent != nil { + header.ExcessBlobs = misc.CalcExcessBlobTransactions(parent, uint64(misc.CountBlobs(txs))) + } } // FinalizeAndAssemble implements consensus.Engine, setting the final state and diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index dcdfb20c6387a..0126af6ea3bf9 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -568,6 +568,9 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade // No block rewards in PoA, so the state remains as is and uncles are dropped header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.UncleHash = types.CalcUncleHash(nil) + if parent := chain.GetHeaderByHash(header.ParentHash); parent != nil { + header.ExcessBlobs = misc.CalcExcessBlobTransactions(parent, uint64(misc.CountBlobs(txs))) + } } // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, @@ -743,6 +746,7 @@ func encodeSigHeader(w io.Writer, header *types.Header) { if header.BaseFee != nil { enc = append(enc, header.BaseFee) } + enc = append(enc, header.ExcessBlobs) if err := rlp.Encode(w, enc); err != nil { panic("can't encode: " + err.Error()) } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 1c38b80ea59b7..74030a983df3b 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -601,6 +601,9 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. // Accumulate any block and uncle rewards and commit the final state root accumulateRewards(chain.Config(), state, header, uncles) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + if parent := chain.GetHeaderByHash(header.ParentHash); parent != nil { + header.ExcessBlobs = misc.CalcExcessBlobTransactions(parent, uint64(misc.CountBlobs(txs))) + } } // FinalizeAndAssemble implements consensus.Engine, accumulating the block and @@ -635,6 +638,7 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { if header.BaseFee != nil { enc = append(enc, header.BaseFee) } + enc = append(enc, header.ExcessBlobs) rlp.Encode(hasher, enc) hasher.Sum(hash[:0]) return hash diff --git a/consensus/misc/eip4844.go b/consensus/misc/eip4844.go new file mode 100644 index 0000000000000..6301553a05cd2 --- /dev/null +++ b/consensus/misc/eip4844.go @@ -0,0 +1,49 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package misc + +import ( + "math" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +// CalcExcessBlobTransactions calculates the number of blobs above the target +func CalcExcessBlobTransactions(parent *types.Header, blobs uint64) uint64 { + adjusted := parent.ExcessBlobs + blobs + if adjusted < params.TargetBlobsPerBlock { + return 0 + } + return adjusted - params.TargetBlobsPerBlock +} + +// FakeExponential approximates 2 ** (num / denom) +func FakeExponential(num uint64, denom uint64) uint64 { + cofactor := uint64(math.Exp2(float64(num / denom))) + fractional := num % denom + return cofactor + (fractional*cofactor*2+ + (uint64(math.Pow(float64(fractional), 2))*cofactor)/denom)/(denom*3) +} + +func CountBlobs(txs []*types.Transaction) int { + var count int + for _, tx := range txs { + count += len(tx.DataHashes()) + } + return count +} diff --git a/core/beacon/gen_ed.go b/core/beacon/gen_ed.go index dcee3bf18c79f..584116da01e75 100644 --- a/core/beacon/gen_ed.go +++ b/core/beacon/gen_ed.go @@ -28,6 +28,7 @@ func (e ExecutableDataV1) MarshalJSON() ([]byte, error) { Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"` BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + ExcessBlobs hexutil.Uint64 `json:"excessBlobs" gencodec:"required"` BlockHash common.Hash `json:"blockHash" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } @@ -44,6 +45,7 @@ func (e ExecutableDataV1) MarshalJSON() ([]byte, error) { enc.Timestamp = hexutil.Uint64(e.Timestamp) enc.ExtraData = e.ExtraData enc.BaseFeePerGas = (*hexutil.Big)(e.BaseFeePerGas) + enc.ExcessBlobs = hexutil.Uint64(e.ExcessBlobs) enc.BlockHash = e.BlockHash if e.Transactions != nil { enc.Transactions = make([]hexutil.Bytes, len(e.Transactions)) @@ -69,6 +71,7 @@ func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error { Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"` BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + ExcessBlobs *hexutil.Uint64 `json:"excessBlobs" gencodec:"required"` BlockHash *common.Hash `json:"blockHash" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } @@ -124,6 +127,10 @@ func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'baseFeePerGas' for ExecutableDataV1") } e.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas) + if dec.ExcessBlobs == nil { + return errors.New("missing required field 'excessBlobs' for ExecutableDataV1") + } + e.ExcessBlobs = uint64(*dec.ExcessBlobs) if dec.BlockHash == nil { return errors.New("missing required field 'blockHash' for ExecutableDataV1") } diff --git a/core/beacon/types.go b/core/beacon/types.go index 71de3e23fa651..25ab014a7316b 100644 --- a/core/beacon/types.go +++ b/core/beacon/types.go @@ -65,6 +65,7 @@ type ExecutableDataV1 struct { Timestamp uint64 `json:"timestamp" gencodec:"required"` ExtraData []byte `json:"extraData" gencodec:"required"` BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` + ExcessBlobs uint64 `json:"excessBlobs" gencodec:"required"` BlockHash common.Hash `json:"blockHash" gencodec:"required"` Transactions [][]byte `json:"transactions" gencodec:"required"` } @@ -76,6 +77,7 @@ type executableDataMarshaling struct { GasUsed hexutil.Uint64 Timestamp hexutil.Uint64 BaseFeePerGas *hexutil.Big + ExcessBlobs hexutil.Uint64 ExtraData hexutil.Bytes LogsBloom hexutil.Bytes Transactions []hexutil.Bytes @@ -178,6 +180,7 @@ func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) { GasUsed: params.GasUsed, Time: params.Timestamp, BaseFee: params.BaseFeePerGas, + ExcessBlobs: params.ExcessBlobs, Extra: params.ExtraData, MixDigest: params.Random, } @@ -201,6 +204,7 @@ func BlockToExecutableData(block *types.Block) *ExecutableDataV1 { GasLimit: block.GasLimit(), GasUsed: block.GasUsed(), BaseFeePerGas: block.BaseFee(), + ExcessBlobs: block.ExcessBlobs(), Timestamp: block.Time(), ReceiptsRoot: block.ReceiptHash(), LogsBloom: block.Bloom().Bytes(), diff --git a/core/bench_test.go b/core/bench_test.go index 86a75552f942e..3d7b4bb2e7305 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -83,7 +83,12 @@ func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) - gas, _ := IntrinsicGas(data, nil, 0, false, false, false) + rules := IntrinsicGasChainRules{ + Homestead: false, + EIP2028: false, + EIP4844: false, + } + gas, _ := IntrinsicGas(data, nil, 0, 0, false, rules) signer := types.MakeSigner(gen.config, big.NewInt(int64(i))) gasPrice := big.NewInt(0) if gen.header.BaseFee != nil { diff --git a/core/evm.go b/core/evm.go index 25b4a8d5d7479..8fc139c645c25 100644 --- a/core/evm.go +++ b/core/evm.go @@ -64,6 +64,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common Time: new(big.Int).SetUint64(header.Time), Difficulty: new(big.Int).Set(header.Difficulty), BaseFee: baseFee, + ExcessBlobs: header.ExcessBlobs, GasLimit: header.GasLimit, Random: random, } diff --git a/core/gen_genesis.go b/core/gen_genesis.go index 4e0844e889ab4..6abada50a62a8 100644 --- a/core/gen_genesis.go +++ b/core/gen_genesis.go @@ -18,19 +18,20 @@ var _ = (*genesisSpecMarshaling)(nil) // MarshalJSON marshals as JSON. func (g Genesis) MarshalJSON() ([]byte, error) { type Genesis struct { - Config *params.ChainConfig `json:"config"` - Nonce math.HexOrDecimal64 `json:"nonce"` - Timestamp math.HexOrDecimal64 `json:"timestamp"` - ExtraData hexutil.Bytes `json:"extraData"` - GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` - Mixhash common.Hash `json:"mixHash"` - Coinbase common.Address `json:"coinbase"` - Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` - Number math.HexOrDecimal64 `json:"number"` - GasUsed math.HexOrDecimal64 `json:"gasUsed"` - ParentHash common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` + Config *params.ChainConfig `json:"config"` + Nonce math.HexOrDecimal64 `json:"nonce"` + Timestamp math.HexOrDecimal64 `json:"timestamp"` + ExtraData hexutil.Bytes `json:"extraData"` + GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` + Mixhash common.Hash `json:"mixHash"` + Coinbase common.Address `json:"coinbase"` + Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` + Number math.HexOrDecimal64 `json:"number"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + ParentHash common.Hash `json:"parentHash"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` + ExcessBlobs math.HexOrDecimal64 `json:"excessBlobs"` } var enc Genesis enc.Config = g.Config @@ -51,25 +52,27 @@ func (g Genesis) MarshalJSON() ([]byte, error) { enc.GasUsed = math.HexOrDecimal64(g.GasUsed) enc.ParentHash = g.ParentHash enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee) + enc.ExcessBlobs = math.HexOrDecimal64(g.ExcessBlobs) return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. func (g *Genesis) UnmarshalJSON(input []byte) error { type Genesis struct { - Config *params.ChainConfig `json:"config"` - Nonce *math.HexOrDecimal64 `json:"nonce"` - Timestamp *math.HexOrDecimal64 `json:"timestamp"` - ExtraData *hexutil.Bytes `json:"extraData"` - GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` - Mixhash *common.Hash `json:"mixHash"` - Coinbase *common.Address `json:"coinbase"` - Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` - Number *math.HexOrDecimal64 `json:"number"` - GasUsed *math.HexOrDecimal64 `json:"gasUsed"` - ParentHash *common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` + Config *params.ChainConfig `json:"config"` + Nonce *math.HexOrDecimal64 `json:"nonce"` + Timestamp *math.HexOrDecimal64 `json:"timestamp"` + ExtraData *hexutil.Bytes `json:"extraData"` + GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` + Mixhash *common.Hash `json:"mixHash"` + Coinbase *common.Address `json:"coinbase"` + Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` + Number *math.HexOrDecimal64 `json:"number"` + GasUsed *math.HexOrDecimal64 `json:"gasUsed"` + ParentHash *common.Hash `json:"parentHash"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` + ExcessBlobs *math.HexOrDecimal64 `json:"excessBlobs"` } var dec Genesis if err := json.Unmarshal(input, &dec); err != nil { @@ -120,5 +123,8 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { if dec.BaseFee != nil { g.BaseFee = (*big.Int)(dec.BaseFee) } + if dec.ExcessBlobs != nil { + g.ExcessBlobs = uint64(*dec.ExcessBlobs) + } return nil } diff --git a/core/genesis.go b/core/genesis.go index 03c5decf96642..18c60e88abc38 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -59,10 +59,11 @@ type Genesis struct { // These fields are used for consensus tests. Please don't use them // in actual genesis blocks. - Number uint64 `json:"number"` - GasUsed uint64 `json:"gasUsed"` - ParentHash common.Hash `json:"parentHash"` - BaseFee *big.Int `json:"baseFeePerGas"` + Number uint64 `json:"number"` + GasUsed uint64 `json:"gasUsed"` + ParentHash common.Hash `json:"parentHash"` + BaseFee *big.Int `json:"baseFeePerGas"` + ExcessBlobs uint64 `json:"excessBlobs"` } // GenesisAlloc specifies the initial state that is part of the genesis block. @@ -181,15 +182,16 @@ type GenesisAccount struct { // field type overrides for gencodec type genesisSpecMarshaling struct { - Nonce math.HexOrDecimal64 - Timestamp math.HexOrDecimal64 - ExtraData hexutil.Bytes - GasLimit math.HexOrDecimal64 - GasUsed math.HexOrDecimal64 - Number math.HexOrDecimal64 - Difficulty *math.HexOrDecimal256 - BaseFee *math.HexOrDecimal256 - Alloc map[common.UnprefixedAddress]GenesisAccount + Nonce math.HexOrDecimal64 + Timestamp math.HexOrDecimal64 + ExtraData hexutil.Bytes + GasLimit math.HexOrDecimal64 + GasUsed math.HexOrDecimal64 + Number math.HexOrDecimal64 + Difficulty *math.HexOrDecimal256 + BaseFee *math.HexOrDecimal256 + ExcessBlobs math.HexOrDecimal64 + Alloc map[common.UnprefixedAddress]GenesisAccount } type genesisAccountMarshaling struct { @@ -369,18 +371,19 @@ func (g *Genesis) ToBlock() *types.Block { panic(err) } head := &types.Header{ - Number: new(big.Int).SetUint64(g.Number), - Nonce: types.EncodeNonce(g.Nonce), - Time: g.Timestamp, - ParentHash: g.ParentHash, - Extra: g.ExtraData, - GasLimit: g.GasLimit, - GasUsed: g.GasUsed, - BaseFee: g.BaseFee, - Difficulty: g.Difficulty, - MixDigest: g.Mixhash, - Coinbase: g.Coinbase, - Root: root, + Number: new(big.Int).SetUint64(g.Number), + Nonce: types.EncodeNonce(g.Nonce), + Time: g.Timestamp, + ParentHash: g.ParentHash, + Extra: g.ExtraData, + GasLimit: g.GasLimit, + GasUsed: g.GasUsed, + BaseFee: g.BaseFee, + ExcessBlobs: g.ExcessBlobs, + Difficulty: g.Difficulty, + MixDigest: g.Mixhash, + Coinbase: g.Coinbase, + Root: root, } if g.GasLimit == 0 { head.GasLimit = params.GenesisGasLimit diff --git a/core/state_transition.go b/core/state_transition.go index 6bd4049ede10e..61d006c844a35 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" cmath "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -88,6 +89,13 @@ type ExecutionResult struct { ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) } +// IntrinsicGasChainRules specifies the rules used when computing the intrinsic gas +type IntrinsicGasChainRules struct { + Homestead bool + EIP2028 bool + EIP4844 bool +} + // Unwrap returns the internal evm error which allows us for further // analysis outside. func (result *ExecutionResult) Unwrap() error { @@ -116,10 +124,10 @@ func (result *ExecutionResult) Revert() []byte { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, blobCount int, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, blobCount int, blockExcessBlobs uint64, isContractCreation bool, rules IntrinsicGasChainRules) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 - if isContractCreation && isHomestead { + if isContractCreation && rules.Homestead { gas = params.TxGasContractCreation } else { gas = params.TxGas @@ -135,7 +143,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, blobCount int, isCon } // Make sure we don't exceed uint64 for all data combinations nonZeroGas := params.TxDataNonZeroGasFrontier - if isEIP2028 { + if rules.EIP2028 { nonZeroGas = params.TxDataNonZeroGasEIP2028 } if (math.MaxUint64-gas)/nonZeroGas < nz { @@ -153,10 +161,16 @@ func IntrinsicGas(data []byte, accessList types.AccessList, blobCount int, isCon gas += uint64(len(accessList)) * params.TxAccessListAddressGas gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas } - gas += uint64(blobCount) * params.BlobGas + if rules.EIP4844 { + gas += uint64(blobCount) * getBlobGas(blockExcessBlobs) + } return gas, nil } +func getBlobGas(blockExcessBlobs uint64) uint64 { + return misc.FakeExponential(blockExcessBlobs, params.GasPriceUpdateFractionPerBlob) +} + // NewStateTransition initialises and returns a new state transition object. func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ @@ -304,8 +318,13 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { contractCreation = msg.To() == nil ) + intrinsicGasRules := IntrinsicGasChainRules{ + Homestead: rules.IsHomestead, + EIP2028: rules.IsIstanbul, + EIP4844: rules.IsSharding, + } // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(st.data, st.msg.AccessList(), len(st.msg.DataHashes()), contractCreation, rules.IsHomestead, rules.IsIstanbul) + gas, err := IntrinsicGas(st.data, st.msg.AccessList(), len(st.msg.DataHashes()), st.evm.Context.ExcessBlobs, contractCreation, intrinsicGasRules) if err != nil { return nil, err } diff --git a/core/tx_pool.go b/core/tx_pool.go index 3569a379f925a..9410961c57282 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -253,12 +253,12 @@ type TxPool struct { istanbul bool // Fork indicator whether we are in the istanbul stage. eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions. + eip4844 bool // Fork indicator whether we are using EIP-4844 type transactions. - eipDataBlobs bool // Fork indicator whether we are using data blobs (mini danksharding) - - currentState *state.StateDB // Current state in the blockchain head - pendingNonces *txNoncer // Pending state tracking virtual nonces - currentMaxGas uint64 // Current gas limit for transaction caps + currentState *state.StateDB // Current state in the blockchain head + pendingNonces *txNoncer // Pending state tracking virtual nonces + currentMaxGas uint64 // Current gas limit for transaction caps + currentExcessBlobs uint64 // Current block excess_blobs locals *accountSet // Set of local transaction to exempt from eviction rules journal *txJournal // Journal of local transaction to back up to disk @@ -608,7 +608,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrTxTypeNotSupported } // Reject data blob transactions until data blob EIP activates. - if !pool.eipDataBlobs && tx.Type() == types.BlobTxType { + if !pool.eip4844 && tx.Type() == types.BlobTxType { return ErrTxTypeNotSupported } // Reject transactions over defined size to prevent DOS attacks. @@ -659,7 +659,12 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrInsufficientFunds } // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), len(tx.BlobVersionedHashes()), tx.To() == nil, true, pool.istanbul) + rules := IntrinsicGasChainRules{ + Homestead: true, + EIP2028: pool.istanbul, + EIP4844: pool.eip4844, + } + intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), len(tx.DataHashes()), pool.currentExcessBlobs, tx.To() == nil, rules) if err != nil { return err } @@ -1385,6 +1390,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { pool.currentState = statedb pool.pendingNonces = newTxNoncer(statedb) pool.currentMaxGas = newHead.GasLimit + pool.currentExcessBlobs = newHead.ExcessBlobs // Inject any transactions discarded due to reorgs log.Debug("Reinjecting stale transactions", "count", len(reinject)) @@ -1396,7 +1402,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { pool.istanbul = pool.chainconfig.IsIstanbul(next) pool.eip2718 = pool.chainconfig.IsBerlin(next) pool.eip1559 = pool.chainconfig.IsLondon(next) - pool.eipDataBlobs = pool.chainconfig.IsSharding(next) + pool.eip4844 = pool.chainconfig.IsSharding(next) } // promoteExecutables moves transactions that have become processable from the diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go index 620848fe624a3..f73cd49a99f38 100644 --- a/core/types/access_list_tx.go +++ b/core/types/access_list_tx.go @@ -94,17 +94,18 @@ func (tx *AccessListTx) copy() TxData { } // accessors for innerTx. -func (tx *AccessListTx) txType() byte { return AccessListTxType } -func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID } -func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } -func (tx *AccessListTx) data() []byte { return tx.Data } -func (tx *AccessListTx) gas() uint64 { return tx.Gas } -func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } -func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice } -func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice } -func (tx *AccessListTx) value() *big.Int { return tx.Value } -func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } -func (tx *AccessListTx) to() *common.Address { return tx.To } +func (tx *AccessListTx) txType() byte { return AccessListTxType } +func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID } +func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } +func (tx *AccessListTx) dataHashes() []common.Hash { return nil } +func (tx *AccessListTx) data() []byte { return tx.Data } +func (tx *AccessListTx) gas() uint64 { return tx.Gas } +func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) value() *big.Int { return tx.Value } +func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } +func (tx *AccessListTx) to() *common.Address { return tx.To } func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S diff --git a/core/types/block.go b/core/types/block.go index 088e0419a420f..cf421aac69df5 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -88,6 +88,9 @@ type Header struct { // BaseFee was added by EIP-1559 and is ignored in legacy headers. BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` + // ExcessBlobs was added by EIP-4844 and is ignored in legacy headers. + ExcessBlobs uint64 `json:"excessBlobs" rlp:"optional"` + /* TODO (MariusVanDerWijden) Add this field once needed // Random was added during the merge and contains the BeaconState randomness @@ -97,14 +100,15 @@ type Header struct { // field type overrides for gencodec type headerMarshaling struct { - Difficulty *hexutil.Big - Number *hexutil.Big - GasLimit hexutil.Uint64 - GasUsed hexutil.Uint64 - Time hexutil.Uint64 - Extra hexutil.Bytes - BaseFee *hexutil.Big - Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON + Difficulty *hexutil.Big + Number *hexutil.Big + GasLimit hexutil.Uint64 + GasUsed hexutil.Uint64 + Time hexutil.Uint64 + Extra hexutil.Bytes + BaseFee *hexutil.Big + ExcessBlobs hexutil.Uint64 + Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON } // Hash returns the block hash of the header, which is simply the keccak256 hash of its @@ -390,6 +394,8 @@ func (b *Block) BaseFee() *big.Int { return new(big.Int).Set(b.header.BaseFee) } +func (b *Block) ExcessBlobs() uint64 { return b.header.ExcessBlobs } + func (b *Block) Header() *Header { return CopyHeader(b.header) } // Body returns the non-header content of the block. diff --git a/core/types/data_blob_tx.go b/core/types/data_blob_tx.go index 906ebb8256c23..fb4d4277b958a 100644 --- a/core/types/data_blob_tx.go +++ b/core/types/data_blob_tx.go @@ -416,17 +416,18 @@ func u256ToBig(v *Uint256View) *big.Int { } // accessors for innerTx. -func (stx *SignedBlobTx) txType() byte { return BlobTxType } -func (stx *SignedBlobTx) chainID() *big.Int { return u256ToBig(&stx.Message.ChainID) } -func (stx *SignedBlobTx) accessList() AccessList { return AccessList(stx.Message.AccessList) } -func (stx *SignedBlobTx) data() []byte { return stx.Message.Data } -func (stx *SignedBlobTx) gas() uint64 { return uint64(stx.Message.Gas) } -func (stx *SignedBlobTx) gasFeeCap() *big.Int { return u256ToBig(&stx.Message.GasFeeCap) } -func (stx *SignedBlobTx) gasTipCap() *big.Int { return u256ToBig(&stx.Message.GasTipCap) } -func (stx *SignedBlobTx) gasPrice() *big.Int { return u256ToBig(&stx.Message.GasFeeCap) } -func (stx *SignedBlobTx) value() *big.Int { return u256ToBig(&stx.Message.Value) } -func (stx *SignedBlobTx) nonce() uint64 { return uint64(stx.Message.Nonce) } -func (stx *SignedBlobTx) to() *common.Address { return (*common.Address)(stx.Message.To.Address) } +func (stx *SignedBlobTx) txType() byte { return BlobTxType } +func (stx *SignedBlobTx) chainID() *big.Int { return u256ToBig(&stx.Message.ChainID) } +func (stx *SignedBlobTx) accessList() AccessList { return AccessList(stx.Message.AccessList) } +func (stx *SignedBlobTx) dataHashes() []common.Hash { return stx.Message.BlobVersionedHashes } +func (stx *SignedBlobTx) data() []byte { return stx.Message.Data } +func (stx *SignedBlobTx) gas() uint64 { return uint64(stx.Message.Gas) } +func (stx *SignedBlobTx) gasFeeCap() *big.Int { return u256ToBig(&stx.Message.GasFeeCap) } +func (stx *SignedBlobTx) gasTipCap() *big.Int { return u256ToBig(&stx.Message.GasTipCap) } +func (stx *SignedBlobTx) gasPrice() *big.Int { return u256ToBig(&stx.Message.GasFeeCap) } +func (stx *SignedBlobTx) value() *big.Int { return u256ToBig(&stx.Message.Value) } +func (stx *SignedBlobTx) nonce() uint64 { return uint64(stx.Message.Nonce) } +func (stx *SignedBlobTx) to() *common.Address { return (*common.Address)(stx.Message.To.Address) } func (stx *SignedBlobTx) rawSignatureValues() (v, r, s *big.Int) { return big.NewInt(int64(stx.Signature.V)), u256ToBig(&stx.Signature.R), u256ToBig(&stx.Signature.S) diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go index 53f246ea1fadc..9dfec62420f72 100644 --- a/core/types/dynamic_fee_tx.go +++ b/core/types/dynamic_fee_tx.go @@ -82,17 +82,18 @@ func (tx *DynamicFeeTx) copy() TxData { } // accessors for innerTx. -func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType } -func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID } -func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList } -func (tx *DynamicFeeTx) data() []byte { return tx.Data } -func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } -func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap } -func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap } -func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap } -func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } -func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } -func (tx *DynamicFeeTx) to() *common.Address { return tx.To } +func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType } +func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID } +func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList } +func (tx *DynamicFeeTx) dataHashes() []common.Hash { return nil } +func (tx *DynamicFeeTx) data() []byte { return tx.Data } +func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } +func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap } +func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap } +func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap } +func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } +func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } +func (tx *DynamicFeeTx) to() *common.Address { return tx.To } func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index 74746d033aa01..ebf6693dfbc3b 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -32,6 +32,7 @@ func (h Header) MarshalJSON() ([]byte, error) { MixDigest common.Hash `json:"mixHash"` Nonce BlockNonce `json:"nonce"` BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + ExcessBlobs hexutil.Uint64 `json:"excessBlobs" rlp:"optional"` Hash common.Hash `json:"hash"` } var enc Header @@ -51,6 +52,7 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.MixDigest = h.MixDigest enc.Nonce = h.Nonce enc.BaseFee = (*hexutil.Big)(h.BaseFee) + enc.ExcessBlobs = hexutil.Uint64(h.ExcessBlobs) enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -74,6 +76,7 @@ func (h *Header) UnmarshalJSON(input []byte) error { MixDigest *common.Hash `json:"mixHash"` Nonce *BlockNonce `json:"nonce"` BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + ExcessBlobs *hexutil.Uint64 `json:"excessBlobs" rlp:"optional"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -139,5 +142,8 @@ func (h *Header) UnmarshalJSON(input []byte) error { if dec.BaseFee != nil { h.BaseFee = (*big.Int)(dec.BaseFee) } + if dec.ExcessBlobs != nil { + h.ExcessBlobs = uint64(*dec.ExcessBlobs) + } return nil } diff --git a/core/types/gen_header_rlp.go b/core/types/gen_header_rlp.go index e1a6873318533..2012390644a70 100644 --- a/core/types/gen_header_rlp.go +++ b/core/types/gen_header_rlp.go @@ -41,7 +41,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { w.WriteBytes(obj.MixDigest[:]) w.WriteBytes(obj.Nonce[:]) _tmp1 := obj.BaseFee != nil - if _tmp1 { + _tmp2 := obj.ExcessBlobs != 0 + if _tmp1 || _tmp2 { if obj.BaseFee == nil { w.Write(rlp.EmptyString) } else { @@ -51,6 +52,9 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { w.WriteBigInt(obj.BaseFee) } } + if _tmp2 { + w.WriteUint64(obj.ExcessBlobs) + } w.ListEnd(_tmp0) return w.Flush() } diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index 14d307829cc92..c0f3fb797e8c6 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -91,17 +91,18 @@ func (tx *LegacyTx) copy() TxData { } // accessors for innerTx. -func (tx *LegacyTx) txType() byte { return LegacyTxType } -func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) } -func (tx *LegacyTx) accessList() AccessList { return nil } -func (tx *LegacyTx) data() []byte { return tx.Data } -func (tx *LegacyTx) gas() uint64 { return tx.Gas } -func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } -func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice } -func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice } -func (tx *LegacyTx) value() *big.Int { return tx.Value } -func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } -func (tx *LegacyTx) to() *common.Address { return tx.To } +func (tx *LegacyTx) txType() byte { return LegacyTxType } +func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) } +func (tx *LegacyTx) accessList() AccessList { return nil } +func (tx *LegacyTx) dataHashes() []common.Hash { return nil } +func (tx *LegacyTx) data() []byte { return tx.Data } +func (tx *LegacyTx) gas() uint64 { return tx.Gas } +func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) value() *big.Int { return tx.Value } +func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } +func (tx *LegacyTx) to() *common.Address { return tx.To } func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S diff --git a/core/types/transaction.go b/core/types/transaction.go index 2563f8a5ec9b8..09fcba84b3c54 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -109,6 +109,7 @@ type TxData interface { chainID() *big.Int accessList() AccessList + dataHashes() []common.Hash data() []byte gas() uint64 gasPrice() *big.Int @@ -369,6 +370,9 @@ func (tx *Transaction) Data() []byte { return tx.inner.data() } // AccessList returns the access list of the transaction. func (tx *Transaction) AccessList() AccessList { return tx.inner.accessList() } +// DataHashes returns the blob versioned hashes of the transaction. +func (tx *Transaction) DataHashes() []common.Hash { return tx.inner.dataHashes() } + // Gas returns the gas limit of the transaction. func (tx *Transaction) Gas() uint64 { return tx.inner.gas() } @@ -545,14 +549,6 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e return out, nil } -func (tx *Transaction) BlobVersionedHashes() []common.Hash { - blobTx, ok := tx.inner.(*SignedBlobTx) - if !ok { - return nil - } - return blobTx.Message.BlobVersionedHashes -} - // Transactions implements DerivableList for transactions. type Transactions []*Transaction @@ -779,7 +775,7 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) { amount: tx.Value(), data: tx.Data(), accessList: tx.AccessList(), - dataHashes: tx.BlobVersionedHashes(), + dataHashes: tx.DataHashes(), isFake: false, } // If baseFee provided, set gasPrice to effectiveGasPrice. diff --git a/core/types/types_test.go b/core/types/types_test.go index 3db8221d2a8e9..066a83a780ace 100644 --- a/core/types/types_test.go +++ b/core/types/types_test.go @@ -72,6 +72,19 @@ func benchRLP(b *testing.B, encode bool) { BaseFee: big.NewInt(10000000000), }, }, + { + "protodanksharding-header", + &Header{ + Difficulty: big.NewInt(10000000000), + Number: big.NewInt(1000), + GasLimit: 8_000_000, + GasUsed: 8_000_000, + Time: 555, + Extra: make([]byte, 32), + BaseFee: big.NewInt(10000000000), + Blobs: 2, + }, + }, { "receipt-for-storage", &ReceiptForStorage{ diff --git a/core/vm/evm.go b/core/vm/evm.go index 3141582d23bee..88f1fcf503f19 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -77,6 +77,7 @@ type BlockContext struct { Time *big.Int // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY BaseFee *big.Int // Provides information for BASEFEE + ExcessBlobs uint64 // Provides information for EIP-4844 fee calculation Random *common.Hash // Provides information for RANDOM } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 0d945993eb37a..fd6902aab0c1f 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -704,6 +704,7 @@ func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 { GasUsed: data.GasUsed, Time: data.Timestamp, BaseFee: data.BaseFeePerGas, + Blobs: data.Blobs, Extra: data.ExtraData, MixDigest: data.Random, } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 7d18d3c9b18dd..a6a0004d7aa57 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -884,13 +884,14 @@ func (diff *StateOverride) Apply(state *state.StateDB) error { // BlockOverrides is a set of header fields to override. type BlockOverrides struct { - Number *hexutil.Big - Difficulty *hexutil.Big - Time *hexutil.Big - GasLimit *hexutil.Uint64 - Coinbase *common.Address - Random *common.Hash - BaseFee *hexutil.Big + Number *hexutil.Big + Difficulty *hexutil.Big + Time *hexutil.Big + GasLimit *hexutil.Uint64 + Coinbase *common.Address + Random *common.Hash + BaseFee *hexutil.Big + ExcessBlobs *hexutil.Uint64 } // Apply overrides the given header fields into the given block context. @@ -919,6 +920,9 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) { if diff.BaseFee != nil { blockCtx.BaseFee = diff.BaseFee.ToInt() } + if diff.ExcessBlobs != nil { + blockCtx.ExcessBlobs = uint64(*diff.ExcessBlobs) + } } func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { @@ -1174,6 +1178,7 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { "timestamp": hexutil.Uint64(head.Time), "transactionsRoot": head.TxHash, "receiptsRoot": head.ReceiptHash, + "excessBlobs": head.ExcessBlobs, } if head.BaseFee != nil { @@ -1279,7 +1284,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber Input: hexutil.Bytes(tx.Data()), Nonce: hexutil.Uint64(tx.Nonce()), To: tx.To(), - BlobVersionedHashes: tx.BlobVersionedHashes(), + BlobVersionedHashes: tx.DataHashes(), Value: (*hexutil.Big)(tx.Value()), V: (*hexutil.Big)(v), R: (*hexutil.Big)(r), diff --git a/les/catalyst/api_test.go b/les/catalyst/api_test.go index 26c49d6ef908b..78b98551c9544 100644 --- a/les/catalyst/api_test.go +++ b/les/catalyst/api_test.go @@ -135,6 +135,7 @@ func TestExecutePayloadV1(t *testing.T) { Timestamp: fakeBlock.Time(), ExtraData: fakeBlock.Extra(), BaseFeePerGas: fakeBlock.BaseFee(), + Blobs: fakeBlock.ExcessBlobs(), BlockHash: fakeBlock.Hash(), Transactions: encodeTransactions(fakeBlock.Transactions()), }) diff --git a/light/txpool.go b/light/txpool.go index c8765f7ebea32..4028d41f713e8 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -69,6 +69,7 @@ type TxPool struct { istanbul bool // Fork indicator whether we are in the istanbul stage. eip2718 bool // Fork indicator whether we are in the eip2718 stage. + eip4844 bool // Fork indicator whether we are in the eip4844 stage. } // TxRelayBackend provides an interface to the mechanism that forwards transacions @@ -315,6 +316,7 @@ func (pool *TxPool) setNewHead(head *types.Header) { next := new(big.Int).Add(head.Number, big.NewInt(1)) pool.istanbul = pool.config.IsIstanbul(next) pool.eip2718 = pool.config.IsBerlin(next) + pool.eip4844 = pool.config.IsSharding(next) } // Stop stops the light transaction pool @@ -382,7 +384,12 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error } // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), 0, tx.To() == nil, true, pool.istanbul) + rules := core.IntrinsicGasChainRules{ + Homestead: true, + EIP2028: pool.istanbul, + EIP4844: pool.eip4844, + } + gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), len(tx.DataHashes()), header.ExcessBlobs, tx.To() == nil, rules) if err != nil { return err } diff --git a/miner/worker.go b/miner/worker.go index 93fb6288bb45c..bb5838385da29 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1010,6 +1010,10 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) } } + // Initialize the prestate excess_blobs field used during state transition + if w.chainConfig.IsSharding(parent.Number()) { + header.ExcessBlobs = parent.Header().ExcessBlobs + } // Run the consensus preparation with the default or customized consensus engine. if err := w.engine.Prepare(w.chain, header); err != nil { log.Error("Failed to prepare header for sealing", "err", err) diff --git a/params/protocol_params.go b/params/protocol_params.go index f406fda5ea3e4..befb1ab5dfbf1 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -161,9 +161,11 @@ const ( // Fixed cost for sending a data blob. BlobGas uint64 = 120000 - MaxBlobsPerTx = 2 - MaxBlobsPerBlock = 16 - FieldElementsPerBlob = 4096 // each field element is 32 bytes + MaxBlobsPerTx = 2 + MaxBlobsPerBlock = 16 + TargetBlobsPerBlock = 8 + FieldElementsPerBlob = 4096 // each field element is 32 bytes + GasPriceUpdateFractionPerBlob = 64 BlobVerificationGas uint64 = 1800000 BlobCommitmentVersionKZG uint8 = 0x01 diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 8975702608649..d6d5261e5f082 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -37,6 +37,7 @@ type TransactionTest struct { EIP158 ttFork Frontier ttFork Homestead ttFork + Sharding ttFork } type ttFork struct { @@ -45,7 +46,7 @@ type ttFork struct { } func (tt *TransactionTest) Run(config *params.ChainConfig) error { - validateTx := func(rlpData hexutil.Bytes, signer types.Signer, isHomestead bool, isIstanbul bool) (*common.Address, *common.Hash, error) { + validateTx := func(rlpData hexutil.Bytes, signer types.Signer, isHomestead bool, isIstanbul bool, isSharding bool) (*common.Address, *common.Hash, error) { tx := new(types.Transaction) if err := rlp.DecodeBytes(rlpData, tx); err != nil { return nil, nil, err @@ -55,7 +56,12 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error { return nil, nil, err } // Intrinsic gas - requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), len(tx.BlobVersionedHashes()), tx.To() == nil, isHomestead, isIstanbul) + rules := core.IntrinsicGasChainRules{ + Homestead: isHomestead, + EIP2028: isIstanbul, + EIP4844: isSharding, + } + requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), len(tx.DataHashes()), 0, tx.To() == nil, rules) if err != nil { return nil, nil, err } @@ -72,16 +78,18 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error { fork ttFork isHomestead bool isIstanbul bool + isSharding bool }{ - {"Frontier", types.FrontierSigner{}, tt.Frontier, false, false}, - {"Homestead", types.HomesteadSigner{}, tt.Homestead, true, false}, - {"EIP150", types.HomesteadSigner{}, tt.EIP150, true, false}, - {"EIP158", types.NewEIP155Signer(config.ChainID), tt.EIP158, true, false}, - {"Byzantium", types.NewEIP155Signer(config.ChainID), tt.Byzantium, true, false}, - {"Constantinople", types.NewEIP155Signer(config.ChainID), tt.Constantinople, true, false}, - {"Istanbul", types.NewEIP155Signer(config.ChainID), tt.Istanbul, true, true}, + {"Frontier", types.FrontierSigner{}, tt.Frontier, false, false, false}, + {"Homestead", types.HomesteadSigner{}, tt.Homestead, true, false, false}, + {"EIP150", types.HomesteadSigner{}, tt.EIP150, true, false, false}, + {"EIP158", types.NewEIP155Signer(config.ChainID), tt.EIP158, true, false, false}, + {"Byzantium", types.NewEIP155Signer(config.ChainID), tt.Byzantium, true, false, false}, + {"Constantinople", types.NewEIP155Signer(config.ChainID), tt.Constantinople, true, false, false}, + {"Istanbul", types.NewEIP155Signer(config.ChainID), tt.Istanbul, true, true, false}, + {"Sharding", types.NewEIP155Signer(config.ChainID), tt.Sharding, true, true, false}, } { - sender, txhash, err := validateTx(tt.RLP, testcase.signer, testcase.isHomestead, testcase.isIstanbul) + sender, txhash, err := validateTx(tt.RLP, testcase.signer, testcase.isHomestead, testcase.isIstanbul, testcase.isSharding) if testcase.fork.Sender == (common.UnprefixedAddress{}) { if err == nil {