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
}