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.

517 lines
18 KiB

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