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