github.com/core-coin/go-core/v2@v2.1.9/les/protocol.go (about)

     1  // Copyright 2016 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package les
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"math/big"
    24  
    25  	"github.com/core-coin/go-core/v2/common"
    26  	"github.com/core-coin/go-core/v2/crypto"
    27  	lpc "github.com/core-coin/go-core/v2/les/lespay/client"
    28  	"github.com/core-coin/go-core/v2/p2p/enode"
    29  	"github.com/core-coin/go-core/v2/rlp"
    30  )
    31  
    32  // Constants to match up protocol versions and messages
    33  const (
    34  	lpv2 = 2
    35  	lpv3 = 3
    36  	lpv4 = 4
    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, lpv4: 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  	ErrForkIDRejected
   154  )
   155  
   156  func (e errCode) String() string {
   157  	return errorToString[int(e)]
   158  }
   159  
   160  // XXX change once legacy code is out
   161  var errorToString = map[int]string{
   162  	ErrMsgTooLarge:             "Message too long",
   163  	ErrDecode:                  "Invalid message",
   164  	ErrInvalidMsgCode:          "Invalid message code",
   165  	ErrProtocolVersionMismatch: "Protocol version mismatch",
   166  	ErrNetworkIdMismatch:       "NetworkId mismatch",
   167  	ErrGenesisBlockMismatch:    "Genesis block mismatch",
   168  	ErrNoStatusMsg:             "No status message",
   169  	ErrExtraStatusMsg:          "Extra status message",
   170  	ErrSuspendedPeer:           "Suspended peer",
   171  	ErrRequestRejected:         "Request rejected",
   172  	ErrUnexpectedResponse:      "Unexpected response",
   173  	ErrInvalidResponse:         "Invalid response",
   174  	ErrTooManyTimeouts:         "Too many request timeouts",
   175  	ErrMissingKey:              "Key missing from list",
   176  	ErrForkIDRejected:          "ForkID rejected",
   177  }
   178  
   179  // announceData is the network packet for the block announcements.
   180  type announceData struct {
   181  	Hash       common.Hash // Hash of one particular block being announced
   182  	Number     uint64      // Number of one particular block being announced
   183  	Td         *big.Int    // Total difficulty of one particular block being announced
   184  	ReorgDepth uint64
   185  	Update     keyValueList
   186  }
   187  
   188  // sanityCheck verifies that the values are reasonable, as a DoS protection
   189  func (a *announceData) sanityCheck() error {
   190  	if tdlen := a.Td.BitLen(); tdlen > 100 {
   191  		return fmt.Errorf("too large block TD: bitlen %d", tdlen)
   192  	}
   193  	return nil
   194  }
   195  
   196  // sign adds a signature to the block announcement by the given privKey
   197  func (a *announceData) sign(privKey *crypto.PrivateKey) {
   198  	rlp, _ := rlp.EncodeToBytes(blockInfo{a.Hash, a.Number, a.Td})
   199  	sig, _ := crypto.Sign(crypto.SHA3(rlp), privKey)
   200  	a.Update = a.Update.add("sign", sig)
   201  }
   202  
   203  // checkSignature verifies if the block announcement has a valid signature by the given pubKey
   204  func (a *announceData) checkSignature(id enode.ID, update keyValueMap) error {
   205  	var sig []byte
   206  	if err := update.get("sign", &sig); err != nil {
   207  		return err
   208  	}
   209  	rlp, _ := rlp.EncodeToBytes(blockInfo{a.Hash, a.Number, a.Td})
   210  	recPubkey, err := crypto.SigToPub(crypto.SHA3(rlp), sig)
   211  	if err != nil {
   212  		return err
   213  	}
   214  	if id == enode.PubkeyToIDV4(recPubkey) {
   215  		return nil
   216  	}
   217  	return errors.New("wrong signature")
   218  }
   219  
   220  type blockInfo struct {
   221  	Hash   common.Hash // Hash of one particular block being announced
   222  	Number uint64      // Number of one particular block being announced
   223  	Td     *big.Int    // Total difficulty of one particular block being announced
   224  }
   225  
   226  // getBlockHeadersData represents a block header query.
   227  type getBlockHeadersData struct {
   228  	Origin  hashOrNumber // Block from which to retrieve headers
   229  	Amount  uint64       // Maximum number of headers to retrieve
   230  	Skip    uint64       // Blocks to skip between consecutive headers
   231  	Reverse bool         // Query direction (false = rising towards latest, true = falling towards genesis)
   232  }
   233  
   234  // hashOrNumber is a combined field for specifying an origin block.
   235  type hashOrNumber struct {
   236  	Hash   common.Hash // Block hash from which to retrieve headers (excludes Number)
   237  	Number uint64      // Block hash from which to retrieve headers (excludes Hash)
   238  }
   239  
   240  // EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the
   241  // two contained union fields.
   242  func (hn *hashOrNumber) EncodeRLP(w io.Writer) error {
   243  	if hn.Hash == (common.Hash{}) {
   244  		return rlp.Encode(w, hn.Number)
   245  	}
   246  	if hn.Number != 0 {
   247  		return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number)
   248  	}
   249  	return rlp.Encode(w, hn.Hash)
   250  }
   251  
   252  // DecodeRLP is a specialized decoder for hashOrNumber to decode the contents
   253  // into either a block hash or a block number.
   254  func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error {
   255  	_, size, _ := s.Kind()
   256  	origin, err := s.Raw()
   257  	if err == nil {
   258  		switch {
   259  		case size == 32:
   260  			err = rlp.DecodeBytes(origin, &hn.Hash)
   261  		case size <= 8:
   262  			err = rlp.DecodeBytes(origin, &hn.Number)
   263  		default:
   264  			err = fmt.Errorf("invalid input size %d for origin", size)
   265  		}
   266  	}
   267  	return err
   268  }
   269  
   270  // CodeData is the network response packet for a node data retrieval.
   271  type CodeData []struct {
   272  	Value []byte
   273  }