arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/txselector/txselector.go
txselector/txselector.goRaw
  1package txselector
  2
  3// current: very simple version of TxSelector
  4
  5import (
  6	"fmt"
  7	"math/big"
  8	"sort"
  9
 10	ethCommon "github.com/ethereum/go-ethereum/common"
 11	"github.com/hermeznetwork/hermez-node/common"
 12	"github.com/hermeznetwork/hermez-node/db/kvdb"
 13	"github.com/hermeznetwork/hermez-node/db/l2db"
 14	"github.com/hermeznetwork/hermez-node/db/statedb"
 15	"github.com/hermeznetwork/hermez-node/log"
 16	"github.com/hermeznetwork/hermez-node/txprocessor"
 17	"github.com/hermeznetwork/tracerr"
 18	"github.com/iden3/go-iden3-crypto/babyjub"
 19)
 20
 21// CoordAccount contains the data of the Coordinator account, that will be used
 22// to create new transactions of CreateAccountDeposit type to add new TokenID
 23// accounts for the Coordinator to receive the fees.
 24type CoordAccount struct {
 25	Addr                ethCommon.Address
 26	BJJ                 babyjub.PublicKeyComp
 27	AccountCreationAuth []byte // signature in byte array format
 28}
 29
 30// TxSelector implements all the functionalities to select the txs for the next
 31// batch
 32type TxSelector struct {
 33	l2db            *l2db.L2DB
 34	localAccountsDB *statedb.LocalStateDB
 35
 36	coordAccount *CoordAccount
 37}
 38
 39// NewTxSelector returns a *TxSelector
 40func NewTxSelector(coordAccount *CoordAccount, dbpath string,
 41	synchronizerStateDB *statedb.StateDB, l2 *l2db.L2DB) (*TxSelector, error) {
 42	localAccountsDB, err := statedb.NewLocalStateDB(
 43		statedb.Config{
 44			Path:    dbpath,
 45			Keep:    kvdb.DefaultKeep,
 46			Type:    statedb.TypeTxSelector,
 47			NLevels: 0,
 48		},
 49		synchronizerStateDB) // without merkletree
 50	if err != nil {
 51		return nil, tracerr.Wrap(err)
 52	}
 53
 54	return &TxSelector{
 55		l2db:            l2,
 56		localAccountsDB: localAccountsDB,
 57		coordAccount:    coordAccount,
 58	}, nil
 59}
 60
 61// LocalAccountsDB returns the LocalStateDB of the TxSelector
 62func (txsel *TxSelector) LocalAccountsDB() *statedb.LocalStateDB {
 63	return txsel.localAccountsDB
 64}
 65
 66// Reset tells the TxSelector to get it's internal AccountsDB
 67// from the required `batchNum`
 68func (txsel *TxSelector) Reset(batchNum common.BatchNum, fromSynchronizer bool) error {
 69	return tracerr.Wrap(txsel.localAccountsDB.Reset(batchNum, fromSynchronizer))
 70}
 71
 72func (txsel *TxSelector) getCoordIdx(tokenID common.TokenID) (common.Idx, error) {
 73	return txsel.localAccountsDB.GetIdxByEthAddrBJJ(txsel.coordAccount.Addr,
 74		txsel.coordAccount.BJJ, tokenID)
 75}
 76
 77// coordAccountForTokenID creates a new L1CoordinatorTx to create a new
 78// Coordinator account for the given TokenID in the case that the account does
 79// not exist yet in the db, and does not exist a L1CoordinatorTx to creat that
 80// account in the given array of L1CoordinatorTxs. If a new Coordinator account
 81// needs to be created, a new L1CoordinatorTx will be returned from this
 82// function. After calling this method, if the l1CoordinatorTx is added to the
 83// selection, positionL1 must be increased 1.
 84func (txsel *TxSelector) coordAccountForTokenID(l1CoordinatorTxs []common.L1Tx,
 85	tokenID common.TokenID, positionL1 int) (*common.L1Tx, int, error) {
 86	// check if CoordinatorAccount for TokenID is already pending to create
 87	if checkAlreadyPendingToCreate(l1CoordinatorTxs, tokenID,
 88		txsel.coordAccount.Addr, txsel.coordAccount.BJJ) {
 89		return nil, positionL1, nil
 90	}
 91	_, err := txsel.getCoordIdx(tokenID)
 92	if tracerr.Unwrap(err) == statedb.ErrIdxNotFound {
 93		// create L1CoordinatorTx to create new CoordAccount for
 94		// TokenID
 95		l1CoordinatorTx := common.L1Tx{
 96			Position:      positionL1,
 97			UserOrigin:    false,
 98			FromEthAddr:   txsel.coordAccount.Addr,
 99			FromBJJ:       txsel.coordAccount.BJJ,
100			TokenID:       tokenID,
101			Amount:        big.NewInt(0),
102			DepositAmount: big.NewInt(0),
103			Type:          common.TxTypeCreateAccountDeposit,
104		}
105
106		return &l1CoordinatorTx, positionL1, nil
107	}
108	if err != nil {
109		return nil, positionL1, tracerr.Wrap(err)
110	}
111	// CoordAccount for TokenID already exists
112	return nil, positionL1, nil
113}
114
115// GetL2TxSelection returns the L1CoordinatorTxs and a selection of the L2Txs
116// for the next batch, from the L2DB pool.
117// It returns: the CoordinatorIdxs used to receive the fees of the selected
118// L2Txs. An array of bytearrays with the signatures of the
119// AccountCreationAuthorization of the accounts of the users created by the
120// Coordinator with L1CoordinatorTxs of those accounts that does not exist yet
121// but there is a transactions to them and the authorization of account
122// creation exists. The L1UserTxs, L1CoordinatorTxs, PoolL2Txs that will be
123// included in the next batch.
124func (txsel *TxSelector) GetL2TxSelection(selectionConfig txprocessor.Config) ([]common.Idx,
125	[][]byte, []common.L1Tx, []common.PoolL2Tx, []common.PoolL2Tx, error) {
126	metricGetL2TxSelection.Inc()
127	coordIdxs, accCreationAuths, _, l1CoordinatorTxs, l2Txs,
128		discardedL2Txs, err := txsel.getL1L2TxSelection(selectionConfig, []common.L1Tx{})
129	return coordIdxs, accCreationAuths, l1CoordinatorTxs, l2Txs,
130		discardedL2Txs, tracerr.Wrap(err)
131}
132
133// GetL1L2TxSelection returns the selection of L1 + L2 txs.
134// It returns: the CoordinatorIdxs used to receive the fees of the selected
135// L2Txs. An array of bytearrays with the signatures of the
136// AccountCreationAuthorization of the accounts of the users created by the
137// Coordinator with L1CoordinatorTxs of those accounts that does not exist yet
138// but there is a transactions to them and the authorization of account
139// creation exists. The L1UserTxs, L1CoordinatorTxs, PoolL2Txs that will be
140// included in the next batch.
141func (txsel *TxSelector) GetL1L2TxSelection(selectionConfig txprocessor.Config,
142	l1UserTxs []common.L1Tx) ([]common.Idx, [][]byte, []common.L1Tx,
143	[]common.L1Tx, []common.PoolL2Tx, []common.PoolL2Tx, error) {
144	metricGetL1L2TxSelection.Inc()
145	coordIdxs, accCreationAuths, l1UserTxs, l1CoordinatorTxs, l2Txs,
146		discardedL2Txs, err := txsel.getL1L2TxSelection(selectionConfig, l1UserTxs)
147	return coordIdxs, accCreationAuths, l1UserTxs, l1CoordinatorTxs, l2Txs,
148		discardedL2Txs, tracerr.Wrap(err)
149}
150
151// getL1L2TxSelection returns the selection of L1 + L2 txs.
152// It returns: the CoordinatorIdxs used to receive the fees of the selected
153// L2Txs. An array of bytearrays with the signatures of the
154// AccountCreationAuthorization of the accounts of the users created by the
155// Coordinator with L1CoordinatorTxs of those accounts that does not exist yet
156// but there is a transactions to them and the authorization of account
157// creation exists. The L1UserTxs, L1CoordinatorTxs, PoolL2Txs that will be
158// included in the next batch.
159func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
160	l1UserTxs []common.L1Tx) ([]common.Idx, [][]byte, []common.L1Tx,
161	[]common.L1Tx, []common.PoolL2Tx, []common.PoolL2Tx, error) {
162	// WIP.0: the TxSelector is not optimized and will need a redesign. The
163	// current version is implemented in order to have a functional
164	// implementation that can be used ASAP.
165
166	// Steps of this method:
167	// - getPendingTxs
168	// - ProcessL1Txs
169	// - getProfitable (sort by fee & nonce)
170	// - loop over l2Txs
171	//         - Fill tx.TokenID tx.Nonce
172	//         - Check enough Balance on sender
173	//         - Check Nonce
174	//         - Create CoordAccount L1CoordTx for TokenID if needed
175	//                 - & ProcessL1Tx of L1CoordTx
176	//         - Check validity of receiver Account for ToEthAddr / ToBJJ
177	//         - Create UserAccount L1CoordTx if needed (and possible)
178	//         - If everything is fine, store l2Tx to validTxs & update NoncesMap
179	// - Prepare coordIdxsMap & AccumulatedFees
180	// - Distribute AccumulatedFees to CoordIdxs
181	// - MakeCheckpoint
182
183	// get pending l2-tx from tx-pool
184	l2TxsRaw, err := txsel.l2db.GetPendingTxs()
185	if err != nil {
186		return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
187	}
188
189	txselStateDB := txsel.localAccountsDB.StateDB
190	tp := txprocessor.NewTxProcessor(txselStateDB, selectionConfig)
191
192	// Process L1UserTxs
193	for i := 0; i < len(l1UserTxs); i++ {
194		// assumption: l1usertx are sorted by L1Tx.Position
195		_, _, _, _, err := tp.ProcessL1Tx(nil, &l1UserTxs[i])
196		if err != nil {
197			return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
198		}
199	}
200
201	var l1CoordinatorTxs []common.L1Tx
202	positionL1 := len(l1UserTxs)
203	var accAuths [][]byte
204
205	// Sort l2TxsRaw (cropping at MaxTx at this point).
206	// discardedL2Txs contains an array of the L2Txs that have not been
207	// selected in this Batch.
208	l2Txs, discardedL2Txs := txsel.getL2Profitable(l2TxsRaw, selectionConfig.MaxTx-uint32(len(l1UserTxs)))
209	for i := range discardedL2Txs {
210		discardedL2Txs[i].Info =
211			"Tx not selected due to low absolute fee (does not fit inside the profitable set)"
212	}
213
214	var validTxs []common.PoolL2Tx
215	tp.AccumulatedFees = make(map[common.Idx]*big.Int)
216	// Iterate over l2Txs
217	// - check Nonces
218	// - check enough Balance for the Amount+Fee
219	// - if needed, create new L1CoordinatorTxs for unexisting ToIdx
220	// 	- keep used accAuths
221	// - put the valid txs into validTxs array
222	for i := 0; i < len(l2Txs); i++ {
223		// Check if there is space for more L2Txs in the selection
224		maxL2Txs := int(selectionConfig.MaxTx) -
225			len(l1UserTxs) - len(l1CoordinatorTxs)
226		if len(validTxs) >= maxL2Txs {
227			// no more available slots for L2Txs
228			l2Txs[i].Info =
229				"Tx not selected due not available slots for L2Txs"
230			discardedL2Txs = append(discardedL2Txs, l2Txs[i])
231			continue
232		}
233
234		// get Nonce & TokenID from the Account by l2Tx.FromIdx
235		accSender, err := tp.StateDB().GetAccount(l2Txs[i].FromIdx)
236		if err != nil {
237			return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
238		}
239		l2Txs[i].TokenID = accSender.TokenID
240
241		// Check enough Balance on sender
242		enoughBalance, balance, feeAndAmount := tp.CheckEnoughBalance(l2Txs[i])
243		if !enoughBalance {
244			// not valid Amount with current Balance. Discard L2Tx,
245			// and update Info parameter of the tx, and add it to
246			// the discardedTxs array
247			l2Txs[i].Info = fmt.Sprintf("Tx not selected due to not enough Balance at the sender. "+
248				"Current sender account Balance: %s, Amount+Fee: %s",
249				balance.String(), feeAndAmount.String())
250			discardedL2Txs = append(discardedL2Txs, l2Txs[i])
251			continue
252		}
253
254		// Check if Nonce is correct
255		if l2Txs[i].Nonce != accSender.Nonce {
256			// not valid Nonce at tx. Discard L2Tx, and update Info
257			// parameter of the tx, and add it to the discardedTxs
258			// array
259			l2Txs[i].Info = fmt.Sprintf("Tx not selected due to not current Nonce. "+
260				"Tx.Nonce: %d, Account.Nonce: %d", l2Txs[i].Nonce, accSender.Nonce)
261			discardedL2Txs = append(discardedL2Txs, l2Txs[i])
262			continue
263		}
264
265		// if TokenID does not exist yet, create new L1CoordinatorTx to
266		// create the CoordinatorAccount for that TokenID, to receive
267		// the fees. Only in the case that there does not exist yet a
268		// pending L1CoordinatorTx to create the account for the
269		// Coordinator for that TokenID
270		var newL1CoordTx *common.L1Tx
271		newL1CoordTx, positionL1, err =
272			txsel.coordAccountForTokenID(l1CoordinatorTxs,
273				accSender.TokenID, positionL1)
274		if err != nil {
275			return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
276		}
277		if newL1CoordTx != nil {
278			// if there is no space for the L1CoordinatorTx as MaxL1Tx, or no space
279			// for L1CoordinatorTx + L2Tx as MaxTx, discard the L2Tx
280			if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-len(l1UserTxs) ||
281				len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-len(l1UserTxs) {
282				// discard L2Tx, and update Info parameter of
283				// the tx, and add it to the discardedTxs array
284				l2Txs[i].Info = "Tx not selected because the L2Tx depends on a " +
285					"L1CoordinatorTx and there is not enough space for L1Coordinator"
286				discardedL2Txs = append(discardedL2Txs, l2Txs[i])
287				continue
288			}
289			// increase positionL1
290			positionL1++
291			l1CoordinatorTxs = append(l1CoordinatorTxs, *newL1CoordTx)
292			accAuths = append(accAuths, txsel.coordAccount.AccountCreationAuth)
293
294			// process the L1CoordTx
295			_, _, _, _, err := tp.ProcessL1Tx(nil, newL1CoordTx)
296			if err != nil {
297				return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
298			}
299		}
300
301		// If tx.ToIdx>=256, tx.ToIdx should exist to localAccountsDB,
302		// if so, tx is used.  If tx.ToIdx==0, for an L2Tx will be the
303		// case of TxToEthAddr or TxToBJJ, check if
304		// tx.ToEthAddr/tx.ToBJJ exist in localAccountsDB, if yes tx is
305		// used; if not, check if tx.ToEthAddr is in
306		// AccountCreationAuthDB, if so, tx is used and L1CoordinatorTx
307		// of CreateAccountAndDeposit is created. If tx.ToIdx==1, is a
308		// Exit type and is used.
309		if l2Txs[i].ToIdx == 0 { // ToEthAddr/ToBJJ case
310			validL2Tx, l1CoordinatorTx, accAuth, err :=
311				txsel.processTxToEthAddrBJJ(validTxs, selectionConfig,
312					len(l1UserTxs), l1CoordinatorTxs, positionL1, l2Txs[i])
313			if err != nil {
314				log.Debugw("txsel.processTxToEthAddrBJJ", "err", err)
315				// Discard L2Tx, and update Info parameter of
316				// the tx, and add it to the discardedTxs array
317				l2Txs[i].Info = fmt.Sprintf("Tx not selected (in processTxToEthAddrBJJ) due to %s",
318					err.Error())
319				discardedL2Txs = append(discardedL2Txs, l2Txs[i])
320				continue
321			}
322			// if there is no space for the L1CoordinatorTx as MaxL1Tx, or no space
323			// for L1CoordinatorTx + L2Tx as MaxTx, discard the L2Tx
324			if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-len(l1UserTxs) ||
325				len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-len(l1UserTxs) {
326				// discard L2Tx, and update Info parameter of
327				// the tx, and add it to the discardedTxs array
328				l2Txs[i].Info = "Tx not selected because the L2Tx depends on a " +
329					"L1CoordinatorTx and there is not enough space for L1Coordinator"
330				discardedL2Txs = append(discardedL2Txs, l2Txs[i])
331				continue
332			}
333
334			if l1CoordinatorTx != nil && validL2Tx != nil {
335				// If ToEthAddr == 0xff.. this means that we
336				// are handling a TransferToBJJ, which doesn't
337				// require an authorization because it doesn't
338				// contain a valid ethereum address.
339				// Otherwise only create the account if we have
340				// the corresponding authorization
341				if validL2Tx.ToEthAddr == common.FFAddr {
342					accAuths = append(accAuths, common.EmptyEthSignature)
343					l1CoordinatorTxs = append(l1CoordinatorTxs, *l1CoordinatorTx)
344					positionL1++
345				} else if accAuth != nil {
346					accAuths = append(accAuths, accAuth.Signature)
347					l1CoordinatorTxs = append(l1CoordinatorTxs, *l1CoordinatorTx)
348					positionL1++
349				}
350
351				// process the L1CoordTx
352				_, _, _, _, err := tp.ProcessL1Tx(nil, l1CoordinatorTx)
353				if err != nil {
354					return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
355				}
356			}
357			if validL2Tx == nil {
358				discardedL2Txs = append(discardedL2Txs, l2Txs[i])
359				continue
360			}
361		} else if l2Txs[i].ToIdx >= common.IdxUserThreshold {
362			receiverAcc, err := txsel.localAccountsDB.GetAccount(l2Txs[i].ToIdx)
363			if err != nil {
364				// tx not valid
365				log.Debugw("invalid L2Tx: ToIdx not found in StateDB",
366					"ToIdx", l2Txs[i].ToIdx)
367				// Discard L2Tx, and update Info parameter of
368				// the tx, and add it to the discardedTxs array
369				l2Txs[i].Info = fmt.Sprintf("Tx not selected due to tx.ToIdx not found in StateDB. "+
370					"ToIdx: %d", l2Txs[i].ToIdx)
371				discardedL2Txs = append(discardedL2Txs, l2Txs[i])
372				continue
373			}
374			if l2Txs[i].ToEthAddr != common.EmptyAddr {
375				if l2Txs[i].ToEthAddr != receiverAcc.EthAddr {
376					log.Debugw("invalid L2Tx: ToEthAddr does not correspond to the Account.EthAddr",
377						"ToIdx", l2Txs[i].ToIdx, "tx.ToEthAddr",
378						l2Txs[i].ToEthAddr, "account.EthAddr", receiverAcc.EthAddr)
379					// Discard L2Tx, and update Info
380					// parameter of the tx, and add it to
381					// the discardedTxs array
382					l2Txs[i].Info = fmt.Sprintf("Tx not selected because ToEthAddr "+
383						"does not correspond to the Account.EthAddr. "+
384						"tx.ToIdx: %d, tx.ToEthAddr: %s, account.EthAddr: %s",
385						l2Txs[i].ToIdx, l2Txs[i].ToEthAddr, receiverAcc.EthAddr)
386					discardedL2Txs = append(discardedL2Txs, l2Txs[i])
387					continue
388				}
389			}
390			if l2Txs[i].ToBJJ != common.EmptyBJJComp {
391				if l2Txs[i].ToBJJ != receiverAcc.BJJ {
392					log.Debugw("invalid L2Tx: ToBJJ does not correspond to the Account.BJJ",
393						"ToIdx", l2Txs[i].ToIdx, "tx.ToEthAddr", l2Txs[i].ToBJJ,
394						"account.BJJ", receiverAcc.BJJ)
395					// Discard L2Tx, and update Info
396					// parameter of the tx, and add it to
397					// the discardedTxs array
398					l2Txs[i].Info = fmt.Sprintf("Tx not selected because tx.ToBJJ "+
399						"does not correspond to the Account.BJJ. "+
400						"tx.ToIdx: %d, tx.ToEthAddr: %s, tx.ToBJJ: %s, account.BJJ: %s",
401						l2Txs[i].ToIdx, l2Txs[i].ToEthAddr, l2Txs[i].ToBJJ, receiverAcc.BJJ)
402					discardedL2Txs = append(discardedL2Txs, l2Txs[i])
403					continue
404				}
405			}
406		}
407
408		// get CoordIdxsMap for the TokenID of the current l2Txs[i]
409		// get TokenID from tx.Sender account
410		tokenID := accSender.TokenID
411		coordIdx, err := txsel.getCoordIdx(tokenID)
412		if err != nil {
413			// if err is db.ErrNotFound, should not happen, as all
414			// the validTxs.TokenID should have a CoordinatorIdx
415			// created in the DB at this point
416			return nil, nil, nil, nil, nil, nil,
417				tracerr.Wrap(fmt.Errorf("Could not get CoordIdx for TokenID=%d, "+
418					"due: %s", tokenID, err))
419		}
420		// prepare temp coordIdxsMap & AccumulatedFees for the call to
421		// ProcessL2Tx
422		coordIdxsMap := map[common.TokenID]common.Idx{tokenID: coordIdx}
423		// tp.AccumulatedFees = make(map[common.Idx]*big.Int)
424		if _, ok := tp.AccumulatedFees[coordIdx]; !ok {
425			tp.AccumulatedFees[coordIdx] = big.NewInt(0)
426		}
427
428		_, _, _, err = tp.ProcessL2Tx(coordIdxsMap, nil, nil, &l2Txs[i])
429		if err != nil {
430			log.Debugw("txselector.getL1L2TxSelection at ProcessL2Tx", "err", err)
431			// Discard L2Tx, and update Info parameter of the tx,
432			// and add it to the discardedTxs array
433			l2Txs[i].Info = fmt.Sprintf("Tx not selected (in ProcessL2Tx) due to %s",
434				err.Error())
435			discardedL2Txs = append(discardedL2Txs, l2Txs[i])
436			continue
437		}
438
439		validTxs = append(validTxs, l2Txs[i])
440	} // after this loop, no checks to discard txs should be done
441
442	// get CoordIdxsMap for the TokenIDs
443	coordIdxsMap := make(map[common.TokenID]common.Idx)
444	for i := 0; i < len(validTxs); i++ {
445		// get TokenID from tx.Sender
446		accSender, err := tp.StateDB().GetAccount(validTxs[i].FromIdx)
447		if err != nil {
448			return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
449		}
450		tokenID := accSender.TokenID
451
452		coordIdx, err := txsel.getCoordIdx(tokenID)
453		if err != nil {
454			// if err is db.ErrNotFound, should not happen, as all
455			// the validTxs.TokenID should have a CoordinatorIdx
456			// created in the DB at this point
457			return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
458		}
459		coordIdxsMap[tokenID] = coordIdx
460	}
461
462	var coordIdxs []common.Idx
463	for _, idx := range coordIdxsMap {
464		coordIdxs = append(coordIdxs, idx)
465	}
466	// sort CoordIdxs
467	sort.SliceStable(coordIdxs, func(i, j int) bool {
468		return coordIdxs[i] < coordIdxs[j]
469	})
470
471	// distribute the AccumulatedFees from the processed L2Txs into the
472	// Coordinator Idxs
473	for idx, accumulatedFee := range tp.AccumulatedFees {
474		cmp := accumulatedFee.Cmp(big.NewInt(0))
475		if cmp == 1 { // accumulatedFee>0
476			// send the fee to the Idx of the Coordinator for the TokenID
477			accCoord, err := txsel.localAccountsDB.GetAccount(idx)
478			if err != nil {
479				log.Errorw("Can not distribute accumulated fees to coordinator "+
480					"account: No coord Idx to receive fee", "idx", idx)
481				return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
482			}
483			accCoord.Balance = new(big.Int).Add(accCoord.Balance, accumulatedFee)
484			_, err = txsel.localAccountsDB.UpdateAccount(idx, accCoord)
485			if err != nil {
486				log.Error(err)
487				return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
488			}
489		}
490	}
491
492	err = tp.StateDB().MakeCheckpoint()
493	if err != nil {
494		return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
495	}
496
497	metricSelectedL1CoordinatorTxs.Set(float64(len(l1CoordinatorTxs)))
498	metricSelectedL1UserTxs.Set(float64(len(l1UserTxs)))
499	metricSelectedL2Txs.Set(float64(len(validTxs)))
500	metricDiscardedL2Txs.Set(float64(len(discardedL2Txs)))
501
502	// return coordIdxs, accAuths, l1UserTxs, l1CoordinatorTxs, validTxs, discardedL2Txs, nil
503	return coordIdxs, accAuths, l1UserTxs, l1CoordinatorTxs, validTxs, discardedL2Txs, nil
504}
505
506// processTxsToEthAddrBJJ process the common.PoolL2Tx in the case where
507// ToIdx==0, which can be the tx type of ToEthAddr or ToBJJ. If the receiver
508// does not have an account yet, a new L1CoordinatorTx of type
509// CreateAccountDeposit (with 0 as DepositAmount) is created and added to the
510// l1CoordinatorTxs array, and then the PoolL2Tx is added into the validTxs
511// array.
512func (txsel *TxSelector) processTxToEthAddrBJJ(validTxs []common.PoolL2Tx,
513	selectionConfig txprocessor.Config, nL1UserTxs int, l1CoordinatorTxs []common.L1Tx,
514	positionL1 int, l2Tx common.PoolL2Tx) (*common.PoolL2Tx, *common.L1Tx,
515	*common.AccountCreationAuth, error) {
516	// if L2Tx needs a new L1CoordinatorTx of CreateAccount type, and a
517	// previous L2Tx in the current process already created a
518	// L1CoordinatorTx of this type, in the DB there still seem that needs
519	// to create a new L1CoordinatorTx, but as is already created, the tx
520	// is valid
521	if checkAlreadyPendingToCreate(l1CoordinatorTxs, l2Tx.TokenID, l2Tx.ToEthAddr, l2Tx.ToBJJ) {
522		return &l2Tx, nil, nil, nil
523	}
524
525	var l1CoordinatorTx *common.L1Tx
526	var accAuth *common.AccountCreationAuth
527	if l2Tx.ToEthAddr != common.EmptyAddr && l2Tx.ToEthAddr != common.FFAddr {
528		// case: ToEthAddr != 0x00 neither 0xff
529		if l2Tx.ToBJJ != common.EmptyBJJComp {
530			// case: ToBJJ!=0:
531			// if idx exist for EthAddr&BJJ use it
532			_, err := txsel.localAccountsDB.GetIdxByEthAddrBJJ(l2Tx.ToEthAddr,
533				l2Tx.ToBJJ, l2Tx.TokenID)
534			if err == nil {
535				// account for ToEthAddr&ToBJJ already exist,
536				// there is no need to create a new one.
537				// tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx
538				return &l2Tx, nil, nil, nil
539			}
540			// if not, check if AccountCreationAuth exist for that
541			// ToEthAddr
542			accAuth, err = txsel.l2db.GetAccountCreationAuth(l2Tx.ToEthAddr)
543			if err != nil {
544				// not found, l2Tx will not be added in the selection
545				return nil, nil, nil,
546					tracerr.Wrap(fmt.Errorf("invalid L2Tx: ToIdx not found "+
547						"in StateDB, neither ToEthAddr found in AccountCreationAuths L2DB. ToIdx: %d, ToEthAddr: %s",
548						l2Tx.ToIdx, l2Tx.ToEthAddr.Hex()))
549			}
550			if accAuth.BJJ != l2Tx.ToBJJ {
551				// if AccountCreationAuth.BJJ is not the same
552				// than in the tx, tx is not accepted
553				return nil, nil, nil,
554					tracerr.Wrap(fmt.Errorf("invalid L2Tx: ToIdx not found in StateDB, "+
555						"neither ToEthAddr & ToBJJ found in AccountCreationAuths L2DB. "+
556						"ToIdx: %d, ToEthAddr: %s, ToBJJ: %s",
557						l2Tx.ToIdx, l2Tx.ToEthAddr.Hex(), l2Tx.ToBJJ.String()))
558			}
559		} else {
560			// case: ToBJJ==0:
561			// if idx exist for EthAddr use it
562			_, err := txsel.localAccountsDB.GetIdxByEthAddr(l2Tx.ToEthAddr, l2Tx.TokenID)
563			if err == nil {
564				// account for ToEthAddr already exist,
565				// there is no need to create a new one.
566				// tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx
567				return &l2Tx, nil, nil, nil
568			}
569			// if not, check if AccountCreationAuth exist for that ToEthAddr
570			accAuth, err = txsel.l2db.GetAccountCreationAuth(l2Tx.ToEthAddr)
571			if err != nil {
572				// not found, l2Tx will not be added in the selection
573				return nil, nil, nil,
574					tracerr.Wrap(fmt.Errorf("invalid L2Tx: ToIdx not found in "+
575						"StateDB, neither ToEthAddr found in "+
576						"AccountCreationAuths L2DB. ToIdx: %d, ToEthAddr: %s",
577						l2Tx.ToIdx, l2Tx.ToEthAddr))
578			}
579		}
580		// create L1CoordinatorTx for the accountCreation
581		l1CoordinatorTx = &common.L1Tx{
582			Position:      positionL1,
583			UserOrigin:    false,
584			FromEthAddr:   accAuth.EthAddr,
585			FromBJJ:       accAuth.BJJ,
586			TokenID:       l2Tx.TokenID,
587			Amount:        big.NewInt(0),
588			DepositAmount: big.NewInt(0),
589			Type:          common.TxTypeCreateAccountDeposit,
590		}
591	} else if l2Tx.ToEthAddr == common.FFAddr && l2Tx.ToBJJ != common.EmptyBJJComp {
592		// if idx exist for EthAddr&BJJ use it
593		_, err := txsel.localAccountsDB.GetIdxByEthAddrBJJ(l2Tx.ToEthAddr, l2Tx.ToBJJ,
594			l2Tx.TokenID)
595		if err == nil {
596			// account for ToEthAddr&ToBJJ already exist, (where ToEthAddr==0xff)
597			// there is no need to create a new one.
598			// tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx
599			return &l2Tx, nil, nil, nil
600		}
601		// if idx don't exist for EthAddr&BJJ, coordinator can create a
602		// new account without L1Authorization, as ToEthAddr==0xff
603		// create L1CoordinatorTx for the accountCreation
604		l1CoordinatorTx = &common.L1Tx{
605			Position:      positionL1,
606			UserOrigin:    false,
607			FromEthAddr:   l2Tx.ToEthAddr,
608			FromBJJ:       l2Tx.ToBJJ,
609			TokenID:       l2Tx.TokenID,
610			Amount:        big.NewInt(0),
611			DepositAmount: big.NewInt(0),
612			Type:          common.TxTypeCreateAccountDeposit,
613		}
614	}
615	// if there is no space for the L1CoordinatorTx as MaxL1Tx, or no space
616	// for L1CoordinatorTx + L2Tx as MaxTx, discard the L2Tx
617	if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-nL1UserTxs ||
618		len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-nL1UserTxs {
619		// L2Tx discarded
620		return nil, nil, nil, tracerr.Wrap(fmt.Errorf("L2Tx discarded due to no available slots " +
621			"for L1CoordinatorTx to create a new account for receiver of L2Tx"))
622	}
623
624	return &l2Tx, l1CoordinatorTx, accAuth, nil
625}
626
627func checkAlreadyPendingToCreate(l1CoordinatorTxs []common.L1Tx, tokenID common.TokenID,
628	addr ethCommon.Address, bjj babyjub.PublicKeyComp) bool {
629	for i := 0; i < len(l1CoordinatorTxs); i++ {
630		if l1CoordinatorTxs[i].FromEthAddr == addr &&
631			l1CoordinatorTxs[i].TokenID == tokenID &&
632			l1CoordinatorTxs[i].FromBJJ == bjj {
633			return true
634		}
635	}
636	return false
637}
638
639// getL2Profitable returns the profitable selection of L2Txssorted by Nonce
640func (txsel *TxSelector) getL2Profitable(l2Txs []common.PoolL2Tx, max uint32) ([]common.PoolL2Tx,
641	[]common.PoolL2Tx) {
642	// First sort by nonce so that txs from the same account are sorted so
643	// that they could be applied in succession.
644	sort.Slice(l2Txs, func(i, j int) bool {
645		return l2Txs[i].Nonce < l2Txs[j].Nonce
646	})
647	// Sort by absolute fee with SliceStable, so that txs with same
648	// AbsoluteFee are not rearranged and nonce order is kept in such case
649	sort.SliceStable(l2Txs, func(i, j int) bool {
650		return l2Txs[i].AbsoluteFee > l2Txs[j].AbsoluteFee
651	})
652
653	discardedL2Txs := []common.PoolL2Tx{}
654	if len(l2Txs) > int(max) {
655		discardedL2Txs = l2Txs[max:]
656		l2Txs = l2Txs[:max]
657	}
658
659	// sort l2Txs by Nonce. This can be done in many different ways, what
660	// is needed is to output the l2Txs where the Nonce of l2Txs for each
661	// Account is sorted, but the l2Txs can not be grouped by sender Account
662	// neither by Fee. This is because later on the Nonces will need to be
663	// sequential for the zkproof generation.
664	sort.Slice(l2Txs, func(i, j int) bool {
665		return l2Txs[i].Nonce < l2Txs[j].Nonce
666	})
667
668	return l2Txs, discardedL2Txs
669}