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