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