github.com/etherbanking/go-etherbanking@v1.7.1-0.20181009210156-cf649bca5aba/les/odr_requests.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 light implements on-demand retrieval capable state and chain objects 18 // for the Ethereum Light Client. 19 package les 20 21 import ( 22 "encoding/binary" 23 "errors" 24 "fmt" 25 26 "github.com/etherbanking/go-etherbanking/common" 27 "github.com/etherbanking/go-etherbanking/core" 28 "github.com/etherbanking/go-etherbanking/core/types" 29 "github.com/etherbanking/go-etherbanking/crypto" 30 "github.com/etherbanking/go-etherbanking/ethdb" 31 "github.com/etherbanking/go-etherbanking/light" 32 "github.com/etherbanking/go-etherbanking/log" 33 "github.com/etherbanking/go-etherbanking/rlp" 34 "github.com/etherbanking/go-etherbanking/trie" 35 ) 36 37 var ( 38 errInvalidMessageType = errors.New("invalid message type") 39 errMultipleEntries = errors.New("multiple response entries") 40 errHeaderUnavailable = errors.New("header unavailable") 41 errTxHashMismatch = errors.New("transaction hash mismatch") 42 errUncleHashMismatch = errors.New("uncle hash mismatch") 43 errReceiptHashMismatch = errors.New("receipt hash mismatch") 44 errDataHashMismatch = errors.New("data hash mismatch") 45 errCHTHashMismatch = errors.New("cht hash mismatch") 46 ) 47 48 type LesOdrRequest interface { 49 GetCost(*peer) uint64 50 CanSend(*peer) bool 51 Request(uint64, *peer) error 52 Validate(ethdb.Database, *Msg) error 53 } 54 55 func LesRequest(req light.OdrRequest) LesOdrRequest { 56 switch r := req.(type) { 57 case *light.BlockRequest: 58 return (*BlockRequest)(r) 59 case *light.ReceiptsRequest: 60 return (*ReceiptsRequest)(r) 61 case *light.TrieRequest: 62 return (*TrieRequest)(r) 63 case *light.CodeRequest: 64 return (*CodeRequest)(r) 65 case *light.ChtRequest: 66 return (*ChtRequest)(r) 67 default: 68 return nil 69 } 70 } 71 72 // BlockRequest is the ODR request type for block bodies 73 type BlockRequest light.BlockRequest 74 75 // GetCost returns the cost of the given ODR request according to the serving 76 // peer's cost table (implementation of LesOdrRequest) 77 func (r *BlockRequest) GetCost(peer *peer) uint64 { 78 return peer.GetRequestCost(GetBlockBodiesMsg, 1) 79 } 80 81 // CanSend tells if a certain peer is suitable for serving the given request 82 func (r *BlockRequest) CanSend(peer *peer) bool { 83 return peer.HasBlock(r.Hash, r.Number) 84 } 85 86 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 87 func (r *BlockRequest) Request(reqID uint64, peer *peer) error { 88 peer.Log().Debug("Requesting block body", "hash", r.Hash) 89 return peer.RequestBodies(reqID, r.GetCost(peer), []common.Hash{r.Hash}) 90 } 91 92 // Valid processes an ODR request reply message from the LES network 93 // returns true and stores results in memory if the message was a valid reply 94 // to the request (implementation of LesOdrRequest) 95 func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error { 96 log.Debug("Validating block body", "hash", r.Hash) 97 98 // Ensure we have a correct message with a single block body 99 if msg.MsgType != MsgBlockBodies { 100 return errInvalidMessageType 101 } 102 bodies := msg.Obj.([]*types.Body) 103 if len(bodies) != 1 { 104 return errMultipleEntries 105 } 106 body := bodies[0] 107 108 // Retrieve our stored header and validate block content against it 109 header := core.GetHeader(db, r.Hash, r.Number) 110 if header == nil { 111 return errHeaderUnavailable 112 } 113 if header.TxHash != types.DeriveSha(types.Transactions(body.Transactions)) { 114 return errTxHashMismatch 115 } 116 if header.UncleHash != types.CalcUncleHash(body.Uncles) { 117 return errUncleHashMismatch 118 } 119 // Validations passed, encode and store RLP 120 data, err := rlp.EncodeToBytes(body) 121 if err != nil { 122 return err 123 } 124 r.Rlp = data 125 return nil 126 } 127 128 // ReceiptsRequest is the ODR request type for block receipts by block hash 129 type ReceiptsRequest light.ReceiptsRequest 130 131 // GetCost returns the cost of the given ODR request according to the serving 132 // peer's cost table (implementation of LesOdrRequest) 133 func (r *ReceiptsRequest) GetCost(peer *peer) uint64 { 134 return peer.GetRequestCost(GetReceiptsMsg, 1) 135 } 136 137 // CanSend tells if a certain peer is suitable for serving the given request 138 func (r *ReceiptsRequest) CanSend(peer *peer) bool { 139 return peer.HasBlock(r.Hash, r.Number) 140 } 141 142 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 143 func (r *ReceiptsRequest) Request(reqID uint64, peer *peer) error { 144 peer.Log().Debug("Requesting block receipts", "hash", r.Hash) 145 return peer.RequestReceipts(reqID, r.GetCost(peer), []common.Hash{r.Hash}) 146 } 147 148 // Valid processes an ODR request reply message from the LES network 149 // returns true and stores results in memory if the message was a valid reply 150 // to the request (implementation of LesOdrRequest) 151 func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error { 152 log.Debug("Validating block receipts", "hash", r.Hash) 153 154 // Ensure we have a correct message with a single block receipt 155 if msg.MsgType != MsgReceipts { 156 return errInvalidMessageType 157 } 158 receipts := msg.Obj.([]types.Receipts) 159 if len(receipts) != 1 { 160 return errMultipleEntries 161 } 162 receipt := receipts[0] 163 164 // Retrieve our stored header and validate receipt content against it 165 header := core.GetHeader(db, r.Hash, r.Number) 166 if header == nil { 167 return errHeaderUnavailable 168 } 169 if header.ReceiptHash != types.DeriveSha(receipt) { 170 return errReceiptHashMismatch 171 } 172 // Validations passed, store and return 173 r.Receipts = receipt 174 return nil 175 } 176 177 type ProofReq struct { 178 BHash common.Hash 179 AccKey, Key []byte 180 FromLevel uint 181 } 182 183 // ODR request type for state/storage trie entries, see LesOdrRequest interface 184 type TrieRequest light.TrieRequest 185 186 // GetCost returns the cost of the given ODR request according to the serving 187 // peer's cost table (implementation of LesOdrRequest) 188 func (r *TrieRequest) GetCost(peer *peer) uint64 { 189 return peer.GetRequestCost(GetProofsMsg, 1) 190 } 191 192 // CanSend tells if a certain peer is suitable for serving the given request 193 func (r *TrieRequest) CanSend(peer *peer) bool { 194 return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber) 195 } 196 197 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 198 func (r *TrieRequest) Request(reqID uint64, peer *peer) error { 199 peer.Log().Debug("Requesting trie proof", "root", r.Id.Root, "key", r.Key) 200 req := &ProofReq{ 201 BHash: r.Id.BlockHash, 202 AccKey: r.Id.AccKey, 203 Key: r.Key, 204 } 205 return peer.RequestProofs(reqID, r.GetCost(peer), []*ProofReq{req}) 206 } 207 208 // Valid processes an ODR request reply message from the LES network 209 // returns true and stores results in memory if the message was a valid reply 210 // to the request (implementation of LesOdrRequest) 211 func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error { 212 log.Debug("Validating trie proof", "root", r.Id.Root, "key", r.Key) 213 214 // Ensure we have a correct message with a single proof 215 if msg.MsgType != MsgProofs { 216 return errInvalidMessageType 217 } 218 proofs := msg.Obj.([][]rlp.RawValue) 219 if len(proofs) != 1 { 220 return errMultipleEntries 221 } 222 // Verify the proof and store if checks out 223 if _, err := trie.VerifyProof(r.Id.Root, r.Key, proofs[0]); err != nil { 224 return fmt.Errorf("merkle proof verification failed: %v", err) 225 } 226 r.Proof = proofs[0] 227 return nil 228 } 229 230 type CodeReq struct { 231 BHash common.Hash 232 AccKey []byte 233 } 234 235 // ODR request type for node data (used for retrieving contract code), see LesOdrRequest interface 236 type CodeRequest light.CodeRequest 237 238 // GetCost returns the cost of the given ODR request according to the serving 239 // peer's cost table (implementation of LesOdrRequest) 240 func (r *CodeRequest) GetCost(peer *peer) uint64 { 241 return peer.GetRequestCost(GetCodeMsg, 1) 242 } 243 244 // CanSend tells if a certain peer is suitable for serving the given request 245 func (r *CodeRequest) CanSend(peer *peer) bool { 246 return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber) 247 } 248 249 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 250 func (r *CodeRequest) Request(reqID uint64, peer *peer) error { 251 peer.Log().Debug("Requesting code data", "hash", r.Hash) 252 req := &CodeReq{ 253 BHash: r.Id.BlockHash, 254 AccKey: r.Id.AccKey, 255 } 256 return peer.RequestCode(reqID, r.GetCost(peer), []*CodeReq{req}) 257 } 258 259 // Valid processes an ODR request reply message from the LES network 260 // returns true and stores results in memory if the message was a valid reply 261 // to the request (implementation of LesOdrRequest) 262 func (r *CodeRequest) Validate(db ethdb.Database, msg *Msg) error { 263 log.Debug("Validating code data", "hash", r.Hash) 264 265 // Ensure we have a correct message with a single code element 266 if msg.MsgType != MsgCode { 267 return errInvalidMessageType 268 } 269 reply := msg.Obj.([][]byte) 270 if len(reply) != 1 { 271 return errMultipleEntries 272 } 273 data := reply[0] 274 275 // Verify the data and store if checks out 276 if hash := crypto.Keccak256Hash(data); r.Hash != hash { 277 return errDataHashMismatch 278 } 279 r.Data = data 280 return nil 281 } 282 283 type ChtReq struct { 284 ChtNum, BlockNum, FromLevel uint64 285 } 286 287 type ChtResp struct { 288 Header *types.Header 289 Proof []rlp.RawValue 290 } 291 292 // ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface 293 type ChtRequest light.ChtRequest 294 295 // GetCost returns the cost of the given ODR request according to the serving 296 // peer's cost table (implementation of LesOdrRequest) 297 func (r *ChtRequest) GetCost(peer *peer) uint64 { 298 return peer.GetRequestCost(GetHeaderProofsMsg, 1) 299 } 300 301 // CanSend tells if a certain peer is suitable for serving the given request 302 func (r *ChtRequest) CanSend(peer *peer) bool { 303 peer.lock.RLock() 304 defer peer.lock.RUnlock() 305 306 return r.ChtNum <= (peer.headInfo.Number-light.ChtConfirmations)/light.ChtFrequency 307 } 308 309 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 310 func (r *ChtRequest) Request(reqID uint64, peer *peer) error { 311 peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.BlockNum) 312 req := &ChtReq{ 313 ChtNum: r.ChtNum, 314 BlockNum: r.BlockNum, 315 } 316 return peer.RequestHeaderProofs(reqID, r.GetCost(peer), []*ChtReq{req}) 317 } 318 319 // Valid processes an ODR request reply message from the LES network 320 // returns true and stores results in memory if the message was a valid reply 321 // to the request (implementation of LesOdrRequest) 322 func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error { 323 log.Debug("Validating CHT", "cht", r.ChtNum, "block", r.BlockNum) 324 325 // Ensure we have a correct message with a single proof element 326 if msg.MsgType != MsgHeaderProofs { 327 return errInvalidMessageType 328 } 329 proofs := msg.Obj.([]ChtResp) 330 if len(proofs) != 1 { 331 return errMultipleEntries 332 } 333 proof := proofs[0] 334 335 // Verify the CHT 336 var encNumber [8]byte 337 binary.BigEndian.PutUint64(encNumber[:], r.BlockNum) 338 339 value, err := trie.VerifyProof(r.ChtRoot, encNumber[:], proof.Proof) 340 if err != nil { 341 return err 342 } 343 var node light.ChtNode 344 if err := rlp.DecodeBytes(value, &node); err != nil { 345 return err 346 } 347 if node.Hash != proof.Header.Hash() { 348 return errCHTHashMismatch 349 } 350 // Verifications passed, store and return 351 r.Header = proof.Header 352 r.Proof = proof.Proof 353 r.Td = node.Td 354 355 return nil 356 }