hermez-node/db/historydb/apiqueries.go
db/historydb/apiqueries.goRaw
1package historydb
2
3import (
4 "database/sql"
5 "errors"
6 "fmt"
7 "math/big"
8 "time"
9
10 ethCommon "github.com/ethereum/go-ethereum/common"
11 "github.com/hermeznetwork/hermez-node/apitypes"
12 "github.com/hermeznetwork/hermez-node/common"
13 "github.com/hermeznetwork/hermez-node/db"
14 "github.com/hermeznetwork/tracerr"
15 "github.com/iden3/go-iden3-crypto/babyjub"
16 "github.com/jmoiron/sqlx"
17 "github.com/russross/meddler"
18)
19
20// GetLastBlockAPI retrieve the block with the highest block number from the DB
21func (hdb *HistoryDB) GetLastBlockAPI() (*common.Block, error) {
22 cancel, err := hdb.apiConnCon.Acquire()
23 defer cancel()
24 if err != nil {
25 return nil, tracerr.Wrap(err)
26 }
27 defer hdb.apiConnCon.Release()
28 return hdb.GetLastBlock()
29}
30
31// GetBatchAPI return the batch with the given batchNum
32func (hdb *HistoryDB) GetBatchAPI(batchNum common.BatchNum) (*BatchAPI, error) {
33 cancel, err := hdb.apiConnCon.Acquire()
34 defer cancel()
35 if err != nil {
36 return nil, tracerr.Wrap(err)
37 }
38 defer hdb.apiConnCon.Release()
39 return hdb.getBatchAPI(hdb.dbRead, batchNum)
40}
41
42// GetBatchInternalAPI return the batch with the given batchNum
43func (hdb *HistoryDB) GetBatchInternalAPI(batchNum common.BatchNum) (*BatchAPI, error) {
44 return hdb.getBatchAPI(hdb.dbRead, batchNum)
45}
46
47func (hdb *HistoryDB) getBatchAPI(d meddler.DB, batchNum common.BatchNum) (*BatchAPI, error) {
48 batch := &BatchAPI{}
49 if err := meddler.QueryRow(
50 d, batch,
51 `SELECT batch.item_id, batch.batch_num, batch.eth_block_num,
52 batch.forger_addr, batch.fees_collected, batch.total_fees_usd, batch.state_root,
53 batch.num_accounts, batch.exit_root, batch.forge_l1_txs_num, batch.slot_num,
54 block.timestamp, block.hash,
55 COALESCE ((SELECT COUNT(*) FROM tx WHERE batch_num = batch.batch_num), 0) AS forged_txs
56 FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num
57 WHERE batch_num = $1;`, batchNum,
58 ); err != nil {
59 return nil, tracerr.Wrap(err)
60 }
61 batch.CollectedFeesAPI = apitypes.NewCollectedFeesAPI(batch.CollectedFeesDB)
62 return batch, nil
63}
64
65// GetBatchesAPI return the batches applying the given filters
66func (hdb *HistoryDB) GetBatchesAPI(
67 minBatchNum, maxBatchNum, slotNum *uint,
68 forgerAddr *ethCommon.Address,
69 fromItem, limit *uint, order string,
70) ([]BatchAPI, uint64, error) {
71 cancel, err := hdb.apiConnCon.Acquire()
72 defer cancel()
73 if err != nil {
74 return nil, 0, tracerr.Wrap(err)
75 }
76 defer hdb.apiConnCon.Release()
77 var query string
78 var args []interface{}
79 queryStr := `SELECT batch.item_id, batch.batch_num, batch.eth_block_num,
80 batch.forger_addr, batch.fees_collected, batch.total_fees_usd, batch.state_root,
81 batch.num_accounts, batch.exit_root, batch.forge_l1_txs_num, batch.slot_num,
82 block.timestamp, block.hash,
83 COALESCE ((SELECT COUNT(*) FROM tx WHERE batch_num = batch.batch_num), 0) AS forged_txs,
84 count(*) OVER() AS total_items
85 FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num `
86 // Apply filters
87 nextIsAnd := false
88 // minBatchNum filter
89 if minBatchNum != nil {
90 if nextIsAnd {
91 queryStr += "AND "
92 } else {
93 queryStr += "WHERE "
94 }
95 queryStr += "batch.batch_num > ? "
96 args = append(args, minBatchNum)
97 nextIsAnd = true
98 }
99 // maxBatchNum filter
100 if maxBatchNum != nil {
101 if nextIsAnd {
102 queryStr += "AND "
103 } else {
104 queryStr += "WHERE "
105 }
106 queryStr += "batch.batch_num < ? "
107 args = append(args, maxBatchNum)
108 nextIsAnd = true
109 }
110 // slotNum filter
111 if slotNum != nil {
112 if nextIsAnd {
113 queryStr += "AND "
114 } else {
115 queryStr += "WHERE "
116 }
117 queryStr += "batch.slot_num = ? "
118 args = append(args, slotNum)
119 nextIsAnd = true
120 }
121 // forgerAddr filter
122 if forgerAddr != nil {
123 if nextIsAnd {
124 queryStr += "AND "
125 } else {
126 queryStr += "WHERE "
127 }
128 queryStr += "batch.forger_addr = ? "
129 args = append(args, forgerAddr)
130 nextIsAnd = true
131 }
132 // pagination
133 if fromItem != nil {
134 if nextIsAnd {
135 queryStr += "AND "
136 } else {
137 queryStr += "WHERE "
138 }
139 if order == OrderAsc {
140 queryStr += "batch.item_id >= ? "
141 } else {
142 queryStr += "batch.item_id <= ? "
143 }
144 args = append(args, fromItem)
145 }
146 queryStr += "ORDER BY batch.item_id "
147 if order == OrderAsc {
148 queryStr += " ASC "
149 } else {
150 queryStr += " DESC "
151 }
152 queryStr += fmt.Sprintf("LIMIT %d;", *limit)
153 query = hdb.dbRead.Rebind(queryStr)
154 // log.Debug(query)
155 batchPtrs := []*BatchAPI{}
156 if err := meddler.QueryAll(hdb.dbRead, &batchPtrs, query, args...); err != nil {
157 return nil, 0, tracerr.Wrap(err)
158 }
159 batches := db.SlicePtrsToSlice(batchPtrs).([]BatchAPI)
160 if len(batches) == 0 {
161 return batches, 0, nil
162 }
163 for i := range batches {
164 batches[i].CollectedFeesAPI = apitypes.NewCollectedFeesAPI(batches[i].CollectedFeesDB)
165 }
166 return batches, batches[0].TotalItems - uint64(len(batches)), nil
167}
168
169// GetBestBidAPI returns the best bid in specific slot by slotNum
170func (hdb *HistoryDB) GetBestBidAPI(slotNum *int64) (BidAPI, error) {
171 bid := &BidAPI{}
172 cancel, err := hdb.apiConnCon.Acquire()
173 defer cancel()
174 if err != nil {
175 return *bid, tracerr.Wrap(err)
176 }
177 defer hdb.apiConnCon.Release()
178 err = meddler.QueryRow(
179 hdb.dbRead, bid, `SELECT bid.*, block.timestamp, coordinator.forger_addr, coordinator.url
180 FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num
181 INNER JOIN (
182 SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
183 GROUP BY bidder_addr
184 ) c ON bid.bidder_addr = c.bidder_addr
185 INNER JOIN coordinator ON c.item_id = coordinator.item_id
186 WHERE slot_num = $1 ORDER BY item_id DESC LIMIT 1;`, slotNum,
187 )
188 return *bid, tracerr.Wrap(err)
189}
190
191// GetBestBidsAPI returns the best bid in specific slot by slotNum
192func (hdb *HistoryDB) GetBestBidsAPI(
193 minSlotNum, maxSlotNum *int64,
194 bidderAddr *ethCommon.Address,
195 limit *uint, order string,
196) ([]BidAPI, uint64, error) {
197 cancel, err := hdb.apiConnCon.Acquire()
198 defer cancel()
199 if err != nil {
200 return nil, 0, tracerr.Wrap(err)
201 }
202 defer hdb.apiConnCon.Release()
203 return hdb.getBestBidsAPI(hdb.dbRead, minSlotNum, maxSlotNum, bidderAddr, limit, order)
204}
205func (hdb *HistoryDB) getBestBidsAPI(
206 d meddler.DB,
207 minSlotNum, maxSlotNum *int64,
208 bidderAddr *ethCommon.Address,
209 limit *uint, order string,
210) ([]BidAPI, uint64, error) {
211 var query string
212 var args []interface{}
213 // JOIN the best bid of each slot with the latest update of each coordinator
214 queryStr := `SELECT b.*, block.timestamp, coordinator.forger_addr, coordinator.url,
215 COUNT(*) OVER() AS total_items FROM (
216 SELECT slot_num, MAX(item_id) as maxitem
217 FROM bid GROUP BY slot_num
218 )
219 AS x INNER JOIN bid AS b ON b.item_id = x.maxitem
220 INNER JOIN block ON b.eth_block_num = block.eth_block_num
221 INNER JOIN (
222 SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
223 GROUP BY bidder_addr
224 ) c ON b.bidder_addr = c.bidder_addr
225 INNER JOIN coordinator ON c.item_id = coordinator.item_id
226 WHERE (b.slot_num >= ? AND b.slot_num <= ?)`
227 args = append(args, minSlotNum)
228 args = append(args, maxSlotNum)
229 // Apply filters
230 if bidderAddr != nil {
231 queryStr += " AND b.bidder_addr = ? "
232 args = append(args, bidderAddr)
233 }
234 queryStr += " ORDER BY b.slot_num "
235 if order == OrderAsc {
236 queryStr += "ASC "
237 } else {
238 queryStr += "DESC "
239 }
240 if limit != nil {
241 queryStr += fmt.Sprintf("LIMIT %d;", *limit)
242 }
243 query = hdb.dbRead.Rebind(queryStr)
244 bidPtrs := []*BidAPI{}
245 if err := meddler.QueryAll(d, &bidPtrs, query, args...); err != nil {
246 return nil, 0, tracerr.Wrap(err)
247 }
248 // log.Debug(query)
249 bids := db.SlicePtrsToSlice(bidPtrs).([]BidAPI)
250 if len(bids) == 0 {
251 return bids, 0, nil
252 }
253 return bids, bids[0].TotalItems - uint64(len(bids)), nil
254}
255
256// GetBidsAPI return the bids applying the given filters
257func (hdb *HistoryDB) GetBidsAPI(
258 slotNum *int64, bidderAddr *ethCommon.Address,
259 fromItem, limit *uint, order string,
260) ([]BidAPI, uint64, error) {
261 cancel, err := hdb.apiConnCon.Acquire()
262 defer cancel()
263 if err != nil {
264 return nil, 0, tracerr.Wrap(err)
265 }
266 defer hdb.apiConnCon.Release()
267 var query string
268 var args []interface{}
269 // JOIN each bid with the latest update of each coordinator
270 queryStr := `SELECT bid.*, block.timestamp, coord.forger_addr, coord.url,
271 COUNT(*) OVER() AS total_items
272 FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num
273 INNER JOIN (
274 SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
275 GROUP BY bidder_addr
276 ) c ON bid.bidder_addr = c.bidder_addr
277 INNER JOIN coordinator coord ON c.item_id = coord.item_id `
278 // Apply filters
279 nextIsAnd := false
280 // slotNum filter
281 if slotNum != nil {
282 if nextIsAnd {
283 queryStr += "AND "
284 } else {
285 queryStr += "WHERE "
286 }
287 queryStr += "bid.slot_num = ? "
288 args = append(args, slotNum)
289 nextIsAnd = true
290 }
291 // bidder filter
292 if bidderAddr != nil {
293 if nextIsAnd {
294 queryStr += "AND "
295 } else {
296 queryStr += "WHERE "
297 }
298 queryStr += "bid.bidder_addr = ? "
299 args = append(args, bidderAddr)
300 nextIsAnd = true
301 }
302 if fromItem != nil {
303 if nextIsAnd {
304 queryStr += "AND "
305 } else {
306 queryStr += "WHERE "
307 }
308 if order == OrderAsc {
309 queryStr += "bid.item_id >= ? "
310 } else {
311 queryStr += "bid.item_id <= ? "
312 }
313 args = append(args, fromItem)
314 }
315 // pagination
316 queryStr += "ORDER BY bid.item_id "
317 if order == OrderAsc {
318 queryStr += "ASC "
319 } else {
320 queryStr += "DESC "
321 }
322 queryStr += fmt.Sprintf("LIMIT %d;", *limit)
323 query, argsQ, err := sqlx.In(queryStr, args...)
324 if err != nil {
325 return nil, 0, tracerr.Wrap(err)
326 }
327 query = hdb.dbRead.Rebind(query)
328 bids := []*BidAPI{}
329 if err := meddler.QueryAll(hdb.dbRead, &bids, query, argsQ...); err != nil {
330 return nil, 0, tracerr.Wrap(err)
331 }
332 if len(bids) == 0 {
333 return []BidAPI{}, 0, nil
334 }
335 return db.SlicePtrsToSlice(bids).([]BidAPI), bids[0].TotalItems - uint64(len(bids)), nil
336}
337
338// GetTokenAPI returns a token from the DB given a TokenID
339func (hdb *HistoryDB) GetTokenAPI(tokenID common.TokenID) (*TokenWithUSD, error) {
340 cancel, err := hdb.apiConnCon.Acquire()
341 defer cancel()
342 if err != nil {
343 return nil, tracerr.Wrap(err)
344 }
345 defer hdb.apiConnCon.Release()
346 return hdb.GetToken(tokenID)
347}
348
349// GetTokensAPI returns a list of tokens from the DB
350func (hdb *HistoryDB) GetTokensAPI(
351 ids []common.TokenID, symbols []string, name string, fromItem,
352 limit *uint, order string,
353) ([]TokenWithUSD, uint64, error) {
354 cancel, err := hdb.apiConnCon.Acquire()
355 defer cancel()
356 if err != nil {
357 return nil, 0, tracerr.Wrap(err)
358 }
359 defer hdb.apiConnCon.Release()
360 var query string
361 var args []interface{}
362 queryStr := `SELECT * , COUNT(*) OVER() AS total_items FROM token `
363 // Apply filters
364 nextIsAnd := false
365 if len(ids) > 0 {
366 queryStr += "WHERE token_id IN (?) "
367 nextIsAnd = true
368 args = append(args, ids)
369 }
370 if len(symbols) > 0 {
371 if nextIsAnd {
372 queryStr += "AND "
373 } else {
374 queryStr += "WHERE "
375 }
376 queryStr += "symbol IN (?) "
377 args = append(args, symbols)
378 nextIsAnd = true
379 }
380 if name != "" {
381 if nextIsAnd {
382 queryStr += "AND "
383 } else {
384 queryStr += "WHERE "
385 }
386 queryStr += "name ~ ? "
387 args = append(args, name)
388 nextIsAnd = true
389 }
390 if fromItem != nil {
391 if nextIsAnd {
392 queryStr += "AND "
393 } else {
394 queryStr += "WHERE "
395 }
396 if order == OrderAsc {
397 queryStr += "item_id >= ? "
398 } else {
399 queryStr += "item_id <= ? "
400 }
401 args = append(args, fromItem)
402 }
403 // pagination
404 queryStr += "ORDER BY item_id "
405 if order == OrderAsc {
406 queryStr += "ASC "
407 } else {
408 queryStr += "DESC "
409 }
410 queryStr += fmt.Sprintf("LIMIT %d;", *limit)
411 query, argsQ, err := sqlx.In(queryStr, args...)
412 if err != nil {
413 return nil, 0, tracerr.Wrap(err)
414 }
415 query = hdb.dbRead.Rebind(query)
416 tokens := []*TokenWithUSD{}
417 if err := meddler.QueryAll(hdb.dbRead, &tokens, query, argsQ...); err != nil {
418 return nil, 0, tracerr.Wrap(err)
419 }
420 if len(tokens) == 0 {
421 return []TokenWithUSD{}, 0, nil
422 }
423 return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), uint64(len(tokens)) - tokens[0].TotalItems, nil
424}
425
426// GetTxAPI returns a tx from the DB given a TxID
427func (hdb *HistoryDB) GetTxAPI(txID common.TxID) (*TxAPI, error) {
428 // Warning: amount_success and deposit_amount_success have true as default for
429 // performance reasons. The expected default value is false (when txs are unforged)
430 // this case is handled at the function func (tx TxAPI) MarshalJSON() ([]byte, error)
431 cancel, err := hdb.apiConnCon.Acquire()
432 defer cancel()
433 if err != nil {
434 return nil, tracerr.Wrap(err)
435 }
436 defer hdb.apiConnCon.Release()
437 tx := &TxAPI{}
438 err = meddler.QueryRow(
439 hdb.dbRead, tx, `SELECT tx.item_id, tx.is_l1, tx.id, tx.type, tx.position,
440 hez_idx(tx.effective_from_idx, token.symbol) AS from_idx, tx.from_eth_addr, tx.from_bjj,
441 hez_idx(tx.to_idx, token.symbol) AS to_idx, tx.to_eth_addr, tx.to_bjj,
442 tx.amount, tx.amount_success, tx.token_id, tx.amount_usd,
443 tx.batch_num, tx.eth_block_num, tx.to_forge_l1_txs_num, tx.user_origin,
444 tx.deposit_amount, tx.deposit_amount_usd, tx.deposit_amount_success, tx.fee, tx.fee_usd, tx.nonce,
445 token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
446 token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
447 token.usd_update, block.timestamp
448 FROM tx INNER JOIN token ON tx.token_id = token.token_id
449 INNER JOIN block ON tx.eth_block_num = block.eth_block_num
450 WHERE tx.id = $1;`, txID,
451 )
452 return tx, tracerr.Wrap(err)
453}
454
455// GetTxsAPI returns a list of txs from the DB using the HistoryTx struct
456// and pagination info
457func (hdb *HistoryDB) GetTxsAPI(
458 ethAddr *ethCommon.Address, bjj *babyjub.PublicKeyComp,
459 tokenID *common.TokenID, idx *common.Idx, batchNum *uint, txType *common.TxType,
460 fromItem, limit *uint, order string,
461) ([]TxAPI, uint64, error) {
462 // Warning: amount_success and deposit_amount_success have true as default for
463 // performance reasons. The expected default value is false (when txs are unforged)
464 // this case is handled at the function func (tx TxAPI) MarshalJSON() ([]byte, error)
465 cancel, err := hdb.apiConnCon.Acquire()
466 defer cancel()
467 if err != nil {
468 return nil, 0, tracerr.Wrap(err)
469 }
470 defer hdb.apiConnCon.Release()
471 if ethAddr != nil && bjj != nil {
472 return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
473 }
474 var query string
475 var args []interface{}
476 queryStr := `SELECT tx.item_id, tx.is_l1, tx.id, tx.type, tx.position,
477 hez_idx(tx.effective_from_idx, token.symbol) AS from_idx, tx.from_eth_addr, tx.from_bjj,
478 hez_idx(tx.to_idx, token.symbol) AS to_idx, tx.to_eth_addr, tx.to_bjj,
479 tx.amount, tx.amount_success, tx.token_id, tx.amount_usd,
480 tx.batch_num, tx.eth_block_num, tx.to_forge_l1_txs_num, tx.user_origin,
481 tx.deposit_amount, tx.deposit_amount_usd, tx.deposit_amount_success, tx.fee, tx.fee_usd, tx.nonce,
482 token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
483 token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
484 token.usd_update, block.timestamp, count(*) OVER() AS total_items
485 FROM tx INNER JOIN token ON tx.token_id = token.token_id
486 INNER JOIN block ON tx.eth_block_num = block.eth_block_num `
487 // Apply filters
488 nextIsAnd := false
489 // ethAddr filter
490 if ethAddr != nil {
491 queryStr += "WHERE (tx.from_eth_addr = ? OR tx.to_eth_addr = ?) "
492 nextIsAnd = true
493 args = append(args, ethAddr, ethAddr)
494 } else if bjj != nil { // bjj filter
495 queryStr += "WHERE (tx.from_bjj = ? OR tx.to_bjj = ?) "
496 nextIsAnd = true
497 args = append(args, bjj, bjj)
498 }
499 // tokenID filter
500 if tokenID != nil {
501 if nextIsAnd {
502 queryStr += "AND "
503 } else {
504 queryStr += "WHERE "
505 }
506 queryStr += "tx.token_id = ? "
507 args = append(args, tokenID)
508 nextIsAnd = true
509 }
510 // idx filter
511 if idx != nil {
512 if nextIsAnd {
513 queryStr += "AND "
514 } else {
515 queryStr += "WHERE "
516 }
517 queryStr += "(tx.effective_from_idx = ? OR tx.to_idx = ?) "
518 args = append(args, idx, idx)
519 nextIsAnd = true
520 }
521 // batchNum filter
522 if batchNum != nil {
523 if nextIsAnd {
524 queryStr += "AND "
525 } else {
526 queryStr += "WHERE "
527 }
528 queryStr += "tx.batch_num = ? "
529 args = append(args, batchNum)
530 nextIsAnd = true
531 }
532 // txType filter
533 if txType != nil {
534 if nextIsAnd {
535 queryStr += "AND "
536 } else {
537 queryStr += "WHERE "
538 }
539 queryStr += "tx.type = ? "
540 args = append(args, txType)
541 nextIsAnd = true
542 }
543 if fromItem != nil {
544 if nextIsAnd {
545 queryStr += "AND "
546 } else {
547 queryStr += "WHERE "
548 }
549 if order == OrderAsc {
550 queryStr += "tx.item_id >= ? "
551 } else {
552 queryStr += "tx.item_id <= ? "
553 }
554 args = append(args, fromItem)
555 nextIsAnd = true
556 }
557 if nextIsAnd {
558 queryStr += "AND "
559 } else {
560 queryStr += "WHERE "
561 }
562 queryStr += "tx.batch_num IS NOT NULL "
563
564 // pagination
565 queryStr += "ORDER BY tx.item_id "
566 if order == OrderAsc {
567 queryStr += " ASC "
568 } else {
569 queryStr += " DESC "
570 }
571 queryStr += fmt.Sprintf("LIMIT %d;", *limit)
572 query = hdb.dbRead.Rebind(queryStr)
573 // log.Debug(query)
574 txsPtrs := []*TxAPI{}
575 if err := meddler.QueryAll(hdb.dbRead, &txsPtrs, query, args...); err != nil {
576 return nil, 0, tracerr.Wrap(err)
577 }
578 txs := db.SlicePtrsToSlice(txsPtrs).([]TxAPI)
579 if len(txs) == 0 {
580 return txs, 0, nil
581 }
582 return txs, txs[0].TotalItems - uint64(len(txs)), nil
583}
584
585// GetExitAPI returns a exit from the DB
586func (hdb *HistoryDB) GetExitAPI(batchNum *uint, idx *common.Idx) (*ExitAPI, error) {
587 cancel, err := hdb.apiConnCon.Acquire()
588 defer cancel()
589 if err != nil {
590 return nil, tracerr.Wrap(err)
591 }
592 defer hdb.apiConnCon.Release()
593 exit := &ExitAPI{}
594 err = meddler.QueryRow(
595 hdb.dbRead, exit, `SELECT exit_tree.item_id, exit_tree.batch_num,
596 hez_idx(exit_tree.account_idx, token.symbol) AS account_idx,
597 account.bjj, account.eth_addr,
598 exit_tree.merkle_proof, exit_tree.balance, exit_tree.instant_withdrawn,
599 exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
600 token.token_id, token.item_id AS token_item_id,
601 token.eth_block_num AS token_block, token.eth_addr AS token_eth_addr, token.name, token.symbol,
602 token.decimals, token.usd, token.usd_update
603 FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx
604 INNER JOIN token ON account.token_id = token.token_id
605 WHERE exit_tree.batch_num = $1 AND exit_tree.account_idx = $2;`, batchNum, idx,
606 )
607 return exit, tracerr.Wrap(err)
608}
609
610// GetExitsAPI returns a list of exits from the DB and pagination info
611func (hdb *HistoryDB) GetExitsAPI(
612 ethAddr *ethCommon.Address, bjj *babyjub.PublicKeyComp, tokenID *common.TokenID,
613 idx *common.Idx, batchNum *uint, onlyPendingWithdraws *bool,
614 fromItem, limit *uint, order string,
615) ([]ExitAPI, uint64, error) {
616 if ethAddr != nil && bjj != nil {
617 return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
618 }
619 cancel, err := hdb.apiConnCon.Acquire()
620 defer cancel()
621 if err != nil {
622 return nil, 0, tracerr.Wrap(err)
623 }
624 defer hdb.apiConnCon.Release()
625 var query string
626 var args []interface{}
627 queryStr := `SELECT exit_tree.item_id, exit_tree.batch_num,
628 hez_idx(exit_tree.account_idx, token.symbol) AS account_idx,
629 account.bjj, account.eth_addr,
630 exit_tree.merkle_proof, exit_tree.balance, exit_tree.instant_withdrawn,
631 exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
632 token.token_id, token.item_id AS token_item_id,
633 token.eth_block_num AS token_block, token.eth_addr AS token_eth_addr, token.name, token.symbol,
634 token.decimals, token.usd, token.usd_update, COUNT(*) OVER() AS total_items
635 FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx
636 INNER JOIN token ON account.token_id = token.token_id `
637 // Apply filters
638 nextIsAnd := false
639 // ethAddr filter
640 if ethAddr != nil {
641 queryStr += "WHERE account.eth_addr = ? "
642 nextIsAnd = true
643 args = append(args, ethAddr)
644 } else if bjj != nil { // bjj filter
645 queryStr += "WHERE account.bjj = ? "
646 nextIsAnd = true
647 args = append(args, bjj)
648 }
649 // tokenID filter
650 if tokenID != nil {
651 if nextIsAnd {
652 queryStr += "AND "
653 } else {
654 queryStr += "WHERE "
655 }
656 queryStr += "account.token_id = ? "
657 args = append(args, tokenID)
658 nextIsAnd = true
659 }
660 // idx filter
661 if idx != nil {
662 if nextIsAnd {
663 queryStr += "AND "
664 } else {
665 queryStr += "WHERE "
666 }
667 queryStr += "exit_tree.account_idx = ? "
668 args = append(args, idx)
669 nextIsAnd = true
670 }
671 // batchNum filter
672 if batchNum != nil {
673 if nextIsAnd {
674 queryStr += "AND "
675 } else {
676 queryStr += "WHERE "
677 }
678 queryStr += "exit_tree.batch_num = ? "
679 args = append(args, batchNum)
680 nextIsAnd = true
681 }
682 // onlyPendingWithdraws
683 if onlyPendingWithdraws != nil {
684 if *onlyPendingWithdraws {
685 if nextIsAnd {
686 queryStr += "AND "
687 } else {
688 queryStr += "WHERE "
689 }
690 queryStr += "(exit_tree.instant_withdrawn IS NULL AND exit_tree.delayed_withdrawn IS NULL) "
691 nextIsAnd = true
692 }
693 }
694 if fromItem != nil {
695 if nextIsAnd {
696 queryStr += "AND "
697 } else {
698 queryStr += "WHERE "
699 }
700 if order == OrderAsc {
701 queryStr += "exit_tree.item_id >= ? "
702 } else {
703 queryStr += "exit_tree.item_id <= ? "
704 }
705 args = append(args, fromItem)
706 // nextIsAnd = true
707 }
708 // pagination
709 queryStr += "ORDER BY exit_tree.item_id "
710 if order == OrderAsc {
711 queryStr += " ASC "
712 } else {
713 queryStr += " DESC "
714 }
715 queryStr += fmt.Sprintf("LIMIT %d;", *limit)
716 query = hdb.dbRead.Rebind(queryStr)
717 // log.Debug(query)
718 exits := []*ExitAPI{}
719 if err := meddler.QueryAll(hdb.dbRead, &exits, query, args...); err != nil {
720 return nil, 0, tracerr.Wrap(err)
721 }
722 if len(exits) == 0 {
723 return []ExitAPI{}, 0, nil
724 }
725 return db.SlicePtrsToSlice(exits).([]ExitAPI), exits[0].TotalItems - uint64(len(exits)), nil
726}
727
728// GetCoordinatorsAPI returns a list of coordinators from the DB and pagination info
729func (hdb *HistoryDB) GetCoordinatorsAPI(
730 bidderAddr, forgerAddr *ethCommon.Address,
731 fromItem, limit *uint, order string,
732) ([]CoordinatorAPI, uint64, error) {
733 cancel, err := hdb.apiConnCon.Acquire()
734 defer cancel()
735 if err != nil {
736 return nil, 0, tracerr.Wrap(err)
737 }
738 defer hdb.apiConnCon.Release()
739 var query string
740 var args []interface{}
741 queryStr := `SELECT coordinator.*, COUNT(*) OVER() AS total_items
742 FROM coordinator INNER JOIN (
743 SELECT MAX(item_id) AS item_id FROM coordinator
744 GROUP BY bidder_addr
745 ) c ON coordinator.item_id = c.item_id `
746 // Apply filters
747 nextIsAnd := false
748 if bidderAddr != nil {
749 queryStr += "WHERE bidder_addr = ? "
750 nextIsAnd = true
751 args = append(args, bidderAddr)
752 }
753 if forgerAddr != nil {
754 if nextIsAnd {
755 queryStr += "AND "
756 } else {
757 queryStr += "WHERE "
758 }
759 queryStr += "forger_addr = ? "
760 nextIsAnd = true
761 args = append(args, forgerAddr)
762 }
763 if fromItem != nil {
764 if nextIsAnd {
765 queryStr += "AND "
766 } else {
767 queryStr += "WHERE "
768 }
769 if order == OrderAsc {
770 queryStr += "coordinator.item_id >= ? "
771 } else {
772 queryStr += "coordinator.item_id <= ? "
773 }
774 args = append(args, fromItem)
775 }
776 // pagination
777 queryStr += "ORDER BY coordinator.item_id "
778 if order == OrderAsc {
779 queryStr += " ASC "
780 } else {
781 queryStr += " DESC "
782 }
783 queryStr += fmt.Sprintf("LIMIT %d;", *limit)
784 query = hdb.dbRead.Rebind(queryStr)
785
786 coordinators := []*CoordinatorAPI{}
787 if err := meddler.QueryAll(hdb.dbRead, &coordinators, query, args...); err != nil {
788 return nil, 0, tracerr.Wrap(err)
789 }
790 if len(coordinators) == 0 {
791 return []CoordinatorAPI{}, 0, nil
792 }
793 return db.SlicePtrsToSlice(coordinators).([]CoordinatorAPI),
794 coordinators[0].TotalItems - uint64(len(coordinators)), nil
795}
796
797// GetAuctionVarsAPI returns auction variables
798func (hdb *HistoryDB) GetAuctionVarsAPI() (*common.AuctionVariables, error) {
799 cancel, err := hdb.apiConnCon.Acquire()
800 defer cancel()
801 if err != nil {
802 return nil, tracerr.Wrap(err)
803 }
804 defer hdb.apiConnCon.Release()
805 auctionVars := &common.AuctionVariables{}
806 err = meddler.QueryRow(
807 hdb.dbRead, auctionVars, `SELECT * FROM auction_vars;`,
808 )
809 return auctionVars, tracerr.Wrap(err)
810}
811
812// GetAccountAPI returns an account by its index
813func (hdb *HistoryDB) GetAccountAPI(idx common.Idx) (*AccountAPI, error) {
814 cancel, err := hdb.apiConnCon.Acquire()
815 defer cancel()
816 if err != nil {
817 return nil, tracerr.Wrap(err)
818 }
819 defer hdb.apiConnCon.Release()
820 account := &AccountAPI{}
821 err = meddler.QueryRow(hdb.dbRead, account, `SELECT account.item_id, hez_idx(account.idx,
822 token.symbol) as idx, account.batch_num, account.bjj, account.eth_addr,
823 token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
824 token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd,
825 token.usd_update, account_update.nonce, account_update.balance
826 FROM account inner JOIN (
827 SELECT idx, nonce, balance
828 FROM account_update
829 WHERE idx = $1
830 ORDER BY item_id DESC LIMIT 1
831 ) AS account_update ON account_update.idx = account.idx
832 INNER JOIN token ON account.token_id = token.token_id
833 WHERE account.idx = $1;`, idx)
834
835 if err != nil {
836 return nil, tracerr.Wrap(err)
837 }
838
839 return account, nil
840}
841
842// GetAccountsAPI returns a list of accounts from the DB and pagination info
843func (hdb *HistoryDB) GetAccountsAPI(
844 tokenIDs []common.TokenID, ethAddr *ethCommon.Address,
845 bjj *babyjub.PublicKeyComp, fromItem, limit *uint, order string,
846) ([]AccountAPI, uint64, error) {
847 if ethAddr != nil && bjj != nil {
848 return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
849 }
850 cancel, err := hdb.apiConnCon.Acquire()
851 defer cancel()
852 if err != nil {
853 return nil, 0, tracerr.Wrap(err)
854 }
855 defer hdb.apiConnCon.Release()
856 var query string
857 var args []interface{}
858 queryStr := `SELECT account.item_id, hez_idx(account.idx, token.symbol) as idx, account.batch_num,
859 account.bjj, account.eth_addr, token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
860 token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd, token.usd_update,
861 account_update.nonce, account_update.balance, COUNT(*) OVER() AS total_items
862 FROM account inner JOIN (
863 SELECT DISTINCT idx,
864 first_value(nonce) over(partition by idx ORDER BY item_id DESC) as nonce,
865 first_value(balance) over(partition by idx ORDER BY item_id DESC) as balance
866 FROM account_update
867 ) AS account_update ON account_update.idx = account.idx INNER JOIN token ON account.token_id = token.token_id `
868 // Apply filters
869 nextIsAnd := false
870 // ethAddr filter
871 if ethAddr != nil {
872 queryStr += "WHERE account.eth_addr = ? "
873 nextIsAnd = true
874 args = append(args, ethAddr)
875 } else if bjj != nil { // bjj filter
876 queryStr += "WHERE account.bjj = ? "
877 nextIsAnd = true
878 args = append(args, bjj)
879 }
880 // tokenID filter
881 if len(tokenIDs) > 0 {
882 if nextIsAnd {
883 queryStr += "AND "
884 } else {
885 queryStr += "WHERE "
886 }
887 queryStr += "account.token_id IN (?) "
888 args = append(args, tokenIDs)
889 nextIsAnd = true
890 }
891 if fromItem != nil {
892 if nextIsAnd {
893 queryStr += "AND "
894 } else {
895 queryStr += "WHERE "
896 }
897 if order == OrderAsc {
898 queryStr += "account.item_id >= ? "
899 } else {
900 queryStr += "account.item_id <= ? "
901 }
902 args = append(args, fromItem)
903 }
904 // pagination
905 queryStr += "ORDER BY account.item_id "
906 if order == OrderAsc {
907 queryStr += " ASC "
908 } else {
909 queryStr += " DESC "
910 }
911 queryStr += fmt.Sprintf("LIMIT %d;", *limit)
912 query, argsQ, err := sqlx.In(queryStr, args...)
913 if err != nil {
914 return nil, 0, tracerr.Wrap(err)
915 }
916 query = hdb.dbRead.Rebind(query)
917
918 accounts := []*AccountAPI{}
919 if err := meddler.QueryAll(hdb.dbRead, &accounts, query, argsQ...); err != nil {
920 return nil, 0, tracerr.Wrap(err)
921 }
922 if len(accounts) == 0 {
923 return []AccountAPI{}, 0, nil
924 }
925
926 return db.SlicePtrsToSlice(accounts).([]AccountAPI),
927 accounts[0].TotalItems - uint64(len(accounts)), nil
928}
929
930// GetCommonAccountAPI returns the account associated to an account idx
931func (hdb *HistoryDB) GetCommonAccountAPI(idx common.Idx) (*common.Account, error) {
932 cancel, err := hdb.apiConnCon.Acquire()
933 defer cancel()
934 if err != nil {
935 return nil, tracerr.Wrap(err)
936 }
937 defer hdb.apiConnCon.Release()
938 account := &common.Account{}
939 err = meddler.QueryRow(
940 hdb.dbRead, account, `SELECT idx, token_id, batch_num, bjj, eth_addr
941 FROM account WHERE idx = $1;`, idx,
942 )
943 return account, tracerr.Wrap(err)
944}
945
946// GetCoordinatorAPI returns a coordinator by its bidderAddr
947func (hdb *HistoryDB) GetCoordinatorAPI(bidderAddr ethCommon.Address) (*CoordinatorAPI, error) {
948 cancel, err := hdb.apiConnCon.Acquire()
949 defer cancel()
950 if err != nil {
951 return nil, tracerr.Wrap(err)
952 }
953 defer hdb.apiConnCon.Release()
954 return hdb.getCoordinatorAPI(hdb.dbRead, bidderAddr)
955}
956
957func (hdb *HistoryDB) getCoordinatorAPI(d meddler.DB, bidderAddr ethCommon.Address) (*CoordinatorAPI, error) {
958 coordinator := &CoordinatorAPI{}
959 err := meddler.QueryRow(
960 d, coordinator,
961 "SELECT * FROM coordinator WHERE bidder_addr = $1 ORDER BY item_id DESC LIMIT 1;",
962 bidderAddr,
963 )
964 return coordinator, tracerr.Wrap(err)
965}
966
967// GetNodeInfoAPI retusnt he NodeInfo
968func (hdb *HistoryDB) GetNodeInfoAPI() (*NodeInfo, error) {
969 cancel, err := hdb.apiConnCon.Acquire()
970 defer cancel()
971 if err != nil {
972 return nil, tracerr.Wrap(err)
973 }
974 defer hdb.apiConnCon.Release()
975 return hdb.GetNodeInfo()
976}
977
978// GetBucketUpdatesInternalAPI returns the latest bucket updates
979func (hdb *HistoryDB) GetBucketUpdatesInternalAPI() ([]BucketUpdateAPI, error) {
980 var bucketUpdates []*BucketUpdateAPI
981 err := meddler.QueryAll(
982 hdb.dbRead, &bucketUpdates,
983 `SELECT num_bucket, withdrawals FROM bucket_update
984 WHERE item_id in(SELECT max(item_id) FROM bucket_update
985 group by num_bucket)
986 ORDER BY num_bucket ASC;`,
987 )
988 return db.SlicePtrsToSlice(bucketUpdates).([]BucketUpdateAPI), tracerr.Wrap(err)
989}
990
991// GetNextForgersInternalAPI returns next forgers
992func (hdb *HistoryDB) GetNextForgersInternalAPI(auctionVars *common.AuctionVariables,
993 auctionConsts *common.AuctionConstants,
994 lastBlock common.Block, currentSlot, lastClosedSlot int64) ([]NextForgerAPI, error) {
995 secondsPerBlock := int64(15) //nolint:gomnd
996 // currentSlot and lastClosedSlot included
997 limit := uint(lastClosedSlot - currentSlot + 1)
998 bids, _, err := hdb.getBestBidsAPI(hdb.dbRead, ¤tSlot, &lastClosedSlot, nil, &limit, "ASC")
999 if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
1000 return nil, tracerr.Wrap(err)
1001 }
1002 nextForgers := []NextForgerAPI{}
1003 // Get min bid info
1004 var minBidInfo []MinBidInfo
1005 if currentSlot >= auctionVars.DefaultSlotSetBidSlotNum {
1006 // All min bids can be calculated with the last update of AuctionVariables
1007
1008 minBidInfo = []MinBidInfo{{
1009 DefaultSlotSetBid: auctionVars.DefaultSlotSetBid,
1010 DefaultSlotSetBidSlotNum: auctionVars.DefaultSlotSetBidSlotNum,
1011 }}
1012 } else {
1013 // Get all the relevant updates from the DB
1014 minBidInfo, err = hdb.getMinBidInfo(hdb.dbRead, currentSlot, lastClosedSlot)
1015 if err != nil {
1016 return nil, tracerr.Wrap(err)
1017 }
1018 }
1019 // Create nextForger for each slot
1020 for i := currentSlot; i <= lastClosedSlot; i++ {
1021 fromBlock := i*int64(auctionConsts.BlocksPerSlot) +
1022 auctionConsts.GenesisBlockNum
1023 toBlock := (i+1)*int64(auctionConsts.BlocksPerSlot) +
1024 auctionConsts.GenesisBlockNum - 1
1025 nextForger := NextForgerAPI{
1026 Period: Period{
1027 SlotNum: i,
1028 FromBlock: fromBlock,
1029 ToBlock: toBlock,
1030 FromTimestamp: lastBlock.Timestamp.Add(time.Second *
1031 time.Duration(secondsPerBlock*(fromBlock-lastBlock.Num))),
1032 ToTimestamp: lastBlock.Timestamp.Add(time.Second *
1033 time.Duration(secondsPerBlock*(toBlock-lastBlock.Num))),
1034 },
1035 }
1036 foundForger := false
1037 // If there is a bid for a slot, get forger (coordinator)
1038 for j := range bids {
1039 slotNum := bids[j].SlotNum
1040 if slotNum == i {
1041 // There's a bid for the slot
1042 // Check if the bid is greater than the minimum required
1043 for i := 0; i < len(minBidInfo); i++ {
1044 // Find the most recent update
1045 if slotNum >= minBidInfo[i].DefaultSlotSetBidSlotNum {
1046 // Get min bid
1047 minBidSelector := slotNum % int64(len(auctionVars.DefaultSlotSetBid))
1048 minBid := minBidInfo[i].DefaultSlotSetBid[minBidSelector]
1049 // Check if the bid has beaten the minimum
1050 bid, ok := new(big.Int).SetString(string(bids[j].BidValue), 10)
1051 if !ok {
1052 return nil, tracerr.New("Wrong bid value, error parsing it as big.Int")
1053 }
1054 if minBid.Cmp(bid) == 1 {
1055 // Min bid is greater than bid, the slot will be forged by boot coordinator
1056 break
1057 }
1058 foundForger = true
1059 break
1060 }
1061 }
1062 if !foundForger { // There is no bid or it's smaller than the minimum
1063 break
1064 }
1065 coordinator, err := hdb.getCoordinatorAPI(hdb.dbRead, bids[j].Bidder)
1066 if err != nil {
1067 return nil, tracerr.Wrap(err)
1068 }
1069 nextForger.Coordinator = *coordinator
1070 break
1071 }
1072 }
1073 // If there is no bid, the coordinator that will forge is boot coordinator
1074 if !foundForger {
1075 nextForger.Coordinator = CoordinatorAPI{
1076 Forger: auctionVars.BootCoordinator,
1077 URL: auctionVars.BootCoordinatorURL,
1078 }
1079 }
1080 nextForgers = append(nextForgers, nextForger)
1081 }
1082 return nextForgers, nil
1083}
1084
1085// GetMetricsInternalAPI returns the MetricsAPI
1086func (hdb *HistoryDB) GetMetricsInternalAPI(lastBatchNum common.BatchNum) (metrics *MetricsAPI, poolLoad int64, err error) {
1087 metrics = &MetricsAPI{}
1088 type period struct {
1089 FromBatchNum common.BatchNum `meddler:"from_batch_num"`
1090 FromTimestamp time.Time `meddler:"from_timestamp"`
1091 ToBatchNum common.BatchNum `meddler:"-"`
1092 ToTimestamp time.Time `meddler:"to_timestamp"`
1093 }
1094 p := &period{
1095 ToBatchNum: lastBatchNum,
1096 }
1097 if err := meddler.QueryRow(
1098 hdb.dbRead, p, `SELECT
1099 COALESCE (MIN(batch.batch_num), 0) as from_batch_num,
1100 COALESCE (MIN(block.timestamp), NOW()) AS from_timestamp,
1101 COALESCE (MAX(block.timestamp), NOW()) AS to_timestamp
1102 FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num
1103 WHERE block.timestamp >= NOW() - INTERVAL '24 HOURS';`,
1104 ); err != nil {
1105 return nil, 0, tracerr.Wrap(err)
1106 }
1107 // Get the amount of txs of that period
1108 row := hdb.dbRead.QueryRow(
1109 `SELECT COUNT(*) as total_txs FROM tx WHERE tx.batch_num between $1 AND $2;`,
1110 p.FromBatchNum, p.ToBatchNum,
1111 )
1112 var nTxs int
1113 if err := row.Scan(&nTxs); err != nil {
1114 return nil, 0, tracerr.Wrap(err)
1115 }
1116 // Set txs/s
1117 seconds := p.ToTimestamp.Sub(p.FromTimestamp).Seconds()
1118 if seconds == 0 { // Avoid dividing by 0
1119 seconds++
1120 }
1121 metrics.TransactionsPerSecond = float64(nTxs) / seconds
1122 // Set txs/batch
1123 nBatches := p.ToBatchNum - p.FromBatchNum + 1
1124 if nBatches == 0 { // Avoid dividing by 0
1125 nBatches++
1126 }
1127 if (p.ToBatchNum - p.FromBatchNum) > 0 {
1128 metrics.TransactionsPerBatch = float64(nTxs) /
1129 float64(nBatches)
1130 } else {
1131 metrics.TransactionsPerBatch = 0
1132 }
1133 // Get total fee of that period
1134 row = hdb.dbRead.QueryRow(
1135 `SELECT COALESCE (SUM(total_fees_usd), 0) FROM batch WHERE batch_num between $1 AND $2;`,
1136 p.FromBatchNum, p.ToBatchNum,
1137 )
1138 var totalFee float64
1139 if err := row.Scan(&totalFee); err != nil {
1140 return nil, 0, tracerr.Wrap(err)
1141 }
1142 // Set batch frequency
1143 metrics.BatchFrequency = seconds / float64(nBatches)
1144 // Set avg transaction fee (only L2 txs have fee)
1145 row = hdb.dbRead.QueryRow(
1146 `SELECT COUNT(*) as total_txs FROM tx WHERE tx.batch_num between $1 AND $2 AND NOT is_l1;`,
1147 p.FromBatchNum, p.ToBatchNum,
1148 )
1149 var nL2Txs int
1150 if err := row.Scan(&nL2Txs); err != nil {
1151 return nil, 0, tracerr.Wrap(err)
1152 }
1153 if nL2Txs > 0 {
1154 metrics.AvgTransactionFee = totalFee / float64(nL2Txs)
1155 } else {
1156 metrics.AvgTransactionFee = 0
1157 }
1158 // Get and set amount of registered accounts
1159 type registeredAccounts struct {
1160 TokenAccounts int64 `meddler:"token_accounts"`
1161 Wallets int64 `meddler:"wallets"`
1162 }
1163 ra := ®isteredAccounts{}
1164 if err := meddler.QueryRow(
1165 hdb.dbRead, ra,
1166 `SELECT COUNT(*) AS token_accounts, COUNT(DISTINCT(bjj)) AS wallets FROM account;`,
1167 ); err != nil {
1168 return nil, 0, tracerr.Wrap(err)
1169 }
1170 metrics.TokenAccounts = ra.TokenAccounts
1171 metrics.Wallets = ra.Wallets
1172 // Get and set estimated time to forge L1 tx
1173 row = hdb.dbRead.QueryRow(
1174 `SELECT COALESCE (AVG(EXTRACT(EPOCH FROM (forged.timestamp - added.timestamp))), 0) FROM tx
1175 INNER JOIN block AS added ON tx.eth_block_num = added.eth_block_num
1176 INNER JOIN batch AS forged_batch ON tx.batch_num = forged_batch.batch_num
1177 INNER JOIN block AS forged ON forged_batch.eth_block_num = forged.eth_block_num
1178 WHERE tx.batch_num between $1 and $2 AND tx.is_l1 AND tx.user_origin;`,
1179 p.FromBatchNum, p.ToBatchNum,
1180 )
1181 var timeToForgeL1 float64
1182 if err := row.Scan(&timeToForgeL1); err != nil {
1183 return nil, 0, tracerr.Wrap(err)
1184 }
1185 metrics.EstimatedTimeToForgeL1 = timeToForgeL1
1186 // Get amount of txs in the pool
1187 row = hdb.dbRead.QueryRow(
1188 `SELECT COUNT(*) FROM tx_pool WHERE state = $1 AND NOT external_delete;`,
1189 common.PoolL2TxStatePending,
1190 )
1191 if err := row.Scan(&poolLoad); err != nil {
1192 return nil, 0, tracerr.Wrap(err)
1193 }
1194 return metrics, poolLoad, nil
1195}
1196
1197// GetStateAPI returns the StateAPI
1198func (hdb *HistoryDB) GetStateAPI() (*StateAPI, error) {
1199 cancel, err := hdb.apiConnCon.Acquire()
1200 defer cancel()
1201 if err != nil {
1202 return nil, tracerr.Wrap(err)
1203 }
1204 defer hdb.apiConnCon.Release()
1205 return hdb.getStateAPI(hdb.dbRead)
1206}