arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/coordinator/coordinator.go
coordinator/coordinator.goRaw
  1package coordinator
  2
  3import (
  4	"fmt"
  5
  6	ethCommon "github.com/ethereum/go-ethereum/common"
  7	"github.com/hermeznetwork/hermez-node/batchbuilder"
  8	"github.com/hermeznetwork/hermez-node/common"
  9	"github.com/hermeznetwork/hermez-node/db/historydb"
 10	"github.com/hermeznetwork/hermez-node/eth"
 11	"github.com/hermeznetwork/hermez-node/log"
 12	"github.com/hermeznetwork/hermez-node/txselector"
 13	kvdb "github.com/iden3/go-merkletree/db"
 14	"github.com/iden3/go-merkletree/db/memory"
 15)
 16
 17// ErrStop is returned when the function is stopped asynchronously via the stop
 18// channel.  It doesn't indicate an error.
 19var ErrStop = fmt.Errorf("Stopped")
 20
 21// Config contains the Coordinator configuration
 22type Config struct {
 23	ForgerAddress ethCommon.Address
 24}
 25
 26// Coordinator implements the Coordinator type
 27type Coordinator struct {
 28	forging    bool
 29	isForgeSeq bool // WIP just for testing while implementing
 30
 31	config Config
 32
 33	batchNum        common.BatchNum
 34	serverProofPool ServerProofPool
 35
 36	// synchronizer *synchronizer.Synchronizer
 37	hdb          *historydb.HistoryDB
 38	txsel        *txselector.TxSelector
 39	batchBuilder *batchbuilder.BatchBuilder
 40
 41	ethClient  eth.ClientInterface
 42	ethTxStore kvdb.Storage
 43}
 44
 45// NewCoordinator creates a new Coordinator
 46func NewCoordinator(conf Config,
 47	hdb *historydb.HistoryDB,
 48	txsel *txselector.TxSelector,
 49	bb *batchbuilder.BatchBuilder,
 50	ethClient *eth.Client) *Coordinator { // once synchronizer is ready, synchronizer.Synchronizer will be passed as parameter here
 51	c := Coordinator{
 52		config:       conf,
 53		hdb:          hdb,
 54		txsel:        txsel,
 55		batchBuilder: bb,
 56		ethClient:    ethClient,
 57		ethTxStore:   memory.NewMemoryStorage(),
 58	}
 59	return &c
 60}
 61
 62// ForgeLoopFn is the function ran in a loop that checks if it's time to forge
 63// and forges a batch if so and sends it to outBatchCh.  Returns true if it's
 64// the coordinator turn to forge.
 65func (c *Coordinator) ForgeLoopFn(outBatchCh chan *BatchInfo, stopCh chan bool) (bool, error) {
 66	if !c.isForgeSequence() {
 67		if c.forging {
 68			log.Info("stop forging")
 69			c.forging = false
 70		}
 71		log.Debug("not in forge time")
 72		return false, nil
 73	}
 74	log.Debug("forge time")
 75	if !c.forging {
 76		log.Info("start forging")
 77		// c.batchNum = c.hdb.GetLastBatchNum() // uncomment when HistoryDB is ready
 78		err := c.txsel.Reset(c.batchNum)
 79		if err != nil {
 80			log.Errorw("TxSelector.Reset", "error", err)
 81			return true, err
 82		}
 83		err = c.batchBuilder.Reset(c.batchNum, true)
 84		if err != nil {
 85			log.Errorw("BatchBuilder.Reset", "error", err)
 86			return true, err
 87		}
 88		// c.batchQueue = NewBatchQueue()
 89		c.forging = true
 90	}
 91	// TODO once synchronizer has this method ready:
 92	// If there's been a reorg, handle it
 93	// handleReorg() function decides if the reorg must restart the pipeline or not
 94	// if c.synchronizer.Reorg():
 95	_ = c.handleReorg()
 96
 97	// 0. If there's an available server proof: Start pipeline for batchNum = batchNum + 1.
 98	// non-blocking call, returns nil if a server proof is
 99	// not available, or non-nil otherwise.
100	serverProofInfo, err := c.serverProofPool.GetNextAvailable(stopCh)
101	if err != nil {
102		return true, err
103	}
104
105	log.Debugw("start forge")
106	batchInfo, err := c.forge(serverProofInfo)
107	if err != nil {
108		log.Errorw("forge", "error", err)
109		return true, err
110	}
111	log.Debugw("end forge", "batchNum", batchInfo.batchNum)
112	outBatchCh <- batchInfo
113	return true, nil
114}
115
116// GetProofCallForgeLoopFn is the function ran in a loop that gets a forged
117// batch via inBatchCh, waits for the proof server to finish, calls the ForgeBatch
118// function in the Rollup Smart Contract, and sends the batch to outBatchCh.
119func (c *Coordinator) GetProofCallForgeLoopFn(inBatchCh, outBatchCh chan *BatchInfo, stopCh chan bool) error {
120	select {
121	case <-stopCh:
122		log.Info("forgeLoopFn stopped")
123		return ErrStop
124	case batchInfo := <-inBatchCh:
125		log.Debugw("start getProofCallForge", "batchNum", batchInfo.batchNum)
126		if err := c.getProofCallForge(batchInfo); err != nil {
127			return err
128		}
129		log.Debugw("end getProofCallForge", "batchNum", batchInfo.batchNum)
130		outBatchCh <- batchInfo
131	}
132	return nil
133}
134
135// ForgeCallConfirmLoopFn is the function ran in a loop that gets a batch that
136// has been sent to the Rollup Smart Contract via inBatchCh and waits for the
137// ethereum transaction confirmation.
138func (c *Coordinator) ForgeCallConfirmLoopFn(inBatchCh chan *BatchInfo, stopCh chan bool) error {
139	select {
140	case <-stopCh:
141		log.Info("forgeConfirmLoopFn stopped")
142		return ErrStop
143	case batchInfo := <-inBatchCh:
144		log.Debugw("start forgeCallConfirm", "batchNum", batchInfo.batchNum)
145		if err := c.forgeCallConfirm(batchInfo); err != nil {
146			return err
147		}
148		log.Debugw("end forgeCallConfirm", "batchNum", batchInfo.batchNum)
149	}
150	return nil
151}
152
153func (c *Coordinator) forge(serverProofInfo *ServerProofInfo) (*BatchInfo, error) {
154	// remove transactions from the pool that have been there for too long
155	err := c.purgeRemoveByTimeout()
156	if err != nil {
157		return nil, err
158	}
159
160	c.batchNum = c.batchNum + 1
161	batchInfo := NewBatchInfo(c.batchNum, serverProofInfo) // to accumulate metadata of the batch
162
163	var poolL2Txs []*common.PoolL2Tx
164	// var feesInfo
165	var l1UserTxsExtra, l1OperatorTxs []*common.L1Tx
166	// 1. Decide if we forge L2Tx or L1+L2Tx
167	if c.shouldL1L2Batch() {
168		// 2a: L1+L2 txs
169		// l1UserTxs, toForgeL1TxsNumber := c.hdb.GetNextL1UserTxs() // TODO once HistoryDB is ready, uncomment
170		var l1UserTxs []*common.L1Tx = nil                                                                // tmp, depends on HistoryDB
171		l1UserTxsExtra, l1OperatorTxs, poolL2Txs, err = c.txsel.GetL1L2TxSelection(c.batchNum, l1UserTxs) // TODO once feesInfo is added to method return, add the var
172		if err != nil {
173			return nil, err
174		}
175	} else {
176		// 2b: only L2 txs
177		poolL2Txs, err = c.txsel.GetL2TxSelection(c.batchNum) // TODO once feesInfo is added to method return, add the var
178		if err != nil {
179			return nil, err
180		}
181		l1UserTxsExtra = nil
182		l1OperatorTxs = nil
183	}
184
185	// Run purger to invalidate transactions that become invalid beause of
186	// the poolL2Txs selected.  Will mark as invalid the txs that have a
187	// (fromIdx, nonce) which already appears in the selected txs (includes
188	// all the nonces smaller than the current one)
189	err = c.purgeInvalidDueToL2TxsSelection(poolL2Txs)
190	if err != nil {
191		return nil, err
192	}
193
194	// 3.  Save metadata from TxSelector output for BatchNum
195	batchInfo.SetTxsInfo(l1UserTxsExtra, l1OperatorTxs, poolL2Txs) // TODO feesInfo
196
197	// 4. Call BatchBuilder with TxSelector output
198	configBatch := &batchbuilder.ConfigBatch{
199		ForgerAddress: c.config.ForgerAddress,
200	}
201	zkInputs, err := c.batchBuilder.BuildBatch(configBatch, l1UserTxsExtra, l1OperatorTxs, poolL2Txs, nil) // TODO []common.TokenID --> feesInfo
202	if err != nil {
203		return nil, err
204	}
205
206	// 5. Save metadata from BatchBuilder output for BatchNum
207	batchInfo.SetZKInputs(zkInputs)
208
209	// 6. Call an idle server proof with BatchBuilder output, save server proof info for batchNum
210	err = batchInfo.serverProof.CalculateProof(zkInputs)
211	if err != nil {
212		return nil, err
213	}
214
215	return &batchInfo, nil
216}
217
218// getProofCallForge gets the generated zkProof & sends it to the SmartContract
219func (c *Coordinator) getProofCallForge(batchInfo *BatchInfo) error {
220	serverProofInfo := batchInfo.serverProof
221	proof, err := serverProofInfo.GetProof() // blocking call, until not resolved don't continue. Returns when the proof server has calculated the proof
222	if err != nil {
223		return err
224	}
225	batchInfo.SetProof(proof)
226	forgeBatchArgs := c.prepareForgeBatchArgs(batchInfo)
227	_, err = c.ethClient.RollupForgeBatch(forgeBatchArgs)
228	if err != nil {
229		return err
230	}
231	log.Debugf("ethClient ForgeCall sent, batchNum: %d", c.batchNum)
232
233	// TODO once tx data type is defined, store ethTx (returned by ForgeCall)
234	// TBD if use ethTxStore as a disk k-v database, or use a Queue
235	// tx, err := c.ethTxStore.NewTx()
236	// if err != nil {
237	//         return err
238	// }
239	// tx.Put(ethTx.Hash(), ethTx.Bytes())
240	// if err := tx.Commit(); err!=nil {
241	//         return nil
242	// }
243
244	return nil
245}
246
247func (c *Coordinator) forgeCallConfirm(batchInfo *BatchInfo) error {
248	// TODO strategy of this sequence TBD
249	// confirm eth txs and mark them as accepted sequence
250	// ethTx := ethTxStore.GetFirstPending()
251	// waitForAccepted(ethTx) // blocking call, returns once the ethTx is mined
252	// ethTxStore.MarkAccepted(ethTx)
253	return nil
254}
255
256func (c *Coordinator) handleReorg() error {
257	return nil
258}
259
260// isForgeSequence returns true if the node is the Forger in the current ethereum block
261func (c *Coordinator) isForgeSequence() bool {
262	return c.isForgeSeq
263}
264
265func (c *Coordinator) purgeRemoveByTimeout() error {
266	return nil
267}
268
269func (c *Coordinator) purgeInvalidDueToL2TxsSelection(l2Txs []*common.PoolL2Tx) error {
270	return nil
271}
272
273func (c *Coordinator) shouldL1L2Batch() bool {
274	return false
275}
276
277func (c *Coordinator) prepareForgeBatchArgs(batchInfo *BatchInfo) *eth.RollupForgeBatchArgs {
278	return nil
279}