Redo coordinator structure, connect API to node
- API:
- Modify the constructor so that hardcoded rollup constants don't need
to be passed (introduce a `Config` and use `configAPI` internally)
- Common:
- Update rollup constants with proper *big.Int when required
- Add BidCoordinator and Slot structs used by the HistoryDB and
Synchronizer.
- Add helper methods to AuctionConstants
- AuctionVariables: Add column `DefaultSlotSetBidSlotNum` (in the SQL
table: `default_slot_set_bid_slot_num`), which indicates at which
slotNum does the `DefaultSlotSetBid` specified starts applying.
- Config:
- Move coordinator exclusive configuration from the node config to the
coordinator config
- Coordinator:
- Reorganize the code towards having the goroutines started and stopped
from the coordinator itself instead of the node.
- Remove all stop and stopped channels, and use context.Context and
sync.WaitGroup instead.
- Remove BatchInfo setters and assing variables directly
- In ServerProof and ServerProofPool use context instead stop channel.
- Use message passing to notify the coordinator about sync updates and
reorgs
- Introduce the Pipeline, which can be started and stopped by the
Coordinator
- Introduce the TxManager, which manages ethereum transactions (the
TxManager is also in charge of making the forge call to the rollup
smart contract). The TxManager keeps ethereum transactions and:
1. Waits for the transaction to be accepted
2. Waits for the transaction to be confirmed for N blocks
- In forge logic, first prepare a batch and then wait for an available
server proof to have all work ready once the proof server is ready.
- Remove the `isForgeSequence` method which was querying the smart
contract, and instead use notifications sent by the Synchronizer to
figure out if it's forging time.
- Update test (which is a minimal test to manually see if the
coordinator starts)
- HistoryDB:
- Add method to get the number of batches in a slot (used to detect when
a slot has passed the bid winner forging deadline)
- Add method to get the best bid and associated coordinator of a slot
(used to detect the forgerAddress that can forge the slot)
- General:
- Rename some instances of `currentBlock` to `lastBlock` to be more
clear.
- Node:
- Connect the API to the node and call the methods to update cached
state when the sync advances blocks.
- Call methods to update Coordinator state when the sync advances blocks
and finds reorgs.
- Synchronizer:
- Add Auction field in the Stats, which contain the current slot with
info about highest bidder and other related info required to know who
can forge in the current block.
- Better organization of cached state:
- On Sync, update the internal cached state
- On Init or Reorg, load the state from HistoryDB into the
internal cached state.
4 years ago |
|
/* Package api implements the public interface of the hermez-node using a HTTP REST API. There are two subsets of endpoints: - coordinatorEndpoints: used to receive L2 transactions and account creation authorizations. Targeted for wallets. - explorerEndpoints: used to provide all sorts of information about the network. Targeted for explorers and similar services.
About the configuration of the API: - The API is supposed to be launched using the cli found at the package cli/node, and configured through the configuration file. - The mentioned configuration file allows exposing any combination of the endpoint subsets. - Although the API can run in a "standalone" manner using the serveapi command, it won't work properly unless another process acting as a coord or sync is filling the HistoryDB.
Design principles and considerations: - In order to decouple the API process from the rest of the node, all the communication between this package and the rest of the system is done through the SQL database. As a matter of fact, the only public function of the package is the constructor NewAPI. All the information needed for the API to work should be obtained through the configuration file of the cli or the database. - The format of the requests / responses doesn't match directly with the common types, and for this reason, the package api/apitypes is used to facilitate the format conversion. Most of the time, this is done directly at the db level. - The API endpoints are fully documented using OpenAPI aka Swagger. All the endpoints are tested against the spec to ensure consistency between implementation and specification. To get a sense of which endpoints exist and how they work, it's strongly recommended to check this specification. The specification can be found at api/swagger.yml. - In general, all the API endpoints produce queries to the SQL database in order to retrieve / insert the requested information. The most notable exceptions to this are the /config endpoint, which returns a static object generated at construction time, and the /state, which also is retrieved from the database, but it's generated by API/stateapiupdater package. */ package api
import ( "errors"
ethCommon "github.com/ethereum/go-ethereum/common" "github.com/gin-gonic/gin" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/hermeznetwork/hermez-node/db/l2db" "github.com/hermeznetwork/hermez-node/metric" "github.com/hermeznetwork/tracerr" )
// API serves HTTP requests to allow external interaction with the Hermez node
type API struct { h *historydb.HistoryDB cg *configAPI l2 *l2db.L2DB hermezAddress ethCommon.Address }
// NewAPI sets the endpoints and the appropriate handlers, but doesn't start the server
func NewAPI( coordinatorEndpoints, explorerEndpoints bool, server *gin.Engine, hdb *historydb.HistoryDB, l2db *l2db.L2DB, ) (*API, error) { // Check input
if coordinatorEndpoints && l2db == nil { return nil, tracerr.Wrap(errors.New("cannot serve Coordinator endpoints without L2DB")) } if explorerEndpoints && hdb == nil { return nil, tracerr.Wrap(errors.New("cannot serve Explorer endpoints without HistoryDB")) } consts, err := hdb.GetConstants() if err != nil { return nil, err } a := &API{ h: hdb, cg: &configAPI{ RollupConstants: *newRollupConstants(consts.Rollup), AuctionConstants: consts.Auction, WDelayerConstants: consts.WDelayer, ChainID: consts.ChainID, }, l2: l2db, hermezAddress: consts.HermezAddress, }
middleware, err := metric.PrometheusMiddleware() if err != nil { return nil, err } server.Use(middleware)
server.NoRoute(a.noRoute)
v1 := server.Group("/v1")
// Add coordinator endpoints
if coordinatorEndpoints { // Account creation authorization
v1.POST("/account-creation-authorization", a.postAccountCreationAuth) v1.GET("/account-creation-authorization/:hezEthereumAddress", a.getAccountCreationAuth) // Transaction
v1.POST("/transactions-pool", a.postPoolTx) v1.GET("/transactions-pool/:id", a.getPoolTx) }
// Add explorer endpoints
if explorerEndpoints { // Account
v1.GET("/accounts", a.getAccounts) v1.GET("/accounts/:accountIndex", a.getAccount) v1.GET("/exits", a.getExits) v1.GET("/exits/:batchNum/:accountIndex", a.getExit) // Transaction
v1.GET("/transactions-history", a.getHistoryTxs) v1.GET("/transactions-history/:id", a.getHistoryTx) // Batches
v1.GET("/batches", a.getBatches) v1.GET("/batches/:batchNum", a.getBatch) v1.GET("/full-batches/:batchNum", a.getFullBatch) // Slots
v1.GET("/slots", a.getSlots) v1.GET("/slots/:slotNum", a.getSlot) // Bids
v1.GET("/bids", a.getBids) // State
v1.GET("/state", a.getState) // Config
v1.GET("/config", a.getConfig) // Tokens
v1.GET("/tokens", a.getTokens) v1.GET("/tokens/:id", a.getToken) // Coordinators
v1.GET("/coordinators", a.getCoordinators) }
return a, nil }
|