hermez-node/synchronizer/synchronizer_test.go
synchronizer/synchronizer_test.goRaw
1package synchronizer
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "io/ioutil"
8 "math/big"
9 "os"
10 "sort"
11 "testing"
12 "time"
13
14 ethCommon "github.com/ethereum/go-ethereum/common"
15 "github.com/hermeznetwork/hermez-node/common"
16 dbUtils "github.com/hermeznetwork/hermez-node/db"
17 "github.com/hermeznetwork/hermez-node/db/historydb"
18 "github.com/hermeznetwork/hermez-node/db/statedb"
19 "github.com/hermeznetwork/hermez-node/eth"
20 "github.com/hermeznetwork/hermez-node/test"
21 "github.com/hermeznetwork/hermez-node/test/til"
22 "github.com/jinzhu/copier"
23 "github.com/stretchr/testify/assert"
24 "github.com/stretchr/testify/require"
25)
26
27var tokenConsts = map[common.TokenID]eth.ERC20Consts{}
28
29type timer struct {
30 time int64
31}
32
33func (t *timer) Time() int64 {
34 currentTime := t.time
35 t.time++
36 return currentTime
37}
38
39func accountsCmp(accounts []common.Account) func(i, j int) bool {
40 return func(i, j int) bool { return accounts[i].Idx < accounts[j].Idx }
41}
42
43// Check Sync output and HistoryDB state against expected values generated by
44// til
45func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBlock *common.BlockData) {
46 // Check Blocks
47 dbBlocks, err := s.historyDB.GetAllBlocks()
48 require.NoError(t, err)
49 dbBlocks = dbBlocks[1:] // ignore block 0, added by default in the DB
50 assert.Equal(t, blockNum, len(dbBlocks))
51 assert.Equal(t, int64(blockNum), dbBlocks[blockNum-1].Num)
52 assert.NotEqual(t, dbBlocks[blockNum-1].Hash, dbBlocks[blockNum-2].Hash)
53 assert.Greater(t, dbBlocks[blockNum-1].Timestamp.Unix(), dbBlocks[blockNum-2].Timestamp.Unix())
54
55 // Check Tokens
56 assert.Equal(t, len(block.Rollup.AddedTokens), len(syncBlock.Rollup.AddedTokens))
57 dbTokens, err := s.historyDB.GetAllTokens()
58 require.NoError(t, err)
59 dbTokens = dbTokens[1:] // ignore token 0, added by default in the DB
60 for i, token := range block.Rollup.AddedTokens {
61 dbToken := dbTokens[i]
62 syncToken := syncBlock.Rollup.AddedTokens[i]
63
64 assert.Equal(t, block.Block.Num, syncToken.EthBlockNum)
65 assert.Equal(t, token.TokenID, syncToken.TokenID)
66 assert.Equal(t, token.EthAddr, syncToken.EthAddr)
67 tokenConst := tokenConsts[token.TokenID]
68 assert.Equal(t, tokenConst.Name, syncToken.Name)
69 assert.Equal(t, tokenConst.Symbol, syncToken.Symbol)
70 assert.Equal(t, tokenConst.Decimals, syncToken.Decimals)
71
72 var tokenCpy historydb.TokenWithUSD
73 //nolint:gosec
74 require.Nil(t, copier.Copy(&tokenCpy, &token)) // copy common.Token to historydb.TokenWithUSD
75 require.Nil(t, copier.Copy(&tokenCpy, &tokenConst)) // copy common.Token to historydb.TokenWithUSD
76 tokenCpy.ItemID = dbToken.ItemID // we don't care about ItemID
77 assert.Equal(t, tokenCpy, dbToken)
78 }
79
80 // Check submitted L1UserTxs
81 assert.Equal(t, len(block.Rollup.L1UserTxs), len(syncBlock.Rollup.L1UserTxs))
82 dbL1UserTxs, err := s.historyDB.GetAllL1UserTxs()
83 require.NoError(t, err)
84 // Ignore BatchNum in syncBlock.L1UserTxs because this value is set by
85 // the HistoryDB. Also ignore EffectiveAmount & EffectiveDepositAmount
86 // because this value is set by StateDB.ProcessTxs.
87 for i := range syncBlock.Rollup.L1UserTxs {
88 syncBlock.Rollup.L1UserTxs[i].BatchNum = block.Rollup.L1UserTxs[i].BatchNum
89 assert.Nil(t, syncBlock.Rollup.L1UserTxs[i].EffectiveDepositAmount)
90 assert.Nil(t, syncBlock.Rollup.L1UserTxs[i].EffectiveAmount)
91 }
92 assert.Equal(t, block.Rollup.L1UserTxs, syncBlock.Rollup.L1UserTxs)
93 for _, tx := range block.Rollup.L1UserTxs {
94 var dbTx *common.L1Tx
95 // Find tx in DB output
96 for _, _dbTx := range dbL1UserTxs {
97 if *tx.ToForgeL1TxsNum == *_dbTx.ToForgeL1TxsNum &&
98 tx.Position == _dbTx.Position {
99 dbTx = new(common.L1Tx)
100 *dbTx = _dbTx
101 break
102 }
103 }
104 // If the tx has been forged in this block, this will be
105 // reflected in the DB, and so the Effective values will be
106 // already set
107 if dbTx.BatchNum != nil {
108 tx.EffectiveAmount = tx.Amount
109 tx.EffectiveDepositAmount = tx.DepositAmount
110 }
111 assert.Equal(t, &tx, dbTx) //nolint:gosec
112 }
113
114 // Check Batches
115 assert.Equal(t, len(block.Rollup.Batches), len(syncBlock.Rollup.Batches))
116 dbBatches, err := s.historyDB.GetAllBatches()
117 require.NoError(t, err)
118
119 dbL1CoordinatorTxs, err := s.historyDB.GetAllL1CoordinatorTxs()
120 require.NoError(t, err)
121 dbL2Txs, err := s.historyDB.GetAllL2Txs()
122 require.NoError(t, err)
123 dbExits, err := s.historyDB.GetAllExits()
124 require.NoError(t, err)
125 // dbL1CoordinatorTxs := []common.L1Tx{}
126 for i, batch := range block.Rollup.Batches {
127 var dbBatch *common.Batch
128 // Find batch in DB output
129 for _, _dbBatch := range dbBatches {
130 if batch.Batch.BatchNum == _dbBatch.BatchNum {
131 dbBatch = new(common.Batch)
132 *dbBatch = _dbBatch
133 break
134 }
135 }
136 syncBatch := syncBlock.Rollup.Batches[i]
137
138 // We don't care about TotalFeesUSD. Use the syncBatch that
139 // has a TotalFeesUSD inserted by the HistoryDB
140 batch.Batch.TotalFeesUSD = syncBatch.Batch.TotalFeesUSD
141 assert.Equal(t, batch.CreatedAccounts, syncBatch.CreatedAccounts)
142 batch.Batch.NumAccounts = len(batch.CreatedAccounts)
143
144 // Test field by field to facilitate debugging of errors
145 assert.Equal(t, batch.L1UserTxs, syncBatch.L1UserTxs)
146 assert.Equal(t, batch.L1CoordinatorTxs, syncBatch.L1CoordinatorTxs)
147 assert.Equal(t, batch.L2Txs, syncBatch.L2Txs)
148 // In exit tree, we only check AccountIdx and Balance, because
149 // it's what we have precomputed before.
150 require.Equal(t, len(batch.ExitTree), len(syncBatch.ExitTree))
151 for j := range batch.ExitTree {
152 exit := &batch.ExitTree[j]
153 assert.Equal(t, exit.AccountIdx, syncBatch.ExitTree[j].AccountIdx)
154 assert.Equal(t, exit.Balance, syncBatch.ExitTree[j].Balance)
155 *exit = syncBatch.ExitTree[j]
156 }
157 assert.Equal(t, batch.Batch, syncBatch.Batch)
158 assert.Equal(t, batch, syncBatch)
159 assert.Equal(t, &batch.Batch, dbBatch) //nolint:gosec
160
161 // Check forged L1UserTxs from DB, and check effective values
162 // in sync output
163 for j, tx := range batch.L1UserTxs {
164 var dbTx *common.L1Tx
165 // Find tx in DB output
166 for _, _dbTx := range dbL1UserTxs {
167 if *tx.BatchNum == *_dbTx.BatchNum &&
168 tx.Position == _dbTx.Position {
169 dbTx = new(common.L1Tx)
170 *dbTx = _dbTx
171 break
172 }
173 }
174 assert.Equal(t, &tx, dbTx) //nolint:gosec
175
176 syncTx := &syncBlock.Rollup.Batches[i].L1UserTxs[j]
177 assert.Equal(t, syncTx.DepositAmount, syncTx.EffectiveDepositAmount)
178 assert.Equal(t, syncTx.Amount, syncTx.EffectiveAmount)
179 }
180
181 // Check L1CoordinatorTxs from DB
182 for _, tx := range batch.L1CoordinatorTxs {
183 var dbTx *common.L1Tx
184 // Find tx in DB output
185 for _, _dbTx := range dbL1CoordinatorTxs {
186 if *tx.BatchNum == *_dbTx.BatchNum &&
187 tx.Position == _dbTx.Position {
188 dbTx = new(common.L1Tx)
189 *dbTx = _dbTx
190 break
191 }
192 }
193 assert.Equal(t, &tx, dbTx) //nolint:gosec
194 }
195
196 // Check L2Txs from DB
197 for _, tx := range batch.L2Txs {
198 var dbTx *common.L2Tx
199 // Find tx in DB output
200 for _, _dbTx := range dbL2Txs {
201 if tx.BatchNum == _dbTx.BatchNum &&
202 tx.Position == _dbTx.Position {
203 dbTx = new(common.L2Tx)
204 *dbTx = _dbTx
205 break
206 }
207 }
208 assert.Equal(t, &tx, dbTx) //nolint:gosec
209 }
210
211 // Check Exits from DB
212 for _, exit := range batch.ExitTree {
213 var dbExit *common.ExitInfo
214 // Find exit in DB output
215 for _, _dbExit := range dbExits {
216 if exit.BatchNum == _dbExit.BatchNum &&
217 exit.AccountIdx == _dbExit.AccountIdx {
218 dbExit = new(common.ExitInfo)
219 *dbExit = _dbExit
220 break
221 }
222 }
223 // Compare MerkleProof in JSON because unmarshaled 0
224 // big.Int leaves the internal big.Int array at nil,
225 // and gives trouble when comparing big.Int with
226 // internal big.Int array != nil but empty.
227 mtp, err := json.Marshal(exit.MerkleProof)
228 require.NoError(t, err)
229 dbMtp, err := json.Marshal(dbExit.MerkleProof)
230 require.NoError(t, err)
231 assert.Equal(t, mtp, dbMtp)
232 dbExit.MerkleProof = exit.MerkleProof
233 assert.Equal(t, &exit, dbExit) //nolint:gosec
234 }
235 }
236
237 // Compare accounts from HistoryDB with StateDB (they should match)
238 dbAccounts, err := s.historyDB.GetAllAccounts()
239 require.NoError(t, err)
240 sdbAccounts, err := s.stateDB.GetAccounts()
241 require.NoError(t, err)
242 assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
243}
244
245func assertEqualAccountsHistoryDBStateDB(t *testing.T, hdbAccs, sdbAccs []common.Account) {
246 assert.Equal(t, len(hdbAccs), len(sdbAccs))
247 sort.SliceStable(hdbAccs, accountsCmp(hdbAccs))
248 sort.SliceStable(sdbAccs, accountsCmp(sdbAccs))
249 for i := range hdbAccs {
250 hdbAcc := hdbAccs[i]
251 sdbAcc := sdbAccs[i]
252 assert.Equal(t, hdbAcc.Idx, sdbAcc.Idx)
253 assert.Equal(t, hdbAcc.TokenID, sdbAcc.TokenID)
254 assert.Equal(t, hdbAcc.EthAddr, sdbAcc.EthAddr)
255 assert.Equal(t, hdbAcc.PublicKey, sdbAcc.PublicKey)
256 }
257}
258
259// ethAddTokens adds the tokens from the blocks to the blockchain
260func ethAddTokens(blocks []common.BlockData, client *test.Client) {
261 for _, block := range blocks {
262 for _, token := range block.Rollup.AddedTokens {
263 consts := eth.ERC20Consts{
264 Name: fmt.Sprintf("Token %d", token.TokenID),
265 Symbol: fmt.Sprintf("TK%d", token.TokenID),
266 Decimals: 18,
267 }
268 tokenConsts[token.TokenID] = consts
269 client.CtlAddERC20(token.EthAddr, consts)
270 }
271 }
272}
273
274var chainID uint16 = 0
275var deleteme = []string{}
276
277func TestMain(m *testing.M) {
278 exitVal := m.Run()
279 for _, dir := range deleteme {
280 if err := os.RemoveAll(dir); err != nil {
281 panic(err)
282 }
283 }
284 os.Exit(exitVal)
285}
286
287func newTestModules(t *testing.T) (*statedb.StateDB, *historydb.HistoryDB) {
288 // Int State DB
289 dir, err := ioutil.TempDir("", "tmpdb")
290 require.NoError(t, err)
291 deleteme = append(deleteme, dir)
292
293 stateDB, err := statedb.NewStateDB(dir, 128, statedb.TypeSynchronizer, 32, chainID)
294 require.NoError(t, err)
295
296 // Init History DB
297 pass := os.Getenv("POSTGRES_PASS")
298 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
299 require.NoError(t, err)
300 historyDB := historydb.NewHistoryDB(db)
301 // Clear DB
302 test.WipeDB(historyDB.DB())
303
304 return stateDB, historyDB
305}
306
307func TestSyncGeneral(t *testing.T) {
308 //
309 // Setup
310 //
311
312 stateDB, historyDB := newTestModules(t)
313
314 // Init eth client
315 var timer timer
316 clientSetup := test.NewClientSetupExample()
317 clientSetup.ChainID = big.NewInt(int64(chainID))
318 bootCoordAddr := clientSetup.AuctionVariables.BootCoordinator
319 client := test.NewClient(true, &timer, ðCommon.Address{}, clientSetup)
320
321 // Create Synchronizer
322 s, err := NewSynchronizer(client, historyDB, stateDB, Config{
323 StatsRefreshPeriod: 0 * time.Second,
324 })
325 require.NoError(t, err)
326
327 ctx := context.Background()
328
329 //
330 // First Sync from an initial state
331 //
332 stats := s.Stats()
333 assert.Equal(t, false, stats.Synced())
334
335 // Test Sync for rollup genesis block
336 syncBlock, discards, err := s.Sync2(ctx, nil)
337 require.NoError(t, err)
338 require.Nil(t, discards)
339 require.NotNil(t, syncBlock)
340 require.Nil(t, syncBlock.Rollup.Vars)
341 require.Nil(t, syncBlock.Auction.Vars)
342 require.Nil(t, syncBlock.WDelayer.Vars)
343 assert.Equal(t, int64(1), syncBlock.Block.Num)
344 stats = s.Stats()
345 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
346 assert.Equal(t, int64(1), stats.Eth.LastBlock.Num)
347 assert.Equal(t, int64(1), stats.Sync.LastBlock.Num)
348 vars := s.SCVars()
349 assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
350 assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
351 assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
352
353 dbBlocks, err := s.historyDB.GetAllBlocks()
354 require.NoError(t, err)
355 assert.Equal(t, 2, len(dbBlocks))
356 assert.Equal(t, int64(1), dbBlocks[1].Num)
357
358 // Sync again and expect no new blocks
359 syncBlock, discards, err = s.Sync2(ctx, nil)
360 require.NoError(t, err)
361 require.Nil(t, discards)
362 require.Nil(t, syncBlock)
363
364 //
365 // Generate blockchain and smart contract data, and fill the test smart contracts
366 //
367
368 // Generate blockchain data with til
369 set1 := `
370 Type: Blockchain
371
372 AddToken(1)
373 AddToken(2)
374 AddToken(3)
375
376 CreateAccountDeposit(1) C: 2000 // Idx=256+2=258
377 CreateAccountDeposit(2) A: 2000 // Idx=256+3=259
378 CreateAccountDeposit(1) D: 500 // Idx=256+4=260
379 CreateAccountDeposit(2) B: 500 // Idx=256+5=261
380 CreateAccountDeposit(2) C: 500 // Idx=256+6=262
381
382 CreateAccountCoordinator(1) A // Idx=256+0=256
383 CreateAccountCoordinator(1) B // Idx=256+1=257
384
385 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5}
386 > batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil}
387 > block // blockNum=2
388
389 CreateAccountDepositTransfer(1) E-A: 1000, 200 // Idx=256+7=263
390 ForceTransfer(1) C-B: 80
391 ForceExit(1) A: 100
392 ForceExit(1) B: 80
393 ForceTransfer(1) A-D: 100
394
395 Transfer(1) C-A: 100 (200)
396 Exit(1) C: 50 (200)
397 Exit(1) D: 30 (200)
398
399 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{3}
400 > batchL1 // forge L1UserTxs{3}, freeze defined L1UserTxs{nil}
401 > block // blockNum=3
402 `
403 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
404 tilCfgExtra := til.ConfigExtra{
405 BootCoordAddr: bootCoordAddr,
406 CoordUser: "A",
407 }
408 blocks, err := tc.GenerateBlocks(set1)
409 require.NoError(t, err)
410 // Sanity check
411 require.Equal(t, 2, len(blocks))
412 // blocks 0 (blockNum=2)
413 i := 0
414 require.Equal(t, 2, int(blocks[i].Block.Num))
415 require.Equal(t, 3, len(blocks[i].Rollup.AddedTokens))
416 require.Equal(t, 5, len(blocks[i].Rollup.L1UserTxs))
417 require.Equal(t, 2, len(blocks[i].Rollup.Batches))
418 require.Equal(t, 2, len(blocks[i].Rollup.Batches[0].L1CoordinatorTxs))
419 // blocks 1 (blockNum=3)
420 i = 1
421 require.Equal(t, 3, int(blocks[i].Block.Num))
422 require.Equal(t, 5, len(blocks[i].Rollup.L1UserTxs))
423 require.Equal(t, 2, len(blocks[i].Rollup.Batches))
424 require.Equal(t, 3, len(blocks[i].Rollup.Batches[0].L2Txs))
425
426 // Generate extra required data
427 ethAddTokens(blocks, client)
428
429 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
430 require.NoError(t, err)
431 tc.FillBlocksL1UserTxsBatchNum(blocks)
432 err = tc.FillBlocksForgedL1UserTxs(blocks)
433 require.NoError(t, err)
434
435 // Add block data to the smart contracts
436 err = client.CtlAddBlocks(blocks)
437 require.NoError(t, err)
438
439 //
440 // Sync to synchronize the current state from the test smart contracts,
441 // and check the outcome
442 //
443
444 // Block 2
445
446 syncBlock, discards, err = s.Sync2(ctx, nil)
447 require.NoError(t, err)
448 require.Nil(t, discards)
449 require.NotNil(t, syncBlock)
450 assert.Nil(t, syncBlock.Rollup.Vars)
451 assert.Nil(t, syncBlock.Auction.Vars)
452 assert.Nil(t, syncBlock.WDelayer.Vars)
453 assert.Equal(t, int64(2), syncBlock.Block.Num)
454 stats = s.Stats()
455 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
456 assert.Equal(t, int64(3), stats.Eth.LastBlock.Num)
457 assert.Equal(t, int64(2), stats.Sync.LastBlock.Num)
458
459 checkSyncBlock(t, s, 2, &blocks[0], syncBlock)
460
461 // Block 3
462
463 syncBlock, discards, err = s.Sync2(ctx, nil)
464 assert.NoError(t, err)
465 require.NoError(t, err)
466 require.Nil(t, discards)
467 require.NotNil(t, syncBlock)
468 assert.Nil(t, syncBlock.Rollup.Vars)
469 assert.Nil(t, syncBlock.Auction.Vars)
470 assert.Nil(t, syncBlock.WDelayer.Vars)
471 assert.Equal(t, int64(3), syncBlock.Block.Num)
472 stats = s.Stats()
473 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
474 assert.Equal(t, int64(3), stats.Eth.LastBlock.Num)
475 assert.Equal(t, int64(3), stats.Sync.LastBlock.Num)
476
477 checkSyncBlock(t, s, 3, &blocks[1], syncBlock)
478
479 // Block 4
480 // Generate 2 withdraws manually
481 _, err = client.RollupWithdrawMerkleProof(tc.Users["A"].BJJ.Public().Compress(), 1, 4, 256, big.NewInt(100), []*big.Int{}, true)
482 require.NoError(t, err)
483 _, err = client.RollupWithdrawMerkleProof(tc.Users["C"].BJJ.Public().Compress(), 1, 3, 258, big.NewInt(50), []*big.Int{}, false)
484 require.NoError(t, err)
485 client.CtlMineBlock()
486
487 syncBlock, discards, err = s.Sync2(ctx, nil)
488 require.NoError(t, err)
489 require.Nil(t, discards)
490 require.NotNil(t, syncBlock)
491 assert.Nil(t, syncBlock.Rollup.Vars)
492 assert.Nil(t, syncBlock.Auction.Vars)
493 assert.Nil(t, syncBlock.WDelayer.Vars)
494 assert.Equal(t, int64(4), syncBlock.Block.Num)
495 stats = s.Stats()
496 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
497 assert.Equal(t, int64(4), stats.Eth.LastBlock.Num)
498 assert.Equal(t, int64(4), stats.Sync.LastBlock.Num)
499 vars = s.SCVars()
500 assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
501 assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
502 assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
503
504 dbExits, err := s.historyDB.GetAllExits()
505 require.NoError(t, err)
506 foundA1, foundC1 := false, false
507 for _, exit := range dbExits {
508 if exit.AccountIdx == 256 && exit.BatchNum == 4 {
509 foundA1 = true
510 assert.Equal(t, int64(4), *exit.InstantWithdrawn)
511 }
512 if exit.AccountIdx == 258 && exit.BatchNum == 3 {
513 foundC1 = true
514 assert.Equal(t, int64(4), *exit.DelayedWithdrawRequest)
515 }
516 }
517 assert.True(t, foundA1)
518 assert.True(t, foundC1)
519
520 // Block 5
521 // Update variables manually
522 rollupVars, auctionVars, wDelayerVars, err := s.historyDB.GetSCVars()
523 require.NoError(t, err)
524 rollupVars.ForgeL1L2BatchTimeout = 42
525 _, err = client.RollupUpdateForgeL1L2BatchTimeout(rollupVars.ForgeL1L2BatchTimeout)
526 require.NoError(t, err)
527
528 auctionVars.OpenAuctionSlots = 17
529 _, err = client.AuctionSetOpenAuctionSlots(auctionVars.OpenAuctionSlots)
530 require.NoError(t, err)
531
532 wDelayerVars.WithdrawalDelay = 99
533 _, err = client.WDelayerChangeWithdrawalDelay(wDelayerVars.WithdrawalDelay)
534 require.NoError(t, err)
535
536 client.CtlMineBlock()
537
538 syncBlock, discards, err = s.Sync2(ctx, nil)
539 require.NoError(t, err)
540 require.Nil(t, discards)
541 require.NotNil(t, syncBlock)
542 assert.NotNil(t, syncBlock.Rollup.Vars)
543 assert.NotNil(t, syncBlock.Auction.Vars)
544 assert.NotNil(t, syncBlock.WDelayer.Vars)
545 assert.Equal(t, int64(5), syncBlock.Block.Num)
546 stats = s.Stats()
547 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
548 assert.Equal(t, int64(5), stats.Eth.LastBlock.Num)
549 assert.Equal(t, int64(5), stats.Sync.LastBlock.Num)
550 vars = s.SCVars()
551 assert.NotEqual(t, clientSetup.RollupVariables, vars.Rollup)
552 assert.NotEqual(t, clientSetup.AuctionVariables, vars.Auction)
553 assert.NotEqual(t, clientSetup.WDelayerVariables, vars.WDelayer)
554
555 dbRollupVars, dbAuctionVars, dbWDelayerVars, err := s.historyDB.GetSCVars()
556 require.NoError(t, err)
557 // Set EthBlockNum for Vars to the blockNum in which they were updated (should be 5)
558 rollupVars.EthBlockNum = syncBlock.Block.Num
559 auctionVars.EthBlockNum = syncBlock.Block.Num
560 wDelayerVars.EthBlockNum = syncBlock.Block.Num
561 assert.Equal(t, rollupVars, dbRollupVars)
562 assert.Equal(t, auctionVars, dbAuctionVars)
563 assert.Equal(t, wDelayerVars, dbWDelayerVars)
564
565 //
566 // Reorg test
567 //
568
569 // Redo blocks 2-5 (as a reorg) only leaving:
570 // - 2 create account transactions
571 // - 2 add tokens
572 // We add a 6th block so that the synchronizer can detect the reorg
573 set2 := `
574 Type: Blockchain
575
576 AddToken(1)
577 AddToken(2)
578
579 CreateAccountDeposit(1) C: 2000 // Idx=256+1=257
580
581 CreateAccountCoordinator(1) A // Idx=256+0=256
582
583 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{1}
584 > batchL1 // forge defined L1UserTxs{1}, freeze L1UserTxs{nil}
585 > block // blockNum=2
586 > block // blockNum=3
587 > block // blockNum=4
588 > block // blockNum=5
589 > block // blockNum=6
590 `
591 tc = til.NewContext(chainID, common.RollupConstMaxL1UserTx)
592 tilCfgExtra = til.ConfigExtra{
593 BootCoordAddr: bootCoordAddr,
594 CoordUser: "A",
595 }
596 blocks, err = tc.GenerateBlocks(set2)
597 require.NoError(t, err)
598
599 for i := 0; i < 4; i++ {
600 client.CtlRollback()
601 }
602 block := client.CtlLastBlock()
603 require.Equal(t, int64(1), block.Num)
604
605 // Generate extra required data
606 ethAddTokens(blocks, client)
607
608 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
609 require.NoError(t, err)
610 tc.FillBlocksL1UserTxsBatchNum(blocks)
611
612 // Add block data to the smart contracts
613 err = client.CtlAddBlocks(blocks)
614 require.NoError(t, err)
615
616 // First sync detects the reorg and discards 4 blocks
617 syncBlock, discards, err = s.Sync2(ctx, nil)
618 require.NoError(t, err)
619 expetedDiscards := int64(4)
620 require.Equal(t, &expetedDiscards, discards)
621 require.Nil(t, syncBlock)
622 stats = s.Stats()
623 assert.Equal(t, false, stats.Synced())
624 assert.Equal(t, int64(6), stats.Eth.LastBlock.Num)
625 vars = s.SCVars()
626 assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
627 assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
628 assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
629
630 // At this point, the DB only has data up to block 1
631 dbBlock, err := s.historyDB.GetLastBlock()
632 require.NoError(t, err)
633 assert.Equal(t, int64(1), dbBlock.Num)
634
635 // Accounts in HistoryDB and StateDB must be empty
636 dbAccounts, err := s.historyDB.GetAllAccounts()
637 require.NoError(t, err)
638 sdbAccounts, err := s.stateDB.GetAccounts()
639 require.NoError(t, err)
640 assert.Equal(t, 0, len(dbAccounts))
641 assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
642
643 // Sync blocks 2-6
644 for i := 0; i < 5; i++ {
645 syncBlock, discards, err = s.Sync2(ctx, nil)
646 require.NoError(t, err)
647 require.Nil(t, discards)
648 require.NotNil(t, syncBlock)
649 assert.Nil(t, syncBlock.Rollup.Vars)
650 assert.Nil(t, syncBlock.Auction.Vars)
651 assert.Nil(t, syncBlock.WDelayer.Vars)
652 assert.Equal(t, int64(2+i), syncBlock.Block.Num)
653
654 stats = s.Stats()
655 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
656 assert.Equal(t, int64(6), stats.Eth.LastBlock.Num)
657 assert.Equal(t, int64(2+i), stats.Sync.LastBlock.Num)
658 if i == 4 {
659 assert.Equal(t, true, stats.Synced())
660 } else {
661 assert.Equal(t, false, stats.Synced())
662 }
663
664 vars = s.SCVars()
665 assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
666 assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
667 assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
668 }
669
670 dbBlock, err = s.historyDB.GetLastBlock()
671 require.NoError(t, err)
672 assert.Equal(t, int64(6), dbBlock.Num)
673
674 // Accounts in HistoryDB and StateDB is only 2 entries
675 dbAccounts, err = s.historyDB.GetAllAccounts()
676 require.NoError(t, err)
677 sdbAccounts, err = s.stateDB.GetAccounts()
678 require.NoError(t, err)
679 assert.Equal(t, 2, len(dbAccounts))
680 assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
681}
682
683func TestSyncForgerCommitment(t *testing.T) {
684 stateDB, historyDB := newTestModules(t)
685
686 // Init eth client
687 var timer timer
688 clientSetup := test.NewClientSetupExample()
689 clientSetup.ChainID = big.NewInt(int64(chainID))
690 clientSetup.AuctionConstants.GenesisBlockNum = 2
691 clientSetup.AuctionConstants.BlocksPerSlot = 4
692 clientSetup.AuctionVariables.SlotDeadline = 2
693 bootCoordAddr := clientSetup.AuctionVariables.BootCoordinator
694 client := test.NewClient(true, &timer, ðCommon.Address{}, clientSetup)
695
696 // Create Synchronizer
697 s, err := NewSynchronizer(client, historyDB, stateDB, Config{
698 StatsRefreshPeriod: 0 * time.Second,
699 })
700 require.NoError(t, err)
701
702 ctx := context.Background()
703
704 set := `
705 Type: Blockchain
706
707 // Slot = 0
708
709 > block // 2
710 > block // 3
711 > block // 4
712 > block // 5
713
714 // Slot = 1
715
716 > block // 6
717 > batch
718 > block // 7
719 > block // 8
720 > block // 9
721
722 // Slot = 2
723
724 > block // 10
725 > block // 11
726 > batch
727 > block // 12
728 > block // 13
729
730 `
731 // For each block, true when the slot that belongs to the following
732 // block has forgerCommitment
733 commitment := map[int64]bool{
734 2: false,
735 3: false,
736 4: false,
737 5: false,
738
739 6: false,
740 7: true,
741 8: true,
742 9: false,
743
744 10: false,
745 11: false,
746 12: false,
747 13: false,
748 }
749 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
750 blocks, err := tc.GenerateBlocks(set)
751 assert.NoError(t, err)
752
753 tilCfgExtra := til.ConfigExtra{
754 BootCoordAddr: bootCoordAddr,
755 CoordUser: "A",
756 }
757 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
758 require.NoError(t, err)
759
760 // for i := range blocks {
761 // for j := range blocks[i].Rollup.Batches {
762 // blocks[i].Rollup.Batches[j].Batch.SlotNum = int64(i) / 4
763 // }
764 // }
765
766 // be in sync
767 for {
768 syncBlock, discards, err := s.Sync2(ctx, nil)
769 require.NoError(t, err)
770 require.Nil(t, discards)
771 if syncBlock == nil {
772 break
773 }
774 }
775 stats := s.Stats()
776 require.Equal(t, int64(1), stats.Sync.LastBlock.Num)
777
778 // Store ForgerComitmnent observed at every block by the live synchronizer
779 syncCommitment := map[int64]bool{}
780 // Store ForgerComitmnent observed at every block by a syncrhonizer that is restarted
781 syncRestartedCommitment := map[int64]bool{}
782 for _, block := range blocks {
783 // Add block data to the smart contracts
784 err = client.CtlAddBlocks([]common.BlockData{block})
785 require.NoError(t, err)
786
787 syncBlock, discards, err := s.Sync2(ctx, nil)
788 require.NoError(t, err)
789 require.Nil(t, discards)
790 if syncBlock == nil {
791 break
792 }
793 stats := s.Stats()
794 require.True(t, stats.Synced())
795 syncCommitment[syncBlock.Block.Num] = stats.Sync.Auction.CurrentSlot.ForgerCommitment
796
797 s2, err := NewSynchronizer(client, historyDB, stateDB, Config{
798 StatsRefreshPeriod: 0 * time.Second,
799 })
800 require.NoError(t, err)
801 stats = s2.Stats()
802 require.True(t, stats.Synced())
803 syncRestartedCommitment[syncBlock.Block.Num] = stats.Sync.Auction.CurrentSlot.ForgerCommitment
804 }
805 assert.Equal(t, commitment, syncCommitment)
806 assert.Equal(t, commitment, syncRestartedCommitment)
807}