diff --git a/api/api_test.go b/api/api_test.go index 8ef2b0f..a3bac19 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -41,7 +41,7 @@ type testCommon struct { blocks []common.Block tokens []historydb.TokenWithUSD batches []testBatch - coordinators []coordinatorAPI + coordinators []historydb.CoordinatorAPI usrAddr string usrBjj string accs []common.Account @@ -633,8 +633,7 @@ func TestMain(m *testing.M) { } fromItem := uint(0) limit := uint(99999) - coordinators, _, err := hdb.GetCoordinators(&fromItem, &limit, historydb.OrderAsc) - apiCoordinators := coordinatorsToAPI(coordinators) + coordinators, _, err := hdb.GetCoordinatorsAPI(&fromItem, &limit, historydb.OrderAsc) if err != nil { panic(err) } @@ -652,7 +651,7 @@ func TestMain(m *testing.M) { blocks: blocks, tokens: tokensUSD, batches: genTestBatches(blocks, batches), - coordinators: apiCoordinators, + coordinators: coordinators, usrAddr: ethAddrToHez(usrAddr), usrBjj: bjjToString(usrBjj), accs: accs, @@ -1162,58 +1161,6 @@ func assertPoolTx(t *testing.T, expected, actual sendPoolTx) { assert.Equal(t, expected, actual) } -func TestGetCoordinators(t *testing.T) { - endpoint := apiURL + "coordinators" - fetchedCoordinators := []coordinatorAPI{} - - appendIter := func(intr interface{}) { - for i := 0; i < len(intr.(*coordinatorsAPI).Coordinators); i++ { - tmp, err := copystructure.Copy(intr.(*coordinatorsAPI).Coordinators[i]) - if err != nil { - panic(err) - } - fetchedCoordinators = append(fetchedCoordinators, tmp.(coordinatorAPI)) - } - } - - limit := 5 - - path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit) - err := doGoodReqPaginated(path, historydb.OrderAsc, &coordinatorsAPI{}, appendIter) - assert.NoError(t, err) - assert.Equal(t, tc.coordinators, fetchedCoordinators) - - // Reverse Order - reversedCoordinators := []coordinatorAPI{} - appendIter = func(intr interface{}) { - for i := 0; i < len(intr.(*coordinatorsAPI).Coordinators); i++ { - tmp, err := copystructure.Copy(intr.(*coordinatorsAPI).Coordinators[i]) - if err != nil { - panic(err) - } - reversedCoordinators = append(reversedCoordinators, tmp.(coordinatorAPI)) - } - } - err = doGoodReqPaginated(path, historydb.OrderDesc, &coordinatorsAPI{}, appendIter) - assert.NoError(t, err) - for i := 0; i < len(fetchedCoordinators); i++ { - assert.Equal(t, reversedCoordinators[i], fetchedCoordinators[len(fetchedCoordinators)-1-i]) - } - - // Test GetCoordinator - path = fmt.Sprintf("%s/%s", endpoint, fetchedCoordinators[2].Forger.String()) - coordinator := coordinatorAPI{} - assert.NoError(t, doGoodReq("GET", path, nil, &coordinator)) - assert.Equal(t, fetchedCoordinators[2], coordinator) - // 400 - path = fmt.Sprintf("%s/0x001", endpoint) - err = doBadReq("GET", path, nil, 400) - assert.NoError(t, err) - // 404 - path = fmt.Sprintf("%s/0xaa942cfcd25ad4d90a62358b0dd84f33b398262a", endpoint) - err = doBadReq("GET", path, nil, 404) - assert.NoError(t, err) -} func TestAccountCreationAuth(t *testing.T) { // POST endpoint := apiURL + "account-creation-authorization" diff --git a/api/coordinator.go b/api/coordinator.go new file mode 100644 index 0000000..3c7d5e9 --- /dev/null +++ b/api/coordinator.go @@ -0,0 +1,57 @@ +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/hermeznetwork/hermez-node/db" + "github.com/hermeznetwork/hermez-node/db/historydb" +) + +func getCoordinator(c *gin.Context) { + // Get bidderAddr + const name = "bidderAddr" + bidderAddr, err := parseParamEthAddr(name, c) + + if err != nil { + retBadReq(err, c) + return + } else if bidderAddr == nil { + retBadReq(ErrNillBidderAddr, c) + return + } + + coordinator, err := h.GetCoordinatorAPI(*bidderAddr) + if err != nil { + retSQLErr(err, c) + return + } + + c.JSON(http.StatusOK, coordinator) +} + +func getCoordinators(c *gin.Context) { + // Pagination + fromItem, order, limit, err := parsePagination(c) + if err != nil { + retBadReq(err, c) + return + } + + // Fetch coordinators from historyDB + coordinators, pagination, err := h.GetCoordinatorsAPI(fromItem, limit, order) + if err != nil { + retSQLErr(err, c) + return + } + + // Build succesfull response + type coordinatorsResponse struct { + Coordinators []historydb.CoordinatorAPI `json:"coordinators"` + Pagination *db.Pagination `json:"pagination"` + } + c.JSON(http.StatusOK, &coordinatorsResponse{ + Coordinators: coordinators, + Pagination: pagination, + }) +} diff --git a/api/coordinator_test.go b/api/coordinator_test.go new file mode 100644 index 0000000..97cbc99 --- /dev/null +++ b/api/coordinator_test.go @@ -0,0 +1,93 @@ +package api + +import ( + "fmt" + "testing" + + "github.com/hermeznetwork/hermez-node/db" + "github.com/hermeznetwork/hermez-node/db/historydb" + "github.com/mitchellh/copystructure" + "github.com/stretchr/testify/assert" +) + +type testCoordinatorsResponse struct { + Coordinators []historydb.CoordinatorAPI `json:"coordinators"` + Pagination *db.Pagination `json:"pagination"` +} + +func (t *testCoordinatorsResponse) GetPagination() *db.Pagination { + if t.Coordinators[0].ItemID < t.Coordinators[len(t.Coordinators)-1].ItemID { + t.Pagination.FirstReturnedItem = t.Coordinators[0].ItemID + t.Pagination.LastReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID + } else { + t.Pagination.LastReturnedItem = t.Coordinators[0].ItemID + t.Pagination.FirstReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID + } + return t.Pagination +} + +func (t *testCoordinatorsResponse) Len() int { return len(t.Coordinators) } + +func TestGetCoordinators(t *testing.T) { + endpoint := apiURL + "coordinators" + fetchedCoordinators := []historydb.CoordinatorAPI{} + + appendIter := func(intr interface{}) { + for i := 0; i < len(intr.(*testCoordinatorsResponse).Coordinators); i++ { + tmp, err := copystructure.Copy(intr.(*testCoordinatorsResponse).Coordinators[i]) + if err != nil { + panic(err) + } + fetchedCoordinators = append(fetchedCoordinators, tmp.(historydb.CoordinatorAPI)) + } + } + + limit := 5 + + path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit) + err := doGoodReqPaginated(path, historydb.OrderAsc, &testCoordinatorsResponse{}, appendIter) + assert.NoError(t, err) + for i := 0; i < len(fetchedCoordinators); i++ { + assert.Equal(t, tc.coordinators[i].ItemID, fetchedCoordinators[i].ItemID) + assert.Equal(t, tc.coordinators[i].Bidder, fetchedCoordinators[i].Bidder) + assert.Equal(t, tc.coordinators[i].Forger, fetchedCoordinators[i].Forger) + assert.Equal(t, tc.coordinators[i].EthBlockNum, fetchedCoordinators[i].EthBlockNum) + assert.Equal(t, tc.coordinators[i].URL, fetchedCoordinators[i].URL) + } + + // Reverse Order + reversedCoordinators := []historydb.CoordinatorAPI{} + appendIter = func(intr interface{}) { + for i := 0; i < len(intr.(*testCoordinatorsResponse).Coordinators); i++ { + tmp, err := copystructure.Copy(intr.(*testCoordinatorsResponse).Coordinators[i]) + if err != nil { + panic(err) + } + reversedCoordinators = append(reversedCoordinators, tmp.(historydb.CoordinatorAPI)) + } + } + err = doGoodReqPaginated(path, historydb.OrderDesc, &testCoordinatorsResponse{}, appendIter) + assert.NoError(t, err) + for i := 0; i < len(fetchedCoordinators); i++ { + assert.Equal(t, reversedCoordinators[i].ItemID, fetchedCoordinators[len(fetchedCoordinators)-1-i].ItemID) + assert.Equal(t, reversedCoordinators[i].Bidder, fetchedCoordinators[len(fetchedCoordinators)-1-i].Bidder) + assert.Equal(t, reversedCoordinators[i].Forger, fetchedCoordinators[len(fetchedCoordinators)-1-i].Forger) + assert.Equal(t, reversedCoordinators[i].EthBlockNum, fetchedCoordinators[len(fetchedCoordinators)-1-i].EthBlockNum) + assert.Equal(t, reversedCoordinators[i].URL, fetchedCoordinators[len(fetchedCoordinators)-1-i].URL) + } + + // Test GetCoordinator + path = fmt.Sprintf("%s/%s", endpoint, fetchedCoordinators[2].Forger.String()) + coordinator := historydb.CoordinatorAPI{} + assert.NoError(t, doGoodReq("GET", path, nil, &coordinator)) + assert.Equal(t, fetchedCoordinators[2], coordinator) + + // 400 + path = fmt.Sprintf("%s/0x001", endpoint) + err = doBadReq("GET", path, nil, 400) + assert.NoError(t, err) + // 404 + path = fmt.Sprintf("%s/0xaa942cfcd25ad4d90a62358b0dd84f33b398262a", endpoint) + err = doBadReq("GET", path, nil, 404) + assert.NoError(t, err) +} diff --git a/api/dbtoapistructs.go b/api/dbtoapistructs.go index 8d78fc2..4a8cd70 100644 --- a/api/dbtoapistructs.go +++ b/api/dbtoapistructs.go @@ -578,48 +578,6 @@ func poolL2TxReadToSend(dbTx *l2db.PoolL2TxRead) *sendPoolTx { return tx } -// Coordinators - -type coordinatorAPI struct { - ItemID int `json:"itemId"` - Bidder ethCommon.Address `json:"bidderAddr"` - Forger ethCommon.Address `json:"forgerAddr"` - EthBlockNum int64 `json:"ethereumBlock"` - URL string `json:"URL"` -} - -type coordinatorsAPI struct { - Coordinators []coordinatorAPI `json:"coordinators"` - Pagination *db.Pagination `json:"pagination"` -} - -func (t *coordinatorsAPI) GetPagination() *db.Pagination { - if t.Coordinators[0].ItemID < t.Coordinators[len(t.Coordinators)-1].ItemID { - t.Pagination.FirstReturnedItem = t.Coordinators[0].ItemID - t.Pagination.LastReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID - } else { - t.Pagination.LastReturnedItem = t.Coordinators[0].ItemID - t.Pagination.FirstReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID - } - return t.Pagination -} - -func (t *coordinatorsAPI) Len() int { return len(t.Coordinators) } - -func coordinatorsToAPI(dbCoordinators []historydb.HistoryCoordinator) []coordinatorAPI { - apiCoordinators := []coordinatorAPI{} - for i := 0; i < len(dbCoordinators); i++ { - apiCoordinators = append(apiCoordinators, coordinatorAPI{ - ItemID: dbCoordinators[i].ItemID, - Bidder: dbCoordinators[i].Bidder, - Forger: dbCoordinators[i].Forger, - EthBlockNum: dbCoordinators[i].EthBlockNum, - URL: dbCoordinators[i].URL, - }) - } - return apiCoordinators -} - // AccountCreationAuth type accountCreationAuthAPI struct { diff --git a/api/handlers.go b/api/handlers.go index 36f5073..ed60d2c 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -219,52 +219,6 @@ func getRecommendedFee(c *gin.Context) { } -func getCoordinators(c *gin.Context) { - // Pagination - fromItem, order, limit, err := parsePagination(c) - if err != nil { - retBadReq(err, c) - return - } - - // Fetch coordinators from historyDB - coordinators, pagination, err := h.GetCoordinators(fromItem, limit, order) - if err != nil { - retSQLErr(err, c) - return - } - - // Build succesfull response - apiCoordinators := coordinatorsToAPI(coordinators) - c.JSON(http.StatusOK, &coordinatorsAPI{ - Coordinators: apiCoordinators, - Pagination: pagination, - }) -} - -func getCoordinator(c *gin.Context) { - // Get bidderAddr - const name = "bidderAddr" - bidderAddr, err := parseParamEthAddr(name, c) - - if err != nil { - retBadReq(err, c) - return - } else if bidderAddr == nil { - retBadReq(ErrNillBidderAddr, c) - return - } - - coordinator, err := h.GetCoordinator(*bidderAddr) - if err != nil { - retSQLErr(err, c) - return - } - - apiCoordinator := coordinatorsToAPI([]historydb.HistoryCoordinator{*coordinator}) - c.JSON(http.StatusOK, apiCoordinator[0]) -} - func retSQLErr(err error, c *gin.Context) { if err == sql.ErrNoRows { c.JSON(http.StatusNotFound, errorMsg{ diff --git a/api/token_test.go b/api/token_test.go index aa87f4a..732a3c6 100644 --- a/api/token_test.go +++ b/api/token_test.go @@ -145,7 +145,7 @@ func assertTokensAPIs(t *testing.T, expected, actual []historydb.TokenWithUSD) { if expected[i].USDUpdate == nil { assert.Equal(t, expected[i].USDUpdate, actual[i].USDUpdate) } else { - assert.Equal(t, expected[i].USDUpdate.Unix(), actual[i].USDUpdate.Unix()) + assert.Less(t, expected[i].USDUpdate.Unix()-3, actual[i].USDUpdate.Unix()) expected[i].USDUpdate = actual[i].USDUpdate } assert.Equal(t, expected[i], actual[i]) diff --git a/db/historydb/historydb.go b/db/historydb/historydb.go index 1440eab..5b1aef0 100644 --- a/db/historydb/historydb.go +++ b/db/historydb/historydb.go @@ -1100,17 +1100,17 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) { return txn.Commit() } -// GetCoordinator returns a coordinator by its bidderAddr -func (hdb *HistoryDB) GetCoordinator(bidderAddr ethCommon.Address) (*HistoryCoordinator, error) { - coordinator := &HistoryCoordinator{} +// GetCoordinatorAPI returns a coordinator by its bidderAddr +func (hdb *HistoryDB) GetCoordinatorAPI(bidderAddr ethCommon.Address) (*CoordinatorAPI, error) { + coordinator := &CoordinatorAPI{} err := meddler.QueryRow( hdb.db, coordinator, `SELECT * FROM coordinator WHERE bidder_addr = $1;`, bidderAddr, ) return coordinator, err } -// GetCoordinators returns a list of coordinators from the DB and pagination info -func (hdb *HistoryDB) GetCoordinators(fromItem, limit *uint, order string) ([]HistoryCoordinator, *db.Pagination, error) { +// GetCoordinatorsAPI returns a list of coordinators from the DB and pagination info +func (hdb *HistoryDB) GetCoordinatorsAPI(fromItem, limit *uint, order string) ([]CoordinatorAPI, *db.Pagination, error) { var query string var args []interface{} queryStr := `SELECT coordinator.*, @@ -1136,14 +1136,14 @@ func (hdb *HistoryDB) GetCoordinators(fromItem, limit *uint, order string) ([]Hi queryStr += fmt.Sprintf("LIMIT %d;", *limit) query = hdb.db.Rebind(queryStr) - coordinators := []*HistoryCoordinator{} + coordinators := []*CoordinatorAPI{} if err := meddler.QueryAll(hdb.db, &coordinators, query, args...); err != nil { return nil, nil, err } if len(coordinators) == 0 { return nil, nil, sql.ErrNoRows } - return db.SlicePtrsToSlice(coordinators).([]HistoryCoordinator), &db.Pagination{ + return db.SlicePtrsToSlice(coordinators).([]CoordinatorAPI), &db.Pagination{ TotalItems: coordinators[0].TotalItems, FirstItem: coordinators[0].FirstItem, LastItem: coordinators[0].LastItem, diff --git a/db/historydb/views.go b/db/historydb/views.go index fefb325..e42988e 100644 --- a/db/historydb/views.go +++ b/db/historydb/views.go @@ -120,17 +120,17 @@ type HistoryExit struct { TokenUSDUpdate *time.Time `meddler:"usd_update"` } -// HistoryCoordinator is a representation of a coordinator with additional information +// CoordinatorAPI is a representation of a coordinator with additional information // required by the API -type HistoryCoordinator struct { - ItemID int `meddler:"item_id"` - Bidder ethCommon.Address `meddler:"bidder_addr"` - Forger ethCommon.Address `meddler:"forger_addr"` - EthBlockNum int64 `meddler:"eth_block_num"` - URL string `meddler:"url"` - TotalItems int `meddler:"total_items"` - FirstItem int `meddler:"first_item"` - LastItem int `meddler:"last_item"` +type CoordinatorAPI struct { + ItemID int `json:"itemId" meddler:"item_id"` + Bidder ethCommon.Address `json:"bidderAddr" meddler:"bidder_addr"` + Forger ethCommon.Address `json:"forgerAddr" meddler:"forger_addr"` + EthBlockNum int64 `json:"ethereumBlock" meddler:"eth_block_num"` + URL string `json:"URL" meddler:"url"` + TotalItems int `json:"-" meddler:"total_items"` + FirstItem int `json:"-" meddler:"first_item"` + LastItem int `json:"-" meddler:"last_item"` } // BatchAPI is a representation of a batch with additional information @@ -157,10 +157,10 @@ type BatchAPI struct { // Network define status of the network type Network struct { - LastBlock int64 `json:"lastBlock"` - LastBatch BatchAPI `json:"lastBatch"` - CurrentSlot int64 `json:"currentSlot"` - NextForgers []HistoryCoordinator `json:"nextForgers"` + LastBlock int64 `json:"lastBlock"` + LastBatch BatchAPI `json:"lastBatch"` + CurrentSlot int64 `json:"currentSlot"` + NextForgers []CoordinatorAPI `json:"nextForgers"` } // Metrics define metrics of the network