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