arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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}