You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
4.9 KiB

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