// 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 . 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/rlp" ) // Constants to match up protocol versions and messages const ( ETH69 = 69 ETH70 = 70 ) // 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{ETH70, ETH69} // protocolLengths are the number of implemented message corresponding to // different protocol versions. var protocolLengths = map[uint]uint64{ETH69: 18, ETH70: 18} // 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 ) 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 // NewPooledTransactionHashesPacket represents a transaction announcement packet on eth/68 and newer. type NewPooledTransactionHashesPacket struct { Types []byte Sizes []uint32 Hashes []common.Hash } // 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 } 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 (*NewPooledTransactionHashesPacket) Name() string { return "NewPooledTransactionHashes" } func (*NewPooledTransactionHashesPacket) 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 }