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