This commit splits the eth package, separating the handling of eth and snap protocols. It also includes the capability to run snap sync (https://github.com/ethereum/devp2p/blob/master/caps/snap.md) , but does not enable it by default. Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Martin Holst Swende <martin@swende.se>
		
			
				
	
	
		
			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 }
 |