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