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