arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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		DeployGasLimit:      1000000,
736		GasPriceDiv:         100,
737		ReceiptTimeout:      60 * time.Second,
738		IntervalReceiptLoop: 500 * time.Millisecond,
739	}
740	scryptN := ethKeystore.LightScryptN
741	scryptP := ethKeystore.LightScryptP
742	keyStore := ethKeystore.NewKeyStore(pathKeystore,
743		scryptN, scryptP)
744	account, err := keyStore.ImportECDSA(sk, password)
745	require.NoError(t, err)
746	require.Equal(t, account.Address, addr)
747	err = keyStore.Unlock(account, password)
748	require.NoError(t, err)
749
750	client, err := eth.NewClient(ethClient, &account, keyStore, &eth.ClientConfig{
751		Ethereum: ethCfg,
752		Rollup: eth.RollupConfig{
753			Address: rollupAddr,
754		},
755		Auction: eth.AuctionConfig{
756			Address: ethCommon.Address{},
757			TokenHEZ: eth.TokenConfig{
758				Address: ethCommon.Address{},
759				Name:    "HEZ",
760			},
761		},
762		WDelayer: eth.WDelayerConfig{
763			Address: ethCommon.Address{},
764		},
765	})
766	require.NoError(t, err)
767
768	zkInputs := common.NewZKInputs(chainID, 100, 24, 512, 32, big.NewInt(1))
769	zkInputs.Metadata.NewStateRootRaw = &merkletree.Hash{1}
770	zkInputs.Metadata.NewExitRootRaw = &merkletree.Hash{2}
771	batchInfo.ZKInputs = zkInputs
772	err = proofClient.CalculateProof(ctx, batchInfo.ZKInputs)
773	require.NoError(t, err)
774
775	proof, pubInputs, err := proofClient.GetProof(ctx)
776	require.NoError(t, err)
777	batchInfo.Proof = proof
778	batchInfo.PublicInputs = pubInputs
779
780	batchInfo.ForgeBatchArgs = prepareForgeBatchArgs(batchInfo)
781	_, err = client.RollupForgeBatch(batchInfo.ForgeBatchArgs)
782	require.NoError(t, err)
783	batchInfo.Proof = proof
784}
785
786// TODO: Test Reorg
787// TODO: Test Pipeline
788// TODO: Test TxMonitor
789// TODO: Test forgeBatch
790// TODO: Test waitServerProof
791// TODO: Test handleReorg