hermez-node/coordinator/coordinator_test.go
coordinator/coordinator_test.goRaw
1package coordinator
2
3import (
4 "context"
5 "fmt"
6 "io/ioutil"
7 "math/big"
8 "os"
9 "sync"
10 "testing"
11 "time"
12
13 ethCommon "github.com/ethereum/go-ethereum/common"
14 "github.com/hermeznetwork/hermez-node/batchbuilder"
15 "github.com/hermeznetwork/hermez-node/common"
16 dbUtils "github.com/hermeznetwork/hermez-node/db"
17 "github.com/hermeznetwork/hermez-node/db/historydb"
18 "github.com/hermeznetwork/hermez-node/db/l2db"
19 "github.com/hermeznetwork/hermez-node/db/statedb"
20 "github.com/hermeznetwork/hermez-node/eth"
21 "github.com/hermeznetwork/hermez-node/log"
22 "github.com/hermeznetwork/hermez-node/prover"
23 "github.com/hermeznetwork/hermez-node/synchronizer"
24 "github.com/hermeznetwork/hermez-node/test"
25 "github.com/hermeznetwork/hermez-node/test/til"
26 "github.com/hermeznetwork/hermez-node/txselector"
27 "github.com/hermeznetwork/tracerr"
28 "github.com/iden3/go-merkletree/db/pebble"
29 "github.com/stretchr/testify/assert"
30 "github.com/stretchr/testify/require"
31)
32
33var deleteme = []string{}
34
35func pebbleMakeCheckpoint(source, dest string) error {
36 // Remove dest folder (if it exists) before doing the checkpoint
37 if _, err := os.Stat(dest); !os.IsNotExist(err) {
38 err := os.RemoveAll(dest)
39 if err != nil {
40 return tracerr.Wrap(err)
41 }
42 } else if err != nil && !os.IsNotExist(err) {
43 return tracerr.Wrap(err)
44 }
45
46 sto, err := pebble.NewPebbleStorage(source, false)
47 if err != nil {
48 return tracerr.Wrap(err)
49 }
50 defer func() {
51 errClose := sto.Pebble().Close()
52 if errClose != nil {
53 log.Errorw("Pebble.Close", "err", errClose)
54 }
55 }()
56
57 // execute Checkpoint
58 err = sto.Pebble().Checkpoint(dest)
59 if err != nil {
60 return tracerr.Wrap(err)
61 }
62
63 return nil
64}
65
66func TestMain(m *testing.M) {
67 exitVal := m.Run()
68 for _, dir := range deleteme {
69 if err := os.RemoveAll(dir); err != nil {
70 panic(err)
71 }
72 }
73 os.Exit(exitVal)
74}
75
76var syncDBPath string
77var txSelDBPath string
78var batchBuilderDBPath string
79
80type modules struct {
81 historyDB *historydb.HistoryDB
82 l2DB *l2db.L2DB
83 txSelector *txselector.TxSelector
84 batchBuilder *batchbuilder.BatchBuilder
85 stateDB *statedb.StateDB
86}
87
88var maxL1UserTxs uint64 = 128
89var maxL1Txs uint64 = 256
90var maxL1CoordinatorTxs uint64 = maxL1Txs - maxL1UserTxs
91var maxTxs uint64 = 376
92var nLevels uint32 = 32 //nolint:deadcode,unused
93var maxFeeTxs uint32 = 64 //nolint:deadcode,varcheck
94
95func newTestModules(t *testing.T) modules {
96 var err error
97 syncDBPath, err = ioutil.TempDir("", "tmpSyncDB")
98 require.NoError(t, err)
99 deleteme = append(deleteme, syncDBPath)
100 syncStateDB, err := statedb.NewStateDB(syncDBPath, statedb.TypeSynchronizer, 48)
101 assert.NoError(t, err)
102
103 pass := os.Getenv("POSTGRES_PASS")
104 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
105 require.NoError(t, err)
106 test.WipeDB(db)
107 l2DB := l2db.NewL2DB(db, 10, 100, 24*time.Hour)
108 historyDB := historydb.NewHistoryDB(db)
109
110 txSelDBPath, err = ioutil.TempDir("", "tmpTxSelDB")
111 require.NoError(t, err)
112 deleteme = append(deleteme, txSelDBPath)
113
114 coordAccount := &txselector.CoordAccount{ // TODO TMP
115 Addr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"),
116 BJJ: nil,
117 AccountCreationAuth: nil,
118 }
119 txSelector, err := txselector.NewTxSelector(coordAccount, txSelDBPath, syncStateDB, l2DB)
120 assert.NoError(t, err)
121
122 batchBuilderDBPath, err = ioutil.TempDir("", "tmpBatchBuilderDB")
123 require.NoError(t, err)
124 deleteme = append(deleteme, batchBuilderDBPath)
125 batchBuilder, err := batchbuilder.NewBatchBuilder(batchBuilderDBPath, syncStateDB, nil, 0, uint64(nLevels))
126 assert.NoError(t, err)
127
128 return modules{
129 historyDB: historyDB,
130 l2DB: l2DB,
131 txSelector: txSelector,
132 batchBuilder: batchBuilder,
133 stateDB: syncStateDB,
134 }
135}
136
137type timer struct {
138 time int64
139}
140
141func (t *timer) Time() int64 {
142 currentTime := t.time
143 t.time++
144 return currentTime
145}
146
147var bidder = ethCommon.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f")
148var forger = ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
149
150func newTestCoordinator(t *testing.T, forgerAddr ethCommon.Address, ethClient *test.Client,
151 ethClientSetup *test.ClientSetup, modules modules) *Coordinator {
152 debugBatchPath, err := ioutil.TempDir("", "tmpDebugBatch")
153 require.NoError(t, err)
154 deleteme = append(deleteme, debugBatchPath)
155
156 conf := Config{
157 ForgerAddress: forgerAddr,
158 ConfirmBlocks: 5,
159 L1BatchTimeoutPerc: 0.5,
160 EthClientAttempts: 5,
161 SyncRetryInterval: 400 * time.Microsecond,
162 EthClientAttemptsDelay: 100 * time.Millisecond,
163 TxManagerCheckInterval: 300 * time.Millisecond,
164 DebugBatchPath: debugBatchPath,
165 Purger: PurgerCfg{
166 PurgeBatchDelay: 10,
167 PurgeBlockDelay: 10,
168 InvalidateBatchDelay: 4,
169 InvalidateBlockDelay: 4,
170 },
171 }
172 serverProofs := []prover.Client{
173 &prover.MockClient{Delay: 300 * time.Millisecond},
174 &prover.MockClient{Delay: 400 * time.Millisecond},
175 }
176
177 scConsts := &synchronizer.SCConsts{
178 Rollup: *ethClientSetup.RollupConstants,
179 Auction: *ethClientSetup.AuctionConstants,
180 WDelayer: *ethClientSetup.WDelayerConstants,
181 }
182 initSCVars := &synchronizer.SCVariables{
183 Rollup: *ethClientSetup.RollupVariables,
184 Auction: *ethClientSetup.AuctionVariables,
185 WDelayer: *ethClientSetup.WDelayerVariables,
186 }
187 coord, err := NewCoordinator(conf, modules.historyDB, modules.l2DB, modules.txSelector,
188 modules.batchBuilder, serverProofs, ethClient, scConsts, initSCVars)
189 require.NoError(t, err)
190 return coord
191}
192
193func newTestSynchronizer(t *testing.T, ethClient *test.Client, ethClientSetup *test.ClientSetup,
194 modules modules) *synchronizer.Synchronizer {
195 sync, err := synchronizer.NewSynchronizer(ethClient, modules.historyDB, modules.stateDB,
196 synchronizer.Config{
197 StatsRefreshPeriod: 0 * time.Second,
198 })
199 require.NoError(t, err)
200 return sync
201}
202
203// TestCoordinatorFlow is a test where the coordinator is stared (which means
204// that goroutines are spawned), and ethereum blocks are mined via the
205// test.Client to simulate starting and stopping forging times. This test
206// works without a synchronizer, and no l2txs are inserted in the pool, so all
207// the batches are forged empty. The purpose of this test is to manually
208// observe via the logs that nothing crashes and that the coordinator starts
209// and stops forging at the right blocks.
210func TestCoordinatorFlow(t *testing.T) {
211 if os.Getenv("TEST_COORD_FLOW") == "" {
212 return
213 }
214 ethClientSetup := test.NewClientSetupExample()
215 var timer timer
216 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
217 modules := newTestModules(t)
218 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
219
220 // Bid for slot 2 and 4
221 _, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
222 require.NoError(t, err)
223 _, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
224 require.NoError(t, err)
225 _, err = ethClient.AuctionBidSimple(4, big.NewInt(9999))
226 require.NoError(t, err)
227
228 coord.Start()
229 time.Sleep(1 * time.Second)
230
231 waitForSlot := func(slot int64) {
232 for {
233 blockNum, err := ethClient.EthLastBlock()
234 require.NoError(t, err)
235 nextBlockSlot, err := ethClient.AuctionGetSlotNumber(blockNum + 1)
236 require.NoError(t, err)
237 if nextBlockSlot == slot {
238 break
239 }
240 ethClient.CtlMineBlock()
241 time.Sleep(100 * time.Millisecond)
242 var stats synchronizer.Stats
243 stats.Eth.LastBlock = *ethClient.CtlLastBlock()
244 stats.Sync.LastBlock = stats.Eth.LastBlock
245 stats.Eth.LastBatch = ethClient.CtlLastForgedBatch()
246 stats.Sync.LastBatch = stats.Eth.LastBatch
247 canForge, err := ethClient.AuctionCanForge(forger, blockNum+1)
248 require.NoError(t, err)
249 if canForge {
250 // fmt.Println("DBG canForge")
251 stats.Sync.Auction.CurrentSlot.Forger = forger
252 }
253 // Copy stateDB to synchronizer if there was a new batch
254 source := fmt.Sprintf("%v/BatchNum%v", batchBuilderDBPath, stats.Sync.LastBatch)
255 dest := fmt.Sprintf("%v/BatchNum%v", syncDBPath, stats.Sync.LastBatch)
256 if stats.Sync.LastBatch != 0 {
257 if _, err := os.Stat(dest); os.IsNotExist(err) {
258 log.Infow("Making pebble checkpoint for sync",
259 "source", source, "dest", dest)
260 err = pebbleMakeCheckpoint(source, dest)
261 require.NoError(t, err)
262 }
263 }
264 coord.SendMsg(MsgSyncBlock{
265 Stats: stats,
266 })
267 }
268 }
269
270 // NOTE: With the current test, the coordinator will enter in forge
271 // time before the bidded slot because no one else is forging in the
272 // other slots before the slot deadline.
273 // simulate forgeSequence time
274 waitForSlot(2)
275 log.Info("~~~ simulate entering in forge time")
276 time.Sleep(1 * time.Second)
277
278 // simulate going out from forgeSequence
279 waitForSlot(3)
280 log.Info("~~~ simulate going out from forge time")
281 time.Sleep(1 * time.Second)
282
283 // simulate entering forgeSequence time again
284 waitForSlot(4)
285 log.Info("~~~ simulate entering in forge time again")
286 time.Sleep(2 * time.Second)
287
288 // simulate stopping forgerLoop by channel
289 log.Info("~~~ simulate stopping forgerLoop by closing coordinator stopch")
290 coord.Stop()
291 time.Sleep(1 * time.Second)
292}
293
294func TestCoordinatorStartStop(t *testing.T) {
295 ethClientSetup := test.NewClientSetupExample()
296 var timer timer
297 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
298 modules := newTestModules(t)
299 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
300 coord.Start()
301 coord.Stop()
302}
303
304func TestCoordCanForge(t *testing.T) {
305 ethClientSetup := test.NewClientSetupExample()
306 bootForger := ethClientSetup.AuctionVariables.BootCoordinator
307
308 var timer timer
309 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
310 modules := newTestModules(t)
311 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
312 _, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
313 require.NoError(t, err)
314 bid, ok := new(big.Int).SetString("12000000000000000000", 10)
315 if !ok {
316 panic("bad bid")
317 }
318 _, err = ethClient.AuctionBidSimple(2, bid)
319 require.NoError(t, err)
320
321 modules2 := newTestModules(t)
322 bootCoord := newTestCoordinator(t, bootForger, ethClient, ethClientSetup, modules2)
323
324 assert.Equal(t, forger, coord.cfg.ForgerAddress)
325 assert.Equal(t, bootForger, bootCoord.cfg.ForgerAddress)
326 ethBootCoord, err := ethClient.AuctionGetBootCoordinator()
327 require.NoError(t, err)
328 assert.Equal(t, &bootForger, ethBootCoord)
329
330 var stats synchronizer.Stats
331
332 // Slot 0. No bid, so the winner is the boot coordinator
333 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
334 stats.Sync.LastBlock = stats.Eth.LastBlock
335 stats.Sync.Auction.CurrentSlot.Forger = bootForger
336 assert.Equal(t, false, coord.canForge(&stats))
337 assert.Equal(t, true, bootCoord.canForge(&stats))
338
339 // Slot 0. No bid, and we reach the deadline, so anyone can forge
340 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
341 int64(ethClientSetup.AuctionVariables.SlotDeadline)
342 stats.Sync.LastBlock = stats.Eth.LastBlock
343 stats.Sync.Auction.CurrentSlot.Forger = bootForger
344 assert.Equal(t, true, coord.canForge(&stats))
345 assert.Equal(t, true, bootCoord.canForge(&stats))
346
347 // Slot 1. coordinator bid, so the winner is the coordinator
348 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
349 1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
350 stats.Sync.LastBlock = stats.Eth.LastBlock
351 stats.Sync.Auction.CurrentSlot.Forger = forger
352 assert.Equal(t, true, coord.canForge(&stats))
353 assert.Equal(t, false, bootCoord.canForge(&stats))
354}
355
356func TestCoordHandleMsgSyncBlock(t *testing.T) {
357 ethClientSetup := test.NewClientSetupExample()
358 bootForger := ethClientSetup.AuctionVariables.BootCoordinator
359
360 var timer timer
361 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
362 modules := newTestModules(t)
363 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
364 _, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
365 require.NoError(t, err)
366 bid, ok := new(big.Int).SetString("11000000000000000000", 10)
367 if !ok {
368 panic("bad bid")
369 }
370 _, err = ethClient.AuctionBidSimple(2, bid)
371 require.NoError(t, err)
372
373 var msg MsgSyncBlock
374 stats := &msg.Stats
375 ctx := context.Background()
376
377 // Slot 0. No bid, so the winner is the boot coordinator
378 // pipelineStarted: false -> false
379 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
380 stats.Sync.LastBlock = stats.Eth.LastBlock
381 stats.Sync.Auction.CurrentSlot.Forger = bootForger
382 assert.Equal(t, false, coord.canForge(stats))
383 require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
384 assert.Nil(t, coord.pipeline)
385
386 // Slot 0. No bid, and we reach the deadline, so anyone can forge
387 // pipelineStarted: false -> true
388 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
389 int64(ethClientSetup.AuctionVariables.SlotDeadline)
390 stats.Sync.LastBlock = stats.Eth.LastBlock
391 stats.Sync.Auction.CurrentSlot.Forger = bootForger
392 assert.Equal(t, true, coord.canForge(stats))
393 require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
394 assert.NotNil(t, coord.pipeline)
395
396 // Slot 0. No bid, and we reach the deadline, so anyone can forge
397 // pipelineStarted: true -> true
398 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
399 int64(ethClientSetup.AuctionVariables.SlotDeadline) + 1
400 stats.Sync.LastBlock = stats.Eth.LastBlock
401 stats.Sync.Auction.CurrentSlot.Forger = bootForger
402 assert.Equal(t, true, coord.canForge(stats))
403 require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
404 assert.NotNil(t, coord.pipeline)
405
406 // Slot 0. No bid, so the winner is the boot coordinator
407 // pipelineStarted: true -> false
408 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
409 1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
410 stats.Sync.LastBlock = stats.Eth.LastBlock
411 stats.Sync.Auction.CurrentSlot.Forger = bootForger
412 assert.Equal(t, false, coord.canForge(stats))
413 require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
414 assert.Nil(t, coord.pipeline)
415}
416
417func TestPipelineShouldL1L2Batch(t *testing.T) {
418 ethClientSetup := test.NewClientSetupExample()
419
420 var timer timer
421 ctx := context.Background()
422 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
423 modules := newTestModules(t)
424 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
425 pipeline, err := coord.newPipeline(ctx)
426 require.NoError(t, err)
427 pipeline.vars = coord.vars
428
429 // Check that the parameters are the ones we expect and use in this test
430 require.Equal(t, 0.5, pipeline.cfg.L1BatchTimeoutPerc)
431 require.Equal(t, int64(9), ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout)
432 l1BatchTimeoutPerc := pipeline.cfg.L1BatchTimeoutPerc
433 l1BatchTimeout := ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout
434
435 var stats synchronizer.Stats
436
437 startBlock := int64(100)
438
439 //
440 // No scheduled L1Batch
441 //
442
443 // Last L1Batch was a long time ago
444 stats.Eth.LastBlock.Num = startBlock
445 stats.Sync.LastBlock = stats.Eth.LastBlock
446 stats.Sync.LastL1BatchBlock = 0
447 pipeline.stats = stats
448 assert.Equal(t, true, pipeline.shouldL1L2Batch())
449
450 stats.Sync.LastL1BatchBlock = startBlock
451
452 // We are are one block before the timeout range * 0.5
453 stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc) - 1
454 stats.Sync.LastBlock = stats.Eth.LastBlock
455 pipeline.stats = stats
456 assert.Equal(t, false, pipeline.shouldL1L2Batch())
457
458 // We are are at timeout range * 0.5
459 stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc)
460 stats.Sync.LastBlock = stats.Eth.LastBlock
461 pipeline.stats = stats
462 assert.Equal(t, true, pipeline.shouldL1L2Batch())
463
464 //
465 // Scheduled L1Batch
466 //
467 pipeline.lastScheduledL1BatchBlockNum = startBlock
468 stats.Sync.LastL1BatchBlock = startBlock - 10
469
470 // We are are one block before the timeout range * 0.5
471 stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc) - 1
472 stats.Sync.LastBlock = stats.Eth.LastBlock
473 pipeline.stats = stats
474 assert.Equal(t, false, pipeline.shouldL1L2Batch())
475
476 // We are are at timeout range * 0.5
477 stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc)
478 stats.Sync.LastBlock = stats.Eth.LastBlock
479 pipeline.stats = stats
480 assert.Equal(t, true, pipeline.shouldL1L2Batch())
481}
482
483// ethAddTokens adds the tokens from the blocks to the blockchain
484func ethAddTokens(blocks []common.BlockData, client *test.Client) {
485 for _, block := range blocks {
486 for _, token := range block.Rollup.AddedTokens {
487 consts := eth.ERC20Consts{
488 Name: fmt.Sprintf("Token %d", token.TokenID),
489 Symbol: fmt.Sprintf("TK%d", token.TokenID),
490 Decimals: 18,
491 }
492 // tokenConsts[token.TokenID] = consts
493 client.CtlAddERC20(token.EthAddr, consts)
494 }
495 }
496}
497
498const testTokensLen = 3
499const testUsersLen = 4
500
501func preloadSync(t *testing.T, ethClient *test.Client, sync *synchronizer.Synchronizer,
502 historyDB *historydb.HistoryDB, stateDB *statedb.StateDB) *til.Context {
503 // Create a set with `testTokensLen` tokens and for each token
504 // `testUsersLen` accounts.
505 var set []til.Instruction
506 // set = append(set, til.Instruction{Typ: "Blockchain"})
507 for tokenID := 1; tokenID < testTokensLen; tokenID++ {
508 set = append(set, til.Instruction{
509 Typ: til.TypeAddToken,
510 TokenID: common.TokenID(tokenID),
511 })
512 }
513 depositAmount, ok := new(big.Int).SetString("10225000000000000000000000000000000", 10)
514 require.True(t, ok)
515 for tokenID := 0; tokenID < testTokensLen; tokenID++ {
516 for user := 0; user < testUsersLen; user++ {
517 set = append(set, til.Instruction{
518 Typ: common.TxTypeCreateAccountDeposit,
519 TokenID: common.TokenID(tokenID),
520 DepositAmount: depositAmount,
521 From: fmt.Sprintf("User%d", user),
522 })
523 }
524 }
525 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
526 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
527 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
528
529 tc := til.NewContext(common.RollupConstMaxL1UserTx)
530 blocks, err := tc.GenerateBlocksFromInstructions(set)
531 require.NoError(t, err)
532 require.NotNil(t, blocks)
533
534 ethAddTokens(blocks, ethClient)
535 err = ethClient.CtlAddBlocks(blocks)
536 require.NoError(t, err)
537
538 ctx := context.Background()
539 for {
540 syncBlock, discards, err := sync.Sync2(ctx, nil)
541 require.NoError(t, err)
542 require.Nil(t, discards)
543 if syncBlock == nil {
544 break
545 }
546 }
547 dbTokens, err := historyDB.GetAllTokens()
548 require.Nil(t, err)
549 require.Equal(t, testTokensLen, len(dbTokens))
550
551 dbAccounts, err := historyDB.GetAllAccounts()
552 require.Nil(t, err)
553 require.Equal(t, testTokensLen*testUsersLen, len(dbAccounts))
554
555 sdbAccounts, err := stateDB.GetAccounts()
556 require.Nil(t, err)
557 require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
558
559 return tc
560}
561
562func TestPipeline1(t *testing.T) {
563 ethClientSetup := test.NewClientSetupExample()
564
565 var timer timer
566 ctx := context.Background()
567 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
568 modules := newTestModules(t)
569 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
570 sync := newTestSynchronizer(t, ethClient, ethClientSetup, modules)
571 pipeline, err := coord.newPipeline(ctx)
572 require.NoError(t, err)
573
574 require.NotNil(t, sync)
575 require.NotNil(t, pipeline)
576
577 // preload the synchronier (via the test ethClient) some tokens and
578 // users with positive balances
579 tilCtx := preloadSync(t, ethClient, sync, modules.historyDB, modules.stateDB)
580 syncStats := sync.Stats()
581 batchNum := common.BatchNum(syncStats.Sync.LastBatch)
582 syncSCVars := sync.SCVars()
583
584 // Insert some l2txs in the Pool
585 setPool := `
586Type: PoolL2
587
588PoolTransfer(0) User0-User1: 100 (126)
589PoolTransfer(0) User1-User2: 200 (126)
590PoolTransfer(0) User2-User3: 300 (126)
591 `
592 l2txs, err := tilCtx.GeneratePoolL2Txs(setPool)
593 require.NoError(t, err)
594 for _, tx := range l2txs {
595 err := modules.l2DB.AddTxTest(&tx) //nolint:gosec
596 require.NoError(t, err)
597 }
598
599 err = pipeline.reset(batchNum, syncStats.Sync.LastForgeL1TxsNum, &synchronizer.SCVariables{
600 Rollup: *syncSCVars.Rollup,
601 Auction: *syncSCVars.Auction,
602 WDelayer: *syncSCVars.WDelayer,
603 })
604 require.NoError(t, err)
605 // Sanity check
606 sdbAccounts, err := pipeline.txSelector.LocalAccountsDB().GetAccounts()
607 require.Nil(t, err)
608 require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
609
610 // Sanity check
611 sdbAccounts, err = pipeline.batchBuilder.LocalStateDB().GetAccounts()
612 require.Nil(t, err)
613 require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
614
615 // Sanity check
616 require.Equal(t, modules.stateDB.MerkleTree().Root(),
617 pipeline.batchBuilder.LocalStateDB().MerkleTree().Root())
618
619 batchNum++
620
621 selectionConfig := &txselector.SelectionConfig{
622 MaxL1UserTxs: maxL1UserTxs,
623 MaxL1CoordinatorTxs: maxL1CoordinatorTxs,
624 ProcessTxsConfig: statedb.ProcessTxsConfig{
625 NLevels: nLevels,
626 MaxFeeTx: maxFeeTxs,
627 MaxTx: uint32(maxTxs),
628 MaxL1Tx: uint32(maxL1Txs),
629 },
630 }
631
632 batchInfo, err := pipeline.forgeBatch(ctx, batchNum, selectionConfig)
633 require.NoError(t, err)
634 assert.Equal(t, 3, len(batchInfo.L2Txs))
635
636 batchNum++
637 batchInfo, err = pipeline.forgeBatch(ctx, batchNum, selectionConfig)
638 require.NoError(t, err)
639 assert.Equal(t, 0, len(batchInfo.L2Txs))
640}
641
642func TestCoordinatorStress(t *testing.T) {
643 if os.Getenv("TEST_COORD_STRESS") == "" {
644 return
645 }
646 log.Info("Begin Test Coord Stress")
647 ethClientSetup := test.NewClientSetupExample()
648 var timer timer
649 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
650 modules := newTestModules(t)
651 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
652 syn := newTestSynchronizer(t, ethClient, ethClientSetup, modules)
653
654 coord.Start()
655 ctx, cancel := context.WithCancel(context.Background())
656 var wg sync.WaitGroup
657
658 // Synchronizer loop
659 wg.Add(1)
660 go func() {
661 for {
662 blockData, _, err := syn.Sync2(ctx, nil)
663 if ctx.Err() != nil {
664 wg.Done()
665 return
666 }
667 require.NoError(t, err)
668 if blockData != nil {
669 stats := syn.Stats()
670 coord.SendMsg(MsgSyncBlock{
671 Stats: *stats,
672 Batches: blockData.Rollup.Batches,
673 Vars: synchronizer.SCVariablesPtr{
674 Rollup: blockData.Rollup.Vars,
675 Auction: blockData.Auction.Vars,
676 WDelayer: blockData.WDelayer.Vars,
677 },
678 })
679 } else {
680 time.Sleep(100 * time.Millisecond)
681 }
682 }
683 }()
684
685 // Blockchain mining loop
686 wg.Add(1)
687 go func() {
688 for {
689 select {
690 case <-ctx.Done():
691 wg.Done()
692 return
693 case <-time.After(100 * time.Millisecond):
694 ethClient.CtlMineBlock()
695 }
696 }
697 }()
698
699 time.Sleep(600 * time.Second)
700
701 cancel()
702 wg.Wait()
703 coord.Stop()
704}
705
706// TODO: Test Reorg
707// TODO: Test Pipeline
708// TODO: Test TxMonitor
709// TODO: Test forgeBatch
710// TODO: Test waitServerProof
711// TODO: Test handleReorg