hermez-node/txprocessor/txprocessor_test.go
txprocessor/txprocessor_test.goRaw
1package txprocessor
2
3import (
4 "io/ioutil"
5 "math/big"
6 "os"
7 "sort"
8 "testing"
9
10 ethCommon "github.com/ethereum/go-ethereum/common"
11 "github.com/hermeznetwork/hermez-node/common"
12 "github.com/hermeznetwork/hermez-node/db/statedb"
13 "github.com/hermeznetwork/hermez-node/log"
14 "github.com/hermeznetwork/hermez-node/test/til"
15 "github.com/hermeznetwork/hermez-node/test/txsets"
16 "github.com/stretchr/testify/assert"
17 "github.com/stretchr/testify/require"
18)
19
20func checkBalance(t *testing.T, tc *til.Context, sdb *statedb.StateDB, username string, tokenid int, expected string) {
21 idx := tc.Users[username].Accounts[common.TokenID(tokenid)].Idx
22 acc, err := sdb.GetAccount(idx)
23 require.NoError(t, err)
24 assert.Equal(t, expected, acc.Balance.String())
25}
26
27func checkBalanceByIdx(t *testing.T, sdb *statedb.StateDB, idx common.Idx, expected string) {
28 acc, err := sdb.GetAccount(idx)
29 require.NoError(t, err)
30 assert.Equal(t, expected, acc.Balance.String())
31}
32
33func TestComputeEffectiveAmounts(t *testing.T) {
34 dir, err := ioutil.TempDir("", "tmpdb")
35 require.NoError(t, err)
36 defer assert.NoError(t, os.RemoveAll(dir))
37
38 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
39 Type: statedb.TypeSynchronizer, NLevels: 32})
40 assert.NoError(t, err)
41
42 set := `
43 Type: Blockchain
44 AddToken(1)
45
46 CreateAccountDeposit(0) A: 10
47 CreateAccountDeposit(0) B: 10
48 CreateAccountDeposit(1) C: 10
49 > batchL1
50 > batchL1
51 > block
52 `
53 chainID := uint16(0)
54 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
55 blocks, err := tc.GenerateBlocks(set)
56 require.NoError(t, err)
57
58 config := Config{
59 NLevels: 32,
60 MaxFeeTx: 64,
61 MaxTx: 512,
62 MaxL1Tx: 16,
63 ChainID: chainID,
64 }
65 tp := NewTxProcessor(sdb, config)
66 _, err = tp.ProcessTxs(nil, blocks[0].Rollup.L1UserTxs, nil, nil)
67 require.NoError(t, err)
68
69 tx := common.L1Tx{
70 FromIdx: 256,
71 ToIdx: 257,
72 Amount: big.NewInt(10),
73 DepositAmount: big.NewInt(0),
74 FromEthAddr: tc.Users["A"].Addr,
75 UserOrigin: true,
76 }
77 tp.computeEffectiveAmounts(&tx)
78 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
79 assert.Equal(t, big.NewInt(10), tx.EffectiveAmount)
80
81 // expect error due not enough funds
82 tx = common.L1Tx{
83 FromIdx: 256,
84 ToIdx: 257,
85 Amount: big.NewInt(11),
86 DepositAmount: big.NewInt(0),
87 FromEthAddr: tc.Users["A"].Addr,
88 UserOrigin: true,
89 }
90 tp.computeEffectiveAmounts(&tx)
91 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
92 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
93
94 // expect no-error as there are enough funds in a
95 // CreateAccountDepositTransfer transction
96 tx = common.L1Tx{
97 FromIdx: 0,
98 ToIdx: 257,
99 Amount: big.NewInt(10),
100 DepositAmount: big.NewInt(10),
101 UserOrigin: true,
102 }
103 tp.computeEffectiveAmounts(&tx)
104 assert.Equal(t, big.NewInt(10), tx.EffectiveDepositAmount)
105 assert.Equal(t, big.NewInt(10), tx.EffectiveAmount)
106
107 // expect error due not enough funds in a CreateAccountDepositTransfer
108 // transction
109 tx = common.L1Tx{
110 FromIdx: 0,
111 ToIdx: 257,
112 Amount: big.NewInt(11),
113 DepositAmount: big.NewInt(10),
114 UserOrigin: true,
115 }
116 tp.computeEffectiveAmounts(&tx)
117 assert.Equal(t, big.NewInt(10), tx.EffectiveDepositAmount)
118 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
119
120 // expect error due not same TokenID
121 tx = common.L1Tx{
122 FromIdx: 256,
123 ToIdx: 258,
124 Amount: big.NewInt(5),
125 DepositAmount: big.NewInt(0),
126 FromEthAddr: tc.Users["A"].Addr,
127 UserOrigin: true,
128 }
129 tp.computeEffectiveAmounts(&tx)
130 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
131 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
132
133 // expect error due not same EthAddr
134 tx = common.L1Tx{
135 FromIdx: 256,
136 ToIdx: 257,
137 Amount: big.NewInt(8),
138 DepositAmount: big.NewInt(0),
139 FromEthAddr: tc.Users["B"].Addr,
140 UserOrigin: true,
141 }
142 tp.computeEffectiveAmounts(&tx)
143 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
144 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
145
146 // expect on TxTypeDepositTransfer EffectiveAmount=0, but
147 // EffectiveDepositAmount!=0, due not enough funds to make the transfer
148 tx = common.L1Tx{
149 FromIdx: 256,
150 ToIdx: 257,
151 Amount: big.NewInt(20),
152 DepositAmount: big.NewInt(8),
153 FromEthAddr: tc.Users["A"].Addr,
154 UserOrigin: true,
155 }
156 tp.computeEffectiveAmounts(&tx)
157 assert.Equal(t, big.NewInt(8), tx.EffectiveDepositAmount)
158 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
159
160 // expect on TxTypeDepositTransfer EffectiveAmount=0, but
161 // EffectiveDepositAmount!=0, due different EthAddr from FromIdx
162 // address
163 tx = common.L1Tx{
164 FromIdx: 256,
165 ToIdx: 257,
166 Amount: big.NewInt(8),
167 DepositAmount: big.NewInt(8),
168 FromEthAddr: tc.Users["B"].Addr,
169 UserOrigin: true,
170 }
171 tp.computeEffectiveAmounts(&tx)
172 assert.Equal(t, big.NewInt(8), tx.EffectiveDepositAmount)
173 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
174
175 // CreateAccountDepositTransfer for TokenID=1 when receiver does not
176 // have an account for that TokenID, expect that the
177 // EffectiveDepositAmount=DepositAmount, but EffectiveAmount==0
178 tx = common.L1Tx{
179 FromIdx: 0,
180 ToIdx: 257,
181 Amount: big.NewInt(8),
182 DepositAmount: big.NewInt(8),
183 FromEthAddr: tc.Users["A"].Addr,
184 TokenID: 2,
185 UserOrigin: true,
186 Type: common.TxTypeCreateAccountDepositTransfer,
187 }
188 tp.computeEffectiveAmounts(&tx)
189 assert.Equal(t, big.NewInt(8), tx.EffectiveDepositAmount)
190 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
191
192 // DepositTransfer for TokenID=1 when receiver does not have an account
193 // for that TokenID, expect that the
194 // EffectiveDepositAmount=DepositAmount, but EffectiveAmount=0
195 tx = common.L1Tx{
196 FromIdx: 258,
197 ToIdx: 256,
198 Amount: big.NewInt(8),
199 DepositAmount: big.NewInt(8),
200 FromEthAddr: tc.Users["C"].Addr,
201 TokenID: 1,
202 UserOrigin: true,
203 Type: common.TxTypeDepositTransfer,
204 }
205 tp.computeEffectiveAmounts(&tx)
206 assert.Equal(t, big.NewInt(8), tx.EffectiveDepositAmount)
207 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
208}
209
210func TestProcessTxsBalances(t *testing.T) {
211 dir, err := ioutil.TempDir("", "tmpdb")
212 require.NoError(t, err)
213 defer assert.NoError(t, os.RemoveAll(dir))
214
215 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
216 Type: statedb.TypeSynchronizer, NLevels: 32})
217 assert.NoError(t, err)
218
219 chainID := uint16(0)
220 // generate test transactions from test.SetBlockchain0 code
221 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
222 blocks, err := tc.GenerateBlocks(txsets.SetBlockchainMinimumFlow0)
223 require.NoError(t, err)
224
225 config := Config{
226 NLevels: 32,
227 MaxFeeTx: 64,
228 MaxTx: 512,
229 MaxL1Tx: 16,
230 ChainID: chainID,
231 }
232 tp := NewTxProcessor(sdb, config)
233
234 log.Debug("block:0 batch:1, only L1CoordinatorTxs")
235 _, err = tp.ProcessTxs(nil, nil, blocks[0].Rollup.Batches[0].L1CoordinatorTxs, nil)
236 require.NoError(t, err)
237 assert.Equal(t, "0", tp.s.MT.Root().BigInt().String())
238
239 log.Debug("block:0 batch:2")
240 l1UserTxs := []common.L1Tx{}
241 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
242 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
243 require.NoError(t, err)
244 assert.Equal(t, "0", tp.s.MT.Root().BigInt().String())
245
246 log.Debug("block:0 batch:3")
247 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
248 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[2].L2Txs)
249 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[2].L1CoordinatorTxs, l2Txs)
250 require.NoError(t, err)
251 checkBalance(t, tc, sdb, "A", 0, "500")
252 assert.Equal(t, "13644148972047617726265275926674266298636745191961029124811988256139761111521", tp.s.MT.Root().BigInt().String())
253
254 log.Debug("block:0 batch:4")
255 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[3].Batch.ForgeL1TxsNum])
256 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[3].L2Txs)
257 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[3].L1CoordinatorTxs, l2Txs)
258 require.NoError(t, err)
259 checkBalance(t, tc, sdb, "A", 0, "500")
260 checkBalance(t, tc, sdb, "A", 1, "500")
261 assert.Equal(t, "12433441613247342495680642890662773367605896324555599297255745922589338651261", tp.s.MT.Root().BigInt().String())
262
263 log.Debug("block:0 batch:5")
264 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[4].Batch.ForgeL1TxsNum])
265 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[4].L2Txs)
266 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[4].L1CoordinatorTxs, l2Txs)
267 require.NoError(t, err)
268 checkBalance(t, tc, sdb, "A", 0, "500")
269 checkBalance(t, tc, sdb, "A", 1, "500")
270 assert.Equal(t, "12433441613247342495680642890662773367605896324555599297255745922589338651261", tp.s.MT.Root().BigInt().String())
271
272 log.Debug("block:0 batch:6")
273 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[5].Batch.ForgeL1TxsNum])
274 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[5].L2Txs)
275 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[5].L1CoordinatorTxs, l2Txs)
276 require.NoError(t, err)
277 checkBalance(t, tc, sdb, "A", 0, "600")
278 checkBalance(t, tc, sdb, "A", 1, "500")
279 checkBalance(t, tc, sdb, "B", 0, "400")
280 assert.Equal(t, "4191361650490017591061467288209836928064232431729236465872209988325272262963", tp.s.MT.Root().BigInt().String())
281
282 coordIdxs := []common.Idx{261, 262}
283 log.Debug("block:0 batch:7")
284 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[6].Batch.ForgeL1TxsNum])
285 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[6].L2Txs)
286 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, blocks[0].Rollup.Batches[6].L1CoordinatorTxs, l2Txs)
287 require.NoError(t, err)
288 checkBalance(t, tc, sdb, "Coord", 0, "10")
289 checkBalance(t, tc, sdb, "Coord", 1, "20")
290 checkBalance(t, tc, sdb, "A", 0, "600")
291 checkBalance(t, tc, sdb, "A", 1, "280")
292 checkBalance(t, tc, sdb, "B", 0, "290")
293 checkBalance(t, tc, sdb, "B", 1, "200")
294 checkBalance(t, tc, sdb, "C", 0, "100")
295 checkBalance(t, tc, sdb, "D", 0, "800")
296 assert.Equal(t, "7614010373759339299470010949167613050707822522530721724565424494781010548240", tp.s.MT.Root().BigInt().String())
297
298 log.Debug("block:0 batch:8")
299 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[7].Batch.ForgeL1TxsNum])
300 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[7].L2Txs)
301 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, blocks[0].Rollup.Batches[7].L1CoordinatorTxs, l2Txs)
302 require.NoError(t, err)
303 checkBalance(t, tc, sdb, "Coord", 0, "35")
304 checkBalance(t, tc, sdb, "Coord", 1, "30")
305 checkBalance(t, tc, sdb, "A", 0, "430")
306 checkBalance(t, tc, sdb, "A", 1, "280")
307 checkBalance(t, tc, sdb, "B", 0, "390")
308 checkBalance(t, tc, sdb, "B", 1, "90")
309 checkBalance(t, tc, sdb, "C", 0, "45")
310 checkBalance(t, tc, sdb, "C", 1, "100")
311 checkBalance(t, tc, sdb, "D", 0, "800")
312 assert.Equal(t, "21231789250434471575486264439945776732824482207853465397552873521865656677689", tp.s.MT.Root().BigInt().String())
313
314 coordIdxs = []common.Idx{262}
315 log.Debug("block:1 batch:1")
316 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[0].Batch.ForgeL1TxsNum])
317 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[0].L2Txs)
318 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, blocks[1].Rollup.Batches[0].L1CoordinatorTxs, l2Txs)
319 require.NoError(t, err)
320 checkBalance(t, tc, sdb, "Coord", 0, "75")
321 checkBalance(t, tc, sdb, "Coord", 1, "30")
322 checkBalance(t, tc, sdb, "A", 0, "730")
323 checkBalance(t, tc, sdb, "A", 1, "280")
324 checkBalance(t, tc, sdb, "B", 0, "380")
325 checkBalance(t, tc, sdb, "B", 1, "90")
326 checkBalance(t, tc, sdb, "C", 0, "845")
327 checkBalance(t, tc, sdb, "C", 1, "100")
328 checkBalance(t, tc, sdb, "D", 0, "470")
329 assert.Equal(t, "11289313644810782435120113035387729451095637380468777086895109386127538554246", tp.s.MT.Root().BigInt().String())
330
331 coordIdxs = []common.Idx{}
332 log.Debug("block:1 batch:2")
333 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[1].Batch.ForgeL1TxsNum])
334 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[1].L2Txs)
335 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, blocks[1].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
336 require.NoError(t, err)
337 assert.Equal(t, "10342681351319338354912862547249967104198317571995055517008223832276478908482", tp.s.MT.Root().BigInt().String())
338
339 // use Set of PoolL2 txs
340 poolL2Txs, err := tc.GeneratePoolL2Txs(txsets.SetPoolL2MinimumFlow1)
341 assert.NoError(t, err)
342
343 _, err = tp.ProcessTxs(coordIdxs, []common.L1Tx{}, []common.L1Tx{}, poolL2Txs)
344 require.NoError(t, err)
345 checkBalance(t, tc, sdb, "Coord", 0, "75")
346 checkBalance(t, tc, sdb, "Coord", 1, "30")
347 checkBalance(t, tc, sdb, "A", 0, "510")
348 checkBalance(t, tc, sdb, "A", 1, "170")
349 checkBalance(t, tc, sdb, "B", 0, "480")
350 checkBalance(t, tc, sdb, "B", 1, "190")
351 checkBalance(t, tc, sdb, "C", 0, "845")
352 checkBalance(t, tc, sdb, "C", 1, "100")
353 checkBalance(t, tc, sdb, "D", 0, "360")
354 checkBalance(t, tc, sdb, "F", 0, "100")
355}
356
357func TestProcessTxsSynchronizer(t *testing.T) {
358 dir, err := ioutil.TempDir("", "tmpdb")
359 require.NoError(t, err)
360 defer assert.NoError(t, os.RemoveAll(dir))
361
362 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
363 Type: statedb.TypeSynchronizer, NLevels: 32})
364 assert.NoError(t, err)
365
366 chainID := uint16(0)
367 // generate test transactions from test.SetBlockchain0 code
368 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
369 blocks, err := tc.GenerateBlocks(txsets.SetBlockchain0)
370 require.NoError(t, err)
371
372 assert.Equal(t, 31, len(blocks[0].Rollup.L1UserTxs))
373 assert.Equal(t, 4, len(blocks[0].Rollup.Batches[0].L1CoordinatorTxs))
374 assert.Equal(t, 0, len(blocks[0].Rollup.Batches[1].L1CoordinatorTxs))
375 assert.Equal(t, 22, len(blocks[0].Rollup.Batches[2].L2Txs))
376 assert.Equal(t, 1, len(blocks[1].Rollup.Batches[0].L1CoordinatorTxs))
377 assert.Equal(t, 62, len(blocks[1].Rollup.Batches[0].L2Txs))
378 assert.Equal(t, 1, len(blocks[1].Rollup.Batches[1].L1CoordinatorTxs))
379 assert.Equal(t, 8, len(blocks[1].Rollup.Batches[1].L2Txs))
380
381 // Coordinator Idx where to send the fees
382 coordIdxs := []common.Idx{256, 257, 258, 259}
383
384 // Idx of user 'A'
385 idxA1 := tc.Users["A"].Accounts[common.TokenID(1)].Idx
386
387 config := Config{
388 NLevels: 32,
389 MaxFeeTx: 64,
390 MaxTx: 512,
391 MaxL1Tx: 32,
392 ChainID: chainID,
393 }
394 tp := NewTxProcessor(sdb, config)
395
396 // Process the 1st batch, which contains the L1CoordinatorTxs necessary
397 // to create the Coordinator accounts to receive the fees
398 log.Debug("block:0 batch:1, only L1CoordinatorTxs")
399 ptOut, err := tp.ProcessTxs(nil, nil, blocks[0].Rollup.Batches[0].L1CoordinatorTxs, nil)
400 require.NoError(t, err)
401 assert.Equal(t, 4, len(ptOut.CreatedAccounts))
402 assert.Equal(t, 0, len(ptOut.CollectedFees))
403
404 log.Debug("block:0 batch:2")
405 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
406 ptOut, err = tp.ProcessTxs(coordIdxs, blocks[0].Rollup.L1UserTxs,
407 blocks[0].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
408 require.NoError(t, err)
409 assert.Equal(t, 0, len(ptOut.ExitInfos))
410 assert.Equal(t, 31, len(ptOut.CreatedAccounts))
411 assert.Equal(t, 4, len(ptOut.CollectedFees))
412 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(0)].String())
413 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(1)].String())
414 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(2)].String())
415 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(3)].String())
416 acc, err := sdb.GetAccount(idxA1)
417 require.NoError(t, err)
418 assert.Equal(t, "50", acc.Balance.String())
419
420 log.Debug("block:0 batch:3")
421 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[2].L2Txs)
422 ptOut, err = tp.ProcessTxs(coordIdxs, nil, blocks[0].Rollup.Batches[2].L1CoordinatorTxs, l2Txs)
423 require.NoError(t, err)
424 assert.Equal(t, 0, len(ptOut.ExitInfos))
425 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
426 assert.Equal(t, 4, len(ptOut.CollectedFees))
427 assert.Equal(t, "2", ptOut.CollectedFees[common.TokenID(0)].String())
428 assert.Equal(t, "1", ptOut.CollectedFees[common.TokenID(1)].String())
429 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(2)].String())
430 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(3)].String())
431 acc, err = sdb.GetAccount(idxA1)
432 require.NoError(t, err)
433 assert.Equal(t, "35", acc.Balance.String())
434
435 log.Debug("block:1 batch:1")
436 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[0].L2Txs)
437 // before processing expect l2Txs[0:2].Nonce==0
438 assert.Equal(t, common.Nonce(0), l2Txs[0].Nonce)
439 assert.Equal(t, common.Nonce(0), l2Txs[1].Nonce)
440 assert.Equal(t, common.Nonce(0), l2Txs[2].Nonce)
441
442 ptOut, err = tp.ProcessTxs(coordIdxs, nil, blocks[1].Rollup.Batches[0].L1CoordinatorTxs, l2Txs)
443 require.NoError(t, err)
444
445 // after processing expect l2Txs[0:2].Nonce!=0 and has expected value
446 assert.Equal(t, common.Nonce(5), l2Txs[0].Nonce)
447 assert.Equal(t, common.Nonce(6), l2Txs[1].Nonce)
448 assert.Equal(t, common.Nonce(7), l2Txs[2].Nonce)
449
450 assert.Equal(t, 4, len(ptOut.ExitInfos)) // the 'ForceExit(1)' is not computed yet, as the batch is without L1UserTxs
451 assert.Equal(t, 1, len(ptOut.CreatedAccounts))
452 assert.Equal(t, 4, len(ptOut.CollectedFees))
453 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(0)].String())
454 assert.Equal(t, "1", ptOut.CollectedFees[common.TokenID(1)].String())
455 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(2)].String())
456 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(3)].String())
457 acc, err = sdb.GetAccount(idxA1)
458 require.NoError(t, err)
459 assert.Equal(t, "57", acc.Balance.String())
460
461 log.Debug("block:1 batch:2")
462 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[1].L2Txs)
463 ptOut, err = tp.ProcessTxs(coordIdxs, blocks[1].Rollup.L1UserTxs,
464 blocks[1].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
465 require.NoError(t, err)
466
467 assert.Equal(t, 1, len(ptOut.ExitInfos)) // 1, as previous batch was without L1UserTxs, and has pending the 'ForceExit(1) A: 5', and the 2 exit transactions get grouped under 1 ExitInfo
468 assert.Equal(t, 1, len(ptOut.CreatedAccounts))
469 assert.Equal(t, 4, len(ptOut.CollectedFees))
470 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(0)].String())
471 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(1)].String())
472 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(2)].String())
473 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(3)].String())
474 acc, err = sdb.GetAccount(idxA1)
475 assert.NoError(t, err)
476 assert.Equal(t, "77", acc.Balance.String())
477
478 idxB0 := tc.Users["C"].Accounts[common.TokenID(0)].Idx
479 acc, err = sdb.GetAccount(idxB0)
480 require.NoError(t, err)
481 assert.Equal(t, "51", acc.Balance.String())
482
483 // get balance of Coordinator account for TokenID==0
484 acc, err = sdb.GetAccount(common.Idx(256))
485 require.NoError(t, err)
486 assert.Equal(t, "2", acc.Balance.String())
487}
488
489func TestProcessTxsBatchBuilder(t *testing.T) {
490 dir, err := ioutil.TempDir("", "tmpdb")
491 require.NoError(t, err)
492 defer assert.NoError(t, os.RemoveAll(dir))
493
494 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
495 Type: statedb.TypeBatchBuilder, NLevels: 32})
496 assert.NoError(t, err)
497
498 chainID := uint16(0)
499 // generate test transactions from test.SetBlockchain0 code
500 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
501 blocks, err := tc.GenerateBlocks(txsets.SetBlockchain0)
502 require.NoError(t, err)
503
504 // Coordinator Idx where to send the fees
505 coordIdxs := []common.Idx{256, 257, 258, 259}
506
507 // Idx of user 'A'
508 idxA1 := tc.Users["A"].Accounts[common.TokenID(1)].Idx
509
510 config := Config{
511 NLevels: 32,
512 MaxFeeTx: 64,
513 MaxTx: 512,
514 MaxL1Tx: 32,
515 ChainID: chainID,
516 }
517 tp := NewTxProcessor(sdb, config)
518
519 // Process the 1st batch, which contains the L1CoordinatorTxs necessary
520 // to create the Coordinator accounts to receive the fees
521 log.Debug("block:0 batch:1, only L1CoordinatorTxs")
522 ptOut, err := tp.ProcessTxs(nil, nil, blocks[0].Rollup.Batches[0].L1CoordinatorTxs, nil)
523 require.NoError(t, err)
524 // expect 0 at CreatedAccount, as is only computed when StateDB.Type==TypeSynchronizer
525 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
526
527 log.Debug("block:0 batch:2")
528 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
529 ptOut, err = tp.ProcessTxs(coordIdxs, blocks[0].Rollup.L1UserTxs, blocks[0].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
530 require.NoError(t, err)
531 assert.Equal(t, 0, len(ptOut.ExitInfos))
532 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
533 acc, err := sdb.GetAccount(idxA1)
534 require.NoError(t, err)
535 assert.Equal(t, "50", acc.Balance.String())
536
537 log.Debug("block:0 batch:3")
538 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[2].L2Txs)
539 ptOut, err = tp.ProcessTxs(coordIdxs, nil, blocks[0].Rollup.Batches[2].L1CoordinatorTxs, l2Txs)
540 require.NoError(t, err)
541 assert.Equal(t, 0, len(ptOut.ExitInfos))
542 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
543 acc, err = sdb.GetAccount(idxA1)
544 require.NoError(t, err)
545 assert.Equal(t, "35", acc.Balance.String())
546
547 log.Debug("block:1 batch:1")
548 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[0].L2Txs)
549 _, err = tp.ProcessTxs(coordIdxs, nil, blocks[1].Rollup.Batches[0].L1CoordinatorTxs, l2Txs)
550 require.NoError(t, err)
551 acc, err = sdb.GetAccount(idxA1)
552 require.NoError(t, err)
553 assert.Equal(t, "57", acc.Balance.String())
554
555 log.Debug("block:1 batch:2")
556 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[1].L2Txs)
557 _, err = tp.ProcessTxs(coordIdxs, blocks[1].Rollup.L1UserTxs, blocks[1].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
558 require.NoError(t, err)
559 acc, err = sdb.GetAccount(idxA1)
560 assert.NoError(t, err)
561 assert.Equal(t, "77", acc.Balance.String())
562
563 idxB0 := tc.Users["C"].Accounts[common.TokenID(0)].Idx
564 acc, err = sdb.GetAccount(idxB0)
565 require.NoError(t, err)
566 assert.Equal(t, "51", acc.Balance.String())
567
568 // get balance of Coordinator account for TokenID==0
569 acc, err = sdb.GetAccount(common.Idx(256))
570 require.NoError(t, err)
571 assert.Equal(t, common.TokenID(0), acc.TokenID)
572 assert.Equal(t, "2", acc.Balance.String())
573 acc, err = sdb.GetAccount(common.Idx(257))
574 require.NoError(t, err)
575 assert.Equal(t, common.TokenID(1), acc.TokenID)
576 assert.Equal(t, "2", acc.Balance.String())
577
578 assert.Equal(t, "18894163991492573893706613133132363559300580460789469708968288074813925659539", sdb.MT.Root().BigInt().String())
579}
580
581func TestProcessTxsRootTestVectors(t *testing.T) {
582 dir, err := ioutil.TempDir("", "tmpdb")
583 require.NoError(t, err)
584 defer assert.NoError(t, os.RemoveAll(dir))
585
586 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
587 Type: statedb.TypeBatchBuilder, NLevels: 32})
588 assert.NoError(t, err)
589
590 // same values than in the js test
591 bjj0, err := common.BJJFromStringWithChecksum("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7")
592 assert.NoError(t, err)
593 l1Txs := []common.L1Tx{
594 {
595 FromIdx: 0,
596 DepositAmount: big.NewInt(16000000),
597 Amount: big.NewInt(0),
598 TokenID: 1,
599 FromBJJ: bjj0,
600 FromEthAddr: ethCommon.HexToAddress("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf"),
601 ToIdx: 0,
602 Type: common.TxTypeCreateAccountDeposit,
603 UserOrigin: true,
604 },
605 }
606 l2Txs := []common.PoolL2Tx{
607 {
608 FromIdx: 256,
609 ToIdx: 256,
610 TokenID: 1,
611 Amount: big.NewInt(1000),
612 Nonce: 0,
613 Fee: 126,
614 Type: common.TxTypeTransfer,
615 },
616 }
617
618 chainID := uint16(0)
619 config := Config{
620 NLevels: 32,
621 MaxFeeTx: 8,
622 MaxTx: 32,
623 MaxL1Tx: 16,
624 ChainID: chainID,
625 }
626 tp := NewTxProcessor(sdb, config)
627 _, err = tp.ProcessTxs(nil, l1Txs, nil, l2Txs)
628 require.NoError(t, err)
629 assert.Equal(t, "9827704113668630072730115158977131501210702363656902211840117643154933433410", sdb.MT.Root().BigInt().String())
630}
631
632func TestCreateAccountDepositMaxValue(t *testing.T) {
633 dir, err := ioutil.TempDir("", "tmpdb")
634 require.NoError(t, err)
635 defer assert.NoError(t, os.RemoveAll(dir))
636
637 nLevels := 16
638 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
639 Type: statedb.TypeBatchBuilder, NLevels: nLevels})
640 assert.NoError(t, err)
641
642 users := txsets.GenerateJsUsers(t)
643
644 daMaxF40 := common.Float40(0xFFFFFFFFFF)
645 daMaxBI, err := daMaxF40.BigInt()
646 require.NoError(t, err)
647 assert.Equal(t, "343597383670000000000000000000000000000000", daMaxBI.String())
648
649 daMax1F40 := common.Float40(0xFFFFFFFFFE)
650 require.NoError(t, err)
651 daMax1BI, err := daMax1F40.BigInt()
652 require.NoError(t, err)
653 assert.Equal(t, "343597383660000000000000000000000000000000", daMax1BI.String())
654
655 l1Txs := []common.L1Tx{
656 {
657 FromIdx: 0,
658 DepositAmount: daMaxBI,
659 Amount: big.NewInt(0),
660 TokenID: 1,
661 FromBJJ: users[0].BJJ.Public().Compress(),
662 FromEthAddr: users[0].Addr,
663 ToIdx: 0,
664 Type: common.TxTypeCreateAccountDeposit,
665 UserOrigin: true,
666 },
667 {
668 FromIdx: 0,
669 DepositAmount: daMax1BI,
670 Amount: big.NewInt(0),
671 TokenID: 1,
672 FromBJJ: users[1].BJJ.Public().Compress(),
673 FromEthAddr: users[1].Addr,
674 ToIdx: 0,
675 Type: common.TxTypeCreateAccountDeposit,
676 UserOrigin: true,
677 },
678 }
679
680 chainID := uint16(0)
681 config := Config{
682 NLevels: uint32(nLevels),
683 MaxTx: 3,
684 MaxL1Tx: 2,
685 MaxFeeTx: 2,
686 ChainID: chainID,
687 }
688 tp := NewTxProcessor(sdb, config)
689
690 _, err = tp.ProcessTxs(nil, l1Txs, nil, nil)
691 require.NoError(t, err)
692
693 // check balances
694 acc, err := sdb.GetAccount(common.Idx(256))
695 require.NoError(t, err)
696 assert.Equal(t, daMaxBI, acc.Balance)
697 acc, err = sdb.GetAccount(common.Idx(257))
698 require.NoError(t, err)
699 assert.Equal(t, daMax1BI, acc.Balance)
700}
701
702func initTestMultipleCoordIdxForTokenID(t *testing.T) (*TxProcessor, *til.Context, []common.BlockData) {
703 dir, err := ioutil.TempDir("", "tmpdb")
704 require.NoError(t, err)
705 defer assert.NoError(t, os.RemoveAll(dir))
706
707 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
708 Type: statedb.TypeBatchBuilder, NLevels: 32})
709 assert.NoError(t, err)
710
711 chainID := uint16(1)
712
713 // generate test transactions from test.SetBlockchain0 code
714 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
715
716 set := `
717 Type: Blockchain
718
719 CreateAccountDeposit(0) A: 200
720
721 > batchL1 // freeze L1User{1}
722
723 CreateAccountCoordinator(0) Coord
724 CreateAccountCoordinator(0) B
725
726 Transfer(0) A-B: 100 (126)
727
728 > batchL1 // forge L1User{1}, forge L1Coord{4}, forge L2{2}
729 > block
730 `
731 blocks, err := tc.GenerateBlocks(set)
732 require.NoError(t, err)
733
734 config := Config{
735 NLevels: 32,
736 MaxFeeTx: 64,
737 MaxTx: 512,
738 MaxL1Tx: 16,
739 ChainID: chainID,
740 }
741 tp := NewTxProcessor(sdb, config)
742 // batch1
743 _, err = tp.ProcessTxs(nil, nil, nil, nil) // to simulate the first batch from the Til set
744 require.NoError(t, err)
745
746 return tp, tc, blocks
747}
748
749func TestMultipleCoordIdxForTokenID(t *testing.T) {
750 // Check that ProcessTxs always uses the first occurrence of the
751 // CoordIdx for each TokenID
752
753 coordIdxs := []common.Idx{257, 257, 257}
754 tp, tc, blocks := initTestMultipleCoordIdxForTokenID(t)
755 l1UserTxs := til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
756 l1CoordTxs := blocks[0].Rollup.Batches[1].L1CoordinatorTxs
757 l1CoordTxs = append(l1CoordTxs, l1CoordTxs[0]) // duplicate the CoordAccount for TokenID=0
758 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
759 _, err := tp.ProcessTxs(coordIdxs, l1UserTxs, l1CoordTxs, l2Txs)
760 require.NoError(t, err)
761
762 checkBalanceByIdx(t, tp.s, 256, "90") // A
763 checkBalanceByIdx(t, tp.s, 257, "10") // Coord0
764 checkBalanceByIdx(t, tp.s, 258, "100") // B
765 checkBalanceByIdx(t, tp.s, 259, "0") // Coord0
766
767 // reset StateDB values
768 coordIdxs = []common.Idx{259, 257}
769 tp, tc, blocks = initTestMultipleCoordIdxForTokenID(t)
770 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
771 l1CoordTxs = blocks[0].Rollup.Batches[1].L1CoordinatorTxs
772 l1CoordTxs = append(l1CoordTxs, l1CoordTxs[0]) // duplicate the CoordAccount for TokenID=0
773 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
774 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, l1CoordTxs, l2Txs)
775 require.NoError(t, err)
776
777 checkBalanceByIdx(t, tp.s, 256, "90") // A
778 checkBalanceByIdx(t, tp.s, 257, "0") // Coord0
779 checkBalanceByIdx(t, tp.s, 258, "100") // B
780 checkBalanceByIdx(t, tp.s, 259, "10") // Coord0
781
782 // reset StateDB values
783 coordIdxs = []common.Idx{257, 259}
784 tp, tc, blocks = initTestMultipleCoordIdxForTokenID(t)
785 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
786 l1CoordTxs = blocks[0].Rollup.Batches[1].L1CoordinatorTxs
787 l1CoordTxs = append(l1CoordTxs, l1CoordTxs[0]) // duplicate the CoordAccount for TokenID=0
788 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
789 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, l1CoordTxs, l2Txs)
790 require.NoError(t, err)
791
792 checkBalanceByIdx(t, tp.s, 256, "90") // A
793 checkBalanceByIdx(t, tp.s, 257, "10") // Coord0
794 checkBalanceByIdx(t, tp.s, 258, "100") // B
795 checkBalanceByIdx(t, tp.s, 259, "0") // Coord0
796}
797
798func testTwoExits(t *testing.T, stateDBType statedb.TypeStateDB) ([]*ProcessTxOutput,
799 []*ProcessTxOutput, []*ProcessTxOutput) {
800 // In the first part we generate a batch with two force exits for the
801 // same account of 20 each. The txprocessor output should be a single
802 // exitInfo with balance of 40.
803 dir, err := ioutil.TempDir("", "tmpdb")
804 require.NoError(t, err)
805 defer assert.NoError(t, os.RemoveAll(dir))
806
807 nLevels := 16
808 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
809 Type: stateDBType, NLevels: nLevels})
810 assert.NoError(t, err)
811
812 chainID := uint16(1)
813
814 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
815
816 // Two exits for the same account. The tx processor should output a
817 // single exit with the accumulated exit balance
818 set := `
819 Type: Blockchain
820
821 CreateAccountDeposit(0) A: 100
822
823 > batchL1 // freeze L1User{1}
824 > batchL1 // forge L1User{1}
825
826 ForceExit(0) A: 20
827 ForceExit(0) A: 20
828
829 > batchL1 // freeze L1User{2}
830 > batchL1 // forge L1User{2}
831 > block
832 `
833 blocks, err := tc.GenerateBlocks(set)
834 require.NoError(t, err)
835 err = tc.FillBlocksExtra(blocks, &til.ConfigExtra{})
836 require.NoError(t, err)
837 err = tc.FillBlocksForgedL1UserTxs(blocks)
838 require.NoError(t, err)
839
840 // Sanity check
841 require.Equal(t, 1, len(blocks[0].Rollup.Batches[1].L1UserTxs))
842 require.Equal(t, 2, len(blocks[0].Rollup.Batches[3].L1UserTxs))
843
844 config := Config{
845 NLevels: uint32(nLevels),
846 MaxTx: 3,
847 MaxL1Tx: 2,
848 MaxFeeTx: 2,
849 ChainID: chainID,
850 }
851 tp := NewTxProcessor(sdb, config)
852 ptOuts := []*ProcessTxOutput{}
853 for _, block := range blocks {
854 for _, batch := range block.Rollup.Batches {
855 ptOut, err := tp.ProcessTxs(nil, batch.L1UserTxs, nil, nil)
856 require.NoError(t, err)
857 ptOuts = append(ptOuts, ptOut)
858 }
859 }
860
861 acc, err := sdb.GetAccount(256)
862 require.NoError(t, err)
863 assert.Equal(t, big.NewInt(60), acc.Balance)
864
865 // In the second part we start a fresh statedb and generate a batch
866 // with one force exit for the same account as before. The txprocessor
867 // output should be a single exitInfo with balance of 40, and the exit
868 // merkle tree proof should be equal to the previous one.
869
870 dir2, err := ioutil.TempDir("", "tmpdb")
871 require.NoError(t, err)
872 defer assert.NoError(t, os.RemoveAll(dir2))
873
874 sdb2, err := statedb.NewStateDB(statedb.Config{Path: dir2, Keep: 128,
875 Type: stateDBType, NLevels: nLevels})
876 assert.NoError(t, err)
877
878 tc = til.NewContext(chainID, common.RollupConstMaxL1UserTx)
879
880 // Single exit with balance of both exits in previous set. The exit
881 // root should match.
882 set2 := `
883 Type: Blockchain
884
885 CreateAccountDeposit(0) A: 100
886
887 > batchL1 // freeze L1User{1}
888 > batchL1 // forge L1User{1}
889
890 ForceExit(0) A: 40
891
892 > batchL1 // freeze L1User{2}
893 > batchL1 // forge L1User{2}
894 > block
895 `
896 blocks, err = tc.GenerateBlocks(set2)
897 require.NoError(t, err)
898 err = tc.FillBlocksExtra(blocks, &til.ConfigExtra{})
899 require.NoError(t, err)
900 err = tc.FillBlocksForgedL1UserTxs(blocks)
901 require.NoError(t, err)
902
903 tp = NewTxProcessor(sdb2, config)
904 ptOuts2 := []*ProcessTxOutput{}
905 for _, block := range blocks {
906 for _, batch := range block.Rollup.Batches {
907 ptOut, err := tp.ProcessTxs(nil, batch.L1UserTxs, nil, nil)
908 require.NoError(t, err)
909 ptOuts2 = append(ptOuts2, ptOut)
910 }
911 }
912
913 // In the third part we start a fresh statedb and generate a batch with
914 // two force exit for the same account as before but where the 1st Exit
915 // is with all the amount, and the 2nd Exit is with more amount than
916 // the available balance. The txprocessor output should be a single
917 // exitInfo with balance of 40, and the exit merkle tree proof should
918 // be equal to the previous ones.
919
920 dir3, err := ioutil.TempDir("", "tmpdb")
921 require.NoError(t, err)
922 defer assert.NoError(t, os.RemoveAll(dir3))
923
924 sdb3, err := statedb.NewStateDB(statedb.Config{Path: dir3, Keep: 128,
925 Type: stateDBType, NLevels: nLevels})
926 assert.NoError(t, err)
927
928 tc = til.NewContext(chainID, common.RollupConstMaxL1UserTx)
929
930 // Single exit with balance of both exits in previous set. The exit
931 // root should match.
932 set3 := `
933 Type: Blockchain
934
935 CreateAccountDeposit(0) A: 100
936
937 > batchL1 // freeze L1User{1}
938 > batchL1 // forge L1User{1}
939
940 ForceExit(0) A: 40
941 ForceExit(0) A: 100
942
943 > batchL1 // freeze L1User{2}
944 > batchL1 // forge L1User{2}
945 > block
946 `
947 blocks, err = tc.GenerateBlocks(set3)
948 require.NoError(t, err)
949 err = tc.FillBlocksExtra(blocks, &til.ConfigExtra{})
950 require.NoError(t, err)
951 err = tc.FillBlocksForgedL1UserTxs(blocks)
952 require.NoError(t, err)
953
954 tp = NewTxProcessor(sdb3, config)
955 ptOuts3 := []*ProcessTxOutput{}
956 for _, block := range blocks {
957 for _, batch := range block.Rollup.Batches {
958 ptOut, err := tp.ProcessTxs(nil, batch.L1UserTxs, nil, nil)
959 require.NoError(t, err)
960 ptOuts3 = append(ptOuts3, ptOut)
961 }
962 }
963
964 return ptOuts, ptOuts2, ptOuts3
965}
966
967func TestTwoExitsSynchronizer(t *testing.T) {
968 ptOuts, ptOuts2, ptOuts3 := testTwoExits(t, statedb.TypeSynchronizer)
969
970 assert.Equal(t, 1, len(ptOuts[3].ExitInfos))
971 assert.Equal(t, big.NewInt(40), ptOuts[3].ExitInfos[0].Balance)
972
973 assert.Equal(t, ptOuts[3].ExitInfos[0].MerkleProof, ptOuts2[3].ExitInfos[0].MerkleProof)
974 assert.Equal(t, ptOuts[3].ExitInfos[0].MerkleProof, ptOuts3[3].ExitInfos[0].MerkleProof)
975}
976
977func TestExitOf0Amount(t *testing.T) {
978 // Test to check that when doing an Exit with amount 0 the Exit Root
979 // does not change (as there is no new Exit Leaf created)
980
981 dir, err := ioutil.TempDir("", "tmpdb")
982 require.NoError(t, err)
983 defer assert.NoError(t, os.RemoveAll(dir))
984
985 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
986 Type: statedb.TypeBatchBuilder, NLevels: 32})
987 assert.NoError(t, err)
988
989 chainID := uint16(1)
990
991 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
992
993 set := `
994 Type: Blockchain
995
996 CreateAccountDeposit(0) A: 100
997 CreateAccountDeposit(0) B: 100
998
999 > batchL1 // batch1: freeze L1User{2}
1000 > batchL1 // batch2: forge L1User{2}
1001
1002 ForceExit(0) A: 10
1003 ForceExit(0) B: 0
1004
1005 > batchL1 // batch3: freeze L1User{2}
1006 > batchL1 // batch4: forge L1User{2}
1007
1008 ForceExit(0) A: 10
1009
1010 > batchL1 // batch5: freeze L1User{1}
1011 > batchL1 // batch6: forge L1User{1}
1012
1013 ForceExit(0) A: 0
1014 > batchL1 // batch7: freeze L1User{1}
1015 > batchL1 // batch8: forge L1User{1}
1016 > block
1017 `
1018 blocks, err := tc.GenerateBlocks(set)
1019 require.NoError(t, err)
1020 err = tc.FillBlocksExtra(blocks, &til.ConfigExtra{})
1021 require.NoError(t, err)
1022 err = tc.FillBlocksForgedL1UserTxs(blocks)
1023 require.NoError(t, err)
1024
1025 // Sanity check
1026 require.Equal(t, 2, len(blocks[0].Rollup.Batches[1].L1UserTxs))
1027 require.Equal(t, 2, len(blocks[0].Rollup.Batches[3].L1UserTxs))
1028 require.Equal(t, big.NewInt(10), blocks[0].Rollup.Batches[3].L1UserTxs[0].Amount)
1029 require.Equal(t, big.NewInt(0), blocks[0].Rollup.Batches[3].L1UserTxs[1].Amount)
1030
1031 config := Config{
1032 NLevels: 32,
1033 MaxFeeTx: 64,
1034 MaxTx: 512,
1035 MaxL1Tx: 16,
1036 ChainID: chainID,
1037 }
1038 tp := NewTxProcessor(sdb, config)
1039
1040 // For this test are only processed the batches with transactions:
1041 // - Batch2, equivalent to Batches[1]
1042 // - Batch4, equivalent to Batches[3]
1043 // - Batch6, equivalent to Batches[5]
1044 // - Batch8, equivalent to Batches[7]
1045
1046 // process Batch2:
1047 _, err = tp.ProcessTxs(nil, blocks[0].Rollup.Batches[1].L1UserTxs, nil, nil)
1048 require.NoError(t, err)
1049 // process Batch4:
1050 ptOut, err := tp.ProcessTxs(nil, blocks[0].Rollup.Batches[3].L1UserTxs, nil, nil)
1051 require.NoError(t, err)
1052 assert.Equal(t, "14329759303391468223438874789317921522067594445474390443816827472846339238908", ptOut.ZKInputs.Metadata.NewExitRootRaw.BigInt().String())
1053 exitRootBatch4 := ptOut.ZKInputs.Metadata.NewExitRootRaw.BigInt().String()
1054
1055 // process Batch6:
1056 ptOut, err = tp.ProcessTxs(nil, blocks[0].Rollup.Batches[5].L1UserTxs, nil, nil)
1057 require.NoError(t, err)
1058 assert.Equal(t, "14329759303391468223438874789317921522067594445474390443816827472846339238908", ptOut.ZKInputs.Metadata.NewExitRootRaw.BigInt().String())
1059 // Expect that the ExitRoot for the Batch6 will be equal than for the
1060 // Batch4, as the Batch4 & Batch6 have the same tx with Exit Amount=10,
1061 // and Batch4 has a 2nd tx with Exit Amount=0.
1062 assert.Equal(t, exitRootBatch4, ptOut.ZKInputs.Metadata.NewExitRootRaw.BigInt().String())
1063
1064 // For the Batch8, as there is only 1 exit with Amount=0, the ExitRoot
1065 // should be 0.
1066 // process Batch8:
1067 ptOut, err = tp.ProcessTxs(nil, blocks[0].Rollup.Batches[7].L1UserTxs, nil, nil)
1068 require.NoError(t, err)
1069 assert.Equal(t, "0", ptOut.ZKInputs.Metadata.NewExitRootRaw.BigInt().String())
1070}
1071
1072func TestUpdatedAccounts(t *testing.T) {
1073 dir, err := ioutil.TempDir("", "tmpdb")
1074 require.NoError(t, err)
1075 defer assert.NoError(t, os.RemoveAll(dir))
1076
1077 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
1078 Type: statedb.TypeSynchronizer, NLevels: 32})
1079 assert.NoError(t, err)
1080
1081 set := `
1082 Type: Blockchain
1083 AddToken(1)
1084 CreateAccountCoordinator(0) Coord // 256
1085 CreateAccountCoordinator(1) Coord // 257
1086 > batch // 1
1087 CreateAccountDeposit(0) A: 50 // 258
1088 CreateAccountDeposit(0) B: 60 // 259
1089 CreateAccountDeposit(1) A: 70 // 260
1090 CreateAccountDeposit(1) B: 80 // 261
1091 > batchL1 // 2
1092 > batchL1 // 3
1093 Transfer(0) A-B: 5 (126)
1094 > batch // 4
1095 Exit(1) B: 5 (126)
1096 > batch // 5
1097 > block
1098 `
1099
1100 chainID := uint16(0)
1101 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
1102 blocks, err := tc.GenerateBlocks(set)
1103 require.NoError(t, err)
1104 tilCfgExtra := til.ConfigExtra{
1105 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
1106 CoordUser: "Coord",
1107 }
1108 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
1109 require.NoError(t, err)
1110 tc.FillBlocksL1UserTxsBatchNum(blocks)
1111 err = tc.FillBlocksForgedL1UserTxs(blocks)
1112 require.NoError(t, err)
1113
1114 require.Equal(t, 5, len(blocks[0].Rollup.Batches))
1115
1116 config := Config{
1117 NLevels: 32,
1118 MaxFeeTx: 64,
1119 MaxTx: 512,
1120 MaxL1Tx: 16,
1121 ChainID: chainID,
1122 }
1123 tp := NewTxProcessor(sdb, config)
1124
1125 sortedKeys := func(m map[common.Idx]*common.Account) []int {
1126 keys := make([]int, 0)
1127 for k := range m {
1128 keys = append(keys, int(k))
1129 }
1130 sort.Ints(keys)
1131 return keys
1132 }
1133
1134 for _, batch := range blocks[0].Rollup.Batches {
1135 l2Txs := common.L2TxsToPoolL2Txs(batch.L2Txs)
1136 ptOut, err := tp.ProcessTxs(batch.Batch.FeeIdxsCoordinator, batch.L1UserTxs,
1137 batch.L1CoordinatorTxs, l2Txs)
1138 require.NoError(t, err)
1139 switch batch.Batch.BatchNum {
1140 case 1:
1141 assert.Equal(t, 2, len(ptOut.UpdatedAccounts))
1142 assert.Equal(t, []int{256, 257}, sortedKeys(ptOut.UpdatedAccounts))
1143 case 2:
1144 assert.Equal(t, 0, len(ptOut.UpdatedAccounts))
1145 assert.Equal(t, []int{}, sortedKeys(ptOut.UpdatedAccounts))
1146 case 3:
1147 assert.Equal(t, 4, len(ptOut.UpdatedAccounts))
1148 assert.Equal(t, []int{258, 259, 260, 261}, sortedKeys(ptOut.UpdatedAccounts))
1149 case 4:
1150 assert.Equal(t, 2+1, len(ptOut.UpdatedAccounts))
1151 assert.Equal(t, []int{256, 258, 259}, sortedKeys(ptOut.UpdatedAccounts))
1152 case 5:
1153 assert.Equal(t, 1+1, len(ptOut.UpdatedAccounts))
1154 assert.Equal(t, []int{257, 261}, sortedKeys(ptOut.UpdatedAccounts))
1155 }
1156 for idx, updAcc := range ptOut.UpdatedAccounts {
1157 acc, err := sdb.GetAccount(idx)
1158 require.NoError(t, err)
1159 // If acc.Balance is 0, set it to 0 with big.NewInt so
1160 // that the comparison succeeds. Without this, the
1161 // comparison will not succeed because acc.Balance is
1162 // set from a slice, and thus the internal big.Int
1163 // buffer is not nil (big.Int.abs)
1164 if acc.Balance.BitLen() == 0 {
1165 acc.Balance = big.NewInt(0)
1166 }
1167 assert.Equal(t, acc, updAcc)
1168 }
1169 }
1170}