arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/txselector/txselector.go
txselector/txselector.goRaw
  1package txselector
  2
  3import (
  4	"sort"
  5
  6	"github.com/hermeznetwork/hermez-node/common"
  7	"github.com/hermeznetwork/hermez-node/db/l2db"
  8	"github.com/hermeznetwork/hermez-node/db/statedb"
  9)
 10
 11// txs implements the interface Sort for an array of Tx
 12type txs []common.PoolL2Tx
 13
 14func (t txs) Len() int {
 15	return len(t)
 16}
 17func (t txs) Swap(i, j int) {
 18	t[i], t[j] = t[j], t[i]
 19}
 20func (t txs) Less(i, j int) bool {
 21	return t[i].AbsoluteFee > t[j].AbsoluteFee
 22}
 23
 24// TxSelector implements all the functionalities to select the txs for the next batch
 25type TxSelector struct {
 26	// MaxL1UserTxs is the maximum L1-user-tx for a batch
 27	MaxL1UserTxs uint64
 28	// MaxL1OperatorTxs is the maximum L1-operator-tx for a batch
 29	MaxL1OperatorTxs uint64
 30	// MaxTxs is the maximum txs for a batch
 31	MaxTxs uint64
 32
 33	l2db            *l2db.L2DB
 34	localAccountsDB *statedb.LocalStateDB
 35}
 36
 37// NewTxSelector returns a *TxSelector
 38func NewTxSelector(synchronizerStateDB *statedb.StateDB, l2 *l2db.L2DB, maxL1UserTxs, maxL1OperatorTxs, maxTxs uint64) (*TxSelector, error) {
 39	localAccountsDB, err := statedb.NewLocalStateDB(synchronizerStateDB, false, 0) // without merkletree
 40	if err != nil {
 41		return nil, err
 42	}
 43
 44	return &TxSelector{
 45		MaxL1UserTxs:     maxL1UserTxs,
 46		MaxL1OperatorTxs: maxL1OperatorTxs,
 47		MaxTxs:           maxTxs,
 48		l2db:             l2,
 49		localAccountsDB:  localAccountsDB,
 50	}, nil
 51}
 52
 53// Reset tells the TxSelector to get it's internal AccountsDB
 54// from the required `batchNum`
 55func (txsel *TxSelector) Reset(batchNum int) error {
 56	err := txsel.localAccountsDB.Reset(batchNum, true)
 57	if err != nil {
 58		return err
 59	}
 60	return nil
 61}
 62
 63// GetL2TxSelection returns a selection of the L2Txs for the next batch, from the L2DB pool
 64func (txsel *TxSelector) GetL2TxSelection(batchNum int) ([]common.PoolL2Tx, error) {
 65	// get pending l2-tx from tx-pool
 66	l2TxsRaw, err := txsel.l2db.GetPendingTxs() // once l2db ready, maybe use parameter 'batchNum'
 67	if err != nil {
 68		return nil, err
 69	}
 70
 71	// discard the txs that don't have an Account in the AccountDB
 72	var validTxs txs
 73	for _, tx := range l2TxsRaw {
 74		_, err = txsel.localAccountsDB.GetAccount(tx.FromIdx)
 75		if err == nil {
 76			// if FromIdx has an account into the AccountsDB
 77			validTxs = append(validTxs, tx)
 78		}
 79	}
 80
 81	// get most profitable L2-tx
 82	txs := txsel.getL2Profitable(validTxs, txsel.MaxTxs)
 83
 84	// apply L2-tx to local AccountDB, make checkpoint tagged with BatchID
 85	//     update balances
 86	//     update nonces
 87
 88	// return selected txs
 89	return txs, nil
 90}
 91
 92// GetL1L2TxSelection returns the selection of L1 + L2 txs
 93func (txsel *TxSelector) GetL1L2TxSelection(batchNum int, l1txs []common.Tx) ([]common.Tx, []common.PoolL2Tx, []common.Tx, error) {
 94	// apply l1-user-tx to localAccountDB
 95	//     create new leaves
 96	//     update balances
 97	//     update nonces
 98
 99	// get pending l2-tx from tx-pool
100	l2TxsRaw, err := txsel.l2db.GetPendingTxs() // (batchID)
101	if err != nil {
102		return nil, nil, nil, err
103	}
104
105	// discard the txs that don't have an Account in the AccountDB
106	// neither appear in the AccountCreationAuthsDB
107	var validTxs txs
108	for _, tx := range l2TxsRaw {
109		if txsel.checkIfAccountExistOrPending(tx.FromIdx) {
110			// if FromIdx has an account into the AccountsDB
111			validTxs = append(validTxs, tx)
112		}
113	}
114
115	// prepare (from the selected l2txs) pending to create from the AccountCreationAuthsDB
116	var accountCreationAuths []common.Account
117	// TODO once DB ready:
118	// if tx.ToIdx is in AccountCreationAuthsDB, take it and add it to
119	// the array 'accountCreationAuths'
120	// for _, tx := range l2txs {
121	//         account, err := txsel.localAccountsDB.GetAccount(tx.ToIdx)
122	//         if err != nil {
123	//                 return nil, nil, nil, err
124	//         }
125	//         if accountToCreate, ok := txsel.DB.AccountCreationAuthsDB[accountID]; ok {
126	//                 accountCreationAuths = append(accountCreationAuths, accountToCreate)
127	//         }
128	// }
129
130	// create L1-operator-tx for each L2-tx selected in which the recipient does not have account
131	l1OperatorTxs := txsel.createL1OperatorTxForL2Tx(accountCreationAuths) // only with the L2-tx selected ones
132
133	// get most profitable L2-tx
134	maxL2Txs := txsel.MaxTxs - uint64(len(l1OperatorTxs)) // - len(l1UserTxs)
135	l2txs := txsel.getL2Profitable(validTxs, maxL2Txs)
136
137	return l1txs, l2txs, l1OperatorTxs, nil
138}
139
140func (txsel *TxSelector) checkIfAccountExistOrPending(idx common.Idx) bool {
141	// check if account exist in AccountDB
142	_, err := txsel.localAccountsDB.GetAccount(idx)
143	if err != nil {
144		return false
145	}
146	// check if account is pending to create
147	// TODO need a method in the DB to get the PendingRegisters
148	// if _, ok := txsel.DB.AccountCreationAuthsDB[accountID]; ok {
149	//         return true
150	// }
151	return false
152}
153
154func (txsel *TxSelector) getL2Profitable(txs txs, max uint64) txs {
155	sort.Sort(txs)
156	return txs[:max]
157}
158func (txsel *TxSelector) createL1OperatorTxForL2Tx(accounts []common.Account) []common.Tx {
159	//
160	return nil
161}