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/hermeznetwork/tracerr"
 16	"github.com/iden3/go-iden3-crypto/babyjub"
 17)
 18
 19func newBatchData(batchNum int) common.BatchData {
 20	return common.BatchData{
 21		L1CoordinatorTxs: []common.L1Tx{},
 22		L2Txs:            []common.L2Tx{},
 23		Batch: common.Batch{
 24			BatchNum:  common.BatchNum(batchNum),
 25			StateRoot: big.NewInt(0), ExitRoot: big.NewInt(0),
 26			FeeIdxsCoordinator: make([]common.Idx, 0),
 27			CollectedFees:      make(map[common.TokenID]*big.Int),
 28		},
 29	}
 30}
 31
 32func newBlock(blockNum int64) common.BlockData {
 33	return common.BlockData{
 34		Block: common.Block{
 35			Num: blockNum,
 36		},
 37		Rollup: common.RollupData{
 38			L1UserTxs: []common.L1Tx{},
 39		},
 40	}
 41}
 42
 43type contextExtra struct {
 44	openToForge     int64
 45	toForgeL1TxsNum int64
 46	nonces          map[common.Idx]common.Nonce
 47	idx             int
 48}
 49
 50// Context contains the data of the test
 51type Context struct {
 52	Instructions          []instruction
 53	userNames             []string
 54	Users                 map[string]*User // Name -> *User
 55	UsersByIdx            map[int]*User
 56	accountsByIdx         map[int]*Account
 57	LastRegisteredTokenID common.TokenID
 58	l1CreatedAccounts     map[string]*Account // (Name, TokenID) -> *Account
 59
 60	// rollupConstMaxL1UserTx Maximum L1-user transactions allowed to be queued in a batch
 61	rollupConstMaxL1UserTx int
 62
 63	idx           int
 64	currBlock     common.BlockData
 65	currBatch     common.BatchData
 66	currBatchNum  int
 67	Queues        [][]L1Tx
 68	ToForgeNum    int
 69	openToForge   int
 70	currBatchTest struct {
 71		l1CoordinatorTxs []L1Tx
 72		l2Txs            []L2Tx
 73	}
 74	blockNum int64
 75
 76	extra contextExtra
 77}
 78
 79// NewContext returns a new Context
 80func NewContext(rollupConstMaxL1UserTx int) *Context {
 81	currBatchNum := 1 // The protocol defines the first batchNum to be 1
 82	return &Context{
 83		Users:                 make(map[string]*User),
 84		l1CreatedAccounts:     make(map[string]*Account),
 85		UsersByIdx:            make(map[int]*User),
 86		accountsByIdx:         make(map[int]*Account),
 87		LastRegisteredTokenID: 0,
 88
 89		rollupConstMaxL1UserTx: rollupConstMaxL1UserTx,
 90		idx:                    common.UserThreshold,
 91		// We use some placeholder values for StateRoot and ExitTree
 92		// because these values will never be nil
 93		currBlock:    newBlock(2), //nolint:gomnd
 94		currBatch:    newBatchData(currBatchNum),
 95		currBatchNum: currBatchNum,
 96		// start with 2 queues, one for toForge, and the other for openToForge
 97		Queues:      make([][]L1Tx, 2),
 98		ToForgeNum:  0,
 99		openToForge: 1,
100		//nolint:gomnd
101		blockNum: 2, // rollup genesis blockNum
102		extra: contextExtra{
103			openToForge:     0,
104			toForgeL1TxsNum: 0,
105			nonces:          make(map[common.Idx]common.Nonce),
106			idx:             common.UserThreshold,
107		},
108	}
109}
110
111// Account contains the data related to the account for a specific TokenID of a User
112type Account struct {
113	Idx      common.Idx
114	TokenID  common.TokenID
115	Nonce    common.Nonce
116	BatchNum int
117}
118
119// User contains the data related to a testing user
120type User struct {
121	Name     string
122	BJJ      *babyjub.PrivateKey
123	Addr     ethCommon.Address
124	Accounts map[common.TokenID]*Account
125}
126
127// L1Tx is the data structure used internally for transaction test generation,
128// which contains a common.L1Tx data plus some intermediate data for the
129// transaction generation.
130type L1Tx struct {
131	lineNum     int
132	fromIdxName string
133	toIdxName   string
134
135	L1Tx common.L1Tx
136}
137
138// L2Tx is the data structure used internally for transaction test generation,
139// which contains a common.L2Tx data plus some intermediate data for the
140// transaction generation.
141type L2Tx struct {
142	lineNum     int
143	fromIdxName string
144	toIdxName   string
145	tokenID     common.TokenID
146	L2Tx        common.L2Tx
147}
148
149// GenerateBlocks returns an array of BlockData for a given set. It uses the
150// users (keys & nonces) of the Context.
151func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) {
152	parser := newParser(strings.NewReader(set))
153	parsedSet, err := parser.parse()
154	if err != nil {
155		return nil, tracerr.Wrap(err)
156	}
157	if parsedSet.typ != setTypeBlockchain {
158		return nil, tracerr.Wrap(fmt.Errorf("Expected set type: %s, found: %s", setTypeBlockchain, parsedSet.typ))
159	}
160
161	tc.Instructions = parsedSet.instructions
162	tc.userNames = parsedSet.users
163
164	tc.generateKeys(tc.userNames)
165
166	var blocks []common.BlockData
167	for _, inst := range parsedSet.instructions {
168		switch inst.typ {
169		case txTypeCreateAccountDepositCoordinator: // tx source: L1CoordinatorTx
170			if err := tc.checkIfTokenIsRegistered(inst); err != nil {
171				log.Error(err)
172				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
173			}
174			tx := common.L1Tx{
175				FromEthAddr: tc.Users[inst.from].Addr,
176				FromBJJ:     tc.Users[inst.from].BJJ.Public(),
177				TokenID:     inst.tokenID,
178				Amount:      big.NewInt(0),
179				LoadAmount:  big.NewInt(0),
180				Type:        common.TxTypeCreateAccountDeposit, // as txTypeCreateAccountDepositCoordinator is not valid oustide Til package
181			}
182			testTx := L1Tx{
183				lineNum:     inst.lineNum,
184				fromIdxName: inst.from,
185				L1Tx:        tx,
186			}
187
188			tc.currBatchTest.l1CoordinatorTxs = append(tc.currBatchTest.l1CoordinatorTxs, testTx)
189		case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer: // tx source: L1UserTx
190			if err := tc.checkIfTokenIsRegistered(inst); err != nil {
191				log.Error(err)
192				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
193			}
194			tx := common.L1Tx{
195				FromEthAddr: tc.Users[inst.from].Addr,
196				FromBJJ:     tc.Users[inst.from].BJJ.Public(),
197				TokenID:     inst.tokenID,
198				Amount:      big.NewInt(0),
199				LoadAmount:  inst.loadAmount,
200				Type:        inst.typ,
201			}
202			if inst.typ == common.TxTypeCreateAccountDepositTransfer {
203				tx.Amount = inst.amount
204			}
205			testTx := L1Tx{
206				lineNum:     inst.lineNum,
207				fromIdxName: inst.from,
208				toIdxName:   inst.to,
209				L1Tx:        tx,
210			}
211			if err := tc.addToL1UserQueue(testTx); err != nil {
212				return nil, tracerr.Wrap(err)
213			}
214		case common.TxTypeDeposit, common.TxTypeDepositTransfer: // tx source: L1UserTx
215			if err := tc.checkIfTokenIsRegistered(inst); err != nil {
216				log.Error(err)
217				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
218			}
219			if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
220				log.Error(err)
221				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
222			}
223			tx := common.L1Tx{
224				TokenID:    inst.tokenID,
225				Amount:     big.NewInt(0),
226				LoadAmount: inst.loadAmount,
227				Type:       inst.typ,
228			}
229			if inst.typ == common.TxTypeDepositTransfer {
230				tx.Amount = inst.amount
231			}
232			testTx := L1Tx{
233				lineNum:     inst.lineNum,
234				fromIdxName: inst.from,
235				toIdxName:   inst.to,
236				L1Tx:        tx,
237			}
238			if err := tc.addToL1UserQueue(testTx); err != nil {
239				return nil, tracerr.Wrap(err)
240			}
241		case common.TxTypeTransfer: // L2Tx
242			if err := tc.checkIfTokenIsRegistered(inst); err != nil {
243				log.Error(err)
244				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
245			}
246			tx := common.L2Tx{
247				Amount:      inst.amount,
248				Fee:         common.FeeSelector(inst.fee),
249				Type:        common.TxTypeTransfer,
250				EthBlockNum: tc.blockNum,
251			}
252			tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
253			testTx := L2Tx{
254				lineNum:     inst.lineNum,
255				fromIdxName: inst.from,
256				toIdxName:   inst.to,
257				tokenID:     inst.tokenID,
258				L2Tx:        tx,
259			}
260			tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx)
261		case common.TxTypeForceTransfer: // tx source: L1UserTx
262			if err := tc.checkIfTokenIsRegistered(inst); err != nil {
263				log.Error(err)
264				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
265			}
266			tx := common.L1Tx{
267				TokenID:    inst.tokenID,
268				Amount:     inst.amount,
269				LoadAmount: big.NewInt(0),
270				Type:       common.TxTypeForceTransfer,
271			}
272			testTx := L1Tx{
273				lineNum:     inst.lineNum,
274				fromIdxName: inst.from,
275				toIdxName:   inst.to,
276				L1Tx:        tx,
277			}
278			if err := tc.addToL1UserQueue(testTx); err != nil {
279				return nil, tracerr.Wrap(err)
280			}
281		case common.TxTypeExit: // tx source: L2Tx
282			if err := tc.checkIfTokenIsRegistered(inst); err != nil {
283				log.Error(err)
284				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
285			}
286			tx := common.L2Tx{
287				ToIdx:       common.Idx(1), // as is an Exit
288				Fee:         common.FeeSelector(inst.fee),
289				Amount:      inst.amount,
290				Type:        common.TxTypeExit,
291				EthBlockNum: tc.blockNum,
292			}
293			tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
294			testTx := L2Tx{
295				lineNum:     inst.lineNum,
296				fromIdxName: inst.from,
297				toIdxName:   inst.to,
298				tokenID:     inst.tokenID,
299				L2Tx:        tx,
300			}
301			tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx)
302		case common.TxTypeForceExit: // tx source: L1UserTx
303			if err := tc.checkIfTokenIsRegistered(inst); err != nil {
304				log.Error(err)
305				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
306			}
307			tx := common.L1Tx{
308				ToIdx:      common.Idx(1), // as is an Exit
309				TokenID:    inst.tokenID,
310				Amount:     inst.amount,
311				LoadAmount: big.NewInt(0),
312				Type:       common.TxTypeForceExit,
313			}
314			testTx := L1Tx{
315				lineNum:     inst.lineNum,
316				fromIdxName: inst.from,
317				toIdxName:   inst.to,
318				L1Tx:        tx,
319			}
320			if err := tc.addToL1UserQueue(testTx); err != nil {
321				return nil, tracerr.Wrap(err)
322			}
323		case typeNewBatch:
324			if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
325				return nil, tracerr.Wrap(err)
326			}
327			if err = tc.setIdxs(); err != nil {
328				log.Error(err)
329				return nil, tracerr.Wrap(err)
330			}
331		case typeNewBatchL1:
332			// for each L1UserTx of the Queues[ToForgeNum], calculate the Idx
333			if err = tc.calculateIdxForL1Txs(false, tc.Queues[tc.ToForgeNum]); err != nil {
334				return nil, tracerr.Wrap(err)
335			}
336			if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
337				return nil, tracerr.Wrap(err)
338			}
339			tc.currBatch.L1Batch = true
340			if err = tc.setIdxs(); err != nil {
341				log.Error(err)
342				return nil, tracerr.Wrap(err)
343			}
344			toForgeL1TxsNum := int64(tc.openToForge)
345			tc.currBatch.Batch.ForgeL1TxsNum = &toForgeL1TxsNum
346			// advance batch
347			tc.ToForgeNum++
348			if tc.ToForgeNum == tc.openToForge {
349				tc.openToForge++
350				newQueue := []L1Tx{}
351				tc.Queues = append(tc.Queues, newQueue)
352			}
353		case typeNewBlock:
354			blocks = append(blocks, tc.currBlock)
355			tc.blockNum++
356			tc.currBlock = newBlock(tc.blockNum)
357		case typeAddToken:
358			newToken := common.Token{
359				EthAddr: ethCommon.BigToAddress(big.NewInt(int64(inst.tokenID * 100))), //nolint:gomnd
360				// Name:        fmt.Sprintf("Token %d", inst.tokenID),
361				// Symbol:      fmt.Sprintf("TK%d", inst.tokenID),
362				// Decimals:    18,
363				TokenID:     inst.tokenID,
364				EthBlockNum: tc.blockNum,
365			}
366			if inst.tokenID != tc.LastRegisteredTokenID+1 {
367				return nil, tracerr.Wrap(fmt.Errorf("Line %d: AddToken TokenID should be sequential, expected TokenID: %d, defined TokenID: %d", inst.lineNum, tc.LastRegisteredTokenID+1, inst.tokenID))
368			}
369			tc.LastRegisteredTokenID++
370			tc.currBlock.Rollup.AddedTokens = append(tc.currBlock.Rollup.AddedTokens, newToken)
371		default:
372			return nil, tracerr.Wrap(fmt.Errorf("Line %d: Unexpected type: %s", inst.lineNum, inst.typ))
373		}
374	}
375
376	return blocks, nil
377}
378
379// calculateIdxsForL1Txs calculates new Idx for new created accounts. If
380// 'isCoordinatorTxs==true', adds the tx to tc.currBatch.L1CoordinatorTxs.
381func (tc *Context) calculateIdxForL1Txs(isCoordinatorTxs bool, txs []L1Tx) error {
382	// for each batch.L1CoordinatorTxs of the Queues[ToForgeNum], calculate the Idx
383	for i := 0; i < len(txs); i++ {
384		tx := txs[i]
385		if tx.L1Tx.Type == common.TxTypeCreateAccountDeposit || tx.L1Tx.Type == common.TxTypeCreateAccountDepositTransfer {
386			if tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] != nil { // if account already exists, return error
387				return tracerr.Wrap(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))
388			}
389			tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] = &Account{
390				Idx:      common.Idx(tc.idx),
391				TokenID:  tx.L1Tx.TokenID,
392				Nonce:    common.Nonce(0),
393				BatchNum: tc.currBatchNum,
394			}
395			tc.l1CreatedAccounts[idxTokenIDToString(tx.fromIdxName, tx.L1Tx.TokenID)] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
396			tc.accountsByIdx[tc.idx] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
397			tc.UsersByIdx[tc.idx] = tc.Users[tx.fromIdxName]
398			tc.idx++
399		}
400		if isCoordinatorTxs {
401			tc.currBatch.L1CoordinatorTxs = append(tc.currBatch.L1CoordinatorTxs, tx.L1Tx)
402		}
403	}
404	return nil
405}
406
407// setIdxs sets the Idxs to the transactions of the tc.currBatch
408func (tc *Context) setIdxs() error {
409	// once Idxs are calculated, update transactions to use the new Idxs
410	for i := 0; i < len(tc.currBatchTest.l2Txs); i++ {
411		testTx := &tc.currBatchTest.l2Txs[i]
412
413		if tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID] == nil {
414			return tracerr.Wrap(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))
415		}
416		if testTx.L2Tx.Type == common.TxTypeTransfer {
417			if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(testTx.toIdxName, testTx.tokenID)]; !ok {
418				return tracerr.Wrap(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))
419			}
420		}
421		tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce++
422		// next line is commented to avoid Blockchain L2Txs to have
423		// Nonce different from 0, as from Blockchain those
424		// transactions will come without Nonce
425		// testTx.L2Tx.Nonce = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce
426
427		// set real Idx
428		testTx.L2Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Idx
429		if testTx.L2Tx.Type == common.TxTypeTransfer {
430			testTx.L2Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.tokenID].Idx
431		}
432		// in case Type==Exit, ToIdx=1, already set at the
433		// GenerateBlocks main switch inside TxTypeExit case
434
435		nTx, err := common.NewL2Tx(&testTx.L2Tx)
436		if err != nil {
437			return tracerr.Wrap(fmt.Errorf("Line %d: %s", testTx.lineNum, err.Error()))
438		}
439		testTx.L2Tx = *nTx
440
441		tc.currBatch.L2Txs = append(tc.currBatch.L2Txs, testTx.L2Tx)
442	}
443
444	tc.currBatch.Batch.LastIdx = int64(tc.idx - 1) // `-1` because tc.idx is the next available idx
445	tc.currBlock.Rollup.Batches = append(tc.currBlock.Rollup.Batches, tc.currBatch)
446	tc.currBatchNum++
447	tc.currBatch = newBatchData(tc.currBatchNum)
448	tc.currBatchTest.l1CoordinatorTxs = nil
449	tc.currBatchTest.l2Txs = nil
450	return nil
451}
452
453// addToL1UserQueue adds the L1UserTx into the queue that is open and has space
454func (tc *Context) addToL1UserQueue(tx L1Tx) error {
455	if len(tc.Queues[tc.openToForge]) >= tc.rollupConstMaxL1UserTx {
456		// if current OpenToForge queue reached its Max, move into a
457		// new queue
458		tc.openToForge++
459		newQueue := []L1Tx{}
460		tc.Queues = append(tc.Queues, newQueue)
461	}
462	// Fill L1UserTx specific parameters
463	tx.L1Tx.UserOrigin = true
464	toForgeL1TxsNum := int64(tc.openToForge)
465	tx.L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
466	tx.L1Tx.EthBlockNum = tc.blockNum
467	tx.L1Tx.Position = len(tc.Queues[tc.openToForge])
468
469	// When an L1UserTx is generated, all idxs must be available (except when idx == 0 or idx == 1)
470	if tx.L1Tx.Type != common.TxTypeCreateAccountDeposit && tx.L1Tx.Type != common.TxTypeCreateAccountDepositTransfer {
471		tx.L1Tx.FromIdx = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID].Idx
472	}
473	tx.L1Tx.FromEthAddr = tc.Users[tx.fromIdxName].Addr
474	tx.L1Tx.FromBJJ = tc.Users[tx.fromIdxName].BJJ.Public()
475	if tx.toIdxName == "" {
476		tx.L1Tx.ToIdx = common.Idx(0)
477	} else {
478		account, ok := tc.Users[tx.toIdxName].Accounts[tx.L1Tx.TokenID]
479		if !ok {
480			return tracerr.Wrap(fmt.Errorf("Line %d: Transfer to User: %s, for TokenID: %d, "+
481				"while account not created yet", tx.lineNum, tx.toIdxName, tx.L1Tx.TokenID))
482		}
483		tx.L1Tx.ToIdx = account.Idx
484	}
485	if tx.L1Tx.Type == common.TxTypeForceExit {
486		tx.L1Tx.ToIdx = common.Idx(1)
487	}
488	nTx, err := common.NewL1Tx(&tx.L1Tx)
489	if err != nil {
490		return tracerr.Wrap(fmt.Errorf("Line %d: %s", tx.lineNum, err.Error()))
491	}
492	tx.L1Tx = *nTx
493
494	tc.Queues[tc.openToForge] = append(tc.Queues[tc.openToForge], tx)
495	tc.currBlock.Rollup.L1UserTxs = append(tc.currBlock.Rollup.L1UserTxs, tx.L1Tx)
496
497	return nil
498}
499
500func (tc *Context) checkIfAccountExists(tf string, inst instruction) error {
501	if tc.Users[tf].Accounts[inst.tokenID] == nil {
502		return tracerr.Wrap(fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.typ, tf, inst.tokenID))
503	}
504	return nil
505}
506func (tc *Context) checkIfTokenIsRegistered(inst instruction) error {
507	if inst.tokenID > tc.LastRegisteredTokenID {
508		return tracerr.Wrap(fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.typ, inst.tokenID, tc.LastRegisteredTokenID))
509	}
510	return nil
511}
512
513// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
514// uses the users (keys) of the Context.
515func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
516	parser := newParser(strings.NewReader(set))
517	parsedSet, err := parser.parse()
518	if err != nil {
519		return nil, tracerr.Wrap(err)
520	}
521	if parsedSet.typ != setTypePoolL2 {
522		return nil, tracerr.Wrap(fmt.Errorf("Expected set type: %s, found: %s", setTypePoolL2, parsedSet.typ))
523	}
524
525	tc.Instructions = parsedSet.instructions
526	tc.userNames = parsedSet.users
527
528	tc.generateKeys(tc.userNames)
529
530	txs := []common.PoolL2Tx{}
531	for _, inst := range tc.Instructions {
532		switch inst.typ {
533		case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
534			if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
535				log.Error(err)
536				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
537			}
538			if inst.typ == common.TxTypeTransfer {
539				// if TxTypeTransfer, need to exist the ToIdx account
540				if err := tc.checkIfAccountExists(inst.to, inst); err != nil {
541					log.Error(err)
542					return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
543				}
544			}
545			tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
546			// if account of receiver does not exist, don't use
547			// ToIdx, and use only ToEthAddr & ToBJJ
548			tx := common.PoolL2Tx{
549				FromIdx:     tc.Users[inst.from].Accounts[inst.tokenID].Idx,
550				TokenID:     inst.tokenID,
551				Amount:      inst.amount,
552				Fee:         common.FeeSelector(inst.fee),
553				Nonce:       tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
554				State:       common.PoolL2TxStatePending,
555				Timestamp:   time.Now(),
556				RqToEthAddr: common.EmptyAddr,
557				RqToBJJ:     nil,
558				Type:        inst.typ,
559			}
560			if tx.Type == common.TxTypeTransfer {
561				tx.ToIdx = tc.Users[inst.to].Accounts[inst.tokenID].Idx
562				tx.ToEthAddr = tc.Users[inst.to].Addr
563				tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
564			} else if tx.Type == common.TxTypeTransferToEthAddr {
565				tx.ToIdx = common.Idx(0)
566				tx.ToEthAddr = tc.Users[inst.to].Addr
567			} else if tx.Type == common.TxTypeTransferToBJJ {
568				tx.ToIdx = common.Idx(0)
569				tx.ToEthAddr = common.FFAddr
570				tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
571			}
572			nTx, err := common.NewPoolL2Tx(&tx)
573			if err != nil {
574				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
575			}
576			tx = *nTx
577			// perform signature and set it to tx.Signature
578			toSign, err := tx.HashToSign()
579			if err != nil {
580				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
581			}
582			sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
583			tx.Signature = sig.Compress()
584
585			txs = append(txs, tx)
586		case common.TxTypeExit:
587			tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
588			tx := common.PoolL2Tx{
589				FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
590				ToIdx:   common.Idx(1), // as is an Exit
591				Fee:     common.FeeSelector(inst.fee),
592				TokenID: inst.tokenID,
593				Amount:  inst.amount,
594				Nonce:   tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
595				State:   common.PoolL2TxStatePending,
596				Type:    common.TxTypeExit,
597			}
598			nTx, err := common.NewPoolL2Tx(&tx)
599			if err != nil {
600				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
601			}
602			tx = *nTx
603			// perform signature and set it to tx.Signature
604			toSign, err := tx.HashToSign()
605			if err != nil {
606				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()))
607			}
608			sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
609			tx.Signature = sig.Compress()
610			txs = append(txs, tx)
611		default:
612			return nil, tracerr.Wrap(fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.lineNum, inst.typ))
613		}
614	}
615
616	return txs, nil
617}
618
619// generateKeys generates BabyJubJub & Address keys for the given list of user
620// names in a deterministic way. This means, that for the same given
621// 'userNames' in a certain order, the keys will be always the same.
622func (tc *Context) generateKeys(userNames []string) {
623	for i := 1; i < len(userNames)+1; i++ {
624		if _, ok := tc.Users[userNames[i-1]]; ok {
625			// account already created
626			continue
627		}
628		// babyjubjub key
629		var sk babyjub.PrivateKey
630		copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
631
632		// eth address
633		var key ecdsa.PrivateKey
634		key.D = big.NewInt(int64(i)) // only for testing
635		key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
636		key.Curve = ethCrypto.S256()
637		addr := ethCrypto.PubkeyToAddress(key.PublicKey)
638
639		u := User{
640			Name:     userNames[i-1],
641			BJJ:      &sk,
642			Addr:     addr,
643			Accounts: make(map[common.TokenID]*Account),
644		}
645		tc.Users[userNames[i-1]] = &u
646	}
647}
648
649// L1TxsToCommonL1Txs converts an array of []til.L1Tx to []common.L1Tx
650func L1TxsToCommonL1Txs(l1 []L1Tx) []common.L1Tx {
651	var r []common.L1Tx
652	for i := 0; i < len(l1); i++ {
653		r = append(r, l1[i].L1Tx)
654	}
655	return r
656}
657
658// ConfigExtra is the configuration used in FillBlocksExtra to extend the
659// blocks returned by til.
660type ConfigExtra struct {
661	// Address to set as forger for each batch
662	BootCoordAddr ethCommon.Address
663	// Coordinator user name used to select the corresponding accounts to
664	// collect coordinator fees
665	CoordUser string
666}
667
668// FillBlocksL1UserTxsBatchNum fills the BatchNum of forged L1UserTxs:
669// - blocks[].Rollup.L1UserTxs[].BatchNum
670func (tc *Context) FillBlocksL1UserTxsBatchNum(blocks []common.BlockData) {
671	for i := range blocks {
672		block := &blocks[i]
673		for j := range block.Rollup.Batches {
674			batch := &block.Rollup.Batches[j]
675			if batch.L1Batch {
676				// Set BatchNum for forged L1UserTxs to til blocks
677				bn := batch.Batch.BatchNum
678				for k := range blocks {
679					block := &blocks[k]
680					for l := range block.Rollup.L1UserTxs {
681						tx := &block.Rollup.L1UserTxs[l]
682						if *tx.ToForgeL1TxsNum == tc.extra.openToForge {
683							tx.BatchNum = &bn
684						}
685					}
686				}
687				tc.extra.openToForge++
688			}
689		}
690	}
691}
692
693// FillBlocksExtra fills extra fields not generated by til in each block, so
694// that the blockData is closer to what the HistoryDB stores.  The filled fields are:
695// - blocks[].Rollup.Batch.EthBlockNum
696// - blocks[].Rollup.Batch.ForgerAddr
697// - blocks[].Rollup.Batch.ForgeL1TxsNum
698// - blocks[].Rollup.Batch.L1CoordinatorTxs[].TxID
699// - blocks[].Rollup.Batch.L1CoordinatorTxs[].BatchNum
700// - blocks[].Rollup.Batch.L1CoordinatorTxs[].EthBlockNum
701// - blocks[].Rollup.Batch.L1CoordinatorTxs[].Position
702// - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveAmount
703// - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveLoadAmount
704// - blocks[].Rollup.Batch.L2Txs[].TxID
705// - blocks[].Rollup.Batch.L2Txs[].Position
706// - blocks[].Rollup.Batch.L2Txs[].Nonce
707// - blocks[].Rollup.Batch.ExitTree
708// - blocks[].Rollup.Batch.CreatedAccounts
709// - blocks[].Rollup.Batch.FeeIdxCoordinator
710// - blocks[].Rollup.Batch.CollectedFees
711func (tc *Context) FillBlocksExtra(blocks []common.BlockData, cfg *ConfigExtra) error {
712	// Fill extra fields not generated by til in til block
713	for i := range blocks {
714		block := &blocks[i]
715		for j := range block.Rollup.Batches {
716			batch := &block.Rollup.Batches[j]
717			batch.Batch.EthBlockNum = block.Block.Num
718			// til doesn't fill the batch forger addr
719			batch.Batch.ForgerAddr = cfg.BootCoordAddr
720			if batch.L1Batch {
721				toForgeL1TxsNumCpy := tc.extra.toForgeL1TxsNum
722				// til doesn't fill the ForgeL1TxsNum
723				batch.Batch.ForgeL1TxsNum = &toForgeL1TxsNumCpy
724				tc.extra.toForgeL1TxsNum++
725			}
726
727			batchNum := batch.Batch.BatchNum
728			for k := range batch.L1CoordinatorTxs {
729				tx := &batch.L1CoordinatorTxs[k]
730				tx.BatchNum = &batchNum
731				tx.EthBlockNum = batch.Batch.EthBlockNum
732			}
733		}
734	}
735
736	// Fill CreatedAccounts
737	for i := range blocks {
738		block := &blocks[i]
739		for j := range block.Rollup.Batches {
740			batch := &block.Rollup.Batches[j]
741			l1Txs := []common.L1Tx{}
742			if batch.L1Batch {
743				for _, tx := range tc.Queues[*batch.Batch.ForgeL1TxsNum] {
744					l1Txs = append(l1Txs, tx.L1Tx)
745				}
746			}
747			l1Txs = append(l1Txs, batch.L1CoordinatorTxs...)
748			for k := range l1Txs {
749				tx := &l1Txs[k]
750				if tx.Type == common.TxTypeCreateAccountDeposit ||
751					tx.Type == common.TxTypeCreateAccountDepositTransfer {
752					user, ok := tc.UsersByIdx[tc.extra.idx]
753					if !ok {
754						return tracerr.Wrap(fmt.Errorf("Created account with idx: %v not found", tc.extra.idx))
755					}
756					batch.CreatedAccounts = append(batch.CreatedAccounts,
757						common.Account{
758							Idx:       common.Idx(tc.extra.idx),
759							TokenID:   tx.TokenID,
760							BatchNum:  batch.Batch.BatchNum,
761							PublicKey: user.BJJ.Public(),
762							EthAddr:   user.Addr,
763							Nonce:     0,
764							Balance:   big.NewInt(0),
765						})
766					tc.extra.idx++
767				}
768			}
769		}
770	}
771
772	// Fill expected positions in L1CoordinatorTxs and L2Txs
773	for i := range blocks {
774		block := &blocks[i]
775		for j := range block.Rollup.Batches {
776			batch := &block.Rollup.Batches[j]
777			position := 0
778			if batch.L1Batch {
779				position = len(tc.Queues[*batch.Batch.ForgeL1TxsNum])
780			}
781			for k := range batch.L1CoordinatorTxs {
782				tx := &batch.L1CoordinatorTxs[k]
783				tx.Position = position
784				position++
785				tx.EffectiveAmount = big.NewInt(0)
786				tx.EffectiveLoadAmount = big.NewInt(0)
787				nTx, err := common.NewL1Tx(tx)
788				if err != nil {
789					return tracerr.Wrap(err)
790				}
791				*tx = *nTx
792			}
793			for k := range batch.L2Txs {
794				tx := &batch.L2Txs[k]
795				tx.Position = position
796				position++
797				tc.extra.nonces[tx.FromIdx]++
798				tx.Nonce = tc.extra.nonces[tx.FromIdx]
799				nTx, err := common.NewL2Tx(tx)
800				if err != nil {
801					return tracerr.Wrap(err)
802				}
803				*tx = *nTx
804			}
805		}
806	}
807
808	// Fill ExitTree (only AccountIdx and Balance)
809	for i := range blocks {
810		block := &blocks[i]
811		for j := range block.Rollup.Batches {
812			batch := &block.Rollup.Batches[j]
813			if batch.L1Batch {
814				for _, _tx := range tc.Queues[*batch.Batch.ForgeL1TxsNum] {
815					tx := _tx.L1Tx
816					if tx.Type == common.TxTypeForceExit {
817						batch.ExitTree =
818							append(batch.ExitTree,
819								common.ExitInfo{
820									BatchNum:   batch.Batch.BatchNum,
821									AccountIdx: tx.FromIdx,
822									Balance:    tx.Amount,
823								})
824					}
825				}
826			}
827			for k := range batch.L2Txs {
828				tx := &batch.L2Txs[k]
829				if tx.Type == common.TxTypeExit {
830					batch.ExitTree = append(batch.ExitTree, common.ExitInfo{
831						BatchNum:   batch.Batch.BatchNum,
832						AccountIdx: tx.FromIdx,
833						Balance:    tx.Amount,
834					})
835				}
836				fee, err := common.CalcFeeAmount(tx.Amount, tx.Fee)
837				if err != nil {
838					return tracerr.Wrap(err)
839				}
840
841				// Find the TokenID of the tx
842				fromAcc, ok := tc.accountsByIdx[int(tx.FromIdx)]
843				if !ok {
844					return tracerr.Wrap(fmt.Errorf("L2tx.FromIdx idx: %v not found", tx.FromIdx))
845				}
846
847				// Find the idx of the CoordUser for the
848				// TokenID, and if it exists, add the fee to
849				// the collectedFees.  Only consider the
850				// coordinator account to receive fee if it was
851				// created in this or a previous batch
852				if acc, ok := tc.l1CreatedAccounts[idxTokenIDToString(cfg.CoordUser, fromAcc.TokenID)]; ok &&
853					common.BatchNum(acc.BatchNum) <= batch.Batch.BatchNum {
854					found := false
855					for _, idx := range batch.Batch.FeeIdxsCoordinator {
856						if idx == common.Idx(acc.Idx) {
857							found = true
858							break
859						}
860					}
861					if !found {
862						batch.Batch.FeeIdxsCoordinator = append(batch.Batch.FeeIdxsCoordinator,
863							common.Idx(acc.Idx))
864						batch.Batch.CollectedFees[fromAcc.TokenID] = big.NewInt(0)
865					}
866					collected := batch.Batch.CollectedFees[fromAcc.TokenID]
867					collected.Add(collected, fee)
868				}
869			}
870		}
871	}
872	return nil
873}