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