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