hermez-node/txprocessor/txprocessor_test.go
txprocessor/txprocessor_test.goRaw
1package txprocessor
2
3import (
4 "encoding/binary"
5 "encoding/hex"
6 "fmt"
7 "io/ioutil"
8 "math/big"
9 "os"
10 "testing"
11
12 ethCommon "github.com/ethereum/go-ethereum/common"
13 "github.com/hermeznetwork/hermez-node/common"
14 "github.com/hermeznetwork/hermez-node/db/statedb"
15 "github.com/hermeznetwork/hermez-node/log"
16 "github.com/hermeznetwork/hermez-node/test/til"
17 "github.com/hermeznetwork/hermez-node/test/txsets"
18 "github.com/stretchr/testify/assert"
19 "github.com/stretchr/testify/require"
20)
21
22func checkBalance(t *testing.T, tc *til.Context, sdb *statedb.StateDB, username string, tokenid int, expected string) {
23 idx := tc.Users[username].Accounts[common.TokenID(tokenid)].Idx
24 acc, err := sdb.GetAccount(idx)
25 require.NoError(t, err)
26 assert.Equal(t, expected, acc.Balance.String())
27}
28
29func checkBalanceByIdx(t *testing.T, sdb *statedb.StateDB, idx common.Idx, expected string) {
30 acc, err := sdb.GetAccount(idx)
31 require.NoError(t, err)
32 assert.Equal(t, expected, acc.Balance.String())
33}
34
35func TestComputeEffectiveAmounts(t *testing.T) {
36 dir, err := ioutil.TempDir("", "tmpdb")
37 require.NoError(t, err)
38 defer assert.NoError(t, os.RemoveAll(dir))
39
40 sdb, err := statedb.NewStateDB(dir, 128, statedb.TypeSynchronizer, 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(dir, 128, statedb.TypeSynchronizer, 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(dir, 128, statedb.TypeSynchronizer, 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, 2, len(ptOut.ExitInfos)) // 2, as previous batch was without L1UserTxs, and has pending the 'ForceExit(1) A: 5'
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(dir, 128, statedb.TypeBatchBuilder, 32)
494 assert.NoError(t, err)
495
496 chainID := uint16(0)
497 // generate test transactions from test.SetBlockchain0 code
498 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
499 blocks, err := tc.GenerateBlocks(txsets.SetBlockchain0)
500 require.NoError(t, err)
501
502 // Coordinator Idx where to send the fees
503 coordIdxs := []common.Idx{256, 257, 258, 259}
504
505 // Idx of user 'A'
506 idxA1 := tc.Users["A"].Accounts[common.TokenID(1)].Idx
507
508 config := Config{
509 NLevels: 32,
510 MaxFeeTx: 64,
511 MaxTx: 512,
512 MaxL1Tx: 32,
513 ChainID: chainID,
514 }
515 tp := NewTxProcessor(sdb, config)
516
517 // Process the 1st batch, which contains the L1CoordinatorTxs necessary
518 // to create the Coordinator accounts to receive the fees
519 log.Debug("block:0 batch:1, only L1CoordinatorTxs")
520 ptOut, err := tp.ProcessTxs(nil, nil, blocks[0].Rollup.Batches[0].L1CoordinatorTxs, nil)
521 require.NoError(t, err)
522 // expect 0 at CreatedAccount, as is only computed when StateDB.Type==TypeSynchronizer
523 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
524
525 log.Debug("block:0 batch:2")
526 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
527 ptOut, err = tp.ProcessTxs(coordIdxs, blocks[0].Rollup.L1UserTxs, blocks[0].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
528 require.NoError(t, err)
529 assert.Equal(t, 0, len(ptOut.ExitInfos))
530 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
531 acc, err := sdb.GetAccount(idxA1)
532 require.NoError(t, err)
533 assert.Equal(t, "50", acc.Balance.String())
534
535 log.Debug("block:0 batch:3")
536 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[2].L2Txs)
537 ptOut, err = tp.ProcessTxs(coordIdxs, nil, blocks[0].Rollup.Batches[2].L1CoordinatorTxs, l2Txs)
538 require.NoError(t, err)
539 assert.Equal(t, 0, len(ptOut.ExitInfos))
540 assert.Equal(t, 0, len(ptOut.CreatedAccounts))
541 acc, err = sdb.GetAccount(idxA1)
542 require.NoError(t, err)
543 assert.Equal(t, "35", acc.Balance.String())
544
545 log.Debug("block:1 batch:1")
546 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[0].L2Txs)
547 _, err = tp.ProcessTxs(coordIdxs, nil, blocks[1].Rollup.Batches[0].L1CoordinatorTxs, l2Txs)
548 require.NoError(t, err)
549 acc, err = sdb.GetAccount(idxA1)
550 require.NoError(t, err)
551 assert.Equal(t, "57", acc.Balance.String())
552
553 log.Debug("block:1 batch:2")
554 l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Rollup.Batches[1].L2Txs)
555 _, err = tp.ProcessTxs(coordIdxs, blocks[1].Rollup.L1UserTxs, blocks[1].Rollup.Batches[1].L1CoordinatorTxs, l2Txs)
556 require.NoError(t, err)
557 acc, err = sdb.GetAccount(idxA1)
558 assert.NoError(t, err)
559 assert.Equal(t, "77", acc.Balance.String())
560
561 idxB0 := tc.Users["C"].Accounts[common.TokenID(0)].Idx
562 acc, err = sdb.GetAccount(idxB0)
563 require.NoError(t, err)
564 assert.Equal(t, "51", acc.Balance.String())
565
566 // get balance of Coordinator account for TokenID==0
567 acc, err = sdb.GetAccount(common.Idx(256))
568 require.NoError(t, err)
569 assert.Equal(t, common.TokenID(0), acc.TokenID)
570 assert.Equal(t, "2", acc.Balance.String())
571 acc, err = sdb.GetAccount(common.Idx(257))
572 require.NoError(t, err)
573 assert.Equal(t, common.TokenID(1), acc.TokenID)
574 assert.Equal(t, "2", acc.Balance.String())
575
576 assert.Equal(t, "18894163991492573893706613133132363559300580460789469708968288074813925659539", sdb.MT.Root().BigInt().String())
577}
578
579func TestProcessTxsRootTestVectors(t *testing.T) {
580 dir, err := ioutil.TempDir("", "tmpdb")
581 require.NoError(t, err)
582 defer assert.NoError(t, os.RemoveAll(dir))
583
584 sdb, err := statedb.NewStateDB(dir, 128, statedb.TypeBatchBuilder, 32)
585 assert.NoError(t, err)
586
587 // same values than in the js test
588 bjj0, err := common.BJJFromStringWithChecksum("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7")
589 assert.NoError(t, err)
590 l1Txs := []common.L1Tx{
591 {
592 FromIdx: 0,
593 DepositAmount: big.NewInt(16000000),
594 Amount: big.NewInt(0),
595 TokenID: 1,
596 FromBJJ: bjj0,
597 FromEthAddr: ethCommon.HexToAddress("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf"),
598 ToIdx: 0,
599 Type: common.TxTypeCreateAccountDeposit,
600 UserOrigin: true,
601 },
602 }
603 l2Txs := []common.PoolL2Tx{
604 {
605 FromIdx: 256,
606 ToIdx: 256,
607 TokenID: 1,
608 Amount: big.NewInt(1000),
609 Nonce: 0,
610 Fee: 126,
611 Type: common.TxTypeTransfer,
612 },
613 }
614
615 chainID := uint16(0)
616 config := Config{
617 NLevels: 32,
618 MaxFeeTx: 8,
619 MaxTx: 32,
620 MaxL1Tx: 16,
621 ChainID: chainID,
622 }
623 tp := NewTxProcessor(sdb, config)
624 _, err = tp.ProcessTxs(nil, l1Txs, nil, l2Txs)
625 require.NoError(t, err)
626 assert.Equal(t, "9827704113668630072730115158977131501210702363656902211840117643154933433410", sdb.MT.Root().BigInt().String())
627}
628
629func TestCreateAccountDepositMaxValue(t *testing.T) {
630 dir, err := ioutil.TempDir("", "tmpdb")
631 require.NoError(t, err)
632 defer assert.NoError(t, os.RemoveAll(dir))
633
634 nLevels := 16
635 sdb, err := statedb.NewStateDB(dir, 128, statedb.TypeBatchBuilder, nLevels)
636 assert.NoError(t, err)
637
638 users := txsets.GenerateJsUsers(t)
639
640 daMaxHex, err := hex.DecodeString("FFFF")
641 require.NoError(t, err)
642 daMaxF16 := common.Float16(binary.BigEndian.Uint16(daMaxHex))
643 daMaxBI := daMaxF16.BigInt()
644 assert.Equal(t, "10235000000000000000000000000000000", daMaxBI.String())
645
646 daMax1Hex, err := hex.DecodeString("FFFE")
647 require.NoError(t, err)
648 daMax1F16 := common.Float16(binary.BigEndian.Uint16(daMax1Hex))
649 daMax1BI := daMax1F16.BigInt()
650 assert.Equal(t, "10225000000000000000000000000000000", daMax1BI.String())
651
652 l1Txs := []common.L1Tx{
653 {
654 FromIdx: 0,
655 DepositAmount: daMaxBI,
656 Amount: big.NewInt(0),
657 TokenID: 1,
658 FromBJJ: users[0].BJJ.Public().Compress(),
659 FromEthAddr: users[0].Addr,
660 ToIdx: 0,
661 Type: common.TxTypeCreateAccountDeposit,
662 UserOrigin: true,
663 },
664 {
665 FromIdx: 0,
666 DepositAmount: daMax1BI,
667 Amount: big.NewInt(0),
668 TokenID: 1,
669 FromBJJ: users[1].BJJ.Public().Compress(),
670 FromEthAddr: users[1].Addr,
671 ToIdx: 0,
672 Type: common.TxTypeCreateAccountDeposit,
673 UserOrigin: true,
674 },
675 }
676
677 chainID := uint16(0)
678 config := Config{
679 NLevels: uint32(nLevels),
680 MaxTx: 3,
681 MaxL1Tx: 2,
682 MaxFeeTx: 2,
683 ChainID: chainID,
684 }
685 tp := NewTxProcessor(sdb, config)
686
687 _, err = tp.ProcessTxs(nil, l1Txs, nil, nil)
688 require.NoError(t, err)
689
690 // check balances
691 acc, err := sdb.GetAccount(common.Idx(256))
692 require.NoError(t, err)
693 assert.Equal(t, daMaxBI, acc.Balance)
694 acc, err = sdb.GetAccount(common.Idx(257))
695 require.NoError(t, err)
696 assert.Equal(t, daMax1BI, acc.Balance)
697}
698
699func initTestMultipleCoordIdxForTokenID(t *testing.T) (*TxProcessor, *til.Context, []common.BlockData) {
700 dir, err := ioutil.TempDir("", "tmpdb")
701 require.NoError(t, err)
702 defer assert.NoError(t, os.RemoveAll(dir))
703
704 sdb, err := statedb.NewStateDB(dir, 128, statedb.TypeBatchBuilder, 32)
705 assert.NoError(t, err)
706
707 chainID := uint16(1)
708
709 // generate test transactions from test.SetBlockchain0 code
710 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
711
712 set := `
713 Type: Blockchain
714
715 CreateAccountDeposit(0) A: 200
716
717 > batchL1 // freeze L1User{1}
718
719 CreateAccountCoordinator(0) Coord
720 CreateAccountCoordinator(0) B
721
722 Transfer(0) A-B: 100 (126)
723
724 > batchL1 // forge L1User{1}, forge L1Coord{4}, forge L2{2}
725 > block
726 `
727 blocks, err := tc.GenerateBlocks(set)
728 require.NoError(t, err)
729
730 config := Config{
731 NLevels: 32,
732 MaxFeeTx: 64,
733 MaxTx: 512,
734 MaxL1Tx: 16,
735 ChainID: chainID,
736 }
737 tp := NewTxProcessor(sdb, config)
738 // batch1
739 _, err = tp.ProcessTxs(nil, nil, nil, nil) // to simulate the first batch from the Til set
740 require.NoError(t, err)
741
742 return tp, tc, blocks
743}
744
745func TestMultipleCoordIdxForTokenID(t *testing.T) {
746 // Check that ProcessTxs always uses the first occurrence of the
747 // CoordIdx for each TokenID
748
749 coordIdxs := []common.Idx{257, 257, 257}
750 tp, tc, blocks := initTestMultipleCoordIdxForTokenID(t)
751 l1UserTxs := til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
752 l1CoordTxs := blocks[0].Rollup.Batches[1].L1CoordinatorTxs
753 l1CoordTxs = append(l1CoordTxs, l1CoordTxs[0]) // duplicate the CoordAccount for TokenID=0
754 l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
755 _, err := tp.ProcessTxs(coordIdxs, l1UserTxs, l1CoordTxs, l2Txs)
756 require.NoError(t, err)
757
758 checkBalanceByIdx(t, tp.s, 256, "90") // A
759 checkBalanceByIdx(t, tp.s, 257, "10") // Coord0
760 checkBalanceByIdx(t, tp.s, 258, "100") // B
761 checkBalanceByIdx(t, tp.s, 259, "0") // Coord0
762
763 // reset StateDB values
764 coordIdxs = []common.Idx{259, 257}
765 tp, tc, blocks = initTestMultipleCoordIdxForTokenID(t)
766 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
767 l1CoordTxs = blocks[0].Rollup.Batches[1].L1CoordinatorTxs
768 l1CoordTxs = append(l1CoordTxs, l1CoordTxs[0]) // duplicate the CoordAccount for TokenID=0
769 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
770 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, l1CoordTxs, l2Txs)
771 require.NoError(t, err)
772
773 checkBalanceByIdx(t, tp.s, 256, "90") // A
774 checkBalanceByIdx(t, tp.s, 257, "0") // Coord0
775 checkBalanceByIdx(t, tp.s, 258, "100") // B
776 checkBalanceByIdx(t, tp.s, 259, "10") // Coord0
777
778 // reset StateDB values
779 coordIdxs = []common.Idx{257, 259}
780 tp, tc, blocks = initTestMultipleCoordIdxForTokenID(t)
781 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
782 l1CoordTxs = blocks[0].Rollup.Batches[1].L1CoordinatorTxs
783 l1CoordTxs = append(l1CoordTxs, l1CoordTxs[0]) // duplicate the CoordAccount for TokenID=0
784 l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Rollup.Batches[1].L2Txs)
785 _, err = tp.ProcessTxs(coordIdxs, l1UserTxs, l1CoordTxs, l2Txs)
786 require.NoError(t, err)
787
788 checkBalanceByIdx(t, tp.s, 256, "90") // A
789 checkBalanceByIdx(t, tp.s, 257, "10") // Coord0
790 checkBalanceByIdx(t, tp.s, 258, "100") // B
791 checkBalanceByIdx(t, tp.s, 259, "0") // Coord0
792}
793
794func TestTwoExits(t *testing.T) {
795 dir, err := ioutil.TempDir("", "tmpdb")
796 require.NoError(t, err)
797 defer assert.NoError(t, os.RemoveAll(dir))
798
799 sdb, err := statedb.NewStateDB(dir, 128, statedb.TypeSynchronizer, 32)
800 assert.NoError(t, err)
801
802 chainID := uint16(1)
803
804 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
805
806 // Two exits for the same account. The tx processor should output a
807 // single exit with the accumulated exit balance
808 set := `
809 Type: Blockchain
810
811 CreateAccountDeposit(0) A: 100
812
813 > batchL1 // freeze L1User{1}
814 > batchL1 // forge L1User{1}
815
816 ForceExit(0) A: 20
817 ForceExit(0) A: 20
818
819 > batchL1 // freeze L1User{2}
820 > batchL1 // forge L1User{2}
821 > block
822 `
823 blocks, err := tc.GenerateBlocks(set)
824 require.NoError(t, err)
825 err = tc.FillBlocksExtra(blocks, &til.ConfigExtra{})
826 require.NoError(t, err)
827 err = tc.FillBlocksForgedL1UserTxs(blocks)
828 require.NoError(t, err)
829
830 // Sanity check
831 require.Equal(t, 1, len(blocks[0].Rollup.Batches[1].L1UserTxs))
832 require.Equal(t, 2, len(blocks[0].Rollup.Batches[3].L1UserTxs))
833
834 config := Config{
835 NLevels: 32,
836 MaxFeeTx: 64,
837 MaxTx: 512,
838 MaxL1Tx: 16,
839 ChainID: chainID,
840 }
841 tp := NewTxProcessor(sdb, config)
842 ptOuts := []*ProcessTxOutput{}
843 for _, block := range blocks {
844 for _, batch := range block.Rollup.Batches {
845 // fmt.Printf("Batch %v\n%#v\n", batch.Batch.BatchNum, batch.L1UserTxs)
846 ptOut, err := tp.ProcessTxs(nil, batch.L1UserTxs, nil, nil)
847 require.NoError(t, err)
848 fmt.Printf("Exits (batch %v)\n%#v\n", batch.Batch.BatchNum, ptOut.ExitInfos)
849 ptOuts = append(ptOuts, ptOut)
850 }
851 }
852
853 assert.Equal(t, 1, len(ptOuts[3].ExitInfos))
854 assert.Equal(t, big.NewInt(40), ptOuts[3].ExitInfos[0].Balance)
855 acc, err := sdb.GetAccount(256)
856 require.NoError(t, err)
857 assert.Equal(t, big.NewInt(60), acc.Balance)
858}