hermez-node/txprocessor/txprocessor_test.go
txprocessor/txprocessor_test.goRaw
1package txprocessor
2
3import (
4 "io/ioutil"
5 "math/big"
6 "os"
7 "testing"
8
9 ethCommon "github.com/ethereum/go-ethereum/common"
10 "github.com/hermeznetwork/hermez-node/common"
11 "github.com/hermeznetwork/hermez-node/db/statedb"
12 "github.com/hermeznetwork/hermez-node/log"
13 "github.com/hermeznetwork/hermez-node/test/til"
14 "github.com/hermeznetwork/hermez-node/test/txsets"
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/require"
17)
18
19func checkBalance(t *testing.T, tc *til.Context, sdb *statedb.StateDB, username string, tokenid int, expected string) {
20 idx := tc.Users[username].Accounts[common.TokenID(tokenid)].Idx
21 acc, err := sdb.GetAccount(idx)
22 require.NoError(t, err)
23 assert.Equal(t, expected, acc.Balance.String())
24}
25
26func checkBalanceByIdx(t *testing.T, sdb *statedb.StateDB, idx common.Idx, expected string) {
27 acc, err := sdb.GetAccount(idx)
28 require.NoError(t, err)
29 assert.Equal(t, expected, acc.Balance.String())
30}
31
32func TestComputeEffectiveAmounts(t *testing.T) {
33 dir, err := ioutil.TempDir("", "tmpdb")
34 require.NoError(t, err)
35 defer assert.NoError(t, os.RemoveAll(dir))
36
37 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
38 Type: statedb.TypeSynchronizer, NLevels: 32})
39 assert.NoError(t, err)
40
41 set := `
42 Type: Blockchain
43 AddToken(1)
44
45 CreateAccountDeposit(0) A: 10
46 CreateAccountDeposit(0) B: 10
47 CreateAccountDeposit(1) C: 10
48 > batchL1
49 > batchL1
50 > block
51 `
52 chainID := uint16(0)
53 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
54 blocks, err := tc.GenerateBlocks(set)
55 require.NoError(t, err)
56
57 config := Config{
58 NLevels: 32,
59 MaxFeeTx: 64,
60 MaxTx: 512,
61 MaxL1Tx: 16,
62 ChainID: chainID,
63 }
64 tp := NewTxProcessor(sdb, config)
65 _, err = tp.ProcessTxs(nil, blocks[0].Rollup.L1UserTxs, nil, nil)
66 require.NoError(t, err)
67
68 tx := common.L1Tx{
69 FromIdx: 256,
70 ToIdx: 257,
71 Amount: big.NewInt(10),
72 DepositAmount: big.NewInt(0),
73 FromEthAddr: tc.Users["A"].Addr,
74 UserOrigin: true,
75 }
76 tp.computeEffectiveAmounts(&tx)
77 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
78 assert.Equal(t, big.NewInt(10), tx.EffectiveAmount)
79
80 // expect error due not enough funds
81 tx = common.L1Tx{
82 FromIdx: 256,
83 ToIdx: 257,
84 Amount: big.NewInt(11),
85 DepositAmount: big.NewInt(0),
86 FromEthAddr: tc.Users["A"].Addr,
87 UserOrigin: true,
88 }
89 tp.computeEffectiveAmounts(&tx)
90 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
91 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
92
93 // expect no-error as there are enough funds in a
94 // CreateAccountDepositTransfer transction
95 tx = common.L1Tx{
96 FromIdx: 0,
97 ToIdx: 257,
98 Amount: big.NewInt(10),
99 DepositAmount: big.NewInt(10),
100 UserOrigin: true,
101 }
102 tp.computeEffectiveAmounts(&tx)
103 assert.Equal(t, big.NewInt(10), tx.EffectiveDepositAmount)
104 assert.Equal(t, big.NewInt(10), tx.EffectiveAmount)
105
106 // expect error due not enough funds in a CreateAccountDepositTransfer
107 // transction
108 tx = common.L1Tx{
109 FromIdx: 0,
110 ToIdx: 257,
111 Amount: big.NewInt(11),
112 DepositAmount: big.NewInt(10),
113 UserOrigin: true,
114 }
115 tp.computeEffectiveAmounts(&tx)
116 assert.Equal(t, big.NewInt(10), tx.EffectiveDepositAmount)
117 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
118
119 // expect error due not same TokenID
120 tx = common.L1Tx{
121 FromIdx: 256,
122 ToIdx: 258,
123 Amount: big.NewInt(5),
124 DepositAmount: big.NewInt(0),
125 FromEthAddr: tc.Users["A"].Addr,
126 UserOrigin: true,
127 }
128 tp.computeEffectiveAmounts(&tx)
129 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
130 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
131
132 // expect error due not same EthAddr
133 tx = common.L1Tx{
134 FromIdx: 256,
135 ToIdx: 257,
136 Amount: big.NewInt(8),
137 DepositAmount: big.NewInt(0),
138 FromEthAddr: tc.Users["B"].Addr,
139 UserOrigin: true,
140 }
141 tp.computeEffectiveAmounts(&tx)
142 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
143 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
144
145 // expect on TxTypeDepositTransfer EffectiveAmount=0, but
146 // EffectiveDepositAmount!=0, due not enough funds to make the transfer
147 tx = common.L1Tx{
148 FromIdx: 256,
149 ToIdx: 257,
150 Amount: big.NewInt(20),
151 DepositAmount: big.NewInt(8),
152 FromEthAddr: tc.Users["A"].Addr,
153 UserOrigin: true,
154 }
155 tp.computeEffectiveAmounts(&tx)
156 assert.Equal(t, big.NewInt(8), tx.EffectiveDepositAmount)
157 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
158
159 // expect on TxTypeDepositTransfer EffectiveAmount=0, but
160 // EffectiveDepositAmount!=0, due different EthAddr from FromIdx
161 // address
162 tx = common.L1Tx{
163 FromIdx: 256,
164 ToIdx: 257,
165 Amount: big.NewInt(8),
166 DepositAmount: big.NewInt(8),
167 FromEthAddr: tc.Users["B"].Addr,
168 UserOrigin: true,
169 }
170 tp.computeEffectiveAmounts(&tx)
171 assert.Equal(t, big.NewInt(8), tx.EffectiveDepositAmount)
172 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
173
174 // CreateAccountDepositTransfer for TokenID=1 when receiver does not
175 // have an account for that TokenID, expect that the
176 // EffectiveDepositAmount=DepositAmount, but EffectiveAmount==0
177 tx = common.L1Tx{
178 FromIdx: 0,
179 ToIdx: 257,
180 Amount: big.NewInt(8),
181 DepositAmount: big.NewInt(8),
182 FromEthAddr: tc.Users["A"].Addr,
183 TokenID: 2,
184 UserOrigin: true,
185 Type: common.TxTypeCreateAccountDepositTransfer,
186 }
187 tp.computeEffectiveAmounts(&tx)
188 assert.Equal(t, big.NewInt(8), tx.EffectiveDepositAmount)
189 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
190
191 // DepositTransfer for TokenID=1 when receiver does not have an account
192 // for that TokenID, expect that the
193 // EffectiveDepositAmount=DepositAmount, but EffectiveAmount=0
194 tx = common.L1Tx{
195 FromIdx: 258,
196 ToIdx: 256,
197 Amount: big.NewInt(8),
198 DepositAmount: big.NewInt(8),
199 FromEthAddr: tc.Users["C"].Addr,
200 TokenID: 1,
201 UserOrigin: true,
202 Type: common.TxTypeDepositTransfer,
203 }
204 tp.computeEffectiveAmounts(&tx)
205 assert.Equal(t, big.NewInt(8), tx.EffectiveDepositAmount)
206 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
207}
208
209func TestProcessTxsBalances(t *testing.T) {
210 dir, err := ioutil.TempDir("", "tmpdb")
211 require.NoError(t, err)
212 defer assert.NoError(t, os.RemoveAll(dir))
213
214 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
215 Type: statedb.TypeSynchronizer, NLevels: 32})
216 assert.NoError(t, err)
217
218 chainID := uint16(0)
219 // generate test transactions from test.SetBlockchain0 code
220 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
221 blocks, err := tc.GenerateBlocks(txsets.SetBlockchainMinimumFlow0)
222 require.NoError(t, err)
223
224 config := Config{
225 NLevels: 32,
226 MaxFeeTx: 64,
227 MaxTx: 512,
228 MaxL1Tx: 16,
229 ChainID: chainID,
230 }
231 tp := NewTxProcessor(sdb, config)
232
233 log.Debug("block:0 batch:1, only L1CoordinatorTxs")
234 _, err = tp.ProcessTxs(nil, nil, blocks[0].Rollup.Batches[0].L1CoordinatorTxs, nil)
235 require.NoError(t, err)
236 assert.Equal(t, "0", tp.s.MT.Root().BigInt().String())
237
238 log.Debug("block:0 batch:2")
239 l1UserTxs := []common.L1Tx{}
240 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
241 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
242 require.NoError(t, err)
243 assert.Equal(t, "0", tp.s.MT.Root().BigInt().String())
244
245 log.Debug("block:0 batch:3")
246 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
247 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[2].L2Txs)
248 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[2].L1CoordinatorTxs, l2Txs)
249 require.NoError(t, err)
250 checkBalance(t, tc, sdb, "A", 0, "500")
251 assert.Equal(t, "13644148972047617726265275926674266298636745191961029124811988256139761111521", tp.s.MT.Root().BigInt().String())
252
253 log.Debug("block:0 batch:4")
254 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[3].Batch.ForgeL1TxsNum])
255 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[3].L2Txs)
256 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[3].L1CoordinatorTxs, l2Txs)
257 require.NoError(t, err)
258 checkBalance(t, tc, sdb, "A", 0, "500")
259 checkBalance(t, tc, sdb, "A", 1, "500")
260 assert.Equal(t, "12433441613247342495680642890662773367605896324555599297255745922589338651261", tp.s.MT.Root().BigInt().String())
261
262 log.Debug("block:0 batch:5")
263 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[4].Batch.ForgeL1TxsNum])
264 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[4].L2Txs)
265 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[4].L1CoordinatorTxs, l2Txs)
266 require.NoError(t, err)
267 checkBalance(t, tc, sdb, "A", 0, "500")
268 checkBalance(t, tc, sdb, "A", 1, "500")
269 assert.Equal(t, "12433441613247342495680642890662773367605896324555599297255745922589338651261", tp.s.MT.Root().BigInt().String())
270
271 log.Debug("block:0 batch:6")
272 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[5].Batch.ForgeL1TxsNum])
273 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[5].L2Txs)
274 _, err = tp.ProcessTxs(nil, l1UserTxs, blocks[0].Rollup.Batches[5].L1CoordinatorTxs, l2Txs)
275 require.NoError(t, err)
276 checkBalance(t, tc, sdb, "A", 0, "600")
277 checkBalance(t, tc, sdb, "A", 1, "500")
278 checkBalance(t, tc, sdb, "B", 0, "400")
279 assert.Equal(t, "4191361650490017591061467288209836928064232431729236465872209988325272262963", tp.s.MT.Root().BigInt().String())
280
281 coordIdxs := []common.Idx{261, 262}
282 log.Debug("block:0 batch:7")
283 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[6].Batch.ForgeL1TxsNum])
284 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[6].L2Txs)
285 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, blocks[0].Rollup.Batches[6].L1CoordinatorTxs, l2Txs)
286 require.NoError(t, err)
287 checkBalance(t, tc, sdb, "Coord", 0, "10")
288 checkBalance(t, tc, sdb, "Coord", 1, "20")
289 checkBalance(t, tc, sdb, "A", 0, "600")
290 checkBalance(t, tc, sdb, "A", 1, "280")
291 checkBalance(t, tc, sdb, "B", 0, "290")
292 checkBalance(t, tc, sdb, "B", 1, "200")
293 checkBalance(t, tc, sdb, "C", 0, "100")
294 checkBalance(t, tc, sdb, "D", 0, "800")
295 assert.Equal(t, "7614010373759339299470010949167613050707822522530721724565424494781010548240", tp.s.MT.Root().BigInt().String())
296
297 log.Debug("block:0 batch:8")
298 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[7].Batch.ForgeL1TxsNum])
299 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[7].L2Txs)
300 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, blocks[0].Rollup.Batches[7].L1CoordinatorTxs, l2Txs)
301 require.NoError(t, err)
302 checkBalance(t, tc, sdb, "Coord", 0, "35")
303 checkBalance(t, tc, sdb, "Coord", 1, "30")
304 checkBalance(t, tc, sdb, "A", 0, "430")
305 checkBalance(t, tc, sdb, "A", 1, "280")
306 checkBalance(t, tc, sdb, "B", 0, "390")
307 checkBalance(t, tc, sdb, "B", 1, "90")
308 checkBalance(t, tc, sdb, "C", 0, "45")
309 checkBalance(t, tc, sdb, "C", 1, "100")
310 checkBalance(t, tc, sdb, "D", 0, "800")
311 assert.Equal(t, "21231789250434471575486264439945776732824482207853465397552873521865656677689", tp.s.MT.Root().BigInt().String())
312
313 coordIdxs = []common.Idx{262}
314 log.Debug("block:1 batch:1")
315 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[0].Batch.ForgeL1TxsNum])
316 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[0].L2Txs)
317 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, blocks[1].Rollup.Batches[0].L1CoordinatorTxs, l2Txs)
318 require.NoError(t, err)
319 checkBalance(t, tc, sdb, "Coord", 0, "75")
320 checkBalance(t, tc, sdb, "Coord", 1, "30")
321 checkBalance(t, tc, sdb, "A", 0, "730")
322 checkBalance(t, tc, sdb, "A", 1, "280")
323 checkBalance(t, tc, sdb, "B", 0, "380")
324 checkBalance(t, tc, sdb, "B", 1, "90")
325 checkBalance(t, tc, sdb, "C", 0, "845")
326 checkBalance(t, tc, sdb, "C", 1, "100")
327 checkBalance(t, tc, sdb, "D", 0, "470")
328 assert.Equal(t, "11289313644810782435120113035387729451095637380468777086895109386127538554246", tp.s.MT.Root().BigInt().String())
329
330 coordIdxs = []common.Idx{}
331 log.Debug("block:1 batch:2")
332 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[1].Batch.ForgeL1TxsNum])
333 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[1].L2Txs)
334 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, blocks[1].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
335 require.NoError(t, err)
336 assert.Equal(t, "10342681351319338354912862547249967104198317571995055517008223832276478908482", tp.s.MT.Root().BigInt().String())
337
338 // use Set of PoolL2 txs
339 poolL2Txs, err := tc.GeneratePoolL2Txs(txsets.SetPoolL2MinimumFlow1)
340 assert.NoError(t, err)
341
342 _, err = tp.ProcessTxs(coordIdxs, []common.L1Tx{}, []common.L1Tx{}, poolL2Txs)
343 require.NoError(t, err)
344 checkBalance(t, tc, sdb, "Coord", 0, "75")
345 checkBalance(t, tc, sdb, "Coord", 1, "30")
346 checkBalance(t, tc, sdb, "A", 0, "510")
347 checkBalance(t, tc, sdb, "A", 1, "170")
348 checkBalance(t, tc, sdb, "B", 0, "480")
349 checkBalance(t, tc, sdb, "B", 1, "190")
350 checkBalance(t, tc, sdb, "C", 0, "845")
351 checkBalance(t, tc, sdb, "C", 1, "100")
352 checkBalance(t, tc, sdb, "D", 0, "360")
353 checkBalance(t, tc, sdb, "F", 0, "100")
354}
355
356func TestProcessTxsSynchronizer(t *testing.T) {
357 dir, err := ioutil.TempDir("", "tmpdb")
358 require.NoError(t, err)
359 defer assert.NoError(t, os.RemoveAll(dir))
360
361 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
362 Type: statedb.TypeSynchronizer, NLevels: 32})
363 assert.NoError(t, err)
364
365 chainID := uint16(0)
366 // generate test transactions from test.SetBlockchain0 code
367 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
368 blocks, err := tc.GenerateBlocks(txsets.SetBlockchain0)
369 require.NoError(t, err)
370
371 assert.Equal(t, 31, len(blocks[0].Rollup.L1UserTxs))
372 assert.Equal(t, 4, len(blocks[0].Rollup.Batches[0].L1CoordinatorTxs))
373 assert.Equal(t, 0, len(blocks[0].Rollup.Batches[1].L1CoordinatorTxs))
374 assert.Equal(t, 22, len(blocks[0].Rollup.Batches[2].L2Txs))
375 assert.Equal(t, 1, len(blocks[1].Rollup.Batches[0].L1CoordinatorTxs))
376 assert.Equal(t, 62, len(blocks[1].Rollup.Batches[0].L2Txs))
377 assert.Equal(t, 1, len(blocks[1].Rollup.Batches[1].L1CoordinatorTxs))
378 assert.Equal(t, 8, len(blocks[1].Rollup.Batches[1].L2Txs))
379
380 // Coordinator Idx where to send the fees
381 coordIdxs := []common.Idx{256, 257, 258, 259}
382
383 // Idx of user 'A'
384 idxA1 := tc.Users["A"].Accounts[common.TokenID(1)].Idx
385
386 config := Config{
387 NLevels: 32,
388 MaxFeeTx: 64,
389 MaxTx: 512,
390 MaxL1Tx: 32,
391 ChainID: chainID,
392 }
393 tp := NewTxProcessor(sdb, config)
394
395 // Process the 1st batch, which contains the L1CoordinatorTxs necessary
396 // to create the Coordinator accounts to receive the fees
397 log.Debug("block:0 batch:1, only L1CoordinatorTxs")
398 ptOut, err := tp.ProcessTxs(nil, nil, blocks[0].Rollup.Batches[0].L1CoordinatorTxs, nil)
399 require.NoError(t, err)
400 assert.Equal(t, 4, len(ptOut.CreatedAccounts))
401 assert.Equal(t, 0, len(ptOut.CollectedFees))
402
403 log.Debug("block:0 batch:2")
404 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
405 ptOut, err = tp.ProcessTxs(coordIdxs, blocks[0].Rollup.L1UserTxs,
406 blocks[0].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
407 require.NoError(t, err)
408 assert.Equal(t, 0, len(ptOut.ExitInfos))
409 assert.Equal(t, 31, len(ptOut.CreatedAccounts))
410 assert.Equal(t, 4, len(ptOut.CollectedFees))
411 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(0)].String())
412 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(1)].String())
413 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(2)].String())
414 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(3)].String())
415 acc, err := sdb.GetAccount(idxA1)
416 require.NoError(t, err)
417 assert.Equal(t, "50", acc.Balance.String())
418
419 log.Debug("block:0 batch:3")
420 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[2].L2Txs)
421 ptOut, err = tp.ProcessTxs(coordIdxs, nil, blocks[0].Rollup.Batches[2].L1CoordinatorTxs, l2Txs)
422 require.NoError(t, err)
423 assert.Equal(t, 0, len(ptOut.ExitInfos))
424 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
425 assert.Equal(t, 4, len(ptOut.CollectedFees))
426 assert.Equal(t, "2", ptOut.CollectedFees[common.TokenID(0)].String())
427 assert.Equal(t, "1", ptOut.CollectedFees[common.TokenID(1)].String())
428 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(2)].String())
429 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(3)].String())
430 acc, err = sdb.GetAccount(idxA1)
431 require.NoError(t, err)
432 assert.Equal(t, "35", acc.Balance.String())
433
434 log.Debug("block:1 batch:1")
435 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[0].L2Txs)
436 // before processing expect l2Txs[0:2].Nonce==0
437 assert.Equal(t, common.Nonce(0), l2Txs[0].Nonce)
438 assert.Equal(t, common.Nonce(0), l2Txs[1].Nonce)
439 assert.Equal(t, common.Nonce(0), l2Txs[2].Nonce)
440
441 ptOut, err = tp.ProcessTxs(coordIdxs, nil, blocks[1].Rollup.Batches[0].L1CoordinatorTxs, l2Txs)
442 require.NoError(t, err)
443
444 // after processing expect l2Txs[0:2].Nonce!=0 and has expected value
445 assert.Equal(t, common.Nonce(5), l2Txs[0].Nonce)
446 assert.Equal(t, common.Nonce(6), l2Txs[1].Nonce)
447 assert.Equal(t, common.Nonce(7), l2Txs[2].Nonce)
448
449 assert.Equal(t, 4, len(ptOut.ExitInfos)) // the 'ForceExit(1)' is not computed yet, as the batch is without L1UserTxs
450 assert.Equal(t, 1, len(ptOut.CreatedAccounts))
451 assert.Equal(t, 4, len(ptOut.CollectedFees))
452 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(0)].String())
453 assert.Equal(t, "1", ptOut.CollectedFees[common.TokenID(1)].String())
454 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(2)].String())
455 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(3)].String())
456 acc, err = sdb.GetAccount(idxA1)
457 require.NoError(t, err)
458 assert.Equal(t, "57", acc.Balance.String())
459
460 log.Debug("block:1 batch:2")
461 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[1].L2Txs)
462 ptOut, err = tp.ProcessTxs(coordIdxs, blocks[1].Rollup.L1UserTxs,
463 blocks[1].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
464 require.NoError(t, err)
465
466 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
467 assert.Equal(t, 1, len(ptOut.CreatedAccounts))
468 assert.Equal(t, 4, len(ptOut.CollectedFees))
469 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(0)].String())
470 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(1)].String())
471 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(2)].String())
472 assert.Equal(t, "0", ptOut.CollectedFees[common.TokenID(3)].String())
473 acc, err = sdb.GetAccount(idxA1)
474 assert.NoError(t, err)
475 assert.Equal(t, "77", acc.Balance.String())
476
477 idxB0 := tc.Users["C"].Accounts[common.TokenID(0)].Idx
478 acc, err = sdb.GetAccount(idxB0)
479 require.NoError(t, err)
480 assert.Equal(t, "51", acc.Balance.String())
481
482 // get balance of Coordinator account for TokenID==0
483 acc, err = sdb.GetAccount(common.Idx(256))
484 require.NoError(t, err)
485 assert.Equal(t, "2", acc.Balance.String())
486}
487
488func TestProcessTxsBatchBuilder(t *testing.T) {
489 dir, err := ioutil.TempDir("", "tmpdb")
490 require.NoError(t, err)
491 defer assert.NoError(t, os.RemoveAll(dir))
492
493 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
494 Type: statedb.TypeBatchBuilder, NLevels: 32})
495 assert.NoError(t, err)
496
497 chainID := uint16(0)
498 // generate test transactions from test.SetBlockchain0 code
499 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
500 blocks, err := tc.GenerateBlocks(txsets.SetBlockchain0)
501 require.NoError(t, err)
502
503 // Coordinator Idx where to send the fees
504 coordIdxs := []common.Idx{256, 257, 258, 259}
505
506 // Idx of user 'A'
507 idxA1 := tc.Users["A"].Accounts[common.TokenID(1)].Idx
508
509 config := Config{
510 NLevels: 32,
511 MaxFeeTx: 64,
512 MaxTx: 512,
513 MaxL1Tx: 32,
514 ChainID: chainID,
515 }
516 tp := NewTxProcessor(sdb, config)
517
518 // Process the 1st batch, which contains the L1CoordinatorTxs necessary
519 // to create the Coordinator accounts to receive the fees
520 log.Debug("block:0 batch:1, only L1CoordinatorTxs")
521 ptOut, err := tp.ProcessTxs(nil, nil, blocks[0].Rollup.Batches[0].L1CoordinatorTxs, nil)
522 require.NoError(t, err)
523 // expect 0 at CreatedAccount, as is only computed when StateDB.Type==TypeSynchronizer
524 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
525
526 log.Debug("block:0 batch:2")
527 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
528 ptOut, err = tp.ProcessTxs(coordIdxs, blocks[0].Rollup.L1UserTxs, blocks[0].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
529 require.NoError(t, err)
530 assert.Equal(t, 0, len(ptOut.ExitInfos))
531 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
532 acc, err := sdb.GetAccount(idxA1)
533 require.NoError(t, err)
534 assert.Equal(t, "50", acc.Balance.String())
535
536 log.Debug("block:0 batch:3")
537 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[2].L2Txs)
538 ptOut, err = tp.ProcessTxs(coordIdxs, nil, blocks[0].Rollup.Batches[2].L1CoordinatorTxs, l2Txs)
539 require.NoError(t, err)
540 assert.Equal(t, 0, len(ptOut.ExitInfos))
541 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
542 acc, err = sdb.GetAccount(idxA1)
543 require.NoError(t, err)
544 assert.Equal(t, "35", acc.Balance.String())
545
546 log.Debug("block:1 batch:1")
547 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[0].L2Txs)
548 _, err = tp.ProcessTxs(coordIdxs, nil, blocks[1].Rollup.Batches[0].L1CoordinatorTxs, l2Txs)
549 require.NoError(t, err)
550 acc, err = sdb.GetAccount(idxA1)
551 require.NoError(t, err)
552 assert.Equal(t, "57", acc.Balance.String())
553
554 log.Debug("block:1 batch:2")
555 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[1].L2Txs)
556 _, err = tp.ProcessTxs(coordIdxs, blocks[1].Rollup.L1UserTxs, blocks[1].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
557 require.NoError(t, err)
558 acc, err = sdb.GetAccount(idxA1)
559 assert.NoError(t, err)
560 assert.Equal(t, "77", acc.Balance.String())
561
562 idxB0 := tc.Users["C"].Accounts[common.TokenID(0)].Idx
563 acc, err = sdb.GetAccount(idxB0)
564 require.NoError(t, err)
565 assert.Equal(t, "51", acc.Balance.String())
566
567 // get balance of Coordinator account for TokenID==0
568 acc, err = sdb.GetAccount(common.Idx(256))
569 require.NoError(t, err)
570 assert.Equal(t, common.TokenID(0), acc.TokenID)
571 assert.Equal(t, "2", acc.Balance.String())
572 acc, err = sdb.GetAccount(common.Idx(257))
573 require.NoError(t, err)
574 assert.Equal(t, common.TokenID(1), acc.TokenID)
575 assert.Equal(t, "2", acc.Balance.String())
576
577 assert.Equal(t, "18894163991492573893706613133132363559300580460789469708968288074813925659539", sdb.MT.Root().BigInt().String())
578}
579
580func TestProcessTxsRootTestVectors(t *testing.T) {
581 dir, err := ioutil.TempDir("", "tmpdb")
582 require.NoError(t, err)
583 defer assert.NoError(t, os.RemoveAll(dir))
584
585 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
586 Type: statedb.TypeBatchBuilder, NLevels: 32})
587 assert.NoError(t, err)
588
589 // same values than in the js test
590 bjj0, err := common.BJJFromStringWithChecksum("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7")
591 assert.NoError(t, err)
592 l1Txs := []common.L1Tx{
593 {
594 FromIdx: 0,
595 DepositAmount: big.NewInt(16000000),
596 Amount: big.NewInt(0),
597 TokenID: 1,
598 FromBJJ: bjj0,
599 FromEthAddr: ethCommon.HexToAddress("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf"),
600 ToIdx: 0,
601 Type: common.TxTypeCreateAccountDeposit,
602 UserOrigin: true,
603 },
604 }
605 l2Txs := []common.PoolL2Tx{
606 {
607 FromIdx: 256,
608 ToIdx: 256,
609 TokenID: 1,
610 Amount: big.NewInt(1000),
611 Nonce: 0,
612 Fee: 126,
613 Type: common.TxTypeTransfer,
614 },
615 }
616
617 chainID := uint16(0)
618 config := Config{
619 NLevels: 32,
620 MaxFeeTx: 8,
621 MaxTx: 32,
622 MaxL1Tx: 16,
623 ChainID: chainID,
624 }
625 tp := NewTxProcessor(sdb, config)
626 _, err = tp.ProcessTxs(nil, l1Txs, nil, l2Txs)
627 require.NoError(t, err)
628 assert.Equal(t, "9827704113668630072730115158977131501210702363656902211840117643154933433410", sdb.MT.Root().BigInt().String())
629}
630
631func TestCreateAccountDepositMaxValue(t *testing.T) {
632 dir, err := ioutil.TempDir("", "tmpdb")
633 require.NoError(t, err)
634 defer assert.NoError(t, os.RemoveAll(dir))
635
636 nLevels := 16
637 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
638 Type: statedb.TypeBatchBuilder, NLevels: nLevels})
639 assert.NoError(t, err)
640
641 users := txsets.GenerateJsUsers(t)
642
643 daMaxF40 := common.Float40(0xFFFFFFFFFF)
644 daMaxBI, err := daMaxF40.BigInt()
645 require.NoError(t, err)
646 assert.Equal(t, "343597383670000000000000000000000000000000", daMaxBI.String())
647
648 daMax1F40 := common.Float40(0xFFFFFFFFFE)
649 require.NoError(t, err)
650 daMax1BI, err := daMax1F40.BigInt()
651 require.NoError(t, err)
652 assert.Equal(t, "343597383660000000000000000000000000000000", daMax1BI.String())
653
654 l1Txs := []common.L1Tx{
655 {
656 FromIdx: 0,
657 DepositAmount: daMaxBI,
658 Amount: big.NewInt(0),
659 TokenID: 1,
660 FromBJJ: users[0].BJJ.Public().Compress(),
661 FromEthAddr: users[0].Addr,
662 ToIdx: 0,
663 Type: common.TxTypeCreateAccountDeposit,
664 UserOrigin: true,
665 },
666 {
667 FromIdx: 0,
668 DepositAmount: daMax1BI,
669 Amount: big.NewInt(0),
670 TokenID: 1,
671 FromBJJ: users[1].BJJ.Public().Compress(),
672 FromEthAddr: users[1].Addr,
673 ToIdx: 0,
674 Type: common.TxTypeCreateAccountDeposit,
675 UserOrigin: true,
676 },
677 }
678
679 chainID := uint16(0)
680 config := Config{
681 NLevels: uint32(nLevels),
682 MaxTx: 3,
683 MaxL1Tx: 2,
684 MaxFeeTx: 2,
685 ChainID: chainID,
686 }
687 tp := NewTxProcessor(sdb, config)
688
689 _, err = tp.ProcessTxs(nil, l1Txs, nil, nil)
690 require.NoError(t, err)
691
692 // check balances
693 acc, err := sdb.GetAccount(common.Idx(256))
694 require.NoError(t, err)
695 assert.Equal(t, daMaxBI, acc.Balance)
696 acc, err = sdb.GetAccount(common.Idx(257))
697 require.NoError(t, err)
698 assert.Equal(t, daMax1BI, acc.Balance)
699}
700
701func initTestMultipleCoordIdxForTokenID(t *testing.T) (*TxProcessor, *til.Context, []common.BlockData) {
702 dir, err := ioutil.TempDir("", "tmpdb")
703 require.NoError(t, err)
704 defer assert.NoError(t, os.RemoveAll(dir))
705
706 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
707 Type: statedb.TypeBatchBuilder, NLevels: 32})
708 assert.NoError(t, err)
709
710 chainID := uint16(1)
711
712 // generate test transactions from test.SetBlockchain0 code
713 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
714
715 set := `
716 Type: Blockchain
717
718 CreateAccountDeposit(0) A: 200
719
720 > batchL1 // freeze L1User{1}
721
722 CreateAccountCoordinator(0) Coord
723 CreateAccountCoordinator(0) B
724
725 Transfer(0) A-B: 100 (126)
726
727 > batchL1 // forge L1User{1}, forge L1Coord{4}, forge L2{2}
728 > block
729 `
730 blocks, err := tc.GenerateBlocks(set)
731 require.NoError(t, err)
732
733 config := Config{
734 NLevels: 32,
735 MaxFeeTx: 64,
736 MaxTx: 512,
737 MaxL1Tx: 16,
738 ChainID: chainID,
739 }
740 tp := NewTxProcessor(sdb, config)
741 // batch1
742 _, err = tp.ProcessTxs(nil, nil, nil, nil) // to simulate the first batch from the Til set
743 require.NoError(t, err)
744
745 return tp, tc, blocks
746}
747
748func TestMultipleCoordIdxForTokenID(t *testing.T) {
749 // Check that ProcessTxs always uses the first occurrence of the
750 // CoordIdx for each TokenID
751
752 coordIdxs := []common.Idx{257, 257, 257}
753 tp, tc, blocks := initTestMultipleCoordIdxForTokenID(t)
754 l1UserTxs := til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
755 l1CoordTxs := blocks[0].Rollup.Batches[1].L1CoordinatorTxs
756 l1CoordTxs = append(l1CoordTxs, l1CoordTxs[0]) // duplicate the CoordAccount for TokenID=0
757 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
758 _, err := tp.ProcessTxs(coordIdxs, l1UserTxs, l1CoordTxs, l2Txs)
759 require.NoError(t, err)
760
761 checkBalanceByIdx(t, tp.s, 256, "90") // A
762 checkBalanceByIdx(t, tp.s, 257, "10") // Coord0
763 checkBalanceByIdx(t, tp.s, 258, "100") // B
764 checkBalanceByIdx(t, tp.s, 259, "0") // Coord0
765
766 // reset StateDB values
767 coordIdxs = []common.Idx{259, 257}
768 tp, tc, blocks = initTestMultipleCoordIdxForTokenID(t)
769 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
770 l1CoordTxs = blocks[0].Rollup.Batches[1].L1CoordinatorTxs
771 l1CoordTxs = append(l1CoordTxs, l1CoordTxs[0]) // duplicate the CoordAccount for TokenID=0
772 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
773 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, l1CoordTxs, l2Txs)
774 require.NoError(t, err)
775
776 checkBalanceByIdx(t, tp.s, 256, "90") // A
777 checkBalanceByIdx(t, tp.s, 257, "0") // Coord0
778 checkBalanceByIdx(t, tp.s, 258, "100") // B
779 checkBalanceByIdx(t, tp.s, 259, "10") // Coord0
780
781 // reset StateDB values
782 coordIdxs = []common.Idx{257, 259}
783 tp, tc, blocks = initTestMultipleCoordIdxForTokenID(t)
784 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
785 l1CoordTxs = blocks[0].Rollup.Batches[1].L1CoordinatorTxs
786 l1CoordTxs = append(l1CoordTxs, l1CoordTxs[0]) // duplicate the CoordAccount for TokenID=0
787 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
788 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, l1CoordTxs, l2Txs)
789 require.NoError(t, err)
790
791 checkBalanceByIdx(t, tp.s, 256, "90") // A
792 checkBalanceByIdx(t, tp.s, 257, "10") // Coord0
793 checkBalanceByIdx(t, tp.s, 258, "100") // B
794 checkBalanceByIdx(t, tp.s, 259, "0") // Coord0
795}
796
797func TestTwoExits(t *testing.T) {
798 // In the first part we generate a batch with two force exits for the
799 // same account of 20 each. The txprocessor output should be a single
800 // exitInfo with balance of 40.
801 dir, err := ioutil.TempDir("", "tmpdb")
802 require.NoError(t, err)
803 defer assert.NoError(t, os.RemoveAll(dir))
804
805 sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
806 Type: statedb.TypeSynchronizer, NLevels: 32})
807 assert.NoError(t, err)
808
809 chainID := uint16(1)
810
811 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
812
813 // Two exits for the same account. The tx processor should output a
814 // single exit with the accumulated exit balance
815 set := `
816 Type: Blockchain
817
818 CreateAccountDeposit(0) A: 100
819
820 > batchL1 // freeze L1User{1}
821 > batchL1 // forge L1User{1}
822
823 ForceExit(0) A: 20
824 ForceExit(0) A: 20
825
826 > batchL1 // freeze L1User{2}
827 > batchL1 // forge L1User{2}
828 > block
829 `
830 blocks, err := tc.GenerateBlocks(set)
831 require.NoError(t, err)
832 err = tc.FillBlocksExtra(blocks, &til.ConfigExtra{})
833 require.NoError(t, err)
834 err = tc.FillBlocksForgedL1UserTxs(blocks)
835 require.NoError(t, err)
836
837 // Sanity check
838 require.Equal(t, 1, len(blocks[0].Rollup.Batches[1].L1UserTxs))
839 require.Equal(t, 2, len(blocks[0].Rollup.Batches[3].L1UserTxs))
840
841 config := Config{
842 NLevels: 32,
843 MaxFeeTx: 64,
844 MaxTx: 512,
845 MaxL1Tx: 16,
846 ChainID: chainID,
847 }
848 tp := NewTxProcessor(sdb, config)
849 ptOuts := []*ProcessTxOutput{}
850 for _, block := range blocks {
851 for _, batch := range block.Rollup.Batches {
852 ptOut, err := tp.ProcessTxs(nil, batch.L1UserTxs, nil, nil)
853 require.NoError(t, err)
854 ptOuts = append(ptOuts, ptOut)
855 }
856 }
857
858 assert.Equal(t, 1, len(ptOuts[3].ExitInfos))
859 assert.Equal(t, big.NewInt(40), ptOuts[3].ExitInfos[0].Balance)
860 acc, err := sdb.GetAccount(256)
861 require.NoError(t, err)
862 assert.Equal(t, big.NewInt(60), acc.Balance)
863
864 // In the second part we start a fresh statedb and generate a batch
865 // with one force exit for the same account as before. The txprocessor
866 // output should be a single exitInfo with balance of 40, and the exit
867 // merkle tree proof should be equal to the previous one.
868
869 dir2, err := ioutil.TempDir("", "tmpdb")
870 require.NoError(t, err)
871 defer assert.NoError(t, os.RemoveAll(dir2))
872
873 sdb2, err := statedb.NewStateDB(statedb.Config{Path: dir2, Keep: 128,
874 Type: statedb.TypeSynchronizer, NLevels: 32})
875 assert.NoError(t, err)
876
877 tc = til.NewContext(chainID, common.RollupConstMaxL1UserTx)
878
879 // Single exit with balance of both exits in previous set. The exit
880 // root should match.
881 set2 := `
882 Type: Blockchain
883
884 CreateAccountDeposit(0) A: 100
885
886 > batchL1 // freeze L1User{1}
887 > batchL1 // forge L1User{1}
888
889 ForceExit(0) A: 40
890
891 > batchL1 // freeze L1User{2}
892 > batchL1 // forge L1User{2}
893 > block
894 `
895 blocks, err = tc.GenerateBlocks(set2)
896 require.NoError(t, err)
897 err = tc.FillBlocksExtra(blocks, &til.ConfigExtra{})
898 require.NoError(t, err)
899 err = tc.FillBlocksForgedL1UserTxs(blocks)
900 require.NoError(t, err)
901
902 tp = NewTxProcessor(sdb2, config)
903 ptOuts2 := []*ProcessTxOutput{}
904 for _, block := range blocks {
905 for _, batch := range block.Rollup.Batches {
906 ptOut, err := tp.ProcessTxs(nil, batch.L1UserTxs, nil, nil)
907 require.NoError(t, err)
908 ptOuts2 = append(ptOuts2, ptOut)
909 }
910 }
911
912 assert.Equal(t, ptOuts[3].ExitInfos[0].MerkleProof, ptOuts2[3].ExitInfos[0].MerkleProof)
913}