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