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.

1326 lines
44 KiB

  1. diff --git a/api/api.go b/api/api.go
  2. index 631d76e..5afa8a8 100644
  3. --- a/api/api.go
  4. +++ b/api/api.go
  5. @@ -2,40 +2,19 @@ package api
  6. import (
  7. "errors"
  8. - "sync"
  9. ethCommon "github.com/ethereum/go-ethereum/common"
  10. "github.com/gin-gonic/gin"
  11. - "github.com/hermeznetwork/hermez-node/common"
  12. "github.com/hermeznetwork/hermez-node/db/historydb"
  13. "github.com/hermeznetwork/hermez-node/db/l2db"
  14. "github.com/hermeznetwork/tracerr"
  15. )
  16. -// TODO: Add correct values to constants
  17. -const (
  18. - createAccountExtraFeePercentage float64 = 2
  19. - createAccountInternalExtraFeePercentage float64 = 2.5
  20. -)
  21. -
  22. -// Status define status of the network
  23. -type Status struct {
  24. - sync.RWMutex
  25. - NodeConfig NodeConfig `json:"nodeConfig"`
  26. - Network Network `json:"network"`
  27. - Metrics historydb.Metrics `json:"metrics"`
  28. - Rollup historydb.RollupVariablesAPI `json:"rollup"`
  29. - Auction historydb.AuctionVariablesAPI `json:"auction"`
  30. - WithdrawalDelayer common.WDelayerVariables `json:"withdrawalDelayer"`
  31. - RecommendedFee common.RecommendedFee `json:"recommendedFee"`
  32. -}
  33. -
  34. // API serves HTTP requests to allow external interaction with the Hermez node
  35. type API struct {
  36. h *historydb.HistoryDB
  37. cg *configAPI
  38. l2 *l2db.L2DB
  39. - status Status
  40. chainID uint16
  41. hermezAddress ethCommon.Address
  42. }
  43. @@ -46,8 +25,6 @@ func NewAPI(
  44. server *gin.Engine,
  45. hdb *historydb.HistoryDB,
  46. l2db *l2db.L2DB,
  47. - config *Config,
  48. - nodeConfig *NodeConfig,
  49. ) (*API, error) {
  50. // Check input
  51. // TODO: is stateDB only needed for explorer endpoints or for both?
  52. @@ -57,20 +34,20 @@ func NewAPI(
  53. if explorerEndpoints && hdb == nil {
  54. return nil, tracerr.Wrap(errors.New("cannot serve Explorer endpoints without HistoryDB"))
  55. }
  56. -
  57. + ni, err := hdb.GetNodeInfo()
  58. + if err != nil {
  59. + return nil, err
  60. + }
  61. a := &API{
  62. h: hdb,
  63. cg: &configAPI{
  64. - RollupConstants: *newRollupConstants(config.RollupConstants),
  65. - AuctionConstants: config.AuctionConstants,
  66. - WDelayerConstants: config.WDelayerConstants,
  67. - },
  68. - l2: l2db,
  69. - status: Status{
  70. - NodeConfig: *nodeConfig,
  71. + RollupConstants: *newRollupConstants(ni.Constants.RollupConstants),
  72. + AuctionConstants: ni.Constants.AuctionConstants,
  73. + WDelayerConstants: ni.Constants.WDelayerConstants,
  74. },
  75. - chainID: config.ChainID,
  76. - hermezAddress: config.HermezAddress,
  77. + l2: l2db,
  78. + chainID: ni.Constants.ChainID,
  79. + hermezAddress: ni.Constants.HermezAddress,
  80. }
  81. // Add coordinator endpoints
  82. diff --git a/api/state.go b/api/state.go
  83. index eecdd77..ebb0a3c 100644
  84. --- a/api/state.go
  85. +++ b/api/state.go
  86. @@ -1,320 +1,16 @@
  87. package api
  88. import (
  89. - "database/sql"
  90. - "fmt"
  91. - "math"
  92. - "math/big"
  93. "net/http"
  94. - "time"
  95. "github.com/gin-gonic/gin"
  96. - "github.com/hermeznetwork/hermez-node/apitypes"
  97. - "github.com/hermeznetwork/hermez-node/common"
  98. - "github.com/hermeznetwork/hermez-node/db/historydb"
  99. - "github.com/hermeznetwork/tracerr"
  100. )
  101. -// Network define status of the network
  102. -type Network struct {
  103. - LastEthBlock int64 `json:"lastEthereumBlock"`
  104. - LastSyncBlock int64 `json:"lastSynchedBlock"`
  105. - LastBatch *historydb.BatchAPI `json:"lastBatch"`
  106. - CurrentSlot int64 `json:"currentSlot"`
  107. - NextForgers []NextForger `json:"nextForgers"`
  108. -}
  109. -
  110. -// NodeConfig is the configuration of the node that is exposed via API
  111. -type NodeConfig struct {
  112. - // ForgeDelay in seconds
  113. - ForgeDelay float64 `json:"forgeDelay"`
  114. -}
  115. -
  116. -// NextForger is a representation of the information of a coordinator and the period will forge
  117. -type NextForger struct {
  118. - Coordinator historydb.CoordinatorAPI `json:"coordinator"`
  119. - Period Period `json:"period"`
  120. -}
  121. -
  122. -// Period is a representation of a period
  123. -type Period struct {
  124. - SlotNum int64 `json:"slotNum"`
  125. - FromBlock int64 `json:"fromBlock"`
  126. - ToBlock int64 `json:"toBlock"`
  127. - FromTimestamp time.Time `json:"fromTimestamp"`
  128. - ToTimestamp time.Time `json:"toTimestamp"`
  129. -}
  130. -
  131. func (a *API) getState(c *gin.Context) {
  132. - // TODO: There are no events for the buckets information, so now this information will be 0
  133. - a.status.RLock()
  134. - status := a.status //nolint
  135. - a.status.RUnlock()
  136. - c.JSON(http.StatusOK, status) //nolint
  137. -}
  138. -
  139. -// SC Vars
  140. -
  141. -// SetRollupVariables set Status.Rollup variables
  142. -func (a *API) SetRollupVariables(rollupVariables common.RollupVariables) {
  143. - a.status.Lock()
  144. - var rollupVAPI historydb.RollupVariablesAPI
  145. - rollupVAPI.EthBlockNum = rollupVariables.EthBlockNum
  146. - rollupVAPI.FeeAddToken = apitypes.NewBigIntStr(rollupVariables.FeeAddToken)
  147. - rollupVAPI.ForgeL1L2BatchTimeout = rollupVariables.ForgeL1L2BatchTimeout
  148. - rollupVAPI.WithdrawalDelay = rollupVariables.WithdrawalDelay
  149. -
  150. - for i, bucket := range rollupVariables.Buckets {
  151. - var apiBucket historydb.BucketParamsAPI
  152. - apiBucket.CeilUSD = apitypes.NewBigIntStr(bucket.CeilUSD)
  153. - apiBucket.Withdrawals = apitypes.NewBigIntStr(bucket.Withdrawals)
  154. - apiBucket.BlockWithdrawalRate = apitypes.NewBigIntStr(bucket.BlockWithdrawalRate)
  155. - apiBucket.MaxWithdrawals = apitypes.NewBigIntStr(bucket.MaxWithdrawals)
  156. - rollupVAPI.Buckets[i] = apiBucket
  157. - }
  158. -
  159. - rollupVAPI.SafeMode = rollupVariables.SafeMode
  160. - a.status.Rollup = rollupVAPI
  161. - a.status.Unlock()
  162. -}
  163. -
  164. -// SetWDelayerVariables set Status.WithdrawalDelayer variables
  165. -func (a *API) SetWDelayerVariables(wDelayerVariables common.WDelayerVariables) {
  166. - a.status.Lock()
  167. - a.status.WithdrawalDelayer = wDelayerVariables
  168. - a.status.Unlock()
  169. -}
  170. -
  171. -// SetAuctionVariables set Status.Auction variables
  172. -func (a *API) SetAuctionVariables(auctionVariables common.AuctionVariables) {
  173. - a.status.Lock()
  174. - var auctionAPI historydb.AuctionVariablesAPI
  175. -
  176. - auctionAPI.EthBlockNum = auctionVariables.EthBlockNum
  177. - auctionAPI.DonationAddress = auctionVariables.DonationAddress
  178. - auctionAPI.BootCoordinator = auctionVariables.BootCoordinator
  179. - auctionAPI.BootCoordinatorURL = auctionVariables.BootCoordinatorURL
  180. - auctionAPI.DefaultSlotSetBidSlotNum = auctionVariables.DefaultSlotSetBidSlotNum
  181. - auctionAPI.ClosedAuctionSlots = auctionVariables.ClosedAuctionSlots
  182. - auctionAPI.OpenAuctionSlots = auctionVariables.OpenAuctionSlots
  183. - auctionAPI.Outbidding = auctionVariables.Outbidding
  184. - auctionAPI.SlotDeadline = auctionVariables.SlotDeadline
  185. -
  186. - for i, slot := range auctionVariables.DefaultSlotSetBid {
  187. - auctionAPI.DefaultSlotSetBid[i] = apitypes.NewBigIntStr(slot)
  188. - }
  189. -
  190. - for i, ratio := range auctionVariables.AllocationRatio {
  191. - auctionAPI.AllocationRatio[i] = ratio
  192. - }
  193. -
  194. - a.status.Auction = auctionAPI
  195. - a.status.Unlock()
  196. -}
  197. -
  198. -// Network
  199. -
  200. -// UpdateNetworkInfoBlock update Status.Network block related information
  201. -func (a *API) UpdateNetworkInfoBlock(
  202. - lastEthBlock, lastSyncBlock common.Block,
  203. -) {
  204. - a.status.Network.LastSyncBlock = lastSyncBlock.Num
  205. - a.status.Network.LastEthBlock = lastEthBlock.Num
  206. -}
  207. -
  208. -// UpdateNetworkInfo update Status.Network information
  209. -func (a *API) UpdateNetworkInfo(
  210. - lastEthBlock, lastSyncBlock common.Block,
  211. - lastBatchNum common.BatchNum, currentSlot int64,
  212. -) error {
  213. - lastBatch, err := a.h.GetBatchAPI(lastBatchNum)
  214. - if tracerr.Unwrap(err) == sql.ErrNoRows {
  215. - lastBatch = nil
  216. - } else if err != nil {
  217. - return tracerr.Wrap(err)
  218. - }
  219. - lastClosedSlot := currentSlot + int64(a.status.Auction.ClosedAuctionSlots)
  220. - nextForgers, err := a.getNextForgers(lastSyncBlock, currentSlot, lastClosedSlot)
  221. - if tracerr.Unwrap(err) == sql.ErrNoRows {
  222. - nextForgers = nil
  223. - } else if err != nil {
  224. - return tracerr.Wrap(err)
  225. - }
  226. - a.status.Lock()
  227. - a.status.Network.LastSyncBlock = lastSyncBlock.Num
  228. - a.status.Network.LastEthBlock = lastEthBlock.Num
  229. - a.status.Network.LastBatch = lastBatch
  230. - a.status.Network.CurrentSlot = currentSlot
  231. - a.status.Network.NextForgers = nextForgers
  232. -
  233. - // Update buckets withdrawals
  234. - bucketsUpdate, err := a.h.GetBucketUpdatesAPI()
  235. - if tracerr.Unwrap(err) == sql.ErrNoRows {
  236. - bucketsUpdate = nil
  237. - } else if err != nil {
  238. - return tracerr.Wrap(err)
  239. - }
  240. -
  241. - for i, bucketParams := range a.status.Rollup.Buckets {
  242. - for _, bucketUpdate := range bucketsUpdate {
  243. - if bucketUpdate.NumBucket == i {
  244. - bucketParams.Withdrawals = bucketUpdate.Withdrawals
  245. - a.status.Rollup.Buckets[i] = bucketParams
  246. - break
  247. - }
  248. - }
  249. - }
  250. - a.status.Unlock()
  251. - return nil
  252. -}
  253. -
  254. -// apiSlotToBigInts converts from [6]*apitypes.BigIntStr to [6]*big.Int
  255. -func apiSlotToBigInts(defaultSlotSetBid [6]*apitypes.BigIntStr) ([6]*big.Int, error) {
  256. - var slots [6]*big.Int
  257. -
  258. - for i, slot := range defaultSlotSetBid {
  259. - bigInt, ok := new(big.Int).SetString(string(*slot), 10)
  260. - if !ok {
  261. - return slots, tracerr.Wrap(fmt.Errorf("can't convert %T into big.Int", slot))
  262. - }
  263. - slots[i] = bigInt
  264. - }
  265. -
  266. - return slots, nil
  267. -}
  268. -
  269. -// getNextForgers returns next forgers
  270. -func (a *API) getNextForgers(lastBlock common.Block, currentSlot, lastClosedSlot int64) ([]NextForger, error) {
  271. - secondsPerBlock := int64(15) //nolint:gomnd
  272. - // currentSlot and lastClosedSlot included
  273. - limit := uint(lastClosedSlot - currentSlot + 1)
  274. - bids, _, err := a.h.GetBestBidsAPI(&currentSlot, &lastClosedSlot, nil, &limit, "ASC")
  275. - if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
  276. - return nil, tracerr.Wrap(err)
  277. - }
  278. - nextForgers := []NextForger{}
  279. - // Get min bid info
  280. - var minBidInfo []historydb.MinBidInfo
  281. - if currentSlot >= a.status.Auction.DefaultSlotSetBidSlotNum {
  282. - // All min bids can be calculated with the last update of AuctionVariables
  283. - bigIntSlots, err := apiSlotToBigInts(a.status.Auction.DefaultSlotSetBid)
  284. - if err != nil {
  285. - return nil, tracerr.Wrap(err)
  286. - }
  287. -
  288. - minBidInfo = []historydb.MinBidInfo{{
  289. - DefaultSlotSetBid: bigIntSlots,
  290. - DefaultSlotSetBidSlotNum: a.status.Auction.DefaultSlotSetBidSlotNum,
  291. - }}
  292. - } else {
  293. - // Get all the relevant updates from the DB
  294. - minBidInfo, err = a.h.GetAuctionVarsUntilSetSlotNumAPI(lastClosedSlot, int(lastClosedSlot-currentSlot)+1)
  295. - if err != nil {
  296. - return nil, tracerr.Wrap(err)
  297. - }
  298. - }
  299. - // Create nextForger for each slot
  300. - for i := currentSlot; i <= lastClosedSlot; i++ {
  301. - fromBlock := i*int64(a.cg.AuctionConstants.BlocksPerSlot) + a.cg.AuctionConstants.GenesisBlockNum
  302. - toBlock := (i+1)*int64(a.cg.AuctionConstants.BlocksPerSlot) + a.cg.AuctionConstants.GenesisBlockNum - 1
  303. - nextForger := NextForger{
  304. - Period: Period{
  305. - SlotNum: i,
  306. - FromBlock: fromBlock,
  307. - ToBlock: toBlock,
  308. - FromTimestamp: lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(fromBlock-lastBlock.Num))),
  309. - ToTimestamp: lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(toBlock-lastBlock.Num))),
  310. - },
  311. - }
  312. - foundForger := false
  313. - // If there is a bid for a slot, get forger (coordinator)
  314. - for j := range bids {
  315. - slotNum := bids[j].SlotNum
  316. - if slotNum == i {
  317. - // There's a bid for the slot
  318. - // Check if the bid is greater than the minimum required
  319. - for i := 0; i < len(minBidInfo); i++ {
  320. - // Find the most recent update
  321. - if slotNum >= minBidInfo[i].DefaultSlotSetBidSlotNum {
  322. - // Get min bid
  323. - minBidSelector := slotNum % int64(len(a.status.Auction.DefaultSlotSetBid))
  324. - minBid := minBidInfo[i].DefaultSlotSetBid[minBidSelector]
  325. - // Check if the bid has beaten the minimum
  326. - bid, ok := new(big.Int).SetString(string(bids[j].BidValue), 10)
  327. - if !ok {
  328. - return nil, tracerr.New("Wrong bid value, error parsing it as big.Int")
  329. - }
  330. - if minBid.Cmp(bid) == 1 {
  331. - // Min bid is greater than bid, the slot will be forged by boot coordinator
  332. - break
  333. - }
  334. - foundForger = true
  335. - break
  336. - }
  337. - }
  338. - if !foundForger { // There is no bid or it's smaller than the minimum
  339. - break
  340. - }
  341. - coordinator, err := a.h.GetCoordinatorAPI(bids[j].Bidder)
  342. - if err != nil {
  343. - return nil, tracerr.Wrap(err)
  344. - }
  345. - nextForger.Coordinator = *coordinator
  346. - break
  347. - }
  348. - }
  349. - // If there is no bid, the coordinator that will forge is boot coordinator
  350. - if !foundForger {
  351. - nextForger.Coordinator = historydb.CoordinatorAPI{
  352. - Forger: a.status.Auction.BootCoordinator,
  353. - URL: a.status.Auction.BootCoordinatorURL,
  354. - }
  355. - }
  356. - nextForgers = append(nextForgers, nextForger)
  357. - }
  358. - return nextForgers, nil
  359. -}
  360. -
  361. -// Metrics
  362. -
  363. -// UpdateMetrics update Status.Metrics information
  364. -func (a *API) UpdateMetrics() error {
  365. - a.status.RLock()
  366. - if a.status.Network.LastBatch == nil {
  367. - a.status.RUnlock()
  368. - return nil
  369. - }
  370. - batchNum := a.status.Network.LastBatch.BatchNum
  371. - a.status.RUnlock()
  372. - metrics, err := a.h.GetMetricsAPI(batchNum)
  373. + ni, err := a.h.GetNodeInfo()
  374. if err != nil {
  375. - return tracerr.Wrap(err)
  376. - }
  377. - a.status.Lock()
  378. - a.status.Metrics = *metrics
  379. - a.status.Unlock()
  380. - return nil
  381. -}
  382. -
  383. -// Recommended fee
  384. -
  385. -// UpdateRecommendedFee update Status.RecommendedFee information
  386. -func (a *API) UpdateRecommendedFee() error {
  387. - feeExistingAccount, err := a.h.GetAvgTxFeeAPI()
  388. - if err != nil {
  389. - return tracerr.Wrap(err)
  390. - }
  391. - var minFeeUSD float64
  392. - if a.l2 != nil {
  393. - minFeeUSD = a.l2.MinFeeUSD()
  394. + retBadReq(err, c)
  395. + return
  396. }
  397. - a.status.Lock()
  398. - a.status.RecommendedFee.ExistingAccount =
  399. - math.Max(feeExistingAccount, minFeeUSD)
  400. - a.status.RecommendedFee.CreatesAccount =
  401. - math.Max(createAccountExtraFeePercentage*feeExistingAccount, minFeeUSD)
  402. - a.status.RecommendedFee.CreatesAccountAndRegister =
  403. - math.Max(createAccountInternalExtraFeePercentage*feeExistingAccount, minFeeUSD)
  404. - a.status.Unlock()
  405. - return nil
  406. + c.JSON(http.StatusOK, ni.StateAPI)
  407. }
  408. diff --git a/cli/node/main.go b/cli/node/main.go
  409. index 0138640..d7bf69d 100644
  410. --- a/cli/node/main.go
  411. +++ b/cli/node/main.go
  412. @@ -143,6 +143,47 @@ func cmdRun(c *cli.Context) error {
  413. return nil
  414. }
  415. +func cmdServeAPI(c *cli.Context) error {
  416. + cfgPath := c.String(flagCfg)
  417. + cfg, err := config.LoadAPIServer(cfgPath)
  418. + if err != nil {
  419. + if err := cli.ShowAppHelp(c); err != nil {
  420. + panic(err)
  421. + }
  422. + return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  423. + }
  424. +
  425. + node, err := node.NewNode(cfg.mode, cfg.node)
  426. + if err != nil {
  427. + return tracerr.Wrap(fmt.Errorf("error starting node: %w", err))
  428. + }
  429. + node.Start()
  430. +
  431. + stopCh := make(chan interface{})
  432. +
  433. + // catch ^C to send the stop signal
  434. + ossig := make(chan os.Signal, 1)
  435. + signal.Notify(ossig, os.Interrupt)
  436. + const forceStopCount = 3
  437. + go func() {
  438. + n := 0
  439. + for sig := range ossig {
  440. + if sig == os.Interrupt {
  441. + log.Info("Received Interrupt Signal")
  442. + stopCh <- nil
  443. + n++
  444. + if n == forceStopCount {
  445. + log.Fatalf("Received %v Interrupt Signals", forceStopCount)
  446. + }
  447. + }
  448. + }
  449. + }()
  450. + <-stopCh
  451. + node.Stop()
  452. +
  453. + return nil
  454. +}
  455. +
  456. func cmdDiscard(c *cli.Context) error {
  457. _cfg, err := parseCli(c)
  458. if err != nil {
  459. @@ -225,9 +266,6 @@ func getConfig(c *cli.Context) (*Config, error) {
  460. var cfg Config
  461. mode := c.String(flagMode)
  462. nodeCfgPath := c.String(flagCfg)
  463. - if nodeCfgPath == "" {
  464. - return nil, tracerr.Wrap(fmt.Errorf("required flag \"%v\" not set", flagCfg))
  465. - }
  466. var err error
  467. switch mode {
  468. case modeSync:
  469. @@ -304,6 +342,12 @@ func main() {
  470. Usage: "Run the hermez-node in the indicated mode",
  471. Action: cmdRun,
  472. },
  473. + {
  474. + Name: "serveapi",
  475. + Aliases: []string{},
  476. + Usage: "Serve the API only",
  477. + Action: cmdServeAPI,
  478. + },
  479. {
  480. Name: "discard",
  481. Aliases: []string{},
  482. diff --git a/config/config.go b/config/config.go
  483. index a9f495f..f517497 100644
  484. --- a/config/config.go
  485. +++ b/config/config.go
  486. @@ -44,6 +44,13 @@ type ForgeBatchGasCost struct {
  487. L2Tx uint64 `validate:"required"`
  488. }
  489. +// CoordinatorAPI specifies the configuration parameters of the API in mode
  490. +// coordinator
  491. +type CoordinatorAPI struct {
  492. + // Coordinator enables the coordinator API endpoints
  493. + Coordinator bool
  494. +}
  495. +
  496. // Coordinator is the coordinator specific configuration.
  497. type Coordinator struct {
  498. // ForgerAddress is the address under which this coordinator is forging
  499. @@ -197,10 +204,7 @@ type Coordinator struct {
  500. // ForgeBatch transaction.
  501. ForgeBatchGasCost ForgeBatchGasCost `validate:"required"`
  502. } `validate:"required"`
  503. - API struct {
  504. - // Coordinator enables the coordinator API endpoints
  505. - Coordinator bool
  506. - } `validate:"required"`
  507. + API CoordinatorAPI `validate:"required"`
  508. Debug struct {
  509. // BatchPath if set, specifies the path where batchInfo is stored
  510. // in JSON in every step/update of the pipeline
  511. @@ -215,6 +219,64 @@ type Coordinator struct {
  512. }
  513. }
  514. +// NodeAPI specifies the configuration parameters of the API
  515. +type NodeAPI struct {
  516. + // Address where the API will listen if set
  517. + Address string
  518. + // Explorer enables the Explorer API endpoints
  519. + Explorer bool
  520. + // UpdateMetricsInterval is the interval between updates of the
  521. + // API metrics
  522. + UpdateMetricsInterval Duration
  523. + // UpdateRecommendedFeeInterval is the interval between updates of the
  524. + // recommended fees
  525. + UpdateRecommendedFeeInterval Duration
  526. + // Maximum concurrent connections allowed between API and SQL
  527. + MaxSQLConnections int `validate:"required"`
  528. + // SQLConnectionTimeout is the maximum amount of time that an API request
  529. + // can wait to stablish a SQL connection
  530. + SQLConnectionTimeout Duration
  531. +}
  532. +
  533. +// It's possible to use diferentiated SQL connections for read/write.
  534. +// If the read configuration is not provided, the write one it's going to be used
  535. +// for both reads and writes
  536. +type PostgreSQL struct {
  537. + // Port of the PostgreSQL write server
  538. + PortWrite int `validate:"required"`
  539. + // Host of the PostgreSQL write server
  540. + HostWrite string `validate:"required"`
  541. + // User of the PostgreSQL write server
  542. + UserWrite string `validate:"required"`
  543. + // Password of the PostgreSQL write server
  544. + PasswordWrite string `validate:"required"`
  545. + // Name of the PostgreSQL write server database
  546. + NameWrite string `validate:"required"`
  547. + // Port of the PostgreSQL read server
  548. + PortRead int
  549. + // Host of the PostgreSQL read server
  550. + HostRead string
  551. + // User of the PostgreSQL read server
  552. + UserRead string
  553. + // Password of the PostgreSQL read server
  554. + PasswordRead string
  555. + // Name of the PostgreSQL read server database
  556. + NameRead string
  557. +}
  558. +
  559. +// NodeDebug specifies debug configuration parameters
  560. +type NodeDebug struct {
  561. + // APIAddress is the address where the debugAPI will listen if
  562. + // set
  563. + APIAddress string
  564. + // MeddlerLogs enables meddler debug mode, where unused columns and struct
  565. + // fields will be logged
  566. + MeddlerLogs bool
  567. + // GinDebugMode sets Gin-Gonic (the web framework) to run in
  568. + // debug mode
  569. + GinDebugMode bool
  570. +}
  571. +
  572. // Node is the hermez node configuration.
  573. type Node struct {
  574. PriceUpdater struct {
  575. @@ -231,32 +293,8 @@ type Node struct {
  576. // Keep is the number of checkpoints to keep
  577. Keep int `validate:"required"`
  578. } `validate:"required"`
  579. - // It's possible to use diferentiated SQL connections for read/write.
  580. - // If the read configuration is not provided, the write one it's going to be used
  581. - // for both reads and writes
  582. - PostgreSQL struct {
  583. - // Port of the PostgreSQL write server
  584. - PortWrite int `validate:"required"`
  585. - // Host of the PostgreSQL write server
  586. - HostWrite string `validate:"required"`
  587. - // User of the PostgreSQL write server
  588. - UserWrite string `validate:"required"`
  589. - // Password of the PostgreSQL write server
  590. - PasswordWrite string `validate:"required"`
  591. - // Name of the PostgreSQL write server database
  592. - NameWrite string `validate:"required"`
  593. - // Port of the PostgreSQL read server
  594. - PortRead int
  595. - // Host of the PostgreSQL read server
  596. - HostRead string
  597. - // User of the PostgreSQL read server
  598. - UserRead string
  599. - // Password of the PostgreSQL read server
  600. - PasswordRead string
  601. - // Name of the PostgreSQL read server database
  602. - NameRead string
  603. - } `validate:"required"`
  604. - Web3 struct {
  605. + PostgreSQL PostgreSQL `validate:"required"`
  606. + Web3 struct {
  607. // URL is the URL of the web3 ethereum-node RPC server
  608. URL string `validate:"required"`
  609. } `validate:"required"`
  610. @@ -286,37 +324,34 @@ type Node struct {
  611. // TokenHEZ address
  612. TokenHEZName string `validate:"required"`
  613. } `validate:"required"`
  614. - API struct {
  615. - // Address where the API will listen if set
  616. - Address string
  617. - // Explorer enables the Explorer API endpoints
  618. - Explorer bool
  619. - // UpdateMetricsInterval is the interval between updates of the
  620. - // API metrics
  621. - UpdateMetricsInterval Duration
  622. - // UpdateRecommendedFeeInterval is the interval between updates of the
  623. - // recommended fees
  624. - UpdateRecommendedFeeInterval Duration
  625. - // Maximum concurrent connections allowed between API and SQL
  626. - MaxSQLConnections int `validate:"required"`
  627. - // SQLConnectionTimeout is the maximum amount of time that an API request
  628. - // can wait to stablish a SQL connection
  629. - SQLConnectionTimeout Duration
  630. - } `validate:"required"`
  631. - Debug struct {
  632. - // APIAddress is the address where the debugAPI will listen if
  633. - // set
  634. - APIAddress string
  635. - // MeddlerLogs enables meddler debug mode, where unused columns and struct
  636. - // fields will be logged
  637. - MeddlerLogs bool
  638. - // GinDebugMode sets Gin-Gonic (the web framework) to run in
  639. - // debug mode
  640. - GinDebugMode bool
  641. - }
  642. + API NodeAPI `validate:"required"`
  643. + Debug NodeDebug `validate:"required"`
  644. Coordinator Coordinator `validate:"-"`
  645. }
  646. +type APIServer struct {
  647. + API NodeAPI `validate:"required"`
  648. + PostgreSQL PostgreSQL `validate:"required"`
  649. + Coordinator struct {
  650. + API struct {
  651. + // Coordinator enables the coordinator API endpoints
  652. + Coordinator bool
  653. + } `validate:"required"`
  654. + } `validate:"required"`
  655. + L2DB struct {
  656. + // MaxTxs is the maximum number of pending L2Txs that can be
  657. + // stored in the pool. Once this number of pending L2Txs is
  658. + // reached, inserts to the pool will be denied until some of
  659. + // the pending txs are forged.
  660. + MaxTxs uint32 `validate:"required"`
  661. + // MinFeeUSD is the minimum fee in USD that a tx must pay in
  662. + // order to be accepted into the pool. Txs with lower than
  663. + // minimum fee will be rejected at the API level.
  664. + MinFeeUSD float64
  665. + } `validate:"required"`
  666. + Debug NodeDebug `validate:"required"`
  667. +}
  668. +
  669. // Load loads a generic config.
  670. func Load(path string, cfg interface{}) error {
  671. bs, err := ioutil.ReadFile(path) //nolint:gosec
  672. @@ -358,3 +393,16 @@ func LoadNode(path string) (*Node, error) {
  673. }
  674. return &cfg, nil
  675. }
  676. +
  677. +// LoadAPIServer loads the APIServer configuration from path.
  678. +func LoadAPIServer(path string) (*APIServer, error) {
  679. + var cfg APIServer
  680. + if err := Load(path, &cfg); err != nil {
  681. + return nil, tracerr.Wrap(fmt.Errorf("error loading apiServer configuration file: %w", err))
  682. + }
  683. + validate := validator.New()
  684. + if err := validate.Struct(cfg); err != nil {
  685. + return nil, tracerr.Wrap(fmt.Errorf("error validating configuration file: %w", err))
  686. + }
  687. + return &cfg, nil
  688. +}
  689. diff --git a/db/historydb/apiqueries.go b/db/historydb/apiqueries.go
  690. index 073608c..e187448 100644
  691. --- a/db/historydb/apiqueries.go
  692. +++ b/db/historydb/apiqueries.go
  693. @@ -32,9 +32,12 @@ func (hdb *HistoryDB) GetBatchAPI(batchNum common.BatchNum) (*BatchAPI, error) {
  694. return nil, tracerr.Wrap(err)
  695. }
  696. defer hdb.apiConnCon.Release()
  697. + return hdb.getBatchAPI(hdb.dbRead, batchNum)
  698. +}
  699. +func (hdb *HistoryDB) getBatchAPI(d meddler.DB, batchNum common.BatchNum) (*BatchAPI, error) {
  700. batch := &BatchAPI{}
  701. return batch, tracerr.Wrap(meddler.QueryRow(
  702. - hdb.dbRead, batch,
  703. + d, batch,
  704. `SELECT batch.item_id, batch.batch_num, batch.eth_block_num,
  705. batch.forger_addr, batch.fees_collected, batch.total_fees_usd, batch.state_root,
  706. batch.num_accounts, batch.exit_root, batch.forge_l1_txs_num, batch.slot_num,
  707. @@ -180,6 +183,14 @@ func (hdb *HistoryDB) GetBestBidsAPI(
  708. return nil, 0, tracerr.Wrap(err)
  709. }
  710. defer hdb.apiConnCon.Release()
  711. + return hdb.getBestBidsAPI(hdb.dbRead, minSlotNum, maxSlotNum, bidderAddr, limit, order)
  712. +}
  713. +func (hdb *HistoryDB) getBestBidsAPI(
  714. + d meddler.DB,
  715. + minSlotNum, maxSlotNum *int64,
  716. + bidderAddr *ethCommon.Address,
  717. + limit *uint, order string,
  718. +) ([]BidAPI, uint64, error) {
  719. var query string
  720. var args []interface{}
  721. // JOIN the best bid of each slot with the latest update of each coordinator
  722. @@ -214,7 +225,7 @@ func (hdb *HistoryDB) GetBestBidsAPI(
  723. }
  724. query = hdb.dbRead.Rebind(queryStr)
  725. bidPtrs := []*BidAPI{}
  726. - if err := meddler.QueryAll(hdb.dbRead, &bidPtrs, query, args...); err != nil {
  727. + if err := meddler.QueryAll(d, &bidPtrs, query, args...); err != nil {
  728. return nil, 0, tracerr.Wrap(err)
  729. }
  730. // log.Debug(query)
  731. @@ -697,25 +708,6 @@ func (hdb *HistoryDB) GetExitsAPI(
  732. return db.SlicePtrsToSlice(exits).([]ExitAPI), exits[0].TotalItems - uint64(len(exits)), nil
  733. }
  734. -// GetBucketUpdatesAPI retrieves latest values for each bucket
  735. -func (hdb *HistoryDB) GetBucketUpdatesAPI() ([]BucketUpdateAPI, error) {
  736. - cancel, err := hdb.apiConnCon.Acquire()
  737. - defer cancel()
  738. - if err != nil {
  739. - return nil, tracerr.Wrap(err)
  740. - }
  741. - defer hdb.apiConnCon.Release()
  742. - var bucketUpdates []*BucketUpdateAPI
  743. - err = meddler.QueryAll(
  744. - hdb.dbRead, &bucketUpdates,
  745. - `SELECT num_bucket, withdrawals FROM bucket_update
  746. - WHERE item_id in(SELECT max(item_id) FROM bucket_update
  747. - group by num_bucket)
  748. - ORDER BY num_bucket ASC;`,
  749. - )
  750. - return db.SlicePtrsToSlice(bucketUpdates).([]BucketUpdateAPI), tracerr.Wrap(err)
  751. -}
  752. -
  753. // GetCoordinatorsAPI returns a list of coordinators from the DB and pagination info
  754. func (hdb *HistoryDB) GetCoordinatorsAPI(
  755. bidderAddr, forgerAddr *ethCommon.Address,
  756. @@ -800,29 +792,6 @@ func (hdb *HistoryDB) GetAuctionVarsAPI() (*common.AuctionVariables, error) {
  757. return auctionVars, tracerr.Wrap(err)
  758. }
  759. -// GetAuctionVarsUntilSetSlotNumAPI returns all the updates of the auction vars
  760. -// from the last entry in which DefaultSlotSetBidSlotNum <= slotNum
  761. -func (hdb *HistoryDB) GetAuctionVarsUntilSetSlotNumAPI(slotNum int64, maxItems int) ([]MinBidInfo, error) {
  762. - cancel, err := hdb.apiConnCon.Acquire()
  763. - defer cancel()
  764. - if err != nil {
  765. - return nil, tracerr.Wrap(err)
  766. - }
  767. - defer hdb.apiConnCon.Release()
  768. - auctionVars := []*MinBidInfo{}
  769. - query := `
  770. - SELECT DISTINCT default_slot_set_bid, default_slot_set_bid_slot_num FROM auction_vars
  771. - WHERE default_slot_set_bid_slot_num < $1
  772. - ORDER BY default_slot_set_bid_slot_num DESC
  773. - LIMIT $2;
  774. - `
  775. - err = meddler.QueryAll(hdb.dbRead, &auctionVars, query, slotNum, maxItems)
  776. - if err != nil {
  777. - return nil, tracerr.Wrap(err)
  778. - }
  779. - return db.SlicePtrsToSlice(auctionVars).([]MinBidInfo), nil
  780. -}
  781. -
  782. // GetAccountAPI returns an account by its index
  783. func (hdb *HistoryDB) GetAccountAPI(idx common.Idx) (*AccountAPI, error) {
  784. cancel, err := hdb.apiConnCon.Acquire()
  785. @@ -941,135 +910,47 @@ func (hdb *HistoryDB) GetAccountsAPI(
  786. accounts[0].TotalItems - uint64(len(accounts)), nil
  787. }
  788. -// GetMetricsAPI returns metrics
  789. -func (hdb *HistoryDB) GetMetricsAPI(lastBatchNum common.BatchNum) (*Metrics, error) {
  790. +// GetCommonAccountAPI returns the account associated to an account idx
  791. +func (hdb *HistoryDB) GetCommonAccountAPI(idx common.Idx) (*common.Account, error) {
  792. cancel, err := hdb.apiConnCon.Acquire()
  793. defer cancel()
  794. if err != nil {
  795. return nil, tracerr.Wrap(err)
  796. }
  797. defer hdb.apiConnCon.Release()
  798. - metricsTotals := &MetricsTotals{}
  799. - metrics := &Metrics{}
  800. - err = meddler.QueryRow(
  801. - hdb.dbRead, metricsTotals, `SELECT
  802. - COALESCE (MIN(batch.batch_num), 0) as batch_num,
  803. - COALESCE (MIN(block.timestamp), NOW()) AS min_timestamp,
  804. - COALESCE (MAX(block.timestamp), NOW()) AS max_timestamp
  805. - FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num
  806. - WHERE block.timestamp >= NOW() - INTERVAL '24 HOURS' and batch.batch_num <= $1;`, lastBatchNum)
  807. - if err != nil {
  808. - return nil, tracerr.Wrap(err)
  809. - }
  810. -
  811. - err = meddler.QueryRow(
  812. - hdb.dbRead, metricsTotals, `SELECT COUNT(*) as total_txs
  813. - FROM tx WHERE tx.batch_num between $1 AND $2;`, metricsTotals.FirstBatchNum, lastBatchNum)
  814. - if err != nil {
  815. - return nil, tracerr.Wrap(err)
  816. - }
  817. -
  818. - seconds := metricsTotals.MaxTimestamp.Sub(metricsTotals.MinTimestamp).Seconds()
  819. - // Avoid dividing by 0
  820. - if seconds == 0 {
  821. - seconds++
  822. - }
  823. -
  824. - metrics.TransactionsPerSecond = float64(metricsTotals.TotalTransactions) / seconds
  825. -
  826. - if (lastBatchNum - metricsTotals.FirstBatchNum) > 0 {
  827. - metrics.TransactionsPerBatch = float64(metricsTotals.TotalTransactions) /
  828. - float64(lastBatchNum-metricsTotals.FirstBatchNum+1)
  829. - } else {
  830. - metrics.TransactionsPerBatch = float64(0)
  831. - }
  832. -
  833. - err = meddler.QueryRow(
  834. - hdb.dbRead, metricsTotals, `SELECT COUNT(*) AS total_batches,
  835. - COALESCE (SUM(total_fees_usd), 0) AS total_fees FROM batch
  836. - WHERE batch_num between $1 and $2;`, metricsTotals.FirstBatchNum, lastBatchNum)
  837. - if err != nil {
  838. - return nil, tracerr.Wrap(err)
  839. - }
  840. -
  841. - if metricsTotals.TotalBatches > 0 {
  842. - metrics.BatchFrequency = seconds / float64(metricsTotals.TotalBatches)
  843. - } else {
  844. - metrics.BatchFrequency = 0
  845. - }
  846. - if metricsTotals.TotalTransactions > 0 {
  847. - metrics.AvgTransactionFee = metricsTotals.TotalFeesUSD / float64(metricsTotals.TotalTransactions)
  848. - } else {
  849. - metrics.AvgTransactionFee = 0
  850. - }
  851. - err = meddler.QueryRow(
  852. - hdb.dbRead, metrics,
  853. - `SELECT COUNT(*) AS total_bjjs, COUNT(DISTINCT(bjj)) AS total_accounts FROM account;`)
  854. - if err != nil {
  855. - return nil, tracerr.Wrap(err)
  856. - }
  857. + account := &common.Account{}
  858. err = meddler.QueryRow(
  859. - hdb.dbRead, metrics,
  860. - `SELECT COALESCE (AVG(EXTRACT(EPOCH FROM (forged.timestamp - added.timestamp))), 0) AS estimatedTimeToForgeL1 FROM tx
  861. - INNER JOIN block AS added ON tx.eth_block_num = added.eth_block_num
  862. - INNER JOIN batch AS forged_batch ON tx.batch_num = forged_batch.batch_num
  863. - INNER JOIN block AS forged ON forged_batch.eth_block_num = forged.eth_block_num
  864. - WHERE tx.batch_num between $1 and $2 AND tx.is_l1 AND tx.user_origin;`,
  865. - metricsTotals.FirstBatchNum, lastBatchNum,
  866. + hdb.dbRead, account, `SELECT * FROM account WHERE idx = $1;`, idx,
  867. )
  868. - if err != nil {
  869. - return nil, tracerr.Wrap(err)
  870. - }
  871. -
  872. - return metrics, nil
  873. + return account, tracerr.Wrap(err)
  874. }
  875. -// GetAvgTxFeeAPI returns average transaction fee of the last 1h
  876. -func (hdb *HistoryDB) GetAvgTxFeeAPI() (float64, error) {
  877. +// GetCoordinatorAPI returns a coordinator by its bidderAddr
  878. +func (hdb *HistoryDB) GetCoordinatorAPI(bidderAddr ethCommon.Address) (*CoordinatorAPI, error) {
  879. cancel, err := hdb.apiConnCon.Acquire()
  880. defer cancel()
  881. if err != nil {
  882. - return 0, tracerr.Wrap(err)
  883. + return nil, tracerr.Wrap(err)
  884. }
  885. defer hdb.apiConnCon.Release()
  886. - metricsTotals := &MetricsTotals{}
  887. - err = meddler.QueryRow(
  888. - hdb.dbRead, metricsTotals, `SELECT COUNT(tx.*) as total_txs,
  889. - COALESCE (MIN(tx.batch_num), 0) as batch_num
  890. - FROM tx INNER JOIN block ON tx.eth_block_num = block.eth_block_num
  891. - WHERE block.timestamp >= NOW() - INTERVAL '1 HOURS';`)
  892. - if err != nil {
  893. - return 0, tracerr.Wrap(err)
  894. - }
  895. - err = meddler.QueryRow(
  896. - hdb.dbRead, metricsTotals, `SELECT COUNT(*) AS total_batches,
  897. - COALESCE (SUM(total_fees_usd), 0) AS total_fees FROM batch
  898. - WHERE batch_num > $1;`, metricsTotals.FirstBatchNum)
  899. - if err != nil {
  900. - return 0, tracerr.Wrap(err)
  901. - }
  902. -
  903. - var avgTransactionFee float64
  904. - if metricsTotals.TotalTransactions > 0 {
  905. - avgTransactionFee = metricsTotals.TotalFeesUSD / float64(metricsTotals.TotalTransactions)
  906. - } else {
  907. - avgTransactionFee = 0
  908. - }
  909. -
  910. - return avgTransactionFee, nil
  911. + return hdb.getCoordinatorAPI(hdb.dbRead, bidderAddr)
  912. +}
  913. +func (hdb *HistoryDB) getCoordinatorAPI(d meddler.DB, bidderAddr ethCommon.Address) (*CoordinatorAPI, error) {
  914. + coordinator := &CoordinatorAPI{}
  915. + err := meddler.QueryRow(
  916. + d, coordinator,
  917. + "SELECT * FROM coordinator WHERE bidder_addr = $1 ORDER BY item_id DESC LIMIT 1;",
  918. + bidderAddr,
  919. + )
  920. + return coordinator, tracerr.Wrap(err)
  921. }
  922. -// GetCommonAccountAPI returns the account associated to an account idx
  923. -func (hdb *HistoryDB) GetCommonAccountAPI(idx common.Idx) (*common.Account, error) {
  924. +func (hdb *HistoryDB) GetNodeInfoAPI() (*NodeInfo, error) {
  925. cancel, err := hdb.apiConnCon.Acquire()
  926. defer cancel()
  927. if err != nil {
  928. return nil, tracerr.Wrap(err)
  929. }
  930. defer hdb.apiConnCon.Release()
  931. - account := &common.Account{}
  932. - err = meddler.QueryRow(
  933. - hdb.dbRead, account, `SELECT * FROM account WHERE idx = $1;`, idx,
  934. - )
  935. - return account, tracerr.Wrap(err)
  936. + return hdb.GetNodeInfo()
  937. }
  938. diff --git a/db/historydb/historydb.go b/db/historydb/historydb.go
  939. index e887e70..3997f57 100644
  940. --- a/db/historydb/historydb.go
  941. +++ b/db/historydb/historydb.go
  942. @@ -1139,17 +1139,6 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) {
  943. return tracerr.Wrap(txn.Commit())
  944. }
  945. -// GetCoordinatorAPI returns a coordinator by its bidderAddr
  946. -func (hdb *HistoryDB) GetCoordinatorAPI(bidderAddr ethCommon.Address) (*CoordinatorAPI, error) {
  947. - coordinator := &CoordinatorAPI{}
  948. - err := meddler.QueryRow(
  949. - hdb.dbRead, coordinator,
  950. - "SELECT * FROM coordinator WHERE bidder_addr = $1 ORDER BY item_id DESC LIMIT 1;",
  951. - bidderAddr,
  952. - )
  953. - return coordinator, tracerr.Wrap(err)
  954. -}
  955. -
  956. // AddAuctionVars insert auction vars into the DB
  957. func (hdb *HistoryDB) AddAuctionVars(auctionVars *common.AuctionVariables) error {
  958. return tracerr.Wrap(meddler.Insert(hdb.dbWrite, "auction_vars", auctionVars))
  959. diff --git a/db/historydb/views.go b/db/historydb/views.go
  960. index fe71733..e7bebe5 100644
  961. --- a/db/historydb/views.go
  962. +++ b/db/historydb/views.go
  963. @@ -313,17 +313,6 @@ type Metrics struct {
  964. EstimatedTimeToForgeL1 float64 `json:"estimatedTimeToForgeL1" meddler:"estimatedTimeToForgeL1"`
  965. }
  966. -// MetricsTotals is used to get temporal information from HistoryDB
  967. -// to calculate data to be stored into the Metrics struct
  968. -type MetricsTotals struct {
  969. - TotalTransactions uint64 `meddler:"total_txs"`
  970. - FirstBatchNum common.BatchNum `meddler:"batch_num"`
  971. - TotalBatches int64 `meddler:"total_batches"`
  972. - TotalFeesUSD float64 `meddler:"total_fees"`
  973. - MinTimestamp time.Time `meddler:"min_timestamp,utctime"`
  974. - MaxTimestamp time.Time `meddler:"max_timestamp,utctime"`
  975. -}
  976. -
  977. // BidAPI is a representation of a bid with additional information
  978. // required by the API
  979. type BidAPI struct {
  980. diff --git a/db/migrations/0001.sql b/db/migrations/0001.sql
  981. index d09a432..ce5dbe0 100644
  982. --- a/db/migrations/0001.sql
  983. +++ b/db/migrations/0001.sql
  984. @@ -661,6 +661,13 @@ CREATE TABLE account_creation_auth (
  985. timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT timezone('utc', now())
  986. );
  987. +CREATE TABLE node_info (
  988. + state BYTEA, -- object returned by GET /state
  989. + pool_max_txs BIGINT, -- L2DB config
  990. + min_fee NUMERIC, -- L2DB config
  991. + constants BYTEA -- info of the network that is constant
  992. +)
  993. +
  994. -- +migrate Down
  995. -- triggers
  996. DROP TRIGGER IF EXISTS trigger_token_usd_update ON token;
  997. @@ -675,6 +682,7 @@ DROP FUNCTION IF EXISTS set_tx;
  998. DROP FUNCTION IF EXISTS forge_l1_user_txs;
  999. DROP FUNCTION IF EXISTS set_pool_tx;
  1000. -- drop tables IF EXISTS
  1001. +DROP TABLE IF EXISTS node_info;
  1002. DROP TABLE IF EXISTS account_creation_auth;
  1003. DROP TABLE IF EXISTS tx_pool;
  1004. DROP TABLE IF EXISTS auction_vars;
  1005. diff --git a/go.mod b/go.mod
  1006. index 2fdd0e5..3f03bdc 100644
  1007. --- a/go.mod
  1008. +++ b/go.mod
  1009. @@ -26,6 +26,7 @@ require (
  1010. github.com/russross/meddler v1.0.0
  1011. github.com/stretchr/testify v1.6.1
  1012. github.com/urfave/cli/v2 v2.2.0
  1013. + github.com/ztrue/tracerr v0.3.0
  1014. go.uber.org/zap v1.16.0
  1015. golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
  1016. golang.org/x/net v0.0.0-20200822124328-c89045814202
  1017. diff --git a/go.sum b/go.sum
  1018. index 3def156..749aa10 100644
  1019. --- a/go.sum
  1020. +++ b/go.sum
  1021. @@ -690,6 +690,8 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDf
  1022. github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
  1023. github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
  1024. github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
  1025. +github.com/ztrue/tracerr v0.3.0 h1:lDi6EgEYhPYPnKcjsYzmWw4EkFEoA/gfe+I9Y5f+h6Y=
  1026. +github.com/ztrue/tracerr v0.3.0/go.mod h1:qEalzze4VN9O8tnhBXScfCrmoJo10o8TN5ciKjm6Mww=
  1027. go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
  1028. go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
  1029. go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
  1030. diff --git a/node/node.go b/node/node.go
  1031. index ddd5bdf..252b9b3 100644
  1032. --- a/node/node.go
  1033. +++ b/node/node.go
  1034. @@ -67,6 +67,7 @@ type Node struct {
  1035. mode Mode
  1036. sqlConnRead *sqlx.DB
  1037. sqlConnWrite *sqlx.DB
  1038. + historyDB *historydb.HistoryDB
  1039. ctx context.Context
  1040. wg sync.WaitGroup
  1041. cancel context.CancelFunc
  1042. @@ -235,6 +236,20 @@ func NewNode(mode Mode, cfg *config.Node) (*Node, error) {
  1043. WDelayer: *sync.WDelayerConstants(),
  1044. }
  1045. + if err := historyDB.SetInitialNodeInfo(
  1046. + cfg.Coordinator.L2DB.MaxTxs,
  1047. + cfg.Coordinator.L2DB.MinFeeUSD,
  1048. + &historydb.Constants{
  1049. + RollupConstants: scConsts.Rollup,
  1050. + AuctionConstants: scConsts.Auction,
  1051. + WDelayerConstants: scConsts.WDelayer,
  1052. + ChainID: chainIDU16,
  1053. + HermezAddress: cfg.SmartContracts.Rollup,
  1054. + },
  1055. + ); err != nil {
  1056. + return nil, tracerr.Wrap(err)
  1057. + }
  1058. +
  1059. var coord *coordinator.Coordinator
  1060. var l2DB *l2db.L2DB
  1061. if mode == ModeCoordinator {
  1062. @@ -400,23 +415,11 @@ func NewNode(mode Mode, cfg *config.Node) (*Node, error) {
  1063. coord, cfg.API.Explorer,
  1064. server,
  1065. historyDB,
  1066. - stateDB,
  1067. l2DB,
  1068. - &api.Config{
  1069. - RollupConstants: scConsts.Rollup,
  1070. - AuctionConstants: scConsts.Auction,
  1071. - WDelayerConstants: scConsts.WDelayer,
  1072. - ChainID: chainIDU16,
  1073. - HermezAddress: cfg.SmartContracts.Rollup,
  1074. - },
  1075. - cfg.Coordinator.ForgeDelay.Duration,
  1076. )
  1077. if err != nil {
  1078. return nil, tracerr.Wrap(err)
  1079. }
  1080. - nodeAPI.api.SetRollupVariables(*initSCVars.Rollup)
  1081. - nodeAPI.api.SetAuctionVariables(*initSCVars.Auction)
  1082. - nodeAPI.api.SetWDelayerVariables(*initSCVars.WDelayer)
  1083. }
  1084. var debugAPI *debugapi.DebugAPI
  1085. if cfg.Debug.APIAddress != "" {
  1086. @@ -438,11 +441,108 @@ func NewNode(mode Mode, cfg *config.Node) (*Node, error) {
  1087. mode: mode,
  1088. sqlConnRead: dbRead,
  1089. sqlConnWrite: dbWrite,
  1090. + historyDB: historyDB,
  1091. ctx: ctx,
  1092. cancel: cancel,
  1093. }, nil
  1094. }
  1095. +// APIServer is a server that only runs the API
  1096. +type APIServer struct {
  1097. + nodeAPI *NodeAPI
  1098. +}
  1099. +
  1100. +func NewAPIServer(mode Mode, cfg *config.APIServer) (*APIServer, error) {
  1101. + // NOTE: I just copied some parts of NewNode related to starting the
  1102. + // API, but it still cotains many parameters that are not available
  1103. + meddler.Debug = cfg.Debug.MeddlerLogs
  1104. + // Stablish DB connection
  1105. + dbWrite, err := dbUtils.InitSQLDB(
  1106. + cfg.PostgreSQL.PortWrite,
  1107. + cfg.PostgreSQL.HostWrite,
  1108. + cfg.PostgreSQL.UserWrite,
  1109. + cfg.PostgreSQL.PasswordWrite,
  1110. + cfg.PostgreSQL.NameWrite,
  1111. + )
  1112. + if err != nil {
  1113. + return nil, tracerr.Wrap(fmt.Errorf("dbUtils.InitSQLDB: %w", err))
  1114. + }
  1115. + var dbRead *sqlx.DB
  1116. + if cfg.PostgreSQL.HostRead == "" {
  1117. + dbRead = dbWrite
  1118. + } else if cfg.PostgreSQL.HostRead == cfg.PostgreSQL.HostWrite {
  1119. + return nil, tracerr.Wrap(fmt.Errorf(
  1120. + "PostgreSQL.HostRead and PostgreSQL.HostWrite must be different",
  1121. + ))
  1122. + } else {
  1123. + dbRead, err = dbUtils.InitSQLDB(
  1124. + cfg.PostgreSQL.PortRead,
  1125. + cfg.PostgreSQL.HostRead,
  1126. + cfg.PostgreSQL.UserRead,
  1127. + cfg.PostgreSQL.PasswordRead,
  1128. + cfg.PostgreSQL.NameRead,
  1129. + )
  1130. + if err != nil {
  1131. + return nil, tracerr.Wrap(fmt.Errorf("dbUtils.InitSQLDB: %w", err))
  1132. + }
  1133. + }
  1134. + var apiConnCon *dbUtils.APIConnectionController
  1135. + if cfg.API.Explorer || mode == ModeCoordinator {
  1136. + apiConnCon = dbUtils.NewAPICnnectionController(
  1137. + cfg.API.MaxSQLConnections,
  1138. + cfg.API.SQLConnectionTimeout.Duration,
  1139. + )
  1140. + }
  1141. +
  1142. + historyDB := historydb.NewHistoryDB(dbRead, dbWrite, apiConnCon)
  1143. +
  1144. + var l2DB *l2db.L2DB
  1145. + if mode == ModeCoordinator {
  1146. + l2DB = l2db.NewL2DB(
  1147. + dbRead, dbWrite,
  1148. + cfg.L2DB.SafetyPeriod,
  1149. + cfg.L2DB.MaxTxs,
  1150. + cfg.L2DB.MinFeeUSD,
  1151. + cfg.L2DB,
  1152. + apiConnCon,
  1153. + )
  1154. + }
  1155. +
  1156. + var nodeAPI *NodeAPI
  1157. + if cfg.API.Address != "" {
  1158. + if cfg.Debug.GinDebugMode {
  1159. + gin.SetMode(gin.DebugMode)
  1160. + } else {
  1161. + gin.SetMode(gin.ReleaseMode)
  1162. + }
  1163. + if cfg.API.UpdateMetricsInterval.Duration == 0 {
  1164. + return nil, tracerr.Wrap(fmt.Errorf("invalid cfg.API.UpdateMetricsInterval: %v",
  1165. + cfg.API.UpdateMetricsInterval.Duration))
  1166. + }
  1167. + if cfg.API.UpdateRecommendedFeeInterval.Duration == 0 {
  1168. + return nil, tracerr.Wrap(fmt.Errorf("invalid cfg.API.UpdateRecommendedFeeInterval: %v",
  1169. + cfg.API.UpdateRecommendedFeeInterval.Duration))
  1170. + }
  1171. + server := gin.Default()
  1172. + coord := false
  1173. + if mode == ModeCoordinator {
  1174. + coord = cfg.Coordinator.API.Coordinator
  1175. + }
  1176. + var err error
  1177. + nodeAPI, err = NewNodeAPI(
  1178. + cfg.API.Address,
  1179. + coord, cfg.API.Explorer,
  1180. + server,
  1181. + historyDB,
  1182. + l2DB,
  1183. + )
  1184. + if err != nil {
  1185. + return nil, tracerr.Wrap(err)
  1186. + }
  1187. + }
  1188. + // ETC...
  1189. +}
  1190. +
  1191. // NodeAPI holds the node http API
  1192. type NodeAPI struct { //nolint:golint
  1193. api *api.API
  1194. @@ -462,10 +562,7 @@ func NewNodeAPI(
  1195. coordinatorEndpoints, explorerEndpoints bool,
  1196. server *gin.Engine,
  1197. hdb *historydb.HistoryDB,
  1198. - sdb *statedb.StateDB,
  1199. l2db *l2db.L2DB,
  1200. - config *api.Config,
  1201. - forgeDelay time.Duration,
  1202. ) (*NodeAPI, error) {
  1203. engine := gin.Default()
  1204. engine.NoRoute(handleNoRoute)
  1205. @@ -475,10 +572,6 @@ func NewNodeAPI(
  1206. engine,
  1207. hdb,
  1208. l2db,
  1209. - config,
  1210. - &api.NodeConfig{
  1211. - ForgeDelay: forgeDelay.Seconds(),
  1212. - },
  1213. )
  1214. if err != nil {
  1215. return nil, tracerr.Wrap(err)
  1216. @@ -534,17 +627,17 @@ func (n *Node) handleNewBlock(ctx context.Context, stats *synchronizer.Stats, va
  1217. }
  1218. if n.nodeAPI != nil {
  1219. if vars.Rollup != nil {
  1220. - n.nodeAPI.api.SetRollupVariables(*vars.Rollup)
  1221. + n.historyDB.SetRollupVariables(*vars.Rollup)
  1222. }
  1223. if vars.Auction != nil {
  1224. - n.nodeAPI.api.SetAuctionVariables(*vars.Auction)
  1225. + n.historyDB.SetAuctionVariables(*vars.Auction)
  1226. }
  1227. if vars.WDelayer != nil {
  1228. - n.nodeAPI.api.SetWDelayerVariables(*vars.WDelayer)
  1229. + n.historyDB.SetWDelayerVariables(*vars.WDelayer)
  1230. }
  1231. if stats.Synced() {
  1232. - if err := n.nodeAPI.api.UpdateNetworkInfo(
  1233. + if err := n.historyDB.UpdateNetworkInfo(
  1234. stats.Eth.LastBlock, stats.Sync.LastBlock,
  1235. common.BatchNum(stats.Eth.LastBatchNum),
  1236. stats.Sync.Auction.CurrentSlot.SlotNum,
  1237. @@ -552,7 +645,7 @@ func (n *Node) handleNewBlock(ctx context.Context, stats *synchronizer.Stats, va
  1238. log.Errorw("API.UpdateNetworkInfo", "err", err)
  1239. }
  1240. } else {
  1241. - n.nodeAPI.api.UpdateNetworkInfoBlock(
  1242. + n.historyDB.UpdateNetworkInfoBlock(
  1243. stats.Eth.LastBlock, stats.Sync.LastBlock,
  1244. )
  1245. }
  1246. @@ -566,15 +659,13 @@ func (n *Node) handleReorg(ctx context.Context, stats *synchronizer.Stats, vars
  1247. Vars: vars,
  1248. })
  1249. }
  1250. - if n.nodeAPI != nil {
  1251. - vars := n.sync.SCVars()
  1252. - n.nodeAPI.api.SetRollupVariables(*vars.Rollup)
  1253. - n.nodeAPI.api.SetAuctionVariables(*vars.Auction)
  1254. - n.nodeAPI.api.SetWDelayerVariables(*vars.WDelayer)
  1255. - n.nodeAPI.api.UpdateNetworkInfoBlock(
  1256. - stats.Eth.LastBlock, stats.Sync.LastBlock,
  1257. - )
  1258. - }
  1259. + vars = n.sync.SCVars()
  1260. + n.historyDB.SetRollupVariables(*vars.Rollup)
  1261. + n.historyDB.SetAuctionVariables(*vars.Auction)
  1262. + n.historyDB.SetWDelayerVariables(*vars.WDelayer)
  1263. + n.historyDB.UpdateNetworkInfoBlock(
  1264. + stats.Eth.LastBlock, stats.Sync.LastBlock,
  1265. + )
  1266. }
  1267. // TODO(Edu): Consider keeping the `lastBlock` inside synchronizer so that we
  1268. @@ -704,7 +795,7 @@ func (n *Node) StartNodeAPI() {
  1269. n.wg.Add(1)
  1270. go func() {
  1271. // Do an initial update on startup
  1272. - if err := n.nodeAPI.api.UpdateMetrics(); err != nil {
  1273. + if err := n.historyDB.UpdateMetrics(); err != nil {
  1274. log.Errorw("API.UpdateMetrics", "err", err)
  1275. }
  1276. for {
  1277. @@ -714,7 +805,7 @@ func (n *Node) StartNodeAPI() {
  1278. n.wg.Done()
  1279. return
  1280. case <-time.After(n.cfg.API.UpdateMetricsInterval.Duration):
  1281. - if err := n.nodeAPI.api.UpdateMetrics(); err != nil {
  1282. + if err := n.historyDB.UpdateMetrics(); err != nil {
  1283. log.Errorw("API.UpdateMetrics", "err", err)
  1284. }
  1285. }
  1286. @@ -724,7 +815,7 @@ func (n *Node) StartNodeAPI() {
  1287. n.wg.Add(1)
  1288. go func() {
  1289. // Do an initial update on startup
  1290. - if err := n.nodeAPI.api.UpdateRecommendedFee(); err != nil {
  1291. + if err := n.historyDB.UpdateRecommendedFee(); err != nil {
  1292. log.Errorw("API.UpdateRecommendedFee", "err", err)
  1293. }
  1294. for {
  1295. @@ -734,7 +825,7 @@ func (n *Node) StartNodeAPI() {
  1296. n.wg.Done()
  1297. return
  1298. case <-time.After(n.cfg.API.UpdateRecommendedFeeInterval.Duration):
  1299. - if err := n.nodeAPI.api.UpdateRecommendedFee(); err != nil {
  1300. + if err := n.historyDB.UpdateRecommendedFee(); err != nil {
  1301. log.Errorw("API.UpdateRecommendedFee", "err", err)
  1302. }
  1303. }