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 }