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, 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 StoreAccountUpdates: true,
351 })
352 require.NoError(t, err)
353
354 ctx := context.Background()
355
356 //
357 // First Sync from an initial state
358 //
359 stats := s.Stats()
360 assert.Equal(t, false, stats.Synced())
361
362 // Test Sync for rollup genesis block
363 syncBlock, discards, err := s.Sync2(ctx, nil)
364 require.NoError(t, err)
365 require.Nil(t, discards)
366 require.NotNil(t, syncBlock)
367 require.Nil(t, syncBlock.Rollup.Vars)
368 require.Nil(t, syncBlock.Auction.Vars)
369 require.Nil(t, syncBlock.WDelayer.Vars)
370 assert.Equal(t, int64(1), syncBlock.Block.Num)
371 stats = s.Stats()
372 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
373 assert.Equal(t, int64(1), stats.Eth.LastBlock.Num)
374 assert.Equal(t, int64(1), stats.Sync.LastBlock.Num)
375 vars := s.SCVars()
376 assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
377 assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
378 assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
379
380 dbBlocks, err := s.historyDB.GetAllBlocks()
381 require.NoError(t, err)
382 assert.Equal(t, 2, len(dbBlocks))
383 assert.Equal(t, int64(1), dbBlocks[1].Num)
384
385 // Sync again and expect no new blocks
386 syncBlock, discards, err = s.Sync2(ctx, nil)
387 require.NoError(t, err)
388 require.Nil(t, discards)
389 require.Nil(t, syncBlock)
390
391 //
392 // Generate blockchain and smart contract data, and fill the test smart contracts
393 //
394
395 // Generate blockchain data with til
396 set1 := `
397 Type: Blockchain
398
399 AddToken(1)
400 AddToken(2)
401 AddToken(3)
402
403 CreateAccountDeposit(1) C: 2000 // Idx=256+2=258
404 CreateAccountDeposit(2) A: 2000 // Idx=256+3=259
405 CreateAccountDeposit(1) D: 500 // Idx=256+4=260
406 CreateAccountDeposit(2) B: 500 // Idx=256+5=261
407 CreateAccountDeposit(2) C: 500 // Idx=256+6=262
408
409 CreateAccountCoordinator(1) A // Idx=256+0=256
410 CreateAccountCoordinator(1) B // Idx=256+1=257
411
412 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5}
413 > batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil}
414 > block // blockNum=2
415
416 CreateAccountDepositTransfer(1) E-A: 1000, 200 // Idx=256+7=263
417 ForceTransfer(1) C-B: 80
418 ForceExit(1) A: 100
419 ForceExit(1) B: 80
420 ForceTransfer(1) A-D: 100
421
422 Transfer(1) C-A: 100 (126)
423 Exit(1) C: 50 (100)
424 Exit(1) D: 30 (100)
425
426 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{3}
427 > batchL1 // forge L1UserTxs{3}, freeze defined L1UserTxs{nil}
428 > block // blockNum=3
429 `
430 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
431 tilCfgExtra := til.ConfigExtra{
432 BootCoordAddr: bootCoordAddr,
433 CoordUser: "A",
434 }
435 blocks, err := tc.GenerateBlocks(set1)
436 require.NoError(t, err)
437 // Sanity check
438 require.Equal(t, 2, len(blocks))
439 // blocks 0 (blockNum=2)
440 i := 0
441 require.Equal(t, 2, int(blocks[i].Block.Num))
442 require.Equal(t, 3, len(blocks[i].Rollup.AddedTokens))
443 require.Equal(t, 5, len(blocks[i].Rollup.L1UserTxs))
444 require.Equal(t, 2, len(blocks[i].Rollup.Batches))
445 require.Equal(t, 2, len(blocks[i].Rollup.Batches[0].L1CoordinatorTxs))
446 // Set StateRoots for batches manually (til doesn't set it)
447 blocks[i].Rollup.Batches[0].Batch.StateRoot =
448 newBigInt("18906357591508007884273218035694076596537737437965299189312069102730480717391")
449 blocks[i].Rollup.Batches[1].Batch.StateRoot =
450 newBigInt("9513185123401321669660637227182204000277156839501731093239187625486561933297")
451 // blocks 1 (blockNum=3)
452 i = 1
453 require.Equal(t, 3, int(blocks[i].Block.Num))
454 require.Equal(t, 5, len(blocks[i].Rollup.L1UserTxs))
455 require.Equal(t, 2, len(blocks[i].Rollup.Batches))
456 require.Equal(t, 3, len(blocks[i].Rollup.Batches[0].L2Txs))
457 // Set StateRoots for batches manually (til doesn't set it)
458 blocks[i].Rollup.Batches[0].Batch.StateRoot =
459 newBigInt("13060270878200012606074130020925677466793317216609491464427188889005039616594")
460 blocks[i].Rollup.Batches[1].Batch.StateRoot =
461 newBigInt("21427104994652624302859637783375978708867165042357535792408500519060088086054")
462
463 // Generate extra required data
464 ethAddTokens(blocks, client)
465
466 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
467 require.NoError(t, err)
468 tc.FillBlocksL1UserTxsBatchNum(blocks)
469 err = tc.FillBlocksForgedL1UserTxs(blocks)
470 require.NoError(t, err)
471
472 // Add block data to the smart contracts
473 err = client.CtlAddBlocks(blocks)
474 require.NoError(t, err)
475
476 //
477 // Sync to synchronize the current state from the test smart contracts,
478 // and check the outcome
479 //
480
481 // Block 2
482
483 syncBlock, discards, err = s.Sync2(ctx, nil)
484 require.NoError(t, err)
485 require.Nil(t, discards)
486 require.NotNil(t, syncBlock)
487 assert.Nil(t, syncBlock.Rollup.Vars)
488 assert.Nil(t, syncBlock.Auction.Vars)
489 assert.Nil(t, syncBlock.WDelayer.Vars)
490 assert.Equal(t, int64(2), syncBlock.Block.Num)
491 stats = s.Stats()
492 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
493 assert.Equal(t, int64(3), stats.Eth.LastBlock.Num)
494 assert.Equal(t, int64(2), stats.Sync.LastBlock.Num)
495
496 checkSyncBlock(t, s, 2, &blocks[0], syncBlock)
497
498 // Block 3
499
500 syncBlock, discards, err = s.Sync2(ctx, nil)
501 assert.NoError(t, err)
502 require.NoError(t, err)
503 require.Nil(t, discards)
504 require.NotNil(t, syncBlock)
505 assert.Nil(t, syncBlock.Rollup.Vars)
506 assert.Nil(t, syncBlock.Auction.Vars)
507 assert.Nil(t, syncBlock.WDelayer.Vars)
508 assert.Equal(t, int64(3), syncBlock.Block.Num)
509 stats = s.Stats()
510 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
511 assert.Equal(t, int64(3), stats.Eth.LastBlock.Num)
512 assert.Equal(t, int64(3), stats.Sync.LastBlock.Num)
513
514 checkSyncBlock(t, s, 3, &blocks[1], syncBlock)
515
516 // Block 4
517 // Generate 2 withdraws manually
518 _, err = client.RollupWithdrawMerkleProof(tc.Users["A"].BJJ.Public().Compress(), 1, 4, 256, big.NewInt(100), []*big.Int{}, true)
519 require.NoError(t, err)
520 _, err = client.RollupWithdrawMerkleProof(tc.Users["C"].BJJ.Public().Compress(), 1, 3, 258, big.NewInt(50), []*big.Int{}, false)
521 require.NoError(t, err)
522 client.CtlMineBlock()
523
524 syncBlock, discards, err = s.Sync2(ctx, nil)
525 require.NoError(t, err)
526 require.Nil(t, discards)
527 require.NotNil(t, syncBlock)
528 assert.Nil(t, syncBlock.Rollup.Vars)
529 assert.Nil(t, syncBlock.Auction.Vars)
530 assert.Nil(t, syncBlock.WDelayer.Vars)
531 assert.Equal(t, int64(4), syncBlock.Block.Num)
532 stats = s.Stats()
533 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
534 assert.Equal(t, int64(4), stats.Eth.LastBlock.Num)
535 assert.Equal(t, int64(4), stats.Sync.LastBlock.Num)
536 vars = s.SCVars()
537 assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
538 assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
539 assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
540
541 dbExits, err := s.historyDB.GetAllExits()
542 require.NoError(t, err)
543 foundA1, foundC1 := false, false
544 for _, exit := range dbExits {
545 if exit.AccountIdx == 256 && exit.BatchNum == 4 {
546 foundA1 = true
547 assert.Equal(t, int64(4), *exit.InstantWithdrawn)
548 }
549 if exit.AccountIdx == 258 && exit.BatchNum == 3 {
550 foundC1 = true
551 assert.Equal(t, int64(4), *exit.DelayedWithdrawRequest)
552 }
553 }
554 assert.True(t, foundA1)
555 assert.True(t, foundC1)
556
557 // Block 5
558 // Update variables manually
559 rollupVars, auctionVars, wDelayerVars, err := s.historyDB.GetSCVars()
560 require.NoError(t, err)
561 rollupVars.ForgeL1L2BatchTimeout = 42
562 _, err = client.RollupUpdateForgeL1L2BatchTimeout(rollupVars.ForgeL1L2BatchTimeout)
563 require.NoError(t, err)
564
565 auctionVars.OpenAuctionSlots = 17
566 _, err = client.AuctionSetOpenAuctionSlots(auctionVars.OpenAuctionSlots)
567 require.NoError(t, err)
568
569 wDelayerVars.WithdrawalDelay = 99
570 _, err = client.WDelayerChangeWithdrawalDelay(wDelayerVars.WithdrawalDelay)
571 require.NoError(t, err)
572
573 client.CtlMineBlock()
574
575 syncBlock, discards, err = s.Sync2(ctx, nil)
576 require.NoError(t, err)
577 require.Nil(t, discards)
578 require.NotNil(t, syncBlock)
579 assert.NotNil(t, syncBlock.Rollup.Vars)
580 assert.NotNil(t, syncBlock.Auction.Vars)
581 assert.NotNil(t, syncBlock.WDelayer.Vars)
582 assert.Equal(t, int64(5), syncBlock.Block.Num)
583 stats = s.Stats()
584 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
585 assert.Equal(t, int64(5), stats.Eth.LastBlock.Num)
586 assert.Equal(t, int64(5), stats.Sync.LastBlock.Num)
587 vars = s.SCVars()
588 assert.NotEqual(t, clientSetup.RollupVariables, vars.Rollup)
589 assert.NotEqual(t, clientSetup.AuctionVariables, vars.Auction)
590 assert.NotEqual(t, clientSetup.WDelayerVariables, vars.WDelayer)
591
592 dbRollupVars, dbAuctionVars, dbWDelayerVars, err := s.historyDB.GetSCVars()
593 require.NoError(t, err)
594 // Set EthBlockNum for Vars to the blockNum in which they were updated (should be 5)
595 rollupVars.EthBlockNum = syncBlock.Block.Num
596 auctionVars.EthBlockNum = syncBlock.Block.Num
597 wDelayerVars.EthBlockNum = syncBlock.Block.Num
598 assert.Equal(t, rollupVars, dbRollupVars)
599 assert.Equal(t, auctionVars, dbAuctionVars)
600 assert.Equal(t, wDelayerVars, dbWDelayerVars)
601
602 //
603 // Reorg test
604 //
605
606 // Redo blocks 2-5 (as a reorg) only leaving:
607 // - 2 create account transactions
608 // - 2 add tokens
609 // We add a 6th block so that the synchronizer can detect the reorg
610 set2 := `
611 Type: Blockchain
612
613 AddToken(1)
614 AddToken(2)
615
616 CreateAccountDeposit(1) C: 2000 // Idx=256+1=257
617
618 CreateAccountCoordinator(1) A // Idx=256+0=256
619
620 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{1}
621 > batchL1 // forge defined L1UserTxs{1}, freeze L1UserTxs{nil}
622 > block // blockNum=2
623 > block // blockNum=3
624 > block // blockNum=4
625 > block // blockNum=5
626 > block // blockNum=6
627 `
628 tc = til.NewContext(chainID, common.RollupConstMaxL1UserTx)
629 tilCfgExtra = til.ConfigExtra{
630 BootCoordAddr: bootCoordAddr,
631 CoordUser: "A",
632 }
633 blocks, err = tc.GenerateBlocks(set2)
634 require.NoError(t, err)
635
636 // Set StateRoots for batches manually (til doesn't set it)
637 blocks[0].Rollup.Batches[0].Batch.StateRoot =
638 newBigInt("11218510534825843475100588932060366395781087435899915642332104464234485046683")
639 blocks[0].Rollup.Batches[1].Batch.StateRoot =
640 newBigInt("20283020730369146334077598087403837297563965802277806438205710455191646998983")
641
642 for i := 0; i < 4; i++ {
643 client.CtlRollback()
644 }
645 block := client.CtlLastBlock()
646 require.Equal(t, int64(1), block.Num)
647
648 // Generate extra required data
649 ethAddTokens(blocks, client)
650
651 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
652 require.NoError(t, err)
653 tc.FillBlocksL1UserTxsBatchNum(blocks)
654
655 // Add block data to the smart contracts
656 err = client.CtlAddBlocks(blocks)
657 require.NoError(t, err)
658
659 // First sync detects the reorg and discards 4 blocks
660 syncBlock, discards, err = s.Sync2(ctx, nil)
661 require.NoError(t, err)
662 expetedDiscards := int64(4)
663 require.Equal(t, &expetedDiscards, discards)
664 require.Nil(t, syncBlock)
665 stats = s.Stats()
666 assert.Equal(t, false, stats.Synced())
667 assert.Equal(t, int64(6), stats.Eth.LastBlock.Num)
668 vars = s.SCVars()
669 assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
670 assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
671 assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
672
673 // At this point, the DB only has data up to block 1
674 dbBlock, err := s.historyDB.GetLastBlock()
675 require.NoError(t, err)
676 assert.Equal(t, int64(1), dbBlock.Num)
677
678 // Accounts in HistoryDB and StateDB must be empty
679 dbAccounts, err := s.historyDB.GetAllAccounts()
680 require.NoError(t, err)
681 sdbAccounts, err := s.stateDB.TestGetAccounts()
682 require.NoError(t, err)
683 assert.Equal(t, 0, len(dbAccounts))
684 assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
685
686 // Sync blocks 2-6
687 for i := 0; i < 5; i++ {
688 syncBlock, discards, err = s.Sync2(ctx, nil)
689 require.NoError(t, err)
690 require.Nil(t, discards)
691 require.NotNil(t, syncBlock)
692 assert.Nil(t, syncBlock.Rollup.Vars)
693 assert.Nil(t, syncBlock.Auction.Vars)
694 assert.Nil(t, syncBlock.WDelayer.Vars)
695 assert.Equal(t, int64(2+i), syncBlock.Block.Num)
696
697 stats = s.Stats()
698 assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
699 assert.Equal(t, int64(6), stats.Eth.LastBlock.Num)
700 assert.Equal(t, int64(2+i), stats.Sync.LastBlock.Num)
701 if i == 4 {
702 assert.Equal(t, true, stats.Synced())
703 } else {
704 assert.Equal(t, false, stats.Synced())
705 }
706
707 vars = s.SCVars()
708 assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
709 assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
710 assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
711 }
712
713 dbBlock, err = s.historyDB.GetLastBlock()
714 require.NoError(t, err)
715 assert.Equal(t, int64(6), dbBlock.Num)
716
717 // Accounts in HistoryDB and StateDB is only 2 entries
718 dbAccounts, err = s.historyDB.GetAllAccounts()
719 require.NoError(t, err)
720 sdbAccounts, err = s.stateDB.TestGetAccounts()
721 require.NoError(t, err)
722 assert.Equal(t, 2, len(dbAccounts))
723 assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
724}
725
726func TestSyncForgerCommitment(t *testing.T) {
727 stateDB, historyDB := newTestModules(t)
728
729 // Init eth client
730 var timer timer
731 clientSetup := test.NewClientSetupExample()
732 clientSetup.ChainID = big.NewInt(int64(chainID))
733 clientSetup.AuctionConstants.GenesisBlockNum = 2
734 clientSetup.AuctionConstants.BlocksPerSlot = 4
735 clientSetup.AuctionVariables.SlotDeadline = 2
736 bootCoordAddr := clientSetup.AuctionVariables.BootCoordinator
737 client := test.NewClient(true, &timer, ðCommon.Address{}, clientSetup)
738
739 // Create Synchronizer
740 s, err := NewSynchronizer(client, historyDB, stateDB, Config{
741 StatsRefreshPeriod: 0 * time.Second,
742 StoreAccountUpdates: true,
743 })
744 require.NoError(t, err)
745
746 ctx := context.Background()
747
748 set := `
749 Type: Blockchain
750
751 // Slot = 0
752
753 > block // 2
754 > block // 3
755 > block // 4
756 > block // 5
757
758 // Slot = 1
759
760 > block // 6
761 > batch
762 > block // 7
763 > block // 8
764 > block // 9
765
766 // Slot = 2
767
768 > block // 10
769 > block // 11
770 > batch
771 > block // 12
772 > block // 13
773
774 `
775 // For each block, true when the slot that belongs to the following
776 // block has forgerCommitment
777 commitment := map[int64]bool{
778 2: false,
779 3: false,
780 4: false,
781 5: false,
782
783 6: false,
784 7: true,
785 8: true,
786 9: false,
787
788 10: false,
789 11: false,
790 12: false,
791 13: false,
792 }
793 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
794 blocks, err := tc.GenerateBlocks(set)
795 assert.NoError(t, err)
796
797 tilCfgExtra := til.ConfigExtra{
798 BootCoordAddr: bootCoordAddr,
799 CoordUser: "A",
800 }
801 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
802 require.NoError(t, err)
803
804 // for i := range blocks {
805 // for j := range blocks[i].Rollup.Batches {
806 // blocks[i].Rollup.Batches[j].Batch.SlotNum = int64(i) / 4
807 // }
808 // }
809
810 // be in sync
811 for {
812 syncBlock, discards, err := s.Sync2(ctx, nil)
813 require.NoError(t, err)
814 require.Nil(t, discards)
815 if syncBlock == nil {
816 break
817 }
818 }
819 stats := s.Stats()
820 require.Equal(t, int64(1), stats.Sync.LastBlock.Num)
821
822 // Store ForgerComitmnent observed at every block by the live synchronizer
823 syncCommitment := map[int64]bool{}
824 // Store ForgerComitmnent observed at every block by a syncrhonizer that is restarted
825 syncRestartedCommitment := map[int64]bool{}
826 for _, block := range blocks {
827 // Add block data to the smart contracts
828 err = client.CtlAddBlocks([]common.BlockData{block})
829 require.NoError(t, err)
830
831 syncBlock, discards, err := s.Sync2(ctx, nil)
832 require.NoError(t, err)
833 require.Nil(t, discards)
834 if syncBlock == nil {
835 break
836 }
837 stats := s.Stats()
838 require.True(t, stats.Synced())
839 syncCommitment[syncBlock.Block.Num] = stats.Sync.Auction.CurrentSlot.ForgerCommitment
840
841 s2, err := NewSynchronizer(client, historyDB, stateDB, Config{
842 StatsRefreshPeriod: 0 * time.Second,
843 StoreAccountUpdates: true,
844 })
845 require.NoError(t, err)
846 stats = s2.Stats()
847 require.True(t, stats.Synced())
848 syncRestartedCommitment[syncBlock.Block.Num] = stats.Sync.Auction.CurrentSlot.ForgerCommitment
849 }
850 assert.Equal(t, commitment, syncCommitment)
851 assert.Equal(t, commitment, syncRestartedCommitment)
852}