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