github.com/snowblossomcoin/go-ethereum@v1.9.25/les/protocol.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package les
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"math/big"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/crypto"
    28  	lpc "github.com/ethereum/go-ethereum/les/lespay/client"
    29  	"github.com/ethereum/go-ethereum/p2p/enode"
    30  	"github.com/ethereum/go-ethereum/rlp"
    31  )
    32  
    33  // Constants to match up protocol versions and messages
    34  const (
    35  	lpv2 = 2
    36  	lpv3 = 3
    37  )
    38  
    39  // Supported versions of the les protocol (first is primary)
    40  var (
    41  	ClientProtocolVersions    = []uint{lpv2, lpv3}
    42  	ServerProtocolVersions    = []uint{lpv2, lpv3}
    43  	AdvertiseProtocolVersions = []uint{lpv2} // clients are searching for the first advertised protocol in the list
    44  )
    45  
    46  // Number of implemented message corresponding to different protocol versions.
    47  var ProtocolLengths = map[uint]uint64{lpv2: 22, lpv3: 24}
    48  
    49  const (
    50  	NetworkId          = 1
    51  	ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
    52  )
    53  
    54  // les protocol message codes
    55  const (
    56  	// Protocol messages inherited from LPV1
    57  	StatusMsg          = 0x00
    58  	AnnounceMsg        = 0x01
    59  	GetBlockHeadersMsg = 0x02
    60  	BlockHeadersMsg    = 0x03
    61  	GetBlockBodiesMsg  = 0x04
    62  	BlockBodiesMsg     = 0x05
    63  	GetReceiptsMsg     = 0x06
    64  	ReceiptsMsg        = 0x07
    65  	GetCodeMsg         = 0x0a
    66  	CodeMsg            = 0x0b
    67  	// Protocol messages introduced in LPV2
    68  	GetProofsV2Msg         = 0x0f
    69  	ProofsV2Msg            = 0x10
    70  	GetHelperTrieProofsMsg = 0x11
    71  	HelperTrieProofsMsg    = 0x12
    72  	SendTxV2Msg            = 0x13
    73  	GetTxStatusMsg         = 0x14
    74  	TxStatusMsg            = 0x15
    75  	// Protocol messages introduced in LPV3
    76  	StopMsg   = 0x16
    77  	ResumeMsg = 0x17
    78  )
    79  
    80  type requestInfo struct {
    81  	name                          string
    82  	maxCount                      uint64
    83  	refBasketFirst, refBasketRest float64
    84  }
    85  
    86  // reqMapping maps an LES request to one or two lespay service vector entries.
    87  // If rest != -1 and the request type is used with amounts larger than one then the
    88  // first one of the multi-request is mapped to first while the rest is mapped to rest.
    89  type reqMapping struct {
    90  	first, rest int
    91  }
    92  
    93  var (
    94  	// requests describes the available LES request types and their initializing amounts
    95  	// in the lespay/client.ValueTracker reference basket. Initial values are estimates
    96  	// based on the same values as the server's default cost estimates (reqAvgTimeCost).
    97  	requests = map[uint64]requestInfo{
    98  		GetBlockHeadersMsg:     {"GetBlockHeaders", MaxHeaderFetch, 10, 1000},
    99  		GetBlockBodiesMsg:      {"GetBlockBodies", MaxBodyFetch, 1, 0},
   100  		GetReceiptsMsg:         {"GetReceipts", MaxReceiptFetch, 1, 0},
   101  		GetCodeMsg:             {"GetCode", MaxCodeFetch, 1, 0},
   102  		GetProofsV2Msg:         {"GetProofsV2", MaxProofsFetch, 10, 0},
   103  		GetHelperTrieProofsMsg: {"GetHelperTrieProofs", MaxHelperTrieProofsFetch, 10, 100},
   104  		SendTxV2Msg:            {"SendTxV2", MaxTxSend, 1, 0},
   105  		GetTxStatusMsg:         {"GetTxStatus", MaxTxStatus, 10, 0},
   106  	}
   107  	requestList    []lpc.RequestInfo
   108  	requestMapping map[uint32]reqMapping
   109  )
   110  
   111  // init creates a request list and mapping between protocol message codes and lespay
   112  // service vector indices.
   113  func init() {
   114  	requestMapping = make(map[uint32]reqMapping)
   115  	for code, req := range requests {
   116  		cost := reqAvgTimeCost[code]
   117  		rm := reqMapping{len(requestList), -1}
   118  		requestList = append(requestList, lpc.RequestInfo{
   119  			Name:       req.name + ".first",
   120  			InitAmount: req.refBasketFirst,
   121  			InitValue:  float64(cost.baseCost + cost.reqCost),
   122  		})
   123  		if req.refBasketRest != 0 {
   124  			rm.rest = len(requestList)
   125  			requestList = append(requestList, lpc.RequestInfo{
   126  				Name:       req.name + ".rest",
   127  				InitAmount: req.refBasketRest,
   128  				InitValue:  float64(cost.reqCost),
   129  			})
   130  		}
   131  		requestMapping[uint32(code)] = rm
   132  	}
   133  }
   134  
   135  type errCode int
   136  
   137  const (
   138  	ErrMsgTooLarge = iota
   139  	ErrDecode
   140  	ErrInvalidMsgCode
   141  	ErrProtocolVersionMismatch
   142  	ErrNetworkIdMismatch
   143  	ErrGenesisBlockMismatch
   144  	ErrNoStatusMsg
   145  	ErrExtraStatusMsg
   146  	ErrSuspendedPeer
   147  	ErrUselessPeer
   148  	ErrRequestRejected
   149  	ErrUnexpectedResponse
   150  	ErrInvalidResponse
   151  	ErrTooManyTimeouts
   152  	ErrMissingKey
   153  )
   154  
   155  func (e errCode) String() string {
   156  	return errorToString[int(e)]
   157  }
   158  
   159  // XXX change once legacy code is out
   160  var errorToString = map[int]string{
   161  	ErrMsgTooLarge:             "Message too long",
   162  	ErrDecode:                  "Invalid message",
   163  	ErrInvalidMsgCode:          "Invalid message code",
   164  	ErrProtocolVersionMismatch: "Protocol version mismatch",
   165  	ErrNetworkIdMismatch:       "NetworkId mismatch",
   166  	ErrGenesisBlockMismatch:    "Genesis block mismatch",
   167  	ErrNoStatusMsg:             "No status message",
   168  	ErrExtraStatusMsg:          "Extra status message",
   169  	ErrSuspendedPeer:           "Suspended peer",
   170  	ErrRequestRejected:         "Request rejected",
   171  	ErrUnexpectedResponse:      "Unexpected response",
   172  	ErrInvalidResponse:         "Invalid response",
   173  	ErrTooManyTimeouts:         "Too many request timeouts",
   174  	ErrMissingKey:              "Key missing from list",
   175  }
   176  
   177  // announceData is the network packet for the block announcements.
   178  type announceData struct {
   179  	Hash       common.Hash // Hash of one particular block being announced
   180  	Number     uint64      // Number of one particular block being announced
   181  	Td         *big.Int    // Total difficulty of one particular block being announced
   182  	ReorgDepth uint64
   183  	Update     keyValueList
   184  }
   185  
   186  // sanityCheck verifies that the values are reasonable, as a DoS protection
   187  func (a *announceData) sanityCheck() error {
   188  	if tdlen := a.Td.BitLen(); tdlen > 100 {
   189  		return fmt.Errorf("too large block TD: bitlen %d", tdlen)
   190  	}
   191  	return nil
   192  }
   193  
   194  // sign adds a signature to the block announcement by the given privKey
   195  func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
   196  	rlp, _ := rlp.EncodeToBytes(blockInfo{a.Hash, a.Number, a.Td})
   197  	sig, _ := crypto.Sign(crypto.Keccak256(rlp), privKey)
   198  	a.Update = a.Update.add("sign", sig)
   199  }
   200  
   201  // checkSignature verifies if the block announcement has a valid signature by the given pubKey
   202  func (a *announceData) checkSignature(id enode.ID, update keyValueMap) error {
   203  	var sig []byte
   204  	if err := update.get("sign", &sig); err != nil {
   205  		return err
   206  	}
   207  	rlp, _ := rlp.EncodeToBytes(blockInfo{a.Hash, a.Number, a.Td})
   208  	recPubkey, err := crypto.SigToPub(crypto.Keccak256(rlp), sig)
   209  	if err != nil {
   210  		return err
   211  	}
   212  	if id == enode.PubkeyToIDV4(recPubkey) {
   213  		return nil
   214  	}
   215  	return errors.New("wrong signature")
   216  }
   217  
   218  type blockInfo struct {
   219  	Hash   common.Hash // Hash of one particular block being announced
   220  	Number uint64      // Number of one particular block being announced
   221  	Td     *big.Int    // Total difficulty of one particular block being announced
   222  }
   223  
   224  // getBlockHeadersData represents a block header query.
   225  type getBlockHeadersData struct {
   226  	Origin  hashOrNumber // Block from which to retrieve headers
   227  	Amount  uint64       // Maximum number of headers to retrieve
   228  	Skip    uint64       // Blocks to skip between consecutive headers
   229  	Reverse bool         // Query direction (false = rising towards latest, true = falling towards genesis)
   230  }
   231  
   232  // hashOrNumber is a combined field for specifying an origin block.
   233  type hashOrNumber struct {
   234  	Hash   common.Hash // Block hash from which to retrieve headers (excludes Number)
   235  	Number uint64      // Block hash from which to retrieve headers (excludes Hash)
   236  }
   237  
   238  // EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the
   239  // two contained union fields.
   240  func (hn *hashOrNumber) EncodeRLP(w io.Writer) error {
   241  	if hn.Hash == (common.Hash{}) {
   242  		return rlp.Encode(w, hn.Number)
   243  	}
   244  	if hn.Number != 0 {
   245  		return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number)
   246  	}
   247  	return rlp.Encode(w, hn.Hash)
   248  }
   249  
   250  // DecodeRLP is a specialized decoder for hashOrNumber to decode the contents
   251  // into either a block hash or a block number.
   252  func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error {
   253  	_, size, _ := s.Kind()
   254  	origin, err := s.Raw()
   255  	if err == nil {
   256  		switch {
   257  		case size == 32:
   258  			err = rlp.DecodeBytes(origin, &hn.Hash)
   259  		case size <= 8:
   260  			err = rlp.DecodeBytes(origin, &hn.Number)
   261  		default:
   262  			err = fmt.Errorf("invalid input size %d for origin", size)
   263  		}
   264  	}
   265  	return err
   266  }
   267  
   268  // CodeData is the network response packet for a node data retrieval.
   269  type CodeData []struct {
   270  	Value []byte
   271  }