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 stats.Sync.Auction.CurrentSlot.Forger = forger
269 }
270 // Copy stateDB to synchronizer if there was a new batch
271 source := fmt.Sprintf("%v/BatchNum%v", batchBuilderDBPath, stats.Sync.LastBatch)
272 dest := fmt.Sprintf("%v/BatchNum%v", syncDBPath, stats.Sync.LastBatch)
273 if stats.Sync.LastBatch != 0 {
274 if _, err := os.Stat(dest); os.IsNotExist(err) {
275 log.Infow("Making pebble checkpoint for sync",
276 "source", source, "dest", dest)
277 err = pebbleMakeCheckpoint(source, dest)
278 require.NoError(t, err)
279 }
280 }
281 coord.SendMsg(MsgSyncBlock{
282 Stats: stats,
283 })
284 }
285 }
286
287 // NOTE: With the current test, the coordinator will enter in forge
288 // time before the bidded slot because no one else is forging in the
289 // other slots before the slot deadline.
290 // simulate forgeSequence time
291 waitForSlot(2)
292 log.Info("~~~ simulate entering in forge time")
293 time.Sleep(1 * time.Second)
294
295 // simulate going out from forgeSequence
296 waitForSlot(3)
297 log.Info("~~~ simulate going out from forge time")
298 time.Sleep(1 * time.Second)
299
300 // simulate entering forgeSequence time again
301 waitForSlot(4)
302 log.Info("~~~ simulate entering in forge time again")
303 time.Sleep(2 * time.Second)
304
305 // simulate stopping forgerLoop by channel
306 log.Info("~~~ simulate stopping forgerLoop by closing coordinator stopch")
307 coord.Stop()
308 time.Sleep(1 * time.Second)
309}
310
311func TestCoordinatorStartStop(t *testing.T) {
312 ethClientSetup := test.NewClientSetupExample()
313 ethClientSetup.ChainID = big.NewInt(int64(chainID))
314 var timer timer
315 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
316 modules := newTestModules(t)
317 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
318 coord.Start()
319 coord.Stop()
320}
321
322func TestCoordCanForge(t *testing.T) {
323 ethClientSetup := test.NewClientSetupExample()
324 ethClientSetup.ChainID = big.NewInt(int64(chainID))
325 bootForger := ethClientSetup.AuctionVariables.BootCoordinator
326
327 var timer timer
328 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
329 modules := newTestModules(t)
330 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
331 _, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
332 require.NoError(t, err)
333 bid, ok := new(big.Int).SetString("12000000000000000000", 10)
334 if !ok {
335 panic("bad bid")
336 }
337 _, err = ethClient.AuctionBidSimple(2, bid)
338 require.NoError(t, err)
339
340 modules2 := newTestModules(t)
341 bootCoord := newTestCoordinator(t, bootForger, ethClient, ethClientSetup, modules2)
342
343 assert.Equal(t, forger, coord.cfg.ForgerAddress)
344 assert.Equal(t, bootForger, bootCoord.cfg.ForgerAddress)
345 ethBootCoord, err := ethClient.AuctionGetBootCoordinator()
346 require.NoError(t, err)
347 assert.Equal(t, &bootForger, ethBootCoord)
348
349 var stats synchronizer.Stats
350
351 // Slot 0. No bid, so the winner is the boot coordinator
352 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
353 stats.Sync.LastBlock = stats.Eth.LastBlock
354 stats.Sync.Auction.CurrentSlot.Forger = bootForger
355 assert.Equal(t, false, coord.canForge(&stats))
356 assert.Equal(t, true, bootCoord.canForge(&stats))
357
358 // Slot 0. No bid, and we reach the deadline, so anyone can forge
359 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
360 int64(ethClientSetup.AuctionVariables.SlotDeadline)
361 stats.Sync.LastBlock = stats.Eth.LastBlock
362 stats.Sync.Auction.CurrentSlot.Forger = bootForger
363 assert.Equal(t, true, coord.canForge(&stats))
364 assert.Equal(t, true, bootCoord.canForge(&stats))
365
366 // Slot 1. coordinator bid, so the winner is the coordinator
367 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
368 1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
369 stats.Sync.LastBlock = stats.Eth.LastBlock
370 stats.Sync.Auction.CurrentSlot.Forger = forger
371 assert.Equal(t, true, coord.canForge(&stats))
372 assert.Equal(t, false, bootCoord.canForge(&stats))
373}
374
375func TestCoordHandleMsgSyncBlock(t *testing.T) {
376 ethClientSetup := test.NewClientSetupExample()
377 ethClientSetup.ChainID = big.NewInt(int64(chainID))
378 bootForger := ethClientSetup.AuctionVariables.BootCoordinator
379
380 var timer timer
381 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
382 modules := newTestModules(t)
383 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
384 _, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
385 require.NoError(t, err)
386 bid, ok := new(big.Int).SetString("11000000000000000000", 10)
387 if !ok {
388 panic("bad bid")
389 }
390 _, err = ethClient.AuctionBidSimple(2, bid)
391 require.NoError(t, err)
392
393 var msg MsgSyncBlock
394 stats := &msg.Stats
395 ctx := context.Background()
396
397 // Slot 0. No bid, so the winner is the boot coordinator
398 // pipelineStarted: false -> false
399 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
400 stats.Sync.LastBlock = stats.Eth.LastBlock
401 stats.Sync.Auction.CurrentSlot.Forger = bootForger
402 assert.Equal(t, false, coord.canForge(stats))
403 require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
404 assert.Nil(t, coord.pipeline)
405
406 // Slot 0. No bid, and we reach the deadline, so anyone can forge
407 // pipelineStarted: false -> true
408 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
409 int64(ethClientSetup.AuctionVariables.SlotDeadline)
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, and we reach the deadline, so anyone can forge
417 // pipelineStarted: true -> true
418 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
419 int64(ethClientSetup.AuctionVariables.SlotDeadline) + 1
420 stats.Sync.LastBlock = stats.Eth.LastBlock
421 stats.Sync.Auction.CurrentSlot.Forger = bootForger
422 assert.Equal(t, true, coord.canForge(stats))
423 require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
424 assert.NotNil(t, coord.pipeline)
425
426 // Slot 0. No bid, so the winner is the boot coordinator
427 // pipelineStarted: true -> false
428 stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
429 1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
430 stats.Sync.LastBlock = stats.Eth.LastBlock
431 stats.Sync.Auction.CurrentSlot.Forger = bootForger
432 assert.Equal(t, false, coord.canForge(stats))
433 require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
434 assert.Nil(t, coord.pipeline)
435}
436
437func TestPipelineShouldL1L2Batch(t *testing.T) {
438 ethClientSetup := test.NewClientSetupExample()
439 ethClientSetup.ChainID = big.NewInt(int64(chainID))
440
441 var timer timer
442 ctx := context.Background()
443 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
444 modules := newTestModules(t)
445 var stats synchronizer.Stats
446 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
447 pipeline, err := coord.newPipeline(ctx)
448 require.NoError(t, err)
449 pipeline.vars = coord.vars
450
451 // Check that the parameters are the ones we expect and use in this test
452 require.Equal(t, 0.5, pipeline.cfg.L1BatchTimeoutPerc)
453 require.Equal(t, int64(10), ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout)
454 l1BatchTimeoutPerc := pipeline.cfg.L1BatchTimeoutPerc
455 l1BatchTimeout := ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout
456
457 startBlock := int64(100)
458 // Empty batchInfo to pass to shouldL1L2Batch() which sets debug information
459 batchInfo := BatchInfo{}
460
461 //
462 // No scheduled L1Batch
463 //
464
465 // Last L1Batch was a long time ago
466 stats.Eth.LastBlock.Num = startBlock
467 stats.Sync.LastBlock = stats.Eth.LastBlock
468 stats.Sync.LastL1BatchBlock = 0
469 pipeline.stats = stats
470 assert.Equal(t, true, pipeline.shouldL1L2Batch(&batchInfo))
471
472 stats.Sync.LastL1BatchBlock = startBlock
473
474 // We are are one block before the timeout range * 0.5
475 stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc) - 1
476 stats.Sync.LastBlock = stats.Eth.LastBlock
477 pipeline.stats = stats
478 assert.Equal(t, false, pipeline.shouldL1L2Batch(&batchInfo))
479
480 // We are are at timeout range * 0.5
481 stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc)
482 stats.Sync.LastBlock = stats.Eth.LastBlock
483 pipeline.stats = stats
484 assert.Equal(t, true, pipeline.shouldL1L2Batch(&batchInfo))
485
486 //
487 // Scheduled L1Batch
488 //
489 pipeline.lastScheduledL1BatchBlockNum = startBlock
490 stats.Sync.LastL1BatchBlock = startBlock - 10
491
492 // We are are one block before the timeout range * 0.5
493 stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc) - 1
494 stats.Sync.LastBlock = stats.Eth.LastBlock
495 pipeline.stats = stats
496 assert.Equal(t, false, pipeline.shouldL1L2Batch(&batchInfo))
497
498 // We are are at timeout range * 0.5
499 stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc)
500 stats.Sync.LastBlock = stats.Eth.LastBlock
501 pipeline.stats = stats
502 assert.Equal(t, true, pipeline.shouldL1L2Batch(&batchInfo))
503}
504
505// ethAddTokens adds the tokens from the blocks to the blockchain
506func ethAddTokens(blocks []common.BlockData, client *test.Client) {
507 for _, block := range blocks {
508 for _, token := range block.Rollup.AddedTokens {
509 consts := eth.ERC20Consts{
510 Name: fmt.Sprintf("Token %d", token.TokenID),
511 Symbol: fmt.Sprintf("TK%d", token.TokenID),
512 Decimals: 18,
513 }
514 // tokenConsts[token.TokenID] = consts
515 client.CtlAddERC20(token.EthAddr, consts)
516 }
517 }
518}
519
520const testTokensLen = 3
521const testUsersLen = 4
522
523func preloadSync(t *testing.T, ethClient *test.Client, sync *synchronizer.Synchronizer,
524 historyDB *historydb.HistoryDB, stateDB *statedb.StateDB) *til.Context {
525 // Create a set with `testTokensLen` tokens and for each token
526 // `testUsersLen` accounts.
527 var set []til.Instruction
528 // set = append(set, til.Instruction{Typ: "Blockchain"})
529 for tokenID := 1; tokenID < testTokensLen; tokenID++ {
530 set = append(set, til.Instruction{
531 Typ: til.TypeAddToken,
532 TokenID: common.TokenID(tokenID),
533 })
534 }
535 depositAmount, ok := new(big.Int).SetString("10225000000000000000000000000000000", 10)
536 require.True(t, ok)
537 for tokenID := 0; tokenID < testTokensLen; tokenID++ {
538 for user := 0; user < testUsersLen; user++ {
539 set = append(set, til.Instruction{
540 Typ: common.TxTypeCreateAccountDeposit,
541 TokenID: common.TokenID(tokenID),
542 DepositAmount: depositAmount,
543 From: fmt.Sprintf("User%d", user),
544 })
545 }
546 }
547 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
548 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
549 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
550
551 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
552 blocks, err := tc.GenerateBlocksFromInstructions(set)
553 require.NoError(t, err)
554 require.NotNil(t, blocks)
555
556 ethAddTokens(blocks, ethClient)
557 err = ethClient.CtlAddBlocks(blocks)
558 require.NoError(t, err)
559
560 ctx := context.Background()
561 for {
562 syncBlock, discards, err := sync.Sync2(ctx, nil)
563 require.NoError(t, err)
564 require.Nil(t, discards)
565 if syncBlock == nil {
566 break
567 }
568 }
569 dbTokens, err := historyDB.GetAllTokens()
570 require.Nil(t, err)
571 require.Equal(t, testTokensLen, len(dbTokens))
572
573 dbAccounts, err := historyDB.GetAllAccounts()
574 require.Nil(t, err)
575 require.Equal(t, testTokensLen*testUsersLen, len(dbAccounts))
576
577 sdbAccounts, err := stateDB.GetAccounts()
578 require.Nil(t, err)
579 require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
580
581 return tc
582}
583
584func TestPipeline1(t *testing.T) {
585 ethClientSetup := test.NewClientSetupExample()
586 ethClientSetup.ChainID = big.NewInt(int64(chainID))
587
588 var timer timer
589 ctx := context.Background()
590 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
591 modules := newTestModules(t)
592 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
593 sync := newTestSynchronizer(t, ethClient, ethClientSetup, modules)
594
595 // preload the synchronier (via the test ethClient) some tokens and
596 // users with positive balances
597 tilCtx := preloadSync(t, ethClient, sync, modules.historyDB, modules.stateDB)
598 syncStats := sync.Stats()
599 batchNum := common.BatchNum(syncStats.Sync.LastBatch)
600 syncSCVars := sync.SCVars()
601
602 pipeline, err := coord.newPipeline(ctx)
603 require.NoError(t, err)
604
605 // Insert some l2txs in the Pool
606 setPool := `
607Type: PoolL2
608
609PoolTransfer(0) User0-User1: 100 (126)
610PoolTransfer(0) User1-User2: 200 (126)
611PoolTransfer(0) User2-User3: 300 (126)
612 `
613 l2txs, err := tilCtx.GeneratePoolL2Txs(setPool)
614 require.NoError(t, err)
615 for _, tx := range l2txs {
616 err := modules.l2DB.AddTxTest(&tx) //nolint:gosec
617 require.NoError(t, err)
618 }
619
620 err = pipeline.reset(batchNum, syncStats.Sync.LastForgeL1TxsNum, syncStats, &synchronizer.SCVariables{
621 Rollup: *syncSCVars.Rollup,
622 Auction: *syncSCVars.Auction,
623 WDelayer: *syncSCVars.WDelayer,
624 })
625 require.NoError(t, err)
626 // Sanity check
627 sdbAccounts, err := pipeline.txSelector.LocalAccountsDB().GetAccounts()
628 require.Nil(t, err)
629 require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
630
631 // Sanity check
632 sdbAccounts, err = pipeline.batchBuilder.LocalStateDB().GetAccounts()
633 require.Nil(t, err)
634 require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
635
636 // Sanity check
637 require.Equal(t, modules.stateDB.MT.Root(),
638 pipeline.batchBuilder.LocalStateDB().MT.Root())
639
640 batchNum++
641
642 batchInfo, err := pipeline.forgeBatch(batchNum)
643 require.NoError(t, err)
644 assert.Equal(t, 3, len(batchInfo.L2Txs))
645
646 batchNum++
647 batchInfo, err = pipeline.forgeBatch(batchNum)
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 GasPriceDiv: 100,
741 }
742 scryptN := ethKeystore.LightScryptN
743 scryptP := ethKeystore.LightScryptP
744 keyStore := ethKeystore.NewKeyStore(pathKeystore,
745 scryptN, scryptP)
746 account, err := keyStore.ImportECDSA(sk, password)
747 require.NoError(t, err)
748 require.Equal(t, account.Address, addr)
749 err = keyStore.Unlock(account, password)
750 require.NoError(t, err)
751
752 client, err := eth.NewClient(ethClient, &account, keyStore, ð.ClientConfig{
753 Ethereum: ethCfg,
754 Rollup: eth.RollupConfig{
755 Address: rollupAddr,
756 },
757 Auction: eth.AuctionConfig{
758 Address: ethCommon.Address{},
759 TokenHEZ: eth.TokenConfig{
760 Address: ethCommon.Address{},
761 Name: "HEZ",
762 },
763 },
764 WDelayer: eth.WDelayerConfig{
765 Address: ethCommon.Address{},
766 },
767 })
768 require.NoError(t, err)
769
770 zkInputs := common.NewZKInputs(chainID, 100, 24, 512, 32, big.NewInt(1))
771 zkInputs.Metadata.NewStateRootRaw = &merkletree.Hash{1}
772 zkInputs.Metadata.NewExitRootRaw = &merkletree.Hash{2}
773 batchInfo.ZKInputs = zkInputs
774 err = proofClient.CalculateProof(ctx, batchInfo.ZKInputs)
775 require.NoError(t, err)
776
777 proof, pubInputs, err := proofClient.GetProof(ctx)
778 require.NoError(t, err)
779 batchInfo.Proof = proof
780 batchInfo.PublicInputs = pubInputs
781
782 batchInfo.ForgeBatchArgs = prepareForgeBatchArgs(batchInfo)
783 _, err = client.RollupForgeBatch(batchInfo.ForgeBatchArgs)
784 require.NoError(t, err)
785 batchInfo.Proof = proof
786}
787
788// TODO: Test Reorg
789// TODO: Test Pipeline
790// TODO: Test TxMonitor
791// TODO: Test forgeBatch
792// TODO: Test waitServerProof
793// TODO: Test handleReorg