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