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(dbpath string, synchronizerStateDB *statedb.StateDB, l2 *l2db.L2DB, maxL1UserTxs, maxL1OperatorTxs, maxTxs uint64) (*TxSelector, error) {
39 localAccountsDB, err := statedb.NewLocalStateDB(dbpath, 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 uint64) 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 uint64) ([]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 uint64, l1txs []common.L1Tx) ([]common.L1Tx, []common.L1Tx, []common.PoolL2Tx, 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, l1OperatorTxs, l2txs, 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.L1Tx {
159 //
160 return nil
161}