server_requests.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. // Copyright 2021 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. package les
  17. import (
  18. "encoding/binary"
  19. "encoding/json"
  20. "github.com/ethereum/go-ethereum/common"
  21. "github.com/ethereum/go-ethereum/core"
  22. "github.com/ethereum/go-ethereum/core/state"
  23. "github.com/ethereum/go-ethereum/core/types"
  24. "github.com/ethereum/go-ethereum/light"
  25. "github.com/ethereum/go-ethereum/log"
  26. "github.com/ethereum/go-ethereum/metrics"
  27. "github.com/ethereum/go-ethereum/rlp"
  28. "github.com/ethereum/go-ethereum/trie"
  29. )
  30. // serverBackend defines the backend functions needed for serving LES requests
  31. type serverBackend interface {
  32. ArchiveMode() bool
  33. AddTxsSync() bool
  34. BlockChain() *core.BlockChain
  35. TxPool() *core.TxPool
  36. GetHelperTrie(typ uint, index uint64) *trie.Trie
  37. }
  38. // Decoder is implemented by the messages passed to the handler functions
  39. type Decoder interface {
  40. Decode(val interface{}) error
  41. }
  42. // RequestType is a static struct that describes an LES request type and references
  43. // its handler function.
  44. type RequestType struct {
  45. Name string
  46. MaxCount uint64
  47. InPacketsMeter, InTrafficMeter, OutPacketsMeter, OutTrafficMeter metrics.Meter
  48. ServingTimeMeter metrics.Timer
  49. Handle func(msg Decoder) (serve serveRequestFn, reqID, amount uint64, err error)
  50. }
  51. // serveRequestFn is returned by the request handler functions after decoding the request.
  52. // This function does the actual request serving using the supplied backend. waitOrStop is
  53. // called between serving individual request items and may block if the serving process
  54. // needs to be throttled. If it returns false then the process is terminated.
  55. // The reply is not sent by this function yet. The flow control feedback value is supplied
  56. // by the protocol handler when calling the send function of the returned reply struct.
  57. type serveRequestFn func(backend serverBackend, peer *clientPeer, waitOrStop func() bool) *reply
  58. // Les3 contains the request types supported by les/2 and les/3
  59. var Les3 = map[uint64]RequestType{
  60. GetBlockHeadersMsg: {
  61. Name: "block header request",
  62. MaxCount: MaxHeaderFetch,
  63. InPacketsMeter: miscInHeaderPacketsMeter,
  64. InTrafficMeter: miscInHeaderTrafficMeter,
  65. OutPacketsMeter: miscOutHeaderPacketsMeter,
  66. OutTrafficMeter: miscOutHeaderTrafficMeter,
  67. ServingTimeMeter: miscServingTimeHeaderTimer,
  68. Handle: handleGetBlockHeaders,
  69. },
  70. GetBlockBodiesMsg: {
  71. Name: "block bodies request",
  72. MaxCount: MaxBodyFetch,
  73. InPacketsMeter: miscInBodyPacketsMeter,
  74. InTrafficMeter: miscInBodyTrafficMeter,
  75. OutPacketsMeter: miscOutBodyPacketsMeter,
  76. OutTrafficMeter: miscOutBodyTrafficMeter,
  77. ServingTimeMeter: miscServingTimeBodyTimer,
  78. Handle: handleGetBlockBodies,
  79. },
  80. GetCodeMsg: {
  81. Name: "code request",
  82. MaxCount: MaxCodeFetch,
  83. InPacketsMeter: miscInCodePacketsMeter,
  84. InTrafficMeter: miscInCodeTrafficMeter,
  85. OutPacketsMeter: miscOutCodePacketsMeter,
  86. OutTrafficMeter: miscOutCodeTrafficMeter,
  87. ServingTimeMeter: miscServingTimeCodeTimer,
  88. Handle: handleGetCode,
  89. },
  90. GetReceiptsMsg: {
  91. Name: "receipts request",
  92. MaxCount: MaxReceiptFetch,
  93. InPacketsMeter: miscInReceiptPacketsMeter,
  94. InTrafficMeter: miscInReceiptTrafficMeter,
  95. OutPacketsMeter: miscOutReceiptPacketsMeter,
  96. OutTrafficMeter: miscOutReceiptTrafficMeter,
  97. ServingTimeMeter: miscServingTimeReceiptTimer,
  98. Handle: handleGetReceipts,
  99. },
  100. GetProofsV2Msg: {
  101. Name: "les/2 proofs request",
  102. MaxCount: MaxProofsFetch,
  103. InPacketsMeter: miscInTrieProofPacketsMeter,
  104. InTrafficMeter: miscInTrieProofTrafficMeter,
  105. OutPacketsMeter: miscOutTrieProofPacketsMeter,
  106. OutTrafficMeter: miscOutTrieProofTrafficMeter,
  107. ServingTimeMeter: miscServingTimeTrieProofTimer,
  108. Handle: handleGetProofs,
  109. },
  110. GetHelperTrieProofsMsg: {
  111. Name: "helper trie proof request",
  112. MaxCount: MaxHelperTrieProofsFetch,
  113. InPacketsMeter: miscInHelperTriePacketsMeter,
  114. InTrafficMeter: miscInHelperTrieTrafficMeter,
  115. OutPacketsMeter: miscOutHelperTriePacketsMeter,
  116. OutTrafficMeter: miscOutHelperTrieTrafficMeter,
  117. ServingTimeMeter: miscServingTimeHelperTrieTimer,
  118. Handle: handleGetHelperTrieProofs,
  119. },
  120. SendTxV2Msg: {
  121. Name: "new transactions",
  122. MaxCount: MaxTxSend,
  123. InPacketsMeter: miscInTxsPacketsMeter,
  124. InTrafficMeter: miscInTxsTrafficMeter,
  125. OutPacketsMeter: miscOutTxsPacketsMeter,
  126. OutTrafficMeter: miscOutTxsTrafficMeter,
  127. ServingTimeMeter: miscServingTimeTxTimer,
  128. Handle: handleSendTx,
  129. },
  130. GetTxStatusMsg: {
  131. Name: "transaction status query request",
  132. MaxCount: MaxTxStatus,
  133. InPacketsMeter: miscInTxStatusPacketsMeter,
  134. InTrafficMeter: miscInTxStatusTrafficMeter,
  135. OutPacketsMeter: miscOutTxStatusPacketsMeter,
  136. OutTrafficMeter: miscOutTxStatusTrafficMeter,
  137. ServingTimeMeter: miscServingTimeTxStatusTimer,
  138. Handle: handleGetTxStatus,
  139. },
  140. }
  141. // handleGetBlockHeaders handles a block header request
  142. func handleGetBlockHeaders(msg Decoder) (serveRequestFn, uint64, uint64, error) {
  143. var r GetBlockHeadersPacket
  144. if err := msg.Decode(&r); err != nil {
  145. return nil, 0, 0, err
  146. }
  147. return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
  148. // Gather headers until the fetch or network limits is reached
  149. var (
  150. bc = backend.BlockChain()
  151. hashMode = r.Query.Origin.Hash != (common.Hash{})
  152. first = true
  153. maxNonCanonical = uint64(100)
  154. bytes common.StorageSize
  155. headers []*types.Header
  156. unknown bool
  157. )
  158. for !unknown && len(headers) < int(r.Query.Amount) && bytes < softResponseLimit {
  159. if !first && !waitOrStop() {
  160. return nil
  161. }
  162. // Retrieve the next header satisfying the r
  163. var origin *types.Header
  164. if hashMode {
  165. if first {
  166. origin = bc.GetHeaderByHash(r.Query.Origin.Hash)
  167. if origin != nil {
  168. r.Query.Origin.Number = origin.Number.Uint64()
  169. }
  170. } else {
  171. origin = bc.GetHeader(r.Query.Origin.Hash, r.Query.Origin.Number)
  172. }
  173. } else {
  174. origin = bc.GetHeaderByNumber(r.Query.Origin.Number)
  175. }
  176. if origin == nil {
  177. break
  178. }
  179. headers = append(headers, origin)
  180. bytes += estHeaderRlpSize
  181. // Advance to the next header of the r
  182. switch {
  183. case hashMode && r.Query.Reverse:
  184. // Hash based traversal towards the genesis block
  185. ancestor := r.Query.Skip + 1
  186. if ancestor == 0 {
  187. unknown = true
  188. } else {
  189. r.Query.Origin.Hash, r.Query.Origin.Number = bc.GetAncestor(r.Query.Origin.Hash, r.Query.Origin.Number, ancestor, &maxNonCanonical)
  190. unknown = r.Query.Origin.Hash == common.Hash{}
  191. }
  192. case hashMode && !r.Query.Reverse:
  193. // Hash based traversal towards the leaf block
  194. var (
  195. current = origin.Number.Uint64()
  196. next = current + r.Query.Skip + 1
  197. )
  198. if next <= current {
  199. infos, _ := json.Marshal(p.Peer.Info())
  200. p.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", r.Query.Skip, "next", next, "attacker", string(infos))
  201. unknown = true
  202. } else {
  203. if header := bc.GetHeaderByNumber(next); header != nil {
  204. nextHash := header.Hash()
  205. expOldHash, _ := bc.GetAncestor(nextHash, next, r.Query.Skip+1, &maxNonCanonical)
  206. if expOldHash == r.Query.Origin.Hash {
  207. r.Query.Origin.Hash, r.Query.Origin.Number = nextHash, next
  208. } else {
  209. unknown = true
  210. }
  211. } else {
  212. unknown = true
  213. }
  214. }
  215. case r.Query.Reverse:
  216. // Number based traversal towards the genesis block
  217. if r.Query.Origin.Number >= r.Query.Skip+1 {
  218. r.Query.Origin.Number -= r.Query.Skip + 1
  219. } else {
  220. unknown = true
  221. }
  222. case !r.Query.Reverse:
  223. // Number based traversal towards the leaf block
  224. r.Query.Origin.Number += r.Query.Skip + 1
  225. }
  226. first = false
  227. }
  228. return p.replyBlockHeaders(r.ReqID, headers)
  229. }, r.ReqID, r.Query.Amount, nil
  230. }
  231. // handleGetBlockBodies handles a block body request
  232. func handleGetBlockBodies(msg Decoder) (serveRequestFn, uint64, uint64, error) {
  233. var r GetBlockBodiesPacket
  234. if err := msg.Decode(&r); err != nil {
  235. return nil, 0, 0, err
  236. }
  237. return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
  238. var (
  239. bytes int
  240. bodies []rlp.RawValue
  241. )
  242. bc := backend.BlockChain()
  243. for i, hash := range r.Hashes {
  244. if i != 0 && !waitOrStop() {
  245. return nil
  246. }
  247. if bytes >= softResponseLimit {
  248. break
  249. }
  250. body := bc.GetBodyRLP(hash)
  251. if body == nil {
  252. p.bumpInvalid()
  253. continue
  254. }
  255. bodies = append(bodies, body)
  256. bytes += len(body)
  257. }
  258. return p.replyBlockBodiesRLP(r.ReqID, bodies)
  259. }, r.ReqID, uint64(len(r.Hashes)), nil
  260. }
  261. // handleGetCode handles a contract code request
  262. func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) {
  263. var r GetCodePacket
  264. if err := msg.Decode(&r); err != nil {
  265. return nil, 0, 0, err
  266. }
  267. return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
  268. var (
  269. bytes int
  270. data [][]byte
  271. )
  272. bc := backend.BlockChain()
  273. for i, request := range r.Reqs {
  274. if i != 0 && !waitOrStop() {
  275. return nil
  276. }
  277. // Look up the root hash belonging to the request
  278. header := bc.GetHeaderByHash(request.BHash)
  279. if header == nil {
  280. p.Log().Warn("Failed to retrieve associate header for code", "hash", request.BHash)
  281. p.bumpInvalid()
  282. continue
  283. }
  284. // Refuse to search stale state data in the database since looking for
  285. // a non-exist key is kind of expensive.
  286. local := bc.CurrentHeader().Number.Uint64()
  287. if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local {
  288. p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local)
  289. p.bumpInvalid()
  290. continue
  291. }
  292. triedb := bc.StateCache().TrieDB()
  293. account, err := getAccount(triedb, header.Root, common.BytesToHash(request.AccKey))
  294. if err != nil {
  295. p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err)
  296. p.bumpInvalid()
  297. continue
  298. }
  299. code, err := bc.StateCache().ContractCode(common.BytesToHash(request.AccKey), common.BytesToHash(account.CodeHash))
  300. if err != nil {
  301. p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.CodeHash), "err", err)
  302. continue
  303. }
  304. // Accumulate the code and abort if enough data was retrieved
  305. data = append(data, code)
  306. if bytes += len(code); bytes >= softResponseLimit {
  307. break
  308. }
  309. }
  310. return p.replyCode(r.ReqID, data)
  311. }, r.ReqID, uint64(len(r.Reqs)), nil
  312. }
  313. // handleGetReceipts handles a block receipts request
  314. func handleGetReceipts(msg Decoder) (serveRequestFn, uint64, uint64, error) {
  315. var r GetReceiptsPacket
  316. if err := msg.Decode(&r); err != nil {
  317. return nil, 0, 0, err
  318. }
  319. return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
  320. var (
  321. bytes int
  322. receipts []rlp.RawValue
  323. )
  324. bc := backend.BlockChain()
  325. for i, hash := range r.Hashes {
  326. if i != 0 && !waitOrStop() {
  327. return nil
  328. }
  329. if bytes >= softResponseLimit {
  330. break
  331. }
  332. // Retrieve the requested block's receipts, skipping if unknown to us
  333. results := bc.GetReceiptsByHash(hash)
  334. if results == nil {
  335. if header := bc.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
  336. p.bumpInvalid()
  337. continue
  338. }
  339. }
  340. // If known, encode and queue for response packet
  341. if encoded, err := rlp.EncodeToBytes(results); err != nil {
  342. log.Error("Failed to encode receipt", "err", err)
  343. } else {
  344. receipts = append(receipts, encoded)
  345. bytes += len(encoded)
  346. }
  347. }
  348. return p.replyReceiptsRLP(r.ReqID, receipts)
  349. }, r.ReqID, uint64(len(r.Hashes)), nil
  350. }
  351. // handleGetProofs handles a proof request
  352. func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
  353. var r GetProofsPacket
  354. if err := msg.Decode(&r); err != nil {
  355. return nil, 0, 0, err
  356. }
  357. return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
  358. var (
  359. lastBHash common.Hash
  360. root common.Hash
  361. header *types.Header
  362. err error
  363. )
  364. bc := backend.BlockChain()
  365. nodes := light.NewNodeSet()
  366. for i, request := range r.Reqs {
  367. if i != 0 && !waitOrStop() {
  368. return nil
  369. }
  370. // Look up the root hash belonging to the request
  371. if request.BHash != lastBHash {
  372. root, lastBHash = common.Hash{}, request.BHash
  373. if header = bc.GetHeaderByHash(request.BHash); header == nil {
  374. p.Log().Warn("Failed to retrieve header for proof", "hash", request.BHash)
  375. p.bumpInvalid()
  376. continue
  377. }
  378. // Refuse to search stale state data in the database since looking for
  379. // a non-exist key is kind of expensive.
  380. local := bc.CurrentHeader().Number.Uint64()
  381. if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local {
  382. p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local)
  383. p.bumpInvalid()
  384. continue
  385. }
  386. root = header.Root
  387. }
  388. // If a header lookup failed (non existent), ignore subsequent requests for the same header
  389. if root == (common.Hash{}) {
  390. p.bumpInvalid()
  391. continue
  392. }
  393. // Open the account or storage trie for the request
  394. statedb := bc.StateCache()
  395. var trie state.Trie
  396. switch len(request.AccKey) {
  397. case 0:
  398. // No account key specified, open an account trie
  399. trie, err = statedb.OpenTrie(root)
  400. if trie == nil || err != nil {
  401. p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "root", root, "err", err)
  402. continue
  403. }
  404. default:
  405. // Account key specified, open a storage trie
  406. account, err := getAccount(statedb.TrieDB(), root, common.BytesToHash(request.AccKey))
  407. if err != nil {
  408. p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err)
  409. p.bumpInvalid()
  410. continue
  411. }
  412. trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root)
  413. if trie == nil || err != nil {
  414. p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "root", account.Root, "err", err)
  415. continue
  416. }
  417. }
  418. // Prove the user's request from the account or stroage trie
  419. if err := trie.Prove(request.Key, request.FromLevel, nodes); err != nil {
  420. p.Log().Warn("Failed to prove state request", "block", header.Number, "hash", header.Hash(), "err", err)
  421. continue
  422. }
  423. if nodes.DataSize() >= softResponseLimit {
  424. break
  425. }
  426. }
  427. return p.replyProofsV2(r.ReqID, nodes.NodeList())
  428. }, r.ReqID, uint64(len(r.Reqs)), nil
  429. }
  430. // handleGetHelperTrieProofs handles a helper trie proof request
  431. func handleGetHelperTrieProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
  432. var r GetHelperTrieProofsPacket
  433. if err := msg.Decode(&r); err != nil {
  434. return nil, 0, 0, err
  435. }
  436. return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
  437. var (
  438. lastIdx uint64
  439. lastType uint
  440. auxTrie *trie.Trie
  441. auxBytes int
  442. auxData [][]byte
  443. )
  444. bc := backend.BlockChain()
  445. nodes := light.NewNodeSet()
  446. for i, request := range r.Reqs {
  447. if i != 0 && !waitOrStop() {
  448. return nil
  449. }
  450. if auxTrie == nil || request.Type != lastType || request.TrieIdx != lastIdx {
  451. lastType, lastIdx = request.Type, request.TrieIdx
  452. auxTrie = backend.GetHelperTrie(request.Type, request.TrieIdx)
  453. }
  454. if auxTrie == nil {
  455. return nil
  456. }
  457. // TODO(rjl493456442) short circuit if the proving is failed.
  458. // The original client side code has a dirty hack to retrieve
  459. // the headers with no valid proof. Keep the compatibility for
  460. // legacy les protocol and drop this hack when the les2/3 are
  461. // not supported.
  462. err := auxTrie.Prove(request.Key, request.FromLevel, nodes)
  463. if p.version >= lpv4 && err != nil {
  464. return nil
  465. }
  466. if request.Type == htCanonical && request.AuxReq == htAuxHeader && len(request.Key) == 8 {
  467. header := bc.GetHeaderByNumber(binary.BigEndian.Uint64(request.Key))
  468. data, err := rlp.EncodeToBytes(header)
  469. if err != nil {
  470. log.Error("Failed to encode header", "err", err)
  471. return nil
  472. }
  473. auxData = append(auxData, data)
  474. auxBytes += len(data)
  475. }
  476. if nodes.DataSize()+auxBytes >= softResponseLimit {
  477. break
  478. }
  479. }
  480. return p.replyHelperTrieProofs(r.ReqID, HelperTrieResps{Proofs: nodes.NodeList(), AuxData: auxData})
  481. }, r.ReqID, uint64(len(r.Reqs)), nil
  482. }
  483. // handleSendTx handles a transaction propagation request
  484. func handleSendTx(msg Decoder) (serveRequestFn, uint64, uint64, error) {
  485. var r SendTxPacket
  486. if err := msg.Decode(&r); err != nil {
  487. return nil, 0, 0, err
  488. }
  489. amount := uint64(len(r.Txs))
  490. return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
  491. stats := make([]light.TxStatus, len(r.Txs))
  492. for i, tx := range r.Txs {
  493. if i != 0 && !waitOrStop() {
  494. return nil
  495. }
  496. hash := tx.Hash()
  497. stats[i] = txStatus(backend, hash)
  498. if stats[i].Status == core.TxStatusUnknown {
  499. addFn := backend.TxPool().AddRemotes
  500. // Add txs synchronously for testing purpose
  501. if backend.AddTxsSync() {
  502. addFn = backend.TxPool().AddRemotesSync
  503. }
  504. if errs := addFn([]*types.Transaction{tx}); errs[0] != nil {
  505. stats[i].Error = errs[0].Error()
  506. continue
  507. }
  508. stats[i] = txStatus(backend, hash)
  509. }
  510. }
  511. return p.replyTxStatus(r.ReqID, stats)
  512. }, r.ReqID, amount, nil
  513. }
  514. // handleGetTxStatus handles a transaction status query
  515. func handleGetTxStatus(msg Decoder) (serveRequestFn, uint64, uint64, error) {
  516. var r GetTxStatusPacket
  517. if err := msg.Decode(&r); err != nil {
  518. return nil, 0, 0, err
  519. }
  520. return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
  521. stats := make([]light.TxStatus, len(r.Hashes))
  522. for i, hash := range r.Hashes {
  523. if i != 0 && !waitOrStop() {
  524. return nil
  525. }
  526. stats[i] = txStatus(backend, hash)
  527. }
  528. return p.replyTxStatus(r.ReqID, stats)
  529. }, r.ReqID, uint64(len(r.Hashes)), nil
  530. }
  531. // txStatus returns the status of a specified transaction.
  532. func txStatus(b serverBackend, hash common.Hash) light.TxStatus {
  533. var stat light.TxStatus
  534. // Looking the transaction in txpool first.
  535. stat.Status = b.TxPool().Status([]common.Hash{hash})[0]
  536. // If the transaction is unknown to the pool, try looking it up locally.
  537. if stat.Status == core.TxStatusUnknown {
  538. lookup := b.BlockChain().GetTransactionLookup(hash)
  539. if lookup != nil {
  540. stat.Status = core.TxStatusIncluded
  541. stat.Lookup = lookup
  542. }
  543. }
  544. return stat
  545. }