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				log.Error(err)
374				return nil, tracerr.Wrap(err)
375			}
376			toForgeL1TxsNum := int64(tc.openToForge)
377			tc.currBatch.Batch.ForgeL1TxsNum = &toForgeL1TxsNum
378			// advance batch
379			tc.ToForgeNum++
380			if tc.ToForgeNum == tc.openToForge {
381				tc.openToForge++
382				newQueue := []L1Tx{}
383				tc.Queues = append(tc.Queues, newQueue)
384			}
385		case TypeNewBlock:
386			blocks = append(blocks, tc.currBlock)
387			tc.blockNum++
388			tc.currBlock = newBlock(tc.blockNum)
389		case TypeAddToken:
390			newToken := common.Token{
391				EthAddr: ethCommon.BigToAddress(big.NewInt(int64(inst.TokenID * 100))), //nolint:gomnd
392				// Name:        fmt.Sprintf("Token %d", inst.TokenID),
393				// Symbol:      fmt.Sprintf("TK%d", inst.TokenID),
394				// Decimals:    18,
395				TokenID:     inst.TokenID,
396				EthBlockNum: tc.blockNum,
397			}
398			if inst.TokenID != tc.LastRegisteredTokenID+1 {
399				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))
400			}
401			tc.LastRegisteredTokenID++
402			tc.currBlock.Rollup.AddedTokens = append(tc.currBlock.Rollup.AddedTokens, newToken)
403		default:
404			return nil, tracerr.Wrap(fmt.Errorf("Line %d: Unexpected type: %s", inst.LineNum, inst.Typ))
405		}
406	}
407
408	return blocks, nil
409}
410
411// calculateIdxsForL1Txs calculates new Idx for new created accounts. If
412// 'isCoordinatorTxs==true', adds the tx to tc.currBatch.L1CoordinatorTxs.
413func (tc *Context) calculateIdxForL1Txs(isCoordinatorTxs bool, txs []L1Tx) error {
414	// for each batch.L1CoordinatorTxs of the Queues[ToForgeNum], calculate the Idx
415	for i := 0; i < len(txs); i++ {
416		tx := txs[i]
417		if tx.L1Tx.Type == common.TxTypeCreateAccountDeposit || tx.L1Tx.Type == common.TxTypeCreateAccountDepositTransfer {
418			if tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] != nil { // if account already exists, return error
419				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))
420			}
421			tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] = &Account{
422				Idx:      common.Idx(tc.idx),
423				TokenID:  tx.L1Tx.TokenID,
424				Nonce:    common.Nonce(0),
425				BatchNum: tc.currBatchNum,
426			}
427			tc.l1CreatedAccounts[idxTokenIDToString(tx.fromIdxName, tx.L1Tx.TokenID)] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
428			tc.accountsByIdx[tc.idx] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
429			tc.UsersByIdx[tc.idx] = tc.Users[tx.fromIdxName]
430			tc.idx++
431		}
432		if isCoordinatorTxs {
433			tc.currBatch.L1CoordinatorTxs = append(tc.currBatch.L1CoordinatorTxs, tx.L1Tx)
434		}
435	}
436	return nil
437}
438
439// setIdxs sets the Idxs to the transactions of the tc.currBatch
440func (tc *Context) setIdxs() error {
441	// once Idxs are calculated, update transactions to use the new Idxs
442	for i := 0; i < len(tc.currBatchTest.l2Txs); i++ {
443		testTx := &tc.currBatchTest.l2Txs[i]
444
445		if tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID] == nil {
446			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))
447		}
448		if testTx.L2Tx.Type == common.TxTypeTransfer {
449			if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(testTx.toIdxName, testTx.tokenID)]; !ok {
450				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))
451			}
452		}
453		tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce++
454		// next line is commented to avoid Blockchain L2Txs to have
455		// Nonce different from 0, as from Blockchain those
456		// transactions will come without Nonce
457		// testTx.L2Tx.Nonce = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce
458
459		// set real Idx
460		testTx.L2Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Idx
461		if testTx.L2Tx.Type == common.TxTypeTransfer {
462			testTx.L2Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.tokenID].Idx
463		}
464		// in case Type==Exit, ToIdx=1, already set at the
465		// GenerateBlocks main switch inside TxTypeExit case
466
467		nTx, err := common.NewL2Tx(&testTx.L2Tx)
468		if err != nil {
469			return tracerr.Wrap(fmt.Errorf("Line %d: %s", testTx.lineNum, err.Error()))
470		}
471		testTx.L2Tx = *nTx
472
473		tc.currBatch.L2Txs = append(tc.currBatch.L2Txs, testTx.L2Tx)
474	}
475
476	tc.currBatch.Batch.LastIdx = int64(tc.idx - 1) // `-1` because tc.idx is the next available idx
477	tc.currBlock.Rollup.Batches = append(tc.currBlock.Rollup.Batches, tc.currBatch)
478	tc.currBatchNum++
479	tc.currBatch = newBatchData(tc.currBatchNum)
480	tc.currBatchTest.l1CoordinatorTxs = nil
481	tc.currBatchTest.l2Txs = nil
482	return nil
483}
484
485// addToL1UserQueue adds the L1UserTx into the queue that is open and has space
486func (tc *Context) addToL1UserQueue(tx L1Tx) error {
487	if len(tc.Queues[tc.openToForge]) >= tc.rollupConstMaxL1UserTx {
488		// if current OpenToForge queue reached its Max, move into a
489		// new queue
490		tc.openToForge++
491		newQueue := []L1Tx{}
492		tc.Queues = append(tc.Queues, newQueue)
493	}
494	// Fill L1UserTx specific parameters
495	tx.L1Tx.UserOrigin = true
496	toForgeL1TxsNum := int64(tc.openToForge)
497	tx.L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
498	tx.L1Tx.EthBlockNum = tc.blockNum
499	tx.L1Tx.Position = len(tc.Queues[tc.openToForge])
500
501	// When an L1UserTx is generated, all idxs must be available (except when idx == 0 or idx == 1)
502	if tx.L1Tx.Type != common.TxTypeCreateAccountDeposit && tx.L1Tx.Type != common.TxTypeCreateAccountDepositTransfer {
503		tx.L1Tx.FromIdx = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID].Idx
504	}
505	tx.L1Tx.FromEthAddr = tc.Users[tx.fromIdxName].Addr
506	tx.L1Tx.FromBJJ = tc.Users[tx.fromIdxName].BJJ.Public().Compress()
507	if tx.toIdxName == "" {
508		tx.L1Tx.ToIdx = common.Idx(0)
509	} else {
510		account, ok := tc.Users[tx.toIdxName].Accounts[tx.L1Tx.TokenID]
511		if !ok {
512			return tracerr.Wrap(fmt.Errorf("Line %d: Transfer to User: %s, for TokenID: %d, "+
513				"while account not created yet", tx.lineNum, tx.toIdxName, tx.L1Tx.TokenID))
514		}
515		tx.L1Tx.ToIdx = account.Idx
516	}
517	if tx.L1Tx.Type == common.TxTypeForceExit {
518		tx.L1Tx.ToIdx = common.Idx(1)
519	}
520	nTx, err := common.NewL1Tx(&tx.L1Tx)
521	if err != nil {
522		return tracerr.Wrap(fmt.Errorf("Line %d: %s", tx.lineNum, err.Error()))
523	}
524	tx.L1Tx = *nTx
525
526	tc.Queues[tc.openToForge] = append(tc.Queues[tc.openToForge], tx)
527	tc.currBlock.Rollup.L1UserTxs = append(tc.currBlock.Rollup.L1UserTxs, tx.L1Tx)
528
529	return nil
530}
531
532func (tc *Context) checkIfAccountExists(tf string, inst Instruction) error {
533	if tc.Users[tf].Accounts[inst.TokenID] == nil {
534		return tracerr.Wrap(fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.Typ, tf, inst.TokenID))
535	}
536	return nil
537}
538
539func (tc *Context) checkIfTokenIsRegistered(inst Instruction) error {
540	if inst.TokenID > tc.LastRegisteredTokenID {
541		return tracerr.Wrap(fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.Typ, inst.TokenID, tc.LastRegisteredTokenID))
542	}
543	return nil
544}
545
546// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set made
547// of a string. It uses the users (keys) of the Context.
548func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
549	parser := newParser(strings.NewReader(set))
550	parsedSet, err := parser.parse()
551	if err != nil {
552		return nil, tracerr.Wrap(err)
553	}
554	if parsedSet.typ != SetTypePoolL2 {
555		return nil, tracerr.Wrap(fmt.Errorf("Expected set type: %s, found: %s", SetTypePoolL2, parsedSet.typ))
556	}
557
558	tc.instructions = parsedSet.instructions
559	tc.userNames = parsedSet.users
560
561	return tc.generatePoolL2Txs()
562}
563
564// GeneratePoolL2TxsFromInstructions returns an array of common.PoolL2Tx from a
565// given set made of instructions. It uses the users (keys) of the Context.
566func (tc *Context) GeneratePoolL2TxsFromInstructions(set []Instruction) ([]common.PoolL2Tx, error) {
567	userNames := []string{}
568	addedNames := make(map[string]bool)
569	for _, inst := range set {
570		if _, ok := addedNames[inst.From]; !ok {
571			// If the name wasn't already added
572			userNames = append(userNames, inst.From)
573			addedNames[inst.From] = true
574		}
575		if _, ok := addedNames[inst.To]; !ok {
576			// If the name wasn't already added
577			userNames = append(userNames, inst.To)
578			addedNames[inst.To] = true
579		}
580	}
581	tc.userNames = userNames
582	tc.instructions = set
583
584	return tc.generatePoolL2Txs()
585}
586
587func (tc *Context) generatePoolL2Txs() ([]common.PoolL2Tx, error) {
588	tc.generateKeys(tc.userNames)
589
590	txs := []common.PoolL2Tx{}
591	for _, inst := range tc.instructions {
592		switch inst.Typ {
593		case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
594			if err := tc.checkIfAccountExists(inst.From, inst); err != nil {
595				log.Error(err)
596				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
597			}
598			if inst.Typ == common.TxTypeTransfer {
599				// if TxTypeTransfer, need to exist the ToIdx account
600				if err := tc.checkIfAccountExists(inst.To, inst); err != nil {
601					log.Error(err)
602					return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
603				}
604			}
605			// if account of receiver does not exist, don't use
606			// ToIdx, and use only ToEthAddr & ToBJJ
607			tx := common.PoolL2Tx{
608				FromIdx:     tc.Users[inst.From].Accounts[inst.TokenID].Idx,
609				TokenID:     inst.TokenID,
610				Amount:      inst.Amount,
611				Fee:         common.FeeSelector(inst.Fee),
612				Nonce:       tc.Users[inst.From].Accounts[inst.TokenID].Nonce,
613				State:       common.PoolL2TxStatePending,
614				Timestamp:   time.Now(),
615				RqToEthAddr: common.EmptyAddr,
616				RqToBJJ:     common.EmptyBJJComp,
617				Type:        inst.Typ,
618			}
619			tc.Users[inst.From].Accounts[inst.TokenID].Nonce++
620			if tx.Type == common.TxTypeTransfer {
621				tx.ToIdx = tc.Users[inst.To].Accounts[inst.TokenID].Idx
622				tx.ToEthAddr = tc.Users[inst.To].Addr
623				tx.ToBJJ = tc.Users[inst.To].BJJ.Public().Compress()
624			} else if tx.Type == common.TxTypeTransferToEthAddr {
625				tx.ToIdx = common.Idx(0)
626				tx.ToEthAddr = tc.Users[inst.To].Addr
627			} else if tx.Type == common.TxTypeTransferToBJJ {
628				tx.ToIdx = common.Idx(0)
629				tx.ToEthAddr = common.FFAddr
630				tx.ToBJJ = tc.Users[inst.To].BJJ.Public().Compress()
631			}
632			nTx, err := common.NewPoolL2Tx(&tx)
633			if err != nil {
634				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
635			}
636			tx = *nTx
637			// perform signature and set it to tx.Signature
638			toSign, err := tx.HashToSign(tc.chainID)
639			if err != nil {
640				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
641			}
642			sig := tc.Users[inst.From].BJJ.SignPoseidon(toSign)
643			tx.Signature = sig.Compress()
644
645			txs = append(txs, tx)
646		case common.TxTypeExit:
647			tx := common.PoolL2Tx{
648				FromIdx: tc.Users[inst.From].Accounts[inst.TokenID].Idx,
649				ToIdx:   common.Idx(1), // as is an Exit
650				Fee:     common.FeeSelector(inst.Fee),
651				TokenID: inst.TokenID,
652				Amount:  inst.Amount,
653				Nonce:   tc.Users[inst.From].Accounts[inst.TokenID].Nonce,
654				State:   common.PoolL2TxStatePending,
655				Type:    common.TxTypeExit,
656			}
657			tc.Users[inst.From].Accounts[inst.TokenID].Nonce++
658			nTx, err := common.NewPoolL2Tx(&tx)
659			if err != nil {
660				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
661			}
662			tx = *nTx
663			// perform signature and set it to tx.Signature
664			toSign, err := tx.HashToSign(tc.chainID)
665			if err != nil {
666				return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
667			}
668			sig := tc.Users[inst.From].BJJ.SignPoseidon(toSign)
669			tx.Signature = sig.Compress()
670			txs = append(txs, tx)
671		default:
672			return nil, tracerr.Wrap(fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.LineNum, inst.Typ))
673		}
674	}
675
676	return txs, nil
677}
678
679// RestartNonces sets all the Users.Accounts.Nonces to 0
680func (tc *Context) RestartNonces() {
681	for name, user := range tc.Users {
682		for tokenID := range user.Accounts {
683			tc.Users[name].Accounts[tokenID].Nonce = common.Nonce(0)
684		}
685	}
686}
687
688// generateKeys generates BabyJubJub & Address keys for the given list of user
689// names in a deterministic way. This means, that for the same given
690// 'userNames' in a certain order, the keys will be always the same.
691func (tc *Context) generateKeys(userNames []string) {
692	for i := 1; i < len(userNames)+1; i++ {
693		if _, ok := tc.Users[userNames[i-1]]; ok {
694			// account already created
695			continue
696		}
697		// babyjubjub key
698		var sk babyjub.PrivateKey
699		var iBytes [8]byte
700		binary.LittleEndian.PutUint64(iBytes[:], uint64(i))
701		copy(sk[:], iBytes[:]) // only for testing
702
703		// eth address
704		var key ecdsa.PrivateKey
705		key.D = big.NewInt(int64(i)) // only for testing
706		key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
707		key.Curve = ethCrypto.S256()
708		addr := ethCrypto.PubkeyToAddress(key.PublicKey)
709
710		u := User{
711			Name:     userNames[i-1],
712			BJJ:      &sk,
713			EthSk:    &key,
714			Addr:     addr,
715			Accounts: make(map[common.TokenID]*Account),
716		}
717		tc.Users[userNames[i-1]] = &u
718	}
719}
720
721// L1TxsToCommonL1Txs converts an array of []til.L1Tx to []common.L1Tx
722func L1TxsToCommonL1Txs(l1 []L1Tx) []common.L1Tx {
723	var r []common.L1Tx
724	for i := 0; i < len(l1); i++ {
725		r = append(r, l1[i].L1Tx)
726	}
727	return r
728}
729
730// ConfigExtra is the configuration used in FillBlocksExtra to extend the
731// blocks returned by til.
732type ConfigExtra struct {
733	// Address to set as forger for each batch
734	BootCoordAddr ethCommon.Address
735	// Coordinator user name used to select the corresponding accounts to
736	// collect coordinator fees
737	CoordUser string
738}
739
740// FillBlocksL1UserTxsBatchNum fills the BatchNum of forged L1UserTxs:
741// - blocks[].Rollup.L1UserTxs[].BatchNum
742func (tc *Context) FillBlocksL1UserTxsBatchNum(blocks []common.BlockData) {
743	for i := range blocks {
744		block := &blocks[i]
745		for j := range block.Rollup.Batches {
746			batch := &block.Rollup.Batches[j]
747			if batch.L1Batch {
748				// Set BatchNum for forged L1UserTxs to til blocks
749				bn := batch.Batch.BatchNum
750				for k := range blocks {
751					block := &blocks[k]
752					for l := range block.Rollup.L1UserTxs {
753						tx := &block.Rollup.L1UserTxs[l]
754						if *tx.ToForgeL1TxsNum == tc.extra.openToForge {
755							tx.BatchNum = &bn
756						}
757					}
758				}
759				tc.extra.openToForge++
760			}
761		}
762	}
763}
764
765// FillBlocksForgedL1UserTxs fills the L1UserTxs of a batch with the L1UserTxs
766// that are forged in that batch.  It always sets `EffectiveAmount` = `Amount`
767// and `EffectiveDepositAmount` = `DepositAmount`.  This function requires a
768// previous call to `FillBlocksExtra`.
769// - blocks[].Rollup.L1UserTxs[].BatchNum
770// - blocks[].Rollup.L1UserTxs[].EffectiveAmount
771// - blocks[].Rollup.L1UserTxs[].EffectiveDepositAmount
772// - blocks[].Rollup.L1UserTxs[].EffectiveFromIdx
773func (tc *Context) FillBlocksForgedL1UserTxs(blocks []common.BlockData) error {
774	for i := range blocks {
775		block := &blocks[i]
776		for j := range block.Rollup.Batches {
777			batch := &block.Rollup.Batches[j]
778			if batch.L1Batch {
779				batchNum := batch.Batch.BatchNum
780				queue := tc.Queues[int(*batch.Batch.ForgeL1TxsNum)]
781				batch.L1UserTxs = make([]common.L1Tx, len(queue))
782				for k := range queue {
783					tx := &batch.L1UserTxs[k]
784					*tx = queue[k].L1Tx
785					tx.EffectiveAmount = tx.Amount
786					tx.EffectiveDepositAmount = tx.DepositAmount
787					tx.BatchNum = &batchNum
788					_tx, err := common.NewL1Tx(tx)
789					if err != nil {
790						return tracerr.Wrap(err)
791					}
792					*tx = *_tx
793					if tx.FromIdx == 0 {
794						tx.EffectiveFromIdx = tc.extra.idxByTxID[tx.TxID]
795					} else {
796						tx.EffectiveFromIdx = tx.FromIdx
797					}
798				}
799			}
800		}
801	}
802	return nil
803}
804
805// FillBlocksExtra fills extra fields not generated by til in each block, so
806// that the blockData is closer to what the HistoryDB stores.  The filled
807// fields are:
808// - blocks[].Rollup.Batch.EthBlockNum
809// - blocks[].Rollup.Batch.ForgerAddr
810// - blocks[].Rollup.Batch.ForgeL1TxsNum
811// - blocks[].Rollup.Batch.L1CoordinatorTxs[].TxID
812// - blocks[].Rollup.Batch.L1CoordinatorTxs[].BatchNum
813// - blocks[].Rollup.Batch.L1CoordinatorTxs[].EthBlockNum
814// - blocks[].Rollup.Batch.L1CoordinatorTxs[].Position
815// - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveAmount
816// - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveDepositAmount
817// - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveFromIdx
818// - blocks[].Rollup.Batch.L2Txs[].TxID
819// - blocks[].Rollup.Batch.L2Txs[].Position
820// - blocks[].Rollup.Batch.L2Txs[].Nonce
821// - blocks[].Rollup.Batch.L2Txs[].TokenID
822// - blocks[].Rollup.Batch.ExitTree
823// - blocks[].Rollup.Batch.CreatedAccounts
824// - blocks[].Rollup.Batch.FeeIdxCoordinator
825// - blocks[].Rollup.Batch.CollectedFees
826func (tc *Context) FillBlocksExtra(blocks []common.BlockData, cfg *ConfigExtra) error {
827	// Fill extra fields not generated by til in til block
828	for i := range blocks {
829		block := &blocks[i]
830		for j := range block.Rollup.Batches {
831			batch := &block.Rollup.Batches[j]
832			batch.Batch.EthBlockNum = block.Block.Num
833			// til doesn't fill the batch forger addr
834			batch.Batch.ForgerAddr = cfg.BootCoordAddr
835			if batch.L1Batch {
836				toForgeL1TxsNumCpy := tc.extra.toForgeL1TxsNum
837				// til doesn't fill the ForgeL1TxsNum
838				batch.Batch.ForgeL1TxsNum = &toForgeL1TxsNumCpy
839				tc.extra.toForgeL1TxsNum++
840			}
841
842			batchNum := batch.Batch.BatchNum
843			for k := range batch.L1CoordinatorTxs {
844				tx := &batch.L1CoordinatorTxs[k]
845				tx.BatchNum = &batchNum
846				tx.EthBlockNum = batch.Batch.EthBlockNum
847			}
848		}
849	}
850
851	// Fill CreatedAccounts
852	for i := range blocks {
853		block := &blocks[i]
854		for j := range block.Rollup.Batches {
855			batch := &block.Rollup.Batches[j]
856			l1Txs := []*common.L1Tx{}
857			if batch.L1Batch {
858				for k := range tc.Queues[*batch.Batch.ForgeL1TxsNum] {
859					l1Txs = append(l1Txs, &tc.Queues[*batch.Batch.ForgeL1TxsNum][k].L1Tx)
860				}
861			}
862			for k := range batch.L1CoordinatorTxs {
863				l1Txs = append(l1Txs, &batch.L1CoordinatorTxs[k])
864			}
865			for k := range l1Txs {
866				tx := l1Txs[k]
867				if tx.Type == common.TxTypeCreateAccountDeposit ||
868					tx.Type == common.TxTypeCreateAccountDepositTransfer {
869					user, ok := tc.UsersByIdx[tc.extra.idx]
870					if !ok {
871						return tracerr.Wrap(fmt.Errorf("Created account with idx: %v not found", tc.extra.idx))
872					}
873					batch.CreatedAccounts = append(batch.CreatedAccounts,
874						common.Account{
875							Idx:      common.Idx(tc.extra.idx),
876							TokenID:  tx.TokenID,
877							BatchNum: batch.Batch.BatchNum,
878							BJJ:      user.BJJ.Public().Compress(),
879							EthAddr:  user.Addr,
880							Nonce:    0,
881							Balance:  big.NewInt(0),
882						})
883					if !tx.UserOrigin {
884						tx.EffectiveFromIdx = common.Idx(tc.extra.idx)
885					}
886					tc.extra.idxByTxID[tx.TxID] = common.Idx(tc.extra.idx)
887					tc.extra.idx++
888				}
889			}
890		}
891	}
892
893	// Fill expected positions in L1CoordinatorTxs and L2Txs
894	for i := range blocks {
895		block := &blocks[i]
896		for j := range block.Rollup.Batches {
897			batch := &block.Rollup.Batches[j]
898			position := 0
899			if batch.L1Batch {
900				position = len(tc.Queues[*batch.Batch.ForgeL1TxsNum])
901			}
902			for k := range batch.L1CoordinatorTxs {
903				tx := &batch.L1CoordinatorTxs[k]
904				tx.Position = position
905				position++
906				tx.EffectiveAmount = big.NewInt(0)
907				tx.EffectiveDepositAmount = big.NewInt(0)
908				nTx, err := common.NewL1Tx(tx)
909				if err != nil {
910					return tracerr.Wrap(err)
911				}
912				*tx = *nTx
913			}
914			for k := range batch.L2Txs {
915				tx := &batch.L2Txs[k]
916				tx.Position = position
917				position++
918				tx.Nonce = tc.extra.nonces[tx.FromIdx]
919				tx.TokenID = tc.accountsByIdx[int(tx.FromIdx)].TokenID
920				tc.extra.nonces[tx.FromIdx]++
921				if err := tx.SetID(); err != nil {
922					return tracerr.Wrap(err)
923				}
924				nTx, err := common.NewL2Tx(tx)
925				if err != nil {
926					return tracerr.Wrap(err)
927				}
928				*tx = *nTx
929			}
930		}
931	}
932
933	// Fill ExitTree (only AccountIdx and Balance)
934	for i := range blocks {
935		block := &blocks[i]
936		for j := range block.Rollup.Batches {
937			batch := &block.Rollup.Batches[j]
938			if batch.L1Batch {
939				for _, _tx := range tc.Queues[*batch.Batch.ForgeL1TxsNum] {
940					tx := _tx.L1Tx
941					if tx.Type == common.TxTypeForceExit {
942						batch.ExitTree =
943							append(batch.ExitTree,
944								common.ExitInfo{
945									BatchNum:   batch.Batch.BatchNum,
946									AccountIdx: tx.FromIdx,
947									Balance:    tx.Amount,
948								})
949					}
950				}
951			}
952			for k := range batch.L2Txs {
953				tx := &batch.L2Txs[k]
954				if tx.Type == common.TxTypeExit {
955					batch.ExitTree = append(batch.ExitTree, common.ExitInfo{
956						BatchNum:   batch.Batch.BatchNum,
957						AccountIdx: tx.FromIdx,
958						Balance:    tx.Amount,
959					})
960				}
961				fee, err := common.CalcFeeAmount(tx.Amount, tx.Fee)
962				if err != nil {
963					return tracerr.Wrap(err)
964				}
965
966				// Find the TokenID of the tx
967				fromAcc, ok := tc.accountsByIdx[int(tx.FromIdx)]
968				if !ok {
969					return tracerr.Wrap(fmt.Errorf("L2tx.FromIdx idx: %v not found", tx.FromIdx))
970				}
971
972				// Find the idx of the CoordUser for the
973				// TokenID, and if it exists, add the fee to
974				// the collectedFees.  Only consider the
975				// coordinator account to receive fee if it was
976				// created in this or a previous batch
977				if acc, ok := tc.l1CreatedAccounts[idxTokenIDToString(cfg.CoordUser, fromAcc.TokenID)]; ok &&
978					common.BatchNum(acc.BatchNum) <= batch.Batch.BatchNum {
979					found := false
980					for _, idx := range batch.Batch.FeeIdxsCoordinator {
981						if idx == common.Idx(acc.Idx) {
982							found = true
983							break
984						}
985					}
986					if !found {
987						batch.Batch.FeeIdxsCoordinator = append(batch.Batch.FeeIdxsCoordinator,
988							common.Idx(acc.Idx))
989						batch.Batch.CollectedFees[fromAcc.TokenID] = big.NewInt(0)
990					}
991					collected := batch.Batch.CollectedFees[fromAcc.TokenID]
992					collected.Add(collected, fee)
993				}
994			}
995		}
996	}
997	return nil
998}