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