diff --git a/api/api.go b/api/api.go index 28fc60f..98b64f7 100644 --- a/api/api.go +++ b/api/api.go @@ -12,7 +12,7 @@ import ( // Status define status of the network type Status struct { - Network historydb.Network `json:"network"` + Network Network `json:"network"` Metrics historydb.Metrics `json:"metrics"` Rollup common.RollupVariables `json:"rollup"` Auction common.AuctionVariables `json:"auction"` diff --git a/api/api_test.go b/api/api_test.go index a718e39..faf8417 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -52,6 +52,8 @@ type testCommon struct { bids []testBid slots []testSlot auctionVars common.AuctionVariables + rollupVars common.RollupVariables + wdelayerVars common.WDelayerVariables } var tc testCommon @@ -280,6 +282,15 @@ func TestMain(m *testing.M) { ClosedAuctionSlots: uint16(2), OpenAuctionSlots: uint16(5), } + + rollupVars := common.RollupVariables{ + WithdrawalDelay: uint64(3000), + } + + wdelayerVars := common.WDelayerVariables{ + WithdrawalDelay: uint64(3000), + } + err = api.h.AddAuctionVars(&auctionVars) if err != nil { panic(err) @@ -313,6 +324,8 @@ func TestMain(m *testing.M) { bids: testBids, slots: api.genTestSlots(nSlots, lastBlockNum, testBids, auctionVars), auctionVars: auctionVars, + rollupVars: rollupVars, + wdelayerVars: wdelayerVars, } // Fake server diff --git a/api/state.go b/api/state.go index 69fc66c..8ddd9b2 100644 --- a/api/state.go +++ b/api/state.go @@ -2,10 +2,145 @@ package api import ( "net/http" + "time" + ethCommon "github.com/ethereum/go-ethereum/common" "github.com/gin-gonic/gin" + "github.com/hermeznetwork/hermez-node/common" + "github.com/hermeznetwork/hermez-node/db/historydb" ) +// Network define status of the network +type Network struct { + LastBlock int64 `json:"lastBlock"` + LastBatch historydb.BatchAPI `json:"lastBatch"` + CurrentSlot int64 `json:"currentSlot"` + NextForgers []NextForger `json:"nextForgers"` +} + +// NextForger is a representation of the information of a coordinator and the period will forge +type NextForger struct { + Coordinator historydb.CoordinatorAPI + Period Period +} + +// Period is a representation of a period +type Period struct { + SlotNum int64 + FromBlock int64 + ToBlock int64 + FromTimestamp time.Time + ToTimestamp time.Time +} + +var bootCoordinator historydb.CoordinatorAPI = historydb.CoordinatorAPI{ + ItemID: 0, + Bidder: ethCommon.HexToAddress("0x111111111111111111111111111111111111111"), + Forger: ethCommon.HexToAddress("0x111111111111111111111111111111111111111"), + URL: "https://bootCoordinator", +} + func (a *API) getState(c *gin.Context) { c.JSON(http.StatusOK, a.status) } + +// SC Vars + +// SetRollupVariables set Status.Rollup variables +func (a *API) SetRollupVariables(rollupVariables common.RollupVariables) { + a.status.Rollup = rollupVariables +} + +// SetWDelayerVariables set Status.WithdrawalDelayer variables +func (a *API) SetWDelayerVariables(wDelayerVariables common.WDelayerVariables) { + a.status.WithdrawalDelayer = wDelayerVariables +} + +// SetAuctionVariables set Status.Auction variables +func (a *API) SetAuctionVariables(auctionVariables common.AuctionVariables) { + a.status.Auction = auctionVariables +} + +// Network + +// UpdateNetworkInfo update Status.Network information +func (a *API) UpdateNetworkInfo(lastBlock common.Block, lastBatchNum common.BatchNum, currentSlot int64) error { + a.status.Network.LastBlock = lastBlock.EthBlockNum + lastBatch, err := a.h.GetBatchAPI(lastBatchNum) + if err != nil { + return err + } + a.status.Network.LastBatch = *lastBatch + a.status.Network.CurrentSlot = currentSlot + lastClosedSlot := currentSlot + int64(a.status.Auction.ClosedAuctionSlots) + nextForgers, err := a.GetNextForgers(lastBlock, currentSlot, lastClosedSlot) + if err != nil { + return err + } + a.status.Network.NextForgers = nextForgers + return nil +} + +// GetNextForgers returns next forgers +func (a *API) GetNextForgers(lastBlock common.Block, currentSlot, lastClosedSlot int64) ([]NextForger, error) { + secondsPerBlock := int64(15) //nolint:gomnd + // currentSlot and lastClosedSlot included + limit := uint(lastClosedSlot - currentSlot + 1) + bids, _, err := a.h.GetBestBidsAPI(¤tSlot, &lastClosedSlot, nil, &limit, "ASC") + if err != nil { + return nil, err + } + nextForgers := []NextForger{} + // Create nextForger for each slot + for i := currentSlot; i <= lastClosedSlot; i++ { + fromBlock := i*int64(a.cg.AuctionConstants.BlocksPerSlot) + a.cg.AuctionConstants.GenesisBlockNum + toBlock := (i+1)*int64(a.cg.AuctionConstants.BlocksPerSlot) + a.cg.AuctionConstants.GenesisBlockNum - 1 + nextForger := NextForger{ + Period: Period{ + SlotNum: i, + FromBlock: fromBlock, + ToBlock: toBlock, + FromTimestamp: lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(fromBlock-lastBlock.EthBlockNum))), + ToTimestamp: lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(toBlock-lastBlock.EthBlockNum))), + }, + } + foundBid := false + // If there is a bid for a slot, get forger (coordinator) + for j := range bids { + if bids[j].SlotNum == i { + foundBid = true + coordinator, err := a.h.GetCoordinatorAPI(bids[j].Bidder) + if err != nil { + return nil, err + } + nextForger.Coordinator = *coordinator + break + } + } + // If there is no bid, the coordinator that will forge is boot coordinator + if !foundBid { + nextForger.Coordinator = bootCoordinator + } + nextForgers = append(nextForgers, nextForger) + } + return nextForgers, nil +} + +// Metrics + +// UpdateMetrics update Status.Metrics information +func (a *API) UpdateMetrics() error { + metrics, err := a.h.GetMetrics() + if err != nil { + return err + } + a.status.Metrics = metrics + return nil +} + +// Recommended fee + +// UpdateRecommendedFee update Status.RecommendedFee information +func (a *API) UpdateRecommendedFee() error { + return nil +} diff --git a/api/state_test.go b/api/state_test.go new file mode 100644 index 0000000..c2b3443 --- /dev/null +++ b/api/state_test.go @@ -0,0 +1,79 @@ +package api + +import ( + "testing" + "time" + + "github.com/hermeznetwork/hermez-node/common" + "github.com/stretchr/testify/assert" +) + +const secondsPerBlock = 15 + +func TestSetRollupVariables(t *testing.T) { + rollupVars := &common.RollupVariables{} + assert.Equal(t, *rollupVars, api.status.Rollup) + api.SetRollupVariables(tc.rollupVars) + assert.Equal(t, tc.rollupVars, api.status.Rollup) +} + +func TestSetWDelayerVariables(t *testing.T) { + wdelayerVars := &common.WDelayerVariables{} + assert.Equal(t, *wdelayerVars, api.status.WithdrawalDelayer) + api.SetWDelayerVariables(tc.wdelayerVars) + assert.Equal(t, tc.wdelayerVars, api.status.WithdrawalDelayer) +} + +func TestSetAuctionVariables(t *testing.T) { + auctionVars := &common.AuctionVariables{} + assert.Equal(t, *auctionVars, api.status.Auction) + api.SetAuctionVariables(tc.auctionVars) + assert.Equal(t, tc.auctionVars, api.status.Auction) +} + +func TestNextForgers(t *testing.T) { + // It's assumed that bids for each slot will be received in increasing order + bestBids := make(map[int64]testBid) + for j := range tc.bids { + bestBids[tc.bids[j].SlotNum] = tc.bids[j] + } + lastBlock := tc.blocks[len(tc.blocks)-1] + for i := int64(0); i < tc.slots[len(tc.slots)-1].SlotNum; i++ { + lastClosedSlot := i + int64(api.status.Auction.ClosedAuctionSlots) + nextForgers, err := api.GetNextForgers(tc.blocks[len(tc.blocks)-1], i, lastClosedSlot) + assert.NoError(t, err) + for j := i; j <= lastClosedSlot; j++ { + for q := range nextForgers { + if nextForgers[q].Period.SlotNum == j { + if nextForgers[q].Coordinator.ItemID != 0 { + assert.Equal(t, bestBids[j].Bidder, nextForgers[q].Coordinator.Bidder) + } else { + assert.Equal(t, bootCoordinator.Bidder, nextForgers[q].Coordinator.Bidder) + } + firstBlockSlot, lastBlockSlot := api.getFirstLastBlock(j) + fromTimestamp := lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(firstBlockSlot-lastBlock.EthBlockNum))) + toTimestamp := lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(lastBlockSlot-lastBlock.EthBlockNum))) + assert.Equal(t, fromTimestamp.Unix(), nextForgers[q].Period.FromTimestamp.Unix()) + assert.Equal(t, toTimestamp.Unix(), nextForgers[q].Period.ToTimestamp.Unix()) + } + } + } + } +} + +func TestUpdateNetworkInfo(t *testing.T) { + status := &Network{} + assert.Equal(t, status.LastBlock, api.status.Network.LastBlock) + assert.Equal(t, status.LastBatch.BatchNum, api.status.Network.LastBatch.BatchNum) + assert.Equal(t, status.CurrentSlot, api.status.Network.CurrentSlot) + assert.Equal(t, status.NextForgers, api.status.Network.NextForgers) + lastBlock := tc.blocks[3] + lastBatchNum := common.BatchNum(3) + currentSlotNum := int64(1) + err := api.UpdateNetworkInfo(lastBlock, lastBatchNum, currentSlotNum) + assert.NoError(t, err) + assert.Equal(t, lastBlock.EthBlockNum, api.status.Network.LastBlock) + assert.Equal(t, lastBatchNum, api.status.Network.LastBatch.BatchNum) + assert.Equal(t, currentSlotNum, api.status.Network.CurrentSlot) + assert.Equal(t, int(api.status.Auction.ClosedAuctionSlots)+1, len(api.status.Network.NextForgers)) +} diff --git a/db/historydb/historydb.go b/db/historydb/historydb.go index bc9655f..7e057a1 100644 --- a/db/historydb/historydb.go +++ b/db/historydb/historydb.go @@ -1482,3 +1482,9 @@ func (hdb *HistoryDB) GetAccountsAPI(tokenIDs []common.TokenID, ethAddr *ethComm LastItem: accounts[0].LastItem, }, nil } + +// GetMetrics returns metrics +func (hdb *HistoryDB) GetMetrics() (Metrics, error) { + metrics := Metrics{} + return metrics, nil +} diff --git a/db/historydb/views.go b/db/historydb/views.go index 1ac5763..7b89906 100644 --- a/db/historydb/views.go +++ b/db/historydb/views.go @@ -298,14 +298,6 @@ type BatchAPI struct { LastItem uint64 `json:"-" meddler:"last_item"` } -// Network define status of the network -type Network struct { - LastBlock int64 `json:"lastBlock"` - LastBatch BatchAPI `json:"lastBatch"` - CurrentSlot int64 `json:"currentSlot"` - NextForgers []CoordinatorAPI `json:"nextForgers"` -} - // Metrics define metrics of the network type Metrics struct { TransactionsPerBatch float64 `json:"transactionsPerBatch"`