mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-06 19:06:42 +01:00
Merge pull request #648 from hermeznetwork/feature/txsel-forgablepriority
Update TxSel selection to prioritize the forgable
This commit is contained in:
@@ -164,30 +164,33 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
// implementation that can be used ASAP.
|
||||
|
||||
// Steps of this method:
|
||||
// - getPendingTxs
|
||||
// - ProcessL1Txs
|
||||
// - getProfitable (sort by fee & nonce)
|
||||
// - loop over l2Txs
|
||||
// - Fill tx.TokenID tx.Nonce
|
||||
// - Check enough Balance on sender
|
||||
// - Check Nonce
|
||||
// - Create CoordAccount L1CoordTx for TokenID if needed
|
||||
// - & ProcessL1Tx of L1CoordTx
|
||||
// - Check validity of receiver Account for ToEthAddr / ToBJJ
|
||||
// - Create UserAccount L1CoordTx if needed (and possible)
|
||||
// - If everything is fine, store l2Tx to validTxs & update NoncesMap
|
||||
// - ProcessL1Txs (User txs)
|
||||
// - getPendingTxs (forgable directly with current state & not forgable
|
||||
// yet)
|
||||
// - split between l2TxsForgable & l2TxsNonForgable, where:
|
||||
// - l2TxsForgable are the txs that are directly forgable with the
|
||||
// current state
|
||||
// - l2TxsNonForgable are the txs that are not directly forgable
|
||||
// with the current state, but that may be forgable once the
|
||||
// l2TxsForgable ones are processed
|
||||
// - for l2TxsForgable, and if needed, for l2TxsNonForgable:
|
||||
// - sort by Fee & Nonce
|
||||
// - loop over l2Txs (txsel.processL2Txs)
|
||||
// - Fill tx.TokenID tx.Nonce
|
||||
// - Check enough Balance on sender
|
||||
// - Check Nonce
|
||||
// - Create CoordAccount L1CoordTx for TokenID if needed
|
||||
// - & ProcessL1Tx of L1CoordTx
|
||||
// - Check validity of receiver Account for ToEthAddr / ToBJJ
|
||||
// - Create UserAccount L1CoordTx if needed (and possible)
|
||||
// - If everything is fine, store l2Tx to validTxs & update NoncesMap
|
||||
// - Prepare coordIdxsMap & AccumulatedFees
|
||||
// - Distribute AccumulatedFees to CoordIdxs
|
||||
// - MakeCheckpoint
|
||||
|
||||
// get pending l2-tx from tx-pool
|
||||
l2TxsRaw, err := txsel.l2db.GetPendingTxs()
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
|
||||
txselStateDB := txsel.localAccountsDB.StateDB
|
||||
tp := txprocessor.NewTxProcessor(txselStateDB, selectionConfig)
|
||||
tp.AccumulatedFees = make(map[common.Idx]*big.Int)
|
||||
|
||||
// Process L1UserTxs
|
||||
for i := 0; i < len(l1UserTxs); i++ {
|
||||
@@ -198,21 +201,139 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
}
|
||||
}
|
||||
|
||||
var l1CoordinatorTxs []common.L1Tx
|
||||
positionL1 := len(l1UserTxs)
|
||||
var accAuths [][]byte
|
||||
l2TxsFromDB, err := txsel.l2db.GetPendingTxs()
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
l2TxsForgable, l2TxsNonForgable := splitL2ForgableAndNonForgable(tp, l2TxsFromDB)
|
||||
|
||||
// Sort l2TxsRaw (cropping at MaxTx at this point).
|
||||
// discardedL2Txs contains an array of the L2Txs that have not been
|
||||
// selected in this Batch.
|
||||
l2Txs, discardedL2Txs := txsel.getL2Profitable(l2TxsRaw, selectionConfig.MaxTx-uint32(len(l1UserTxs)))
|
||||
for i := range discardedL2Txs {
|
||||
discardedL2Txs[i].Info =
|
||||
"Tx not selected due to low absolute fee (does not fit inside the profitable set)"
|
||||
// in case that length of l2TxsForgable is 0, no need to continue, there
|
||||
// is no L2Txs to forge at all
|
||||
if len(l2TxsForgable) == 0 {
|
||||
var discardedL2Txs []common.PoolL2Tx
|
||||
for i := 0; i < len(l2TxsNonForgable); i++ {
|
||||
l2TxsNonForgable[i].Info =
|
||||
"Tx not selected due impossibility to be forged with the current state"
|
||||
discardedL2Txs = append(discardedL2Txs, l2TxsNonForgable[i])
|
||||
}
|
||||
err = tp.StateDB().MakeCheckpoint()
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
|
||||
metricSelectedL1UserTxs.Set(float64(len(l1UserTxs)))
|
||||
metricSelectedL1CoordinatorTxs.Set(0)
|
||||
metricSelectedL2Txs.Set(0)
|
||||
metricDiscardedL2Txs.Set(float64(len(discardedL2Txs)))
|
||||
return nil, nil, l1UserTxs, nil, nil, discardedL2Txs, nil
|
||||
}
|
||||
|
||||
var validTxs []common.PoolL2Tx
|
||||
tp.AccumulatedFees = make(map[common.Idx]*big.Int)
|
||||
var accAuths [][]byte
|
||||
var l1CoordinatorTxs []common.L1Tx
|
||||
var validTxs, discardedL2Txs []common.PoolL2Tx
|
||||
l2TxsForgable = sortL2Txs(l2TxsForgable)
|
||||
accAuths, l1CoordinatorTxs, validTxs, discardedL2Txs, err =
|
||||
txsel.processL2Txs(tp, selectionConfig, len(l1UserTxs),
|
||||
l2TxsForgable, validTxs, discardedL2Txs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
|
||||
// if there is space for more txs get also the NonForgable txs, that may
|
||||
// be unblocked once the Forgable ones are processed
|
||||
if len(validTxs) < int(selectionConfig.MaxTx)-(len(l1UserTxs)+len(l1CoordinatorTxs)) {
|
||||
l2TxsNonForgable = sortL2Txs(l2TxsNonForgable)
|
||||
var accAuths2 [][]byte
|
||||
var l1CoordinatorTxs2 []common.L1Tx
|
||||
accAuths2, l1CoordinatorTxs2, validTxs, discardedL2Txs, err =
|
||||
txsel.processL2Txs(tp, selectionConfig,
|
||||
len(l1UserTxs)+len(l1CoordinatorTxs), l2TxsNonForgable,
|
||||
validTxs, discardedL2Txs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
|
||||
accAuths = append(accAuths, accAuths2...)
|
||||
l1CoordinatorTxs = append(l1CoordinatorTxs, l1CoordinatorTxs2...)
|
||||
} else {
|
||||
// if there is no space for NonForgable txs, put them at the
|
||||
// discardedL2Txs array
|
||||
for i := 0; i < len(l2TxsNonForgable); i++ {
|
||||
l2TxsNonForgable[i].Info =
|
||||
"Tx not selected due not available slots for L2Txs"
|
||||
discardedL2Txs = append(discardedL2Txs, l2TxsNonForgable[i])
|
||||
}
|
||||
}
|
||||
|
||||
// get CoordIdxsMap for the TokenIDs
|
||||
coordIdxsMap := make(map[common.TokenID]common.Idx)
|
||||
for i := 0; i < len(validTxs); i++ {
|
||||
// get TokenID from tx.Sender
|
||||
accSender, err := tp.StateDB().GetAccount(validTxs[i].FromIdx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
tokenID := accSender.TokenID
|
||||
|
||||
coordIdx, err := txsel.getCoordIdx(tokenID)
|
||||
if err != nil {
|
||||
// if err is db.ErrNotFound, should not happen, as all
|
||||
// the validTxs.TokenID should have a CoordinatorIdx
|
||||
// created in the DB at this point
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
coordIdxsMap[tokenID] = coordIdx
|
||||
}
|
||||
|
||||
var coordIdxs []common.Idx
|
||||
for _, idx := range coordIdxsMap {
|
||||
coordIdxs = append(coordIdxs, idx)
|
||||
}
|
||||
// sort CoordIdxs
|
||||
sort.SliceStable(coordIdxs, func(i, j int) bool {
|
||||
return coordIdxs[i] < coordIdxs[j]
|
||||
})
|
||||
|
||||
// distribute the AccumulatedFees from the processed L2Txs into the
|
||||
// Coordinator Idxs
|
||||
for idx, accumulatedFee := range tp.AccumulatedFees {
|
||||
cmp := accumulatedFee.Cmp(big.NewInt(0))
|
||||
if cmp == 1 { // accumulatedFee>0
|
||||
// send the fee to the Idx of the Coordinator for the TokenID
|
||||
accCoord, err := txsel.localAccountsDB.GetAccount(idx)
|
||||
if err != nil {
|
||||
log.Errorw("Can not distribute accumulated fees to coordinator "+
|
||||
"account: No coord Idx to receive fee", "idx", idx)
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
accCoord.Balance = new(big.Int).Add(accCoord.Balance, accumulatedFee)
|
||||
_, err = txsel.localAccountsDB.UpdateAccount(idx, accCoord)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = tp.StateDB().MakeCheckpoint()
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
|
||||
metricSelectedL1UserTxs.Set(float64(len(l1UserTxs)))
|
||||
metricSelectedL1CoordinatorTxs.Set(float64(len(l1CoordinatorTxs)))
|
||||
metricSelectedL2Txs.Set(float64(len(validTxs)))
|
||||
metricDiscardedL2Txs.Set(float64(len(discardedL2Txs)))
|
||||
|
||||
return coordIdxs, accAuths, l1UserTxs, l1CoordinatorTxs, validTxs, discardedL2Txs, nil
|
||||
}
|
||||
|
||||
func (txsel *TxSelector) processL2Txs(tp *txprocessor.TxProcessor,
|
||||
selectionConfig txprocessor.Config, nL1Txs int, l2Txs, validTxs, discardedL2Txs []common.PoolL2Tx) (
|
||||
[][]byte, []common.L1Tx, []common.PoolL2Tx, []common.PoolL2Tx, error) {
|
||||
var l1CoordinatorTxs []common.L1Tx
|
||||
positionL1 := nL1Txs
|
||||
var accAuths [][]byte
|
||||
// Iterate over l2Txs
|
||||
// - check Nonces
|
||||
// - check enough Balance for the Amount+Fee
|
||||
@@ -221,20 +342,22 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
// - put the valid txs into validTxs array
|
||||
for i := 0; i < len(l2Txs); i++ {
|
||||
// Check if there is space for more L2Txs in the selection
|
||||
maxL2Txs := int(selectionConfig.MaxTx) -
|
||||
len(l1UserTxs) - len(l1CoordinatorTxs)
|
||||
maxL2Txs := int(selectionConfig.MaxTx) - nL1Txs - len(l1CoordinatorTxs)
|
||||
if len(validTxs) >= maxL2Txs {
|
||||
// no more available slots for L2Txs
|
||||
l2Txs[i].Info =
|
||||
"Tx not selected due not available slots for L2Txs"
|
||||
discardedL2Txs = append(discardedL2Txs, l2Txs[i])
|
||||
continue
|
||||
// no more available slots for L2Txs, so mark this tx
|
||||
// but also the rest of remaining txs as discarded
|
||||
for j := i; j < len(l2Txs); j++ {
|
||||
l2Txs[j].Info =
|
||||
"Tx not selected due not available slots for L2Txs"
|
||||
discardedL2Txs = append(discardedL2Txs, l2Txs[j])
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// get Nonce & TokenID from the Account by l2Tx.FromIdx
|
||||
accSender, err := tp.StateDB().GetAccount(l2Txs[i].FromIdx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
return nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
l2Txs[i].TokenID = accSender.TokenID
|
||||
|
||||
@@ -272,13 +395,13 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
txsel.coordAccountForTokenID(l1CoordinatorTxs,
|
||||
accSender.TokenID, positionL1)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
return nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
if newL1CoordTx != nil {
|
||||
// if there is no space for the L1CoordinatorTx as MaxL1Tx, or no space
|
||||
// for L1CoordinatorTx + L2Tx as MaxTx, discard the L2Tx
|
||||
if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-len(l1UserTxs) ||
|
||||
len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-len(l1UserTxs) {
|
||||
if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-nL1Txs ||
|
||||
len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-nL1Txs {
|
||||
// discard L2Tx, and update Info parameter of
|
||||
// the tx, and add it to the discardedTxs array
|
||||
l2Txs[i].Info = "Tx not selected because the L2Tx depends on a " +
|
||||
@@ -294,7 +417,7 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
// process the L1CoordTx
|
||||
_, _, _, _, err := tp.ProcessL1Tx(nil, newL1CoordTx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
return nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +432,7 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
if l2Txs[i].ToIdx == 0 { // ToEthAddr/ToBJJ case
|
||||
validL2Tx, l1CoordinatorTx, accAuth, err :=
|
||||
txsel.processTxToEthAddrBJJ(validTxs, selectionConfig,
|
||||
len(l1UserTxs), l1CoordinatorTxs, positionL1, l2Txs[i])
|
||||
nL1Txs, l1CoordinatorTxs, positionL1, l2Txs[i])
|
||||
if err != nil {
|
||||
log.Debugw("txsel.processTxToEthAddrBJJ", "err", err)
|
||||
// Discard L2Tx, and update Info parameter of
|
||||
@@ -321,8 +444,8 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
}
|
||||
// if there is no space for the L1CoordinatorTx as MaxL1Tx, or no space
|
||||
// for L1CoordinatorTx + L2Tx as MaxTx, discard the L2Tx
|
||||
if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-len(l1UserTxs) ||
|
||||
len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-len(l1UserTxs) {
|
||||
if len(l1CoordinatorTxs) >= int(selectionConfig.MaxL1Tx)-nL1Txs ||
|
||||
len(l1CoordinatorTxs)+1 >= int(selectionConfig.MaxTx)-nL1Txs {
|
||||
// discard L2Tx, and update Info parameter of
|
||||
// the tx, and add it to the discardedTxs array
|
||||
l2Txs[i].Info = "Tx not selected because the L2Tx depends on a " +
|
||||
@@ -351,7 +474,7 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
// process the L1CoordTx
|
||||
_, _, _, _, err := tp.ProcessL1Tx(nil, l1CoordinatorTx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
return nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
}
|
||||
if validL2Tx == nil {
|
||||
@@ -413,7 +536,7 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
// if err is db.ErrNotFound, should not happen, as all
|
||||
// the validTxs.TokenID should have a CoordinatorIdx
|
||||
// created in the DB at this point
|
||||
return nil, nil, nil, nil, nil, nil,
|
||||
return nil, nil, nil, nil,
|
||||
tracerr.Wrap(fmt.Errorf("Could not get CoordIdx for TokenID=%d, "+
|
||||
"due: %s", tokenID, err))
|
||||
}
|
||||
@@ -439,68 +562,7 @@ func (txsel *TxSelector) getL1L2TxSelection(selectionConfig txprocessor.Config,
|
||||
validTxs = append(validTxs, l2Txs[i])
|
||||
} // after this loop, no checks to discard txs should be done
|
||||
|
||||
// get CoordIdxsMap for the TokenIDs
|
||||
coordIdxsMap := make(map[common.TokenID]common.Idx)
|
||||
for i := 0; i < len(validTxs); i++ {
|
||||
// get TokenID from tx.Sender
|
||||
accSender, err := tp.StateDB().GetAccount(validTxs[i].FromIdx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
tokenID := accSender.TokenID
|
||||
|
||||
coordIdx, err := txsel.getCoordIdx(tokenID)
|
||||
if err != nil {
|
||||
// if err is db.ErrNotFound, should not happen, as all
|
||||
// the validTxs.TokenID should have a CoordinatorIdx
|
||||
// created in the DB at this point
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
coordIdxsMap[tokenID] = coordIdx
|
||||
}
|
||||
|
||||
var coordIdxs []common.Idx
|
||||
for _, idx := range coordIdxsMap {
|
||||
coordIdxs = append(coordIdxs, idx)
|
||||
}
|
||||
// sort CoordIdxs
|
||||
sort.SliceStable(coordIdxs, func(i, j int) bool {
|
||||
return coordIdxs[i] < coordIdxs[j]
|
||||
})
|
||||
|
||||
// distribute the AccumulatedFees from the processed L2Txs into the
|
||||
// Coordinator Idxs
|
||||
for idx, accumulatedFee := range tp.AccumulatedFees {
|
||||
cmp := accumulatedFee.Cmp(big.NewInt(0))
|
||||
if cmp == 1 { // accumulatedFee>0
|
||||
// send the fee to the Idx of the Coordinator for the TokenID
|
||||
accCoord, err := txsel.localAccountsDB.GetAccount(idx)
|
||||
if err != nil {
|
||||
log.Errorw("Can not distribute accumulated fees to coordinator "+
|
||||
"account: No coord Idx to receive fee", "idx", idx)
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
accCoord.Balance = new(big.Int).Add(accCoord.Balance, accumulatedFee)
|
||||
_, err = txsel.localAccountsDB.UpdateAccount(idx, accCoord)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = tp.StateDB().MakeCheckpoint()
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, tracerr.Wrap(err)
|
||||
}
|
||||
|
||||
metricSelectedL1CoordinatorTxs.Set(float64(len(l1CoordinatorTxs)))
|
||||
metricSelectedL1UserTxs.Set(float64(len(l1UserTxs)))
|
||||
metricSelectedL2Txs.Set(float64(len(validTxs)))
|
||||
metricDiscardedL2Txs.Set(float64(len(discardedL2Txs)))
|
||||
|
||||
// return coordIdxs, accAuths, l1UserTxs, l1CoordinatorTxs, validTxs, discardedL2Txs, nil
|
||||
return coordIdxs, accAuths, l1UserTxs, l1CoordinatorTxs, validTxs, discardedL2Txs, nil
|
||||
return accAuths, l1CoordinatorTxs, validTxs, discardedL2Txs, nil
|
||||
}
|
||||
|
||||
// processTxsToEthAddrBJJ process the common.PoolL2Tx in the case where
|
||||
@@ -636,26 +698,14 @@ func checkAlreadyPendingToCreate(l1CoordinatorTxs []common.L1Tx, tokenID common.
|
||||
return false
|
||||
}
|
||||
|
||||
// getL2Profitable returns the profitable selection of L2Txssorted by Nonce
|
||||
func (txsel *TxSelector) getL2Profitable(l2Txs []common.PoolL2Tx, max uint32) ([]common.PoolL2Tx,
|
||||
[]common.PoolL2Tx) {
|
||||
// First sort by nonce so that txs from the same account are sorted so
|
||||
// that they could be applied in succession.
|
||||
sort.Slice(l2Txs, func(i, j int) bool {
|
||||
return l2Txs[i].Nonce < l2Txs[j].Nonce
|
||||
})
|
||||
// sortL2Txs sorts the PoolL2Txs by AbsoluteFee and then by Nonce
|
||||
func sortL2Txs(l2Txs []common.PoolL2Tx) []common.PoolL2Tx {
|
||||
// Sort by absolute fee with SliceStable, so that txs with same
|
||||
// AbsoluteFee are not rearranged and nonce order is kept in such case
|
||||
sort.SliceStable(l2Txs, func(i, j int) bool {
|
||||
return l2Txs[i].AbsoluteFee > l2Txs[j].AbsoluteFee
|
||||
})
|
||||
|
||||
discardedL2Txs := []common.PoolL2Tx{}
|
||||
if len(l2Txs) > int(max) {
|
||||
discardedL2Txs = l2Txs[max:]
|
||||
l2Txs = l2Txs[:max]
|
||||
}
|
||||
|
||||
// sort l2Txs by Nonce. This can be done in many different ways, what
|
||||
// is needed is to output the l2Txs where the Nonce of l2Txs for each
|
||||
// Account is sorted, but the l2Txs can not be grouped by sender Account
|
||||
@@ -665,5 +715,29 @@ func (txsel *TxSelector) getL2Profitable(l2Txs []common.PoolL2Tx, max uint32) ([
|
||||
return l2Txs[i].Nonce < l2Txs[j].Nonce
|
||||
})
|
||||
|
||||
return l2Txs, discardedL2Txs
|
||||
return l2Txs
|
||||
}
|
||||
|
||||
func splitL2ForgableAndNonForgable(tp *txprocessor.TxProcessor,
|
||||
l2Txs []common.PoolL2Tx) ([]common.PoolL2Tx, []common.PoolL2Tx) {
|
||||
var l2TxsForgable, l2TxsNonForgable []common.PoolL2Tx
|
||||
for i := 0; i < len(l2Txs); i++ {
|
||||
accSender, err := tp.StateDB().GetAccount(l2Txs[i].FromIdx)
|
||||
if err != nil {
|
||||
l2TxsNonForgable = append(l2TxsNonForgable, l2Txs[i])
|
||||
continue
|
||||
}
|
||||
|
||||
if l2Txs[i].Nonce != accSender.Nonce {
|
||||
l2TxsNonForgable = append(l2TxsNonForgable, l2Txs[i])
|
||||
continue
|
||||
}
|
||||
enoughBalance, _, _ := tp.CheckEnoughBalance(l2Txs[i])
|
||||
if !enoughBalance {
|
||||
l2TxsNonForgable = append(l2TxsNonForgable, l2Txs[i])
|
||||
continue
|
||||
}
|
||||
l2TxsForgable = append(l2TxsForgable, l2Txs[i])
|
||||
}
|
||||
return l2TxsForgable, l2TxsNonForgable
|
||||
}
|
||||
|
||||
@@ -26,12 +26,14 @@ import (
|
||||
)
|
||||
|
||||
func initTest(t *testing.T, chainID uint16, hermezContractAddr ethCommon.Address,
|
||||
coordUser *til.User) *TxSelector {
|
||||
coordUser *til.User) (*TxSelector, *historydb.HistoryDB) {
|
||||
pass := os.Getenv("POSTGRES_PASS")
|
||||
db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
|
||||
require.NoError(t, err)
|
||||
l2DB := l2db.NewL2DB(db, db, 10, 100, 0.0, 1000.0, 24*time.Hour, nil)
|
||||
|
||||
historyDB := historydb.NewHistoryDB(db, db, nil)
|
||||
|
||||
dir, err := ioutil.TempDir("", "tmpdb")
|
||||
require.NoError(t, err)
|
||||
defer assert.NoError(t, os.RemoveAll(dir))
|
||||
@@ -65,7 +67,7 @@ func initTest(t *testing.T, chainID uint16, hermezContractAddr ethCommon.Address
|
||||
|
||||
test.WipeDB(txsel.l2db.DB())
|
||||
|
||||
return txsel
|
||||
return txsel, historyDB
|
||||
}
|
||||
|
||||
func addAccCreationAuth(t *testing.T, tc *til.Context, txsel *TxSelector, chainID uint16,
|
||||
@@ -157,7 +159,7 @@ func TestGetL2TxSelectionMinimumFlow0(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
||||
txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
|
||||
// restart nonces of TilContext, as will be set by generating directly
|
||||
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
||||
@@ -417,7 +419,7 @@ func TestPoolL2TxsWithoutEnoughBalance(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
||||
txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
|
||||
// restart nonces of TilContext, as will be set by generating directly
|
||||
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
||||
@@ -470,11 +472,6 @@ func TestPoolL2TxsWithoutEnoughBalance(t *testing.T) {
|
||||
tc.RestartNonces()
|
||||
|
||||
// batch3
|
||||
// NOTE: this batch will result with 1 L2Tx, as the PoolExit tx is not
|
||||
// possible, as the PoolTransferToEthAddr is not processed yet when
|
||||
// checking availability of PoolExit. This, in a near-future iteration
|
||||
// of the TxSelector will return the 2 transactions as valid and
|
||||
// selected, as the TxSelector will handle this kind of combinations.
|
||||
batchPoolL2 = `
|
||||
Type: PoolL2
|
||||
PoolTransferToEthAddr(0) A-B: 50 (126)`
|
||||
@@ -488,11 +485,11 @@ func TestPoolL2TxsWithoutEnoughBalance(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(oL1UserTxs))
|
||||
assert.Equal(t, 0, len(oL1CoordTxs))
|
||||
assert.Equal(t, 1, len(oL2Txs)) // see 'NOTE' at the beginning of 'batch3' of this test
|
||||
assert.Equal(t, 2, len(discardedL2Txs))
|
||||
assert.Equal(t, 2, len(oL2Txs))
|
||||
assert.Equal(t, 1, len(discardedL2Txs))
|
||||
assert.Equal(t, expectedTxID2, oL2Txs[0].TxID.String())
|
||||
assert.Equal(t, expectedTxID1, oL2Txs[1].TxID.String())
|
||||
assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
|
||||
assert.Equal(t, expectedTxID1, discardedL2Txs[1].TxID.String())
|
||||
assert.Equal(t, common.TxTypeTransferToEthAddr, oL2Txs[0].Type)
|
||||
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
||||
txsel.localAccountsDB.CurrentBatch())
|
||||
@@ -507,12 +504,8 @@ func TestPoolL2TxsWithoutEnoughBalance(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(oL1UserTxs))
|
||||
assert.Equal(t, 0, len(oL1CoordTxs))
|
||||
assert.Equal(t, 1, len(oL2Txs))
|
||||
assert.Equal(t, 0, len(oL2Txs))
|
||||
assert.Equal(t, 1, len(discardedL2Txs))
|
||||
// the Exit that was not accepted at the batch2
|
||||
assert.Equal(t, expectedTxID1, oL2Txs[0].TxID.String())
|
||||
assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
|
||||
assert.Equal(t, common.TxTypeExit, oL2Txs[0].Type)
|
||||
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
||||
txsel.localAccountsDB.CurrentBatch())
|
||||
require.NoError(t, err)
|
||||
@@ -539,7 +532,7 @@ func TestTransferToBjj(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
||||
txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
|
||||
// restart nonces of TilContext, as will be set by generating directly
|
||||
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
||||
@@ -670,7 +663,7 @@ func TestTransferManyFromSameAccount(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
||||
txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
|
||||
// restart nonces of TilContext, as will be set by generating directly
|
||||
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
||||
@@ -752,7 +745,7 @@ func TestPoolL2TxInvalidNonces(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
||||
txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
|
||||
// restart nonces of TilContext, as will be set by generating directly
|
||||
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
||||
@@ -865,7 +858,7 @@ func TestProcessL2Selection(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
||||
txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
|
||||
// restart nonces of TilContext, as will be set by generating directly
|
||||
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
||||
@@ -921,3 +914,127 @@ func TestProcessL2Selection(t *testing.T) {
|
||||
txsel.localAccountsDB.CurrentBatch())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidTxsWithLowFeeAndInvalidTxsWithHighFee(t *testing.T) {
|
||||
// This test recreates the case where there are
|
||||
|
||||
set := `
|
||||
Type: Blockchain
|
||||
|
||||
CreateAccountDeposit(0) Coord: 0
|
||||
CreateAccountDeposit(0) A: 100
|
||||
CreateAccountDeposit(0) B: 0
|
||||
|
||||
> batchL1 // Batch1: freeze L1User{3}
|
||||
> batchL1 // Batch2: forge L1User{3}
|
||||
> block
|
||||
`
|
||||
|
||||
chainID := uint16(0)
|
||||
tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
|
||||
tilCfgExtra := til.ConfigExtra{
|
||||
BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
|
||||
CoordUser: "Coord",
|
||||
}
|
||||
blocks, err := tc.GenerateBlocks(set)
|
||||
require.NoError(t, err)
|
||||
err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
|
||||
require.NoError(t, err)
|
||||
err = tc.FillBlocksForgedL1UserTxs(blocks)
|
||||
require.NoError(t, err)
|
||||
|
||||
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
||||
txsel, historyDB := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
||||
|
||||
// Insert blocks into DB
|
||||
for i := range blocks {
|
||||
err = historyDB.AddBlockSCData(&blocks[i])
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
err = historyDB.UpdateTokenValue(common.EmptyAddr, 1000)
|
||||
require.NoError(t, err)
|
||||
|
||||
// restart nonces of TilContext, as will be set by generating directly
|
||||
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
||||
tc.RestartNonces()
|
||||
|
||||
tpc := txprocessor.Config{
|
||||
NLevels: 16,
|
||||
MaxFeeTx: 5,
|
||||
MaxTx: 5,
|
||||
MaxL1Tx: 3,
|
||||
ChainID: chainID,
|
||||
}
|
||||
// batch1 to freeze L1UserTxs
|
||||
l1UserTxs := []common.L1Tx{}
|
||||
_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(tpc, l1UserTxs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// batch 2 to crate the accounts (from L1UserTxs)
|
||||
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
|
||||
|
||||
// select L1 & L2 txs
|
||||
_, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
|
||||
txsel.GetL1L2TxSelection(tpc, l1UserTxs)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 3, len(oL1UserTxs))
|
||||
require.Equal(t, 0, len(oL1CoordTxs))
|
||||
require.Equal(t, 0, len(oL2Txs))
|
||||
require.Equal(t, 0, len(discardedL2Txs))
|
||||
require.Equal(t, 0, len(accAuths))
|
||||
|
||||
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
||||
txsel.localAccountsDB.CurrentBatch())
|
||||
require.NoError(t, err)
|
||||
|
||||
// batch 3. The A-B txs have lower fee, but are the only ones possible
|
||||
// with the current Accounts Balances, as the B-A tx of amount 40 will
|
||||
// not be included as will be processed first when there is not enough
|
||||
// balance at B (processed first as the TxSelector sorts by Fee and then
|
||||
// by Nonce).
|
||||
batchPoolL2 := `
|
||||
Type: PoolL2
|
||||
PoolTransfer(0) B-A: 40 (130) // B-A txs are only possible once A-B txs are processed
|
||||
PoolTransfer(0) B-A: 1 (126)
|
||||
PoolTransfer(0) B-A: 1 (126)
|
||||
PoolTransfer(0) B-A: 1 (126)
|
||||
PoolTransfer(0) B-A: 1 (126)
|
||||
PoolTransfer(0) B-A: 1 (126)
|
||||
PoolTransfer(0) B-A: 1 (126)
|
||||
PoolTransfer(0) B-A: 1 (126)
|
||||
PoolTransfer(0) A-B: 20 (20)
|
||||
PoolTransfer(0) A-B: 25 (150)
|
||||
PoolTransfer(0) A-B: 20 (20)
|
||||
`
|
||||
poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 11, len(poolL2Txs))
|
||||
// add the PoolL2Txs to the l2DB
|
||||
addL2Txs(t, txsel, poolL2Txs)
|
||||
l1UserTxs = []common.L1Tx{}
|
||||
_, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
||||
txsel.GetL1L2TxSelection(tpc, l1UserTxs)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 0, len(oL1UserTxs))
|
||||
require.Equal(t, 0, len(oL1CoordTxs))
|
||||
require.Equal(t, 3, len(oL2Txs)) // the 3 txs A-B
|
||||
require.Equal(t, 8, len(discardedL2Txs)) // the 8 txs B-A
|
||||
require.Equal(t, 0, len(accAuths))
|
||||
|
||||
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
||||
txsel.localAccountsDB.CurrentBatch())
|
||||
require.NoError(t, err)
|
||||
|
||||
// batch 4. In this Batch, account B has enough balance to send the txs
|
||||
_, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
||||
txsel.GetL1L2TxSelection(tpc, l1UserTxs)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 0, len(oL1UserTxs))
|
||||
require.Equal(t, 0, len(oL1CoordTxs))
|
||||
require.Equal(t, 5, len(oL2Txs))
|
||||
require.Equal(t, 3, len(discardedL2Txs))
|
||||
require.Equal(t, 0, len(accAuths))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user