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.

782 lines
31 KiB

Update coordinator to work better under real net - cli / node - Update handler of SIGINT so that after 3 SIGINTs, the process terminates unconditionally - coordinator - Store stats without pointer - In all functions that send a variable via channel, check for context done to avoid deadlock (due to no process reading from the channel, which has no queue) when the node is stopped. - Abstract `canForge` so that it can be used outside of the `Coordinator` - In `canForge` check the blockNumber in current and next slot. - Update tests due to smart contract changes in slot handling, and minimum bid defaults - TxManager - Add consts, vars and stats to allow evaluating `canForge` - Add `canForge` method (not used yet) - Store batch and nonces status (last success and last pending) - Track nonces internally instead of relying on the ethereum node (this is required to work with ganache when there are pending txs) - Handle the (common) case of the receipt not being found after the tx is sent. - Don't start the main loop until we get an initial messae fo the stats and vars (so that in the loop the stats and vars are set to synchronizer values) - When a tx fails, check and discard all the failed transactions before sending the message to stop the pipeline. This will avoid sending consecutive messages of stop the pipeline when multiple txs are detected to be failed consecutively. Also, future txs of the same pipeline after a discarded txs are discarded, and their nonces reused. - Robust handling of nonces: - If geth returns nonce is too low, increase it - If geth returns nonce too hight, decrease it - If geth returns underpriced, increase gas price - If geth returns replace underpriced, increase gas price - Add support for resending transactions after a timeout - Store `BatchInfos` in a queue - Pipeline - When an error is found, stop forging batches and send a message to the coordinator to stop the pipeline with information of the failed batch number so that in a restart, non-failed batches are not repated. - When doing a reset of the stateDB, if possible reset from the local checkpoint instead of resetting from the synchronizer. This allows resetting from a batch that is valid but not yet sent / synced. - Every time a pipeline is started, assign it a number from a counter. This allows the TxManager to ignore batches from stopped pipelines, via a message sent by the coordinator. - Avoid forging when we haven't reached the rollup genesis block number. - Add config parameter `StartSlotBlocksDelay`: StartSlotBlocksDelay is the number of blocks of delay to wait before starting the pipeline when we reach a slot in which we can forge. - When detecting a reorg, only reset the pipeline if the batch from which the pipeline started changed and wasn't sent by us. - Add config parameter `ScheduleBatchBlocksAheadCheck`: ScheduleBatchBlocksAheadCheck is the number of blocks ahead in which the forger address is checked to be allowed to forge (apart from checking the next block), used to decide when to stop scheduling new batches (by stopping the pipeline). For example, if we are at block 10 and ScheduleBatchBlocksAheadCheck is 5, eventhough at block 11 we canForge, the pipeline will be stopped if we can't forge at block 15. This value should be the expected number of blocks it takes between scheduling a batch and having it mined. - Add config parameter `SendBatchBlocksMarginCheck`: SendBatchBlocksMarginCheck is the number of margin blocks ahead in which the coordinator is also checked to be allowed to forge, apart from the next block; used to decide when to stop sending batches to the smart contract. For example, if we are at block 10 and SendBatchBlocksMarginCheck is 5, eventhough at block 11 we canForge, the batch will be discarded if we can't forge at block 15. - Add config parameter `TxResendTimeout`: TxResendTimeout is the timeout after which a non-mined ethereum transaction will be resent (reusing the nonce) with a newly calculated gas price - Add config parameter `MaxGasPrice`: MaxGasPrice is the maximum gas price allowed for ethereum transactions - Add config parameter `NoReuseNonce`: NoReuseNonce disables reusing nonces of pending transactions for new replacement transactions. This is useful for testing with Ganache. - Extend BatchInfo with more useful information for debugging - eth / ethereum client - Add necessary methods to create the auth object for transactions manually so that we can set the nonce, gas price, gas limit, etc manually - Update `RollupForgeBatch` to take an auth object as input (so that the coordinator can set parameters manually) - synchronizer - In stats, add `NextSlot` - In stats, store full last batch instead of just last batch number - Instead of calculating a nextSlot from scratch every time, update the current struct (only updating the forger info if we are Synced) - Afer every processed batch, check that the calculated StateDB MTRoot matches the StateRoot found in the forgeBatch event.
3 years ago
  1. package txselector
  2. // current: very simple version of TxSelector
  3. import (
  4. "fmt"
  5. "math/big"
  6. "sort"
  7. ethCommon "github.com/ethereum/go-ethereum/common"
  8. "github.com/hermeznetwork/hermez-node/common"
  9. "github.com/hermeznetwork/hermez-node/db/kvdb"
  10. "github.com/hermeznetwork/hermez-node/db/l2db"
  11. "github.com/hermeznetwork/hermez-node/db/statedb"
  12. "github.com/hermeznetwork/hermez-node/log"
  13. "github.com/hermeznetwork/hermez-node/metric"
  14. "github.com/hermeznetwork/hermez-node/txprocessor"
  15. "github.com/hermeznetwork/tracerr"
  16. "github.com/iden3/go-iden3-crypto/babyjub"
  17. )
  18. // CoordAccount contains the data of the Coordinator account, that will be used
  19. // to create new transactions of CreateAccountDeposit type to add new TokenID
  20. // accounts for the Coordinator to receive the fees.
  21. type CoordAccount struct {
  22. Addr ethCommon.Address
  23. BJJ babyjub.PublicKeyComp
  24. AccountCreationAuth []byte // signature in byte array format
  25. }
  26. // TxSelector implements all the functionalities to select the txs for the next
  27. // batch
  28. type TxSelector struct {
  29. l2db *l2db.L2DB
  30. localAccountsDB *statedb.LocalStateDB
  31. coordAccount *CoordAccount
  32. }
  33. // NewTxSelector returns a *TxSelector
  34. func NewTxSelector(coordAccount *CoordAccount, dbpath string,
  35. synchronizerStateDB *statedb.StateDB, l2 *l2db.L2DB) (*TxSelector, error) {
  36. localAccountsDB, err := statedb.NewLocalStateDB(
  37. statedb.Config{
  38. Path: dbpath,
  39. Keep: kvdb.DefaultKeep,
  40. Type: statedb.TypeTxSelector,
  41. NLevels: 0,
  42. },
  43. synchronizerStateDB) // without merkletree
  44. if err != nil {
  45. return nil, tracerr.Wrap(err)
  46. }
  47. return &TxSelector{
  48. l2db: l2,
  49. localAccountsDB: localAccountsDB,
  50. coordAccount: coordAccount,
  51. }, nil
  52. }
  53. // LocalAccountsDB returns the LocalStateDB of the TxSelector
  54. func (txsel *TxSelector) LocalAccountsDB() *statedb.LocalStateDB {
  55. return txsel.localAccountsDB
  56. }
  57. // Reset tells the TxSelector to get it's internal AccountsDB
  58. // from the required `batchNum`
  59. func (txsel *TxSelector) Reset(batchNum common.BatchNum, fromSynchronizer bool) error {
  60. return tracerr.Wrap(txsel.localAccountsDB.Reset(batchNum, fromSynchronizer))
  61. }
  62. func (txsel *TxSelector) getCoordIdx(tokenID common.TokenID) (common.Idx, error) {
  63. return txsel.localAccountsDB.GetIdxByEthAddrBJJ(txsel.coordAccount.Addr,
  64. txsel.coordAccount.BJJ, tokenID)
  65. }
  66. // coordAccountForTokenID creates a new L1CoordinatorTx to create a new
  67. // Coordinator account for the given TokenID in the case that the account does
  68. // not exist yet in the db, and does not exist a L1CoordinatorTx to creat that
  69. // account in the given array of L1CoordinatorTxs. If a new Coordinator account
  70. // needs to be created, a new L1CoordinatorTx will be returned from this
  71. // function. After calling this method, if the l1CoordinatorTx is added to the
  72. // selection, positionL1 must be increased 1.
  73. func (txsel *TxSelector) coordAccountForTokenID(l1CoordinatorTxs []common.L1Tx,
  74. tokenID common.TokenID, positionL1 int) (*common.L1Tx, int, error) {
  75. // check if CoordinatorAccount for TokenID is already pending to create
  76. if checkPendingToCreateL1CoordTx(l1CoordinatorTxs, tokenID,
  77. txsel.coordAccount.Addr, txsel.coordAccount.BJJ) {
  78. return nil, positionL1, nil
  79. }
  80. _, err := txsel.getCoordIdx(tokenID)
  81. if tracerr.Unwrap(err) == statedb.ErrIdxNotFound {
  82. // create L1CoordinatorTx to create new CoordAccount for
  83. // TokenID
  84. l1CoordinatorTx := common.L1Tx{
  85. Position: positionL1,
  86. UserOrigin: false,
  87. FromEthAddr: txsel.coordAccount.Addr,
  88. FromBJJ: txsel.coordAccount.BJJ,
  89. TokenID: tokenID,
  90. Amount: big.NewInt(0),
  91. DepositAmount: big.NewInt(0),
  92. Type: common.TxTypeCreateAccountDeposit,
  93. }
  94. return &l1CoordinatorTx, positionL1, nil
  95. }
  96. if err != nil {
  97. return nil, positionL1, tracerr.Wrap(err)
  98. }
  99. // CoordAccount for TokenID already exists
  100. return nil, positionL1, nil
  101. }
  102. // GetL2TxSelection returns the L1CoordinatorTxs and a selection of the L2Txs
  103. // for the next batch, from the L2DB pool.
  104. // It returns: the CoordinatorIdxs used to receive the fees of the selected
  105. // L2Txs. An array of bytearrays with the signatures of the
  106. // AccountCreationAuthorization of the accounts of the users created by the
  107. // Coordinator with L1CoordinatorTxs of those accounts that does not exist yet
  108. // but there is a transactions to them and the authorization of account
  109. // creation exists. The L1UserTxs, L1CoordinatorTxs, PoolL2Txs that will be
  110. // included in the next batch.
  111. func (txsel *TxSelector) GetL2TxSelection(selectionConfig txprocessor.Config, l1UserFutureTxs []common.L1Tx) ([]common.Idx,
  112. [][]byte, []common.L1Tx, []common.PoolL2Tx, []common.PoolL2Tx, error) {
  113. metric.GetL2TxSelection.Inc()
  114. coordIdxs, accCreationAuths, _, l1CoordinatorTxs, l2Txs,
  115. discardedL2Txs, err := txsel.getL1L2TxSelection(selectionConfig,
  116. []common.L1Tx{}, l1UserFutureTxs)
  117. return coordIdxs, accCreationAuths, l1CoordinatorTxs, l2Txs,
  118. discardedL2Txs, tracerr.Wrap(err)
  119. }
  120. // GetL1L2TxSelection returns the selection of L1 + L2 txs.
  121. // It returns: the CoordinatorIdxs used to receive the fees of the selected
  122. // L2Txs. An array of bytearrays with the signatures of the
  123. // AccountCreationAuthorization of the accounts of the users created by the
  124. // Coordinator with L1CoordinatorTxs of those accounts that does not exist yet
  125. // but there is a transactions to them and the authorization of account
  126. // creation exists. The L1UserTxs, L1CoordinatorTxs, PoolL2Txs that will be
  127. // included in the next batch.
  128. func (txsel *TxSelector) GetL1L2TxSelection(selectionConfig txprocessor.Config,
  129. l1UserTxs, l1UserFutureTxs []common.L1Tx) ([]common.Idx, [][]byte, []common.L1Tx,
  130. []common.L1Tx, []common.PoolL2Tx, []common.PoolL2Tx, error) {
  131. metric.GetL1L2TxSelection.Inc()
  132. coordIdxs, accCreationAuths, l1UserTxs, l1CoordinatorTxs, l2Txs,
  133. discardedL2Txs, err := txsel.getL1L2TxSelection(selectionConfig, l1UserTxs, l1UserFutureTxs)
  134. return coordIdxs, accCreationAuths, l1UserTxs, l1CoordinatorTxs, l2Txs,
  135. discardedL2Txs, tracerr.Wrap(err)
  136. }
  137. // getL1L2TxSelection returns the selection of L1 + L2 txs.
  138. // It returns: the CoordinatorIdxs used to receive the fees of the selected
  139. // L2Txs. An array of bytearrays with the signatures of the
  140. // AccountCreationAuthorization of the accounts of the users created by the
  141. // Coordinator with L1CoordinatorTxs of those accounts that does not exist yet
  142. // but there is a transactions to them and the authorization of account
  143. // creation exists. The L1UserTxs, L1CoordinatorTxs, PoolL2Txs that will be
  144. // included in the next batch.
  145. func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
  146. l1UserTxs, l1UserFutureTxs []common.L1Tx) ([]common.Idx, [][]byte, []common.L1Tx,
  147. []common.L1Tx, []common.PoolL2Tx, []common.PoolL2Tx, error) {
  148. // WIP.0: the TxSelector is not optimized and will need a redesign. The
  149. // current version is implemented in order to have a functional
  150. // implementation that can be used ASAP.
  151. // Steps of this method:
  152. // - ProcessL1Txs (User txs)
  153. // - getPendingTxs (forgable directly with current state & not forgable
  154. // yet)
  155. // - split between l2TxsForgable & l2TxsNonForgable, where:
  156. // - l2TxsForgable are the txs that are directly forgable with the
  157. // current state
  158. // - l2TxsNonForgable are the txs that are not directly forgable
  159. // with the current state, but that may be forgable once the
  160. // l2TxsForgable ones are processed
  161. // - for l2TxsForgable, and if needed, for l2TxsNonForgable:
  162. // - sort by Fee & Nonce
  163. // - loop over l2Txs (txsel.processL2Txs)
  164. // - Fill tx.TokenID tx.Nonce
  165. // - Check enough Balance on sender
  166. // - Check Nonce
  167. // - Create CoordAccount L1CoordTx for TokenID if needed
  168. // - & ProcessL1Tx of L1CoordTx
  169. // - Check validity of receiver Account for ToEthAddr / ToBJJ
  170. // - Create UserAccount L1CoordTx if needed (and possible)
  171. // - If everything is fine, store l2Tx to validTxs & update NoncesMap
  172. // - Prepare coordIdxsMap & AccumulatedFees
  173. // - Distribute AccumulatedFees to CoordIdxs
  174. // - MakeCheckpoint
  175. txselStateDB := txsel.localAccountsDB.StateDB
  176. tp := txprocessor.NewTxProcessor(txselStateDB, selectionConfig)
  177. tp.AccumulatedFees = make(map[common.Idx]*big.Int)
  178. // Process L1UserTxs
  179. for i := 0; i < len(l1UserTxs); i++ {
  180. // assumption: l1usertx are sorted by L1Tx.Position
  181. _, _, _, _, err := tp.ProcessL1Tx(nil, &l1UserTxs[i])
  182. if err != nil {
  183. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  184. }
  185. }
  186. l2TxsFromDB, err := txsel.l2db.GetPendingTxs()
  187. if err != nil {
  188. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  189. }
  190. l2TxsForgable, l2TxsNonForgable := splitL2ForgableAndNonForgable(tp, l2TxsFromDB)
  191. // in case that length of l2TxsForgable is 0, no need to continue, there
  192. // is no L2Txs to forge at all
  193. if len(l2TxsForgable) == 0 {
  194. var discardedL2Txs []common.PoolL2Tx
  195. for i := 0; i < len(l2TxsNonForgable); i++ {
  196. l2TxsNonForgable[i].Info =
  197. "Tx not selected due impossibility to be forged with the current state"
  198. discardedL2Txs = append(discardedL2Txs, l2TxsNonForgable[i])
  199. }
  200. err = tp.StateDB().MakeCheckpoint()
  201. if err != nil {
  202. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  203. }
  204. metric.SelectedL1UserTxs.Set(float64(len(l1UserTxs)))
  205. metric.SelectedL1CoordinatorTxs.Set(0)
  206. metric.SelectedL2Txs.Set(0)
  207. metric.DiscardedL2Txs.Set(float64(len(discardedL2Txs)))
  208. return nil, nil, l1UserTxs, nil, nil, discardedL2Txs, nil
  209. }
  210. var accAuths [][]byte
  211. var l1CoordinatorTxs []common.L1Tx
  212. var validTxs, discardedL2Txs []common.PoolL2Tx
  213. l2TxsForgable = sortL2Txs(l2TxsForgable)
  214. accAuths, l1CoordinatorTxs, validTxs, discardedL2Txs, err =
  215. txsel.processL2Txs(tp, selectionConfig, len(l1UserTxs), l1UserFutureTxs,
  216. l2TxsForgable, validTxs, discardedL2Txs)
  217. if err != nil {
  218. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  219. }
  220. // if there is space for more txs get also the NonForgable txs, that may
  221. // be unblocked once the Forgable ones are processed
  222. if len(validTxs) < int(selectionConfig.MaxTx)-(len(l1UserTxs)+len(l1CoordinatorTxs)) {
  223. l2TxsNonForgable = sortL2Txs(l2TxsNonForgable)
  224. var accAuths2 [][]byte
  225. var l1CoordinatorTxs2 []common.L1Tx
  226. accAuths2, l1CoordinatorTxs2, validTxs, discardedL2Txs, err =
  227. txsel.processL2Txs(tp, selectionConfig,
  228. len(l1UserTxs)+len(l1CoordinatorTxs), l1UserFutureTxs,
  229. l2TxsNonForgable, validTxs, discardedL2Txs)
  230. if err != nil {
  231. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  232. }
  233. accAuths = append(accAuths, accAuths2...)
  234. l1CoordinatorTxs = append(l1CoordinatorTxs, l1CoordinatorTxs2...)
  235. } else {
  236. // if there is no space for NonForgable txs, put them at the
  237. // discardedL2Txs array
  238. for i := 0; i < len(l2TxsNonForgable); i++ {
  239. l2TxsNonForgable[i].Info =
  240. "Tx not selected due not available slots for L2Txs"
  241. discardedL2Txs = append(discardedL2Txs, l2TxsNonForgable[i])
  242. }
  243. }
  244. // get CoordIdxsMap for the TokenIDs
  245. coordIdxsMap := make(map[common.TokenID]common.Idx)
  246. for i := 0; i < len(validTxs); i++ {
  247. // get TokenID from tx.Sender
  248. accSender, err := tp.StateDB().GetAccount(validTxs[i].FromIdx)
  249. if err != nil {
  250. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  251. }
  252. tokenID := accSender.TokenID
  253. coordIdx, err := txsel.getCoordIdx(tokenID)
  254. if err != nil {
  255. // if err is db.ErrNotFound, should not happen, as all
  256. // the validTxs.TokenID should have a CoordinatorIdx
  257. // created in the DB at this point
  258. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  259. }
  260. coordIdxsMap[tokenID] = coordIdx
  261. }
  262. var coordIdxs []common.Idx
  263. for _, idx := range coordIdxsMap {
  264. coordIdxs = append(coordIdxs, idx)
  265. }
  266. // sort CoordIdxs
  267. sort.SliceStable(coordIdxs, func(i, j int) bool {
  268. return coordIdxs[i] < coordIdxs[j]
  269. })
  270. // distribute the AccumulatedFees from the processed L2Txs into the
  271. // Coordinator Idxs
  272. for idx, accumulatedFee := range tp.AccumulatedFees {
  273. cmp := accumulatedFee.Cmp(big.NewInt(0))
  274. if cmp == 1 { // accumulatedFee>0
  275. // send the fee to the Idx of the Coordinator for the TokenID
  276. accCoord, err := txsel.localAccountsDB.GetAccount(idx)
  277. if err != nil {
  278. log.Errorw("Can not distribute accumulated fees to coordinator "+
  279. "account: No coord Idx to receive fee", "idx", idx)
  280. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  281. }
  282. accCoord.Balance = new(big.Int).Add(accCoord.Balance, accumulatedFee)
  283. _, err = txsel.localAccountsDB.UpdateAccount(idx, accCoord)
  284. if err != nil {
  285. log.Error(err)
  286. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  287. }
  288. }
  289. }
  290. err = tp.StateDB().MakeCheckpoint()
  291. if err != nil {
  292. return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
  293. }
  294. metric.SelectedL1CoordinatorTxs.Set(float64(len(l1CoordinatorTxs)))
  295. metric.SelectedL1UserTxs.Set(float64(len(l1UserTxs)))
  296. metric.SelectedL2Txs.Set(float64(len(validTxs)))
  297. metric.DiscardedL2Txs.Set(float64(len(discardedL2Txs)))
  298. return coordIdxs, accAuths, l1UserTxs, l1CoordinatorTxs, validTxs, discardedL2Txs, nil
  299. }
  300. func (txsel *TxSelector) processL2Txs(tp *txprocessor.TxProcessor,
  301. selectionConfig txprocessor.Config, nL1Txs int, l1UserFutureTxs []common.L1Tx,
  302. l2Txs, validTxs, discardedL2Txs []common.PoolL2Tx) ([][]byte, []common.L1Tx,
  303. []common.PoolL2Tx, []common.PoolL2Tx, error) {
  304. var l1CoordinatorTxs []common.L1Tx
  305. positionL1 := nL1Txs
  306. var accAuths [][]byte
  307. // Iterate over l2Txs
  308. // - check Nonces
  309. // - check enough Balance for the Amount+Fee
  310. // - if needed, create new L1CoordinatorTxs for unexisting ToIdx
  311. // - keep used accAuths
  312. // - put the valid txs into validTxs array
  313. for i := 0; i < len(l2Txs); i++ {
  314. // Check if there is space for more L2Txs in the selection
  315. maxL2Txs := int(selectionConfig.MaxTx) - nL1Txs - len(l1CoordinatorTxs)
  316. if len(validTxs) >= maxL2Txs {
  317. // no more available slots for L2Txs, so mark this tx
  318. // but also the rest of remaining txs as discarded
  319. for j := i; j < len(l2Txs); j++ {
  320. l2Txs[j].Info =
  321. "Tx not selected due not available slots for L2Txs"
  322. discardedL2Txs = append(discardedL2Txs, l2Txs[j])
  323. }
  324. break
  325. }
  326. // get Nonce & TokenID from the Account by l2Tx.FromIdx
  327. accSender, err := tp.StateDB().GetAccount(l2Txs[i].FromIdx)
  328. if err != nil {
  329. return nil, nil, nil, nil, tracerr.Wrap(err)
  330. }
  331. l2Txs[i].TokenID = accSender.TokenID
  332. // Check enough Balance on sender
  333. enoughBalance, balance, feeAndAmount := tp.CheckEnoughBalance(l2Txs[i])
  334. if !enoughBalance {
  335. // not valid Amount with current Balance. Discard L2Tx,
  336. // and update Info parameter of the tx, and add it to
  337. // the discardedTxs array
  338. l2Txs[i].Info = fmt.Sprintf("Tx not selected due to not enough Balance at the sender. "+
  339. "Current sender account Balance: %s, Amount+Fee: %s",
  340. balance.String(), feeAndAmount.String())
  341. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  342. continue
  343. }
  344. // Check if Nonce is correct
  345. if l2Txs[i].Nonce != accSender.Nonce {
  346. // not valid Nonce at tx. Discard L2Tx, and update Info
  347. // parameter of the tx, and add it to the discardedTxs
  348. // array
  349. l2Txs[i].Info = fmt.Sprintf("Tx not selected due to not current Nonce. "+
  350. "Tx.Nonce: %d, Account.Nonce: %d", l2Txs[i].Nonce, accSender.Nonce)
  351. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  352. continue
  353. }
  354. // if TokenID does not exist yet, create new L1CoordinatorTx to
  355. // create the CoordinatorAccount for that TokenID, to receive
  356. // the fees. Only in the case that there does not exist yet a
  357. // pending L1CoordinatorTx to create the account for the
  358. // Coordinator for that TokenID
  359. var newL1CoordTx *common.L1Tx
  360. newL1CoordTx, positionL1, err =
  361. txsel.coordAccountForTokenID(l1CoordinatorTxs,
  362. accSender.TokenID, positionL1)
  363. if err != nil {
  364. return nil, nil, nil, nil, tracerr.Wrap(err)
  365. }
  366. if newL1CoordTx != nil {
  367. // if there is no space for the L1CoordinatorTx as MaxL1Tx, or no space
  368. // for L1CoordinatorTx + L2Tx as MaxTx, discard the L2Tx
  369. if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-nL1Txs ||
  370. len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-nL1Txs {
  371. // discard L2Tx, and update Info parameter of
  372. // the tx, and add it to the discardedTxs array
  373. l2Txs[i].Info = "Tx not selected because the L2Tx depends on a " +
  374. "L1CoordinatorTx and there is not enough space for L1Coordinator"
  375. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  376. continue
  377. }
  378. // increase positionL1
  379. positionL1++
  380. l1CoordinatorTxs = append(l1CoordinatorTxs, *newL1CoordTx)
  381. accAuths = append(accAuths, txsel.coordAccount.AccountCreationAuth)
  382. // process the L1CoordTx
  383. _, _, _, _, err := tp.ProcessL1Tx(nil, newL1CoordTx)
  384. if err != nil {
  385. return nil, nil, nil, nil, tracerr.Wrap(err)
  386. }
  387. }
  388. // If tx.ToIdx>=256, tx.ToIdx should exist to localAccountsDB,
  389. // if so, tx is used. If tx.ToIdx==0, for an L2Tx will be the
  390. // case of TxToEthAddr or TxToBJJ, check if
  391. // tx.ToEthAddr/tx.ToBJJ exist in localAccountsDB, if yes tx is
  392. // used; if not, check if tx.ToEthAddr is in
  393. // AccountCreationAuthDB, if so, tx is used and L1CoordinatorTx
  394. // of CreateAccountAndDeposit is created. If tx.ToIdx==1, is a
  395. // Exit type and is used.
  396. if l2Txs[i].ToIdx == 0 { // ToEthAddr/ToBJJ case
  397. validL2Tx, l1CoordinatorTx, accAuth, err :=
  398. txsel.processTxToEthAddrBJJ(validTxs, selectionConfig,
  399. nL1Txs, l1UserFutureTxs, l1CoordinatorTxs,
  400. positionL1, l2Txs[i])
  401. if err != nil {
  402. log.Debugw("txsel.processTxToEthAddrBJJ", "err", err)
  403. // Discard L2Tx, and update Info parameter of
  404. // the tx, and add it to the discardedTxs array
  405. l2Txs[i].Info = fmt.Sprintf("Tx not selected (in processTxToEthAddrBJJ) due to %s",
  406. err.Error())
  407. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  408. continue
  409. }
  410. // if there is no space for the L1CoordinatorTx as MaxL1Tx, or no space
  411. // for L1CoordinatorTx + L2Tx as MaxTx, discard the L2Tx
  412. if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-nL1Txs ||
  413. len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-nL1Txs {
  414. // discard L2Tx, and update Info parameter of
  415. // the tx, and add it to the discardedTxs array
  416. l2Txs[i].Info = "Tx not selected because the L2Tx depends on a " +
  417. "L1CoordinatorTx and there is not enough space for L1Coordinator"
  418. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  419. continue
  420. }
  421. if l1CoordinatorTx != nil && validL2Tx != nil {
  422. // If ToEthAddr == 0xff.. this means that we
  423. // are handling a TransferToBJJ, which doesn't
  424. // require an authorization because it doesn't
  425. // contain a valid ethereum address.
  426. // Otherwise only create the account if we have
  427. // the corresponding authorization
  428. if validL2Tx.ToEthAddr == common.FFAddr {
  429. accAuths = append(accAuths, common.EmptyEthSignature)
  430. l1CoordinatorTxs = append(l1CoordinatorTxs, *l1CoordinatorTx)
  431. positionL1++
  432. } else if accAuth != nil {
  433. accAuths = append(accAuths, accAuth.Signature)
  434. l1CoordinatorTxs = append(l1CoordinatorTxs, *l1CoordinatorTx)
  435. positionL1++
  436. }
  437. // process the L1CoordTx
  438. _, _, _, _, err := tp.ProcessL1Tx(nil, l1CoordinatorTx)
  439. if err != nil {
  440. return nil, nil, nil, nil, tracerr.Wrap(err)
  441. }
  442. }
  443. if validL2Tx == nil {
  444. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  445. continue
  446. }
  447. } else if l2Txs[i].ToIdx >= common.IdxUserThreshold {
  448. receiverAcc, err := txsel.localAccountsDB.GetAccount(l2Txs[i].ToIdx)
  449. if err != nil {
  450. // tx not valid
  451. log.Debugw("invalid L2Tx: ToIdx not found in StateDB",
  452. "ToIdx", l2Txs[i].ToIdx)
  453. // Discard L2Tx, and update Info parameter of
  454. // the tx, and add it to the discardedTxs array
  455. l2Txs[i].Info = fmt.Sprintf("Tx not selected due to tx.ToIdx not found in StateDB. "+
  456. "ToIdx: %d", l2Txs[i].ToIdx)
  457. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  458. continue
  459. }
  460. if l2Txs[i].ToEthAddr != common.EmptyAddr {
  461. if l2Txs[i].ToEthAddr != receiverAcc.EthAddr {
  462. log.Debugw("invalid L2Tx: ToEthAddr does not correspond to the Account.EthAddr",
  463. "ToIdx", l2Txs[i].ToIdx, "tx.ToEthAddr",
  464. l2Txs[i].ToEthAddr, "account.EthAddr", receiverAcc.EthAddr)
  465. // Discard L2Tx, and update Info
  466. // parameter of the tx, and add it to
  467. // the discardedTxs array
  468. l2Txs[i].Info = fmt.Sprintf("Tx not selected because ToEthAddr "+
  469. "does not correspond to the Account.EthAddr. "+
  470. "tx.ToIdx: %d, tx.ToEthAddr: %s, account.EthAddr: %s",
  471. l2Txs[i].ToIdx, l2Txs[i].ToEthAddr, receiverAcc.EthAddr)
  472. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  473. continue
  474. }
  475. }
  476. if l2Txs[i].ToBJJ != common.EmptyBJJComp {
  477. if l2Txs[i].ToBJJ != receiverAcc.BJJ {
  478. log.Debugw("invalid L2Tx: ToBJJ does not correspond to the Account.BJJ",
  479. "ToIdx", l2Txs[i].ToIdx, "tx.ToEthAddr", l2Txs[i].ToBJJ,
  480. "account.BJJ", receiverAcc.BJJ)
  481. // Discard L2Tx, and update Info
  482. // parameter of the tx, and add it to
  483. // the discardedTxs array
  484. l2Txs[i].Info = fmt.Sprintf("Tx not selected because tx.ToBJJ "+
  485. "does not correspond to the Account.BJJ. "+
  486. "tx.ToIdx: %d, tx.ToEthAddr: %s, tx.ToBJJ: %s, account.BJJ: %s",
  487. l2Txs[i].ToIdx, l2Txs[i].ToEthAddr, l2Txs[i].ToBJJ, receiverAcc.BJJ)
  488. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  489. continue
  490. }
  491. }
  492. }
  493. // get CoordIdxsMap for the TokenID of the current l2Txs[i]
  494. // get TokenID from tx.Sender account
  495. tokenID := accSender.TokenID
  496. coordIdx, err := txsel.getCoordIdx(tokenID)
  497. if err != nil {
  498. // if err is db.ErrNotFound, should not happen, as all
  499. // the validTxs.TokenID should have a CoordinatorIdx
  500. // created in the DB at this point
  501. return nil, nil, nil, nil,
  502. tracerr.Wrap(fmt.Errorf("Could not get CoordIdx for TokenID=%d, "+
  503. "due: %s", tokenID, err))
  504. }
  505. // prepare temp coordIdxsMap & AccumulatedFees for the call to
  506. // ProcessL2Tx
  507. coordIdxsMap := map[common.TokenID]common.Idx{tokenID: coordIdx}
  508. // tp.AccumulatedFees = make(map[common.Idx]*big.Int)
  509. if _, ok := tp.AccumulatedFees[coordIdx]; !ok {
  510. tp.AccumulatedFees[coordIdx] = big.NewInt(0)
  511. }
  512. _, _, _, err = tp.ProcessL2Tx(coordIdxsMap, nil, nil, &l2Txs[i])
  513. if err != nil {
  514. log.Debugw("txselector.getL1L2TxSelection at ProcessL2Tx", "err", err)
  515. // Discard L2Tx, and update Info parameter of the tx,
  516. // and add it to the discardedTxs array
  517. l2Txs[i].Info = fmt.Sprintf("Tx not selected (in ProcessL2Tx) due to %s",
  518. err.Error())
  519. discardedL2Txs = append(discardedL2Txs, l2Txs[i])
  520. continue
  521. }
  522. validTxs = append(validTxs, l2Txs[i])
  523. } // after this loop, no checks to discard txs should be done
  524. return accAuths, l1CoordinatorTxs, validTxs, discardedL2Txs, nil
  525. }
  526. // processTxsToEthAddrBJJ process the common.PoolL2Tx in the case where
  527. // ToIdx==0, which can be the tx type of ToEthAddr or ToBJJ. If the receiver
  528. // does not have an account yet, a new L1CoordinatorTx of type
  529. // CreateAccountDeposit (with 0 as DepositAmount) is created and added to the
  530. // l1CoordinatorTxs array, and then the PoolL2Tx is added into the validTxs
  531. // array.
  532. func (txsel *TxSelector) processTxToEthAddrBJJ(validTxs []common.PoolL2Tx,
  533. selectionConfig txprocessor.Config, nL1UserTxs int, l1UserFutureTxs,
  534. l1CoordinatorTxs []common.L1Tx, positionL1 int, l2Tx common.PoolL2Tx) (
  535. *common.PoolL2Tx, *common.L1Tx, *common.AccountCreationAuth, error) {
  536. // if L2Tx needs a new L1CoordinatorTx of CreateAccount type, and a
  537. // previous L2Tx in the current process already created a
  538. // L1CoordinatorTx of this type, in the DB there still seem that needs
  539. // to create a new L1CoordinatorTx, but as is already created, the tx
  540. // is valid
  541. if checkPendingToCreateL1CoordTx(l1CoordinatorTxs, l2Tx.TokenID, l2Tx.ToEthAddr, l2Tx.ToBJJ) {
  542. return &l2Tx, nil, nil, nil
  543. }
  544. // check if L2Tx receiver account will be created by a L1UserFutureTxs
  545. // (in the next batch, the current frozen queue). In that case, the L2Tx
  546. // will be discarded at the current batch, even if there is an
  547. // AccountCreationAuth for the account, as there is a L1UserTx in the
  548. // frozen queue that will create the receiver Account. The L2Tx is
  549. // discarded to avoid the Coordinator creating a new L1CoordinatorTx to
  550. // create the receiver account, which will be also created in the next
  551. // batch from the L1UserFutureTx, ending with the user having 2
  552. // different accounts for the same TokenID. The double account creation
  553. // is supported by the Hermez zkRollup specification, but it was decided
  554. // to mitigate it at the TxSelector level for the explained cases.
  555. if checkPendingToCreateFutureTxs(l1UserFutureTxs, l2Tx.TokenID, l2Tx.ToEthAddr, l2Tx.ToBJJ) {
  556. return nil, nil, nil, fmt.Errorf("L2Tx discarded at the current batch, as the" +
  557. " receiver account does not exist yet, and there is a L1UserTx that" +
  558. " will create that account in a future batch.")
  559. }
  560. var l1CoordinatorTx *common.L1Tx
  561. var accAuth *common.AccountCreationAuth
  562. if l2Tx.ToEthAddr != common.EmptyAddr && l2Tx.ToEthAddr != common.FFAddr {
  563. // case: ToEthAddr != 0x00 neither 0xff
  564. if l2Tx.ToBJJ != common.EmptyBJJComp {
  565. // case: ToBJJ!=0:
  566. // if idx exist for EthAddr&BJJ use it
  567. _, err := txsel.localAccountsDB.GetIdxByEthAddrBJJ(l2Tx.ToEthAddr,
  568. l2Tx.ToBJJ, l2Tx.TokenID)
  569. if err == nil {
  570. // account for ToEthAddr&ToBJJ already exist,
  571. // there is no need to create a new one.
  572. // tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx
  573. return &l2Tx, nil, nil, nil
  574. }
  575. // if not, check if AccountCreationAuth exist for that
  576. // ToEthAddr
  577. accAuth, err = txsel.l2db.GetAccountCreationAuth(l2Tx.ToEthAddr)
  578. if err != nil {
  579. // not found, l2Tx will not be added in the selection
  580. return nil, nil, nil,
  581. tracerr.Wrap(fmt.Errorf("invalid L2Tx: ToIdx not found "+
  582. "in StateDB, neither ToEthAddr found in AccountCreationAuths L2DB. ToIdx: %d, ToEthAddr: %s",
  583. l2Tx.ToIdx, l2Tx.ToEthAddr.Hex()))
  584. }
  585. if accAuth.BJJ != l2Tx.ToBJJ {
  586. // if AccountCreationAuth.BJJ is not the same
  587. // than in the tx, tx is not accepted
  588. return nil, nil, nil,
  589. tracerr.Wrap(fmt.Errorf("invalid L2Tx: ToIdx not found in StateDB, "+
  590. "neither ToEthAddr & ToBJJ found in AccountCreationAuths L2DB. "+
  591. "ToIdx: %d, ToEthAddr: %s, ToBJJ: %s",
  592. l2Tx.ToIdx, l2Tx.ToEthAddr.Hex(), l2Tx.ToBJJ.String()))
  593. }
  594. } else {
  595. // case: ToBJJ==0:
  596. // if idx exist for EthAddr use it
  597. _, err := txsel.localAccountsDB.GetIdxByEthAddr(l2Tx.ToEthAddr, l2Tx.TokenID)
  598. if err == nil {
  599. // account for ToEthAddr 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 ToEthAddr
  605. accAuth, err = txsel.l2db.GetAccountCreationAuth(l2Tx.ToEthAddr)
  606. if err != nil {
  607. // not found, l2Tx will not be added in the selection
  608. return nil, nil, nil,
  609. tracerr.Wrap(fmt.Errorf("invalid L2Tx: ToIdx not found in "+
  610. "StateDB, neither ToEthAddr found in "+
  611. "AccountCreationAuths L2DB. ToIdx: %d, ToEthAddr: %s",
  612. l2Tx.ToIdx, l2Tx.ToEthAddr))
  613. }
  614. }
  615. // create L1CoordinatorTx for the accountCreation
  616. l1CoordinatorTx = &common.L1Tx{
  617. Position: positionL1,
  618. UserOrigin: false,
  619. FromEthAddr: accAuth.EthAddr,
  620. FromBJJ: accAuth.BJJ,
  621. TokenID: l2Tx.TokenID,
  622. Amount: big.NewInt(0),
  623. DepositAmount: big.NewInt(0),
  624. Type: common.TxTypeCreateAccountDeposit,
  625. }
  626. } else if l2Tx.ToEthAddr == common.FFAddr && l2Tx.ToBJJ != common.EmptyBJJComp {
  627. // if idx exist for EthAddr&BJJ use it
  628. _, err := txsel.localAccountsDB.GetIdxByEthAddrBJJ(l2Tx.ToEthAddr, l2Tx.ToBJJ,
  629. l2Tx.TokenID)
  630. if err == nil {
  631. // account for ToEthAddr&ToBJJ already exist, (where ToEthAddr==0xff)
  632. // there is no need to create a new one.
  633. // tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx
  634. return &l2Tx, nil, nil, nil
  635. }
  636. // if idx don't exist for EthAddr&BJJ, coordinator can create a
  637. // new account without L1Authorization, as ToEthAddr==0xff
  638. // create L1CoordinatorTx for the accountCreation
  639. l1CoordinatorTx = &common.L1Tx{
  640. Position: positionL1,
  641. UserOrigin: false,
  642. FromEthAddr: l2Tx.ToEthAddr,
  643. FromBJJ: l2Tx.ToBJJ,
  644. TokenID: l2Tx.TokenID,
  645. Amount: big.NewInt(0),
  646. DepositAmount: big.NewInt(0),
  647. Type: common.TxTypeCreateAccountDeposit,
  648. }
  649. }
  650. // if there is no space for the L1CoordinatorTx as MaxL1Tx, or no space
  651. // for L1CoordinatorTx + L2Tx as MaxTx, discard the L2Tx
  652. if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-nL1UserTxs ||
  653. len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-nL1UserTxs {
  654. // L2Tx discarded
  655. return nil, nil, nil, tracerr.Wrap(fmt.Errorf("L2Tx discarded due to no available slots " +
  656. "for L1CoordinatorTx to create a new account for receiver of L2Tx"))
  657. }
  658. return &l2Tx, l1CoordinatorTx, accAuth, nil
  659. }
  660. func checkPendingToCreateL1CoordTx(l1CoordinatorTxs []common.L1Tx, tokenID common.TokenID,
  661. addr ethCommon.Address, bjj babyjub.PublicKeyComp) bool {
  662. for i := 0; i < len(l1CoordinatorTxs); i++ {
  663. if l1CoordinatorTxs[i].FromEthAddr == addr &&
  664. l1CoordinatorTxs[i].TokenID == tokenID &&
  665. l1CoordinatorTxs[i].FromBJJ == bjj {
  666. return true
  667. }
  668. }
  669. return false
  670. }
  671. func checkPendingToCreateFutureTxs(l1UserFutureTxs []common.L1Tx, tokenID common.TokenID,
  672. addr ethCommon.Address, bjj babyjub.PublicKeyComp) bool {
  673. for i := 0; i < len(l1UserFutureTxs); i++ {
  674. if l1UserFutureTxs[i].FromEthAddr == addr &&
  675. l1UserFutureTxs[i].TokenID == tokenID &&
  676. l1UserFutureTxs[i].FromBJJ == bjj {
  677. return true
  678. }
  679. if l1UserFutureTxs[i].FromEthAddr == addr &&
  680. l1UserFutureTxs[i].TokenID == tokenID &&
  681. common.EmptyBJJComp == bjj {
  682. return true
  683. }
  684. }
  685. return false
  686. }
  687. // sortL2Txs sorts the PoolL2Txs by AbsoluteFee and then by Nonce
  688. func sortL2Txs(l2Txs []common.PoolL2Tx) []common.PoolL2Tx {
  689. // Sort by absolute fee with SliceStable, so that txs with same
  690. // AbsoluteFee are not rearranged and nonce order is kept in such case
  691. sort.SliceStable(l2Txs, func(i, j int) bool {
  692. return l2Txs[i].AbsoluteFee > l2Txs[j].AbsoluteFee
  693. })
  694. // sort l2Txs by Nonce. This can be done in many different ways, what
  695. // is needed is to output the l2Txs where the Nonce of l2Txs for each
  696. // Account is sorted, but the l2Txs can not be grouped by sender Account
  697. // neither by Fee. This is because later on the Nonces will need to be
  698. // sequential for the zkproof generation.
  699. sort.Slice(l2Txs, func(i, j int) bool {
  700. return l2Txs[i].Nonce < l2Txs[j].Nonce
  701. })
  702. return l2Txs
  703. }
  704. func splitL2ForgableAndNonForgable(tp *txprocessor.TxProcessor,
  705. l2Txs []common.PoolL2Tx) ([]common.PoolL2Tx, []common.PoolL2Tx) {
  706. var l2TxsForgable, l2TxsNonForgable []common.PoolL2Tx
  707. for i := 0; i < len(l2Txs); i++ {
  708. accSender, err := tp.StateDB().GetAccount(l2Txs[i].FromIdx)
  709. if err != nil {
  710. l2TxsNonForgable = append(l2TxsNonForgable, l2Txs[i])
  711. continue
  712. }
  713. if l2Txs[i].Nonce != accSender.Nonce {
  714. l2TxsNonForgable = append(l2TxsNonForgable, l2Txs[i])
  715. continue
  716. }
  717. enoughBalance, _, _ := tp.CheckEnoughBalance(l2Txs[i])
  718. if !enoughBalance {
  719. l2TxsNonForgable = append(l2TxsNonForgable, l2Txs[i])
  720. continue
  721. }
  722. l2TxsForgable = append(l2TxsForgable, l2Txs[i])
  723. }
  724. return l2TxsForgable, l2TxsNonForgable
  725. }