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