// Copyright 2014 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" "math/big" "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 ( ETH64 = 64 ETH65 = 65 ) // 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{ETH65, ETH64} // protocolLengths are the number of implemented message corresponding to // different protocol versions. var protocolLengths = map[uint]uint64{ETH65: 17, ETH64: 17} // maxMessageSize is the maximum cap on the size of a protocol message. const maxMessageSize = 10 * 1024 * 1024 const ( // Protocol messages in eth/64 StatusMsg = 0x00 NewBlockHashesMsg = 0x01 TransactionsMsg = 0x02 GetBlockHeadersMsg = 0x03 BlockHeadersMsg = 0x04 GetBlockBodiesMsg = 0x05 BlockBodiesMsg = 0x06 NewBlockMsg = 0x07 GetNodeDataMsg = 0x0d NodeDataMsg = 0x0e GetReceiptsMsg = 0x0f ReceiptsMsg = 0x10 // Protocol messages overloaded in eth/65 NewPooledTransactionHashesMsg = 0x08 GetPooledTransactionsMsg = 0x09 PooledTransactionsMsg = 0x0a ) var ( errNoStatusMsg = errors.New("no status message") errMsgTooLarge = errors.New("message too long") errDecode = errors.New("invalid message") errInvalidMsgCode = errors.New("invalid message code") errProtocolVersionMismatch = errors.New("protocol version mismatch") errNetworkIDMismatch = errors.New("network ID mismatch") errGenesisMismatch = errors.New("genesis mismatch") errForkIDRejected = errors.New("fork ID rejected") errExtraStatusMsg = errors.New("extra status message") ) // 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 for eth/64 and later. type StatusPacket struct { ProtocolVersion uint32 NetworkID uint64 TD *big.Int Head common.Hash Genesis common.Hash ForkID forkid.ID } // NewBlockHashesPacket is the network packet for the block announcements. type NewBlockHashesPacket []struct { Hash common.Hash // Hash of one particular block being announced Number uint64 // Number of one particular block being announced } // Unpack retrieves the block hashes and numbers from the announcement packet // and returns them in a split flat format that's more consistent with the // internal data structures. func (p *NewBlockHashesPacket) Unpack() ([]common.Hash, []uint64) { var ( hashes = make([]common.Hash, len(*p)) numbers = make([]uint64, len(*p)) ) for i, body := range *p { hashes[i], numbers[i] = body.Hash, body.Number } return hashes, numbers } // TransactionsPacket is the network packet for broadcasting new transactions. type TransactionsPacket []*types.Transaction // GetBlockHeadersPacket represents a block header query. type GetBlockHeadersPacket 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) } // 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, _ := s.Kind() origin, err := s.Raw() if err == nil { switch { case size == 32: err = rlp.DecodeBytes(origin, &hn.Hash) case size <= 8: err = rlp.DecodeBytes(origin, &hn.Number) default: err = fmt.Errorf("invalid input size %d for origin", size) } } return err } // BlockHeadersPacket represents a block header response. type BlockHeadersPacket []*types.Header // NewBlockPacket is the network packet for the block propagation message. type NewBlockPacket struct { Block *types.Block TD *big.Int } // sanityCheck verifies that the values are reasonable, as a DoS protection func (request *NewBlockPacket) sanityCheck() error { if err := request.Block.SanityCheck(); err != nil { return err } //TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times // larger, it will still fit within 100 bits if tdlen := request.TD.BitLen(); tdlen > 100 { return fmt.Errorf("too large block TD: bitlen %d", tdlen) } return nil } // GetBlockBodiesPacket represents a block body query. type GetBlockBodiesPacket []common.Hash // BlockBodiesPacket is the network packet for block content distribution. type BlockBodiesPacket []*BlockBody // BlockBody represents the data content of a single block. type BlockBody struct { Transactions []*types.Transaction // Transactions contained within a block Uncles []*types.Header // Uncles contained within a block } // Unpack retrieves the transactions and uncles from the range packet and returns // them in a split flat format that's more consistent with the internal data structures. func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header) { var ( txset = make([][]*types.Transaction, len(*p)) uncleset = make([][]*types.Header, len(*p)) ) for i, body := range *p { txset[i], uncleset[i] = body.Transactions, body.Uncles } return txset, uncleset } // GetNodeDataPacket represents a trie node data query. type GetNodeDataPacket []common.Hash // NodeDataPacket is the network packet for trie node data distribution. type NodeDataPacket [][]byte // GetReceiptsPacket represents a block receipts query. type GetReceiptsPacket []common.Hash // ReceiptsPacket is the network packet for block receipts distribution. type ReceiptsPacket [][]*types.Receipt // NewPooledTransactionHashesPacket represents a transaction announcement packet. type NewPooledTransactionHashesPacket []common.Hash // GetPooledTransactionsPacket represents a transaction query. type GetPooledTransactionsPacket []common.Hash // PooledTransactionsPacket is the network packet for transaction distribution. type PooledTransactionsPacket []*types.Transaction func (*StatusPacket) Name() string { return "Status" } func (*StatusPacket) Kind() byte { return StatusMsg } func (*NewBlockHashesPacket) Name() string { return "NewBlockHashes" } func (*NewBlockHashesPacket) Kind() byte { return NewBlockHashesMsg } func (*TransactionsPacket) Name() string { return "Transactions" } func (*TransactionsPacket) Kind() byte { return TransactionsMsg } func (*GetBlockHeadersPacket) Name() string { return "GetBlockHeaders" } func (*GetBlockHeadersPacket) Kind() byte { return GetBlockHeadersMsg } func (*BlockHeadersPacket) Name() string { return "BlockHeaders" } func (*BlockHeadersPacket) Kind() byte { return BlockHeadersMsg } func (*GetBlockBodiesPacket) Name() string { return "GetBlockBodies" } func (*GetBlockBodiesPacket) Kind() byte { return GetBlockBodiesMsg } func (*BlockBodiesPacket) Name() string { return "BlockBodies" } func (*BlockBodiesPacket) Kind() byte { return BlockBodiesMsg } func (*NewBlockPacket) Name() string { return "NewBlock" } func (*NewBlockPacket) Kind() byte { return NewBlockMsg } func (*GetNodeDataPacket) Name() string { return "GetNodeData" } func (*GetNodeDataPacket) Kind() byte { return GetNodeDataMsg } func (*NodeDataPacket) Name() string { return "NodeData" } func (*NodeDataPacket) Kind() byte { return NodeDataMsg } func (*GetReceiptsPacket) Name() string { return "GetReceipts" } func (*GetReceiptsPacket) Kind() byte { return GetReceiptsMsg } func (*ReceiptsPacket) Name() string { return "Receipts" } func (*ReceiptsPacket) Kind() byte { return ReceiptsMsg } func (*NewPooledTransactionHashesPacket) Name() string { return "NewPooledTransactionHashes" } func (*NewPooledTransactionHashesPacket) Kind() byte { return NewPooledTransactionHashesMsg } func (*GetPooledTransactionsPacket) Name() string { return "GetPooledTransactions" } func (*GetPooledTransactionsPacket) Kind() byte { return GetPooledTransactionsMsg } func (*PooledTransactionsPacket) Name() string { return "PooledTransactions" } func (*PooledTransactionsPacket) Kind() byte { return PooledTransactionsMsg }