* eth: request ID based message dispatcher * eth: fix dispatcher cancellation, rework fetchers idleness tracker * eth/downloader: drop peers who refuse to serve advertised chains
		
			
				
	
	
		
			219 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			8.3 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 snap
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 
 | |
| 	"github.com/ethereum/go-ethereum/common"
 | |
| 	"github.com/ethereum/go-ethereum/core/state/snapshot"
 | |
| 	"github.com/ethereum/go-ethereum/rlp"
 | |
| )
 | |
| 
 | |
| // Constants to match up protocol versions and messages
 | |
| const (
 | |
| 	SNAP1 = 1
 | |
| )
 | |
| 
 | |
| // ProtocolName is the official short name of the `snap` protocol used during
 | |
| // devp2p capability negotiation.
 | |
| const ProtocolName = "snap"
 | |
| 
 | |
| // ProtocolVersions are the supported versions of the `snap` protocol (first
 | |
| // is primary).
 | |
| var ProtocolVersions = []uint{SNAP1}
 | |
| 
 | |
| // protocolLengths are the number of implemented message corresponding to
 | |
| // different protocol versions.
 | |
| var protocolLengths = map[uint]uint64{SNAP1: 8}
 | |
| 
 | |
| // maxMessageSize is the maximum cap on the size of a protocol message.
 | |
| const maxMessageSize = 10 * 1024 * 1024
 | |
| 
 | |
| const (
 | |
| 	GetAccountRangeMsg  = 0x00
 | |
| 	AccountRangeMsg     = 0x01
 | |
| 	GetStorageRangesMsg = 0x02
 | |
| 	StorageRangesMsg    = 0x03
 | |
| 	GetByteCodesMsg     = 0x04
 | |
| 	ByteCodesMsg        = 0x05
 | |
| 	GetTrieNodesMsg     = 0x06
 | |
| 	TrieNodesMsg        = 0x07
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	errMsgTooLarge    = errors.New("message too long")
 | |
| 	errDecode         = errors.New("invalid message")
 | |
| 	errInvalidMsgCode = errors.New("invalid message code")
 | |
| 	errBadRequest     = errors.New("bad request")
 | |
| )
 | |
| 
 | |
| // Packet represents a p2p message in the `snap` protocol.
 | |
| type Packet interface {
 | |
| 	Name() string // Name returns a string corresponding to the message type.
 | |
| 	Kind() byte   // Kind returns the message type.
 | |
| }
 | |
| 
 | |
| // GetAccountRangePacket represents an account query.
 | |
| type GetAccountRangePacket struct {
 | |
| 	ID     uint64      // Request ID to match up responses with
 | |
| 	Root   common.Hash // Root hash of the account trie to serve
 | |
| 	Origin common.Hash // Hash of the first account to retrieve
 | |
| 	Limit  common.Hash // Hash of the last account to retrieve
 | |
| 	Bytes  uint64      // Soft limit at which to stop returning data
 | |
| }
 | |
| 
 | |
| // AccountRangePacket represents an account query response.
 | |
| type AccountRangePacket struct {
 | |
| 	ID       uint64         // ID of the request this is a response for
 | |
| 	Accounts []*AccountData // List of consecutive accounts from the trie
 | |
| 	Proof    [][]byte       // List of trie nodes proving the account range
 | |
| }
 | |
| 
 | |
| // AccountData represents a single account in a query response.
 | |
| type AccountData struct {
 | |
| 	Hash common.Hash  // Hash of the account
 | |
| 	Body rlp.RawValue // Account body in slim format
 | |
| }
 | |
| 
 | |
| // Unpack retrieves the accounts from the range packet and converts from slim
 | |
| // wire representation to consensus format. The returned data is RLP encoded
 | |
| // since it's expected to be serialized to disk without further interpretation.
 | |
| //
 | |
| // Note, this method does a round of RLP decoding and reencoding, so only use it
 | |
| // once and cache the results if need be. Ideally discard the packet afterwards
 | |
| // to not double the memory use.
 | |
| func (p *AccountRangePacket) Unpack() ([]common.Hash, [][]byte, error) {
 | |
| 	var (
 | |
| 		hashes   = make([]common.Hash, len(p.Accounts))
 | |
| 		accounts = make([][]byte, len(p.Accounts))
 | |
| 	)
 | |
| 	for i, acc := range p.Accounts {
 | |
| 		val, err := snapshot.FullAccountRLP(acc.Body)
 | |
| 		if err != nil {
 | |
| 			return nil, nil, fmt.Errorf("invalid account %x: %v", acc.Body, err)
 | |
| 		}
 | |
| 		hashes[i], accounts[i] = acc.Hash, val
 | |
| 	}
 | |
| 	return hashes, accounts, nil
 | |
| }
 | |
| 
 | |
| // GetStorageRangesPacket represents an storage slot query.
 | |
| type GetStorageRangesPacket struct {
 | |
| 	ID       uint64        // Request ID to match up responses with
 | |
| 	Root     common.Hash   // Root hash of the account trie to serve
 | |
| 	Accounts []common.Hash // Account hashes of the storage tries to serve
 | |
| 	Origin   []byte        // Hash of the first storage slot to retrieve (large contract mode)
 | |
| 	Limit    []byte        // Hash of the last storage slot to retrieve (large contract mode)
 | |
| 	Bytes    uint64        // Soft limit at which to stop returning data
 | |
| }
 | |
| 
 | |
| // StorageRangesPacket represents a storage slot query response.
 | |
| type StorageRangesPacket struct {
 | |
| 	ID    uint64           // ID of the request this is a response for
 | |
| 	Slots [][]*StorageData // Lists of consecutive storage slots for the requested accounts
 | |
| 	Proof [][]byte         // Merkle proofs for the *last* slot range, if it's incomplete
 | |
| }
 | |
| 
 | |
| // StorageData represents a single storage slot in a query response.
 | |
| type StorageData struct {
 | |
| 	Hash common.Hash // Hash of the storage slot
 | |
| 	Body []byte      // Data content of the slot
 | |
| }
 | |
| 
 | |
| // Unpack retrieves the storage slots from the range packet and returns them in
 | |
| // a split flat format that's more consistent with the internal data structures.
 | |
| func (p *StorageRangesPacket) Unpack() ([][]common.Hash, [][][]byte) {
 | |
| 	var (
 | |
| 		hashset = make([][]common.Hash, len(p.Slots))
 | |
| 		slotset = make([][][]byte, len(p.Slots))
 | |
| 	)
 | |
| 	for i, slots := range p.Slots {
 | |
| 		hashset[i] = make([]common.Hash, len(slots))
 | |
| 		slotset[i] = make([][]byte, len(slots))
 | |
| 		for j, slot := range slots {
 | |
| 			hashset[i][j] = slot.Hash
 | |
| 			slotset[i][j] = slot.Body
 | |
| 		}
 | |
| 	}
 | |
| 	return hashset, slotset
 | |
| }
 | |
| 
 | |
| // GetByteCodesPacket represents a contract bytecode query.
 | |
| type GetByteCodesPacket struct {
 | |
| 	ID     uint64        // Request ID to match up responses with
 | |
| 	Hashes []common.Hash // Code hashes to retrieve the code for
 | |
| 	Bytes  uint64        // Soft limit at which to stop returning data
 | |
| }
 | |
| 
 | |
| // ByteCodesPacket represents a contract bytecode query response.
 | |
| type ByteCodesPacket struct {
 | |
| 	ID    uint64   // ID of the request this is a response for
 | |
| 	Codes [][]byte // Requested contract bytecodes
 | |
| }
 | |
| 
 | |
| // GetTrieNodesPacket represents a state trie node query.
 | |
| type GetTrieNodesPacket struct {
 | |
| 	ID    uint64            // Request ID to match up responses with
 | |
| 	Root  common.Hash       // Root hash of the account trie to serve
 | |
| 	Paths []TrieNodePathSet // Trie node hashes to retrieve the nodes for
 | |
| 	Bytes uint64            // Soft limit at which to stop returning data
 | |
| }
 | |
| 
 | |
| // TrieNodePathSet is a list of trie node paths to retrieve. A naive way to
 | |
| // represent trie nodes would be a simple list of `account || storage` path
 | |
| // segments concatenated, but that would be very wasteful on the network.
 | |
| //
 | |
| // Instead, this array special cases the first element as the path in the
 | |
| // account trie and the remaining elements as paths in the storage trie. To
 | |
| // address an account node, the slice should have a length of 1 consisting
 | |
| // of only the account path. There's no need to be able to address both an
 | |
| // account node and a storage node in the same request as it cannot happen
 | |
| // that a slot is accessed before the account path is fully expanded.
 | |
| type TrieNodePathSet [][]byte
 | |
| 
 | |
| // TrieNodesPacket represents a state trie node query response.
 | |
| type TrieNodesPacket struct {
 | |
| 	ID    uint64   // ID of the request this is a response for
 | |
| 	Nodes [][]byte // Requested state trie nodes
 | |
| }
 | |
| 
 | |
| func (*GetAccountRangePacket) Name() string { return "GetAccountRange" }
 | |
| func (*GetAccountRangePacket) Kind() byte   { return GetAccountRangeMsg }
 | |
| 
 | |
| func (*AccountRangePacket) Name() string { return "AccountRange" }
 | |
| func (*AccountRangePacket) Kind() byte   { return AccountRangeMsg }
 | |
| 
 | |
| func (*GetStorageRangesPacket) Name() string { return "GetStorageRanges" }
 | |
| func (*GetStorageRangesPacket) Kind() byte   { return GetStorageRangesMsg }
 | |
| 
 | |
| func (*StorageRangesPacket) Name() string { return "StorageRanges" }
 | |
| func (*StorageRangesPacket) Kind() byte   { return StorageRangesMsg }
 | |
| 
 | |
| func (*GetByteCodesPacket) Name() string { return "GetByteCodes" }
 | |
| func (*GetByteCodesPacket) Kind() byte   { return GetByteCodesMsg }
 | |
| 
 | |
| func (*ByteCodesPacket) Name() string { return "ByteCodes" }
 | |
| func (*ByteCodesPacket) Kind() byte   { return ByteCodesMsg }
 | |
| 
 | |
| func (*GetTrieNodesPacket) Name() string { return "GetTrieNodes" }
 | |
| func (*GetTrieNodesPacket) Kind() byte   { return GetTrieNodesMsg }
 | |
| 
 | |
| func (*TrieNodesPacket) Name() string { return "TrieNodes" }
 | |
| func (*TrieNodesPacket) Kind() byte   { return TrieNodesMsg }
 |