hermez-node/coordinator/coordinator.go
coordinator/coordinator.goRaw
1package coordinator
2
3import (
4 "time"
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/eth"
10 "github.com/hermeznetwork/hermez-node/txselector"
11 kvdb "github.com/iden3/go-merkletree/db"
12 "github.com/iden3/go-merkletree/db/memory"
13)
14
15// CoordinatorConfig contains the Coordinator configuration
16type CoordinatorConfig struct {
17 ForgerAddress ethCommon.Address
18}
19
20// Coordinator implements the Coordinator type
21type Coordinator struct {
22 config CoordinatorConfig
23
24 batchNum uint64
25 batchQueue *BatchQueue
26 serverProofPool ServerProofPool
27
28 // synchronizer *synchronizer.Synchronizer
29 txsel *txselector.TxSelector
30 batchBuilder *batchbuilder.BatchBuilder
31
32 ethClient *eth.Client
33 ethTxStore kvdb.Storage
34}
35
36// NewCoordinator creates a new Coordinator
37func NewCoordinator() *Coordinator { // once synchronizer is ready, synchronizer.Synchronizer will be passed as parameter here
38 var c *Coordinator
39 // c.ethClient = eth.NewClient() // TBD
40 c.ethTxStore = memory.NewMemoryStorage()
41 return c
42}
43
44// Start starts the Coordinator service
45func (c *Coordinator) Start() {
46 // TODO TBD note: the sequences & loops & errors & logging & goroutines
47 // & channels approach still needs to be defined, the current code is a
48 // wip draft
49
50 // TBD: goroutines strategy
51
52 // if in Forge Sequence:
53 if c.isForgeSequence() {
54 // c.batchNum = c.synchronizer.LastBatchNum()
55 _ = c.txsel.Reset(c.batchNum)
56 _ = c.batchBuilder.Reset(c.batchNum, true)
57 c.batchQueue = NewBatchQueue()
58 go func() {
59 for {
60 _ = c.forgeSequence()
61 time.Sleep(1 * time.Second)
62 }
63 }()
64 go func() {
65 for {
66 _ = c.proveSequence()
67 time.Sleep(1 * time.Second)
68 }
69 }()
70 go func() {
71 for {
72 _ = c.forgeConfirmationSequence()
73 time.Sleep(1 * time.Second)
74 }
75 }()
76 }
77}
78
79func (c *Coordinator) forgeSequence() error {
80 // TODO once synchronizer has this method ready:
81 // If there's been a reorg, handle it
82 // handleReorg() function decides if the reorg must restart the pipeline or not
83 // if c.synchronizer.Reorg():
84 _ = c.handleReorg()
85
86 // 0. If there's an available server proof: Start pipeline for batchNum = batchNum + 1
87 serverProofInfo, err := c.serverProofPool.GetNextAvailable() // blocking call, returns when a server proof is available
88 if err != nil {
89 return err
90 }
91
92 // remove transactions from the pool that have been there for too long
93 err = c.purgeRemoveByTimeout()
94 if err != nil {
95 return err
96 }
97
98 c.batchNum = c.batchNum + 1
99 batchInfo := NewBatchInfo(c.batchNum, serverProofInfo) // to accumulate metadata of the batch
100
101 var poolL2Txs []*common.PoolL2Tx
102 // var feesInfo
103 var l1UserTxsExtra, l1OperatorTxs []*common.L1Tx
104 // 1. Decide if we forge L2Tx or L1+L2Tx
105 if c.shouldL1L2Batch() {
106 // 2a: L1+L2 txs
107 // l1UserTxs, toForgeL1TxsNumber := c.synchronizer.GetNextL1UserTxs() // TODO once synchronizer is ready, uncomment
108 var l1UserTxs []*common.L1Tx = nil // tmp, depends on synchronizer
109 l1UserTxsExtra, l1OperatorTxs, poolL2Txs, err = c.txsel.GetL1L2TxSelection(c.batchNum, l1UserTxs) // TODO once feesInfo is added to method return, add the var
110 if err != nil {
111 return err
112 }
113 } else {
114 // 2b: only L2 txs
115 poolL2Txs, err = c.txsel.GetL2TxSelection(c.batchNum) // TODO once feesInfo is added to method return, add the var
116 if err != nil {
117 return err
118 }
119 l1UserTxsExtra = nil
120 l1OperatorTxs = nil
121 }
122
123 // Run purger to invalidate transactions that become invalid beause of
124 // the poolL2Txs selected. Will mark as invalid the txs that have a
125 // (fromIdx, nonce) which already appears in the selected txs (includes
126 // all the nonces smaller than the current one)
127 err = c.purgeInvalidDueToL2TxsSelection(poolL2Txs)
128 if err != nil {
129 return err
130 }
131
132 // 3. Save metadata from TxSelector output for BatchNum
133 batchInfo.SetTxsInfo(l1UserTxsExtra, l1OperatorTxs, poolL2Txs) // TODO feesInfo
134
135 // 4. Call BatchBuilder with TxSelector output
136 configBatch := &batchbuilder.ConfigBatch{
137 ForgerAddress: c.config.ForgerAddress,
138 }
139 l2Txs := common.PoolL2TxsToL2Txs(poolL2Txs)
140 zkInputs, err := c.batchBuilder.BuildBatch(configBatch, l1UserTxsExtra, l1OperatorTxs, l2Txs, nil) // TODO []common.TokenID --> feesInfo
141 if err != nil {
142 return err
143 }
144
145 // 5. Save metadata from BatchBuilder output for BatchNum
146 batchInfo.SetZKInputs(zkInputs)
147
148 // 6. Call an idle server proof with BatchBuilder output, save server proof info for batchNum
149 err = batchInfo.serverProof.CalculateProof(zkInputs)
150 if err != nil {
151 return err
152 }
153 c.batchQueue.Push(&batchInfo)
154
155 return nil
156}
157
158// proveSequence gets the generated zkProof & sends it to the SmartContract
159func (c *Coordinator) proveSequence() error {
160 batchInfo := c.batchQueue.Pop()
161 if batchInfo == nil {
162 // no batches in queue, return
163 return common.ErrBatchQueueEmpty
164 }
165 serverProofInfo := batchInfo.serverProof
166 proof, err := serverProofInfo.GetProof() // blocking call, until not resolved don't continue. Returns when the proof server has calculated the proof
167 if err != nil {
168 return err
169 }
170 batchInfo.SetProof(proof)
171 callData := c.prepareCallDataForge(batchInfo)
172 _, err = c.ethClient.ForgeCall(callData)
173 if err != nil {
174 return err
175 }
176 // TODO once tx data type is defined, store ethTx (returned by ForgeCall)
177 // TBD if use ethTxStore as a disk k-v database, or use a Queue
178 // tx, err := c.ethTxStore.NewTx()
179 // if err != nil {
180 // return err
181 // }
182 // tx.Put(ethTx.Hash(), ethTx.Bytes())
183 // if err := tx.Commit(); err!=nil {
184 // return nil
185 // }
186
187 return nil
188}
189
190func (c *Coordinator) forgeConfirmationSequence() error {
191 // TODO strategy of this sequence TBD
192 // confirm eth txs and mark them as accepted sequence
193 // ethTx := ethTxStore.GetFirstPending()
194 // waitForAccepted(ethTx) // blocking call, returns once the ethTx is mined
195 // ethTxStore.MarkAccepted(ethTx)
196 return nil
197}
198
199func (c *Coordinator) handleReorg() error {
200 return nil
201}
202
203// isForgeSequence returns true if the node is the Forger in the current ethereum block
204func (c *Coordinator) isForgeSequence() bool {
205
206 return false
207}
208
209func (c *Coordinator) purgeRemoveByTimeout() error {
210
211 return nil
212}
213
214func (c *Coordinator) purgeInvalidDueToL2TxsSelection(l2Txs []*common.PoolL2Tx) error {
215
216 return nil
217}
218
219func (c *Coordinator) shouldL1L2Batch() bool {
220
221 return false
222}
223
224func (c *Coordinator) prepareCallDataForge(batchInfo *BatchInfo) *common.CallDataForge {
225 return nil
226}