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.EthClient
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.NewEthClient() // 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 l2Txs []*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, l2Txs, 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 l2Txs, 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 l2Txs 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(l2Txs)
128 if err != nil {
129 return err
130 }
131
132 // 3. Save metadata from TxSelector output for BatchNum
133 batchInfo.SetTxsInfo(l1UserTxsExtra, l1OperatorTxs, l2Txs) // TODO feesInfo
134
135 // 4. Call BatchBuilder with TxSelector output
136 configBatch := batchbuilder.ConfigBatch{
137 ForgerAddress: c.config.ForgerAddress,
138 }
139 zkInputs, err := c.batchBuilder.BuildBatch(configBatch, l1UserTxsExtra, l1OperatorTxs, l2Txs, nil) // TODO []common.TokenID --> feesInfo
140 if err != nil {
141 return err
142 }
143
144 // 5. Save metadata from BatchBuilder output for BatchNum
145 batchInfo.SetZKInputs(zkInputs)
146
147 // 6. Call an idle server proof with BatchBuilder output, save server proof info for batchNum
148 err = batchInfo.serverProof.CalculateProof(zkInputs)
149 if err != nil {
150 return err
151 }
152 c.batchQueue.Push(&batchInfo)
153
154 return nil
155}
156
157// proveSequence gets the generated zkProof & sends it to the SmartContract
158func (c *Coordinator) proveSequence() error {
159 batchInfo := c.batchQueue.Pop()
160 if batchInfo == nil {
161 // no batches in queue, return
162 return common.ErrBatchQueueEmpty
163 }
164 serverProofInfo := batchInfo.serverProof
165 proof, err := serverProofInfo.GetProof() // blocking call, until not resolved don't continue. Returns when the proof server has calculated the proof
166 if err != nil {
167 return err
168 }
169 batchInfo.SetProof(proof)
170 callData := c.prepareCallDataForge(batchInfo)
171 _, err = c.ethClient.ForgeCall(callData)
172 if err != nil {
173 return err
174 }
175 // TODO once tx data type is defined, store ethTx (returned by ForgeCall)
176 // TBD if use ethTxStore as a disk k-v database, or use a Queue
177 // tx, err := c.ethTxStore.NewTx()
178 // if err != nil {
179 // return err
180 // }
181 // tx.Put(ethTx.Hash(), ethTx.Bytes())
182 // if err := tx.Commit(); err!=nil {
183 // return nil
184 // }
185
186 return nil
187}
188
189func (c *Coordinator) forgeConfirmationSequence() error {
190 // TODO strategy of this sequence TBD
191 // confirm eth txs and mark them as accepted sequence
192 // ethTx := ethTxStore.GetFirstPending()
193 // waitForAccepted(ethTx) // blocking call, returns once the ethTx is mined
194 // ethTxStore.MarkAccepted(ethTx)
195 return nil
196}
197
198func (c *Coordinator) handleReorg() error {
199 return nil
200}
201
202// isForgeSequence returns true if the node is the Forger in the current ethereum block
203func (c *Coordinator) isForgeSequence() bool {
204
205 return false
206}
207
208func (c *Coordinator) purgeRemoveByTimeout() error {
209
210 return nil
211}
212
213func (c *Coordinator) purgeInvalidDueToL2TxsSelection(l2Txs []*common.PoolL2Tx) error {
214
215 return nil
216}
217
218func (c *Coordinator) shouldL1L2Batch() bool {
219
220 return false
221}
222
223func (c *Coordinator) prepareCallDataForge(batchInfo *BatchInfo) *common.CallDataForge {
224 return nil
225}