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