You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1054 lines
31 KiB

  1. package historydb
  2. import (
  3. "errors"
  4. "fmt"
  5. ethCommon "github.com/ethereum/go-ethereum/common"
  6. "github.com/hermeznetwork/hermez-node/common"
  7. "github.com/hermeznetwork/hermez-node/db"
  8. "github.com/hermeznetwork/tracerr"
  9. "github.com/iden3/go-iden3-crypto/babyjub"
  10. "github.com/jmoiron/sqlx"
  11. "github.com/russross/meddler"
  12. )
  13. // GetLastBlockAPI retrieve the block with the highest block number from the DB
  14. func (hdb *HistoryDB) GetLastBlockAPI() (*common.Block, error) {
  15. cancel, err := hdb.apiConnCon.Acquire()
  16. defer cancel()
  17. if err != nil {
  18. return nil, tracerr.Wrap(err)
  19. }
  20. defer hdb.apiConnCon.Release()
  21. return hdb.GetLastBlock()
  22. }
  23. // GetBatchAPI return the batch with the given batchNum
  24. func (hdb *HistoryDB) GetBatchAPI(batchNum common.BatchNum) (*BatchAPI, error) {
  25. cancel, err := hdb.apiConnCon.Acquire()
  26. defer cancel()
  27. if err != nil {
  28. return nil, tracerr.Wrap(err)
  29. }
  30. defer hdb.apiConnCon.Release()
  31. batch := &BatchAPI{}
  32. return batch, tracerr.Wrap(meddler.QueryRow(
  33. hdb.dbRead, batch,
  34. `SELECT batch.item_id, batch.batch_num, batch.eth_block_num,
  35. batch.forger_addr, batch.fees_collected, batch.total_fees_usd, batch.state_root,
  36. batch.num_accounts, batch.exit_root, batch.forge_l1_txs_num, batch.slot_num,
  37. block.timestamp, block.hash,
  38. COALESCE ((SELECT COUNT(*) FROM tx WHERE batch_num = batch.batch_num), 0) AS forged_txs
  39. FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num
  40. WHERE batch_num = $1;`, batchNum,
  41. ))
  42. }
  43. // GetBatchesAPI return the batches applying the given filters
  44. func (hdb *HistoryDB) GetBatchesAPI(
  45. minBatchNum, maxBatchNum, slotNum *uint,
  46. forgerAddr *ethCommon.Address,
  47. fromItem, limit *uint, order string,
  48. ) ([]BatchAPI, uint64, error) {
  49. cancel, err := hdb.apiConnCon.Acquire()
  50. defer cancel()
  51. if err != nil {
  52. return nil, 0, tracerr.Wrap(err)
  53. }
  54. defer hdb.apiConnCon.Release()
  55. var query string
  56. var args []interface{}
  57. queryStr := `SELECT batch.item_id, batch.batch_num, batch.eth_block_num,
  58. batch.forger_addr, batch.fees_collected, batch.total_fees_usd, batch.state_root,
  59. batch.num_accounts, batch.exit_root, batch.forge_l1_txs_num, batch.slot_num,
  60. block.timestamp, block.hash,
  61. COALESCE ((SELECT COUNT(*) FROM tx WHERE batch_num = batch.batch_num), 0) AS forged_txs,
  62. count(*) OVER() AS total_items
  63. FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num `
  64. // Apply filters
  65. nextIsAnd := false
  66. // minBatchNum filter
  67. if minBatchNum != nil {
  68. if nextIsAnd {
  69. queryStr += "AND "
  70. } else {
  71. queryStr += "WHERE "
  72. }
  73. queryStr += "batch.batch_num > ? "
  74. args = append(args, minBatchNum)
  75. nextIsAnd = true
  76. }
  77. // maxBatchNum filter
  78. if maxBatchNum != nil {
  79. if nextIsAnd {
  80. queryStr += "AND "
  81. } else {
  82. queryStr += "WHERE "
  83. }
  84. queryStr += "batch.batch_num < ? "
  85. args = append(args, maxBatchNum)
  86. nextIsAnd = true
  87. }
  88. // slotNum filter
  89. if slotNum != nil {
  90. if nextIsAnd {
  91. queryStr += "AND "
  92. } else {
  93. queryStr += "WHERE "
  94. }
  95. queryStr += "batch.slot_num = ? "
  96. args = append(args, slotNum)
  97. nextIsAnd = true
  98. }
  99. // forgerAddr filter
  100. if forgerAddr != nil {
  101. if nextIsAnd {
  102. queryStr += "AND "
  103. } else {
  104. queryStr += "WHERE "
  105. }
  106. queryStr += "batch.forger_addr = ? "
  107. args = append(args, forgerAddr)
  108. nextIsAnd = true
  109. }
  110. // pagination
  111. if fromItem != nil {
  112. if nextIsAnd {
  113. queryStr += "AND "
  114. } else {
  115. queryStr += "WHERE "
  116. }
  117. if order == OrderAsc {
  118. queryStr += "batch.item_id >= ? "
  119. } else {
  120. queryStr += "batch.item_id <= ? "
  121. }
  122. args = append(args, fromItem)
  123. }
  124. queryStr += "ORDER BY batch.item_id "
  125. if order == OrderAsc {
  126. queryStr += " ASC "
  127. } else {
  128. queryStr += " DESC "
  129. }
  130. queryStr += fmt.Sprintf("LIMIT %d;", *limit)
  131. query = hdb.dbRead.Rebind(queryStr)
  132. // log.Debug(query)
  133. batchPtrs := []*BatchAPI{}
  134. if err := meddler.QueryAll(hdb.dbRead, &batchPtrs, query, args...); err != nil {
  135. return nil, 0, tracerr.Wrap(err)
  136. }
  137. batches := db.SlicePtrsToSlice(batchPtrs).([]BatchAPI)
  138. if len(batches) == 0 {
  139. return batches, 0, nil
  140. }
  141. return batches, batches[0].TotalItems - uint64(len(batches)), nil
  142. }
  143. // GetBestBidAPI returns the best bid in specific slot by slotNum
  144. func (hdb *HistoryDB) GetBestBidAPI(slotNum *int64) (BidAPI, error) {
  145. bid := &BidAPI{}
  146. cancel, err := hdb.apiConnCon.Acquire()
  147. defer cancel()
  148. if err != nil {
  149. return *bid, tracerr.Wrap(err)
  150. }
  151. defer hdb.apiConnCon.Release()
  152. err = meddler.QueryRow(
  153. hdb.dbRead, bid, `SELECT bid.*, block.timestamp, coordinator.forger_addr, coordinator.url
  154. FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num
  155. INNER JOIN (
  156. SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
  157. GROUP BY bidder_addr
  158. ) c ON bid.bidder_addr = c.bidder_addr
  159. INNER JOIN coordinator ON c.item_id = coordinator.item_id
  160. WHERE slot_num = $1 ORDER BY item_id DESC LIMIT 1;`, slotNum,
  161. )
  162. return *bid, tracerr.Wrap(err)
  163. }
  164. // GetBestBidsAPI returns the best bid in specific slot by slotNum
  165. func (hdb *HistoryDB) GetBestBidsAPI(
  166. minSlotNum, maxSlotNum *int64,
  167. bidderAddr *ethCommon.Address,
  168. limit *uint, order string,
  169. ) ([]BidAPI, uint64, error) {
  170. cancel, err := hdb.apiConnCon.Acquire()
  171. defer cancel()
  172. if err != nil {
  173. return nil, 0, tracerr.Wrap(err)
  174. }
  175. defer hdb.apiConnCon.Release()
  176. var query string
  177. var args []interface{}
  178. // JOIN the best bid of each slot with the latest update of each coordinator
  179. queryStr := `SELECT b.*, block.timestamp, coordinator.forger_addr, coordinator.url,
  180. COUNT(*) OVER() AS total_items FROM (
  181. SELECT slot_num, MAX(item_id) as maxitem
  182. FROM bid GROUP BY slot_num
  183. )
  184. AS x INNER JOIN bid AS b ON b.item_id = x.maxitem
  185. INNER JOIN block ON b.eth_block_num = block.eth_block_num
  186. INNER JOIN (
  187. SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
  188. GROUP BY bidder_addr
  189. ) c ON b.bidder_addr = c.bidder_addr
  190. INNER JOIN coordinator ON c.item_id = coordinator.item_id
  191. WHERE (b.slot_num >= ? AND b.slot_num <= ?)`
  192. args = append(args, minSlotNum)
  193. args = append(args, maxSlotNum)
  194. // Apply filters
  195. if bidderAddr != nil {
  196. queryStr += " AND b.bidder_addr = ? "
  197. args = append(args, bidderAddr)
  198. }
  199. queryStr += " ORDER BY b.slot_num "
  200. if order == OrderAsc {
  201. queryStr += "ASC "
  202. } else {
  203. queryStr += "DESC "
  204. }
  205. if limit != nil {
  206. queryStr += fmt.Sprintf("LIMIT %d;", *limit)
  207. }
  208. query = hdb.dbRead.Rebind(queryStr)
  209. bidPtrs := []*BidAPI{}
  210. if err := meddler.QueryAll(hdb.dbRead, &bidPtrs, query, args...); err != nil {
  211. return nil, 0, tracerr.Wrap(err)
  212. }
  213. // log.Debug(query)
  214. bids := db.SlicePtrsToSlice(bidPtrs).([]BidAPI)
  215. if len(bids) == 0 {
  216. return bids, 0, nil
  217. }
  218. return bids, bids[0].TotalItems - uint64(len(bids)), nil
  219. }
  220. // GetBidsAPI return the bids applying the given filters
  221. func (hdb *HistoryDB) GetBidsAPI(
  222. slotNum *int64, bidderAddr *ethCommon.Address,
  223. fromItem, limit *uint, order string,
  224. ) ([]BidAPI, uint64, error) {
  225. cancel, err := hdb.apiConnCon.Acquire()
  226. defer cancel()
  227. if err != nil {
  228. return nil, 0, tracerr.Wrap(err)
  229. }
  230. defer hdb.apiConnCon.Release()
  231. var query string
  232. var args []interface{}
  233. // JOIN each bid with the latest update of each coordinator
  234. queryStr := `SELECT bid.*, block.timestamp, coord.forger_addr, coord.url,
  235. COUNT(*) OVER() AS total_items
  236. FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num
  237. INNER JOIN (
  238. SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
  239. GROUP BY bidder_addr
  240. ) c ON bid.bidder_addr = c.bidder_addr
  241. INNER JOIN coordinator coord ON c.item_id = coord.item_id `
  242. // Apply filters
  243. nextIsAnd := false
  244. // slotNum filter
  245. if slotNum != nil {
  246. if nextIsAnd {
  247. queryStr += "AND "
  248. } else {
  249. queryStr += "WHERE "
  250. }
  251. queryStr += "bid.slot_num = ? "
  252. args = append(args, slotNum)
  253. nextIsAnd = true
  254. }
  255. // bidder filter
  256. if bidderAddr != nil {
  257. if nextIsAnd {
  258. queryStr += "AND "
  259. } else {
  260. queryStr += "WHERE "
  261. }
  262. queryStr += "bid.bidder_addr = ? "
  263. args = append(args, bidderAddr)
  264. nextIsAnd = true
  265. }
  266. if fromItem != nil {
  267. if nextIsAnd {
  268. queryStr += "AND "
  269. } else {
  270. queryStr += "WHERE "
  271. }
  272. if order == OrderAsc {
  273. queryStr += "bid.item_id >= ? "
  274. } else {
  275. queryStr += "bid.item_id <= ? "
  276. }
  277. args = append(args, fromItem)
  278. }
  279. // pagination
  280. queryStr += "ORDER BY bid.item_id "
  281. if order == OrderAsc {
  282. queryStr += "ASC "
  283. } else {
  284. queryStr += "DESC "
  285. }
  286. queryStr += fmt.Sprintf("LIMIT %d;", *limit)
  287. query, argsQ, err := sqlx.In(queryStr, args...)
  288. if err != nil {
  289. return nil, 0, tracerr.Wrap(err)
  290. }
  291. query = hdb.dbRead.Rebind(query)
  292. bids := []*BidAPI{}
  293. if err := meddler.QueryAll(hdb.dbRead, &bids, query, argsQ...); err != nil {
  294. return nil, 0, tracerr.Wrap(err)
  295. }
  296. if len(bids) == 0 {
  297. return []BidAPI{}, 0, nil
  298. }
  299. return db.SlicePtrsToSlice(bids).([]BidAPI), bids[0].TotalItems - uint64(len(bids)), nil
  300. }
  301. // GetTokenAPI returns a token from the DB given a TokenID
  302. func (hdb *HistoryDB) GetTokenAPI(tokenID common.TokenID) (*TokenWithUSD, error) {
  303. cancel, err := hdb.apiConnCon.Acquire()
  304. defer cancel()
  305. if err != nil {
  306. return nil, tracerr.Wrap(err)
  307. }
  308. defer hdb.apiConnCon.Release()
  309. return hdb.GetToken(tokenID)
  310. }
  311. // GetTokensAPI returns a list of tokens from the DB
  312. func (hdb *HistoryDB) GetTokensAPI(
  313. ids []common.TokenID, symbols []string, name string, fromItem,
  314. limit *uint, order string,
  315. ) ([]TokenWithUSD, uint64, error) {
  316. cancel, err := hdb.apiConnCon.Acquire()
  317. defer cancel()
  318. if err != nil {
  319. return nil, 0, tracerr.Wrap(err)
  320. }
  321. defer hdb.apiConnCon.Release()
  322. var query string
  323. var args []interface{}
  324. queryStr := `SELECT * , COUNT(*) OVER() AS total_items FROM token `
  325. // Apply filters
  326. nextIsAnd := false
  327. if len(ids) > 0 {
  328. queryStr += "WHERE token_id IN (?) "
  329. nextIsAnd = true
  330. args = append(args, ids)
  331. }
  332. if len(symbols) > 0 {
  333. if nextIsAnd {
  334. queryStr += "AND "
  335. } else {
  336. queryStr += "WHERE "
  337. }
  338. queryStr += "symbol IN (?) "
  339. args = append(args, symbols)
  340. nextIsAnd = true
  341. }
  342. if name != "" {
  343. if nextIsAnd {
  344. queryStr += "AND "
  345. } else {
  346. queryStr += "WHERE "
  347. }
  348. queryStr += "name ~ ? "
  349. args = append(args, name)
  350. nextIsAnd = true
  351. }
  352. if fromItem != nil {
  353. if nextIsAnd {
  354. queryStr += "AND "
  355. } else {
  356. queryStr += "WHERE "
  357. }
  358. if order == OrderAsc {
  359. queryStr += "item_id >= ? "
  360. } else {
  361. queryStr += "item_id <= ? "
  362. }
  363. args = append(args, fromItem)
  364. }
  365. // pagination
  366. queryStr += "ORDER BY item_id "
  367. if order == OrderAsc {
  368. queryStr += "ASC "
  369. } else {
  370. queryStr += "DESC "
  371. }
  372. queryStr += fmt.Sprintf("LIMIT %d;", *limit)
  373. query, argsQ, err := sqlx.In(queryStr, args...)
  374. if err != nil {
  375. return nil, 0, tracerr.Wrap(err)
  376. }
  377. query = hdb.dbRead.Rebind(query)
  378. tokens := []*TokenWithUSD{}
  379. if err := meddler.QueryAll(hdb.dbRead, &tokens, query, argsQ...); err != nil {
  380. return nil, 0, tracerr.Wrap(err)
  381. }
  382. if len(tokens) == 0 {
  383. return []TokenWithUSD{}, 0, nil
  384. }
  385. return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), uint64(len(tokens)) - tokens[0].TotalItems, nil
  386. }
  387. // GetTxAPI returns a tx from the DB given a TxID
  388. func (hdb *HistoryDB) GetTxAPI(txID common.TxID) (*TxAPI, error) {
  389. // Warning: amount_success and deposit_amount_success have true as default for
  390. // performance reasons. The expected default value is false (when txs are unforged)
  391. // this case is handled at the function func (tx TxAPI) MarshalJSON() ([]byte, error)
  392. cancel, err := hdb.apiConnCon.Acquire()
  393. defer cancel()
  394. if err != nil {
  395. return nil, tracerr.Wrap(err)
  396. }
  397. defer hdb.apiConnCon.Release()
  398. tx := &TxAPI{}
  399. err = meddler.QueryRow(
  400. hdb.dbRead, tx, `SELECT tx.item_id, tx.is_l1, tx.id, tx.type, tx.position,
  401. hez_idx(tx.effective_from_idx, token.symbol) AS from_idx, tx.from_eth_addr, tx.from_bjj,
  402. hez_idx(tx.to_idx, token.symbol) AS to_idx, tx.to_eth_addr, tx.to_bjj,
  403. tx.amount, tx.amount_success, tx.token_id, tx.amount_usd,
  404. tx.batch_num, tx.eth_block_num, tx.to_forge_l1_txs_num, tx.user_origin,
  405. tx.deposit_amount, tx.deposit_amount_usd, tx.deposit_amount_success, tx.fee, tx.fee_usd, tx.nonce,
  406. token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
  407. token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
  408. token.usd_update, block.timestamp
  409. FROM tx INNER JOIN token ON tx.token_id = token.token_id
  410. INNER JOIN block ON tx.eth_block_num = block.eth_block_num
  411. WHERE tx.id = $1;`, txID,
  412. )
  413. return tx, tracerr.Wrap(err)
  414. }
  415. // GetTxsAPI returns a list of txs from the DB using the HistoryTx struct
  416. // and pagination info
  417. func (hdb *HistoryDB) GetTxsAPI(
  418. ethAddr *ethCommon.Address, bjj *babyjub.PublicKeyComp,
  419. tokenID *common.TokenID, idx *common.Idx, batchNum *uint, txType *common.TxType,
  420. fromItem, limit *uint, order string,
  421. ) ([]TxAPI, uint64, error) {
  422. // Warning: amount_success and deposit_amount_success have true as default for
  423. // performance reasons. The expected default value is false (when txs are unforged)
  424. // this case is handled at the function func (tx TxAPI) MarshalJSON() ([]byte, error)
  425. cancel, err := hdb.apiConnCon.Acquire()
  426. defer cancel()
  427. if err != nil {
  428. return nil, 0, tracerr.Wrap(err)
  429. }
  430. defer hdb.apiConnCon.Release()
  431. if ethAddr != nil && bjj != nil {
  432. return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
  433. }
  434. var query string
  435. var args []interface{}
  436. queryStr := `SELECT tx.item_id, tx.is_l1, tx.id, tx.type, tx.position,
  437. hez_idx(tx.effective_from_idx, token.symbol) AS from_idx, tx.from_eth_addr, tx.from_bjj,
  438. hez_idx(tx.to_idx, token.symbol) AS to_idx, tx.to_eth_addr, tx.to_bjj,
  439. tx.amount, tx.amount_success, tx.token_id, tx.amount_usd,
  440. tx.batch_num, tx.eth_block_num, tx.to_forge_l1_txs_num, tx.user_origin,
  441. tx.deposit_amount, tx.deposit_amount_usd, tx.deposit_amount_success, tx.fee, tx.fee_usd, tx.nonce,
  442. token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
  443. token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
  444. token.usd_update, block.timestamp, count(*) OVER() AS total_items
  445. FROM tx INNER JOIN token ON tx.token_id = token.token_id
  446. INNER JOIN block ON tx.eth_block_num = block.eth_block_num `
  447. // Apply filters
  448. nextIsAnd := false
  449. // ethAddr filter
  450. if ethAddr != nil {
  451. queryStr += "WHERE (tx.from_eth_addr = ? OR tx.to_eth_addr = ?) "
  452. nextIsAnd = true
  453. args = append(args, ethAddr, ethAddr)
  454. } else if bjj != nil { // bjj filter
  455. queryStr += "WHERE (tx.from_bjj = ? OR tx.to_bjj = ?) "
  456. nextIsAnd = true
  457. args = append(args, bjj, bjj)
  458. }
  459. // tokenID filter
  460. if tokenID != nil {
  461. if nextIsAnd {
  462. queryStr += "AND "
  463. } else {
  464. queryStr += "WHERE "
  465. }
  466. queryStr += "tx.token_id = ? "
  467. args = append(args, tokenID)
  468. nextIsAnd = true
  469. }
  470. // idx filter
  471. if idx != nil {
  472. if nextIsAnd {
  473. queryStr += "AND "
  474. } else {
  475. queryStr += "WHERE "
  476. }
  477. queryStr += "(tx.effective_from_idx = ? OR tx.to_idx = ?) "
  478. args = append(args, idx, idx)
  479. nextIsAnd = true
  480. }
  481. // batchNum filter
  482. if batchNum != nil {
  483. if nextIsAnd {
  484. queryStr += "AND "
  485. } else {
  486. queryStr += "WHERE "
  487. }
  488. queryStr += "tx.batch_num = ? "
  489. args = append(args, batchNum)
  490. nextIsAnd = true
  491. }
  492. // txType filter
  493. if txType != nil {
  494. if nextIsAnd {
  495. queryStr += "AND "
  496. } else {
  497. queryStr += "WHERE "
  498. }
  499. queryStr += "tx.type = ? "
  500. args = append(args, txType)
  501. nextIsAnd = true
  502. }
  503. if fromItem != nil {
  504. if nextIsAnd {
  505. queryStr += "AND "
  506. } else {
  507. queryStr += "WHERE "
  508. }
  509. if order == OrderAsc {
  510. queryStr += "tx.item_id >= ? "
  511. } else {
  512. queryStr += "tx.item_id <= ? "
  513. }
  514. args = append(args, fromItem)
  515. nextIsAnd = true
  516. }
  517. if nextIsAnd {
  518. queryStr += "AND "
  519. } else {
  520. queryStr += "WHERE "
  521. }
  522. queryStr += "tx.batch_num IS NOT NULL "
  523. // pagination
  524. queryStr += "ORDER BY tx.item_id "
  525. if order == OrderAsc {
  526. queryStr += " ASC "
  527. } else {
  528. queryStr += " DESC "
  529. }
  530. queryStr += fmt.Sprintf("LIMIT %d;", *limit)
  531. query = hdb.dbRead.Rebind(queryStr)
  532. // log.Debug(query)
  533. txsPtrs := []*TxAPI{}
  534. if err := meddler.QueryAll(hdb.dbRead, &txsPtrs, query, args...); err != nil {
  535. return nil, 0, tracerr.Wrap(err)
  536. }
  537. txs := db.SlicePtrsToSlice(txsPtrs).([]TxAPI)
  538. if len(txs) == 0 {
  539. return txs, 0, nil
  540. }
  541. return txs, txs[0].TotalItems - uint64(len(txs)), nil
  542. }
  543. // GetExitAPI returns a exit from the DB
  544. func (hdb *HistoryDB) GetExitAPI(batchNum *uint, idx *common.Idx) (*ExitAPI, error) {
  545. cancel, err := hdb.apiConnCon.Acquire()
  546. defer cancel()
  547. if err != nil {
  548. return nil, tracerr.Wrap(err)
  549. }
  550. defer hdb.apiConnCon.Release()
  551. exit := &ExitAPI{}
  552. err = meddler.QueryRow(
  553. hdb.dbRead, exit, `SELECT exit_tree.item_id, exit_tree.batch_num,
  554. hez_idx(exit_tree.account_idx, token.symbol) AS account_idx,
  555. account.bjj, account.eth_addr,
  556. exit_tree.merkle_proof, exit_tree.balance, exit_tree.instant_withdrawn,
  557. exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
  558. token.token_id, token.item_id AS token_item_id,
  559. token.eth_block_num AS token_block, token.eth_addr AS token_eth_addr, token.name, token.symbol,
  560. token.decimals, token.usd, token.usd_update
  561. FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx
  562. INNER JOIN token ON account.token_id = token.token_id
  563. WHERE exit_tree.batch_num = $1 AND exit_tree.account_idx = $2;`, batchNum, idx,
  564. )
  565. return exit, tracerr.Wrap(err)
  566. }
  567. // GetExitsAPI returns a list of exits from the DB and pagination info
  568. func (hdb *HistoryDB) GetExitsAPI(
  569. ethAddr *ethCommon.Address, bjj *babyjub.PublicKeyComp, tokenID *common.TokenID,
  570. idx *common.Idx, batchNum *uint, onlyPendingWithdraws *bool,
  571. fromItem, limit *uint, order string,
  572. ) ([]ExitAPI, uint64, error) {
  573. if ethAddr != nil && bjj != nil {
  574. return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
  575. }
  576. cancel, err := hdb.apiConnCon.Acquire()
  577. defer cancel()
  578. if err != nil {
  579. return nil, 0, tracerr.Wrap(err)
  580. }
  581. defer hdb.apiConnCon.Release()
  582. var query string
  583. var args []interface{}
  584. queryStr := `SELECT exit_tree.item_id, exit_tree.batch_num,
  585. hez_idx(exit_tree.account_idx, token.symbol) AS account_idx,
  586. account.bjj, account.eth_addr,
  587. exit_tree.merkle_proof, exit_tree.balance, exit_tree.instant_withdrawn,
  588. exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
  589. token.token_id, token.item_id AS token_item_id,
  590. token.eth_block_num AS token_block, token.eth_addr AS token_eth_addr, token.name, token.symbol,
  591. token.decimals, token.usd, token.usd_update, COUNT(*) OVER() AS total_items
  592. FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx
  593. INNER JOIN token ON account.token_id = token.token_id `
  594. // Apply filters
  595. nextIsAnd := false
  596. // ethAddr filter
  597. if ethAddr != nil {
  598. queryStr += "WHERE account.eth_addr = ? "
  599. nextIsAnd = true
  600. args = append(args, ethAddr)
  601. } else if bjj != nil { // bjj filter
  602. queryStr += "WHERE account.bjj = ? "
  603. nextIsAnd = true
  604. args = append(args, bjj)
  605. }
  606. // tokenID filter
  607. if tokenID != nil {
  608. if nextIsAnd {
  609. queryStr += "AND "
  610. } else {
  611. queryStr += "WHERE "
  612. }
  613. queryStr += "account.token_id = ? "
  614. args = append(args, tokenID)
  615. nextIsAnd = true
  616. }
  617. // idx filter
  618. if idx != nil {
  619. if nextIsAnd {
  620. queryStr += "AND "
  621. } else {
  622. queryStr += "WHERE "
  623. }
  624. queryStr += "exit_tree.account_idx = ? "
  625. args = append(args, idx)
  626. nextIsAnd = true
  627. }
  628. // batchNum filter
  629. if batchNum != nil {
  630. if nextIsAnd {
  631. queryStr += "AND "
  632. } else {
  633. queryStr += "WHERE "
  634. }
  635. queryStr += "exit_tree.batch_num = ? "
  636. args = append(args, batchNum)
  637. nextIsAnd = true
  638. }
  639. // onlyPendingWithdraws
  640. if onlyPendingWithdraws != nil {
  641. if *onlyPendingWithdraws {
  642. if nextIsAnd {
  643. queryStr += "AND "
  644. } else {
  645. queryStr += "WHERE "
  646. }
  647. queryStr += "(exit_tree.instant_withdrawn IS NULL AND exit_tree.delayed_withdrawn IS NULL) "
  648. nextIsAnd = true
  649. }
  650. }
  651. if fromItem != nil {
  652. if nextIsAnd {
  653. queryStr += "AND "
  654. } else {
  655. queryStr += "WHERE "
  656. }
  657. if order == OrderAsc {
  658. queryStr += "exit_tree.item_id >= ? "
  659. } else {
  660. queryStr += "exit_tree.item_id <= ? "
  661. }
  662. args = append(args, fromItem)
  663. // nextIsAnd = true
  664. }
  665. // pagination
  666. queryStr += "ORDER BY exit_tree.item_id "
  667. if order == OrderAsc {
  668. queryStr += " ASC "
  669. } else {
  670. queryStr += " DESC "
  671. }
  672. queryStr += fmt.Sprintf("LIMIT %d;", *limit)
  673. query = hdb.dbRead.Rebind(queryStr)
  674. // log.Debug(query)
  675. exits := []*ExitAPI{}
  676. if err := meddler.QueryAll(hdb.dbRead, &exits, query, args...); err != nil {
  677. return nil, 0, tracerr.Wrap(err)
  678. }
  679. if len(exits) == 0 {
  680. return []ExitAPI{}, 0, nil
  681. }
  682. return db.SlicePtrsToSlice(exits).([]ExitAPI), exits[0].TotalItems - uint64(len(exits)), nil
  683. }
  684. // GetBucketUpdatesAPI retrieves latest values for each bucket
  685. func (hdb *HistoryDB) GetBucketUpdatesAPI() ([]BucketUpdateAPI, error) {
  686. cancel, err := hdb.apiConnCon.Acquire()
  687. defer cancel()
  688. if err != nil {
  689. return nil, tracerr.Wrap(err)
  690. }
  691. defer hdb.apiConnCon.Release()
  692. var bucketUpdates []*BucketUpdateAPI
  693. err = meddler.QueryAll(
  694. hdb.dbRead, &bucketUpdates,
  695. `SELECT num_bucket, withdrawals FROM bucket_update
  696. WHERE item_id in(SELECT max(item_id) FROM bucket_update
  697. group by num_bucket)
  698. ORDER BY num_bucket ASC;`,
  699. )
  700. return db.SlicePtrsToSlice(bucketUpdates).([]BucketUpdateAPI), tracerr.Wrap(err)
  701. }
  702. // GetCoordinatorsAPI returns a list of coordinators from the DB and pagination info
  703. func (hdb *HistoryDB) GetCoordinatorsAPI(
  704. bidderAddr, forgerAddr *ethCommon.Address,
  705. fromItem, limit *uint, order string,
  706. ) ([]CoordinatorAPI, uint64, error) {
  707. cancel, err := hdb.apiConnCon.Acquire()
  708. defer cancel()
  709. if err != nil {
  710. return nil, 0, tracerr.Wrap(err)
  711. }
  712. defer hdb.apiConnCon.Release()
  713. var query string
  714. var args []interface{}
  715. queryStr := `SELECT coordinator.*, COUNT(*) OVER() AS total_items
  716. FROM coordinator INNER JOIN (
  717. SELECT MAX(item_id) AS item_id FROM coordinator
  718. GROUP BY bidder_addr
  719. ) c ON coordinator.item_id = c.item_id `
  720. // Apply filters
  721. nextIsAnd := false
  722. if bidderAddr != nil {
  723. queryStr += "WHERE bidder_addr = ? "
  724. nextIsAnd = true
  725. args = append(args, bidderAddr)
  726. }
  727. if forgerAddr != nil {
  728. if nextIsAnd {
  729. queryStr += "AND "
  730. } else {
  731. queryStr += "WHERE "
  732. }
  733. queryStr += "forger_addr = ? "
  734. nextIsAnd = true
  735. args = append(args, forgerAddr)
  736. }
  737. if fromItem != nil {
  738. if nextIsAnd {
  739. queryStr += "AND "
  740. } else {
  741. queryStr += "WHERE "
  742. }
  743. if order == OrderAsc {
  744. queryStr += "coordinator.item_id >= ? "
  745. } else {
  746. queryStr += "coordinator.item_id <= ? "
  747. }
  748. args = append(args, fromItem)
  749. }
  750. // pagination
  751. queryStr += "ORDER BY coordinator.item_id "
  752. if order == OrderAsc {
  753. queryStr += " ASC "
  754. } else {
  755. queryStr += " DESC "
  756. }
  757. queryStr += fmt.Sprintf("LIMIT %d;", *limit)
  758. query = hdb.dbRead.Rebind(queryStr)
  759. coordinators := []*CoordinatorAPI{}
  760. if err := meddler.QueryAll(hdb.dbRead, &coordinators, query, args...); err != nil {
  761. return nil, 0, tracerr.Wrap(err)
  762. }
  763. if len(coordinators) == 0 {
  764. return []CoordinatorAPI{}, 0, nil
  765. }
  766. return db.SlicePtrsToSlice(coordinators).([]CoordinatorAPI),
  767. coordinators[0].TotalItems - uint64(len(coordinators)), nil
  768. }
  769. // GetAuctionVarsAPI returns auction variables
  770. func (hdb *HistoryDB) GetAuctionVarsAPI() (*common.AuctionVariables, error) {
  771. cancel, err := hdb.apiConnCon.Acquire()
  772. defer cancel()
  773. if err != nil {
  774. return nil, tracerr.Wrap(err)
  775. }
  776. defer hdb.apiConnCon.Release()
  777. auctionVars := &common.AuctionVariables{}
  778. err = meddler.QueryRow(
  779. hdb.dbRead, auctionVars, `SELECT * FROM auction_vars;`,
  780. )
  781. return auctionVars, tracerr.Wrap(err)
  782. }
  783. // GetAuctionVarsUntilSetSlotNumAPI returns all the updates of the auction vars
  784. // from the last entry in which DefaultSlotSetBidSlotNum <= slotNum
  785. func (hdb *HistoryDB) GetAuctionVarsUntilSetSlotNumAPI(slotNum int64, maxItems int) ([]MinBidInfo, error) {
  786. cancel, err := hdb.apiConnCon.Acquire()
  787. defer cancel()
  788. if err != nil {
  789. return nil, tracerr.Wrap(err)
  790. }
  791. defer hdb.apiConnCon.Release()
  792. auctionVars := []*MinBidInfo{}
  793. query := `
  794. SELECT DISTINCT default_slot_set_bid, default_slot_set_bid_slot_num FROM auction_vars
  795. WHERE default_slot_set_bid_slot_num < $1
  796. ORDER BY default_slot_set_bid_slot_num DESC
  797. LIMIT $2;
  798. `
  799. err = meddler.QueryAll(hdb.dbRead, &auctionVars, query, slotNum, maxItems)
  800. if err != nil {
  801. return nil, tracerr.Wrap(err)
  802. }
  803. return db.SlicePtrsToSlice(auctionVars).([]MinBidInfo), nil
  804. }
  805. // GetAccountAPI returns an account by its index
  806. func (hdb *HistoryDB) GetAccountAPI(idx common.Idx) (*AccountAPI, error) {
  807. cancel, err := hdb.apiConnCon.Acquire()
  808. defer cancel()
  809. if err != nil {
  810. return nil, tracerr.Wrap(err)
  811. }
  812. defer hdb.apiConnCon.Release()
  813. account := &AccountAPI{}
  814. err = meddler.QueryRow(hdb.dbRead, account, `SELECT account.item_id, hez_idx(account.idx,
  815. token.symbol) as idx, account.batch_num, account.bjj, account.eth_addr,
  816. token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
  817. token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd,
  818. token.usd_update, account_update.nonce, account_update.balance
  819. FROM account inner JOIN (
  820. SELECT idx, nonce, balance
  821. FROM account_update
  822. WHERE idx = $1
  823. ORDER BY item_id DESC LIMIT 1
  824. ) AS account_update ON account_update.idx = account.idx
  825. INNER JOIN token ON account.token_id = token.token_id
  826. WHERE account.idx = $1;`, idx)
  827. if err != nil {
  828. return nil, tracerr.Wrap(err)
  829. }
  830. return account, nil
  831. }
  832. // GetAccountsAPI returns a list of accounts from the DB and pagination info
  833. func (hdb *HistoryDB) GetAccountsAPI(
  834. tokenIDs []common.TokenID, ethAddr *ethCommon.Address,
  835. bjj *babyjub.PublicKeyComp, fromItem, limit *uint, order string,
  836. ) ([]AccountAPI, uint64, error) {
  837. if ethAddr != nil && bjj != nil {
  838. return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
  839. }
  840. cancel, err := hdb.apiConnCon.Acquire()
  841. defer cancel()
  842. if err != nil {
  843. return nil, 0, tracerr.Wrap(err)
  844. }
  845. defer hdb.apiConnCon.Release()
  846. var query string
  847. var args []interface{}
  848. queryStr := `SELECT account.item_id, hez_idx(account.idx, token.symbol) as idx, account.batch_num,
  849. account.bjj, account.eth_addr, token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
  850. token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd, token.usd_update,
  851. account_update.nonce, account_update.balance, COUNT(*) OVER() AS total_items
  852. FROM account inner JOIN (
  853. SELECT DISTINCT idx,
  854. first_value(nonce) over(partition by idx ORDER BY item_id DESC) as nonce,
  855. first_value(balance) over(partition by idx ORDER BY item_id DESC) as balance
  856. FROM account_update
  857. ) AS account_update ON account_update.idx = account.idx INNER JOIN token ON account.token_id = token.token_id `
  858. // Apply filters
  859. nextIsAnd := false
  860. // ethAddr filter
  861. if ethAddr != nil {
  862. queryStr += "WHERE account.eth_addr = ? "
  863. nextIsAnd = true
  864. args = append(args, ethAddr)
  865. } else if bjj != nil { // bjj filter
  866. queryStr += "WHERE account.bjj = ? "
  867. nextIsAnd = true
  868. args = append(args, bjj)
  869. }
  870. // tokenID filter
  871. if len(tokenIDs) > 0 {
  872. if nextIsAnd {
  873. queryStr += "AND "
  874. } else {
  875. queryStr += "WHERE "
  876. }
  877. queryStr += "account.token_id IN (?) "
  878. args = append(args, tokenIDs)
  879. nextIsAnd = true
  880. }
  881. if fromItem != nil {
  882. if nextIsAnd {
  883. queryStr += "AND "
  884. } else {
  885. queryStr += "WHERE "
  886. }
  887. if order == OrderAsc {
  888. queryStr += "account.item_id >= ? "
  889. } else {
  890. queryStr += "account.item_id <= ? "
  891. }
  892. args = append(args, fromItem)
  893. }
  894. // pagination
  895. queryStr += "ORDER BY account.item_id "
  896. if order == OrderAsc {
  897. queryStr += " ASC "
  898. } else {
  899. queryStr += " DESC "
  900. }
  901. queryStr += fmt.Sprintf("LIMIT %d;", *limit)
  902. query, argsQ, err := sqlx.In(queryStr, args...)
  903. if err != nil {
  904. return nil, 0, tracerr.Wrap(err)
  905. }
  906. query = hdb.dbRead.Rebind(query)
  907. accounts := []*AccountAPI{}
  908. if err := meddler.QueryAll(hdb.dbRead, &accounts, query, argsQ...); err != nil {
  909. return nil, 0, tracerr.Wrap(err)
  910. }
  911. if len(accounts) == 0 {
  912. return []AccountAPI{}, 0, nil
  913. }
  914. return db.SlicePtrsToSlice(accounts).([]AccountAPI),
  915. accounts[0].TotalItems - uint64(len(accounts)), nil
  916. }
  917. // GetMetricsAPI returns metrics
  918. func (hdb *HistoryDB) GetMetricsAPI(lastBatchNum common.BatchNum) (*Metrics, error) {
  919. cancel, err := hdb.apiConnCon.Acquire()
  920. defer cancel()
  921. if err != nil {
  922. return nil, tracerr.Wrap(err)
  923. }
  924. defer hdb.apiConnCon.Release()
  925. metricsTotals := &MetricsTotals{}
  926. metrics := &Metrics{}
  927. err = meddler.QueryRow(
  928. hdb.dbRead, metricsTotals, `SELECT COUNT(tx.*) as total_txs,
  929. COALESCE (MIN(tx.batch_num), 0) as batch_num, COALESCE (MIN(block.timestamp),
  930. NOW()) AS min_timestamp, COALESCE (MAX(block.timestamp), NOW()) AS max_timestamp
  931. FROM tx INNER JOIN block ON tx.eth_block_num = block.eth_block_num
  932. WHERE block.timestamp >= NOW() - INTERVAL '24 HOURS';`)
  933. if err != nil {
  934. return nil, tracerr.Wrap(err)
  935. }
  936. seconds := metricsTotals.MaxTimestamp.Sub(metricsTotals.MinTimestamp).Seconds()
  937. // Avoid dividing by 0
  938. if seconds == 0 {
  939. seconds++
  940. }
  941. metrics.TransactionsPerSecond = float64(metricsTotals.TotalTransactions) / seconds
  942. if (lastBatchNum - metricsTotals.FirstBatchNum) > 0 {
  943. metrics.TransactionsPerBatch = float64(metricsTotals.TotalTransactions) /
  944. float64(lastBatchNum-metricsTotals.FirstBatchNum+1)
  945. } else {
  946. metrics.TransactionsPerBatch = float64(0)
  947. }
  948. err = meddler.QueryRow(
  949. hdb.dbRead, metricsTotals, `SELECT COUNT(*) AS total_batches,
  950. COALESCE (SUM(total_fees_usd), 0) AS total_fees FROM batch
  951. WHERE batch_num > $1;`, metricsTotals.FirstBatchNum)
  952. if err != nil {
  953. return nil, tracerr.Wrap(err)
  954. }
  955. if metricsTotals.TotalBatches > 0 {
  956. metrics.BatchFrequency = seconds / float64(metricsTotals.TotalBatches)
  957. } else {
  958. metrics.BatchFrequency = 0
  959. }
  960. if metricsTotals.TotalTransactions > 0 {
  961. metrics.AvgTransactionFee = metricsTotals.TotalFeesUSD / float64(metricsTotals.TotalTransactions)
  962. } else {
  963. metrics.AvgTransactionFee = 0
  964. }
  965. err = meddler.QueryRow(
  966. hdb.dbRead, metrics,
  967. `SELECT COUNT(*) AS total_bjjs, COUNT(DISTINCT(bjj)) AS total_accounts FROM account;`)
  968. if err != nil {
  969. return nil, tracerr.Wrap(err)
  970. }
  971. return metrics, nil
  972. }
  973. // GetAvgTxFeeAPI returns average transaction fee of the last 1h
  974. func (hdb *HistoryDB) GetAvgTxFeeAPI() (float64, error) {
  975. cancel, err := hdb.apiConnCon.Acquire()
  976. defer cancel()
  977. if err != nil {
  978. return 0, tracerr.Wrap(err)
  979. }
  980. defer hdb.apiConnCon.Release()
  981. metricsTotals := &MetricsTotals{}
  982. err = meddler.QueryRow(
  983. hdb.dbRead, metricsTotals, `SELECT COUNT(tx.*) as total_txs,
  984. COALESCE (MIN(tx.batch_num), 0) as batch_num
  985. FROM tx INNER JOIN block ON tx.eth_block_num = block.eth_block_num
  986. WHERE block.timestamp >= NOW() - INTERVAL '1 HOURS';`)
  987. if err != nil {
  988. return 0, tracerr.Wrap(err)
  989. }
  990. err = meddler.QueryRow(
  991. hdb.dbRead, metricsTotals, `SELECT COUNT(*) AS total_batches,
  992. COALESCE (SUM(total_fees_usd), 0) AS total_fees FROM batch
  993. WHERE batch_num > $1;`, metricsTotals.FirstBatchNum)
  994. if err != nil {
  995. return 0, tracerr.Wrap(err)
  996. }
  997. var avgTransactionFee float64
  998. if metricsTotals.TotalTransactions > 0 {
  999. avgTransactionFee = metricsTotals.TotalFeesUSD / float64(metricsTotals.TotalTransactions)
  1000. } else {
  1001. avgTransactionFee = 0
  1002. }
  1003. return avgTransactionFee, nil
  1004. }
  1005. // GetCommonAccountAPI returns the account associated to an account idx
  1006. func (hdb *HistoryDB) GetCommonAccountAPI(idx common.Idx) (*common.Account, error) {
  1007. cancel, err := hdb.apiConnCon.Acquire()
  1008. defer cancel()
  1009. if err != nil {
  1010. return nil, tracerr.Wrap(err)
  1011. }
  1012. defer hdb.apiConnCon.Release()
  1013. account := &common.Account{}
  1014. err = meddler.QueryRow(
  1015. hdb.dbRead, account, `SELECT * FROM account WHERE idx = $1;`, idx,
  1016. )
  1017. return account, tracerr.Wrap(err)
  1018. }