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