From 495692c9db3c296485ab75a8d69ba1323f0e4553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Wed, 31 May 2023 11:12:26 +0300 Subject: [PATCH] core, eth/downloader, params: validate blob tx bodies (#27392) --- core/block_validator.go | 16 ++++++++++++++++ eth/downloader/queue.go | 16 ++++++++++++++++ params/protocol_params.go | 1 + 3 files changed, 33 insertions(+) diff --git a/core/block_validator.go b/core/block_validator.go index ce3e6560ee..6dc9a3fa05 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -84,7 +84,23 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // Blob transactions may be present after the Cancun fork. var blobs int for _, tx := range block.Transactions() { + // Count the number of blobs to validate against the header's dataGasUsed blobs += len(tx.BlobHashes()) + + // Validate the data blobs individually too + if tx.Type() == types.BlobTxType { + if tx.To() == nil { + return errors.New("contract creation attempt by blob transaction") // TODO(karalabe): Why not make the field non-nil-able + } + if len(tx.BlobHashes()) == 0 { + return errors.New("no-blob blob transaction present in block body") + } + for _, hash := range tx.BlobHashes() { + if hash[0] != params.BlobTxHashVersion { + return fmt.Errorf("blob hash version mismatch (have %d, supported %d)", hash[0], params.BlobTxHashVersion) + } + } + } } 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 diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 0a00d48901..25449d8772 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -801,7 +801,23 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH // and zero before the Cancun hardfork var blobs int for _, tx := range txLists[index] { + // Count the number of blobs to validate against the header's dataGasUsed blobs += len(tx.BlobHashes()) + + // Validate the data blobs individually too + if tx.Type() == types.BlobTxType { + if tx.To() == nil { + return errInvalidBody // TODO(karalabe): Why not make the field non-nil-able + } + if len(tx.BlobHashes()) == 0 { + return errInvalidBody + } + for _, hash := range tx.BlobHashes() { + if hash[0] != params.BlobTxHashVersion { + return errInvalidBody + } + } + } } 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 diff --git a/params/protocol_params.go b/params/protocol_params.go index 5ddc13c63b..0b4413f409 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -160,6 +160,7 @@ const ( RefundQuotient uint64 = 2 RefundQuotientEIP3529 uint64 = 5 + BlobTxHashVersion = 0x01 // Version byte of the commitment hash BlobTxMaxDataGasPerBlock = 1 << 19 // Maximum consumable data gas for data blobs per block BlobTxTargetDataGasPerBlock = 1 << 18 // Target consumable data gas for data blobs per block (for 1559-like pricing) BlobTxDataGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size)