hermez-node/test/til/txs.go
test/til/txs.goRaw
1package til
2
3import (
4 "crypto/ecdsa"
5 "fmt"
6 "math/big"
7 "strconv"
8 "strings"
9 "time"
10
11 ethCommon "github.com/ethereum/go-ethereum/common"
12 ethCrypto "github.com/ethereum/go-ethereum/crypto"
13 "github.com/hermeznetwork/hermez-node/common"
14 "github.com/hermeznetwork/hermez-node/log"
15 "github.com/iden3/go-iden3-crypto/babyjub"
16)
17
18func newBatchData(batchNum int) common.BatchData {
19 return common.BatchData{
20 L1CoordinatorTxs: []common.L1Tx{},
21 L2Txs: []common.L2Tx{},
22 Batch: common.Batch{
23 BatchNum: common.BatchNum(batchNum),
24 StateRoot: big.NewInt(0), ExitRoot: big.NewInt(0)},
25 }
26}
27
28func newBlock(blockNum int64) common.BlockData {
29 return common.BlockData{
30 Block: common.Block{
31 EthBlockNum: blockNum,
32 },
33 L1UserTxs: []common.L1Tx{},
34 }
35}
36
37// Context contains the data of the test
38type Context struct {
39 Instructions []instruction
40 userNames []string
41 Users map[string]*User
42 LastRegisteredTokenID common.TokenID
43 l1CreatedAccounts map[string]*Account
44
45 // rollupConstMaxL1UserTx Maximum L1-user transactions allowed to be queued in a batch
46 rollupConstMaxL1UserTx int
47
48 idx int
49 currBlock common.BlockData
50 currBatch common.BatchData
51 currBatchNum int
52 Queues [][]L1Tx
53 ToForgeNum int
54 openToForge int
55 currBatchTest struct {
56 l1CoordinatorTxs []L1Tx
57 l2Txs []L2Tx
58 }
59 blockNum int64
60}
61
62// NewContext returns a new Context
63func NewContext(rollupConstMaxL1UserTx int) *Context {
64 currBatchNum := 1 // The protocol defines the first batchNum to be 1
65 return &Context{
66 Users: make(map[string]*User),
67 l1CreatedAccounts: make(map[string]*Account),
68 LastRegisteredTokenID: 0,
69
70 rollupConstMaxL1UserTx: rollupConstMaxL1UserTx,
71 idx: common.UserThreshold,
72 // We use some placeholder values for StateRoot and ExitTree
73 // because these values will never be nil
74 currBlock: newBlock(2), //nolint:gomnd
75 currBatch: newBatchData(currBatchNum),
76 currBatchNum: currBatchNum,
77 // start with 2 queues, one for toForge, and the other for openToForge
78 Queues: make([][]L1Tx, 2),
79 ToForgeNum: 0,
80 openToForge: 1,
81 //nolint:gomnd
82 blockNum: 2, // rollup genesis blockNum
83 }
84}
85
86// Account contains the data related to the account for a specific TokenID of a User
87type Account struct {
88 Idx common.Idx
89 Nonce common.Nonce
90}
91
92// User contains the data related to a testing user
93type User struct {
94 BJJ *babyjub.PrivateKey
95 Addr ethCommon.Address
96 Accounts map[common.TokenID]*Account
97}
98
99// L1Tx is the data structure used internally for transaction test generation,
100// which contains a common.L1Tx data plus some intermediate data for the
101// transaction generation.
102type L1Tx struct {
103 lineNum int
104 fromIdxName string
105 toIdxName string
106
107 L1Tx common.L1Tx
108}
109
110// L2Tx is the data structure used internally for transaction test generation,
111// which contains a common.L2Tx data plus some intermediate data for the
112// transaction generation.
113type L2Tx struct {
114 lineNum int
115 fromIdxName string
116 toIdxName string
117 tokenID common.TokenID
118 L2Tx common.L2Tx
119}
120
121// GenerateBlocks returns an array of BlockData for a given set. It uses the
122// users (keys & nonces) of the Context.
123func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) {
124 parser := newParser(strings.NewReader(set))
125 parsedSet, err := parser.parse()
126 if err != nil {
127 return nil, err
128 }
129 if parsedSet.typ != setTypeBlockchain {
130 return nil, fmt.Errorf("Expected set type: %s, found: %s", setTypeBlockchain, parsedSet.typ)
131 }
132
133 tc.Instructions = parsedSet.instructions
134 tc.userNames = parsedSet.users
135
136 tc.generateKeys(tc.userNames)
137
138 var blocks []common.BlockData
139 for _, inst := range parsedSet.instructions {
140 switch inst.typ {
141 case txTypeCreateAccountDepositCoordinator: // tx source: L1CoordinatorTx
142 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
143 log.Error(err)
144 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
145 }
146 tx := common.L1Tx{
147 FromEthAddr: tc.Users[inst.from].Addr,
148 FromBJJ: tc.Users[inst.from].BJJ.Public(),
149 TokenID: inst.tokenID,
150 Amount: big.NewInt(0),
151 LoadAmount: big.NewInt(int64(inst.loadAmount)),
152 Type: common.TxTypeCreateAccountDeposit, // as txTypeCreateAccountDepositCoordinator is not valid oustide Til package
153 }
154 testTx := L1Tx{
155 lineNum: inst.lineNum,
156 fromIdxName: inst.from,
157 L1Tx: tx,
158 }
159
160 tc.currBatchTest.l1CoordinatorTxs = append(tc.currBatchTest.l1CoordinatorTxs, testTx)
161 case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer: // tx source: L1UserTx
162 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
163 log.Error(err)
164 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
165 }
166 tx := common.L1Tx{
167 FromEthAddr: tc.Users[inst.from].Addr,
168 FromBJJ: tc.Users[inst.from].BJJ.Public(),
169 TokenID: inst.tokenID,
170 Amount: big.NewInt(0),
171 LoadAmount: big.NewInt(int64(inst.loadAmount)),
172 Type: inst.typ,
173 }
174 if inst.typ == common.TxTypeCreateAccountDepositTransfer {
175 tx.Amount = big.NewInt(int64(inst.amount))
176 }
177 testTx := L1Tx{
178 lineNum: inst.lineNum,
179 fromIdxName: inst.from,
180 toIdxName: inst.to,
181 L1Tx: tx,
182 }
183 if err := tc.addToL1Queue(testTx); err != nil {
184 return nil, err
185 }
186 case common.TxTypeDeposit, common.TxTypeDepositTransfer: // tx source: L1UserTx
187 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
188 log.Error(err)
189 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
190 }
191 if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
192 log.Error(err)
193 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
194 }
195 tx := common.L1Tx{
196 TokenID: inst.tokenID,
197 Amount: big.NewInt(0),
198 LoadAmount: big.NewInt(int64(inst.loadAmount)),
199 Type: inst.typ,
200 }
201 if inst.typ == common.TxTypeDepositTransfer {
202 tx.Amount = big.NewInt(int64(inst.amount))
203 }
204 testTx := L1Tx{
205 lineNum: inst.lineNum,
206 fromIdxName: inst.from,
207 toIdxName: inst.to,
208 L1Tx: tx,
209 }
210 if err := tc.addToL1Queue(testTx); err != nil {
211 return nil, err
212 }
213 case common.TxTypeTransfer: // L2Tx
214 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
215 log.Error(err)
216 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
217 }
218 tx := common.L2Tx{
219 Amount: big.NewInt(int64(inst.amount)),
220 Fee: common.FeeSelector(inst.fee),
221 Type: common.TxTypeTransfer,
222 EthBlockNum: tc.blockNum,
223 }
224 tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
225 testTx := L2Tx{
226 lineNum: inst.lineNum,
227 fromIdxName: inst.from,
228 toIdxName: inst.to,
229 tokenID: inst.tokenID,
230 L2Tx: tx,
231 }
232 tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx)
233 case common.TxTypeForceTransfer: // tx source: L1UserTx
234 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
235 log.Error(err)
236 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
237 }
238 tx := common.L1Tx{
239 TokenID: inst.tokenID,
240 Amount: big.NewInt(int64(inst.amount)),
241 LoadAmount: big.NewInt(0),
242 Type: common.TxTypeForceTransfer,
243 }
244 testTx := L1Tx{
245 lineNum: inst.lineNum,
246 fromIdxName: inst.from,
247 toIdxName: inst.to,
248 L1Tx: tx,
249 }
250 if err := tc.addToL1Queue(testTx); err != nil {
251 return nil, err
252 }
253 case common.TxTypeExit: // tx source: L2Tx
254 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
255 log.Error(err)
256 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
257 }
258 tx := common.L2Tx{
259 ToIdx: common.Idx(1), // as is an Exit
260 Fee: common.FeeSelector(inst.fee),
261 Amount: big.NewInt(int64(inst.amount)),
262 Type: common.TxTypeExit,
263 EthBlockNum: tc.blockNum,
264 }
265 tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
266 testTx := L2Tx{
267 lineNum: inst.lineNum,
268 fromIdxName: inst.from,
269 toIdxName: inst.to,
270 tokenID: inst.tokenID,
271 L2Tx: tx,
272 }
273 tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx)
274 case common.TxTypeForceExit: // tx source: L1UserTx
275 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
276 log.Error(err)
277 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
278 }
279 tx := common.L1Tx{
280 ToIdx: common.Idx(1), // as is an Exit
281 TokenID: inst.tokenID,
282 Amount: big.NewInt(int64(inst.amount)),
283 LoadAmount: big.NewInt(0),
284 Type: common.TxTypeForceExit,
285 }
286 testTx := L1Tx{
287 lineNum: inst.lineNum,
288 fromIdxName: inst.from,
289 toIdxName: inst.to,
290 L1Tx: tx,
291 }
292 if err := tc.addToL1Queue(testTx); err != nil {
293 return nil, err
294 }
295 case typeNewBatch:
296 if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
297 return nil, err
298 }
299 if err = tc.setIdxs(); err != nil {
300 log.Error(err)
301 return nil, err
302 }
303 case typeNewBatchL1:
304 // for each L1UserTx of the Queues[ToForgeNum], calculate the Idx
305 if err = tc.calculateIdxForL1Txs(false, tc.Queues[tc.ToForgeNum]); err != nil {
306 return nil, err
307 }
308 if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
309 return nil, err
310 }
311 tc.currBatch.L1Batch = true
312 if err = tc.setIdxs(); err != nil {
313 log.Error(err)
314 return nil, err
315 }
316 toForgeL1TxsNum := int64(tc.openToForge)
317 tc.currBatch.Batch.ForgeL1TxsNum = &toForgeL1TxsNum
318 // advance batch
319 tc.ToForgeNum++
320 if tc.ToForgeNum == tc.openToForge {
321 tc.openToForge++
322 newQueue := []L1Tx{}
323 tc.Queues = append(tc.Queues, newQueue)
324 }
325 case typeNewBlock:
326 blocks = append(blocks, tc.currBlock)
327 tc.blockNum++
328 tc.currBlock = newBlock(tc.blockNum)
329 case typeAddToken:
330 newToken := common.Token{
331 EthAddr: ethCommon.BigToAddress(big.NewInt(int64(inst.tokenID * 100))), //nolint:gomnd
332 // Name: fmt.Sprintf("Token %d", inst.tokenID),
333 // Symbol: fmt.Sprintf("TK%d", inst.tokenID),
334 // Decimals: 18,
335 TokenID: inst.tokenID,
336 EthBlockNum: tc.blockNum,
337 }
338 if inst.tokenID != tc.LastRegisteredTokenID+1 {
339 return nil, fmt.Errorf("Line %d: AddToken TokenID should be sequential, expected TokenID: %d, defined TokenID: %d", inst.lineNum, tc.LastRegisteredTokenID+1, inst.tokenID)
340 }
341 tc.LastRegisteredTokenID++
342 tc.currBlock.AddedTokens = append(tc.currBlock.AddedTokens, newToken)
343 default:
344 return nil, fmt.Errorf("Line %d: Unexpected type: %s", inst.lineNum, inst.typ)
345 }
346 }
347
348 return blocks, nil
349}
350
351// calculateIdxsForL1Txs calculates new Idx for new created accounts. If
352// 'isCoordinatorTxs==true', adds the tx to tc.currBatch.L1CoordinatorTxs.
353func (tc *Context) calculateIdxForL1Txs(isCoordinatorTxs bool, txs []L1Tx) error {
354 // for each batch.L1CoordinatorTxs of the Queues[ToForgeNum], calculate the Idx
355 for i := 0; i < len(txs); i++ {
356 tx := txs[i]
357 if tx.L1Tx.Type == common.TxTypeCreateAccountDeposit || tx.L1Tx.Type == common.TxTypeCreateAccountDepositTransfer {
358 if tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] != nil { // if account already exists, return error
359 return fmt.Errorf("Can not create same account twice (same User (%s) & same TokenID (%d)) (this is a design property of Til)", tx.fromIdxName, tx.L1Tx.TokenID)
360 }
361 tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] = &Account{
362 Idx: common.Idx(tc.idx),
363 Nonce: common.Nonce(0),
364 }
365 tc.l1CreatedAccounts[idxTokenIDToString(tx.fromIdxName, tx.L1Tx.TokenID)] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
366 tc.idx++
367 }
368 if isCoordinatorTxs {
369 tc.currBatch.L1CoordinatorTxs = append(tc.currBatch.L1CoordinatorTxs, tx.L1Tx)
370 }
371 }
372 return nil
373}
374
375// setIdxs sets the Idxs to the transactions of the tc.currBatch
376func (tc *Context) setIdxs() error {
377 // once Idxs are calculated, update transactions to use the new Idxs
378 for i := 0; i < len(tc.currBatchTest.l2Txs); i++ {
379 testTx := &tc.currBatchTest.l2Txs[i]
380
381 if tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID] == nil {
382 return fmt.Errorf("Line %d: %s from User %s for TokenID %d while account not created yet", testTx.lineNum, testTx.L2Tx.Type, testTx.fromIdxName, testTx.tokenID)
383 }
384 if testTx.L2Tx.Type == common.TxTypeTransfer {
385 if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(testTx.toIdxName, testTx.tokenID)]; !ok {
386 return fmt.Errorf("Line %d: Can not create Transfer for a non existing account. Batch %d, ToIdx name: %s, TokenID: %d", testTx.lineNum, tc.currBatchNum, testTx.toIdxName, testTx.tokenID)
387 }
388 }
389 tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce++
390 // next line is commented to avoid Blockchain L2Txs to have
391 // Nonce different from 0, as from Blockchain those
392 // transactions will come without Nonce
393 // testTx.L2Tx.Nonce = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce
394
395 // set real Idx
396 testTx.L2Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Idx
397 if testTx.L2Tx.Type == common.TxTypeTransfer {
398 testTx.L2Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.tokenID].Idx
399 }
400 // in case Type==Exit, ToIdx=1, already set at the
401 // GenerateBlocks main switch inside TxTypeExit case
402
403 nTx, err := common.NewL2Tx(&testTx.L2Tx)
404 if err != nil {
405 return fmt.Errorf("Line %d: %s", testTx.lineNum, err.Error())
406 }
407 testTx.L2Tx = *nTx
408
409 tc.currBatch.L2Txs = append(tc.currBatch.L2Txs, testTx.L2Tx)
410 }
411
412 tc.currBatch.Batch.LastIdx = int64(tc.idx - 1) // `-1` because tc.idx is the next available idx
413 tc.currBlock.Batches = append(tc.currBlock.Batches, tc.currBatch)
414 tc.currBatchNum++
415 tc.currBatch = newBatchData(tc.currBatchNum)
416 tc.currBatchTest.l1CoordinatorTxs = nil
417 tc.currBatchTest.l2Txs = nil
418 return nil
419}
420
421// addToL1Queue adds the L1Tx into the queue that is open and has space
422func (tc *Context) addToL1Queue(tx L1Tx) error {
423 if len(tc.Queues[tc.openToForge]) >= tc.rollupConstMaxL1UserTx {
424 // if current OpenToForge queue reached its Max, move into a
425 // new queue
426 tc.openToForge++
427 newQueue := []L1Tx{}
428 tc.Queues = append(tc.Queues, newQueue)
429 }
430 // Fill L1UserTx specific parameters
431 tx.L1Tx.UserOrigin = true
432 toForgeL1TxsNum := int64(tc.openToForge)
433 tx.L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
434 tx.L1Tx.EthBlockNum = tc.blockNum
435 tx.L1Tx.Position = len(tc.Queues[tc.openToForge])
436
437 // When an L1UserTx is generated, all idxs must be available (except when idx == 0 or idx == 1)
438 if tx.L1Tx.Type != common.TxTypeCreateAccountDeposit && tx.L1Tx.Type != common.TxTypeCreateAccountDepositTransfer {
439 tx.L1Tx.FromIdx = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID].Idx
440 }
441 tx.L1Tx.FromEthAddr = tc.Users[tx.fromIdxName].Addr
442 tx.L1Tx.FromBJJ = tc.Users[tx.fromIdxName].BJJ.Public()
443 if tx.toIdxName == "" {
444 tx.L1Tx.ToIdx = common.Idx(0)
445 } else {
446 account, ok := tc.Users[tx.toIdxName].Accounts[tx.L1Tx.TokenID]
447 if !ok {
448 return fmt.Errorf("Line %d: Transfer to User: %s, for TokenID: %d, "+
449 "while account not created yet", tx.lineNum, tx.toIdxName, tx.L1Tx.TokenID)
450 }
451 tx.L1Tx.ToIdx = account.Idx
452 }
453 if tx.L1Tx.Type == common.TxTypeForceExit {
454 tx.L1Tx.ToIdx = common.Idx(1)
455 }
456 nTx, err := common.NewL1Tx(&tx.L1Tx)
457 if err != nil {
458 return fmt.Errorf("Line %d: %s", tx.lineNum, err.Error())
459 }
460 tx.L1Tx = *nTx
461
462 tc.Queues[tc.openToForge] = append(tc.Queues[tc.openToForge], tx)
463 tc.currBlock.L1UserTxs = append(tc.currBlock.L1UserTxs, tx.L1Tx)
464
465 return nil
466}
467
468func (tc *Context) checkIfAccountExists(tf string, inst instruction) error {
469 if tc.Users[tf].Accounts[inst.tokenID] == nil {
470 return fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.typ, tf, inst.tokenID)
471 }
472 return nil
473}
474func (tc *Context) checkIfTokenIsRegistered(inst instruction) error {
475 if inst.tokenID > tc.LastRegisteredTokenID {
476 return fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.typ, inst.tokenID, tc.LastRegisteredTokenID)
477 }
478 return nil
479}
480
481// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
482// uses the users (keys) of the Context.
483func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
484 parser := newParser(strings.NewReader(set))
485 parsedSet, err := parser.parse()
486 if err != nil {
487 return nil, err
488 }
489 if parsedSet.typ != setTypePoolL2 {
490 return nil, fmt.Errorf("Expected set type: %s, found: %s", setTypePoolL2, parsedSet.typ)
491 }
492
493 tc.Instructions = parsedSet.instructions
494 tc.userNames = parsedSet.users
495
496 tc.generateKeys(tc.userNames)
497
498 txs := []common.PoolL2Tx{}
499 for _, inst := range tc.Instructions {
500 switch inst.typ {
501 case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
502 if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
503 log.Error(err)
504 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
505 }
506 if inst.typ == common.TxTypeTransfer {
507 // if TxTypeTransfer, need to exist the ToIdx account
508 if err := tc.checkIfAccountExists(inst.to, inst); err != nil {
509 log.Error(err)
510 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
511 }
512 }
513 tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
514 // if account of receiver does not exist, don't use
515 // ToIdx, and use only ToEthAddr & ToBJJ
516 tx := common.PoolL2Tx{
517 FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
518 TokenID: inst.tokenID,
519 Amount: big.NewInt(int64(inst.amount)),
520 Fee: common.FeeSelector(inst.fee),
521 Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
522 State: common.PoolL2TxStatePending,
523 Timestamp: time.Now(),
524 RqToEthAddr: common.EmptyAddr,
525 RqToBJJ: nil,
526 Type: inst.typ,
527 }
528 if tx.Type == common.TxTypeTransfer {
529 tx.ToIdx = tc.Users[inst.to].Accounts[inst.tokenID].Idx
530 tx.ToEthAddr = tc.Users[inst.to].Addr
531 tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
532 } else if tx.Type == common.TxTypeTransferToEthAddr {
533 tx.ToIdx = common.Idx(0)
534 tx.ToEthAddr = tc.Users[inst.to].Addr
535 } else if tx.Type == common.TxTypeTransferToBJJ {
536 tx.ToIdx = common.Idx(0)
537 tx.ToEthAddr = common.FFAddr
538 tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
539 }
540 nTx, err := common.NewPoolL2Tx(&tx)
541 if err != nil {
542 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
543 }
544 tx = *nTx
545 // perform signature and set it to tx.Signature
546 toSign, err := tx.HashToSign()
547 if err != nil {
548 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
549 }
550 sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
551 tx.Signature = sig.Compress()
552
553 txs = append(txs, tx)
554 case common.TxTypeExit:
555 tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
556 tx := common.PoolL2Tx{
557 FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
558 ToIdx: common.Idx(1), // as is an Exit
559 Fee: common.FeeSelector(inst.fee),
560 TokenID: inst.tokenID,
561 Amount: big.NewInt(int64(inst.amount)),
562 Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
563 Type: common.TxTypeExit,
564 }
565 nTx, err := common.NewPoolL2Tx(&tx)
566 if err != nil {
567 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
568 }
569 tx = *nTx
570 // perform signature and set it to tx.Signature
571 toSign, err := tx.HashToSign()
572 if err != nil {
573 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
574 }
575 sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
576 tx.Signature = sig.Compress()
577 txs = append(txs, tx)
578 default:
579 return nil, fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.lineNum, inst.typ)
580 }
581 }
582
583 return txs, nil
584}
585
586// generateKeys generates BabyJubJub & Address keys for the given list of user
587// names in a deterministic way. This means, that for the same given
588// 'userNames' in a certain order, the keys will be always the same.
589func (tc *Context) generateKeys(userNames []string) {
590 for i := 1; i < len(userNames)+1; i++ {
591 if _, ok := tc.Users[userNames[i-1]]; ok {
592 // account already created
593 continue
594 }
595 // babyjubjub key
596 var sk babyjub.PrivateKey
597 copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
598
599 // eth address
600 var key ecdsa.PrivateKey
601 key.D = big.NewInt(int64(i)) // only for testing
602 key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
603 key.Curve = ethCrypto.S256()
604 addr := ethCrypto.PubkeyToAddress(key.PublicKey)
605
606 u := User{
607 BJJ: &sk,
608 Addr: addr,
609 Accounts: make(map[common.TokenID]*Account),
610 }
611 tc.Users[userNames[i-1]] = &u
612 }
613}
614
615// L1TxsToCommonL1Txs converts an array of []til.L1Tx to []common.L1Tx
616func L1TxsToCommonL1Txs(l1 []L1Tx) []common.L1Tx {
617 var r []common.L1Tx
618 for i := 0; i < len(l1); i++ {
619 r = append(r, l1[i].L1Tx)
620 }
621 return r
622}