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