diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolBenchmark.scala index 1e8802027a..cd4176b592 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolBenchmark.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolBenchmark.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.mempool -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.utils.generators.ErgoTransactionGenerators import org.scalameter.KeyValue import org.scalameter.api._ diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/MempoolPerformanceBench.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/MempoolPerformanceBench.scala index c15db166f0..f38a076d89 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/MempoolPerformanceBench.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/MempoolPerformanceBench.scala @@ -1,6 +1,7 @@ package org.ergoplatform.nodeView.mempool -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool import org.ergoplatform.utils.generators.{ErgoGenerators, ErgoTransactionGenerators} import org.scalacheck.Gen import org.scalatest.propspec.AnyPropSpec @@ -15,5 +16,5 @@ class MempoolPerformanceBench extends AnyPropSpec override val memPoolGenerator: Gen[ErgoMemPool] = emptyMemPoolGen override val transactionGenerator: Gen[ErgoTransaction] = invalidErgoTransactionGen override val unconfirmedTxGenerator: Gen[UnconfirmedTransaction] = - invalidErgoTransactionGen.map(tx => UnconfirmedTransaction(tx, None)) + invalidErgoTransactionGen.map(tx => mempool.UnconfirmedTransaction(tx, None)) } diff --git a/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala index 498e73fa91..2d0c3ce94c 100644 --- a/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala @@ -3,9 +3,9 @@ package org.ergoplatform.http.api import akka.actor.ActorRef import akka.http.scaladsl.server.Route import akka.http.scaladsl.server.{Directive1, ValidationRejection} -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} -import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader +import org.ergoplatform.nodeView.mempool.{ErgoMemPoolReader, UnconfirmedTransaction} import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} import org.ergoplatform.settings.{Algos, ErgoSettings} import scorex.core.api.http.{ApiError, ApiRoute} diff --git a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala index 3f5679c499..94077a52df 100644 --- a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala @@ -7,9 +7,9 @@ import akka.pattern.ask import io.circe.Json import io.circe.syntax._ import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, TokenId} -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer} import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} -import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader +import org.ergoplatform.nodeView.mempool.{ErgoMemPoolReader, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.HistogramStats.getFeeHistogram import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} import org.ergoplatform.settings.{Algos, ErgoSettings} @@ -135,7 +135,7 @@ case class TransactionsApiRoute(readersHolder: ActorRef, val feeHistogramParameters: Directive[(Int, Long)] = parameters("bins".as[Int] ? 10, "maxtime".as[Long] ? (60*1000L)) def getFeeHistogramR: Route = (path("poolHistogram") & get & feeHistogramParameters) { (bins, maxtime) => - ApiResponse(getMemPool.map(p => getFeeHistogram(System.currentTimeMillis(), bins, maxtime, p.weightedTransactionIds(Int.MaxValue)).asJson)) + ApiResponse(getMemPool.map(p => getFeeHistogram(System.currentTimeMillis(), bins, maxtime, p.txTimesAndWeights).asJson)) } val feeRequestParameters: Directive[(Int, Int)] = parameters("waitTime".as[Int] ? 1, "txSize".as[Int] ? 100) diff --git a/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala index 7669192f19..43153ec1fb 100644 --- a/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala @@ -7,7 +7,7 @@ import io.circe.syntax._ import io.circe.{Encoder, Json} import org.ergoplatform._ import org.ergoplatform.http.api.requests.HintExtractionRequest -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.wallet._ import org.ergoplatform.nodeView.wallet.requests._ @@ -25,6 +25,7 @@ import scala.concurrent.Future import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} import akka.http.scaladsl.server.MissingQueryParamRejection +import org.ergoplatform.nodeView.mempool.UnconfirmedTransaction case class WalletApiRoute(readersHolder: ActorRef, nodeViewActorRef: ActorRef, diff --git a/src/main/scala/org/ergoplatform/local/CleanupWorker.scala b/src/main/scala/org/ergoplatform/local/CleanupWorker.scala index 1cd15c4c3f..21bd047454 100644 --- a/src/main/scala/org/ergoplatform/local/CleanupWorker.scala +++ b/src/main/scala/org/ergoplatform/local/CleanupWorker.scala @@ -3,8 +3,7 @@ package org.ergoplatform.local import akka.actor.{Actor, ActorRef} import org.ergoplatform.local.CleanupWorker.RunCleanup import org.ergoplatform.local.MempoolAuditor.CleanupDone -import org.ergoplatform.modifiers.mempool.UnconfirmedTransaction -import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader +import org.ergoplatform.nodeView.mempool.{ErgoMemPoolReader, UnconfirmedTransaction} import org.ergoplatform.nodeView.state.UtxoStateReader import org.ergoplatform.settings.NodeConfigurationSettings import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{EliminateTransactions, RecheckedTransactions} diff --git a/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala b/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala index 6578613c5e..b5a9db6452 100644 --- a/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala +++ b/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala @@ -4,8 +4,8 @@ import akka.actor.SupervisorStrategy.{Restart, Stop} import akka.actor.{Actor, ActorInitializationException, ActorKilledException, ActorRef, ActorRefFactory, DeathPactException, OneForOneStrategy, Props} import org.ergoplatform.local.CleanupWorker.RunCleanup import org.ergoplatform.local.MempoolAuditor.CleanupDone -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.{ErgoMemPoolReader, UnconfirmedTransaction} import org.ergoplatform.settings.ErgoSettings import scorex.core.network.Broadcast import scorex.core.network.NetworkController.ReceivableMessages.SendToNetwork @@ -101,15 +101,15 @@ class MempoolAuditor(nodeViewHolderRef: ActorRef, val stateToCheck = utxoState.withUnconfirmedTransactions(toBroadcast) toBroadcast.foreach { unconfirmedTx => if (unconfirmedTx.transaction.inputIds.forall(inputBoxId => stateToCheck.boxById(inputBoxId).isDefined)) { - log.info(s"Rebroadcasting $unconfirmedTx") + log.info(s"Rebroadcasting ${unconfirmedTx.id}") broadcastTx(unconfirmedTx) } else { - log.info(s"Not rebroadcasting $unconfirmedTx as not all the inputs are in place") + log.info(s"Not rebroadcasting ${unconfirmedTx.id} as not all the inputs are in place") } } case _ => toBroadcast.foreach { unconfirmedTx => - log.warn(s"Rebroadcasting $unconfirmedTx while state is not ready or not UTXO set") + log.warn(s"Rebroadcasting ${unconfirmedTx.id} while state is not ready or not UTXO set") broadcastTx(unconfirmedTx) } } diff --git a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala index 91fc5c3ad4..7dea6fad99 100644 --- a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala +++ b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala @@ -11,13 +11,13 @@ import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.{Header, HeaderWithoutPow} import org.ergoplatform.modifiers.history.popow.NipopowAlgos -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{EliminateTransactions, LocallyGeneratedModifier} import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistory.Height import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} -import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader +import org.ergoplatform.nodeView.mempool.{ErgoMemPoolReader, UnconfirmedTransaction} import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UtxoStateReader} import org.ergoplatform.settings.{ErgoSettings, ErgoValidationSettingsUpdate, Parameters} import org.ergoplatform.sdk.wallet.Constants.MaxAssetsPerBox diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index ede79994ba..054de92707 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -3,14 +3,14 @@ package org.ergoplatform.network import akka.actor.SupervisorStrategy.{Restart, Stop} import akka.actor.{Actor, ActorInitializationException, ActorKilledException, ActorRef, ActorRefFactory, DeathPactException, OneForOneStrategy, Props} import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer} import org.ergoplatform.modifiers.{BlockSection, ManifestTypeId, NetworkObjectTypeId, SnapshotsInfoTypeId, UtxoSnapshotChunkTypeId} import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfoV2} import org.ergoplatform.nodeView.history._ import ErgoNodeViewSynchronizer.{CheckModifiersToDownload, IncomingTxInfo, TransactionProcessingCacheRecord} import org.ergoplatform.nodeView.ErgoNodeViewHolder.BlockAppliedTransactions import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} -import org.ergoplatform.nodeView.mempool.{ErgoMemPool, ErgoMemPoolReader} +import org.ergoplatform.nodeView.mempool.{ErgoMemPool, ErgoMemPoolReader, UnconfirmedTransaction} import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages._ import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{ChainIsHealthy, ChainIsStuck, GetNodeViewChanges, IsChainHealthy, ModifiersFromRemote} diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 7978c3ed31..77e67d0701 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -5,10 +5,10 @@ import akka.actor.{Actor, ActorRef, ActorSystem, OneForOneStrategy, Props} import org.ergoplatform.ErgoApp import org.ergoplatform.ErgoApp.CriticalSystemException import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NetworkObjectTypeId} import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.nodeView.mempool.{ErgoMemPool, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome import org.ergoplatform.nodeView.state._ import org.ergoplatform.nodeView.wallet.ErgoWallet diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala index 8e5431c369..a03ffcd552 100644 --- a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala @@ -2,14 +2,12 @@ package org.ergoplatform.nodeView.mempool import org.ergoplatform.ErgoBox.BoxId import org.ergoplatform.mining.emission.EmissionRules -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.OrderedTxPool.WeightedTxId +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption import org.ergoplatform.nodeView.state.{ErgoState, UtxoState} import org.ergoplatform.settings.{ErgoSettings, MonetarySettings, NodeConfigurationSettings} import scorex.core.transaction.state.TransactionValidation -import scorex.util.{ModifierId, ScorexLogging, bytesToId} -import OrderedTxPool.weighted -import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption +import scorex.util.{ModifierId, ScorexLogging} import spire.syntax.all.cfor import scala.annotation.tailrec @@ -32,13 +30,9 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, (implicit settings: ErgoSettings) extends ErgoMemPoolReader with ScorexLogging { - import ErgoMemPool._ import EmissionRules.CoinsInOneErgo + import ErgoMemPool._ - /** - * When there's no reason to re-check transactions immediately, we assign fake cost to them - */ - private val FakeCost = 1000 private val nodeSettings: NodeConfigurationSettings = settings.nodeSettings private implicit val monetarySettings: MonetarySettings = settings.chainSettings.monetary @@ -49,8 +43,8 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, pool.get(modifierId).map(unconfirmedTx => unconfirmedTx.transaction) } - override def contains(modifierId: ModifierId): Boolean = { - pool.contains(modifierId) + def contains(uTx: UnconfirmedTransaction): Boolean = { + pool.contains(uTx) } override def take(limit: Int): Iterable[UnconfirmedTransaction] = { @@ -93,7 +87,7 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, * @return Success(updatedPool), if transaction successfully added to the pool, Failure(_) otherwise */ def put(unconfirmedTx: UnconfirmedTransaction): ErgoMemPool = { - val updatedPool = pool.put(unconfirmedTx, feeFactor(unconfirmedTx)) + val updatedPool = pool.put(unconfirmedTx) new ErgoMemPool(updatedPool, stats, sortingOption) } @@ -102,14 +96,21 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, } private def updateStatsOnRemoval(tx: ErgoTransaction): MemPoolStatistics = { - val wtx = pool.transactionsRegistry.get(tx.id) - wtx.map(wgtx => stats.add(System.currentTimeMillis(), wgtx)) + pool.get(tx.id).map(tx => stats.add(System.currentTimeMillis(), tx)(sortingOption)) .getOrElse(MemPoolStatistics(System.currentTimeMillis(), 0, System.currentTimeMillis())) } /** - * Remove transaction from the pool + * Remove unconfirmed transaction from the pool */ + def remove(utx: UnconfirmedTransaction): ErgoMemPool = { + log.debug(s"Removing unconfirmed transaction ${utx.id} from the mempool") + new ErgoMemPool(pool.remove(utx), updateStatsOnRemoval(utx.transaction), sortingOption) + } + + /** + * Remove transaction from the pool + */ def remove(tx: ErgoTransaction): ErgoMemPool = { log.debug(s"Removing transaction ${tx.id} from the mempool") new ErgoMemPool(pool.remove(tx), updateStatsOnRemoval(tx), sortingOption) @@ -133,14 +134,8 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, pool.get(unconfirmedTransactionId) match { case Some(utx) => invalidate(utx) case None => - log.warn(s"pool.get failed for $unconfirmedTransactionId") - pool.orderedTransactions.valuesIterator.find(_.id == unconfirmedTransactionId) match { - case Some(utx) => - invalidate(utx) - case None => - log.warn(s"Can't invalidate transaction $unconfirmedTransactionId as it is not in the pool") - this - } + log.warn(s"Can't invalidate transaction $unconfirmedTransactionId as it is not in the pool") + this } } @@ -154,14 +149,6 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, */ override def spentInputs: Iterator[BoxId] = pool.inputs.keysIterator - private def feeFactor(unconfirmedTransaction: UnconfirmedTransaction): Int = { - sortingOption match { - case SortingOption.FeePerByte => - unconfirmedTransaction.transactionBytes.map(_.length).getOrElse(unconfirmedTransaction.transaction.size) - case SortingOption.FeePerCycle => - unconfirmedTransaction.lastCost.getOrElse(FakeCost) - } - } // Check if transaction is double-spending inputs spent in the mempool. // If so, the new transacting is replacing older ones if it has bigger weight (fee/byte) than them on average. @@ -170,31 +157,30 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, validationStartTime: Long): (ErgoMemPool, ProcessingOutcome) = { val tx = unconfirmedTransaction.transaction - val doubleSpendingWtxs = tx.inputs.flatMap { inp => + val doubleSpendingIds = tx.inputs.flatMap { inp => pool.inputs.get(inp.boxId) }.toSet - val feeF = feeFactor(unconfirmedTransaction) + implicit val so: SortingOption = sortingOption - if (doubleSpendingWtxs.nonEmpty) { - val ownWtx = weighted(tx, feeF) - val doubleSpendingTotalWeight = doubleSpendingWtxs.map(_.weight).sum / doubleSpendingWtxs.size - if (ownWtx.weight > doubleSpendingTotalWeight) { - val doubleSpendingTxs = doubleSpendingWtxs.map(wtx => pool.orderedTransactions(wtx)).toSeq - val p = pool.put(unconfirmedTransaction, feeF).remove(doubleSpendingTxs) + if (doubleSpendingIds.nonEmpty) { + val doubleSpendingTxs = doubleSpendingIds.map(pool.get(_).get).toSeq + val doubleSpendingTotalWeight = doubleSpendingTxs.map(_.weight).sum / doubleSpendingTxs.size + if (unconfirmedTransaction.weight > doubleSpendingTotalWeight) { + val p = pool.remove(doubleSpendingTxs).put(unconfirmedTransaction) val updPool = new ErgoMemPool(p, stats, sortingOption) updPool -> new ProcessingOutcome.Accepted(unconfirmedTransaction, validationStartTime) } else { - this -> new ProcessingOutcome.DoubleSpendingLoser(doubleSpendingWtxs.map(_.id), validationStartTime) + this -> new ProcessingOutcome.DoubleSpendingLoser(doubleSpendingIds, validationStartTime) } } else { val poolSizeLimit = nodeSettings.mempoolCapacity if (pool.size == poolSizeLimit && - weighted(tx, feeF).weight <= pool.orderedTransactions.lastKey.weight) { + unconfirmedTransaction.weight <= pool.orderedTransactions.last._2.weight) { val exc = new Exception("Transaction pays less than any other in the pool being full") this -> new ProcessingOutcome.Declined(exc, validationStartTime) } else { - val updPool = new ErgoMemPool(pool.put(unconfirmedTransaction, feeF), stats, sortingOption) + val updPool = new ErgoMemPool(pool.put(unconfirmedTransaction), stats, sortingOption) updPool -> new ProcessingOutcome.Accepted(unconfirmedTransaction, validationStartTime) } } @@ -253,7 +239,7 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, acceptIfNoDoubleSpend(unconfirmedTx, validationStartTime) } } else { - val msg = if (this.contains(tx.id)) { + val msg = if (contains(unconfirmedTx)) { s"Pool can not accept transaction ${tx.id}, it is already in the mempool" } else if (pool.size == settings.nodeSettings.mempoolCapacity) { s"Pool can not accept transaction ${tx.id}, the mempool is full" @@ -272,7 +258,7 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, } } - def weightedTransactionIds(limit: Int): Seq[WeightedTxId] = pool.orderedTransactions.keysIterator.take(limit).toSeq + def txTimesAndWeights: Seq[(Long,Long)] = getAll.map(uTx => uTx.createdTime -> uTx.weight(monetarySettings, sortingOption)) private def extractFee(tx: ErgoTransaction): Long = { tx.outputs @@ -309,13 +295,10 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, * @return average time for this transaction to be placed in block */ def getExpectedWaitTime(txFee : Long, txSize : Int): Long = { - // Create dummy transaction entry val feePerKb = txFee * 1024 / txSize - val dummyModifierId = bytesToId(Array.fill(32)(0.toByte)) - val wtx = WeightedTxId(dummyModifierId, feePerKb, feePerKb, 0) // Find position of entry in mempool - val posInPool = pool.orderedTransactions.keySet.until(wtx).size + val posInPool = pool.orderedTransactions.dropWhile(_._2.weight(monetarySettings, sortingOption) > feePerKb).size // Time since statistics measurement interval (needed to calculate average tx rate) val elapsed = System.currentTimeMillis() - stats.startMeasurement @@ -445,7 +428,7 @@ object ErgoMemPool extends ScorexLogging { case SortingOption.FeePerCycle => log.info("Sorting mempool by fee-per-cycle") } new ErgoMemPool( - OrderedTxPool.empty(settings), + OrderedTxPool.empty(settings, sortingOption), MemPoolStatistics(System.currentTimeMillis(), 0, System.currentTimeMillis()), sortingOption )(settings) diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolReader.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolReader.scala index 884e7249e2..e0da84199e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolReader.scala @@ -1,8 +1,7 @@ package org.ergoplatform.nodeView.mempool import org.ergoplatform.ErgoBox.BoxId -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.OrderedTxPool.WeightedTxId +import org.ergoplatform.modifiers.mempool.ErgoTransaction import scorex.core.NodeViewComponent import scorex.core.consensus.ContainsModifiers import scorex.util.ModifierId @@ -45,13 +44,12 @@ trait ErgoMemPoolReader extends NodeViewComponent with ContainsModifiers[ErgoTra def modifierById(modifierId: ModifierId): Option[ErgoTransaction] /** - * Returns transaction ids with weights. Weight depends on a fee a transaction is paying. - * Resulting transactions are sorted by weight in descending order. + * Returns an array of tuples representing transaction creation times with weights. Weight depends on a fee a transaction is paying. + * Resulting array is are sorted by weight in descending order. * - * @param limit - number of weighted transactions to return * @return an ordered sequence of transaction ids with weights */ - def weightedTransactionIds(limit: Int): Seq[WeightedTxId] + def txTimesAndWeights: Seq[(Long,Long)] /** * Get expected wait time for the transaction with specified fee and size diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/HistogramStats.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/HistogramStats.scala index bb25b0dbb1..6ef0815032 100644 --- a/src/main/scala/org/ergoplatform/nodeView/mempool/HistogramStats.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/HistogramStats.scala @@ -1,16 +1,14 @@ package org.ergoplatform.nodeView.mempool -import org.ergoplatform.nodeView.mempool.OrderedTxPool.WeightedTxId - object HistogramStats { - def getFeeHistogram(currTime: Long, nBins : Int, maxWaitTimeMsec: Long, wtxs : Seq[WeightedTxId]): Array[FeeHistogramBin] = { + def getFeeHistogram(currTime: Long, nBins : Int, maxWaitTimeMsec: Long, wtxs : Seq[(Long,Long)]): Array[FeeHistogramBin] = { val histogram = Array.fill(nBins + 1)(FeeHistogramBin(0,0)) val interval = maxWaitTimeMsec / nBins for (wtx <- wtxs) { - val waitTime = currTime - wtx.created + val waitTime = currTime - wtx._1 val bin = if (waitTime < maxWaitTimeMsec) (waitTime/interval).toInt else nBins - histogram.update(bin, FeeHistogramBin(histogram(bin).nTxns + 1, histogram(bin).totalFee + wtx.feePerFactor)) + histogram.update(bin, FeeHistogramBin(histogram(bin).nTxns + 1, histogram(bin).totalFee + wtx._2)) } histogram } diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/MemPoolStatistics.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/MemPoolStatistics.scala index c88f8415d2..37aa1dd6e2 100644 --- a/src/main/scala/org/ergoplatform/nodeView/mempool/MemPoolStatistics.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/MemPoolStatistics.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.mempool -import org.ergoplatform.nodeView.mempool.OrderedTxPool.WeightedTxId +import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption /** @@ -24,7 +24,7 @@ case class MemPoolStatistics(startMeasurement: Long, * prune statistic. To avoid siutation when we do not have statistic at all, we actually keep data up to * 2*measurementIntervalMsec and periodically cut half of range. */ - def add(currTime: Long, wtx: WeightedTxId): MemPoolStatistics = { + def add(currTime: Long, utx: UnconfirmedTransaction)(implicit sortingOption: SortingOption): MemPoolStatistics = { val curTakenTx = takenTxns + 1 val (newTakenTx, newMeasurement, newSnapTxs, newSnapTime) = if (currTime - snapTime > MemPoolStatistics.measurementIntervalMsec) { @@ -36,10 +36,10 @@ case class MemPoolStatistics(startMeasurement: Long, } else { (curTakenTx, startMeasurement, snapTakenTxns, snapTime) } - val durationMinutes = ((currTime - wtx.created) / (60 * 1000)).toInt + val durationMinutes = ((currTime - utx.createdTime) / (60 * 1000)).toInt val newHist = if (durationMinutes < MemPoolStatistics.nHistogramBins) { - val (histx, hisfee) = (histogram(durationMinutes).nTxns + 1, histogram(durationMinutes).totalFee + wtx.feePerFactor) + val (histx, hisfee) = (histogram(durationMinutes).nTxns + 1, histogram(durationMinutes).totalFee + utx.feeFactor(sortingOption)) histogram.updated(durationMinutes, FeeHistogramBin(histx, hisfee)) } else { histogram diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/OrderedTxPool.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/OrderedTxPool.scala index 127edd8201..7918ce5631 100644 --- a/src/main/scala/org/ergoplatform/nodeView/mempool/OrderedTxPool.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/OrderedTxPool.scala @@ -1,8 +1,9 @@ package org.ergoplatform.nodeView.mempool import org.ergoplatform.ErgoBox.BoxId -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.OrderedTxPool.WeightedTxId +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption +import org.ergoplatform.nodeView.mempool.OrderedTxPool.WeightedKey import org.ergoplatform.settings.{Algos, ErgoSettings, MonetarySettings} import scorex.util.{ModifierId, ScorexLogging} @@ -11,20 +12,16 @@ import scala.collection.immutable.TreeMap /** * An immutable pool of transactions of limited size with priority management and blacklisting support. * - * @param orderedTransactions - collection containing transactions ordered by `tx.weight` - * @param transactionsRegistry - mapping `tx.id` -> `WeightedTxId(tx.id,tx.weight)` required for getting transaction by its `id` + * @param orderedTransactions - collection mapping `WeightedKey(id,weight)` -> `UnconfirmedTransaction`, ordered by weight * @param invalidatedTxIds - invalidated transaction ids in bloom filters - * @param outputs - mapping `box.id` -> `WeightedTxId(tx.id,tx.weight)` required for getting a transaction by its output box - * @param inputs - mapping `box.id` -> `WeightedTxId(tx.id,tx.weight)` required for getting a transaction by its input box id + * @param outputs - mapping `box.id` -> `ModifierId` required for getting a transaction by its output box + * @param inputs - mapping `box.id` -> `ModifierId` required for getting a transaction by its input box id */ -class OrderedTxPool(val orderedTransactions: TreeMap[WeightedTxId, UnconfirmedTransaction], - val transactionsRegistry: TreeMap[ModifierId, WeightedTxId], +class OrderedTxPool(val orderedTransactions: TreeMap[WeightedKey, UnconfirmedTransaction], val invalidatedTxIds: ApproximateCacheLike[String], - val outputs: TreeMap[BoxId, WeightedTxId], - val inputs: TreeMap[BoxId, WeightedTxId]) - (implicit settings: ErgoSettings) extends ScorexLogging { - - import OrderedTxPool.weighted + val outputs: TreeMap[BoxId, ModifierId], + val inputs: TreeMap[BoxId, ModifierId]) + (implicit settings: ErgoSettings, sortingOption: SortingOption) extends ScorexLogging { /** * When a transaction has a parent in the mempool, we update its weight, weight of parent's parents etc. @@ -43,14 +40,8 @@ class OrderedTxPool(val orderedTransactions: TreeMap[WeightedTxId, UnconfirmedTr def size: Int = orderedTransactions.size - def get(id: ModifierId): Option[UnconfirmedTransaction] = { - transactionsRegistry.get(id).flatMap { wtx => - orderedTransactions.get(wtx) match { - case s@Some(_) => s - case None => log.warn(s"Found $id in registry but not ordered transactions"); None - } - } - } + def get(id: ModifierId): Option[UnconfirmedTransaction] = + orderedTransactions.valuesIterator.find(_.id == id) /** @@ -62,33 +53,32 @@ class OrderedTxPool(val orderedTransactions: TreeMap[WeightedTxId, UnconfirmedTr * thrown from the pool. * * @param unconfirmedTx - transaction to add + * @param feeFactor - fee factor override, used in tests * @return - modified pool */ - def put(unconfirmedTx: UnconfirmedTransaction, feeFactor: Int): OrderedTxPool = { + def put(unconfirmedTx: UnconfirmedTransaction, feeFactor: Option[Int] = None): OrderedTxPool = { val tx = unconfirmedTx.transaction - val newPool = transactionsRegistry.get(tx.id) match { - case Some(wtx) => - new OrderedTxPool( - orderedTransactions.updated(wtx, unconfirmedTx), - transactionsRegistry, - invalidatedTxIds, - outputs, - inputs - ) - case None => - val wtx = weighted(tx, feeFactor) + feeFactor match { + case Some(factor) => unconfirmedTx._feeFactor = factor + case _ => + } + + val newPool = + if(contains(unconfirmedTx)) + this + else { + val key = WeightedKey(unconfirmedTx) new OrderedTxPool( - orderedTransactions.updated(wtx, unconfirmedTx), - transactionsRegistry.updated(wtx.id, wtx), + orderedTransactions.updated(key, unconfirmedTx), invalidatedTxIds, - outputs ++ tx.outputs.map(_.id -> wtx), - inputs ++ tx.inputs.map(_.boxId -> wtx) - ).updateFamily(tx, wtx.weight, System.currentTimeMillis(), 0) - } + outputs ++ tx.outputs.map(_.id -> tx.id), + inputs ++ tx.inputs.map(_.boxId -> tx.id) + ).updateFamily(tx, unconfirmedTx.weight, System.currentTimeMillis(), 0) + } + if (newPool.orderedTransactions.size > mempoolCapacity) { - val victim = newPool.orderedTransactions.last._2 - newPool.remove(victim) + newPool.remove(newPool.orderedTransactions.last._2) } else { newPool } @@ -104,11 +94,10 @@ class OrderedTxPool(val orderedTransactions: TreeMap[WeightedTxId, UnconfirmedTr * @param tx - Transaction to remove */ def remove(tx: ErgoTransaction): OrderedTxPool = { - transactionsRegistry.get(tx.id) match { + get(tx.id) match { case Some(wtx) => new OrderedTxPool( - orderedTransactions - wtx, - transactionsRegistry - tx.id, + orderedTransactions - WeightedKey(wtx), invalidatedTxIds, outputs -- tx.outputs.map(_.id), inputs -- tx.inputs.map(_.boxId) @@ -117,34 +106,35 @@ class OrderedTxPool(val orderedTransactions: TreeMap[WeightedTxId, UnconfirmedTr } } - def remove(utx: UnconfirmedTransaction): OrderedTxPool = remove(utx.transaction) + def remove(utx: UnconfirmedTransaction): OrderedTxPool = { + val key = WeightedKey(utx.id, utx.weight) + orderedTransactions.get(key) match { + case Some(wtx) => + new OrderedTxPool( + orderedTransactions - key, + invalidatedTxIds, + outputs -- utx.transaction.outputs.map(_.id), + inputs -- utx.transaction.inputs.map(_.boxId) + ).updateFamily(utx.transaction, -wtx.weight, System.currentTimeMillis(), depth = 0) + case None => this + } + } /** * Remove transaction from the pool and add it to invalidated transaction ids cache */ def invalidate(unconfirmedTx: UnconfirmedTransaction): OrderedTxPool = { val tx = unconfirmedTx.transaction - transactionsRegistry.get(tx.id) match { + orderedTransactions.get(WeightedKey(unconfirmedTx)) match { case Some(wtx) => new OrderedTxPool( - orderedTransactions - wtx, - transactionsRegistry - tx.id, + orderedTransactions - WeightedKey(wtx), invalidatedTxIds.put(tx.id), outputs -- tx.outputs.map(_.id), inputs -- tx.inputs.map(_.boxId) ).updateFamily(tx, -wtx.weight, System.currentTimeMillis(), depth = 0) case None => - if (orderedTransactions.valuesIterator.exists(utx => utx.id == tx.id)) { - new OrderedTxPool( - orderedTransactions.filter(_._2.id != tx.id), - transactionsRegistry - tx.id, - invalidatedTxIds.put(tx.id), - outputs -- tx.outputs.map(_.id), - inputs -- tx.inputs.map(_.boxId) - ) - } else { - new OrderedTxPool(orderedTransactions, transactionsRegistry, invalidatedTxIds.put(tx.id), outputs, inputs) - } + new OrderedTxPool(orderedTransactions, invalidatedTxIds.put(tx.id), outputs, inputs) } } @@ -156,17 +146,16 @@ class OrderedTxPool(val orderedTransactions: TreeMap[WeightedTxId, UnconfirmedTr * */ def canAccept(unconfirmedTx: UnconfirmedTransaction): Boolean = { - !contains(unconfirmedTx.id) && size <= mempoolCapacity + !contains(unconfirmedTx) && size <= mempoolCapacity } /** * - * @param id - transaction id + * @param uTx - unconfirmed transaction * @return - true, if transaction is in the pool or invalidated earlier, false otherwise */ - def contains(id: ModifierId): Boolean = { - transactionsRegistry.contains(id) - } + def contains(uTx: UnconfirmedTransaction): Boolean = + orderedTransactions.get(WeightedKey(uTx)).exists(_.lastCheckedTime == uTx.lastCheckedTime) def isInvalidated(id: ModifierId): Boolean = invalidatedTxIds.mightContain(id) @@ -193,18 +182,16 @@ class OrderedTxPool(val orderedTransactions: TreeMap[WeightedTxId, UnconfirmedTr this } else { - val uniqueTxIds: Set[WeightedTxId] = tx.inputs.flatMap(input => this.outputs.get(input.boxId)).toSet - val parentTxs = uniqueTxIds.flatMap(wtx => this.orderedTransactions.get(wtx).map(ut => wtx -> ut)) + val uniqueTxIds: Set[ModifierId] = tx.inputs.flatMap(input => outputs.get(input.boxId)).toSet + val parentTxs = uniqueTxIds.flatMap(get) - parentTxs.foldLeft(this) { case (pool, (wtx, ut)) => - val parent = ut.transaction - val newWtx = WeightedTxId(wtx.id, wtx.weight + weight, wtx.feePerFactor, wtx.created) + parentTxs.foldLeft(this) { case (pool, uTx) => + val parent = uTx.transaction val newPool = new OrderedTxPool( - pool.orderedTransactions - wtx + (newWtx -> ut), - pool.transactionsRegistry.updated(parent.id, newWtx), + (pool.orderedTransactions - WeightedKey(uTx)) + Tuple2(WeightedKey(uTx.id, uTx.weight + weight), uTx.addWeight(weight)), invalidatedTxIds, - parent.outputs.foldLeft(pool.outputs)((newOutputs, box) => newOutputs.updated(box.id, newWtx)), - parent.inputs.foldLeft(pool.inputs)((newInputs, inp) => newInputs.updated(inp.boxId, newWtx)) + parent.outputs.foldLeft(pool.outputs)((newOutputs, box) => newOutputs.updated(box.id, parent.id)), + parent.inputs.foldLeft(pool.inputs)((newInputs, inp) => newInputs.updated(inp.boxId, parent.id)) ) newPool.updateFamily(parent, weight, startTime, depth + 1) } @@ -214,60 +201,26 @@ class OrderedTxPool(val orderedTransactions: TreeMap[WeightedTxId, UnconfirmedTr object OrderedTxPool { - /** - * Weighted transaction id - * - * @param id - Transaction id - * @param weight - Weight of transaction - * @param feePerFactor - Transaction's fee per factor (byte or execution cost) - * @param created - Transaction creation time - */ - case class WeightedTxId(id: ModifierId, weight: Long, feePerFactor: Long, created: Long) { - // `id` depends on `weight` so we can use only the former for comparison. - override def equals(obj: Any): Boolean = obj match { - case that: WeightedTxId => that.id == id - case _ => false - } + case class WeightedKey(_1: ModifierId, _2: Long) - override def hashCode(): Int = id.hashCode() + case object WeightedKey { + def apply(utx: UnconfirmedTransaction)(implicit ms: MonetarySettings, sortingOption: SortingOption): WeightedKey = + WeightedKey(utx.id, utx.weight) } - private implicit val ordWeight: Ordering[WeightedTxId] = Ordering[(Long, ModifierId)].on(x => (-x.weight, x.id)) private implicit val ordBoxId: Ordering[BoxId] = Ordering[String].on(b => Algos.encode(b)) - def empty(settings: ErgoSettings): OrderedTxPool = { + def empty(settings: ErgoSettings, sortingOption: SortingOption): OrderedTxPool = { val cacheSettings = settings.cacheSettings.mempool val frontCacheSize = cacheSettings.invalidModifiersCacheSize val frontCacheExpiration = cacheSettings.invalidModifiersCacheExpiration + implicit val ordWeight: Ordering[WeightedKey] = + Ordering[(Long, ModifierId)].on(x => (-x._2, x._1)) new OrderedTxPool( - TreeMap.empty[WeightedTxId, UnconfirmedTransaction], - TreeMap.empty[ModifierId, WeightedTxId], + TreeMap.empty[WeightedKey, UnconfirmedTransaction], ExpiringApproximateCache.empty(frontCacheSize, frontCacheExpiration), - TreeMap.empty[BoxId, WeightedTxId], - TreeMap.empty[BoxId, WeightedTxId])(settings) - } - - def weighted(unconfirmedTx: UnconfirmedTransaction, feeFactor: Int)(implicit ms: MonetarySettings): WeightedTxId = { - weighted(unconfirmedTx.transaction, feeFactor) - } - - /** - * Wrap transaction into an entity which is storing its mempool sorting weight also - * - * @param tx - transaction - * @param feeFactor - fee-related factor of the transaction `tx`, so size or cost - * @param ms - monetary settings to extract fee proposition from - * @return - transaction and its weight wrapped in `WeightedTxId` - */ - def weighted(tx: ErgoTransaction, feeFactor: Int)(implicit ms: MonetarySettings): WeightedTxId = { - val fee = tx.outputs - .filter(b => java.util.Arrays.equals(b.propositionBytes, ms.feePropositionBytes)) - .map(_.value) - .sum - - // We multiply by 1024 for better precision - val feePerFactor = fee * 1024 / feeFactor - // Weight is equal to feePerFactor here, however, it can be modified later when children transactions will arrive - WeightedTxId(tx.id, feePerFactor, feePerFactor, System.currentTimeMillis()) + TreeMap.empty[BoxId, ModifierId], + TreeMap.empty[BoxId, ModifierId] + )(settings, sortingOption) } } diff --git a/src/main/scala/org/ergoplatform/modifiers/mempool/UnconfirmedTransaction.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/UnconfirmedTransaction.scala similarity index 59% rename from src/main/scala/org/ergoplatform/modifiers/mempool/UnconfirmedTransaction.scala rename to src/main/scala/org/ergoplatform/nodeView/mempool/UnconfirmedTransaction.scala index bd18f338d8..0c080f1b02 100644 --- a/src/main/scala/org/ergoplatform/modifiers/mempool/UnconfirmedTransaction.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/UnconfirmedTransaction.scala @@ -1,5 +1,8 @@ -package org.ergoplatform.modifiers.mempool +package org.ergoplatform.nodeView.mempool +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption +import org.ergoplatform.settings.MonetarySettings import scorex.core.network.ConnectedPeer import scorex.util.{ModifierId, ScorexLogging} @@ -23,6 +26,45 @@ class UnconfirmedTransaction(val transaction: ErgoTransaction, def id: ModifierId = transaction.id + /** + * When there's no reason to re-check transactions immediately, we assign fake cost to them + */ + private val FakeCost = 1000 + + private[mempool] var _feeFactor: Int = -1 + def feeFactor(implicit sortingOption: SortingOption): Int = { + if(_feeFactor == -1) { + sortingOption match { + case SortingOption.FeePerByte => + _feeFactor = transactionBytes.map(_.length).getOrElse(transaction.size) + case SortingOption.FeePerCycle => + _feeFactor = lastCost.getOrElse(FakeCost) + } + } + _feeFactor + } + + private def feePerFactor(implicit ms: MonetarySettings, sortingOption: SortingOption): Long = { + val fee = transaction.outputs + .filter(b => java.util.Arrays.equals(b.propositionBytes, ms.feePropositionBytes)) + .map(_.value) + .sum + // We multiply by 1024 for better precision + fee * 1024 / feeFactor + } + + private var _weight: Long = -1 + def weight(implicit ms: MonetarySettings, sortingOption: SortingOption): Long = { + if(_weight == -1) + _weight = feePerFactor + _weight + } + + def addWeight(weight: Long): UnconfirmedTransaction = { + _weight += weight + this + } + /** * Updates cost and last checked time of unconfirmed transaction */ diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index b856643302..349cbf6132 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -3,8 +3,8 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoBox import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.modifiers.ErgoFullBlock -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.{ErgoMemPoolReader, UnconfirmedTransaction} import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer diff --git a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala index c13bd6cb0c..82364e2340 100644 --- a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala @@ -9,8 +9,9 @@ import io.circe.Json import io.circe.syntax._ import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} import org.ergoplatform.http.api.{ApiCodecs, TransactionsApiRoute} -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.ErgoReadersHolder.{GetDataFromHistory, GetReaders, Readers} +import org.ergoplatform.nodeView.mempool.UnconfirmedTransaction import org.ergoplatform.settings.Constants import org.ergoplatform.utils.Stubs import org.ergoplatform.{DataInput, ErgoBox, ErgoBoxCandidate, Input} @@ -127,7 +128,7 @@ class TransactionApiRouteSpec extends AnyFlatSpec it should "get unconfirmed txs from mempool" in { Get(prefix + "/unconfirmed") ~> route ~> check { status shouldBe StatusCodes.OK - memPool.take(50).map(_.transaction).toSeq shouldBe responseAs[Seq[ErgoTransaction]] + memPool.take(50).toSeq.map(_.transaction) shouldBe responseAs[Seq[ErgoTransaction]] } } diff --git a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala index 5096dec09d..db7ada3bb9 100644 --- a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala +++ b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala @@ -3,10 +3,10 @@ package org.ergoplatform.local import akka.actor.{ActorRef, ActorSystem} import akka.testkit.{TestActorRef, TestProbe} import org.ergoplatform.ErgoAddressEncoder -import org.ergoplatform.modifiers.mempool.UnconfirmedTransaction import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{FailedTransaction, RecheckMempool, SuccessfulTransaction} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{LocallyGeneratedTransaction, RecheckedTransactions} import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome +import org.ergoplatform.nodeView.mempool.UnconfirmedTransaction import org.ergoplatform.nodeView.state.ErgoState import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} diff --git a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala index 16e83f5d00..aefdb61cad 100644 --- a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala @@ -9,11 +9,12 @@ import org.ergoplatform.mining.CandidateGenerator.{Candidate, GenerateCandidate} import org.ergoplatform.mining.ErgoMiner.StartMining import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction, UnsignedErgoTransaction} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistoryReader +import org.ergoplatform.nodeView.mempool.UnconfirmedTransaction import org.ergoplatform.nodeView.state._ import org.ergoplatform.nodeView.wallet._ import org.ergoplatform.nodeView.{ErgoNodeViewRef, ErgoReadersHolderRef} diff --git a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala index c867070220..01a2c41e56 100644 --- a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala +++ b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala @@ -4,11 +4,11 @@ import akka.actor.{ActorRef, ActorSystem} import akka.testkit.TestProbe import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetNodeViewChanges, ModifiersFromRemote} import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} -import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.nodeView.mempool.{ErgoMemPool, UnconfirmedTransaction} import org.ergoplatform.nodeView.state.UtxoState.ManifestId import org.ergoplatform.nodeView.state._ import org.ergoplatform.settings.Algos diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala index d08408e7cf..51d26fdf10 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala @@ -2,10 +2,10 @@ package org.ergoplatform.nodeView.mempool import org.ergoplatform.{ErgoBoxCandidate, Input} import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.{ErgoSettings, MonetarySettings} import org.ergoplatform.utils.ErgoTestHelpers import org.ergoplatform.utils.generators.ErgoGenerators import org.scalatest.flatspec.AnyFlatSpec @@ -41,7 +41,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "respect given sorting order" in { - implicit val ms = settings.chainSettings.monetary + implicit val ms: MonetarySettings = settings.chainSettings.monetary val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get @@ -52,6 +52,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec IndexedSeq(feeOut) ) + implicit val fPb: SortingOption = SortingOption.FeePerByte + implicit val fPc: SortingOption = SortingOption.FeePerCycle + // Randomly initialized settings.nodeSettings.mempoolSorting should (be (SortingOption.FeePerByte) or be (SortingOption.FeePerCycle)) @@ -61,9 +64,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec )) var poolSize = ErgoMemPool.empty(sortBySizeSettings) - poolSize = poolSize.process(UnconfirmedTransaction(tx, None), wus)._1 - val size = tx.size - poolSize.pool.orderedTransactions.firstKey.weight shouldBe OrderedTxPool.weighted(tx, size).weight + val uTx1 = UnconfirmedTransaction(tx, None) + poolSize = poolSize.process(uTx1, wus)._1 + poolSize.pool.orderedTransactions.firstKey._2 shouldBe uTx1.weight(ms,fPb) val sortByCostSettings: ErgoSettings = settings.copy( nodeSettings = settings.nodeSettings.copy( @@ -71,22 +74,22 @@ class ErgoMemPoolSpec extends AnyFlatSpec )) var poolCost = ErgoMemPool.empty(sortByCostSettings) - poolCost = poolCost.process(UnconfirmedTransaction(tx, None), wus)._1 - val cost = wus.validateWithCost(tx, Int.MaxValue).get - poolCost.pool.orderedTransactions.firstKey.weight shouldBe OrderedTxPool.weighted(tx, cost).weight + val uTx2 = UnconfirmedTransaction(tx, None) + poolCost = poolCost.process(uTx2, wus)._1 + poolCost.pool.orderedTransactions.firstKey._2 shouldBe uTx2.withCost(wus.validateWithCost(tx, Int.MaxValue).get).weight(ms,fPc) } it should "decline already contained transaction" in { val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get - val txs = validTransactionsFromUtxoState(wus) + val txs = validTransactionsFromUtxoState(wus).map(UnconfirmedTransaction(_, None)) var pool = ErgoMemPool.empty(settings) txs.foreach { tx => - pool = pool.put(UnconfirmedTransaction(tx, None)) + pool = pool.put(tx) } txs.foreach { tx => - pool.process(UnconfirmedTransaction(tx, None), us)._2.isInstanceOf[ProcessingOutcome.Declined] shouldBe true + pool.process(tx, us)._2.isInstanceOf[ProcessingOutcome.Declined] shouldBe true } } @@ -294,7 +297,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec } pool.size shouldBe (family_depth + 1) * txs.size allTxs.foreach { tx => - pool = pool.remove(tx.transaction) + pool = pool.remove(tx) } pool.size shouldBe 0 } @@ -329,8 +332,8 @@ class ErgoMemPoolSpec extends AnyFlatSpec }) } - val weights = pool.weightedTransactionIds(11) - val ids = weights.map(_.id) + val weights = pool.pool.orderedTransactions.keysIterator.take(11).toSeq + val ids = weights.map(_._1) pool.take(11).toSeq.map(_.transaction.id) shouldBe ids pool.getAll.map(_.transaction.id) shouldBe ids @@ -373,7 +376,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec pool.stats.snapTakenTxns shouldBe MemPoolStatistics(System.currentTimeMillis(),0,System.currentTimeMillis()).snapTakenTxns allTxs.foreach { tx => - pool = pool.remove(tx.transaction) + pool = pool.remove(tx) } pool.size shouldBe 0 pool.stats.takenTxns shouldBe (family_depth + 1) * txs.size @@ -387,11 +390,46 @@ class ErgoMemPoolSpec extends AnyFlatSpec val utx1 = new UnconfirmedTransaction(tx, None, now, now, None, None) val utx2 = new UnconfirmedTransaction(tx, None, now, now, None, None) val utx3 = new UnconfirmedTransaction(tx, None, now + 1, now + 1, None, None) - val updPool = pool.put(utx1, 100).remove(utx1).put(utx2, 500).put(utx3, 5000) + val updPool = pool.put(utx1, Some(100)).remove(utx1).put(utx2, Some(500)).put(utx3, Some(5000)) updPool.size shouldBe 1 updPool.get(utx3.id).get.lastCheckedTime shouldBe (now + 1) } + it should "accept double-spending transaction if it is paying more than one already sitting in the pool" in { + val (us, bh) = createUtxoState(settings) + val genesis = validFullBlock(None, us, bh) + val wus = WrappedUtxoState(us, bh, settings, extendedParameters).applyModifier(genesis)(_ => ()).get + + val input = wus.takeBoxes(100).collectFirst { + case box if box.ergoTree == TrueLeaf.toSigmaProp.treeWithSegregation => box + }.get + + val txCount = 5 + val txs: Array[UnconfirmedTransaction] = Array.ofDim(txCount) + + for(i <- 0 until txCount) { + val out = new ErgoBoxCandidate(input.value, settings.chainSettings.monetary.feeProposition, creationHeight = 0) + val txLike = ErgoTransaction( + IndexedSeq(new Input(input.id, new ProverResult(Array.emptyByteArray, + ContextExtension(Map((1: Byte) -> ByteArrayConstant(Array.fill(1 + txCount - i)(0: Byte)))))) + ), IndexedSeq(out) + ) + txs(i) = UnconfirmedTransaction(ErgoTransaction(txLike.inputs, txLike.outputCandidates), None) + } + + val pool = ErgoMemPool.empty(settings) + + val endPool = txs.foldLeft(pool) { case (p, tx) => + val (newP, txoutcome) = p.process(tx, us) + txoutcome.isInstanceOf[ProcessingOutcome.Accepted] shouldBe true + newP + } + + endPool.pool.orderedTransactions.size shouldBe 1 + endPool.pool.inputs.contains(input.id) shouldBe true + endPool.pool.outputs.size shouldBe 1 + endPool.pool.outputs.contains(txs.last.transaction.outputs(0).id) shouldBe true + } } diff --git a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala index 83e9243ad9..9b8afdef80 100644 --- a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala @@ -3,7 +3,6 @@ package org.ergoplatform.nodeView.viewholder import java.io.File import org.ergoplatform.ErgoBoxCandidate import org.ergoplatform.modifiers.ErgoFullBlock -import org.ergoplatform.modifiers.mempool.UnconfirmedTransaction import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.state.StateType.Utxo import org.ergoplatform.nodeView.state._ @@ -15,6 +14,7 @@ import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ import org.ergoplatform.nodeView.ErgoNodeViewHolder import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.ChainProgress import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome.Accepted +import org.ergoplatform.nodeView.mempool.UnconfirmedTransaction import scorex.crypto.authds.{ADKey, SerializedAdProof} import scorex.testkit.utils.NoShrink import scorex.util.{ModifierId, bytesToId} diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala index c492591ac0..de99c71eed 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala @@ -3,8 +3,8 @@ package org.ergoplatform.nodeView.wallet import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, R1} import org.ergoplatform._ import org.ergoplatform.db.DBSpec -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.{ErgoMemPoolReader, UnconfirmedTransaction} import org.ergoplatform.nodeView.wallet.WalletScanLogic.ScanResults import org.ergoplatform.nodeView.wallet.persistence.{OffChainRegistry, WalletRegistry, WalletStorage} import org.ergoplatform.nodeView.wallet.requests.{AssetIssueRequest, PaymentRequest} diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala index d1a7518e16..b58eb4f266 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala @@ -1,15 +1,15 @@ package org.ergoplatform.sanity import akka.actor.ActorRef -import org.ergoplatform.ErgoBox +import org.ergoplatform.{ErgoBox, nodeView} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.BlockTransactions -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.network.{ErgoNodeViewSynchronizer, ErgoSyncTracker} import org.ergoplatform.nodeView.NodeViewSynchronizerTests import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} -import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.nodeView.mempool.{ErgoMemPool, UnconfirmedTransaction} import org.ergoplatform.nodeView.state.{DigestState, ErgoState, UtxoState} import org.ergoplatform.sanity.ErgoSanity._ import org.ergoplatform.settings.ErgoSettings @@ -43,7 +43,7 @@ trait ErgoSanity[ST <: ErgoState[ST]] extends HistoryTests //Generators override lazy val transactionGenerator: Gen[ErgoTransaction] = invalidErgoTransactionGen override lazy val unconfirmedTxGenerator: Gen[UnconfirmedTransaction] = - invalidErgoTransactionGen.map(tx => UnconfirmedTransaction(tx, None)) + invalidErgoTransactionGen.map(tx => nodeView.mempool.UnconfirmedTransaction(tx, None)) override lazy val memPoolGenerator: Gen[MPool] = emptyMemPoolGen override def syntacticallyValidModifier(history: HT): Header = { diff --git a/src/test/scala/org/ergoplatform/utils/MempoolTestHelpers.scala b/src/test/scala/org/ergoplatform/utils/MempoolTestHelpers.scala index 2473790652..8bdd94e31f 100644 --- a/src/test/scala/org/ergoplatform/utils/MempoolTestHelpers.scala +++ b/src/test/scala/org/ergoplatform/utils/MempoolTestHelpers.scala @@ -1,8 +1,8 @@ package org.ergoplatform.utils import org.ergoplatform.ErgoBox.BoxId -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.{ErgoMemPoolReader, OrderedTxPool} +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.{ErgoMemPoolReader, UnconfirmedTransaction} import scorex.util.ModifierId trait MempoolTestHelpers { @@ -16,7 +16,7 @@ trait MempoolTestHelpers { override def size: Int = ??? - override def weightedTransactionIds(limit: Int): Seq[OrderedTxPool.WeightedTxId] = ??? + override def txTimesAndWeights: Seq[(Long,Long)] = ??? override def getAll: Seq[UnconfirmedTransaction] = ??? diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index 17735eec72..90e53d065a 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -8,11 +8,11 @@ import org.ergoplatform.mining.CandidateGenerator.Candidate import org.ergoplatform.mining.{AutolykosSolution, CandidateGenerator, ErgoMiner, WorkMessage} import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction import org.ergoplatform.nodeView.ErgoReadersHolder.{GetDataFromHistory, GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.nodeView.mempool.{ErgoMemPool, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.ErgoMemPool.{ProcessingOutcome, SortingOption} import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.nodeView.state.{DigestState, ErgoStateContext, StateType} diff --git a/src/test/scala/scorex/testkit/properties/mempool/MemoryPoolTest.scala b/src/test/scala/scorex/testkit/properties/mempool/MemoryPoolTest.scala index 3ee1108614..0405b291b7 100644 --- a/src/test/scala/scorex/testkit/properties/mempool/MemoryPoolTest.scala +++ b/src/test/scala/scorex/testkit/properties/mempool/MemoryPoolTest.scala @@ -1,7 +1,7 @@ package scorex.testkit.properties.mempool -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.{ErgoMemPool, UnconfirmedTransaction} import org.scalacheck.Gen diff --git a/src/test/scala/scorex/testkit/properties/mempool/MempoolFilterPerformanceTest.scala b/src/test/scala/scorex/testkit/properties/mempool/MempoolFilterPerformanceTest.scala index 0d482a790a..81f9b86e34 100644 --- a/src/test/scala/scorex/testkit/properties/mempool/MempoolFilterPerformanceTest.scala +++ b/src/test/scala/scorex/testkit/properties/mempool/MempoolFilterPerformanceTest.scala @@ -1,8 +1,8 @@ package scorex.testkit.properties.mempool import java.security.MessageDigest -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.{ErgoMemPool, UnconfirmedTransaction} import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks diff --git a/src/test/scala/scorex/testkit/properties/mempool/MempoolRemovalTest.scala b/src/test/scala/scorex/testkit/properties/mempool/MempoolRemovalTest.scala index 5506e6ac35..534d2d4181 100644 --- a/src/test/scala/scorex/testkit/properties/mempool/MempoolRemovalTest.scala +++ b/src/test/scala/scorex/testkit/properties/mempool/MempoolRemovalTest.scala @@ -1,8 +1,8 @@ package scorex.testkit.properties.mempool -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.nodeView.mempool.{ErgoMemPool, UnconfirmedTransaction} import org.scalacheck.Gen import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec diff --git a/src/test/scala/scorex/testkit/properties/mempool/MempoolTransactionsTest.scala b/src/test/scala/scorex/testkit/properties/mempool/MempoolTransactionsTest.scala index 891c854c7e..286dbba8e9 100644 --- a/src/test/scala/scorex/testkit/properties/mempool/MempoolTransactionsTest.scala +++ b/src/test/scala/scorex/testkit/properties/mempool/MempoolTransactionsTest.scala @@ -1,7 +1,8 @@ package scorex.testkit.properties.mempool -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool +import org.ergoplatform.nodeView.mempool.{ErgoMemPool, UnconfirmedTransaction} import org.scalacheck.Gen import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec @@ -16,7 +17,7 @@ trait MempoolTransactionsTest val transactionSeqGenerator: Gen[Seq[ErgoTransaction]] = Gen.nonEmptyContainerOf[Seq, ErgoTransaction](transactionGenerator) val unconfirmedTxSeqGenerator: Gen[Seq[UnconfirmedTransaction]] = - transactionSeqGenerator.map(txs => txs.map(tx => UnconfirmedTransaction(tx, None))) + transactionSeqGenerator.map(txs => txs.map(tx => mempool.UnconfirmedTransaction(tx, None))) property("Size of mempool should increase when adding a non-present transaction") { forAll(memPoolGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTx: UnconfirmedTransaction) => @@ -84,7 +85,7 @@ trait MempoolTransactionsTest property("Size of mempool should decrease when removing a present transaction") { forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => val m: ErgoMemPool = mp.put(unconfirmedTxs) - val m2: ErgoMemPool = m.remove(unconfirmedTxs.headOption.get.transaction) + val m2: ErgoMemPool = m.remove(unconfirmedTxs.headOption.get) m2.size shouldBe unconfirmedTxs.size - 1 } } @@ -92,7 +93,7 @@ trait MempoolTransactionsTest property("Size of mempool should not decrease when removing a non-present transaction") { forAll(memPoolGenerator, unconfirmedTxSeqGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction], unconfirmedTx: UnconfirmedTransaction) => val m: ErgoMemPool = mp.put(unconfirmedTxs) - val m2: ErgoMemPool = m.remove(unconfirmedTx.transaction) + val m2: ErgoMemPool = m.remove(unconfirmedTx) m2.size shouldBe unconfirmedTxs.size } }