hermez-node/coordinator/coordinator_test.go
coordinator/coordinator_test.goRaw
1package coordinator
2
3import (
4 "io/ioutil"
5 "math/big"
6 "os"
7 "testing"
8 "time"
9
10 ethCommon "github.com/ethereum/go-ethereum/common"
11 "github.com/hermeznetwork/hermez-node/batchbuilder"
12 dbUtils "github.com/hermeznetwork/hermez-node/db"
13 "github.com/hermeznetwork/hermez-node/db/l2db"
14 "github.com/hermeznetwork/hermez-node/db/statedb"
15 "github.com/hermeznetwork/hermez-node/log"
16 "github.com/hermeznetwork/hermez-node/synchronizer"
17 "github.com/hermeznetwork/hermez-node/test"
18 "github.com/hermeznetwork/hermez-node/txselector"
19 "github.com/stretchr/testify/assert"
20 "github.com/stretchr/testify/require"
21)
22
23var deleteme = []string{}
24
25func TestMain(m *testing.M) {
26 exitVal := m.Run()
27 for _, dir := range deleteme {
28 if err := os.RemoveAll(dir); err != nil {
29 panic(err)
30 }
31 }
32 os.Exit(exitVal)
33}
34
35func newTestModules(t *testing.T) (*txselector.TxSelector, *batchbuilder.BatchBuilder) { // FUTURE once Synchronizer is ready, should return it also
36 nLevels := 32
37
38 syncDBPath, err := ioutil.TempDir("", "tmpSyncDB")
39 require.Nil(t, err)
40 deleteme = append(deleteme, syncDBPath)
41 syncSdb, err := statedb.NewStateDB(syncDBPath, statedb.TypeSynchronizer, nLevels)
42 assert.Nil(t, err)
43
44 pass := os.Getenv("POSTGRES_PASS")
45 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
46 require.Nil(t, err)
47 l2DB := l2db.NewL2DB(db, 10, 100, 24*time.Hour)
48
49 txselDir, err := ioutil.TempDir("", "tmpTxSelDB")
50 require.Nil(t, err)
51 deleteme = append(deleteme, txselDir)
52 txsel, err := txselector.NewTxSelector(txselDir, syncSdb, l2DB, 10, 10, 10)
53 assert.Nil(t, err)
54
55 bbDir, err := ioutil.TempDir("", "tmpBatchBuilderDB")
56 require.Nil(t, err)
57 deleteme = append(deleteme, bbDir)
58 bb, err := batchbuilder.NewBatchBuilder(bbDir, syncSdb, nil, 0, uint64(nLevels))
59 assert.Nil(t, err)
60
61 // l1Txs, coordinatorL1Txs, poolL2Txs := test.GenerateTestTxsFromSet(t, test.SetTest0)
62
63 return txsel, bb
64}
65
66type timer struct {
67 time int64
68}
69
70func (t *timer) Time() int64 {
71 currentTime := t.time
72 t.time++
73 return currentTime
74}
75
76var bidder = ethCommon.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f")
77var forger = ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
78
79func newTestCoordinator(t *testing.T, forgerAddr ethCommon.Address, ethClient *test.Client, ethClientSetup *test.ClientSetup) *Coordinator {
80 txsel, bb := newTestModules(t)
81
82 debugBatchPath, err := ioutil.TempDir("", "tmpDebugBatch")
83 require.Nil(t, err)
84 deleteme = append(deleteme, debugBatchPath)
85
86 conf := Config{
87 ForgerAddress: forgerAddr,
88 ConfirmBlocks: 5,
89 L1BatchTimeoutPerc: 0.5,
90 EthClientAttempts: 5,
91 EthClientAttemptsDelay: 100 * time.Millisecond,
92 TxManagerCheckInterval: 500 * time.Millisecond,
93 DebugBatchPath: debugBatchPath,
94 }
95 serverProofs := []ServerProofInterface{&ServerProofMock{}, &ServerProofMock{}}
96
97 scConsts := &synchronizer.SCConsts{
98 Rollup: *ethClientSetup.RollupConstants,
99 Auction: *ethClientSetup.AuctionConstants,
100 WDelayer: *ethClientSetup.WDelayerConstants,
101 }
102 initSCVars := &synchronizer.SCVariables{
103 Rollup: *ethClientSetup.RollupVariables,
104 Auction: *ethClientSetup.AuctionVariables,
105 WDelayer: *ethClientSetup.WDelayerVariables,
106 }
107 coord, err := NewCoordinator(conf, nil, txsel, bb, serverProofs, ethClient, scConsts, initSCVars)
108 require.Nil(t, err)
109 return coord
110}
111
112func TestCoordinatorFlow(t *testing.T) {
113 if os.Getenv("TEST_COORD_FLOW") == "" {
114 return
115 }
116 ethClientSetup := test.NewClientSetupExample()
117 var timer timer
118 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
119 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
120
121 // Bid for slot 2 and 4
122 _, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
123 require.Nil(t, err)
124 _, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
125 require.Nil(t, err)
126 _, err = ethClient.AuctionBidSimple(4, big.NewInt(9999))
127 require.Nil(t, err)
128
129 coord.Start()
130 time.Sleep(1 * time.Second)
131
132 waitForSlot := func(slot int64) {
133 for {
134 blockNum, err := ethClient.EthLastBlock()
135 require.Nil(t, err)
136 nextBlockSlot, err := ethClient.AuctionGetSlotNumber(blockNum + 1)
137 require.Nil(t, err)
138 if nextBlockSlot == slot {
139 break
140 }
141 ethClient.CtlMineBlock()
142 time.Sleep(100 * time.Millisecond)
143 var stats synchronizer.Stats
144 stats.Eth.LastBlock = *ethClient.CtlLastBlock()
145 stats.Sync.LastBlock = *ethClient.CtlLastBlock()
146 canForge, err := ethClient.AuctionCanForge(forger, blockNum+1)
147 require.Nil(t, err)
148 if canForge {
149 // fmt.Println("DBG canForge")
150 stats.Sync.Auction.CurrentSlot.Forger = forger
151 }
152 coord.SendMsg(MsgSyncStats{
153 Stats: stats,
154 })
155 }
156 }
157
158 // NOTE: With the current test, the coordinator will enter in forge
159 // time before the bidded slot because no one else is forging in the
160 // other slots before the slot deadline.
161 // simulate forgeSequence time
162 waitForSlot(2)
163 log.Info("~~~ simulate entering in forge time")
164 time.Sleep(1 * time.Second)
165
166 // simulate going out from forgeSequence
167 waitForSlot(3)
168 log.Info("~~~ simulate going out from forge time")
169 time.Sleep(1 * time.Second)
170
171 // simulate entering forgeSequence time again
172 waitForSlot(4)
173 log.Info("~~~ simulate entering in forge time again")
174 time.Sleep(2 * time.Second)
175
176 // simulate stopping forgerLoop by channel
177 log.Info("~~~ simulate stopping forgerLoop by closing coordinator stopch")
178 coord.Stop()
179 time.Sleep(1 * time.Second)
180}
181
182func TestCoordinatorStartStop(t *testing.T) {
183 ethClientSetup := test.NewClientSetupExample()
184 var timer timer
185 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
186 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
187 coord.Start()
188 coord.Stop()
189}
190
191func TestCoordCanForge(t *testing.T) {
192 ethClientSetup := test.NewClientSetupExample()
193 bootForger := ethClientSetup.AuctionVariables.BootCoordinator
194
195 var timer timer
196 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
197 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
198 _, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
199 require.Nil(t, err)
200 _, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
201 require.Nil(t, err)
202
203 bootCoord := newTestCoordinator(t, bootForger, ethClient, ethClientSetup)
204
205 assert.Equal(t, forger, coord.cfg.ForgerAddress)
206 assert.Equal(t, bootForger, bootCoord.cfg.ForgerAddress)
207 ethBootCoord, err := ethClient.AuctionGetBootCoordinator()
208 require.Nil(t, err)
209 assert.Equal(t, &bootForger, ethBootCoord)
210
211 var stats synchronizer.Stats
212
213 // Slot 0. No bid, so the winner is the boot coordinator
214 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
215 stats.Sync.LastBlock = stats.Eth.LastBlock
216 stats.Sync.Auction.CurrentSlot.Forger = bootForger
217 assert.Equal(t, false, coord.canForge(&stats))
218 assert.Equal(t, true, bootCoord.canForge(&stats))
219
220 // Slot 0. No bid, and we reach the deadline, so anyone can forge
221 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
222 int64(ethClientSetup.AuctionVariables.SlotDeadline)
223 stats.Sync.LastBlock = stats.Eth.LastBlock
224 stats.Sync.Auction.CurrentSlot.Forger = bootForger
225 assert.Equal(t, true, coord.canForge(&stats))
226 assert.Equal(t, true, bootCoord.canForge(&stats))
227
228 // Slot 1. coordinator bid, so the winner is the coordinator
229 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
230 1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
231 stats.Sync.LastBlock = stats.Eth.LastBlock
232 stats.Sync.Auction.CurrentSlot.Forger = forger
233 assert.Equal(t, true, coord.canForge(&stats))
234 assert.Equal(t, false, bootCoord.canForge(&stats))
235}
236
237func TestCoordHandleMsgSyncStats(t *testing.T) {
238 ethClientSetup := test.NewClientSetupExample()
239 bootForger := ethClientSetup.AuctionVariables.BootCoordinator
240
241 var timer timer
242 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
243 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
244 _, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
245 require.Nil(t, err)
246 _, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
247 require.Nil(t, err)
248
249 var stats synchronizer.Stats
250
251 // Slot 0. No bid, so the winner is the boot coordinator
252 // pipelineStarted: false -> false
253 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
254 stats.Sync.LastBlock = stats.Eth.LastBlock
255 stats.Sync.Auction.CurrentSlot.Forger = bootForger
256 assert.Equal(t, false, coord.canForge(&stats))
257 require.Nil(t, coord.handleMsgSyncStats(&stats))
258 assert.Nil(t, coord.pipeline)
259
260 // Slot 0. No bid, and we reach the deadline, so anyone can forge
261 // pipelineStarted: false -> true
262 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
263 int64(ethClientSetup.AuctionVariables.SlotDeadline)
264 stats.Sync.LastBlock = stats.Eth.LastBlock
265 stats.Sync.Auction.CurrentSlot.Forger = bootForger
266 assert.Equal(t, true, coord.canForge(&stats))
267 require.Nil(t, coord.handleMsgSyncStats(&stats))
268 assert.NotNil(t, coord.pipeline)
269
270 // Slot 0. No bid, and we reach the deadline, so anyone can forge
271 // pipelineStarted: true -> true
272 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
273 int64(ethClientSetup.AuctionVariables.SlotDeadline) + 1
274 stats.Sync.LastBlock = stats.Eth.LastBlock
275 stats.Sync.Auction.CurrentSlot.Forger = bootForger
276 assert.Equal(t, true, coord.canForge(&stats))
277 require.Nil(t, coord.handleMsgSyncStats(&stats))
278 assert.NotNil(t, coord.pipeline)
279
280 // Slot 0. No bid, so the winner is the boot coordinator
281 // pipelineStarted: true -> false
282 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
283 1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
284 stats.Sync.LastBlock = stats.Eth.LastBlock
285 stats.Sync.Auction.CurrentSlot.Forger = bootForger
286 assert.Equal(t, false, coord.canForge(&stats))
287 require.Nil(t, coord.handleMsgSyncStats(&stats))
288 assert.Nil(t, coord.pipeline)
289}
290
291func TestPipelineShouldL1L2Batch(t *testing.T) {
292 ethClientSetup := test.NewClientSetupExample()
293
294 var timer timer
295 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
296 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
297 pipeline := coord.newPipeline()
298 pipeline.vars = coord.vars
299
300 // Check that the parameters are the ones we expect and use in this test
301 require.Equal(t, 0.5, pipeline.cfg.L1BatchTimeoutPerc)
302 require.Equal(t, int64(9), ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout)
303 l1BatchTimeoutPerc := pipeline.cfg.L1BatchTimeoutPerc
304 l1BatchTimeout := ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout
305
306 var stats synchronizer.Stats
307
308 startBlock := int64(100)
309
310 //
311 // No scheduled L1Batch
312 //
313
314 // Last L1Batch was a long time ago
315 stats.Eth.LastBlock.Num = startBlock
316 stats.Sync.LastBlock = stats.Eth.LastBlock
317 stats.Sync.LastL1BatchBlock = 0
318 pipeline.stats = stats
319 assert.Equal(t, true, pipeline.shouldL1L2Batch())
320
321 stats.Sync.LastL1BatchBlock = startBlock
322
323 // We are are one block before the timeout range * 0.5
324 stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc) - 1
325 stats.Sync.LastBlock = stats.Eth.LastBlock
326 pipeline.stats = stats
327 assert.Equal(t, false, pipeline.shouldL1L2Batch())
328
329 // We are are at timeout range * 0.5
330 stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc)
331 stats.Sync.LastBlock = stats.Eth.LastBlock
332 pipeline.stats = stats
333 assert.Equal(t, true, pipeline.shouldL1L2Batch())
334
335 //
336 // Scheduled L1Batch
337 //
338 pipeline.lastScheduledL1BatchBlockNum = startBlock
339 stats.Sync.LastL1BatchBlock = startBlock - 10
340
341 // We are are one block before the timeout range * 0.5
342 stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc) - 1
343 stats.Sync.LastBlock = stats.Eth.LastBlock
344 pipeline.stats = stats
345 assert.Equal(t, false, pipeline.shouldL1L2Batch())
346
347 // We are are at timeout range * 0.5
348 stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc)
349 stats.Sync.LastBlock = stats.Eth.LastBlock
350 pipeline.stats = stats
351 assert.Equal(t, true, pipeline.shouldL1L2Batch())
352}
353
354// TODO: Test Reorg
355// TODO: Test Pipeline
356// TODO: Test TxMonitor