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