From 70bc7f7a25dc7a944132dbfceb879b182ac87b8c Mon Sep 17 00:00:00 2001 From: racytech <82003208+racytech@users.noreply.github.com> Date: Tue, 28 Mar 2023 03:39:15 +0600 Subject: [PATCH] eip-4844: small additions and modified gaspool (#7190) This PR contains very small EIP-4844 additions. GasPool is modified and now it is a struct with 2 fields "gas" and "dataGas" (blobs are priced in dataGas). ExcessDataGas block header field added. ExcessDataGas needed to compute the data gas price. EIP-4844 helper functions are added as well. --- cmd/rpcdaemon/commands/engine_api.go | 17 +++++- consensus/misc/eip4844.go | 80 +++++++++++++++++++++++++ consensus/misc/eip4844_test.go | 89 ++++++++++++++++++++++++++++ core/error.go | 4 ++ core/gaspool.go | 42 ++++++++++--- core/genesis.go | 55 +++++++++-------- core/types/block.go | 85 +++++++++++++++++++++++--- core/types/legacy_tx.go | 5 ++ core/types/transaction.go | 2 + core/vm/eips.go | 23 +++++++ core/vm/evmtypes/evmtypes.go | 24 ++++---- core/vm/opcodes.go | 3 + ethdb/privateapi/ethbackend.go | 15 +++++ params/protocol_params.go | 13 ++++ turbo/adapter/ethapi/api.go | 3 + 15 files changed, 406 insertions(+), 54 deletions(-) create mode 100644 consensus/misc/eip4844.go create mode 100644 consensus/misc/eip4844_test.go diff --git a/cmd/rpcdaemon/commands/engine_api.go b/cmd/rpcdaemon/commands/engine_api.go index e581bcb54..82862367c 100644 --- a/cmd/rpcdaemon/commands/engine_api.go +++ b/cmd/rpcdaemon/commands/engine_api.go @@ -40,6 +40,7 @@ type ExecutionPayload struct { BlockHash common.Hash `json:"blockHash" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` Withdrawals []*types.Withdrawal `json:"withdrawals"` + ExcessDataGas *hexutil.Big `json:"excessDataGas"` } // GetPayloadV2Response represents the response of the getPayloadV2 method @@ -248,6 +249,17 @@ func (e *EngineImpl) newPayload(version uint32, ctx context.Context, payload *Ex ep.Version = 2 ep.Withdrawals = privateapi.ConvertWithdrawalsToRpc(payload.Withdrawals) } + if version >= 3 && payload.ExcessDataGas != nil { + ep.Version = 3 + var excessDataGas *uint256.Int + var overflow bool + excessDataGas, overflow = uint256.FromBig((*big.Int)(payload.ExcessDataGas)) + if overflow { + log.Warn("NewPayload ExcessDataGas overflow") + return nil, fmt.Errorf("invalid request, excess data gas overflow") + } + ep.ExcessDataGas = gointerfaces.ConvertUint256IntToH256(excessDataGas) + } res, err := e.api.EngineNewPayload(ctx, ep) if err != nil { @@ -286,7 +298,10 @@ func convertPayloadFromRpc(payload *types2.ExecutionPayload) *ExecutionPayload { if payload.Version >= 2 { res.Withdrawals = privateapi.ConvertWithdrawalsFromRpc(payload.Withdrawals) } - + if payload.Version >= 3 { + edg := gointerfaces.ConvertH256ToUint256Int(payload.ExcessDataGas).ToBig() + res.ExcessDataGas = (*hexutil.Big)(edg) + } return res } diff --git a/consensus/misc/eip4844.go b/consensus/misc/eip4844.go new file mode 100644 index 000000000..ff356e57c --- /dev/null +++ b/consensus/misc/eip4844.go @@ -0,0 +1,80 @@ +// 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 ( + "fmt" + "math/big" + + "github.com/ledgerwatch/erigon-lib/chain" + + "github.com/ledgerwatch/erigon/core/types" + "github.com/ledgerwatch/erigon/params" +) + +// CalcExcessDataGas implements calc_excess_data_gas from EIP-4844 +func CalcExcessDataGas(parentExcessDataGas *big.Int, newBlobs int) *big.Int { + excessDataGas := new(big.Int) + if parentExcessDataGas != nil { + excessDataGas.Set(parentExcessDataGas) + } + consumedGas := big.NewInt(params.DataGasPerBlob) + consumedGas.Mul(consumedGas, big.NewInt(int64(newBlobs))) + + excessDataGas.Add(excessDataGas, consumedGas) + targetGas := big.NewInt(params.TargetDataGasPerBlock) + if excessDataGas.Cmp(targetGas) < 0 { + return new(big.Int) + } + return new(big.Int).Set(excessDataGas.Sub(excessDataGas, targetGas)) +} + +// FakeExponential approximates factor * e ** (num / denom) using a taylor expansion +// as described in the EIP-4844 spec. +func FakeExponential(factor, num, denom *big.Int) *big.Int { + output := new(big.Int) + numAccum := new(big.Int).Mul(factor, denom) + for i := 1; numAccum.Sign() > 0; i++ { + output.Add(output, numAccum) + numAccum.Mul(numAccum, num) + iBig := big.NewInt(int64(i)) + numAccum.Div(numAccum, iBig.Mul(iBig, denom)) + } + return output.Div(output, denom) +} + +// CountBlobs returns the number of blob transactions in txs +func CountBlobs(txs []types.Transaction) int { + var count int + for _, tx := range txs { + count += len(tx.GetDataHashes()) + } + return count +} + +// VerifyEip4844Header verifies that the header is not malformed +func VerifyEip4844Header(config *chain.Config, parent, header *types.Header) error { + if header.ExcessDataGas == nil { + return fmt.Errorf("header is missing excessDataGas") + } + return nil +} + +// GetDataGasPrice implements get_data_gas_price from EIP-4844 +func GetDataGasPrice(excessDataGas *big.Int) *big.Int { + return FakeExponential(big.NewInt(params.MinDataGasPrice), excessDataGas, big.NewInt(params.DataGasPriceUpdateFraction)) +} diff --git a/consensus/misc/eip4844_test.go b/consensus/misc/eip4844_test.go new file mode 100644 index 000000000..a703ea0ed --- /dev/null +++ b/consensus/misc/eip4844_test.go @@ -0,0 +1,89 @@ +// Copyright 2022 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/big" + "testing" + + "github.com/ledgerwatch/erigon/params" +) + +func TestFakeExponential(t *testing.T) { + var tests = []struct { + factor, num, denom int64 + want int64 + }{ + // When num==0 the return value should always equal the value of factor + {1, 0, 1, 1}, + {38493, 0, 1000, 38493}, + {0, 1234, 2345, 0}, // should be 0 + {1, 2, 1, 6}, // approximate 7.389 + {1, 4, 2, 6}, + {1, 3, 1, 16}, // approximate 20.09 + {1, 6, 2, 18}, + {1, 4, 1, 49}, // approximate 54.60 + {1, 8, 2, 50}, + {10, 8, 2, 542}, // approximate 540.598 + {11, 8, 2, 596}, // approximate 600.58 + {1, 5, 1, 136}, // approximate 148.4 + {1, 5, 2, 11}, // approximate 12.18 + {2, 5, 2, 23}, // approximate 24.36 + } + + for _, tt := range tests { + factor := big.NewInt(tt.factor) + num := big.NewInt(tt.num) + denom := big.NewInt(tt.denom) + result := FakeExponential(factor, num, denom) + //t.Logf("%v*e^(%v/%v): %v", factor, num, denom, result) + if tt.want != result.Int64() { + t.Errorf("got %v want %v", result, tt.want) + } + } +} + +func TestCalcExcessDataGas(t *testing.T) { + var tests = []struct { + parentExcessDataGas int64 + newBlobs int + want int64 + }{ + {0, 0, 0}, + {0, 1, 0}, + {0, params.TargetDataGasPerBlock / params.DataGasPerBlob, 0}, + {0, (params.TargetDataGasPerBlock / params.DataGasPerBlob) + 1, params.DataGasPerBlob}, + {100000, (params.TargetDataGasPerBlock / params.DataGasPerBlob) + 1, params.DataGasPerBlob + 100000}, + {params.TargetDataGasPerBlock, 1, params.DataGasPerBlob}, + {params.TargetDataGasPerBlock, 0, 0}, + {params.TargetDataGasPerBlock, (params.TargetDataGasPerBlock / params.DataGasPerBlob), params.TargetDataGasPerBlock}, + } + + for _, tt := range tests { + parentExcessDataGas := big.NewInt(tt.parentExcessDataGas) + result := CalcExcessDataGas(parentExcessDataGas, tt.newBlobs) + if tt.want != result.Int64() { + t.Errorf("got %v want %v", result, tt.want) + } + } + + // Test nil value for parentExcessDataGas + result := CalcExcessDataGas(nil, (params.TargetDataGasPerBlock/params.DataGasPerBlob)+1) + if result.Int64() != params.DataGasPerBlob { + t.Errorf("got %v want %v", result, params.DataGasPerBlob) + } +} diff --git a/core/error.go b/core/error.go index 392a87e20..82782407d 100644 --- a/core/error.go +++ b/core/error.go @@ -69,6 +69,10 @@ var ( // by a transaction is higher than what's left in the block. ErrGasLimitReached = errors.New("gas limit reached") + // ErrDataGasLimitReached is returned by the gas pool if the amount of data gas required + // by a transaction is higher than what's left in the block. + ErrDataGasLimitReached = errors.New("data gas limit reached") + // ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger // than init code size limit. ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded") diff --git a/core/gaspool.go b/core/gaspool.go index 05b403cb1..74d78876a 100644 --- a/core/gaspool.go +++ b/core/gaspool.go @@ -23,36 +23,62 @@ import ( // GasPool tracks the amount of gas available during execution of the transactions // in a block. The zero value is a pool with zero gas available. -type GasPool uint64 +type GasPool struct { + gas, dataGas uint64 +} func (gp *GasPool) Reset(amount uint64) { - *gp = GasPool(amount) + gp.gas = amount } // AddGas makes gas available for execution. func (gp *GasPool) AddGas(amount uint64) *GasPool { - if uint64(*gp) > math.MaxUint64-amount { + if gp.gas > math.MaxUint64-amount { panic("gas pool pushed above uint64") } - *(*uint64)(gp) += amount + gp.gas += amount return gp } // SubGas deducts the given amount from the pool if enough gas is // available and returns an error otherwise. func (gp *GasPool) SubGas(amount uint64) error { - if uint64(*gp) < amount { + if gp.gas < amount { return ErrGasLimitReached } - *(*uint64)(gp) -= amount + gp.gas -= amount return nil } // Gas returns the amount of gas remaining in the pool. func (gp *GasPool) Gas() uint64 { - return uint64(*gp) + return gp.gas +} + +// AddDataGas makes data gas available for execution. +func (gp *GasPool) AddDataGas(amount uint64) *GasPool { + if gp.dataGas > math.MaxUint64-amount { + panic("data gas pool pushed above uint64") + } + gp.dataGas += amount + return gp +} + +// SubDataGas deducts the given amount from the pool if enough data gas is available and returns an +// error otherwise. +func (gp *GasPool) SubDataGas(amount uint64) error { + if gp.dataGas < amount { + return ErrDataGasLimitReached + } + gp.dataGas -= amount + return nil +} + +// DataGas returns the amount of data gas remaining in the pool. +func (gp *GasPool) DataGas() uint64 { + return gp.dataGas } func (gp *GasPool) String() string { - return fmt.Sprintf("%d", *gp) + return fmt.Sprintf("gas: %d, data_gas: %d", gp.gas, gp.dataGas) } diff --git a/core/genesis.go b/core/genesis.go index 5a8668c14..255f49d5f 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -76,10 +76,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 libcommon.Hash `json:"parentHash"` - BaseFee *big.Int `json:"baseFeePerGas"` + Number uint64 `json:"number"` + GasUsed uint64 `json:"gasUsed"` + ParentHash libcommon.Hash `json:"parentHash"` + BaseFee *big.Int `json:"baseFeePerGas"` + ExcessDataGas *big.Int `json:"excessDataGas"` } // GenesisAlloc specifies the initial state that is part of the genesis block. @@ -120,15 +121,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 + ExcessDataGas *math.HexOrDecimal256 + Alloc map[common.UnprefixedAddress]GenesisAccount } type genesisAccountMarshaling struct { @@ -335,19 +337,20 @@ func (g *Genesis) ToBlock(tmpDir string) (*types.Block, *state.IntraBlockState, _ = g.Alloc //nil-check 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, - Difficulty: g.Difficulty, - MixDigest: g.Mixhash, - Coinbase: g.Coinbase, - BaseFee: g.BaseFee, - AuRaStep: g.AuRaStep, - AuRaSeal: g.AuRaSeal, + 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, + Difficulty: g.Difficulty, + MixDigest: g.Mixhash, + Coinbase: g.Coinbase, + BaseFee: g.BaseFee, + ExcessDataGas: g.ExcessDataGas, + AuRaStep: g.AuRaStep, + AuRaSeal: g.AuRaSeal, } if g.GasLimit == 0 { head.GasLimit = params.GenesisGasLimit diff --git a/core/types/block.go b/core/types/block.go index de11129d2..7f8684d81 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -96,6 +96,7 @@ type Header struct { BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559 WithdrawalsHash *libcommon.Hash `json:"withdrawalsRoot"` // EIP-4895 + ExcessDataGas *big.Int `json:"excessDataGas"` // EIP-4844 // The verkle proof is ignored in legacy headers Verkle bool @@ -103,6 +104,17 @@ type Header struct { VerkleKeyVals []verkle.KeyValuePair } +// ParentExcessDataGas is a helper that returns the excess data gas value of the parent block. It +// returns nil if the parent header could not be fetched, or if the parent block's excess data gas +// is nil. +func (h *Header) ParentExcessDataGas(getHeader func(hash libcommon.Hash, number uint64) *Header) *big.Int { + p := getHeader(h.ParentHash, h.Number.Uint64()) + if p != nil { + return p.ExcessDataGas + } + return nil +} + func bitsToBytes(bitLen int) (byteLen int) { return (bitLen + 7) / 8 } @@ -158,6 +170,11 @@ func (h *Header) EncodingSize() int { encodingSize += 33 } + if h.ExcessDataGas != nil { + encodingSize++ + encodingSize += rlp.BigIntLenExcludingHead(h.ExcessDataGas) + } + if h.Verkle { // Encoding of Verkle Proof encodingSize++ @@ -300,6 +317,12 @@ func (h *Header) EncodeRLP(w io.Writer) error { } } + if h.ExcessDataGas != nil { + if err := rlp.EncodeBigInt(h.ExcessDataGas, w, b[:]); err != nil { + return err + } + } + if h.Verkle { if err := rlp.EncodeString(h.VerkleProof, w, b[:]); err != nil { return err @@ -445,6 +468,19 @@ func (h *Header) DecodeRLP(s *rlp.Stream) error { h.WithdrawalsHash = new(libcommon.Hash) h.WithdrawalsHash.SetBytes(b) + // ExcessDataGas + if b, err = s.Uint256Bytes(); err != nil { + if errors.Is(err, rlp.EOL) { + h.ExcessDataGas = nil + if err := s.ListEnd(); err != nil { + return fmt.Errorf("close header struct (no ExcessDataGas): %w", err) + } + return nil + } + return fmt.Errorf("read ExcessDataGas: %w", err) + } + h.ExcessDataGas = new(big.Int).SetBytes(b) + if h.Verkle { if h.VerkleProof, err = s.Bytes(); err != nil { return fmt.Errorf("read VerkleProof: %w", err) @@ -464,14 +500,27 @@ func (h *Header) DecodeRLP(s *rlp.Stream) error { // 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 libcommon.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 + ExcessDataGas *hexutil.Big + Hash libcommon.Hash `json:"hash"` // adds call to Hash() in MarshalJSON +} + +// SetExcessDataGas sets the excess_data_gas field in the header +func (h *Header) SetExcessDataGas(v *big.Int) { + h.ExcessDataGas = new(big.Int) + if v != nil { + h.ExcessDataGas.Set(v) + } + if h.WithdrawalsHash == nil { + // leaving this nil would result in a buggy encoding + h.WithdrawalsHash = &EmptyRootHash + } } // Hash returns the block hash of the header, which is simply the keccak256 hash of its @@ -492,6 +541,9 @@ func (h *Header) Size() common.StorageSize { if h.WithdrawalsHash != nil { s += common.StorageSize(32) } + if h.ExcessDataGas != nil { + s += common.StorageSize(bitsToBytes(h.ExcessDataGas.BitLen())) + } return s } @@ -516,6 +568,12 @@ func (h *Header) SanityCheck() error { return fmt.Errorf("too large base fee: bitlen %d", bfLen) } } + if h.ExcessDataGas != nil { + if bfLen := h.ExcessDataGas.BitLen(); bfLen > 256 { + return fmt.Errorf("too large excess data gas: bitlen %d", bfLen) + } + } + return nil } @@ -1173,6 +1231,10 @@ func CopyHeader(h *Header) *Header { cpy.WithdrawalsHash = new(libcommon.Hash) cpy.WithdrawalsHash.SetBytes(h.WithdrawalsHash.Bytes()) } + if h.ExcessDataGas != nil { + cpy.ExcessDataGas = new(big.Int) + cpy.ExcessDataGas.Set(h.ExcessDataGas) + } return &cpy } @@ -1400,6 +1462,13 @@ func (b *Block) BaseFee() *big.Int { func (b *Block) WithdrawalsHash() *libcommon.Hash { return b.header.WithdrawalsHash } func (b *Block) Withdrawals() Withdrawals { return b.withdrawals } +func (b *Block) ExcessDataGas() *big.Int { + if b.header.ExcessDataGas == nil { + return nil + } + return new(big.Int).Set(b.header.ExcessDataGas) +} + // Header returns a deep-copy of the entire block header using CopyHeader() func (b *Block) Header() *Header { return CopyHeader(b.header) } func (b *Block) HeaderNoCopy() *Header { return b.header } diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index 70469fd6f..03b2b351b 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -88,6 +88,11 @@ func (ct CommonTx) IsContractDeploy() bool { return ct.GetTo() == nil } +func (ct *CommonTx) GetDataHashes() []libcommon.Hash { + // Only blob txs have data hashes + return []libcommon.Hash{} +} + // LegacyTx is the transaction data of regular Ethereum transactions. type LegacyTx struct { CommonTx diff --git a/core/types/transaction.go b/core/types/transaction.go index 8df16bf45..bd03b668d 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -50,6 +50,7 @@ const ( LegacyTxType = iota AccessListTxType DynamicFeeTxType + BlobTxType = 5 ) // Transaction is an Ethereum transaction. @@ -62,6 +63,7 @@ type Transaction interface { GetEffectiveGasTip(baseFee *uint256.Int) *uint256.Int GetFeeCap() *uint256.Int Cost() *uint256.Int + GetDataHashes() []libcommon.Hash GetGas() uint64 GetValue() *uint256.Int Time() time.Time diff --git a/core/vm/eips.go b/core/vm/eips.go index 292f5c7d0..f2b7dcb68 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -198,3 +198,26 @@ func enable3860(jt *JumpTable) { jt[CREATE].dynamicGas = gasCreateEip3860 jt[CREATE2].dynamicGas = gasCreate2Eip3860 } + +// enable3198 applies mini-danksharding (DATAHASH Opcode) +// - Adds an opcode that returns the versioned data hash of the tx at a index. +func enableSharding(jt *JumpTable) { + jt[DATAHASH] = &operation{ + execute: opDataHash, + constantGas: GasFastestStep, + numPop: 0, + numPush: 1, + } +} + +// opDataHash implements DATAHASH opcode +func opDataHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + idx := scope.Stack.Peek() + if idx.LtUint64(uint64(len(interpreter.evm.TxContext().DataHashes))) { + hash := interpreter.evm.TxContext().DataHashes[idx.Uint64()] + idx.SetBytes(hash.Bytes()) + } else { + idx.Clear() + } + return nil, nil +} diff --git a/core/vm/evmtypes/evmtypes.go b/core/vm/evmtypes/evmtypes.go index e1e5b79c1..efc596120 100644 --- a/core/vm/evmtypes/evmtypes.go +++ b/core/vm/evmtypes/evmtypes.go @@ -22,23 +22,25 @@ type BlockContext struct { GetHash GetHashFunc // Block information - Coinbase libcommon.Address // Provides information for COINBASE - GasLimit uint64 // Provides information for GASLIMIT - MaxGasLimit bool // Use GasLimit override for 2^256-1 (to be compatible with OpenEthereum's trace_call) - BlockNumber uint64 // Provides information for NUMBER - Time uint64 // Provides information for TIME - Difficulty *big.Int // Provides information for DIFFICULTY - BaseFee *uint256.Int // Provides information for BASEFEE - PrevRanDao *libcommon.Hash // Provides information for PREVRANDAO + Coinbase libcommon.Address // Provides information for COINBASE + GasLimit uint64 // Provides information for GASLIMIT + MaxGasLimit bool // Use GasLimit override for 2^256-1 (to be compatible with OpenEthereum's trace_call) + BlockNumber uint64 // Provides information for NUMBER + Time uint64 // Provides information for TIME + Difficulty *big.Int // Provides information for DIFFICULTY + BaseFee *uint256.Int // Provides information for BASEFEE + PrevRanDao *libcommon.Hash // Provides information for PREVRANDAO + ExcessDataGas *big.Int // Provides information for handling data blobs } // TxContext provides the EVM with information about a transaction. // All fields can change between transactions. type TxContext struct { // Message information - TxHash libcommon.Hash - Origin libcommon.Address // Provides information for ORIGIN - GasPrice *uint256.Int // Provides information for GASPRICE + TxHash libcommon.Hash + Origin libcommon.Address // Provides information for ORIGIN + GasPrice *uint256.Int // Provides information for GASPRICE + DataHashes []libcommon.Hash // Provides versioned data hashes for DATAHASH } type ( diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index d61e3214a..7db520fd5 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -104,6 +104,7 @@ const ( CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 BASEFEE OpCode = 0x48 + DATAHASH OpCode = 0x49 ) // 0x50 range - 'storage' and execution. @@ -277,6 +278,7 @@ var opCodeToString = map[OpCode]string{ CHAINID: "CHAINID", SELFBALANCE: "SELFBALANCE", BASEFEE: "BASEFEE", + DATAHASH: "DATAHASH", // 0x50 range - 'storage' and execution. POP: "POP", @@ -428,6 +430,7 @@ var stringToOp = map[string]OpCode{ "CALLDATACOPY": CALLDATACOPY, "CHAINID": CHAINID, "BASEFEE": BASEFEE, + "DATAHASH": DATAHASH, "DELEGATECALL": DELEGATECALL, "STATICCALL": STATICCALL, "CODESIZE": CODESIZE, diff --git a/ethdb/privateapi/ethbackend.go b/ethdb/privateapi/ethbackend.go index 1f9ae3c7c..379ce6339 100644 --- a/ethdb/privateapi/ethbackend.go +++ b/ethdb/privateapi/ethbackend.go @@ -334,6 +334,14 @@ func (s *EthBackendServer) EngineNewPayload(ctx context.Context, req *types2.Exe return nil, err } + if req.Version >= 3 { + header.ExcessDataGas = gointerfaces.ConvertH256ToUint256Int(req.ExcessDataGas).ToBig() + } + + if !s.config.IsCancun(header.Time) && header.ExcessDataGas != nil || s.config.IsCancun(header.Time) && header.ExcessDataGas == nil { + return nil, &rpc.InvalidParamsError{Message: "excess data gas setting doesn't match sharding state"} + } + blockHash := gointerfaces.ConvertH256ToHash(req.BlockHash) if header.Hash() != blockHash { log.Error("[NewPayload] invalid block hash", "stated", libcommon.Hash(blockHash), "actual", header.Hash()) @@ -590,6 +598,13 @@ func (s *EthBackendServer) EngineGetPayload(ctx context.Context, req *remote.Eng payload.Withdrawals = ConvertWithdrawalsToRpc(block.Withdrawals()) } + if block.ExcessDataGas() != nil { + payload.Version = 3 + var excessDataGas uint256.Int + excessDataGas.SetFromBig(block.Header().ExcessDataGas) + payload.ExcessDataGas = gointerfaces.ConvertUint256IntToH256(&excessDataGas) + } + blockValue := blockValue(blockWithReceipts, baseFee) return &remote.EngineGetPayloadResponse{ ExecutionPayload: payload, diff --git a/params/protocol_params.go b/params/protocol_params.go index a09f2a69b..b34caab01 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -165,6 +165,19 @@ const ( // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529 RefundQuotient uint64 = 2 RefundQuotientEIP3529 uint64 = 5 + + // stuff from EIP-4844 + FieldElementsPerBlob = 4096 // each field element is 32 bytes + MaxDataGasPerBlock = 1 << 19 + DataGasPerBlob = 1 << 17 + TargetDataGasPerBlock = 1 << 18 + MinDataGasPrice = 1 + DataGasPriceUpdateFraction = 2225652 + MaxBlobsPerBlock = MaxDataGasPerBlock / DataGasPerBlob + + BlobVerificationGas uint64 = 1800000 + BlobCommitmentVersionKZG uint8 = 0x01 + PointEvaluationGas uint64 = 50000 ) // Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations diff --git a/turbo/adapter/ethapi/api.go b/turbo/adapter/ethapi/api.go index 43c36e434..244b1b573 100644 --- a/turbo/adapter/ethapi/api.go +++ b/turbo/adapter/ethapi/api.go @@ -281,6 +281,9 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { if head.WithdrawalsHash != nil { result["withdrawalsRoot"] = head.WithdrawalsHash } + if head.ExcessDataGas != nil { + result["excessDataGas"] = (*hexutil.Big)(head.ExcessDataGas) + } return result }