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