mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 07:36:44 +00:00
This is the implementation of EIP-8070 Sparse Blobpool. It introduces protocol version eth/72 which relays blob transaction cells instead of full blobs. The blobpool now store 'incomplete' transactions, where only some of the cells are provided. The stored cell indexes are taken from the custody bitmap, which is provided by the consensus layer in forkchoiceUpdatedV4. This method will be called once Glamsterdam activates, and the default custody is full custody, so for now there is no change in the amount of stored cells for now. The main entities added are the BlobBuffer and BlobFetcher, which work together to track and fetch missing cells from connected peers. The partial transactions become available for inclusion in blocks when they are covered by enough peers that hold all cells. This change also introduces engine_getBlobsV4, which allows for cell-based responses (and partial blobs with only some of the cells). We maintain backward compatibility with getBlobsV3 which expects full blob responses by recovering the blob from available cells. Since this process is resource-intensive, we proactively cache the conversion so it is ready in time for getBlobsV3 calls. This mechanism will be removed once support for getBlobsV4 is universal across all consensus layer implementations. devp2p tests for eth/72 are not part of this initial change. This is to avoid breaking test success status for execution clients that do not have eth/72 implemented yet. The tests will be added in a subsequent change. --------- Co-authored-by: Felix Lange <fjl@twurst.com>
402 lines
14 KiB
Go
402 lines
14 KiB
Go
// Copyright 2020 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 <http://www.gnu.org/licenses/>.
|
|
|
|
package eth
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/forkid"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
)
|
|
|
|
// Constants to match up protocol versions and messages
|
|
const (
|
|
ETH69 = 69
|
|
ETH70 = 70
|
|
ETH71 = 71
|
|
ETH72 = 72
|
|
)
|
|
|
|
// ProtocolName is the official short name of the `eth` protocol used during
|
|
// devp2p capability negotiation.
|
|
const ProtocolName = "eth"
|
|
|
|
// ProtocolVersions are the supported versions of the `eth` protocol (first
|
|
// is primary).
|
|
var ProtocolVersions = []uint{ETH72, ETH71, ETH70, ETH69}
|
|
|
|
// protocolLengths are the number of implemented message corresponding to
|
|
// different protocol versions.
|
|
var protocolLengths = map[uint]uint64{ETH69: 18, ETH70: 18, ETH71: 20, ETH72: 22}
|
|
|
|
// maxMessageSize is the maximum cap on the size of a protocol message.
|
|
const maxMessageSize = 10 * 1024 * 1024
|
|
|
|
// This is the maximum number of transactions in a Transactions message.
|
|
const maxTransactionAnnouncements = 5000
|
|
|
|
const (
|
|
StatusMsg = 0x00
|
|
NewBlockHashesMsg = 0x01
|
|
TransactionsMsg = 0x02
|
|
GetBlockHeadersMsg = 0x03
|
|
BlockHeadersMsg = 0x04
|
|
GetBlockBodiesMsg = 0x05
|
|
BlockBodiesMsg = 0x06
|
|
NewBlockMsg = 0x07
|
|
NewPooledTransactionHashesMsg = 0x08
|
|
GetPooledTransactionsMsg = 0x09
|
|
PooledTransactionsMsg = 0x0a
|
|
GetReceiptsMsg = 0x0f
|
|
ReceiptsMsg = 0x10
|
|
BlockRangeUpdateMsg = 0x11
|
|
GetBlockAccessListsMsg = 0x12
|
|
BlockAccessListsMsg = 0x13
|
|
GetCellsMsg = 0x14
|
|
CellsMsg = 0x15
|
|
)
|
|
|
|
var (
|
|
errMsgTooLarge = errors.New("message too long")
|
|
errInvalidMsgCode = errors.New("invalid message code")
|
|
errProtocolVersionMismatch = errors.New("protocol version mismatch")
|
|
// handshake errors
|
|
errNoStatusMsg = errors.New("no status message")
|
|
errNetworkIDMismatch = errors.New("network ID mismatch")
|
|
errGenesisMismatch = errors.New("genesis mismatch")
|
|
errForkIDRejected = errors.New("fork ID rejected")
|
|
errInvalidBlockRange = errors.New("invalid block range in status")
|
|
)
|
|
|
|
// Packet represents a p2p message in the `eth` protocol.
|
|
type Packet interface {
|
|
Name() string // Name returns a string corresponding to the message type.
|
|
Kind() byte // Kind returns the message type.
|
|
}
|
|
|
|
// StatusPacket is the network packet for the status message.
|
|
type StatusPacket struct {
|
|
ProtocolVersion uint32
|
|
NetworkID uint64
|
|
Genesis common.Hash
|
|
ForkID forkid.ID
|
|
// initial available block range
|
|
EarliestBlock uint64
|
|
LatestBlock uint64
|
|
LatestBlockHash common.Hash
|
|
}
|
|
|
|
// TransactionsPacket is the network packet for broadcasting new transactions.
|
|
type TransactionsPacket struct {
|
|
rlp.RawList[*types.Transaction]
|
|
}
|
|
|
|
// GetBlockHeadersRequest represents a block header query.
|
|
type GetBlockHeadersRequest struct {
|
|
Origin HashOrNumber // Block from which to retrieve headers
|
|
Amount uint64 // Maximum number of headers to retrieve
|
|
Skip uint64 // Blocks to skip between consecutive headers
|
|
Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis)
|
|
}
|
|
|
|
// GetBlockHeadersPacket represents a block header query with request ID wrapping.
|
|
type GetBlockHeadersPacket struct {
|
|
RequestId uint64
|
|
*GetBlockHeadersRequest
|
|
}
|
|
|
|
// HashOrNumber is a combined field for specifying an origin block.
|
|
type HashOrNumber struct {
|
|
Hash common.Hash // Block hash from which to retrieve headers (excludes Number)
|
|
Number uint64 // Block hash from which to retrieve headers (excludes Hash)
|
|
}
|
|
|
|
// EncodeRLP is a specialized encoder for HashOrNumber to encode only one of the
|
|
// two contained union fields.
|
|
func (hn *HashOrNumber) EncodeRLP(w io.Writer) error {
|
|
if hn.Hash == (common.Hash{}) {
|
|
return rlp.Encode(w, hn.Number)
|
|
}
|
|
if hn.Number != 0 {
|
|
return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number)
|
|
}
|
|
return rlp.Encode(w, hn.Hash)
|
|
}
|
|
|
|
// DecodeRLP is a specialized decoder for HashOrNumber to decode the contents
|
|
// into either a block hash or a block number.
|
|
func (hn *HashOrNumber) DecodeRLP(s *rlp.Stream) error {
|
|
_, size, err := s.Kind()
|
|
switch {
|
|
case err != nil:
|
|
return err
|
|
case size == 32:
|
|
hn.Number = 0
|
|
return s.Decode(&hn.Hash)
|
|
case size <= 8:
|
|
hn.Hash = common.Hash{}
|
|
return s.Decode(&hn.Number)
|
|
default:
|
|
return fmt.Errorf("invalid input size %d for origin", size)
|
|
}
|
|
}
|
|
|
|
// BlockHeadersRequest represents a block header response.
|
|
type BlockHeadersRequest []*types.Header
|
|
|
|
// BlockHeadersPacket represents a block header response over with request ID wrapping.
|
|
type BlockHeadersPacket struct {
|
|
RequestId uint64
|
|
List rlp.RawList[*types.Header]
|
|
}
|
|
|
|
// BlockHeadersRLPResponse represents a block header response, to use when we already
|
|
// have the headers rlp encoded.
|
|
type BlockHeadersRLPResponse []rlp.RawValue
|
|
|
|
// BlockHeadersRLPPacket represents a block header response with request ID wrapping.
|
|
type BlockHeadersRLPPacket struct {
|
|
RequestId uint64
|
|
BlockHeadersRLPResponse
|
|
}
|
|
|
|
// GetBlockBodiesRequest represents a block body query.
|
|
type GetBlockBodiesRequest []common.Hash
|
|
|
|
// GetBlockBodiesPacket represents a block body query with request ID wrapping.
|
|
type GetBlockBodiesPacket struct {
|
|
RequestId uint64
|
|
GetBlockBodiesRequest
|
|
}
|
|
|
|
// BlockBodiesPacket is the network packet for block content distribution with
|
|
// request ID wrapping.
|
|
type BlockBodiesPacket struct {
|
|
RequestId uint64
|
|
List rlp.RawList[BlockBody]
|
|
}
|
|
|
|
// BlockBodiesRLPResponse is used for replying to block body requests, in cases
|
|
// where we already have them RLP-encoded, and thus can avoid the decode-encode
|
|
// roundtrip.
|
|
type BlockBodiesRLPResponse []rlp.RawValue
|
|
|
|
// BlockBodiesRLPPacket is the BlockBodiesRLPResponse with request ID wrapping.
|
|
type BlockBodiesRLPPacket struct {
|
|
RequestId uint64
|
|
BlockBodiesRLPResponse
|
|
}
|
|
|
|
// BlockBodiesResponse is the network packet for block content distribution.
|
|
type BlockBodiesResponse []BlockBody
|
|
|
|
// BlockBody represents the data content of a single block.
|
|
type BlockBody struct {
|
|
Transactions rlp.RawList[*types.Transaction]
|
|
Uncles rlp.RawList[*types.Header]
|
|
Withdrawals *rlp.RawList[*types.Withdrawal] `rlp:"optional"`
|
|
}
|
|
|
|
// GetReceiptsRequest represents a block receipts query.
|
|
type GetReceiptsRequest []common.Hash
|
|
|
|
// GetReceiptsPacket69 represents a block receipts query with request ID wrapping.
|
|
type GetReceiptsPacket69 struct {
|
|
RequestId uint64
|
|
GetReceiptsRequest
|
|
}
|
|
|
|
// GetReceiptsPacket70 represents a block receipts query with request ID and
|
|
// FirstBlockReceiptIndex wrapping.
|
|
type GetReceiptsPacket70 struct {
|
|
RequestId uint64
|
|
FirstBlockReceiptIndex uint64
|
|
GetReceiptsRequest
|
|
}
|
|
|
|
// ReceiptsResponse is the network packet for block receipts distribution.
|
|
type ReceiptsResponse []types.Receipts
|
|
|
|
// ReceiptsPacket69 is the network packet for block receipts distribution with
|
|
// request ID wrapping.
|
|
type ReceiptsPacket69 struct {
|
|
RequestId uint64
|
|
List rlp.RawList[*ReceiptList]
|
|
}
|
|
|
|
type ReceiptsPacket70 struct {
|
|
RequestId uint64
|
|
LastBlockIncomplete bool
|
|
List rlp.RawList[*ReceiptList]
|
|
}
|
|
|
|
// ReceiptsRLPResponse is used for receipts, when we already have it encoded
|
|
type ReceiptsRLPResponse []rlp.RawValue
|
|
|
|
// NewPooledTransactionHashesPacket71 represents a transaction announcement packet on protocol version
|
|
// less than or equal to 71.
|
|
type NewPooledTransactionHashesPacket71 struct {
|
|
Types []byte
|
|
Sizes []uint32
|
|
Hashes []common.Hash
|
|
}
|
|
|
|
// NewPooledTransactionHashesPacket72 represents a transaction announcement packet on ETH/72
|
|
// with an additional custody bitmap field for cell-based blob data availability.
|
|
type NewPooledTransactionHashesPacket72 struct {
|
|
Types []byte
|
|
Sizes []uint32
|
|
Hashes []common.Hash
|
|
Mask types.CustodyBitmap
|
|
}
|
|
|
|
// GetPooledTransactionsRequest represents a transaction query.
|
|
type GetPooledTransactionsRequest []common.Hash
|
|
|
|
// GetPooledTransactionsPacket represents a transaction query with request ID wrapping.
|
|
type GetPooledTransactionsPacket struct {
|
|
RequestId uint64
|
|
GetPooledTransactionsRequest
|
|
}
|
|
|
|
// PooledTransactionsResponse is the network packet for transaction distribution.
|
|
type PooledTransactionsResponse []*types.Transaction
|
|
|
|
// PooledTransactionsPacket is the network packet for transaction distribution
|
|
// with request ID wrapping.
|
|
type PooledTransactionsPacket struct {
|
|
RequestId uint64
|
|
List rlp.RawList[*types.Transaction]
|
|
}
|
|
|
|
// PooledTransactionsRLPResponse is the network packet for transaction distribution, used
|
|
// in the cases we already have them in rlp-encoded form
|
|
type PooledTransactionsRLPResponse []rlp.RawValue
|
|
|
|
// PooledTransactionsRLPPacket is PooledTransactionsRLPResponse with request ID wrapping.
|
|
type PooledTransactionsRLPPacket struct {
|
|
RequestId uint64
|
|
PooledTransactionsRLPResponse
|
|
}
|
|
|
|
// BlockRangeUpdatePacket is an announcement of the node's available block range.
|
|
type BlockRangeUpdatePacket struct {
|
|
EarliestBlock uint64
|
|
LatestBlock uint64
|
|
LatestBlockHash common.Hash
|
|
}
|
|
|
|
// GetCellsRequest represents a request for cells of blob transactions.
|
|
type GetCellsRequest struct {
|
|
Hashes []common.Hash
|
|
Mask types.CustodyBitmap
|
|
}
|
|
|
|
// GetCellsRequestPacket represents a cell request with request ID wrapping.
|
|
type GetCellsRequestPacket struct {
|
|
RequestId uint64
|
|
GetCellsRequest
|
|
}
|
|
|
|
// CellsResponse represents a response containing cells for blob transactions.
|
|
type CellsResponse struct {
|
|
Hashes []common.Hash
|
|
Cells rlp.RawList[rlp.RawList[kzg4844.Cell]]
|
|
Mask types.CustodyBitmap
|
|
}
|
|
|
|
// CellsPacket represents a cells response with request ID wrapping.
|
|
type CellsPacket struct {
|
|
RequestId uint64
|
|
CellsResponse
|
|
}
|
|
|
|
type GetBlockAccessListsRequest []common.Hash
|
|
|
|
type GetBlockAccessListsPacket struct {
|
|
RequestId uint64
|
|
GetBlockAccessListsRequest
|
|
}
|
|
|
|
// BlockAccessListResponse holds one raw entry per requested hash. Entries are
|
|
// kept as raw values because, per EIP-8159, the RLP empty string signals an
|
|
// unavailable BAL (an empty list is itself a valid BAL).
|
|
type BlockAccessListResponse []rlp.RawValue
|
|
|
|
type BlockAccessListPacket struct {
|
|
RequestId uint64
|
|
List rlp.RawList[rlp.RawValue]
|
|
}
|
|
|
|
func (*StatusPacket) Name() string { return "Status" }
|
|
func (*StatusPacket) Kind() byte { return StatusMsg }
|
|
|
|
func (*TransactionsPacket) Name() string { return "Transactions" }
|
|
func (*TransactionsPacket) Kind() byte { return TransactionsMsg }
|
|
|
|
func (*GetBlockHeadersRequest) Name() string { return "GetBlockHeaders" }
|
|
func (*GetBlockHeadersRequest) Kind() byte { return GetBlockHeadersMsg }
|
|
|
|
func (*BlockHeadersRequest) Name() string { return "BlockHeaders" }
|
|
func (*BlockHeadersRequest) Kind() byte { return BlockHeadersMsg }
|
|
|
|
func (*GetBlockBodiesRequest) Name() string { return "GetBlockBodies" }
|
|
func (*GetBlockBodiesRequest) Kind() byte { return GetBlockBodiesMsg }
|
|
|
|
func (*BlockBodiesResponse) Name() string { return "BlockBodies" }
|
|
func (*BlockBodiesResponse) Kind() byte { return BlockBodiesMsg }
|
|
|
|
func (*NewPooledTransactionHashesPacket71) Name() string { return "NewPooledTransactionHashes" }
|
|
func (*NewPooledTransactionHashesPacket71) Kind() byte { return NewPooledTransactionHashesMsg }
|
|
|
|
func (*NewPooledTransactionHashesPacket72) Name() string { return "NewPooledTransactionHashes" }
|
|
func (*NewPooledTransactionHashesPacket72) Kind() byte { return NewPooledTransactionHashesMsg }
|
|
|
|
func (*GetPooledTransactionsRequest) Name() string { return "GetPooledTransactions" }
|
|
func (*GetPooledTransactionsRequest) Kind() byte { return GetPooledTransactionsMsg }
|
|
|
|
func (*PooledTransactionsPacket) Name() string { return "PooledTransactions" }
|
|
func (*PooledTransactionsPacket) Kind() byte { return PooledTransactionsMsg }
|
|
|
|
func (*GetReceiptsRequest) Name() string { return "GetReceipts" }
|
|
func (*GetReceiptsRequest) Kind() byte { return GetReceiptsMsg }
|
|
|
|
func (*ReceiptsResponse) Name() string { return "Receipts" }
|
|
func (*ReceiptsResponse) Kind() byte { return ReceiptsMsg }
|
|
|
|
func (*ReceiptsRLPResponse) Name() string { return "Receipts" }
|
|
func (*ReceiptsRLPResponse) Kind() byte { return ReceiptsMsg }
|
|
|
|
func (*BlockRangeUpdatePacket) Name() string { return "BlockRangeUpdate" }
|
|
func (*BlockRangeUpdatePacket) Kind() byte { return BlockRangeUpdateMsg }
|
|
|
|
func (*GetBlockAccessListsRequest) Name() string { return "GetBlockAccessLists" }
|
|
func (*GetBlockAccessListsRequest) Kind() byte { return GetBlockAccessListsMsg }
|
|
|
|
func (*BlockAccessListResponse) Name() string { return "BlockAccessLists" }
|
|
func (*BlockAccessListResponse) Kind() byte { return BlockAccessListsMsg }
|
|
|
|
func (*GetCellsRequest) Name() string { return "GetCells" }
|
|
func (*GetCellsRequest) Kind() byte { return GetCellsMsg }
|
|
|
|
func (*CellsResponse) Name() string { return "Cells" }
|
|
func (*CellsResponse) Kind() byte { return CellsMsg }
|