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 }