Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

consensus, core, eth/downloader, params: 4844 chain validation #27382

Merged
merged 1 commit into from May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 9 additions & 4 deletions consensus/beacon/consensus.go
Expand Up @@ -257,7 +257,7 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
return consensus.ErrInvalidNumber
}
// Verify the header's EIP-1559 attributes.
if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil {
if err := misc.VerifyEIP1559Header(chain.Config(), parent, header); err != nil {
return err
}
// Verify existence / non-existence of withdrawalsHash.
Expand All @@ -270,12 +270,17 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
}
// Verify the existence / non-existence of excessDataGas
cancun := chain.Config().IsCancun(header.Number, header.Time)
if cancun && header.ExcessDataGas == nil {
return errors.New("missing excessDataGas")
}
if !cancun && header.ExcessDataGas != nil {
return fmt.Errorf("invalid excessDataGas: have %d, expected nil", header.ExcessDataGas)
}
if !cancun && header.DataGasUsed != nil {
return fmt.Errorf("invalid dataGasUsed: have %d, expected nil", header.DataGasUsed)
}
if cancun {
if err := misc.VerifyEIP4844Header(parent, header); err != nil {
return err
}
}
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion consensus/clique/clique.go
Expand Up @@ -343,7 +343,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
return err
}
} else if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil {
} else if err := misc.VerifyEIP1559Header(chain.Config(), parent, header); err != nil {
// Verify the header's EIP-1559 attributes.
return err
}
Expand Down
2 changes: 1 addition & 1 deletion consensus/ethash/consensus.go
Expand Up @@ -254,7 +254,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
return err
}
} else if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil {
} else if err := misc.VerifyEIP1559Header(chain.Config(), parent, header); err != nil {
// Verify the header's EIP-1559 attributes.
return err
}
Expand Down
4 changes: 2 additions & 2 deletions consensus/misc/eip1559.go
Expand Up @@ -27,10 +27,10 @@ import (
"github.com/ethereum/go-ethereum/params"
)

// VerifyEip1559Header verifies some header attributes which were changed in EIP-1559,
// VerifyEIP1559Header verifies some header attributes which were changed in EIP-1559,
// - gas limit check
// - basefee check
func VerifyEip1559Header(config *params.ChainConfig, parent, header *types.Header) error {
func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Header) error {
// Verify that the gas limit remains within allowed bounds
parentGasLimit := parent.GasLimit
if !config.IsLondon(parent.Number) {
Expand Down
2 changes: 1 addition & 1 deletion consensus/misc/eip1559_test.go
Expand Up @@ -95,7 +95,7 @@ func TestBlockGasLimits(t *testing.T) {
BaseFee: initial,
Number: big.NewInt(tc.pNum + 1),
}
err := VerifyEip1559Header(config(), parent, header)
err := VerifyEIP1559Header(config(), parent, header)
if tc.ok && err != nil {
t.Errorf("test %d: Expected valid header: %s", i, err)
}
Expand Down
56 changes: 50 additions & 6 deletions consensus/misc/eip4844.go
Expand Up @@ -17,8 +17,11 @@
package misc

import (
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)

Expand All @@ -27,13 +30,54 @@ var (
dataGaspriceUpdateFraction = big.NewInt(params.BlobTxDataGaspriceUpdateFraction)
)

// CalcBlobFee calculates the blobfee from the header's excess data gas field.
func CalcBlobFee(excessDataGas *big.Int) *big.Int {
// If this block does not yet have EIP-4844 enabled, return the starting fee
if excessDataGas == nil {
return big.NewInt(params.BlobTxMinDataGasprice)
// VerifyEIP4844Header verifies the presence of the excessDataGas field and that
// if the current block contains no transactions, the excessDataGas is updated
// accordingly.
func VerifyEIP4844Header(parent, header *types.Header) error {
// Verify the header is not malformed
if header.ExcessDataGas == nil {
return errors.New("header is missing excessDataGas")
}
if header.DataGasUsed == nil {
return errors.New("header is missing dataGasUsed")
}
// Verify that the data gas used remains within reasonable limits.
if *header.DataGasUsed > params.BlobTxMaxDataGasPerBlock {
return fmt.Errorf("data gas used %d exceeds maximum allowance %d", *header.DataGasUsed, params.BlobTxMaxDataGasPerBlock)
}
if *header.DataGasUsed%params.BlobTxDataGasPerBlob != 0 {
return fmt.Errorf("data gas used %d not a multiple of data gas per blob %d", header.DataGasUsed, params.BlobTxDataGasPerBlob)
}
// Verify the excessDataGas is correct based on the parent header
var (
parentExcessDataGas uint64
parentDataGasUsed uint64
)
if parent.ExcessDataGas != nil {
parentExcessDataGas = *parent.ExcessDataGas
parentDataGasUsed = *parent.DataGasUsed
}
return fakeExponential(minDataGasPrice, excessDataGas, dataGaspriceUpdateFraction)
expectedExcessDataGas := CalcExcessDataGas(parentExcessDataGas, parentDataGasUsed)
if *header.ExcessDataGas != expectedExcessDataGas {
return fmt.Errorf("invalid excessDataGas: have %d, want %d, parent excessDataGas %d, parent blobDataUsed %d",
*header.ExcessDataGas, expectedExcessDataGas, parentExcessDataGas, parentDataGasUsed)
}
return nil
}

// CalcExcessDataGas calculates the excess data gas after applying the set of
// blobs on top of the excess data gas.
func CalcExcessDataGas(parentExcessDataGas uint64, parentDataGasUsed uint64) uint64 {
excessDataGas := parentExcessDataGas + parentDataGasUsed
if excessDataGas < params.BlobTxTargetDataGasPerBlock {
return 0
}
return excessDataGas - params.BlobTxTargetDataGasPerBlock
}

// CalcBlobFee calculates the blobfee from the header's excess data gas field.
func CalcBlobFee(excessDataGas uint64) *big.Int {
return fakeExponential(minDataGasPrice, new(big.Int).SetUint64(excessDataGas), dataGaspriceUpdateFraction)
}

// fakeExponential approximates factor * e ** (numerator / denominator) using
Expand Down
41 changes: 35 additions & 6 deletions consensus/misc/eip4844_test.go
Expand Up @@ -24,22 +24,51 @@ import (
"github.com/ethereum/go-ethereum/params"
)

func TestCalcExcessDataGas(t *testing.T) {
var tests = []struct {
excess uint64
blobs uint64
want uint64
}{
// The excess data gas should not increase from zero if the used blob
// slots are below - or equal - to the target.
{0, 0, 0},
{0, 1, 0},
{0, params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob, 0},

// If the target data gas is exceeded, the excessDataGas should increase
// by however much it was overshot
{0, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) + 1, params.BlobTxDataGasPerBlob},
{1, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) + 1, params.BlobTxDataGasPerBlob + 1},
{1, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) + 2, 2*params.BlobTxDataGasPerBlob + 1},

// The excess data gas should decrease by however much the target was
// under-shot, capped at zero.
{params.BlobTxTargetDataGasPerBlock, params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob, params.BlobTxTargetDataGasPerBlock},
{params.BlobTxTargetDataGasPerBlock, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) - 1, params.BlobTxDataGasPerBlob},
{params.BlobTxTargetDataGasPerBlock, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) - 2, 0},
{params.BlobTxDataGasPerBlob - 1, (params.BlobTxTargetDataGasPerBlock / params.BlobTxDataGasPerBlob) - 1, 0},
}
for _, tt := range tests {
result := CalcExcessDataGas(tt.excess, tt.blobs*params.BlobTxDataGasPerBlob)
if result != tt.want {
t.Errorf("excess data gas mismatch: have %v, want %v", result, tt.want)
}
}
}

func TestCalcBlobFee(t *testing.T) {
tests := []struct {
excessDataGas int64
excessDataGas uint64
blobfee int64
}{
{0, 1},
{1542706, 1},
{1542707, 2},
{10 * 1024 * 1024, 111},
}
have := CalcBlobFee(nil)
if have.Int64() != params.BlobTxMinDataGasprice {
t.Errorf("nil test: blobfee mismatch: have %v, want %v", have, params.BlobTxMinDataGasprice)
}
for i, tt := range tests {
have := CalcBlobFee(big.NewInt(tt.excessDataGas))
have := CalcBlobFee(tt.excessDataGas)
if have.Int64() != tt.blobfee {
t.Errorf("test %d: blobfee mismatch: have %v want %v", i, have, tt.blobfee)
}
Expand Down
17 changes: 15 additions & 2 deletions core/block_validator.go
Expand Up @@ -78,10 +78,23 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash)
}
} else if block.Withdrawals() != nil {
// Withdrawals are not allowed prior to shanghai fork
// Withdrawals are not allowed prior to Shanghai fork
return errors.New("withdrawals present in block body")
}

// Blob transactions may be present after the Cancun fork.
var blobs int
for _, tx := range block.Transactions() {
blobs += len(tx.BlobHashes())
}
if header.DataGasUsed != nil {
if want := *header.DataGasUsed / params.BlobTxDataGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated
return fmt.Errorf("data gas used mismatch (header %v, calculated %v)", *header.DataGasUsed, blobs*params.BlobTxDataGasPerBlob)
}
} else {
if blobs > 0 {
return errors.New("data blobs present in block body")
}
}
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor
Expand Down
47 changes: 35 additions & 12 deletions core/types/block.go
Expand Up @@ -86,19 +86,24 @@ type Header struct {
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`

// ExcessDataGas was added by EIP-4844 and is ignored in legacy headers.
ExcessDataGas *big.Int `json:"excessDataGas" rlp:"optional"`
ExcessDataGas *uint64 `json:"excessDataGas" rlp:"optional"`

// DataGasUsed was added by EIP-4844 and is ignored in legacy headers.
DataGasUsed *uint64 `json:"dataGasUsed" rlp:"optional"`
}

// 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
Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
ExcessDataGas *hexutil.Uint64
DataGasUsed *hexutil.Uint64
}

// Hash returns the block hash of the header, which is simply the keccak256 hash of its
Expand Down Expand Up @@ -146,10 +151,10 @@ func (h *Header) SanityCheck() error {
// EmptyBody returns true if there is no additional 'body' to complete the header
// that is: no transactions, no uncles and no withdrawals.
func (h *Header) EmptyBody() bool {
if h.WithdrawalsHash == nil {
return h.TxHash == EmptyTxsHash && h.UncleHash == EmptyUncleHash
if h.WithdrawalsHash != nil {
return h.TxHash == EmptyTxsHash && *h.WithdrawalsHash == EmptyWithdrawalsHash
}
return h.TxHash == EmptyTxsHash && h.UncleHash == EmptyUncleHash && *h.WithdrawalsHash == EmptyWithdrawalsHash
return h.TxHash == EmptyTxsHash && h.UncleHash == EmptyUncleHash
}

// EmptyReceipts returns true if there are no receipts for this header/block.
Expand Down Expand Up @@ -347,6 +352,24 @@ func (b *Block) Withdrawals() Withdrawals {
return b.withdrawals
}

func (b *Block) ExcessDataGas() *uint64 {
var excessDataGas *uint64
if b.header.ExcessDataGas != nil {
excessDataGas = new(uint64)
*excessDataGas = *b.header.ExcessDataGas
}
return excessDataGas
}

func (b *Block) DataGasUsed() *uint64 {
var dataGasUsed *uint64
if b.header.DataGasUsed != nil {
dataGasUsed = new(uint64)
*dataGasUsed = *b.header.DataGasUsed
}
return dataGasUsed
}

func (b *Block) Header() *Header { return CopyHeader(b.header) }

// Body returns the non-header content of the block.
Expand Down
50 changes: 28 additions & 22 deletions core/types/gen_header_json.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.