From 0e93da3197defe6296ed52bee4c68d3187f3b869 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 19 Jan 2024 11:41:17 +0100 Subject: [PATCH] crypto/kzg4844: add helpers for versioned blob hashes (#28827) The code to compute a versioned hash was duplicated a couple times, and also had a small issue: if we ever change params.BlobTxHashVersion, it will most likely also cause changes to the actual hash computation. So it's a bit useless to have this constant in params. --- core/state_transition.go | 6 +++--- core/txpool/blobpool/blobpool_test.go | 14 +------------- core/txpool/validation.go | 15 ++++----------- core/types/tx_blob.go | 12 ++---------- crypto/kzg4844/kzg4844.go | 19 +++++++++++++++++++ eth/downloader/queue.go | 3 ++- params/protocol_params.go | 1 - 7 files changed, 31 insertions(+), 39 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 6ae1224e29..df2faa19a9 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -25,6 +25,7 @@ import ( cmath "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/params" ) @@ -324,9 +325,8 @@ func (st *StateTransition) preCheck() error { return ErrMissingBlobHashes } for i, hash := range msg.BlobHashes { - if hash[0] != params.BlobTxHashVersion { - return fmt.Errorf("blob %d hash version mismatch (have %d, supported %d)", - i, hash[0], params.BlobTxHashVersion) + if !kzg4844.IsValidVersionedHash(hash[:]) { + return fmt.Errorf("blob %d has invalid hash version", i) } } } diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index b709ad0e58..09c78cfd80 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -51,21 +51,9 @@ var ( emptyBlob = kzg4844.Blob{} emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit) - emptyBlobVHash = blobHash(emptyBlobCommit) + emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) ) -func blobHash(commit kzg4844.Commitment) common.Hash { - hasher := sha256.New() - hasher.Write(commit[:]) - hash := hasher.Sum(nil) - - var vhash common.Hash - vhash[0] = params.BlobTxHashVersion - copy(vhash[1:], hash[1:]) - - return vhash -} - // Chain configuration with Cancun enabled. // // TODO(karalabe): replace with params.MainnetChainConfig after Cancun. diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 0df363d81d..cac2f334ac 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -143,17 +143,10 @@ func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) err // Blob quantities match up, validate that the provers match with the // transaction hash before getting to the cryptography hasher := sha256.New() - for i, want := range hashes { - hasher.Write(sidecar.Commitments[i][:]) - hash := hasher.Sum(nil) - hasher.Reset() - - var vhash common.Hash - vhash[0] = params.BlobTxHashVersion - copy(vhash[1:], hash[1:]) - - if vhash != want { - return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, vhash, want) + for i, vhash := range hashes { + computed := kzg4844.CalcBlobHashV1(hasher, &sidecar.Commitments[i]) + if vhash != computed { + return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, computed, vhash) } } // Blob commitments match with the hashes in the transaction, verify the diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index da4a9b72f1..caede7cc53 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -61,9 +61,10 @@ type BlobTxSidecar struct { // BlobHashes computes the blob hashes of the given blobs. func (sc *BlobTxSidecar) BlobHashes() []common.Hash { + hasher := sha256.New() h := make([]common.Hash, len(sc.Commitments)) for i := range sc.Blobs { - h[i] = blobHash(&sc.Commitments[i]) + h[i] = kzg4844.CalcBlobHashV1(hasher, &sc.Commitments[i]) } return h } @@ -235,12 +236,3 @@ func (tx *BlobTx) decode(input []byte) error { } return nil } - -func blobHash(commit *kzg4844.Commitment) common.Hash { - hasher := sha256.New() - hasher.Write(commit[:]) - var vhash common.Hash - hasher.Sum(vhash[:0]) - vhash[0] = params.BlobTxHashVersion - return vhash -} diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 5969d1c2ce..4561ef9de9 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -20,6 +20,7 @@ package kzg4844 import ( "embed" "errors" + "hash" "sync/atomic" ) @@ -108,3 +109,21 @@ func VerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error { } return gokzgVerifyBlobProof(blob, commitment, proof) } + +// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment. +// The given hasher must be a sha256 hash instance, otherwise the result will be invalid! +func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) { + if hasher.Size() != 32 { + panic("wrong hash size") + } + hasher.Reset() + hasher.Write(commit[:]) + hasher.Sum(vh[:0]) + vh[0] = 0x01 // version + return vh +} + +// IsValidVersionedHash checks that h is a structurally-valid versioned blob hash. +func IsValidVersionedHash(h []byte) bool { + return len(h) == 32 && h[0] == 0x01 +} diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index e557158797..6ff858d755 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" @@ -810,7 +811,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH return errInvalidBody } for _, hash := range tx.BlobHashes() { - if hash[0] != params.BlobTxHashVersion { + if !kzg4844.IsValidVersionedHash(hash[:]) { return errInvalidBody } } diff --git a/params/protocol_params.go b/params/protocol_params.go index 8a5c011849..7eb63e89ac 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -166,7 +166,6 @@ const ( BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob - BlobTxHashVersion = 0x01 // Version byte of the commitment hash BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size) BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs BlobTxBlobGaspriceUpdateFraction = 3338477 // Controls the maximum rate of change for blob gas price