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.

509 lines
17 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. package historydb
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "math"
  6. "math/big"
  7. "time"
  8. ethCommon "github.com/ethereum/go-ethereum/common"
  9. "github.com/hermeznetwork/hermez-node/apitypes"
  10. "github.com/hermeznetwork/hermez-node/common"
  11. "github.com/hermeznetwork/hermez-node/db"
  12. "github.com/hermeznetwork/tracerr"
  13. "github.com/jmoiron/sqlx"
  14. "github.com/russross/meddler"
  15. )
  16. const (
  17. createAccountExtraFeePercentage float64 = 2
  18. createAccountInternalExtraFeePercentage float64 = 2.5
  19. )
  20. // NodePublicConfig is the configuration of the node that is exposed via API
  21. type NodePublicConfig struct {
  22. // ForgeDelay in seconds
  23. ForgeDelay float64 `json:"forgeDelay"`
  24. }
  25. type Period struct {
  26. SlotNum int64 `json:"slotNum"`
  27. FromBlock int64 `json:"fromBlock"`
  28. ToBlock int64 `json:"toBlock"`
  29. FromTimestamp time.Time `json:"fromTimestamp"`
  30. ToTimestamp time.Time `json:"toTimestamp"`
  31. }
  32. type NextForger struct {
  33. Coordinator CoordinatorAPI `json:"coordinator"`
  34. Period Period `json:"period"`
  35. }
  36. type Network struct {
  37. LastEthBlock int64 `json:"lastEthereumBlock"`
  38. LastSyncBlock int64 `json:"lastSynchedBlock"`
  39. LastBatch *BatchAPI `json:"lastBatch"`
  40. CurrentSlot int64 `json:"currentSlot"`
  41. NextForgers []NextForger `json:"nextForgers"`
  42. }
  43. type StateAPI struct {
  44. NodePublicConfig NodePublicConfig `json:"nodeConfig"`
  45. Network Network `json:"network"`
  46. Metrics Metrics `json:"metrics"`
  47. Rollup RollupVariablesAPI `json:"rollup"`
  48. Auction AuctionVariablesAPI `json:"auction"`
  49. WithdrawalDelayer common.WDelayerVariables `json:"withdrawalDelayer"`
  50. RecommendedFee common.RecommendedFee `json:"recommendedFee"`
  51. }
  52. type Constants struct {
  53. RollupConstants common.RollupConstants
  54. AuctionConstants common.AuctionConstants
  55. WDelayerConstants common.WDelayerConstants
  56. ChainID uint16
  57. HermezAddress ethCommon.Address
  58. }
  59. type NodeInfo struct {
  60. ItemID int `meddler:"item_id"`
  61. MaxPoolTxs *uint32 `meddler:"max_pool_txs"`
  62. MinFeeUSD *float64 `meddler:"min_fee"`
  63. StateAPI *StateAPI `meddler:"state,json"`
  64. Constants *Constants `meddler:"constants,json"`
  65. }
  66. func (hdb *HistoryDB) GetNodeInfo() (*NodeInfo, error) {
  67. ni := &NodeInfo{}
  68. err := meddler.QueryRow(
  69. hdb.dbRead, ni, `SELECT * FROM node_info ORDER BY item_id DESC LIMIT 1;`,
  70. )
  71. return ni, tracerr.Wrap(err)
  72. }
  73. func (hdb *HistoryDB) SetConstants(constants *Constants) error {
  74. _constants := struct {
  75. Constants *Constants `meddler:"constants,json"`
  76. }{constants}
  77. values, err := meddler.Default.Values(&_constants, false)
  78. if err != nil {
  79. return tracerr.Wrap(err)
  80. }
  81. _, err = hdb.dbWrite.Exec(
  82. "UPDATE node_info SET constants = $1 WHERE item_id = 1;",
  83. values[0],
  84. )
  85. return tracerr.Wrap(err)
  86. }
  87. func (hdb *HistoryDB) GetConstants() (*Constants, error) {
  88. var nodeInfo NodeInfo
  89. err := meddler.QueryRow(
  90. hdb.dbRead, &nodeInfo,
  91. "SELECT constants FROM node_info WHERE item_id = 1;",
  92. )
  93. return nodeInfo.Constants, tracerr.Wrap(err)
  94. }
  95. func (hdb *HistoryDB) SetInitialNodeInfo(maxPoolTxs uint32, minFeeUSD float64, constants *Constants) error {
  96. ni := &NodeInfo{
  97. MaxPoolTxs: &maxPoolTxs,
  98. MinFeeUSD: &minFeeUSD,
  99. Constants: constants,
  100. }
  101. return tracerr.Wrap(meddler.Insert(hdb.dbWrite, "node_info", ni))
  102. }
  103. // SetRollupVariables set Status.Rollup variables
  104. func (hdb *HistoryDB) SetRollupVariables(rollupVariables *common.RollupVariables) error {
  105. setUpdatedNodeInfo := func(txn *sqlx.Tx, ni *NodeInfo) error {
  106. rollupVars := NewRollupVariablesAPI(rollupVariables)
  107. ni.StateAPI.Rollup = *rollupVars
  108. return nil
  109. }
  110. return hdb.updateNodeInfo(setUpdatedNodeInfo)
  111. }
  112. // SetWDelayerVariables set Status.WithdrawalDelayer variables
  113. func (hdb *HistoryDB) SetWDelayerVariables(wDelayerVariables *common.WDelayerVariables) error {
  114. setUpdatedNodeInfo := func(txn *sqlx.Tx, ni *NodeInfo) error {
  115. ni.StateAPI.WithdrawalDelayer = *wDelayerVariables
  116. return nil
  117. }
  118. return hdb.updateNodeInfo(setUpdatedNodeInfo)
  119. }
  120. // SetAuctionVariables set Status.Auction variables
  121. func (hdb *HistoryDB) SetAuctionVariables(auctionVariables *common.AuctionVariables) error {
  122. setUpdatedNodeInfo := func(txn *sqlx.Tx, ni *NodeInfo) error {
  123. auctionVars := NewAuctionVariablesAPI(auctionVariables)
  124. ni.StateAPI.Auction = *auctionVars
  125. return nil
  126. }
  127. return hdb.updateNodeInfo(setUpdatedNodeInfo)
  128. }
  129. // UpdateNetworkInfoBlock update Status.Network block related information
  130. func (hdb *HistoryDB) UpdateNetworkInfoBlock(
  131. lastEthBlock, lastSyncBlock common.Block,
  132. ) error {
  133. setUpdatedNodeInfo := func(txn *sqlx.Tx, ni *NodeInfo) error {
  134. ni.StateAPI.Network.LastSyncBlock = lastSyncBlock.Num
  135. ni.StateAPI.Network.LastEthBlock = lastEthBlock.Num
  136. return nil
  137. }
  138. return hdb.updateNodeInfo(setUpdatedNodeInfo)
  139. }
  140. // UpdateNetworkInfo update Status.Network information
  141. func (hdb *HistoryDB) UpdateNetworkInfo(
  142. lastEthBlock, lastSyncBlock common.Block,
  143. lastBatchNum common.BatchNum, currentSlot int64,
  144. ) error {
  145. setUpdatedNodeInfo := func(txn *sqlx.Tx, ni *NodeInfo) error {
  146. // Get last batch in API format
  147. lastBatch, err := hdb.getBatchAPI(txn, lastBatchNum)
  148. if tracerr.Unwrap(err) == sql.ErrNoRows {
  149. lastBatch = nil
  150. } else if err != nil {
  151. return tracerr.Wrap(err)
  152. }
  153. // Get next forrgers
  154. lastClosedSlot := currentSlot + int64(ni.StateAPI.Auction.ClosedAuctionSlots)
  155. nextForgers, err := hdb.getNextForgers(txn, ni, lastSyncBlock, currentSlot, lastClosedSlot)
  156. if tracerr.Unwrap(err) == sql.ErrNoRows {
  157. nextForgers = nil
  158. } else if err != nil {
  159. return tracerr.Wrap(err)
  160. }
  161. // Get buckets withdrawals
  162. var bucketUpdatesPtrs []*BucketUpdateAPI
  163. var bucketUpdates []BucketUpdateAPI
  164. err = meddler.QueryAll(
  165. txn, &bucketUpdatesPtrs,
  166. `SELECT num_bucket, withdrawals FROM bucket_update
  167. WHERE item_id in(SELECT max(item_id) FROM bucket_update
  168. group by num_bucket)
  169. ORDER BY num_bucket ASC;`,
  170. )
  171. if err == sql.ErrNoRows {
  172. bucketUpdates = nil
  173. } else if err != nil {
  174. return tracerr.Wrap(err)
  175. } else {
  176. bucketUpdates = db.SlicePtrsToSlice(bucketUpdatesPtrs).([]BucketUpdateAPI)
  177. }
  178. // Update NodeInfo struct
  179. for i, bucketParams := range ni.StateAPI.Rollup.Buckets {
  180. for _, bucketUpdate := range bucketUpdates {
  181. if bucketUpdate.NumBucket == i {
  182. bucketParams.Withdrawals = bucketUpdate.Withdrawals
  183. ni.StateAPI.Rollup.Buckets[i] = bucketParams
  184. break
  185. }
  186. }
  187. }
  188. ni.StateAPI.Network.LastSyncBlock = lastSyncBlock.Num
  189. ni.StateAPI.Network.LastEthBlock = lastEthBlock.Num
  190. ni.StateAPI.Network.LastBatch = lastBatch
  191. ni.StateAPI.Network.CurrentSlot = currentSlot
  192. ni.StateAPI.Network.NextForgers = nextForgers
  193. return nil
  194. }
  195. return hdb.updateNodeInfo(setUpdatedNodeInfo)
  196. }
  197. // apiSlotToBigInts converts from [6]*apitypes.BigIntStr to [6]*big.Int
  198. func apiSlotToBigInts(defaultSlotSetBid [6]*apitypes.BigIntStr) ([6]*big.Int, error) {
  199. var slots [6]*big.Int
  200. for i, slot := range defaultSlotSetBid {
  201. bigInt, ok := new(big.Int).SetString(string(*slot), 10)
  202. if !ok {
  203. return slots, tracerr.Wrap(fmt.Errorf("can't convert %T into big.Int", slot))
  204. }
  205. slots[i] = bigInt
  206. }
  207. return slots, nil
  208. }
  209. // getNextForgers returns next forgers
  210. func (hdb *HistoryDB) getNextForgers(txn *sqlx.Tx, ni *NodeInfo, lastBlock common.Block, currentSlot, lastClosedSlot int64) ([]NextForger, error) {
  211. secondsPerBlock := int64(15) //nolint:gomnd
  212. // currentSlot and lastClosedSlot included
  213. limit := uint(lastClosedSlot - currentSlot + 1)
  214. bids, _, err := hdb.getBestBidsAPI(txn, &currentSlot, &lastClosedSlot, nil, &limit, "ASC")
  215. if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
  216. return nil, tracerr.Wrap(err)
  217. }
  218. nextForgers := []NextForger{}
  219. // Get min bid info
  220. var minBidInfo []MinBidInfo
  221. if currentSlot >= ni.StateAPI.Auction.DefaultSlotSetBidSlotNum {
  222. // All min bids can be calculated with the last update of AuctionVariables
  223. bigIntSlots, err := apiSlotToBigInts(ni.StateAPI.Auction.DefaultSlotSetBid)
  224. if err != nil {
  225. return nil, tracerr.Wrap(err)
  226. }
  227. minBidInfo = []MinBidInfo{{
  228. DefaultSlotSetBid: bigIntSlots,
  229. DefaultSlotSetBidSlotNum: ni.StateAPI.Auction.DefaultSlotSetBidSlotNum,
  230. }}
  231. } else {
  232. // Get all the relevant updates from the DB
  233. minBidInfoPtrs := []*MinBidInfo{}
  234. query := `
  235. SELECT DISTINCT default_slot_set_bid, default_slot_set_bid_slot_num FROM auction_vars
  236. WHERE default_slot_set_bid_slot_num < $1
  237. ORDER BY default_slot_set_bid_slot_num DESC
  238. LIMIT $2;`
  239. if err := meddler.QueryAll(
  240. txn, &minBidInfoPtrs, query, lastClosedSlot, int(lastClosedSlot-currentSlot)+1,
  241. ); err != nil {
  242. return nil, tracerr.Wrap(err)
  243. }
  244. minBidInfo = db.SlicePtrsToSlice(minBidInfoPtrs).([]MinBidInfo)
  245. }
  246. // Create nextForger for each slot
  247. for i := currentSlot; i <= lastClosedSlot; i++ {
  248. fromBlock := i*int64(ni.Constants.AuctionConstants.BlocksPerSlot) + ni.Constants.AuctionConstants.GenesisBlockNum
  249. toBlock := (i+1)*int64(ni.Constants.AuctionConstants.BlocksPerSlot) + ni.Constants.AuctionConstants.GenesisBlockNum - 1
  250. nextForger := NextForger{
  251. Period: Period{
  252. SlotNum: i,
  253. FromBlock: fromBlock,
  254. ToBlock: toBlock,
  255. FromTimestamp: lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(fromBlock-lastBlock.Num))),
  256. ToTimestamp: lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(toBlock-lastBlock.Num))),
  257. },
  258. }
  259. foundForger := false
  260. // If there is a bid for a slot, get forger (coordinator)
  261. for j := range bids {
  262. slotNum := bids[j].SlotNum
  263. if slotNum == i {
  264. // There's a bid for the slot
  265. // Check if the bid is greater than the minimum required
  266. for i := 0; i < len(minBidInfo); i++ {
  267. // Find the most recent update
  268. if slotNum >= minBidInfo[i].DefaultSlotSetBidSlotNum {
  269. // Get min bid
  270. minBidSelector := slotNum % int64(len(ni.StateAPI.Auction.DefaultSlotSetBid))
  271. minBid := minBidInfo[i].DefaultSlotSetBid[minBidSelector]
  272. // Check if the bid has beaten the minimum
  273. bid, ok := new(big.Int).SetString(string(bids[j].BidValue), 10)
  274. if !ok {
  275. return nil, tracerr.New("Wrong bid value, error parsing it as big.Int")
  276. }
  277. if minBid.Cmp(bid) == 1 {
  278. // Min bid is greater than bid, the slot will be forged by boot coordinator
  279. break
  280. }
  281. foundForger = true
  282. break
  283. }
  284. }
  285. if !foundForger { // There is no bid or it's smaller than the minimum
  286. break
  287. }
  288. coordinator, err := hdb.GetCoordinatorAPI(bids[j].Bidder)
  289. if err != nil {
  290. return nil, tracerr.Wrap(err)
  291. }
  292. nextForger.Coordinator = *coordinator
  293. break
  294. }
  295. }
  296. // If there is no bid, the coordinator that will forge is boot coordinator
  297. if !foundForger {
  298. nextForger.Coordinator = CoordinatorAPI{
  299. Forger: ni.StateAPI.Auction.BootCoordinator,
  300. URL: ni.StateAPI.Auction.BootCoordinatorURL,
  301. }
  302. }
  303. nextForgers = append(nextForgers, nextForger)
  304. }
  305. return nextForgers, nil
  306. }
  307. // UpdateMetrics update Status.Metrics information
  308. func (hdb *HistoryDB) UpdateMetrics() error {
  309. setUpdatedNodeInfo := func(txn *sqlx.Tx, ni *NodeInfo) error {
  310. // Get the first and last batch of the last 24h and their timestamps
  311. if ni.StateAPI.Network.LastBatch == nil {
  312. return nil
  313. }
  314. type period struct {
  315. FromBatchNum common.BatchNum `meddler:"from_batch_num"`
  316. FromTimestamp time.Time `meddler:"from_timestamp"`
  317. ToBatchNum common.BatchNum `meddler:"-"`
  318. ToTimestamp time.Time `meddler:"to_timestamp"`
  319. }
  320. p := &period{
  321. ToBatchNum: ni.StateAPI.Network.LastBatch.BatchNum,
  322. }
  323. if err := meddler.QueryRow(
  324. txn, p, `SELECT
  325. COALESCE (MIN(batch.batch_num), 0) as from_batch_num,
  326. COALESCE (MIN(block.timestamp), NOW()) AS from_timestamp,
  327. COALESCE (MAX(block.timestamp), NOW()) AS to_timestamp
  328. FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num
  329. WHERE block.timestamp >= NOW() - INTERVAL '24 HOURS';`,
  330. ); err != nil {
  331. return tracerr.Wrap(err)
  332. }
  333. // Get the amount of txs of that period
  334. row := txn.QueryRow(
  335. `SELECT COUNT(*) as total_txs FROM tx WHERE tx.batch_num between $1 AND $2;`,
  336. p.FromBatchNum, p.ToBatchNum,
  337. )
  338. var nTxs int
  339. if err := row.Scan(&nTxs); err != nil {
  340. return tracerr.Wrap(err)
  341. }
  342. // Set txs/s
  343. seconds := p.ToTimestamp.Sub(p.FromTimestamp).Seconds()
  344. if seconds == 0 { // Avoid dividing by 0
  345. seconds++
  346. }
  347. ni.StateAPI.Metrics.TransactionsPerSecond = float64(nTxs) / seconds
  348. // Set txs/batch
  349. nBatches := p.ToBatchNum - p.FromBatchNum
  350. if nBatches == 0 { // Avoid dividing by 0
  351. nBatches++
  352. }
  353. if (p.ToBatchNum - p.FromBatchNum) > 0 {
  354. ni.StateAPI.Metrics.TransactionsPerBatch = float64(nTxs) /
  355. float64(nBatches)
  356. } else {
  357. ni.StateAPI.Metrics.TransactionsPerBatch = 0
  358. }
  359. // Get total fee of that period
  360. row = txn.QueryRow(
  361. `SELECT COALESCE (SUM(total_fees_usd), 0) FROM batch WHERE batch_num between $1 AND $2;`,
  362. p.FromBatchNum, p.ToBatchNum,
  363. )
  364. var totalFee float64
  365. if err := row.Scan(&totalFee); err != nil {
  366. return tracerr.Wrap(err)
  367. }
  368. // Set batch frequency
  369. ni.StateAPI.Metrics.BatchFrequency = seconds / float64(nBatches)
  370. if nTxs > 0 {
  371. ni.StateAPI.Metrics.AvgTransactionFee = totalFee / float64(nTxs)
  372. } else {
  373. ni.StateAPI.Metrics.AvgTransactionFee = 0
  374. }
  375. // Get and set amount of registered accounts
  376. type registeredAccounts struct {
  377. TotalIdx int64 `meddler:"total_idx"`
  378. TotalBJJ int64 `meddler:"total_bjj"`
  379. }
  380. ra := &registeredAccounts{}
  381. if err := meddler.QueryRow(
  382. txn, ra,
  383. `SELECT COUNT(*) AS total_bjj, COUNT(DISTINCT(bjj)) AS total_idx FROM account;`,
  384. ); err != nil {
  385. return tracerr.Wrap(err)
  386. }
  387. ni.StateAPI.Metrics.TotalAccounts = ra.TotalIdx
  388. ni.StateAPI.Metrics.TotalBJJs = ra.TotalBJJ
  389. // Get and set estimated time to forge L1 tx
  390. row = txn.QueryRow(
  391. `SELECT COALESCE (AVG(EXTRACT(EPOCH FROM (forged.timestamp - added.timestamp))), 0) FROM tx
  392. INNER JOIN block AS added ON tx.eth_block_num = added.eth_block_num
  393. INNER JOIN batch AS forged_batch ON tx.batch_num = forged_batch.batch_num
  394. INNER JOIN block AS forged ON forged_batch.eth_block_num = forged.eth_block_num
  395. WHERE tx.batch_num between $1 and $2 AND tx.is_l1 AND tx.user_origin;`,
  396. p.FromBatchNum, p.ToBatchNum,
  397. )
  398. var timeToForgeL1 float64
  399. if err := row.Scan(&timeToForgeL1); err != nil {
  400. return tracerr.Wrap(err)
  401. }
  402. ni.StateAPI.Metrics.EstimatedTimeToForgeL1 = timeToForgeL1
  403. return nil
  404. }
  405. return hdb.updateNodeInfo(setUpdatedNodeInfo)
  406. }
  407. // UpdateRecommendedFee update Status.RecommendedFee information
  408. func (hdb *HistoryDB) UpdateRecommendedFee() error {
  409. setUpdatedNodeInfo := func(txn *sqlx.Tx, ni *NodeInfo) error {
  410. // Get total txs and the batch of the first selected tx of the last hour
  411. type totalTxsSinceBatchNum struct {
  412. TotalTxs int `meddler:"total_txs"`
  413. FirstBatchNum common.BatchNum `meddler:"batch_num"`
  414. }
  415. ttsbn := &totalTxsSinceBatchNum{}
  416. if err := meddler.QueryRow(
  417. txn, ttsbn, `SELECT COUNT(tx.*) as total_txs,
  418. COALESCE (MIN(tx.batch_num), 0) as batch_num
  419. FROM tx INNER JOIN block ON tx.eth_block_num = block.eth_block_num
  420. WHERE block.timestamp >= NOW() - INTERVAL '1 HOURS';`,
  421. ); err != nil {
  422. return tracerr.Wrap(err)
  423. }
  424. // Get the amount of batches and acumulated fees for the last hour
  425. type totalBatchesAndFee struct {
  426. TotalBatches int `meddler:"total_batches"`
  427. TotalFees float64 `meddler:"total_fees"`
  428. }
  429. tbf := &totalBatchesAndFee{}
  430. if err := meddler.QueryRow(
  431. txn, tbf, `SELECT COUNT(*) AS total_batches,
  432. COALESCE (SUM(total_fees_usd), 0) AS total_fees FROM batch
  433. WHERE batch_num > $1;`, ttsbn.FirstBatchNum,
  434. ); err != nil {
  435. return tracerr.Wrap(err)
  436. }
  437. // Update NodeInfo struct
  438. var avgTransactionFee float64
  439. if ttsbn.TotalTxs > 0 {
  440. avgTransactionFee = tbf.TotalFees / float64(ttsbn.TotalTxs)
  441. } else {
  442. avgTransactionFee = 0
  443. }
  444. ni.StateAPI.RecommendedFee.ExistingAccount =
  445. math.Max(avgTransactionFee, *ni.MinFeeUSD)
  446. ni.StateAPI.RecommendedFee.CreatesAccount =
  447. math.Max(createAccountExtraFeePercentage*avgTransactionFee, *ni.MinFeeUSD)
  448. ni.StateAPI.RecommendedFee.CreatesAccountAndRegister =
  449. math.Max(createAccountInternalExtraFeePercentage*avgTransactionFee, *ni.MinFeeUSD)
  450. return nil
  451. }
  452. return hdb.updateNodeInfo(setUpdatedNodeInfo)
  453. }
  454. func (hdb *HistoryDB) updateNodeInfo(setUpdatedNodeInfo func(*sqlx.Tx, *NodeInfo) error) error {
  455. // Create a SQL transaction or read and update atomicaly
  456. txn, err := hdb.dbWrite.Beginx()
  457. if err != nil {
  458. return tracerr.Wrap(err)
  459. }
  460. defer func() {
  461. if err != nil {
  462. db.Rollback(txn)
  463. }
  464. }()
  465. // Read current node info
  466. ni := &NodeInfo{}
  467. if err := meddler.QueryRow(
  468. txn, ni, "SELECT * FROM node_info;",
  469. ); err != nil {
  470. return tracerr.Wrap(err)
  471. }
  472. // Update NodeInfo struct
  473. if err := setUpdatedNodeInfo(txn, ni); err != nil {
  474. return tracerr.Wrap(err)
  475. }
  476. // Update NodeInfo at DB
  477. if _, err := txn.Exec("DELETE FROM node_info;"); err != nil {
  478. return tracerr.Wrap(err)
  479. }
  480. if err := meddler.Insert(txn, "node_info", ni); err != nil {
  481. return tracerr.Wrap(err)
  482. }
  483. // Commit NodeInfo update
  484. return tracerr.Wrap(txn.Commit())
  485. }