hermez-node/synchronizer/synchronizer_test.go
synchronizer/synchronizer_test.goRaw
1package synchronizer
2
3import (
4 "context"
5 "fmt"
6 "io/ioutil"
7 "math/big"
8 "os"
9 "testing"
10
11 ethCommon "github.com/ethereum/go-ethereum/common"
12 "github.com/hermeznetwork/hermez-node/common"
13 dbUtils "github.com/hermeznetwork/hermez-node/db"
14 "github.com/hermeznetwork/hermez-node/db/historydb"
15 "github.com/hermeznetwork/hermez-node/db/statedb"
16 "github.com/hermeznetwork/hermez-node/eth"
17 "github.com/hermeznetwork/hermez-node/test"
18 "github.com/hermeznetwork/hermez-node/test/til"
19 "github.com/jinzhu/copier"
20 "github.com/stretchr/testify/assert"
21 "github.com/stretchr/testify/require"
22)
23
24type timer struct {
25 time int64
26}
27
28func (t *timer) Time() int64 {
29 currentTime := t.time
30 t.time++
31 return currentTime
32}
33
34func TestSync(t *testing.T) {
35 //
36 // Setup
37 //
38
39 ctx := context.Background()
40 // Int State DB
41 dir, err := ioutil.TempDir("", "tmpdb")
42 require.Nil(t, err)
43 defer assert.Nil(t, os.RemoveAll(dir))
44
45 stateDB, err := statedb.NewStateDB(dir, statedb.TypeSynchronizer, 32)
46 assert.Nil(t, err)
47
48 // Init History DB
49 pass := os.Getenv("POSTGRES_PASS")
50 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
51 require.Nil(t, err)
52 historyDB := historydb.NewHistoryDB(db)
53 // Clear DB
54 err = historyDB.Reorg(-1)
55 assert.Nil(t, err)
56
57 // Init eth client
58 var timer timer
59 clientSetup := test.NewClientSetupExample()
60 bootCoordAddr := clientSetup.AuctionVariables.BootCoordinator
61 client := test.NewClient(true, &timer, ðCommon.Address{}, clientSetup)
62
63 // Create Synchronizer
64 s, err := NewSynchronizer(client, historyDB, stateDB)
65 require.Nil(t, err)
66
67 //
68 // First Sync from an initial state
69 //
70
71 // Test Sync for rollup genesis block
72 syncBlock, discards, err := s.Sync2(ctx, nil)
73 require.Nil(t, err)
74 require.Nil(t, discards)
75 require.NotNil(t, syncBlock)
76 assert.Equal(t, int64(1), syncBlock.Block.EthBlockNum)
77 dbBlocks, err := s.historyDB.GetAllBlocks()
78 require.Nil(t, err)
79 assert.Equal(t, 1, len(dbBlocks))
80 assert.Equal(t, int64(1), dbBlocks[0].EthBlockNum)
81
82 // Sync again and expect no new blocks
83 syncBlock, discards, err = s.Sync2(ctx, nil)
84 require.Nil(t, err)
85 require.Nil(t, discards)
86 require.Nil(t, syncBlock)
87
88 //
89 // Generate blockchain and smart contract data, and fill the test smart contracts
90 //
91
92 // Generate blockchain data with til
93 set1 := `
94 Type: Blockchain
95
96 AddToken(1)
97 AddToken(2)
98 AddToken(3)
99
100 CreateAccountDeposit(1) A: 20 // Idx=256+1
101 CreateAccountDeposit(2) A: 20 // Idx=256+2
102 CreateAccountDeposit(1) B: 5 // Idx=256+3
103 CreateAccountDeposit(1) C: 5 // Idx=256+4
104 CreateAccountDeposit(1) D: 5 // Idx=256+5
105
106 CreateAccountCoordinator(2) B // Idx=256+0
107
108 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs
109 > batchL1 // forge defined L1UserTxs, freeze L1UserTxs{nil}
110 > block
111
112 `
113 tc := til.NewContext(eth.RollupConstMaxL1UserTx)
114 blocks, err := tc.GenerateBlocks(set1)
115 require.Nil(t, err)
116 require.Equal(t, 1, len(blocks))
117 require.Equal(t, 3, len(blocks[0].AddedTokens))
118 require.Equal(t, 5, len(blocks[0].L1UserTxs))
119 require.Equal(t, 2, len(blocks[0].Batches))
120
121 tokenConsts := map[common.TokenID]eth.ERC20Consts{}
122 // Generate extra required data
123 for _, block := range blocks {
124 for _, token := range block.AddedTokens {
125 consts := eth.ERC20Consts{
126 Name: fmt.Sprintf("Token %d", token.TokenID),
127 Symbol: fmt.Sprintf("TK%d", token.TokenID),
128 Decimals: 18,
129 }
130 tokenConsts[token.TokenID] = consts
131 client.CtlAddERC20(token.EthAddr, consts)
132 }
133 }
134
135 // Add block data to the smart contracts
136 for _, block := range blocks {
137 for _, token := range block.AddedTokens {
138 _, err := client.RollupAddTokenSimple(token.EthAddr, clientSetup.RollupVariables.FeeAddToken)
139 require.Nil(t, err)
140 }
141 for _, tx := range block.L1UserTxs {
142 client.CtlSetAddr(tx.FromEthAddr)
143 _, err := client.RollupL1UserTxERC20ETH(tx.FromBJJ, int64(tx.FromIdx), tx.LoadAmount, tx.Amount,
144 uint32(tx.TokenID), int64(tx.ToIdx))
145 require.Nil(t, err)
146 }
147 client.CtlSetAddr(bootCoordAddr)
148 for _, batch := range block.Batches {
149 _, err := client.RollupForgeBatch(ð.RollupForgeBatchArgs{
150 NewLastIdx: batch.Batch.LastIdx,
151 NewStRoot: batch.Batch.StateRoot,
152 NewExitRoot: batch.Batch.ExitRoot,
153 L1CoordinatorTxs: batch.L1CoordinatorTxs,
154 L1CoordinatorTxsAuths: [][]byte{}, // Intentionally empty
155 L2TxsData: batch.L2Txs,
156 FeeIdxCoordinator: []common.Idx{}, // TODO
157 // Circuit selector
158 VerifierIdx: 0, // Intentionally empty
159 L1Batch: batch.L1Batch,
160 ProofA: [2]*big.Int{}, // Intentionally empty
161 ProofB: [2][2]*big.Int{}, // Intentionally empty
162 ProofC: [2]*big.Int{}, // Intentionally empty
163 })
164 require.Nil(t, err)
165 }
166 // Mine block and sync
167 client.CtlMineBlock()
168 }
169
170 //
171 // Sync to synchronize the current state from the test smart contracts
172 //
173
174 syncBlock, discards, err = s.Sync2(ctx, nil)
175 require.Nil(t, err)
176 require.Nil(t, discards)
177 require.NotNil(t, syncBlock)
178 assert.Equal(t, int64(2), syncBlock.Block.EthBlockNum)
179
180 // Fill extra fields not generated by til in til block
181 openToForge := int64(0)
182 toForgeL1TxsNum := int64(0)
183 for i := range blocks {
184 block := &blocks[i]
185 for j := range block.Batches {
186 batch := &block.Batches[j]
187 if batch.L1Batch {
188 // Set BatchNum for forged L1UserTxs to til blocks
189 bn := batch.Batch.BatchNum
190 for k := range blocks {
191 block := &blocks[k]
192 for l := range block.L1UserTxs {
193 tx := &block.L1UserTxs[l]
194 if *tx.ToForgeL1TxsNum == openToForge {
195 tx.BatchNum = &bn
196 }
197 }
198 }
199 openToForge++
200 }
201
202 batch.Batch.EthBlockNum = block.Block.EthBlockNum
203 batch.Batch.ForgerAddr = bootCoordAddr // til doesn't fill the batch forger addr
204 if batch.L1Batch {
205 toForgeL1TxsNumCpy := toForgeL1TxsNum
206 batch.Batch.ForgeL1TxsNum = &toForgeL1TxsNumCpy // til doesn't fill the ForgeL1TxsNum
207 toForgeL1TxsNum++
208 }
209
210 batchNum := batch.Batch.BatchNum
211 for j := range batch.L1CoordinatorTxs {
212 tx := &batch.L1CoordinatorTxs[j]
213 tx.BatchNum = &batchNum
214 tx.EthBlockNum = batch.Batch.EthBlockNum
215 nTx, err := common.NewL1Tx(tx)
216 require.Nil(t, err)
217 *tx = *nTx
218 }
219 }
220 }
221
222 block := blocks[0]
223
224 //
225 // Check Sync output and HistoryDB state against expected values
226 // generated by til
227 //
228
229 // Check Blocks
230 dbBlocks, err = s.historyDB.GetAllBlocks()
231 require.Nil(t, err)
232 assert.Equal(t, 2, len(dbBlocks))
233 assert.Equal(t, int64(2), dbBlocks[1].EthBlockNum)
234 assert.NotEqual(t, dbBlocks[1].Hash, dbBlocks[0].Hash)
235 assert.Greater(t, dbBlocks[1].Timestamp.Unix(), dbBlocks[0].Timestamp.Unix())
236
237 // Check Tokens
238 assert.Equal(t, len(block.AddedTokens), len(syncBlock.AddedTokens))
239 dbTokens, err := s.historyDB.GetAllTokens()
240 require.Nil(t, err)
241 assert.Equal(t, len(block.AddedTokens), len(dbTokens))
242 for i, token := range block.AddedTokens {
243 dbToken := dbTokens[i]
244 syncToken := syncBlock.AddedTokens[i]
245
246 assert.Equal(t, block.Block.EthBlockNum, syncToken.EthBlockNum)
247 assert.Equal(t, token.TokenID, syncToken.TokenID)
248 assert.Equal(t, token.EthAddr, syncToken.EthAddr)
249 tokenConst := tokenConsts[token.TokenID]
250 assert.Equal(t, tokenConst.Name, syncToken.Name)
251 assert.Equal(t, tokenConst.Symbol, syncToken.Symbol)
252 assert.Equal(t, tokenConst.Decimals, syncToken.Decimals)
253
254 var tokenCpy historydb.TokenWithUSD
255 //nolint:gosec
256 require.Nil(t, copier.Copy(&tokenCpy, &token)) // copy common.Token to historydb.TokenWithUSD
257 require.Nil(t, copier.Copy(&tokenCpy, &tokenConst)) // copy common.Token to historydb.TokenWithUSD
258 tokenCpy.ItemID = dbToken.ItemID // we don't care about ItemID
259 assert.Equal(t, tokenCpy, dbToken)
260 }
261
262 // Check L1UserTxs
263 assert.Equal(t, len(block.L1UserTxs), len(syncBlock.L1UserTxs))
264 dbL1UserTxs, err := s.historyDB.GetAllL1UserTxs()
265 require.Nil(t, err)
266 assert.Equal(t, len(block.L1UserTxs), len(dbL1UserTxs))
267 // Ignore BatchNum in syncBlock.L1UserTxs because this value is set by the HistoryDB
268 for i := range syncBlock.L1UserTxs {
269 syncBlock.L1UserTxs[i].BatchNum = block.L1UserTxs[i].BatchNum
270 }
271 assert.Equal(t, block.L1UserTxs, syncBlock.L1UserTxs)
272 assert.Equal(t, block.L1UserTxs, dbL1UserTxs)
273
274 // Check Batches
275 assert.Equal(t, len(block.Batches), len(syncBlock.Batches))
276 dbBatches, err := s.historyDB.GetAllBatches()
277 require.Nil(t, err)
278 assert.Equal(t, len(block.Batches), len(dbBatches))
279
280 for i, batch := range block.Batches {
281 batchNum := batch.Batch.BatchNum
282 dbBatch := dbBatches[i]
283 syncBatch := syncBlock.Batches[i]
284
285 // We don't care about TotalFeesUSD. Use the syncBatch that
286 // has a TotalFeesUSD inserted by the HistoryDB
287 batch.Batch.TotalFeesUSD = syncBatch.Batch.TotalFeesUSD
288 batch.CreatedAccounts = syncBatch.CreatedAccounts // til doesn't output CreatedAccounts
289
290 // fmt.Printf("DBG Batch %d %+v\n", i, batch)
291 // fmt.Printf("DBG Batch Sync %d %+v\n", i, syncBatch)
292 // assert.Equal(t, batch.L1CoordinatorTxs, syncBatch.L1CoordinatorTxs)
293 fmt.Printf("DBG BatchNum: %d, LastIdx: %d\n", batchNum, batch.Batch.LastIdx)
294 assert.Equal(t, batch, syncBatch)
295 assert.Equal(t, batch.Batch, dbBatch)
296 }
297
298 // Check L1UserTxs in DB
299
300 // TODO: Reorg will be properly tested once we have the mock ethClient implemented
301 /*
302 // Force a Reorg
303 lastSavedBlock, err := historyDB.GetLastBlock()
304 require.Nil(t, err)
305
306 lastSavedBlock.EthBlockNum++
307 err = historyDB.AddBlock(lastSavedBlock)
308 require.Nil(t, err)
309
310 lastSavedBlock.EthBlockNum++
311 err = historyDB.AddBlock(lastSavedBlock)
312 require.Nil(t, err)
313
314 log.Debugf("Wait for the blockchain to generate some blocks...")
315 time.Sleep(40 * time.Second)
316
317
318 err = s.Sync()
319 require.Nil(t, err)
320 */
321}