|
|
@ -3,12 +3,13 @@ package txselector |
|
|
|
import ( |
|
|
|
"sort" |
|
|
|
|
|
|
|
"github.com/hermeznetwork/hermez-node/txselector/common" |
|
|
|
"github.com/hermeznetwork/hermez-node/txselector/mock" |
|
|
|
"github.com/hermeznetwork/hermez-node/common" |
|
|
|
"github.com/hermeznetwork/hermez-node/db/l2db" |
|
|
|
"github.com/hermeznetwork/hermez-node/db/statedb" |
|
|
|
) |
|
|
|
|
|
|
|
// txs implements the interface Sort for an array of Tx
|
|
|
|
type txs []common.Tx |
|
|
|
type txs []common.PoolL2Tx |
|
|
|
|
|
|
|
func (t txs) Len() int { |
|
|
|
return len(t) |
|
|
@ -17,57 +18,68 @@ func (t txs) Swap(i, j int) { |
|
|
|
t[i], t[j] = t[j], t[i] |
|
|
|
} |
|
|
|
func (t txs) Less(i, j int) bool { |
|
|
|
return t[i].UserFeeAbsolute > t[j].UserFeeAbsolute |
|
|
|
return t[i].AbsoluteFee > t[j].AbsoluteFee |
|
|
|
} |
|
|
|
|
|
|
|
// TxSelector implements all the functionalities to select the txs for the next batch
|
|
|
|
type TxSelector struct { |
|
|
|
// NMax is the maximum L1-user-tx for a batch
|
|
|
|
NMax uint64 |
|
|
|
// MMax is the maximum L1-operator-tx for a batch
|
|
|
|
MMax uint64 |
|
|
|
// PMax is the maximum L2-tx for a batch
|
|
|
|
PMax uint64 |
|
|
|
// DB is a pointer to the database interface
|
|
|
|
DB *mock.MockDB |
|
|
|
// MaxL1UserTxs is the maximum L1-user-tx for a batch
|
|
|
|
MaxL1UserTxs uint64 |
|
|
|
// MaxL1OperatorTxs is the maximum L1-operator-tx for a batch
|
|
|
|
MaxL1OperatorTxs uint64 |
|
|
|
// MaxTxs is the maximum txs for a batch
|
|
|
|
MaxTxs uint64 |
|
|
|
|
|
|
|
l2db *l2db.L2DB |
|
|
|
localAccountsDB *statedb.LocalStateDB |
|
|
|
} |
|
|
|
|
|
|
|
func NewTxSelector(db *mock.MockDB, nMax, mMax, pMax uint64) *TxSelector { |
|
|
|
return &TxSelector{ |
|
|
|
NMax: nMax, |
|
|
|
MMax: mMax, |
|
|
|
PMax: pMax, |
|
|
|
DB: db, |
|
|
|
// NewTxSelector returns a *TxSelector
|
|
|
|
func NewTxSelector(synchronizerStateDB *statedb.StateDB, l2 *l2db.L2DB, maxL1UserTxs, maxL1OperatorTxs, maxTxs uint64) (*TxSelector, error) { |
|
|
|
localAccountsDB, err := statedb.NewLocalStateDB(synchronizerStateDB, false, 0) // without merkletree
|
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (txsel *TxSelector) updateLocalAccountDB(batchId uint64) error { |
|
|
|
// if batchID > max(localAccountDB.BatchID) + 1
|
|
|
|
// make a checkpoint of AccountDB at BatchID to a localAccountDB
|
|
|
|
// use localAccountDB[inputBatchID-1]
|
|
|
|
return &TxSelector{ |
|
|
|
MaxL1UserTxs: maxL1UserTxs, |
|
|
|
MaxL1OperatorTxs: maxL1OperatorTxs, |
|
|
|
MaxTxs: maxTxs, |
|
|
|
l2db: l2, |
|
|
|
localAccountsDB: localAccountsDB, |
|
|
|
}, nil |
|
|
|
} |
|
|
|
|
|
|
|
// Reset tells the TxSelector to get it's internal AccountsDB
|
|
|
|
// from the required `batchNum`
|
|
|
|
func (txsel *TxSelector) Reset(batchNum int) error { |
|
|
|
err := txsel.localAccountsDB.Reset(batchNum, true) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func (txsel *TxSelector) GetL2TxSelection(batchID uint64) ([]common.Tx, error) { |
|
|
|
err := txsel.updateLocalAccountDB(batchID) |
|
|
|
// GetL2TxSelection returns a selection of the L2Txs for the next batch, from the L2DB pool
|
|
|
|
func (txsel *TxSelector) GetL2TxSelection(batchNum int) ([]common.PoolL2Tx, error) { |
|
|
|
// get pending l2-tx from tx-pool
|
|
|
|
l2TxsRaw, err := txsel.l2db.GetPendingTxs() // once l2db ready, maybe use parameter 'batchNum'
|
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
// get pending l2-tx from tx-pool
|
|
|
|
txsRaw := txsel.DB.GetTxs(batchID) |
|
|
|
|
|
|
|
// discard the txs that don't have an Account in the AccountDB
|
|
|
|
var validTxs txs |
|
|
|
for _, tx := range txsRaw { |
|
|
|
accountID := getAccountID(tx.ToEthAddr, tx.TokenID) |
|
|
|
if _, ok := txsel.DB.AccountDB[accountID]; ok { |
|
|
|
for _, tx := range l2TxsRaw { |
|
|
|
_, err = txsel.localAccountsDB.GetAccount(tx.FromIdx) |
|
|
|
if err == nil { |
|
|
|
// if FromIdx has an account into the AccountsDB
|
|
|
|
validTxs = append(validTxs, tx) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// get most profitable L2-tx (len<NMax)
|
|
|
|
txs := txsel.getL2Profitable(validTxs) |
|
|
|
// get most profitable L2-tx
|
|
|
|
txs := txsel.getL2Profitable(validTxs, txsel.MaxTxs) |
|
|
|
|
|
|
|
// apply L2-tx to local AccountDB, make checkpoint tagged with BatchID
|
|
|
|
// update balances
|
|
|
@ -77,66 +89,73 @@ func (txsel *TxSelector) GetL2TxSelection(batchID uint64) ([]common.Tx, error) { |
|
|
|
return txs, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (txsel *TxSelector) GetL1L2TxSelection(batchID uint64, l1txs []common.Tx) ([]common.Tx, []common.Tx, []common.Tx, error) { |
|
|
|
err := txsel.updateLocalAccountDB(batchID) |
|
|
|
if err != nil { |
|
|
|
return nil, nil, nil, err |
|
|
|
} |
|
|
|
|
|
|
|
// GetL1L2TxSelection returns the selection of L1 + L2 txs
|
|
|
|
func (txsel *TxSelector) GetL1L2TxSelection(batchNum int, l1txs []common.Tx) ([]common.Tx, []common.PoolL2Tx, []common.Tx, error) { |
|
|
|
// apply l1-user-tx to localAccountDB
|
|
|
|
// create new leaves
|
|
|
|
// update balances
|
|
|
|
// update nonces
|
|
|
|
|
|
|
|
// get pending l2-tx from tx-pool
|
|
|
|
txsRaw := txsel.DB.GetTxs(batchID) |
|
|
|
l2TxsRaw, err := txsel.l2db.GetPendingTxs() // (batchID)
|
|
|
|
if err != nil { |
|
|
|
return nil, nil, nil, err |
|
|
|
} |
|
|
|
|
|
|
|
// discard the txs that don't have an Account in the AccountDB
|
|
|
|
// neither appear in the PendingRegistersDB
|
|
|
|
// neither appear in the AccountCreationAuthsDB
|
|
|
|
var validTxs txs |
|
|
|
for _, tx := range txsRaw { |
|
|
|
accountID := getAccountID(tx.ToEthAddr, tx.TokenID) |
|
|
|
exist := txsel.checkIfAccountExist(accountID) |
|
|
|
if exist { |
|
|
|
for _, tx := range l2TxsRaw { |
|
|
|
if txsel.checkIfAccountExistOrPending(tx.FromIdx) { |
|
|
|
// if FromIdx has an account into the AccountsDB
|
|
|
|
validTxs = append(validTxs, tx) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// get most profitable L2-tx (len<NMax)
|
|
|
|
l2txs := txsel.getL2Profitable(validTxs) |
|
|
|
|
|
|
|
// prepare (from the selected l2txs) pending to register from the PendingRegistersDB
|
|
|
|
var pendingRegisters []common.Account |
|
|
|
for _, tx := range l2txs { |
|
|
|
accountID := getAccountID(tx.ToEthAddr, tx.TokenID) |
|
|
|
if toRegister, ok := txsel.DB.PendingRegistersDB[accountID]; ok { |
|
|
|
pendingRegisters = append(pendingRegisters, toRegister) |
|
|
|
} |
|
|
|
} |
|
|
|
// prepare (from the selected l2txs) pending to create from the AccountCreationAuthsDB
|
|
|
|
var accountCreationAuths []common.Account |
|
|
|
// TODO once DB ready:
|
|
|
|
// if tx.ToIdx is in AccountCreationAuthsDB, take it and add it to
|
|
|
|
// the array 'accountCreationAuths'
|
|
|
|
// for _, tx := range l2txs {
|
|
|
|
// account, err := txsel.localAccountsDB.GetAccount(tx.ToIdx)
|
|
|
|
// if err != nil {
|
|
|
|
// return nil, nil, nil, err
|
|
|
|
// }
|
|
|
|
// if accountToCreate, ok := txsel.DB.AccountCreationAuthsDB[accountID]; ok {
|
|
|
|
// accountCreationAuths = append(accountCreationAuths, accountToCreate)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// create L1-operator-tx for each L2-tx selected in which the recipient does not have account
|
|
|
|
l1OperatorTxs := txsel.createL1OperatorTxForL2Tx(pendingRegisters) // only with the L2-tx selected ones
|
|
|
|
l1OperatorTxs := txsel.createL1OperatorTxForL2Tx(accountCreationAuths) // only with the L2-tx selected ones
|
|
|
|
|
|
|
|
// get most profitable L2-tx
|
|
|
|
maxL2Txs := txsel.MaxTxs - uint64(len(l1OperatorTxs)) // - len(l1UserTxs)
|
|
|
|
l2txs := txsel.getL2Profitable(validTxs, maxL2Txs) |
|
|
|
|
|
|
|
return l1txs, l2txs, l1OperatorTxs, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (txsel *TxSelector) checkIfAccountExist(accountID [36]byte) bool { |
|
|
|
func (txsel *TxSelector) checkIfAccountExistOrPending(idx common.Idx) bool { |
|
|
|
// check if account exist in AccountDB
|
|
|
|
if _, ok := txsel.DB.AccountDB[accountID]; ok { |
|
|
|
return true |
|
|
|
} |
|
|
|
// check if account is pending to register
|
|
|
|
if _, ok := txsel.DB.PendingRegistersDB[accountID]; ok { |
|
|
|
return true |
|
|
|
_, err := txsel.localAccountsDB.GetAccount(idx) |
|
|
|
if err != nil { |
|
|
|
return false |
|
|
|
} |
|
|
|
// check if account is pending to create
|
|
|
|
// TODO need a method in the DB to get the PendingRegisters
|
|
|
|
// if _, ok := txsel.DB.AccountCreationAuthsDB[accountID]; ok {
|
|
|
|
// return true
|
|
|
|
// }
|
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
func (txsel *TxSelector) getL2Profitable(txs txs) txs { |
|
|
|
func (txsel *TxSelector) getL2Profitable(txs txs, max uint64) txs { |
|
|
|
sort.Sort(txs) |
|
|
|
return txs[:txsel.PMax] |
|
|
|
return txs[:max] |
|
|
|
} |
|
|
|
func (txsel *TxSelector) createL1OperatorTxForL2Tx(accounts []common.Account) txs { |
|
|
|
func (txsel *TxSelector) createL1OperatorTxForL2Tx(accounts []common.Account) []common.Tx { |
|
|
|
//
|
|
|
|
return txs{} |
|
|
|
return nil |
|
|
|
} |