From 9eea9e6dd9f107d1131ed75be98d79c08d13e2c4 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 16 Aug 2022 18:50:59 +0300 Subject: [PATCH 01/28] Remove unused method --- .../scala/com/wavesplatform/state/InvokeScriptResult.scala | 6 ------ 1 file changed, 6 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/state/InvokeScriptResult.scala b/node/src/main/scala/com/wavesplatform/state/InvokeScriptResult.scala index 6e173d7eb62..aff3612210d 100644 --- a/node/src/main/scala/com/wavesplatform/state/InvokeScriptResult.scala +++ b/node/src/main/scala/com/wavesplatform/state/InvokeScriptResult.scala @@ -85,12 +85,6 @@ object InvokeScriptResult { implicit val jsonWrites = Json.writes[Lease] } - def paymentsFromPortfolio(addr: Address, portfolio: Portfolio): Seq[Payment] = { - val waves = InvokeScriptResult.Payment(addr, Waves, portfolio.balance) - val assets = portfolio.assets.map { case (assetId, amount) => InvokeScriptResult.Payment(addr, assetId, amount) } - (assets.toVector ++ Some(waves)).filter(_.amount != 0) - } - implicit val issueFormat = Writes[Issue] { iss => Json.obj( "assetId" -> iss.id, From fee694f4e460d67125f9fefa32fe249601c20838 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 17 Aug 2022 10:43:15 +0300 Subject: [PATCH 02/28] Take into account discarded diffs for CommonAccountApi --- .../scala/com/wavesplatform/Application.scala | 44 +++++---- .../api/common/CommonAccountsApi.scala | 2 +- .../state/reader/CompositeBlockchain.scala | 10 +- .../api/common/CommonAccountApiSpec.scala | 99 +++++++++++-------- .../com/wavesplatform/history/Domain.scala | 3 +- 5 files changed, 93 insertions(+), 65 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/Application.scala b/node/src/main/scala/com/wavesplatform/Application.scala index e7b8451449b..b2737b62fe0 100644 --- a/node/src/main/scala/com/wavesplatform/Application.scala +++ b/node/src/main/scala/com/wavesplatform/Application.scala @@ -1,22 +1,17 @@ package com.wavesplatform -import java.io.File -import java.security.Security -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.atomic.AtomicBoolean - import akka.actor.ActorSystem import akka.http.scaladsl.Http import akka.http.scaladsl.Http.ServerBinding -import cats.instances.bigInt._ -import cats.instances.int._ -import cats.syntax.option._ -import com.typesafe.config._ +import cats.instances.bigInt.* +import cats.instances.int.* +import cats.syntax.option.* +import com.typesafe.config.* import com.wavesplatform.account.{Address, AddressScheme} import com.wavesplatform.actor.RootActorSystem import com.wavesplatform.api.BlockMeta -import com.wavesplatform.api.common._ -import com.wavesplatform.api.http._ +import com.wavesplatform.api.common.* +import com.wavesplatform.api.http.* import com.wavesplatform.api.http.alias.AliasApiRoute import com.wavesplatform.api.http.assets.AssetsApiRoute import com.wavesplatform.api.http.eth.EthRpcRoute @@ -27,21 +22,22 @@ import com.wavesplatform.database.{DBExt, Keys, openDB} import com.wavesplatform.events.{BlockchainUpdateTriggers, UtxEvent} import com.wavesplatform.extensions.{Context, Extension} import com.wavesplatform.features.BlockchainFeatures -import com.wavesplatform.features.EstimatorProvider._ +import com.wavesplatform.features.EstimatorProvider.* import com.wavesplatform.features.api.ActivationApiRoute import com.wavesplatform.history.{History, StorageFactory} import com.wavesplatform.lang.ValidationError import com.wavesplatform.metrics.Metrics import com.wavesplatform.mining.{Miner, MinerDebugInfo, MinerImpl} -import com.wavesplatform.network._ +import com.wavesplatform.network.* import com.wavesplatform.settings.WavesSettings import com.wavesplatform.state.appender.{BlockAppender, ExtensionAppender, MicroblockAppender} +import com.wavesplatform.state.reader.CompositeBlockchain import com.wavesplatform.state.{Blockchain, BlockchainUpdaterImpl, Diff, Height, TxMeta} import com.wavesplatform.transaction.TxValidationError.GenericError import com.wavesplatform.transaction.smart.script.trace.TracedResult import com.wavesplatform.transaction.{Asset, DiscardedBlocks, Transaction} -import com.wavesplatform.utils.Schedulers._ -import com.wavesplatform.utils._ +import com.wavesplatform.utils.* +import com.wavesplatform.utils.Schedulers.* import com.wavesplatform.utx.{UtxPool, UtxPoolImpl} import com.wavesplatform.wallet.Wallet import io.netty.channel.Channel @@ -58,16 +54,20 @@ import org.influxdb.dto.Point import org.iq80.leveldb.DB import org.slf4j.LoggerFactory +import java.io.File +import java.security.Security +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicBoolean import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration._ +import scala.concurrent.duration.* import scala.concurrent.{Await, Future} import scala.util.{Failure, Success, Try} class Application(val actorSystem: ActorSystem, val settings: WavesSettings, configRoot: ConfigObject, time: NTP) extends ScorexLogging { app => - import Application._ - import monix.execution.Scheduler.Implicits.{global => scheduler} + import Application.* + import monix.execution.Scheduler.Implicits.global as scheduler private[this] val db = openDB(settings.dbSettings.directory) @@ -127,6 +127,8 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con new UtxPoolImpl(time, blockchainUpdater, settings.utxSettings, settings.minerSettings.enable, utxEvents.onNext) maybeUtx = Some(utxStorage) + def blockchainWithDiscardedDiffs = CompositeBlockchain(blockchainUpdater, utxStorage.priorityPool.validPriorityDiffs) + val timer = new HashedWheelTimer() val utxSynchronizerLogger = LoggerFacade(LoggerFactory.getLogger(classOf[TransactionPublisher])) val timedTxValidator = @@ -224,7 +226,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con override val blocksApi: CommonBlocksApi = CommonBlocksApi(blockchainUpdater, loadBlockMetaAt(db, blockchainUpdater), loadBlockInfoAt(db, blockchainUpdater)) override val accountsApi: CommonAccountsApi = - CommonAccountsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainUpdater) + CommonAccountsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainWithDiscardedDiffs) override val assetsApi: CommonAssetsApi = CommonAssetsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainUpdater) } @@ -477,7 +479,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con object Application extends ScorexLogging { private[wavesplatform] def loadApplicationConfig(external: Option[File] = None): WavesSettings = { - import com.wavesplatform.settings._ + import com.wavesplatform.settings.* val maybeExternalConfig = Try(external.map(f => ConfigFactory.parseFile(f.getAbsoluteFile, ConfigParseOptions.defaults().setAllowMissing(false)))) val config = loadConfig(maybeExternalConfig.getOrElse(None)) @@ -583,8 +585,8 @@ object Application extends ScorexLogging { Metrics.start(settings.metrics, time) def dumpMinerConfig(): Unit = { + import settings.minerSettings as miner import settings.synchronizationSettings.microBlockSynchronizer - import settings.{minerSettings => miner} Metrics.write( Point diff --git a/node/src/main/scala/com/wavesplatform/api/common/CommonAccountsApi.scala b/node/src/main/scala/com/wavesplatform/api/common/CommonAccountsApi.scala index 890de4f2878..15b4a077e82 100644 --- a/node/src/main/scala/com/wavesplatform/api/common/CommonAccountsApi.scala +++ b/node/src/main/scala/com/wavesplatform/api/common/CommonAccountsApi.scala @@ -55,7 +55,7 @@ object CommonAccountsApi { final case class BalanceDetails(regular: Long, generating: Long, available: Long, effective: Long, leaseIn: Long, leaseOut: Long) - def apply(diff: () => Diff, db: DB, blockchain: Blockchain): CommonAccountsApi = new CommonAccountsApi { + def apply(diff: () => Diff, db: DB, blockchain: => Blockchain): CommonAccountsApi = new CommonAccountsApi { override def balance(address: Address, confirmations: Int = 0): Long = { blockchain.balance(address, blockchain.height, confirmations) diff --git a/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala b/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala index d2705775779..d075cc84d10 100644 --- a/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala @@ -95,7 +95,7 @@ final class CompositeBlockchain private ( } override def balanceSnapshots(address: Address, from: Int, to: Option[BlockId]): Seq[BalanceSnapshot] = - if (maybeDiff.isEmpty || to.exists(id => inner.heightOf(id).isDefined)) { + if (maybeDiff.isEmpty || to.exists(id => inner.heightOf(id).exists(_ != height))) { inner.balanceSnapshots(address, from, to) } else { val balance = this.balance(address) @@ -185,6 +185,14 @@ object CompositeBlockchain { case _ => new CompositeBlockchain(inner, Some(diff)) } + def apply(inner: Blockchain, diff: Seq[Diff]): CompositeBlockchain = { + val acc = inner match { + case cb: CompositeBlockchain => cb + case _ => new CompositeBlockchain(inner, None) + } + diff.foldLeft(acc)(_.appendDiff(_)) + } + def apply( inner: Blockchain, diff: Diff, diff --git a/node/src/test/scala/com/wavesplatform/api/common/CommonAccountApiSpec.scala b/node/src/test/scala/com/wavesplatform/api/common/CommonAccountApiSpec.scala index 21cf0c3ec86..1755ff45d25 100644 --- a/node/src/test/scala/com/wavesplatform/api/common/CommonAccountApiSpec.scala +++ b/node/src/test/scala/com/wavesplatform/api/common/CommonAccountApiSpec.scala @@ -1,7 +1,7 @@ package com.wavesplatform.api.common import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.common.utils._ +import com.wavesplatform.common.utils.* import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.features.BlockchainFeatures @@ -10,7 +10,9 @@ import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.lang.v1.traits.domain.{Lease, Recipient} import com.wavesplatform.settings.TestFunctionalitySettings import com.wavesplatform.state.{DataEntry, Diff, EmptyDataEntry, StringDataEntry, diffs} +import com.wavesplatform.test.DomainPresets.RideV6 import com.wavesplatform.test.FreeSpec +import com.wavesplatform.transaction.TxHelpers.secondAddress import com.wavesplatform.transaction.{DataTransaction, GenesisTransaction, TxHelpers} import com.wavesplatform.{BlocksTransactionsHelpers, history} import monix.execution.Scheduler.Implicits.global @@ -38,32 +40,31 @@ class CommonAccountApiSpec extends FreeSpec with WithDomain with BlocksTransacti (block3, mbs3) = UnsafeBlocks.unsafeChainBaseAndMicro(mbs2.last.totalResBlockSig, Seq(data4), Seq(Seq(data5)), acc, 3, ts) } yield (acc, block1, mbs1.head, block2, mbs2.head, block3, mbs3.head) - forAll(preconditions) { - case (acc, block1, mb1, block2, mb2, block3, mb3) => - withDomain( - domainSettingsWithFS( - TestFunctionalitySettings.withFeatures( - BlockchainFeatures.NG, - BlockchainFeatures.DataTransaction, - BlockchainFeatures.BlockV5 - ) + forAll(preconditions) { case (acc, block1, mb1, block2, mb2, block3, mb3) => + withDomain( + domainSettingsWithFS( + TestFunctionalitySettings.withFeatures( + BlockchainFeatures.NG, + BlockchainFeatures.DataTransaction, + BlockchainFeatures.BlockV5 ) - ) { d => - val commonAccountsApi = CommonAccountsApi(() => d.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), d.db, d.blockchainUpdater) - def dataList(): Set[DataEntry[_]] = commonAccountsApi.dataStream(acc.toAddress, None).toListL.runSyncUnsafe().toSet - - d.appendBlock(block1) - d.appendMicroBlock(mb1) - dataList() shouldBe Set(entry1) - d.appendBlock(block2) - dataList() shouldBe Set(entry1, entry2) - d.appendMicroBlock(mb2) - dataList() shouldBe Set(entry1, entry2, entry3) - d.appendBlock(block3) - dataList() shouldBe Set(entry3) - d.appendMicroBlock(mb3) - dataList() shouldBe Set(entry1, entry2) - } + ) + ) { d => + val commonAccountsApi = CommonAccountsApi(() => d.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), d.db, d.blockchainUpdater) + def dataList(): Set[DataEntry[_]] = commonAccountsApi.dataStream(acc.toAddress, None).toListL.runSyncUnsafe().toSet + + d.appendBlock(block1) + d.appendMicroBlock(mb1) + dataList() shouldBe Set(entry1) + d.appendBlock(block2) + dataList() shouldBe Set(entry1, entry2) + d.appendMicroBlock(mb2) + dataList() shouldBe Set(entry1, entry2, entry3) + d.appendBlock(block3) + dataList() shouldBe Set(entry3) + d.appendMicroBlock(mb3) + dataList() shouldBe Set(entry1, entry2) + } } } @@ -84,21 +85,20 @@ class CommonAccountApiSpec extends FreeSpec with WithDomain with BlocksTransacti (block2, mbs2) = UnsafeBlocks.unsafeChainBaseAndMicro(mbs1.last.totalResBlockSig, Seq(data2), Seq(Seq(data3)), acc, 3, ts) } yield (acc, block1, mbs1.head, block2, mbs2.head) - forAll(preconditions) { - case (acc, block1, mb1, block2, mb2) => - withDomain(domainSettingsWithFS(TestFunctionalitySettings.withFeatures(BlockchainFeatures.NG, BlockchainFeatures.DataTransaction))) { d => - val commonAccountsApi = CommonAccountsApi(() => d.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), d.db, d.blockchainUpdater) - def dataList(): Set[DataEntry[_]] = commonAccountsApi.dataStream(acc.toAddress, Some("test_.*")).toListL.runSyncUnsafe().toSet - - d.appendBlock(block1) - dataList() shouldBe empty - d.appendMicroBlock(mb1) - dataList() shouldBe Set(entry1) - d.appendBlock(block2) - dataList() shouldBe Set(entry1) - d.appendMicroBlock(mb2) - dataList() shouldBe Set(entry1, entry3) - } + forAll(preconditions) { case (acc, block1, mb1, block2, mb2) => + withDomain(domainSettingsWithFS(TestFunctionalitySettings.withFeatures(BlockchainFeatures.NG, BlockchainFeatures.DataTransaction))) { d => + val commonAccountsApi = CommonAccountsApi(() => d.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), d.db, d.blockchainUpdater) + def dataList(): Set[DataEntry[_]] = commonAccountsApi.dataStream(acc.toAddress, Some("test_.*")).toListL.runSyncUnsafe().toSet + + d.appendBlock(block1) + dataList() shouldBe empty + d.appendMicroBlock(mb1) + dataList() shouldBe Set(entry1) + d.appendBlock(block2) + dataList() shouldBe Set(entry1) + d.appendMicroBlock(mb2) + dataList() shouldBe Set(entry1, entry3) + } } } } @@ -175,4 +175,21 @@ class CommonAccountApiSpec extends FreeSpec with WithDomain with BlocksTransacti ) } } + + "Take into account discarded diffs" in { + withDomain(RideV6) { d => + val recipient = secondAddress + val startBalance = d.balance(recipient) + + val lastBlock = d.appendBlock().id() + val transfer = TxHelpers.transfer(amount = 123) + + d.appendMicroBlock(transfer) + d.accountsApi.balance(recipient) - startBalance shouldBe 123 + + d.appendKeyBlock(Some(lastBlock)) + d.utxPool.priorityPool.validPriorityDiffs.head.portfolios(secondAddress).balance shouldBe 123 + d.accountsApi.balance(recipient) - startBalance shouldBe 123 + } + } } diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index 9b9031babe7..2beb62f27cc 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -17,6 +17,7 @@ import com.wavesplatform.lang.script.Script import com.wavesplatform.settings.WavesSettings import com.wavesplatform.state.* import com.wavesplatform.state.diffs.TransactionDiffer +import com.wavesplatform.state.reader.CompositeBlockchain import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.smart.script.trace.TracedResult import com.wavesplatform.transaction.{BlockchainUpdater, *} @@ -390,7 +391,7 @@ case class Domain(db: DB, blockchainUpdater: BlockchainUpdaterImpl, levelDBWrite val accountsApi: CommonAccountsApi = CommonAccountsApi( () => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, - blockchain + CompositeBlockchain(blockchainUpdater, utxPool.priorityPool.validPriorityDiffs) ) val assetsApi: CommonAssetsApi = CommonAssetsApi( From 6e040a04a761195dcce966694f4af3c98b384519 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 18 Aug 2022 14:39:19 +0300 Subject: [PATCH 03/28] Set private readLock() in BlockchainUpdaterImpl --- .../scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala b/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala index 99cbe6e296a..345127894e4 100644 --- a/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala +++ b/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala @@ -52,7 +52,7 @@ class BlockchainUpdaterImpl( private val lock = new ReentrantReadWriteLock(true) private def writeLock[B](f: => B): B = inLock(lock.writeLock(), f) - def readLock[B](f: => B): B = inLock(lock.readLock(), f) + private def readLock[B](f: => B): B = inLock(lock.readLock(), f) private lazy val maxBlockReadinessAge = wavesSettings.minerSettings.intervalAfterLastBlockThenGenerationIsAllowed.toMillis From 086942f11f79d7e0394aa344fad4040a2ffc2ff2 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Mon, 22 Aug 2022 23:20:56 +0300 Subject: [PATCH 04/28] Use UTX priority pool lock to avoid inconsistent state and test --- .../scala/com/wavesplatform/Application.scala | 35 ++++++++----- .../com/wavesplatform/db/TestUtxPool.scala | 13 +++++ .../com/wavesplatform/db/WithState.scala | 5 +- .../com/wavesplatform/history/Domain.scala | 24 ++++++--- .../mining/DiscardedMicroBlocksDiffTest.scala | 50 +++++++++++++++++++ 5 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala create mode 100644 node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala diff --git a/node/src/main/scala/com/wavesplatform/Application.scala b/node/src/main/scala/com/wavesplatform/Application.scala index 2b4906356aa..141e4cd5bdc 100644 --- a/node/src/main/scala/com/wavesplatform/Application.scala +++ b/node/src/main/scala/com/wavesplatform/Application.scala @@ -123,11 +123,11 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con val establishedConnections = new ConcurrentHashMap[Channel, PeerInfo] val allChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE) - val utxStorage = - new UtxPoolImpl(time, blockchainUpdater, settings.utxSettings, settings.minerSettings.enable, utxEvents.onNext) + val utxStorage = new UtxPoolImpl(time, blockchainUpdater, settings.utxSettings, settings.minerSettings.enable, utxEvents.onNext) maybeUtx = Some(utxStorage) - def blockchainWithDiscardedDiffs = CompositeBlockchain(blockchainUpdater, utxStorage.priorityPool.validPriorityDiffs) + def blockchainWithDiscardedDiffs = + utxStorage.priorityPool.optimisticRead(CompositeBlockchain(blockchainUpdater, utxStorage.priorityPool.validPriorityDiffs))(_ => true) val timer = new HashedWheelTimer() val utxSynchronizerLogger = LoggerFacade(LoggerFactory.getLogger(classOf[TransactionPublisher])) @@ -145,10 +145,20 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con val pos = PoSSelector(blockchainUpdater, settings.synchronizationSettings.maxBaseTarget) if (settings.minerSettings.enable) - miner = - new MinerImpl(allChannels, blockchainUpdater, settings, time, utxStorage, wallet, pos, minerScheduler, appenderScheduler, utxEvents.collect { - case _: UtxEvent.TxAdded => () - }) + miner = new MinerImpl( + allChannels, + blockchainUpdater, + settings, + time, + utxStorage, + wallet, + pos, + minerScheduler, + appenderScheduler, + utxEvents.collect { case _: UtxEvent.TxAdded => + () + } + ) val processBlock = BlockAppender(blockchainUpdater, time, utxStorage, pos, allChannels, peerDatabase, appenderScheduler) _ @@ -287,12 +297,11 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con syncWithChannelClosed, extensionLoaderScheduler, timeoutSubject - ) { - case (c, b) => - processFork(c, b).doOnFinish { - case None => Task.now(()) - case Some(e) => Task(stopOnAppendError.reportFailure(e)) - } + ) { case (c, b) => + processFork(c, b).doOnFinish { + case None => Task.now(()) + case Some(e) => Task(stopOnAppendError.reportFailure(e)) + } } TransactionSynchronizer( diff --git a/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala b/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala new file mode 100644 index 00000000000..d7fe32cf124 --- /dev/null +++ b/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala @@ -0,0 +1,13 @@ +package com.wavesplatform.db +import com.wavesplatform.settings.UtxSettings +import com.wavesplatform.state.{Blockchain, Diff} +import com.wavesplatform.utils.Time +import com.wavesplatform.utx.UtxPoolImpl + +class TestUtxPool(time: Time, blockchain: Blockchain, utxSettings: UtxSettings, isMiningEnabled: Boolean, beforeSetPriorityDiffs: () => Unit) + extends UtxPoolImpl(time, blockchain, utxSettings, isMiningEnabled) { + override def setPriorityDiffs(discDiffs: Seq[Diff]): Unit = { + beforeSetPriorityDiffs() + super.setPriorityDiffs(discDiffs) + } +} diff --git a/node/src/test/scala/com/wavesplatform/db/WithState.scala b/node/src/test/scala/com/wavesplatform/db/WithState.scala index eef07dc4347..ea2adcf6306 100644 --- a/node/src/test/scala/com/wavesplatform/db/WithState.scala +++ b/node/src/test/scala/com/wavesplatform/db/WithState.scala @@ -167,7 +167,8 @@ trait WithDomain extends WithState { _: Suite => def withDomain[A]( settings: WavesSettings = DomainPresets.SettingsFromDefaultConfig.addFeatures(BlockchainFeatures.SmartAccounts), // SmartAccounts to allow V2 transfers by default balances: Seq[AddrWithBalance] = Seq.empty, - wrapDB: DB => DB = identity + wrapDB: DB => DB = identity, + beforeSetPriorityDiffs: () => Unit = () => () )(test: Domain => A): A = withLevelDBWriter(settings) { blockchain => var domain: Domain = null @@ -179,7 +180,7 @@ trait WithDomain extends WithState { _: Suite => BlockchainUpdateTriggers.combined(domain.triggers), loadActiveLeases(db, _, _) ) - domain = Domain(wrapDB(db), bcu, blockchain, settings) + domain = Domain(wrapDB(db), bcu, blockchain, settings, beforeSetPriorityDiffs) val genesis = balances.map { case AddrWithBalance(address, amount) => TxHelpers.genesis(address, amount) diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index c3e89598573..850a961f279 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -10,6 +10,7 @@ import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.consensus.nxt.NxtLikeConsensusBlockData import com.wavesplatform.consensus.{PoSCalculator, PoSSelector} import com.wavesplatform.database.{DBExt, Keys, LevelDBWriter} +import com.wavesplatform.db.TestUtxPool import com.wavesplatform.events.BlockchainUpdateTriggers import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lang.ValidationError @@ -22,7 +23,6 @@ import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.smart.script.trace.TracedResult import com.wavesplatform.transaction.{BlockchainUpdater, *} import com.wavesplatform.utils.{EthEncoding, SystemTime} -import com.wavesplatform.utx.UtxPoolImpl import com.wavesplatform.wallet.Wallet import com.wavesplatform.{Application, TestValues, database} import monix.execution.Scheduler.Implicits.global @@ -36,7 +36,13 @@ import scala.concurrent.duration.* import scala.util.Try import scala.util.control.NonFatal -case class Domain(db: DB, blockchainUpdater: BlockchainUpdaterImpl, levelDBWriter: LevelDBWriter, settings: WavesSettings) { +case class Domain( + db: DB, + blockchainUpdater: BlockchainUpdaterImpl, + levelDBWriter: LevelDBWriter, + settings: WavesSettings, + beforeSetPriorityDiffs: () => Unit = () => () +) { import Domain.* val blockchain: BlockchainUpdaterImpl = blockchainUpdater @@ -52,7 +58,7 @@ case class Domain(db: DB, blockchainUpdater: BlockchainUpdaterImpl, levelDBWrite def createDiffE(tx: Transaction): Either[ValidationError, Diff] = transactionDiffer(tx).resultE def createDiff(tx: Transaction): Diff = createDiffE(tx).explicitGet() - lazy val utxPool: UtxPoolImpl = new UtxPoolImpl(SystemTime, blockchain, settings.utxSettings, settings.minerSettings.enable) + lazy val utxPool = new TestUtxPool(SystemTime, blockchain, settings.utxSettings, settings.minerSettings.enable, beforeSetPriorityDiffs) lazy val wallet: Wallet = Wallet(settings.walletSettings.copy(file = None)) object commonApi { @@ -227,7 +233,7 @@ case class Domain(db: DB, blockchainUpdater: BlockchainUpdaterImpl, levelDBWrite } def appendKeyBlock(ref: Option[ByteStr] = None): Block = { - val block = createBlock(Block.NgBlockVersion, Nil, ref.orElse(Some(lastBlockId))) + val block = createBlock(Block.NgBlockVersion, Nil, ref.orElse(Some(lastBlockId))) val discardedDiffs = appendBlock(block) utxPool.setPriorityDiffs(discardedDiffs) utxPool.cleanUnconfirmed() @@ -271,7 +277,13 @@ case class Domain(db: DB, blockchainUpdater: BlockchainUpdaterImpl, levelDBWrite blockchainUpdater.removeAfter(blockId).explicitGet() } - def createBlock(version: Byte, txs: Seq[Transaction], ref: Option[ByteStr] = blockchainUpdater.lastBlockId, strictTime: Boolean = false, generator: KeyPair = defaultSigner): Block = { + def createBlock( + version: Byte, + txs: Seq[Transaction], + ref: Option[ByteStr] = blockchainUpdater.lastBlockId, + strictTime: Boolean = false, + generator: KeyPair = defaultSigner + ): Block = { val reference = ref.getOrElse(randomSig) val parent = ref .flatMap { bs => @@ -389,7 +401,7 @@ case class Domain(db: DB, blockchainUpdater: BlockchainUpdaterImpl, levelDBWrite val accountsApi: CommonAccountsApi = CommonAccountsApi( () => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, - CompositeBlockchain(blockchainUpdater, utxPool.priorityPool.validPriorityDiffs) + utxPool.priorityPool.optimisticRead(CompositeBlockchain(blockchainUpdater, utxPool.priorityPool.validPriorityDiffs))(_ => true) ) val assetsApi: CommonAssetsApi = CommonAssetsApi( diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala new file mode 100644 index 00000000000..83b188b3656 --- /dev/null +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -0,0 +1,50 @@ +package com.wavesplatform.mining + +import com.wavesplatform.block.Block.ProtoBlockVersion +import com.wavesplatform.db.WithDomain +import com.wavesplatform.state.appender.BlockAppender +import com.wavesplatform.test.DomainPresets.RideV6 +import com.wavesplatform.test.{PropSpec, TestTime} +import com.wavesplatform.transaction.TxHelpers.* +import monix.execution.Scheduler +import monix.execution.Scheduler.Implicits.global + +import java.util.concurrent.CountDownLatch +import scala.concurrent.duration.Duration.Inf +import scala.concurrent.{Await, Future} + +class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { + property("interim balance") { + val waitInterimState = new CountDownLatch(1) + val exitInterimState = new CountDownLatch(1) + withDomain( + RideV6, + beforeSetPriorityDiffs = { () => + waitInterimState.countDown() + exitInterimState.await() + } + ) { d => + val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ + + val recipient = secondAddress + val startBalance = d.balance(recipient) + val transferTx = transfer(amount = 123) + def balanceDiff() = d.accountsApi.balance(recipient) - startBalance + + val previousBlockId = d.appendBlock().id() + d.appendMicroBlock(transferTx) + balanceDiff() shouldBe 123 + + val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) + val appendKeyBlock = appendBlock(keyBlock).runToFuture + + waitInterimState.await() + val check = Future(balanceDiff() shouldBe 123) + exitInterimState.countDown() + + Await.result(check, Inf) + Await.result(appendKeyBlock, Inf) + balanceDiff() shouldBe 123 + } + } +} From 684e6386001e3a4b1145e841803d1984777ec6b0 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Mon, 22 Aug 2022 23:43:54 +0300 Subject: [PATCH 05/28] Generify test for assets --- .../mining/DiscardedMicroBlocksDiffTest.scala | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 83b188b3656..49577f1eb57 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -5,6 +5,7 @@ import com.wavesplatform.db.WithDomain import com.wavesplatform.state.appender.BlockAppender import com.wavesplatform.test.DomainPresets.RideV6 import com.wavesplatform.test.{PropSpec, TestTime} +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxHelpers.* import monix.execution.Scheduler import monix.execution.Scheduler.Implicits.global @@ -14,37 +15,46 @@ import scala.concurrent.duration.Duration.Inf import scala.concurrent.{Await, Future} class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { - property("interim balance") { - val waitInterimState = new CountDownLatch(1) - val exitInterimState = new CountDownLatch(1) - withDomain( - RideV6, - beforeSetPriorityDiffs = { () => - waitInterimState.countDown() - exitInterimState.await() + property("consistent interim balance") { + Seq(true, false).foreach { useAsset => + val waitInterimState = new CountDownLatch(1) + val getOutOfInterimState = new CountDownLatch(1) + withDomain( + RideV6, + beforeSetPriorityDiffs = { () => + waitInterimState.countDown() + getOutOfInterimState.await() + } + ) { d => + val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ + + val recipient = secondAddress + val issueTx = issue() + val issuedAsset = IssuedAsset(issueTx.id()) + val asset = if (useAsset) issuedAsset else Waves + val startBalance = d.balance(recipient, asset) + val transferTx = transfer(amount = 123, asset = asset) + def balanceDiff() = + if (useAsset) + d.accountsApi.assetBalance(recipient, issuedAsset) - startBalance + else + d.accountsApi.balance(recipient) - startBalance + + val previousBlockId = d.appendBlock(issueTx).id() + d.appendMicroBlock(transferTx) + balanceDiff() shouldBe 123 + + val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) + val appendKeyBlock = appendBlock(keyBlock).runToFuture + + waitInterimState.await() + val check = Future(balanceDiff() shouldBe 123) + getOutOfInterimState.countDown() + + Await.result(check, Inf) + Await.result(appendKeyBlock, Inf) + balanceDiff() shouldBe 123 } - ) { d => - val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ - - val recipient = secondAddress - val startBalance = d.balance(recipient) - val transferTx = transfer(amount = 123) - def balanceDiff() = d.accountsApi.balance(recipient) - startBalance - - val previousBlockId = d.appendBlock().id() - d.appendMicroBlock(transferTx) - balanceDiff() shouldBe 123 - - val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) - val appendKeyBlock = appendBlock(keyBlock).runToFuture - - waitInterimState.await() - val check = Future(balanceDiff() shouldBe 123) - exitInterimState.countDown() - - Await.result(check, Inf) - Await.result(appendKeyBlock, Inf) - balanceDiff() shouldBe 123 } } } From 7fb412046fb366a47c479d5f2d7ccd4e78cdea6c Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 23 Aug 2022 00:38:41 +0300 Subject: [PATCH 06/28] Test account data --- .../mining/DiscardedMicroBlocksDiffTest.scala | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 49577f1eb57..414314e7b3a 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -2,6 +2,8 @@ package com.wavesplatform.mining import com.wavesplatform.block.Block.ProtoBlockVersion import com.wavesplatform.db.WithDomain +import com.wavesplatform.db.WithState.AddrWithBalance +import com.wavesplatform.state.IntegerDataEntry import com.wavesplatform.state.appender.BlockAppender import com.wavesplatform.test.DomainPresets.RideV6 import com.wavesplatform.test.{PropSpec, TestTime} @@ -57,4 +59,37 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { } } } + + property("consistent interim account data") { + val waitInterimState = new CountDownLatch(1) + val getOutOfInterimState = new CountDownLatch(1) + withDomain( + RideV6, + AddrWithBalance.enoughBalances(defaultSigner), + beforeSetPriorityDiffs = { () => + waitInterimState.countDown() + getOutOfInterimState.await() + } + ) { d => + val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ + + val dataTx = dataEntry(defaultSigner, IntegerDataEntry("key", 1)) + def data() = d.accountsApi.data(defaultAddress, "key").map(_.value) + + val previousBlockId = d.appendBlock().id() + d.appendMicroBlock(dataTx) + data() shouldBe Some(1) + + val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) + val appendKeyBlock = appendBlock(keyBlock).runToFuture + + waitInterimState.await() + val check = Future(data() shouldBe Some(1)) + getOutOfInterimState.countDown() + + Await.result(check, Inf) + Await.result(appendKeyBlock, Inf) + data() shouldBe Some(1) + } + } } From 7eee3e5adcc363920b68744c5ae2efc4dae1bd8a Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 23 Aug 2022 11:49:34 +0300 Subject: [PATCH 07/28] Pass blockchainWithDiscardedDiffs to all places where needed and optimize --- .../lang/evaluator/EvaluatorV2Test.scala | 12 +++ .../scala/com/wavesplatform/Application.scala | 18 ++-- .../scala/com/wavesplatform/Importer.scala | 6 +- .../api/common/CommonAccountsApi.scala | 78 +++++++++------- .../api/common/CommonAssetsApi.scala | 18 ++-- .../api/common/CommonTransactionsApi.scala | 8 +- .../api/http/AddressApiRoute.scala | 86 ++++++++--------- .../api/http/TransactionsApiRoute.scala | 9 +- .../api/http/alias/AliasApiRoute.scala | 4 +- .../api/http/assets/AssetsApiRoute.scala | 92 +++++++++---------- .../api/http/eth/EthRpcRoute.scala | 33 +++---- .../api/http/leasing/LeaseApiRoute.scala | 27 +++--- .../api/common/CommonAccountApiSpec.scala | 6 +- .../api/eth/EthRpcRouteSpec.scala | 4 +- .../api/http/CustomJsonMarshallerSpec.scala | 4 +- .../com/wavesplatform/history/Domain.scala | 8 +- .../wavesplatform/http/AddressRouteSpec.scala | 8 +- .../http/AliasBroadcastRouteSpec.scala | 2 +- .../http/AssetsBroadcastRouteSpec.scala | 4 +- .../wavesplatform/http/AssetsRouteSpec.scala | 2 +- .../http/LeaseBroadcastRouteSpec.scala | 3 +- .../wavesplatform/http/LeaseRouteSpec.scala | 5 +- .../http/ProtoVersionTransactionsSpec.scala | 2 +- .../http/SpentComplexitySpec.scala | 2 +- .../http/TransactionBroadcastSpec.scala | 6 +- .../http/TransactionsRouteSpec.scala | 10 +- .../EvaluatedPBSerializationTest.scala | 2 +- .../smart/scenarios/BalancesV4Test.scala | 2 +- 28 files changed, 241 insertions(+), 220 deletions(-) diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala index 7f59414a923..d94e28fb3e6 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala @@ -7,6 +7,7 @@ import com.wavesplatform.lang.directives.DirectiveSet import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.utils.lazyContexts import com.wavesplatform.lang.v1.FunctionHeader +import com.wavesplatform.lang.v1.FunctionHeader.Native import com.wavesplatform.lang.v1.compiler.Terms.{CONST_LONG, *} import com.wavesplatform.lang.v1.compiler.{Decompiler, ExpressionCompiler} import com.wavesplatform.lang.v1.evaluator.{EvaluatorV2, FunctionIds} @@ -1242,4 +1243,15 @@ class EvaluatorV2Test extends PropSpec with Inside { evalOld(script2, 100) shouldBe ((TRUE, "true", 5)) // 3 conditions + ref twice evalNew(script2, 100) shouldBe ((TRUE, "true", 3)) // 3 function call } + + property("test") { + val expr = BLOCK( + LET("n", CONST_LONG(26)), + LET_BLOCK( + LET("complexInt1", CONST_LONG(1973)), + LET_BLOCK(LET("res", CONST_STRING("Unit").explicitGet()), IF(CONST_BOOLEAN(true), REF("nil"), FUNCTION_CALL(Native(2), List(CONST_STRING("Strict value is not equal to itself.").explicitGet())))) + ) + ) + evalOld(expr, 1) shouldBe 1 + } } diff --git a/node/src/main/scala/com/wavesplatform/Application.scala b/node/src/main/scala/com/wavesplatform/Application.scala index 141e4cd5bdc..228a72e6d2b 100644 --- a/node/src/main/scala/com/wavesplatform/Application.scala +++ b/node/src/main/scala/com/wavesplatform/Application.scala @@ -126,7 +126,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con val utxStorage = new UtxPoolImpl(time, blockchainUpdater, settings.utxSettings, settings.minerSettings.enable, utxEvents.onNext) maybeUtx = Some(utxStorage) - def blockchainWithDiscardedDiffs = + def blockchainWithDiscardedDiffs(): CompositeBlockchain = utxStorage.priorityPool.optimisticRead(CompositeBlockchain(blockchainUpdater, utxStorage.priorityPool.validPriorityDiffs))(_ => true) val timer = new HashedWheelTimer() @@ -227,7 +227,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con override val transactionsApi: CommonTransactionsApi = CommonTransactionsApi( blockchainUpdater.bestLiquidDiff.map(diff => Height(blockchainUpdater.height) -> diff), db, - blockchainUpdater, + blockchainWithDiscardedDiffs, utxStorage, tx => transactionPublisher.validateAndBroadcast(tx, None), loadBlockAt(db, blockchainUpdater) @@ -236,7 +236,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con CommonBlocksApi(blockchainUpdater, loadBlockMetaAt(db, blockchainUpdater), loadBlockInfoAt(db, blockchainUpdater)) override val accountsApi: CommonAccountsApi = CommonAccountsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainWithDiscardedDiffs) - override val assetsApi: CommonAssetsApi = CommonAssetsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainUpdater) + override val assetsApi: CommonAssetsApi = CommonAssetsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainWithDiscardedDiffs) } extensions = settings.extensions.map { extensionClassName => @@ -340,14 +340,14 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con ) val apiRoutes = Seq( - new EthRpcRoute(blockchainUpdater, extensionContext.transactionsApi, time), + EthRpcRoute(blockchainWithDiscardedDiffs, extensionContext.transactionsApi, time), NodeApiRoute(settings.restAPISettings, blockchainUpdater, () => shutdown()), BlocksApiRoute(settings.restAPISettings, extensionContext.blocksApi, time), TransactionsApiRoute( settings.restAPISettings, extensionContext.transactionsApi, wallet, - blockchainUpdater, + blockchainWithDiscardedDiffs, () => utxStorage.size, transactionPublisher, time @@ -364,7 +364,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con AddressApiRoute( settings.restAPISettings, wallet, - blockchainUpdater, + blockchainWithDiscardedDiffs, transactionPublisher, time, limitedScheduler, @@ -397,15 +397,15 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con settings.restAPISettings, wallet, transactionPublisher, - blockchainUpdater, + blockchainWithDiscardedDiffs, time, extensionContext.accountsApi, extensionContext.assetsApi, settings.dbSettings.maxRollbackDepth ), ActivationApiRoute(settings.restAPISettings, settings.featuresSettings, blockchainUpdater), - LeaseApiRoute(settings.restAPISettings, wallet, blockchainUpdater, transactionPublisher, time, extensionContext.accountsApi), - AliasApiRoute(settings.restAPISettings, extensionContext.transactionsApi, wallet, transactionPublisher, time, blockchainUpdater), + LeaseApiRoute(settings.restAPISettings, wallet, transactionPublisher, time, extensionContext.accountsApi), + AliasApiRoute(settings.restAPISettings, extensionContext.transactionsApi, wallet, transactionPublisher, time, blockchainWithDiscardedDiffs), RewardApiRoute(blockchainUpdater) ) diff --git a/node/src/main/scala/com/wavesplatform/Importer.scala b/node/src/main/scala/com/wavesplatform/Importer.scala index 118c854c612..2d1a01609c4 100644 --- a/node/src/main/scala/com/wavesplatform/Importer.scala +++ b/node/src/main/scala/com/wavesplatform/Importer.scala @@ -134,7 +134,7 @@ object Importer extends ScorexLogging { CommonTransactionsApi( blockchainUpdater.bestLiquidDiff.map(diff => Height(blockchainUpdater.height) -> diff), db, - blockchainUpdater, + () => blockchainUpdater, utxPool, _ => Future.successful(TracedResult.wrapE(Left(GenericError("Not implemented during import")))), Application.loadBlockAt(db, blockchainUpdater) @@ -142,9 +142,9 @@ object Importer extends ScorexLogging { override def blocksApi: CommonBlocksApi = CommonBlocksApi(blockchainUpdater, Application.loadBlockMetaAt(db, blockchainUpdater), Application.loadBlockInfoAt(db, blockchainUpdater)) override def accountsApi: CommonAccountsApi = - CommonAccountsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainUpdater) + CommonAccountsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, () => blockchainUpdater) override def assetsApi: CommonAssetsApi = - CommonAssetsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainUpdater) + CommonAssetsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, () => blockchainUpdater) } } diff --git a/node/src/main/scala/com/wavesplatform/api/common/CommonAccountsApi.scala b/node/src/main/scala/com/wavesplatform/api/common/CommonAccountsApi.scala index 15b4a077e82..483d7fa08eb 100644 --- a/node/src/main/scala/com/wavesplatform/api/common/CommonAccountsApi.scala +++ b/node/src/main/scala/com/wavesplatform/api/common/CommonAccountsApi.scala @@ -55,22 +55,24 @@ object CommonAccountsApi { final case class BalanceDetails(regular: Long, generating: Long, available: Long, effective: Long, leaseIn: Long, leaseOut: Long) - def apply(diff: () => Diff, db: DB, blockchain: => Blockchain): CommonAccountsApi = new CommonAccountsApi { + def apply(diff: () => Diff, db: DB, blockchain: () => Blockchain): CommonAccountsApi = new CommonAccountsApi { override def balance(address: Address, confirmations: Int = 0): Long = { - blockchain.balance(address, blockchain.height, confirmations) + val bc = blockchain() + bc.balance(address, bc.height, confirmations) } override def effectiveBalance(address: Address, confirmations: Int = 0): Long = { - blockchain.effectiveBalance(address, confirmations) + blockchain().effectiveBalance(address, confirmations) } override def balanceDetails(address: Address): Either[String, BalanceDetails] = { - val portfolio = blockchain.wavesPortfolio(address) + val bc = blockchain() + val portfolio = bc.wavesPortfolio(address) portfolio.effectiveBalance.map(effectiveBalance => BalanceDetails( portfolio.balance, - blockchain.generatingBalance(address), + bc.generatingBalance(address), portfolio.balance - portfolio.lease.out, effectiveBalance, portfolio.lease.in, @@ -79,26 +81,29 @@ object CommonAccountsApi { ) } - override def assetBalance(address: Address, asset: IssuedAsset): Long = blockchain.balance(address, asset) + override def assetBalance(address: Address, asset: IssuedAsset): Long = blockchain().balance(address, asset) override def portfolio(address: Address): Observable[(IssuedAsset, Long)] = { val currentDiff = diff() + val bc = blockchain() db.resourceObservable.flatMap { resource => - Observable.fromIterator(Task(assetBalanceIterator(resource, address, currentDiff, includeNft(blockchain)))) + Observable.fromIterator(Task(assetBalanceIterator(resource, address, currentDiff, includeNft(bc)))) } } override def nftList(address: Address, after: Option[IssuedAsset]): Observable[(IssuedAsset, AssetDescription)] = { val currentDiff = diff() + val bc = blockchain() db.resourceObservable.flatMap { resource => - Observable.fromIterator(Task(nftIterator(resource, address, currentDiff, after, blockchain.assetDescription))) + Observable.fromIterator(Task(nftIterator(resource, address, currentDiff, after, bc.assetDescription))) } } - override def script(address: Address): Option[AccountScriptInfo] = blockchain.accountScript(address) + override def script(address: Address): Option[AccountScriptInfo] = + blockchain().accountScript(address) override def data(address: Address, key: String): Option[DataEntry[?]] = - blockchain.accountData(address, key) + blockchain().accountData(address, key) override def dataStream(address: Address, regex: Option[String]): Observable[DataEntry[?]] = Observable.defer { val pattern = regex.map(_.r.pattern) @@ -125,12 +130,14 @@ object CommonAccountsApi { Observable.fromIterable((entriesFromDiff.values ++ entries).filterNot(_.isEmpty)) } - override def resolveAlias(alias: Alias): Either[ValidationError, Address] = blockchain.resolveAlias(alias) + override def resolveAlias(alias: Alias): Either[ValidationError, Address] = + blockchain().resolveAlias(alias) - override def activeLeases(address: Address): Observable[LeaseInfo] = + override def activeLeases(address: Address): Observable[LeaseInfo] = { + val bc = blockchain() addressTransactions( db, - Some(Height(blockchain.height) -> diff()), + Some(Height(bc.height) -> diff()), address, None, Set(TransactionType.Lease, TransactionType.InvokeScript, TransactionType.InvokeExpression, TransactionType.Ethereum), @@ -142,7 +149,7 @@ object CommonAccountsApi { lt.id(), lt.id(), lt.sender.toAddress, - blockchain.resolveAlias(lt.recipient).explicitGet(), + bc.resolveAlias(lt.recipient).explicitGet(), lt.amount.value, leaseHeight, LeaseInfo.Status.Active @@ -154,13 +161,15 @@ object CommonAccountsApi { extractLeases(address, scriptResult, tx.id(), height) case _ => Seq() } + } private def extractLeases(subject: Address, result: InvokeScriptResult, txId: ByteStr, height: Height): Seq[LeaseInfo] = { + val bc = blockchain() (for { lease <- result.leases - details <- blockchain.leaseDetails(lease.id) if details.isActive + details <- bc.leaseDetails(lease.id) if details.isActive sender = details.sender.toAddress - recipient <- blockchain.resolveAlias(lease.recipient).toOption if subject == sender || subject == recipient + recipient <- bc.resolveAlias(lease.recipient).toOption if subject == sender || subject == recipient } yield LeaseInfo( lease.id, txId, @@ -181,26 +190,29 @@ object CommonAccountsApi { Right(recipientAddress) } - def leaseInfo(leaseId: ByteStr): Option[LeaseInfo] = blockchain.leaseDetails(leaseId) map { ld => - LeaseInfo( - leaseId, - ld.sourceId, - ld.sender.toAddress, - blockchain.resolveAlias(ld.recipient).orElse(resolveDisabledAlias(leaseId)).explicitGet(), - ld.amount, - ld.height, - ld.status match { - case Status.Active => LeaseInfo.Status.Active - case Status.Cancelled(_, _) => LeaseInfo.Status.Canceled - case Status.Expired(_) => LeaseInfo.Status.Expired - }, - ld.status.cancelHeight, - ld.status.cancelTransactionId - ) + def leaseInfo(leaseId: ByteStr): Option[LeaseInfo] = { + val bc = blockchain() + bc.leaseDetails(leaseId) map { ld => + LeaseInfo( + leaseId, + ld.sourceId, + ld.sender.toAddress, + bc.resolveAlias(ld.recipient).orElse(resolveDisabledAlias(leaseId)).explicitGet(), + ld.amount, + ld.height, + ld.status match { + case Status.Active => LeaseInfo.Status.Active + case Status.Cancelled(_, _) => LeaseInfo.Status.Canceled + case Status.Expired(_) => LeaseInfo.Status.Expired + }, + ld.status.cancelHeight, + ld.status.cancelTransactionId + ) + } } private[this] def leaseIsActive(id: ByteStr): Boolean = - blockchain.leaseDetails(id).exists(_.isActive) + blockchain().leaseDetails(id).exists(_.isActive) } } diff --git a/node/src/main/scala/com/wavesplatform/api/common/CommonAssetsApi.scala b/node/src/main/scala/com/wavesplatform/api/common/CommonAssetsApi.scala index 9039424ee81..5098c94bae7 100644 --- a/node/src/main/scala/com/wavesplatform/api/common/CommonAssetsApi.scala +++ b/node/src/main/scala/com/wavesplatform/api/common/CommonAssetsApi.scala @@ -23,26 +23,28 @@ trait CommonAssetsApi { object CommonAssetsApi { final case class AssetInfo(description: AssetDescription, issueTransaction: Option[IssueTransaction], sponsorBalance: Option[Long]) - def apply(diff: () => Diff, db: DB, blockchain: Blockchain): CommonAssetsApi = new CommonAssetsApi { + def apply(diff: () => Diff, db: DB, blockchain: () => Blockchain): CommonAssetsApi = new CommonAssetsApi { def description(assetId: IssuedAsset): Option[AssetDescription] = - blockchain.assetDescription(assetId) + blockchain().assetDescription(assetId) - def fullInfo(assetId: IssuedAsset): Option[AssetInfo] = + def fullInfo(assetId: IssuedAsset): Option[AssetInfo] = { + val bc = blockchain() for { - assetInfo <- blockchain.assetDescription(assetId) - sponsorBalance = if (assetInfo.sponsorship != 0) Some(blockchain.wavesPortfolio(assetInfo.issuer.toAddress).spendableBalance) else None + assetInfo <- bc.assetDescription(assetId) + sponsorBalance = if (assetInfo.sponsorship != 0) Some(bc.wavesPortfolio(assetInfo.issuer.toAddress).spendableBalance) else None } yield AssetInfo( assetInfo, - blockchain.transactionInfo(assetId.id).collect { case (tm, it: IssueTransaction) if tm.succeeded => it }, + bc.transactionInfo(assetId.id).collect { case (tm, it: IssueTransaction) if tm.succeeded => it }, sponsorBalance ) + } override def wavesDistribution(height: Int, after: Option[Address]): Observable[(Address, Long)] = balanceDistribution( db, height, after, - if (height == blockchain.height) diff().portfolios else Map.empty[Address, Portfolio], + if (height == blockchain().height) diff().portfolios else Map.empty[Address, Portfolio], KeyTags.WavesBalance.prefixBytes, bs => AddressId.fromByteArray(bs.slice(2, bs.length - 4)), _.balance @@ -53,7 +55,7 @@ object CommonAssetsApi { db, height, after, - if (height == blockchain.height) diff().portfolios else Map.empty[Address, Portfolio], + if (height == blockchain().height) diff().portfolios else Map.empty[Address, Portfolio], KeyTags.AssetBalance.prefixBytes ++ asset.id.arr, bs => AddressId.fromByteArray(bs.slice(2 + crypto.DigestLength, bs.length - 4)), _.assets.getOrElse(asset, 0L) diff --git a/node/src/main/scala/com/wavesplatform/api/common/CommonTransactionsApi.scala b/node/src/main/scala/com/wavesplatform/api/common/CommonTransactionsApi.scala index a4e3315a4a3..0aba47db34a 100644 --- a/node/src/main/scala/com/wavesplatform/api/common/CommonTransactionsApi.scala +++ b/node/src/main/scala/com/wavesplatform/api/common/CommonTransactionsApi.scala @@ -47,7 +47,7 @@ object CommonTransactionsApi { def apply( maybeDiff: => Option[(Height, Diff)], db: DB, - blockchain: Blockchain, + blockchain: () => Blockchain, utx: UtxPool, publishTransaction: Transaction => Future[TracedResult[ValidationError, Boolean]], blockAt: Int => Option[(BlockMeta, Seq[(TxMeta, Transaction)])] @@ -63,7 +63,7 @@ object CommonTransactionsApi { common.addressTransactions(db, maybeDiff, subject, sender, transactionTypes, fromId) override def transactionById(transactionId: ByteStr): Option[TransactionMeta] = - blockchain.transactionInfo(transactionId).map(common.loadTransactionMeta(db, maybeDiff)) + blockchain().transactionInfo(transactionId).map(common.loadTransactionMeta(db, maybeDiff)) override def unconfirmedTransactions: Seq[Transaction] = utx.all @@ -72,7 +72,7 @@ object CommonTransactionsApi { override def calculateFee(tx: Transaction): Either[ValidationError, (Asset, Long, Long)] = FeeValidation - .getMinFee(blockchain, tx) + .getMinFee(blockchain(), tx) .map { case FeeDetails(asset, _, feeInAsset, feeInWaves) => (asset, feeInAsset, feeInWaves) @@ -83,7 +83,7 @@ object CommonTransactionsApi { override def transactionProofs(transactionIds: List[ByteStr]): List[TransactionProof] = for { transactionId <- transactionIds - (txm, tx) <- blockchain.transactionInfo(transactionId) + (txm, tx) <- blockchain().transactionInfo(transactionId) (meta, allTransactions) <- blockAt(txm.height) if meta.header.version >= Block.ProtoBlockVersion transactionProof <- block.transactionProof(tx, allTransactions.map(_._2)) } yield transactionProof diff --git a/node/src/main/scala/com/wavesplatform/api/http/AddressApiRoute.scala b/node/src/main/scala/com/wavesplatform/api/http/AddressApiRoute.scala index f3eeafc2019..345ba2f416f 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/AddressApiRoute.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/AddressApiRoute.scala @@ -36,7 +36,7 @@ import scala.util.{Success, Try} case class AddressApiRoute( settings: RestAPISettings, wallet: Wallet, - blockchain: Blockchain, + blockchain: () => Blockchain, transactionPublisher: TransactionPublisher, time: Time, limitedScheduler: Scheduler, @@ -59,7 +59,8 @@ case class AddressApiRoute( def scriptInfo: Route = (path("scriptInfo" / AddrSegment) & get) { address => completeLimited { - val scriptInfoOpt = blockchain.accountScript(address) + val bc = blockchain() + val scriptInfoOpt = bc.accountScript(address) val callableComplexitiesOpt = for { scriptInfo <- scriptInfoOpt @@ -67,7 +68,7 @@ case class AddressApiRoute( case ContractScriptImpl(_, DApp(_, _, _, Some(vf))) => Some(vf.u.name) case _ => None } - complexities <- scriptInfo.complexitiesByEstimator.get(blockchain.estimator.version) + complexities <- scriptInfo.complexitiesByEstimator.get(bc.estimator.version) } yield verifierName.fold(complexities)(complexities - _) val callableComplexities = callableComplexitiesOpt.getOrElse(Map[String, Long]()) @@ -82,7 +83,7 @@ case class AddressApiRoute( "complexity" -> maxComplexity, "verifierComplexity" -> verifierComplexity, "callableComplexities" -> callableComplexities, - "extraFee" -> (if (blockchain.hasPaidVerifier(address)) FeeValidation.ScriptExtraFee else 0L) + "extraFee" -> (if (bc.hasPaidVerifier(address)) FeeValidation.ScriptExtraFee else 0L) ) } } @@ -118,7 +119,7 @@ case class AddressApiRoute( def balances: Route = (path("balance") & get & parameters("height".as[Int].?, "address".as[String].*, "asset".?)) { (maybeHeight, addresses, assetId) => - val height = maybeHeight.getOrElse(blockchain.height) + val height = maybeHeight.getOrElse(blockchain().height) validateBalanceDepth(height)( complete( balancesJson(height, addresses.toSeq, assetId.fold(Waves: Asset)(a => IssuedAsset(ByteStr.decodeBase58(a).get))) @@ -127,7 +128,7 @@ case class AddressApiRoute( } def balancesPost: Route = (path("balance") & (post & entity(as[JsObject]))) { request => - val height = (request \ "height").asOpt[Int].getOrElse(blockchain.height) + val height = (request \ "height").asOpt[Int].getOrElse(blockchain().height) val addresses = (request \ "addresses").as[Seq[String]] val assetId = (request \ "asset").asOpt[String] validateBalanceDepth(height)(complete(balancesJson(height, addresses, assetId.fold(Waves: Asset)(a => IssuedAsset(ByteStr.decodeBase58(a).get))))) @@ -137,7 +138,8 @@ case class AddressApiRoute( commonAccountsApi .balanceDetails(address) .fold( - e => complete(CustomValidationError(e)), { details => + e => complete(CustomValidationError(e)), + { details => import details.* complete( Json.obj( @@ -153,11 +155,10 @@ case class AddressApiRoute( } def balanceWithConfirmations: Route = { - (path("balance" / AddrSegment / IntNumber) & get) { - case (address, confirmations) => - validateBalanceDepth(blockchain.height - confirmations)( - complete(balanceJson(address, confirmations)) - ) + (path("balance" / AddrSegment / IntNumber) & get) { case (address, confirmations) => + validateBalanceDepth(blockchain().height - confirmations)( + complete(balanceJson(address, confirmations)) + ) } } @@ -169,7 +170,7 @@ case class AddressApiRoute( def effectiveBalanceWithConfirmations: Route = { path("effectiveBalance" / AddrSegment / IntNumber) { (address, confirmations) => - validateBalanceDepth(blockchain.height - confirmations)( + validateBalanceDepth(blockchain().height - confirmations)( complete(effectiveBalanceJson(address, confirmations)) ) } @@ -199,24 +200,23 @@ case class AddressApiRoute( (path(Segment) & get) { key => complete(accountDataEntry(address, key)) - } ~ extractScheduler( - implicit sc => - strictEntity { - (formField("matches") | parameter("matches")) { matches => - Try(matches.r) - .fold( - { e => - log.trace(s"Error compiling regex $matches: ${e.getMessage}") - complete(ApiError.fromValidationError(GenericError(s"Cannot compile regex"))) - }, - _ => complete(accountData(address, matches)) - ) - } ~ anyParam("key").filter(_.nonEmpty) { keys => - complete(accountDataList(address, keys.toSeq*)) - } ~ get { - complete(accountData(address)) - } + } ~ extractScheduler(implicit sc => + strictEntity { + (formField("matches") | parameter("matches")) { matches => + Try(matches.r) + .fold( + { e => + log.trace(s"Error compiling regex $matches: ${e.getMessage}") + complete(ApiError.fromValidationError(GenericError(s"Cannot compile regex"))) + }, + _ => complete(accountData(address, matches)) + ) + } ~ anyParam("key").filter(_.nonEmpty) { keys => + complete(accountDataList(address, keys.toSeq*)) + } ~ get { + complete(accountData(address)) } + } ) } @@ -225,11 +225,10 @@ case class AddressApiRoute( } def seq: Route = { - (path("seq" / IntNumber / IntNumber) & get) { - case (start, end) => - if (start < 0 || end < 0 || start > end) complete(GenericError("Invalid sequence")) - else if (end - start >= MaxAddressesPerRequest) complete(TooBigArrayAllocation(MaxAddressesPerRequest)) - else complete(wallet.privateKeyAccounts.map(_.toAddress).slice(start, end)) + (path("seq" / IntNumber / IntNumber) & get) { case (start, end) => + if (start < 0 || end < 0 || start > end) complete(GenericError("Invalid sequence")) + else if (end - start >= MaxAddressesPerRequest) complete(TooBigArrayAllocation(MaxAddressesPerRequest)) + else complete(wallet.privateKeyAccounts.map(_.toAddress).slice(start, end)) } } @@ -240,9 +239,10 @@ case class AddressApiRoute( } } - private def balancesJson(height: Int, addresses: Seq[String], assetId: Asset): ToResponseMarshallable = + private def balancesJson(height: Int, addresses: Seq[String], assetId: Asset): ToResponseMarshallable = { + val bc = blockchain() if (addresses.length > settings.transactionsByAddressLimit) TooBigArrayAllocation - else if (height < 1 || height > blockchain.height) CustomValidationError(s"Illegal height: $height") + else if (height < 1 || height > bc.height) CustomValidationError(s"Illegal height: $height") else { implicit val balancesWrites: Writes[(String, Long)] = Writes[(String, Long)] { b => Json.obj("id" -> b._1, "balance" -> b._2) @@ -251,10 +251,11 @@ case class AddressApiRoute( val balances = for { addressStr <- addresses.toSet[String] address <- Address.fromString(addressStr).toOption - } yield blockchain.balanceAtHeight(address, height, assetId).fold(addressStr -> 0L)(addressStr -> _._2) + } yield bc.balanceAtHeight(address, height, assetId).fold(addressStr -> 0L)(addressStr -> _._2) ToResponseMarshallable(balances) } + } private def balanceJson(acc: Address, confirmations: Int) = { Balance(acc.toString, confirmations, commonAccountsApi.balance(acc, confirmations)) @@ -263,7 +264,7 @@ case class AddressApiRoute( private def balanceJson(acc: Address) = Balance(acc.toString, 0, commonAccountsApi.balance(acc)) private def scriptMetaJson(account: Address): Either[ValidationError.ScriptParseError, AccountScriptMeta] = { - val accountScript = blockchain.accountScript(account) + val accountScript = blockchain().accountScript(account) accountScript .map(_.script) @@ -276,8 +277,9 @@ case class AddressApiRoute( } private[this] def validateBalanceDepth(height: Int): Directive0 = { - if (height < blockchain.height - maxBalanceDepth) - complete(CustomValidationError(s"Unable to get balance past height ${blockchain.height - maxBalanceDepth}")) + val bc = blockchain() + if (height < bc.height - maxBalanceDepth) + complete(CustomValidationError(s"Unable to get balance past height ${bc.height - maxBalanceDepth}")) else pass } @@ -330,7 +332,7 @@ case class AddressApiRoute( case (Success(msgBytes), Success(signatureBytes), Success(pubKeyBytes)) => val account = PublicKey(pubKeyBytes) val isValid = account.toAddress == address && - crypto.verify(signatureBytes, msgBytes, PublicKey(pubKeyBytes), blockchain.isFeatureActivated(BlockchainFeatures.RideV6)) + crypto.verify(signatureBytes, msgBytes, PublicKey(pubKeyBytes), blockchain().isFeatureActivated(BlockchainFeatures.RideV6)) Right(Json.obj("valid" -> isValid)) case _ => Left(InvalidMessage) } diff --git a/node/src/main/scala/com/wavesplatform/api/http/TransactionsApiRoute.scala b/node/src/main/scala/com/wavesplatform/api/http/TransactionsApiRoute.scala index 549582f2392..b70be653408 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/TransactionsApiRoute.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/TransactionsApiRoute.scala @@ -40,7 +40,7 @@ case class TransactionsApiRoute( settings: RestAPISettings, commonApi: CommonTransactionsApi, wallet: Wallet, - blockchain: Blockchain, + blockchain: () => Blockchain, utxPoolSize: () => Int, transactionPublisher: TransactionPublisher, time: Time @@ -49,7 +49,7 @@ case class TransactionsApiRoute( with AuthRoute { import TransactionsApiRoute._ - private[this] val serializer = TransactionJsonSerializer(blockchain, commonApi) + private[this] val serializer = TransactionJsonSerializer(blockchain(), commonApi) private[this] implicit val transactionMetaWrites = OWrites[TransactionMeta](serializer.transactionWithMetaJson) override lazy val route: Route = @@ -89,12 +89,13 @@ case class TransactionsApiRoute( private[this] def loadTransactionStatus(id: ByteStr): JsObject = { import Status._ - val statusJson = blockchain.transactionInfo(id) match { + val bc = blockchain() + val statusJson = bc.transactionInfo(id) match { case Some((tm, tx)) => Json.obj( "status" -> Confirmed, "height" -> JsNumber(tm.height), - "confirmations" -> (blockchain.height - tm.height).max(0) + "confirmations" -> (bc.height - tm.height).max(0) ) ++ serializer.metaJson(tm) case None => commonApi.unconfirmedTransactionById(id) match { diff --git a/node/src/main/scala/com/wavesplatform/api/http/alias/AliasApiRoute.scala b/node/src/main/scala/com/wavesplatform/api/http/alias/AliasApiRoute.scala index 9bd9a1f8c50..563ac26d59e 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/alias/AliasApiRoute.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/alias/AliasApiRoute.scala @@ -23,7 +23,7 @@ case class AliasApiRoute( wallet: Wallet, transactionPublisher: TransactionPublisher, time: Time, - blockchain: Blockchain + blockchain: () => Blockchain ) extends ApiRoute with BroadcastRoute with AuthRoute { @@ -44,7 +44,7 @@ case class AliasApiRoute( Alias .create(aliasName) .flatMap { a => - blockchain.resolveAlias(a).bimap(_ => TxValidationError.AliasDoesNotExist(a), addr => Json.obj("address" -> addr.toString)) + blockchain().resolveAlias(a).bimap(_ => TxValidationError.AliasDoesNotExist(a), addr => Json.obj("address" -> addr.toString)) } } } diff --git a/node/src/main/scala/com/wavesplatform/api/http/assets/AssetsApiRoute.scala b/node/src/main/scala/com/wavesplatform/api/http/assets/AssetsApiRoute.scala index 582d2084a4a..714eea0ccdc 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/assets/AssetsApiRoute.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/assets/AssetsApiRoute.scala @@ -1,24 +1,22 @@ package com.wavesplatform.api.http.assets -import java.util.concurrent._ - import akka.NotUsed import akka.http.scaladsl.marshalling.{ToResponseMarshallable, ToResponseMarshaller} import akka.http.scaladsl.model.headers.Accept import akka.http.scaladsl.server.Route import akka.stream.scaladsl.Source import cats.data.Validated -import cats.instances.either._ -import cats.instances.list._ -import cats.syntax.alternative._ -import cats.syntax.either._ -import cats.syntax.traverse._ +import cats.instances.either.* +import cats.instances.list.* +import cats.syntax.alternative.* +import cats.syntax.either.* +import cats.syntax.traverse.* import com.wavesplatform.account.Address import com.wavesplatform.api.common.{CommonAccountsApi, CommonAssetsApi} -import com.wavesplatform.api.http.ApiError._ -import com.wavesplatform.api.http._ +import com.wavesplatform.api.http.* +import com.wavesplatform.api.http.ApiError.* import com.wavesplatform.api.http.assets.AssetsApiRoute.DistributionParams -import com.wavesplatform.api.http.requests._ +import com.wavesplatform.api.http.requests.* import com.wavesplatform.common.state.ByteStr import com.wavesplatform.lang.ValidationError import com.wavesplatform.network.TransactionPublisher @@ -26,25 +24,26 @@ import com.wavesplatform.settings.RestAPISettings import com.wavesplatform.state.{AssetDescription, AssetScriptInfo, Blockchain} import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.EthereumTransaction.Invocation -import com.wavesplatform.transaction.{EthereumTransaction, TransactionFactory} import com.wavesplatform.transaction.TxValidationError.GenericError import com.wavesplatform.transaction.assets.IssueTransaction import com.wavesplatform.transaction.assets.exchange.Order -import com.wavesplatform.transaction.assets.exchange.OrderJson._ +import com.wavesplatform.transaction.assets.exchange.OrderJson.* import com.wavesplatform.transaction.smart.{InvokeExpressionTransaction, InvokeScriptTransaction} +import com.wavesplatform.transaction.{EthereumTransaction, TransactionFactory} import com.wavesplatform.utils.Time import com.wavesplatform.wallet.Wallet import io.netty.util.concurrent.DefaultThreadFactory import monix.execution.Scheduler -import play.api.libs.json._ +import play.api.libs.json.* +import java.util.concurrent.* import scala.concurrent.Future case class AssetsApiRoute( settings: RestAPISettings, wallet: Wallet, transactionPublisher: TransactionPublisher, - blockchain: Blockchain, + blockchain: () => Blockchain, time: Time, commonAccountApi: CommonAccountsApi, commonAssetsApi: CommonAssetsApi, @@ -161,8 +160,8 @@ case class AssetsApiRoute( Json.obj("assetId" -> asset) } - /** - * @param assets Some(assets) for specific asset balances, None for a full portfolio + /** @param assets + * Some(assets) for specific asset balances, None for a full portfolio */ def balances(address: Address, assets: Option[Seq[IssuedAsset]] = None): Route = extractScheduler { implicit s => implicit val jsonStreamingSupport: ToResponseMarshaller[Source[JsObject, NotUsed]] = @@ -171,7 +170,7 @@ case class AssetsApiRoute( val assetBalances = assets match { case Some(assets) => Source(assets) - .map(asset => asset -> blockchain.balance(address, asset)) + .map(asset => asset -> blockchain().balance(address, asset)) case None => Source @@ -179,9 +178,8 @@ case class AssetsApiRoute( .mapConcat(identity) } - val jsonStream = assetBalances.map { - case (assetId, balance) => - fullAssetInfoJson(assetId) ++ Json.obj("balance" -> balance) + val jsonStream = assetBalances.map { case (assetId, balance) => + fullAssetInfoJson(assetId) ++ Json.obj("balance" -> balance) } complete(jsonStream) @@ -206,7 +204,7 @@ case class AssetsApiRoute( } def balanceDistribution(assetId: IssuedAsset): Route = - balanceDistribution(assetId, blockchain.height, Int.MaxValue, None) { l => + balanceDistribution(assetId, blockchain().height, Int.MaxValue, None) { l => Json.toJson(l.map { case (a, b) => a.toString -> b }.toMap) } @@ -214,7 +212,7 @@ case class AssetsApiRoute( optionalHeaderValueByType(Accept) { accept => val paramsEi: Either[ValidationError, DistributionParams] = AssetsApiRoute - .validateDistributionParams(blockchain, heightParam, limitParam, settings.distributionAddressLimit, afterParam, maxDistributionDepth) + .validateDistributionParams(blockchain(), heightParam, limitParam, settings.distributionAddressLimit, afterParam, maxDistributionDepth) paramsEi match { case Right((height, limit, after)) => @@ -222,12 +220,11 @@ case class AssetsApiRoute( Json.obj( "hasNext" -> (l.length == limit), "lastItem" -> l.lastOption.map(_._1), - "items" -> Json.toJson(l.map { - case (a, b) => - a.toString -> accept.fold[JsValue](JsNumber(b)) { - case a if a.mediaRanges.exists(CustomJson.acceptsNumbersAsStrings) => JsString(b.toString) - case _ => JsNumber(b) - } + "items" -> Json.toJson(l.map { case (a, b) => + a.toString -> accept.fold[JsValue](JsNumber(b)) { + case a if a.mediaRanges.exists(CustomJson.acceptsNumbersAsStrings) => JsString(b.toString) + case _ => JsNumber(b) + } }.toMap) ) } @@ -255,7 +252,7 @@ case class AssetsApiRoute( if (limit > settings.transactionsByAddressLimit) complete(TooBigArrayAllocation) else extractScheduler { implicit sc => - import cats.syntax.either._ + import cats.syntax.either.* implicit val jsonStreamingSupport: ToResponseMarshaller[Source[JsValue, NotUsed]] = jsonStreamMarshaller() complete { Source @@ -267,11 +264,10 @@ case class AssetsApiRoute( .runToFuture ) .mapConcat(identity) - .map { - case (assetId, assetDesc) => - AssetsApiRoute - .jsonDetails(blockchain)(assetId, assetDesc, full = true) - .valueOr(err => throw new IllegalArgumentException(err)) + .map { case (assetId, assetDesc) => + AssetsApiRoute + .jsonDetails(blockchain())(assetId, assetDesc, full = true) + .valueOr(err => throw new IllegalArgumentException(err)) } } } @@ -281,13 +277,14 @@ case class AssetsApiRoute( Json.obj( "address" -> address, "assetId" -> assetId.id.toString, - "balance" -> JsNumber(BigDecimal(blockchain.balance(address, assetId))) + "balance" -> JsNumber(BigDecimal(blockchain().balance(address, assetId))) ) private def assetDetails(assetId: IssuedAsset, full: Boolean): Either[ApiError, JsObject] = { + val bc = blockchain() for { - description <- blockchain.assetDescription(assetId).toRight(AssetDoesNotExist(assetId)) - result <- AssetsApiRoute.jsonDetails(blockchain)(assetId, description, full).leftMap(CustomValidationError(_)) + description <- bc.assetDescription(assetId).toRight(AssetDoesNotExist(assetId)) + result <- AssetsApiRoute.jsonDetails(bc)(assetId, description, full).leftMap(CustomValidationError(_)) } yield result } } @@ -353,11 +350,11 @@ object AssetsApiRoute { .toRight("Failed to find issue/invokeScript/invokeExpression transaction by ID") (txm, tx) = tt ts <- (tx match { - case tx: IssueTransaction => Some(tx.timestamp) - case tx: InvokeScriptTransaction => Some(tx.timestamp) - case tx: InvokeExpressionTransaction => Some(tx.timestamp) + case tx: IssueTransaction => Some(tx.timestamp) + case tx: InvokeScriptTransaction => Some(tx.timestamp) + case tx: InvokeExpressionTransaction => Some(tx.timestamp) case tx @ EthereumTransaction(_: Invocation, _, _, _) => Some(tx.timestamp) - case _ => None + case _ => None }).toRight("No issue/invokeScript/invokeExpression transaction found with the given asset ID") } yield (ts, txm.height) @@ -385,13 +382,12 @@ object AssetsApiRoute { case sponsorship => JsNumber(sponsorship) }), "originTransactionId" -> JsString(description.originTransactionId.toString) - ) ++ script.toSeq.map { - case AssetScriptInfo(script, complexity) => - "scriptDetails" -> Json.obj( - "scriptComplexity" -> JsNumber(BigDecimal(complexity)), - "script" -> JsString(script.bytes().base64), - "scriptText" -> JsString(script.expr.toString) // [WAIT] JsString(Script.decompile(script)) - ) + ) ++ script.toSeq.map { case AssetScriptInfo(script, complexity) => + "scriptDetails" -> Json.obj( + "scriptComplexity" -> JsNumber(BigDecimal(complexity)), + "script" -> JsString(script.bytes().base64), + "scriptText" -> JsString(script.expr.toString) // [WAIT] JsString(Script.decompile(script)) + ) } ) } diff --git a/node/src/main/scala/com/wavesplatform/api/http/eth/EthRpcRoute.scala b/node/src/main/scala/com/wavesplatform/api/http/eth/EthRpcRoute.scala index f2d1b6c3ed3..d2123ae0ac8 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/eth/EthRpcRoute.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/eth/EthRpcRoute.scala @@ -1,7 +1,5 @@ package com.wavesplatform.api.http.eth -import java.math.BigInteger - import akka.http.scaladsl.server.* import cats.data.Validated import cats.instances.vector.* @@ -26,18 +24,20 @@ import org.web3j.crypto.* import play.api.libs.json.* import play.api.libs.json.Json.JsValueWrapper +import java.math.BigInteger import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.jdk.CollectionConverters.* -class EthRpcRoute(blockchain: Blockchain, transactionsApi: CommonTransactionsApi, time: Time) extends ApiRoute { +case class EthRpcRoute(blockchain: () => Blockchain, transactionsApi: CommonTransactionsApi, time: Time) extends ApiRoute { val route: Route = pathPrefix("eth") { (path("assets") & anyParam("id", nonEmpty = true, limit = 100).massValidateEthereumIds) { erc20Ids => + val bc = blockchain() val results = erc20Ids .map(id => id -> (for { - wavesId <- blockchain.resolveERC20Address(ERC20Address(id)) - assetDesc <- blockchain.assetDescription(wavesId) + wavesId <- bc.resolveERC20Address(ERC20Address(id)) + assetDesc <- bc.assetDescription(wavesId) } yield (wavesId, assetDesc)) ) .map { case (id, assetOpt) => Validated.fromOption(assetOpt, EthEncoding.toHexString(id.arr)).toValidatedNel } @@ -50,12 +50,13 @@ class EthRpcRoute(blockchain: Blockchain, transactionsApi: CommonTransactionsApi case Validated.Valid(assets) => val jsons = for { (assetId, desc) <- assets - } yield AssetsApiRoute.jsonDetails(blockchain)(assetId, desc, full = false) + } yield AssetsApiRoute.jsonDetails(bc)(assetId, desc, full = false) complete(jsons.sequence.leftMap(CustomValidationError(_)).map(JsArray(_))) // TODO: Only first error is displayed } } ~ (get & path("abi" / AddrSegment)) { addr => - complete(blockchain.accountScript(addr).map(as => ABIConverter(as.script).jsonABI)) + complete(blockchain().accountScript(addr).map(as => ABIConverter(as.script).jsonABI)) } ~ (pathEndOrSingleSlash & post & entity(as[JsObject])) { jso => + val bc = blockchain() val id = (jso \ "id").get val params = (jso \ "params").asOpt[IndexedSeq[JsValue]].getOrElse(Nil) lazy val param1 = params.head @@ -63,9 +64,9 @@ class EthRpcRoute(blockchain: Blockchain, transactionsApi: CommonTransactionsApi (jso \ "method").as[String] match { case "eth_chainId" | "net_version" => - resp(id, quantity(blockchain.settings.addressSchemeCharacter.toInt)) + resp(id, quantity(bc.settings.addressSchemeCharacter.toInt)) case "eth_blockNumber" => - resp(id, quantity(blockchain.height)) + resp(id, quantity(bc.height)) case "eth_getTransactionCount" => resp(id, quantity(time.getTimestamp())) case "eth_getBlockByNumber" => @@ -81,7 +82,7 @@ class EthRpcRoute(blockchain: Blockchain, transactionsApi: CommonTransactionsApi resp( id, - blockchain.heightOf(blockId).flatMap(blockchain.blockHeader).fold[JsValue](JsNull) { _ => + bc.heightOf(blockId).flatMap(bc.blockHeader).fold[JsValue](JsNull) { _ => Json.obj( "baseFeePerGas" -> "0x0" ) @@ -92,7 +93,7 @@ class EthRpcRoute(blockchain: Blockchain, transactionsApi: CommonTransactionsApi resp( id, toHexString( - BigInt(blockchain.balance(address)) * EthereumTransaction.AmountMultiplier + BigInt(bc.balance(address)) * EthereumTransaction.AmountMultiplier ) ) case "eth_sendRawTransaction" => @@ -123,7 +124,7 @@ class EthRpcRoute(blockchain: Blockchain, transactionsApi: CommonTransactionsApi Json.obj( "transactionHash" -> toHexString(tm.transaction.id().arr), "transactionIndex" -> "0x1", - "blockHash" -> toHexString(blockchain.lastBlockId.get.arr), + "blockHash" -> toHexString(bc.lastBlockId.get.arr), "blockNumber" -> toHexString(BigInteger.valueOf(tm.height)), "from" -> toHexString(tx.senderAddress().publicKeyHash), "to" -> tx.underlying.getTo, @@ -154,7 +155,7 @@ class EthRpcRoute(blockchain: Blockchain, transactionsApi: CommonTransactionsApi id, encodeResponse( new Uint256( - assetId(contractAddress).fold(0L)(ia => blockchain.balance(Address.fromHexString(dataString.takeRight(40)), ia)) + assetId(contractAddress).fold(0L)(ia => bc.balance(Address.fromHexString(dataString.takeRight(40)), ia)) ) ) ) @@ -191,7 +192,7 @@ class EthRpcRoute(blockchain: Blockchain, transactionsApi: CommonTransactionsApi resp(id, toHexString(EthereumTransaction.GasPrice)) case "eth_getCode" => val address = Address.fromHexString(param1Str) - resp(id, if (blockchain.hasDApp(address)) "0xff" else "0x") + resp(id, if (bc.hasDApp(address)) "0xff" else "0x") case _ => log.trace(s"Unexpected call: ${Json.stringify(jso)}") complete(Json.obj()) @@ -207,10 +208,10 @@ class EthRpcRoute(blockchain: Blockchain, transactionsApi: CommonTransactionsApi private[this] def resp(id: JsValue, resp: Future[JsValueWrapper]) = complete(resp.map(r => Json.obj("id" -> id, "jsonrpc" -> "2.0", "result" -> r))) private[this] def assetDescription(contractAddress: String) = - assetId(contractAddress).flatMap(blockchain.assetDescription) + assetId(contractAddress).flatMap(blockchain().assetDescription) private[this] def assetId(contractAddress: String): Option[IssuedAsset] = - blockchain.resolveERC20Address(ERC20Address(ByteStr(toBytes(contractAddress)))) + blockchain().resolveERC20Address(ERC20Address(ByteStr(toBytes(contractAddress)))) private[this] def encodeResponse(values: Type*): String = "0x" + FunctionEncoder.encodeConstructor(values.map(Type.unwrap).asJava) diff --git a/node/src/main/scala/com/wavesplatform/api/http/leasing/LeaseApiRoute.scala b/node/src/main/scala/com/wavesplatform/api/http/leasing/LeaseApiRoute.scala index ff914447cfa..2d7fe7b9d45 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/leasing/LeaseApiRoute.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/leasing/LeaseApiRoute.scala @@ -2,31 +2,29 @@ package com.wavesplatform.api.http.leasing import akka.http.scaladsl.server.Route import com.wavesplatform.api.common.{CommonAccountsApi, LeaseInfo} -import com.wavesplatform.api.http.{BroadcastRoute, _} -import com.wavesplatform.api.http.requests.{LeaseCancelRequest, LeaseRequest} +import com.wavesplatform.api.http.* import com.wavesplatform.api.http.ApiError.{InvalidIds, TooBigArrayAllocation, TransactionDoesNotExist} +import com.wavesplatform.api.http.requests.{LeaseCancelRequest, LeaseRequest} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.Base58 import com.wavesplatform.network.TransactionPublisher import com.wavesplatform.settings.RestAPISettings -import com.wavesplatform.state.Blockchain -import com.wavesplatform.transaction._ +import com.wavesplatform.transaction.* import com.wavesplatform.utils.Time import com.wavesplatform.wallet.Wallet +import play.api.libs.json.* import play.api.libs.json.JsonConfiguration.Aux -import play.api.libs.json._ case class LeaseApiRoute( settings: RestAPISettings, wallet: Wallet, - blockchain: Blockchain, transactionPublisher: TransactionPublisher, time: Time, commonAccountApi: CommonAccountsApi ) extends ApiRoute with BroadcastRoute with AuthRoute { - import LeaseApiRoute._ + import LeaseApiRoute.* override val route: Route = pathPrefix("leasing") { active ~ deprecatedRoute @@ -68,18 +66,17 @@ case class LeaseApiRoute( } private[this] def leasingInfosMap(ids: Iterable[String]): Either[InvalidIds, Map[String, LeaseInfo]] = { - val infos = ids.map( - id => - (for { - id <- Base58.tryDecodeWithLimit(id).toOption - li <- commonAccountApi.leaseInfo(ByteStr(id)) - } yield li).toRight(id) + val infos = ids.map(id => + (for { + id <- Base58.tryDecodeWithLimit(id).toOption + li <- commonAccountApi.leaseInfo(ByteStr(id)) + } yield li).toRight(id) ) val failed = infos.flatMap(_.left.toOption) if (failed.isEmpty) { - Right(infos.collect { - case Right(li) => li.id.toString -> li + Right(infos.collect { case Right(li) => + li.id.toString -> li }.toMap) } else { Left(InvalidIds(failed.toVector)) diff --git a/node/src/test/scala/com/wavesplatform/api/common/CommonAccountApiSpec.scala b/node/src/test/scala/com/wavesplatform/api/common/CommonAccountApiSpec.scala index 1755ff45d25..f16bc6e5cac 100644 --- a/node/src/test/scala/com/wavesplatform/api/common/CommonAccountApiSpec.scala +++ b/node/src/test/scala/com/wavesplatform/api/common/CommonAccountApiSpec.scala @@ -50,7 +50,7 @@ class CommonAccountApiSpec extends FreeSpec with WithDomain with BlocksTransacti ) ) ) { d => - val commonAccountsApi = CommonAccountsApi(() => d.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), d.db, d.blockchainUpdater) + val commonAccountsApi = CommonAccountsApi(() => d.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), d.db, () => d.blockchainUpdater) def dataList(): Set[DataEntry[_]] = commonAccountsApi.dataStream(acc.toAddress, None).toListL.runSyncUnsafe().toSet d.appendBlock(block1) @@ -87,7 +87,7 @@ class CommonAccountApiSpec extends FreeSpec with WithDomain with BlocksTransacti forAll(preconditions) { case (acc, block1, mb1, block2, mb2) => withDomain(domainSettingsWithFS(TestFunctionalitySettings.withFeatures(BlockchainFeatures.NG, BlockchainFeatures.DataTransaction))) { d => - val commonAccountsApi = CommonAccountsApi(() => d.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), d.db, d.blockchainUpdater) + val commonAccountsApi = CommonAccountsApi(() => d.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), d.db, () => d.blockchainUpdater) def dataList(): Set[DataEntry[_]] = commonAccountsApi.dataStream(acc.toAddress, Some("test_.*")).toListL.runSyncUnsafe().toSet d.appendBlock(block1) @@ -161,7 +161,7 @@ class CommonAccountApiSpec extends FreeSpec with WithDomain with BlocksTransacti invoke ) - val api = CommonAccountsApi(() => Diff.empty, d.db, d.blockchain) + val api = CommonAccountsApi(() => Diff.empty, d.db, () => d.blockchain) val leaseId = Lease.calculateId( Lease( Recipient.Address(ByteStr(TxHelpers.defaultAddress.bytes)), diff --git a/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala b/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala index 49c01ac161c..d0d8a6df586 100644 --- a/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala @@ -25,7 +25,7 @@ class EthRpcRouteSpec extends RouteSpec("/eth") with WithDomain with EthHelpers Post( routePath(""), Json.obj("method" -> method, "params" -> Json.arr(params*), "id" -> "test") - ) ~> new EthRpcRoute(d.blockchain, d.commonApi.transactions, ntpTime).route ~> check(body) + ) ~> EthRpcRoute(() => d.blockchain, d.commonApi.transactions, ntpTime).route ~> check(body) } "eth_chainId" in withDomain(DefaultWavesSettings) { d => @@ -195,7 +195,7 @@ class EthRpcRouteSpec extends RouteSpec("/eth") with WithDomain with EthHelpers val issue3 = d.blockchain.accountData(randomKP.toAddress, "assetId").get.asInstanceOf[BinaryDataEntry].value - new EthRpcRoute(d.blockchain, d.commonApi.transactions, ntpTime).route + EthRpcRoute(() => d.blockchain, d.commonApi.transactions, ntpTime).route .anyParamTest(routePath("/assets"), "id")( EthEncoding.toHexString(issue1.id().arr.take(20)), EthEncoding.toHexString(issue2.id().arr.take(20)), diff --git a/node/src/test/scala/com/wavesplatform/api/http/CustomJsonMarshallerSpec.scala b/node/src/test/scala/com/wavesplatform/api/http/CustomJsonMarshallerSpec.scala index 7ba49e0e540..723581086e6 100644 --- a/node/src/test/scala/com/wavesplatform/api/http/CustomJsonMarshallerSpec.scala +++ b/node/src/test/scala/com/wavesplatform/api/http/CustomJsonMarshallerSpec.scala @@ -60,7 +60,7 @@ class CustomJsonMarshallerSpec } private val transactionsRoute = - TransactionsApiRoute(restAPISettings, transactionsApi, testWallet, blockchain, () => utx.size, publisher, ntpTime).route + TransactionsApiRoute(restAPISettings, transactionsApi, testWallet, () => blockchain, () => utx.size, publisher, ntpTime).route property("/transactions/info/{id}") { forAll(leaseGen) { lt => @@ -99,7 +99,7 @@ class CustomJsonMarshallerSpec pending // todo: fix when distributions/portfolio become testable } - private val assetsRoute = AssetsApiRoute(restAPISettings, testWallet, publisher, blockchain, ntpTime, accountsApi, assetsApi, 1000).route + private val assetsRoute = AssetsApiRoute(restAPISettings, testWallet, publisher, () => blockchain, ntpTime, accountsApi, assetsApi, 1000).route property("/assets/{assetId}/distribution/{height}/limit/{limit}") { pending // todo: fix when distributions/portfolio become testable diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index 850a961f279..1f99e1a617e 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -93,7 +93,7 @@ case class Domain( lazy val transactions: CommonTransactionsApi = CommonTransactionsApi( blockchainUpdater.bestLiquidDiff.map(diff => Height(blockchainUpdater.height) -> diff), db, - blockchain, + () => blockchain, utxPool, tx => Future.successful(utxPool.putIfNew(tx)), Application.loadBlockAt(db, blockchain) @@ -392,7 +392,7 @@ case class Domain( val transactionsApi: CommonTransactionsApi = CommonTransactionsApi( blockchainUpdater.bestLiquidDiff.map(Height(blockchainUpdater.height) -> _), db, - blockchain, + () => blockchain, utxPool, _ => Future.successful(TracedResult(Right(true))), h => blocksApi.blockAtHeight(h) @@ -401,13 +401,13 @@ case class Domain( val accountsApi: CommonAccountsApi = CommonAccountsApi( () => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, - utxPool.priorityPool.optimisticRead(CompositeBlockchain(blockchainUpdater, utxPool.priorityPool.validPriorityDiffs))(_ => true) + () => utxPool.priorityPool.optimisticRead(CompositeBlockchain(blockchainUpdater, utxPool.priorityPool.validPriorityDiffs))(_ => true) ) val assetsApi: CommonAssetsApi = CommonAssetsApi( () => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, - blockchain + () => blockchain ) } diff --git a/node/src/test/scala/com/wavesplatform/http/AddressRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/AddressRouteSpec.scala index fa71c1745ba..c2201e741d5 100644 --- a/node/src/test/scala/com/wavesplatform/http/AddressRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/AddressRouteSpec.scala @@ -55,7 +55,7 @@ class AddressRouteSpec extends RouteSpec("/addresses") with PathMockFactory with private val addressApiRoute: AddressApiRoute = AddressApiRoute( restAPISettings, wallet, - blockchain, + () => blockchain, utxPoolSynchronizer, new TestTime, Schedulers.timeBoundedFixedPool( @@ -78,7 +78,7 @@ class AddressRouteSpec extends RouteSpec("/addresses") with PathMockFactory with routePath("/balance/{address}/{confirmations}") in withDomain(balances = Seq(AddrWithBalance(TxHelpers.defaultAddress))) { d => val route = addressApiRoute - .copy(blockchain = d.blockchainUpdater, commonAccountsApi = CommonAccountsApi(() => d.liquidDiff, d.db, d.blockchainUpdater)) + .copy(blockchain = () => d.blockchainUpdater, commonAccountsApi = CommonAccountsApi(() => d.liquidDiff, d.db, () => d.blockchainUpdater)) .route val address = TxHelpers.signer(1).toAddress @@ -351,7 +351,7 @@ class AddressRouteSpec extends RouteSpec("/addresses") with PathMockFactory with routePath(s"/scriptInfo/ after ${BlockchainFeatures.SynchronousCalls}") in { val blockchain = stub[Blockchain]("blockchain") - val route = seal(addressApiRoute.copy(blockchain = blockchain).route) + val route = seal(addressApiRoute.copy(blockchain = () => blockchain).route) (() => blockchain.activatedFeatures).when().returning(Map(BlockchainFeatures.SynchronousCalls.id -> 0)) val script = ExprScript(TRUE).explicitGet() @@ -405,7 +405,7 @@ class AddressRouteSpec extends RouteSpec("/addresses") with PathMockFactory with val route = addressApiRoute - .copy(blockchain = d.blockchainUpdater, commonAccountsApi = CommonAccountsApi(() => d.liquidDiff, d.db, d.blockchainUpdater)) + .copy(blockchain = () => d.blockchainUpdater, commonAccountsApi = CommonAccountsApi(() => d.liquidDiff, d.db, () => d.blockchainUpdater)) .route val requestBody = Json.obj("keys" -> Seq("test")) diff --git a/node/src/test/scala/com/wavesplatform/http/AliasBroadcastRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/AliasBroadcastRouteSpec.scala index 4c2794d3721..c3d282b57af 100644 --- a/node/src/test/scala/com/wavesplatform/http/AliasBroadcastRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/AliasBroadcastRouteSpec.scala @@ -17,7 +17,7 @@ import play.api.libs.json.Json.* class AliasBroadcastRouteSpec extends RouteSpec("/alias/broadcast/") with RequestGen with PathMockFactory with RestAPISettingsHelper { private[this] val utxPoolSynchronizer = DummyTransactionPublisher.rejecting(tx => TransactionValidationError(GenericError("foo"), tx)) - val route = AliasApiRoute(restAPISettings, stub[CommonTransactionsApi], stub[Wallet], utxPoolSynchronizer, stub[Time], stub[Blockchain]).route + val route = AliasApiRoute(restAPISettings, stub[CommonTransactionsApi], stub[Wallet], utxPoolSynchronizer, stub[Time], () => stub[Blockchain]).route "returns StateCheckFiled" - { diff --git a/node/src/test/scala/com/wavesplatform/http/AssetsBroadcastRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/AssetsBroadcastRouteSpec.scala index 8e842dc5dc8..9919d4f6802 100644 --- a/node/src/test/scala/com/wavesplatform/http/AssetsBroadcastRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/AssetsBroadcastRouteSpec.scala @@ -27,7 +27,7 @@ class AssetsBroadcastRouteSpec extends RouteSpec("/assets/broadcast/") with Requ restAPISettings, stub[Wallet], DummyTransactionPublisher.rejecting(tx => TransactionValidationError(GenericError("foo"), tx)), - stub[Blockchain], + () => stub[Blockchain], stub[Time], stub[CommonAccountsApi], stub[CommonAssetsApi], @@ -171,7 +171,7 @@ class AssetsBroadcastRouteSpec extends RouteSpec("/assets/broadcast/") with Requ restAPISettings, stub[Wallet], DummyTransactionPublisher.accepting, - stub[Blockchain], + () => stub[Blockchain], stub[Time], stub[CommonAccountsApi], stub[CommonAssetsApi], diff --git a/node/src/test/scala/com/wavesplatform/http/AssetsRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/AssetsRouteSpec.scala index 859fddc4aa4..496df904432 100644 --- a/node/src/test/scala/com/wavesplatform/http/AssetsRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/AssetsRouteSpec.scala @@ -45,7 +45,7 @@ class AssetsRouteSpec extends RouteSpec("/assets") with Eventually with RestAPIS restAPISettings, testWallet, DummyTransactionPublisher.accepting, - d.blockchain, + () => d.blockchain, TestTime(), d.accountsApi, d.assetsApi, diff --git a/node/src/test/scala/com/wavesplatform/http/LeaseBroadcastRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/LeaseBroadcastRouteSpec.scala index 0e6c9e76340..04a206826d1 100644 --- a/node/src/test/scala/com/wavesplatform/http/LeaseBroadcastRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/LeaseBroadcastRouteSpec.scala @@ -4,7 +4,6 @@ import com.wavesplatform.RequestGen import com.wavesplatform.api.common.CommonAccountsApi import com.wavesplatform.api.http.ApiError.* import com.wavesplatform.api.http.leasing.LeaseApiRoute -import com.wavesplatform.state.Blockchain import com.wavesplatform.state.diffs.TransactionDiffer.TransactionValidationError import com.wavesplatform.transaction.Transaction import com.wavesplatform.transaction.TxValidationError.GenericError @@ -19,7 +18,7 @@ import play.api.libs.json.Json.* class LeaseBroadcastRouteSpec extends RouteSpec("/leasing/broadcast/") with RequestGen with PathMockFactory with RestAPISettingsHelper { private[this] val publisher = DummyTransactionPublisher.rejecting(t => TransactionValidationError(GenericError("foo"), t)) - private[this] val route = LeaseApiRoute(restAPISettings, stub[Wallet], stub[Blockchain], publisher, stub[Time], stub[CommonAccountsApi]).route + private[this] val route = LeaseApiRoute(restAPISettings, stub[Wallet], publisher, stub[Time], stub[CommonAccountsApi]).route "returns StateCheckFailed" - { val vt = Table[String, G[_ <: Transaction], JsValue => JsValue]( diff --git a/node/src/test/scala/com/wavesplatform/http/LeaseRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/LeaseRouteSpec.scala index 700b1119a0d..8d07b2cd829 100644 --- a/node/src/test/scala/com/wavesplatform/http/LeaseRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/LeaseRouteSpec.scala @@ -49,10 +49,9 @@ class LeaseRouteSpec LeaseApiRoute( restAPISettings, testWallet, - domain.blockchain, (_, _) => Future.successful(TracedResult(Right(true))), ntpTime, - CommonAccountsApi(() => domain.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), domain.db, domain.blockchain) + CommonAccountsApi(() => domain.blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), domain.db, () => domain.blockchain) ) private def withRoute(balances: Seq[AddrWithBalance], settings: WavesSettings = mostRecent)(f: (Domain, Route) => Unit): Unit = @@ -519,7 +518,7 @@ class LeaseRouteSpec val blockchain = stub[Blockchain] val commonApi = stub[CommonAccountsApi] - val route = LeaseApiRoute(restAPISettings, stub[Wallet], blockchain, stub[TransactionPublisher], SystemTime, commonApi).route + val route = LeaseApiRoute(restAPISettings, stub[Wallet], stub[TransactionPublisher], SystemTime, commonApi).route val lease = TxHelpers.lease() val leaseCancel = TxHelpers.leaseCancel(lease.id()) diff --git a/node/src/test/scala/com/wavesplatform/http/ProtoVersionTransactionsSpec.scala b/node/src/test/scala/com/wavesplatform/http/ProtoVersionTransactionsSpec.scala index 673ffcb7f57..8ce6d63f047 100644 --- a/node/src/test/scala/com/wavesplatform/http/ProtoVersionTransactionsSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/ProtoVersionTransactionsSpec.scala @@ -46,7 +46,7 @@ class ProtoVersionTransactionsSpec extends RouteSpec("/transactions") with RestA private val transactionsApi = mock[CommonTransactionsApi] private val route: Route = - TransactionsApiRoute(restAPISettings, transactionsApi, testWallet, blockchain, () => utx.size, DummyTransactionPublisher.accepting, ntpTime).route + TransactionsApiRoute(restAPISettings, transactionsApi, testWallet, () => blockchain, () => utx.size, DummyTransactionPublisher.accepting, ntpTime).route "Proto transactions should be able to broadcast " - { "CreateAliasTransaction" in { diff --git a/node/src/test/scala/com/wavesplatform/http/SpentComplexitySpec.scala b/node/src/test/scala/com/wavesplatform/http/SpentComplexitySpec.scala index 988045e1310..fc8520acd38 100644 --- a/node/src/test/scala/com/wavesplatform/http/SpentComplexitySpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/SpentComplexitySpec.scala @@ -64,7 +64,7 @@ class SpentComplexitySpec private def route(d: Domain) = seal( - TransactionsApiRoute(restAPISettings, d.transactionsApi, testWallet, d.blockchain, () => 0, DummyTransactionPublisher.accepting, ntpTime).route + TransactionsApiRoute(restAPISettings, d.transactionsApi, testWallet, () => d.blockchain, () => 0, DummyTransactionPublisher.accepting, ntpTime).route ) "Invocation" - { diff --git a/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala b/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala index 3ac5c4ac4b4..8960c318d66 100644 --- a/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala @@ -38,7 +38,7 @@ class TransactionBroadcastSpec restAPISettings, stub[CommonTransactionsApi], stub[Wallet], - blockchain, + () => blockchain, mockFunction[Int], transactionPublisher, testTime @@ -60,7 +60,7 @@ class TransactionBroadcastSpec val transactionPublisher = blockchain.stub.transactionPublisher(testTime) - val route = transactionsApiRoute.copy(blockchain = blockchain, transactionPublisher = transactionPublisher).route + val route = transactionsApiRoute.copy(blockchain = () => blockchain, transactionPublisher = transactionPublisher).route val transaction = TxHelpers.exchange( ethBuyOrder, @@ -269,7 +269,7 @@ class TransactionBroadcastSpec (blockchain.hasAccountScript _).when(*).returns(true) } val publisher = createTxPublisherStub(blockchain) - val route = transactionsApiRoute.copy(blockchain = blockchain, transactionPublisher = publisher).route + val route = transactionsApiRoute.copy(blockchain = () => blockchain, transactionPublisher = publisher).route Post(routePath("/broadcast?trace=true"), invoke.json()) ~> route ~> check { responseAs[JsObject] should matchJson( diff --git a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala index 11654f246f5..b746f2dcb37 100644 --- a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala @@ -67,7 +67,7 @@ class TransactionsRouteSpec restAPISettings, addressTransactions, testWallet, - blockchain, + () => blockchain, utxPoolSize, utxPoolSynchronizer, testTime @@ -123,7 +123,7 @@ class TransactionsRouteSpec restAPISettings, d.commonApi.transactions, testWallet, - d.blockchain, + () => d.blockchain, () => 0, (t, _) => d.commonApi.transactions.broadcastTransaction(t), ntpTime @@ -397,7 +397,7 @@ class TransactionsRouteSpec ) ) - val route = seal(transactionsApiRoute.copy(blockchain = blockchain, commonApi = transactionsApi).route) + val route = seal(transactionsApiRoute.copy(blockchain = () => blockchain, commonApi = transactionsApi).route) Get(routePath(s"/info/${transaction.id()}")) ~> route ~> check { responseAs[JsObject] should matchJson(s"""{ | "type" : 18, @@ -451,7 +451,7 @@ class TransactionsRouteSpec ) ) - val route = seal(transactionsApiRoute.copy(blockchain = blockchain, commonApi = transactionsApi).route) + val route = seal(transactionsApiRoute.copy(blockchain = () => blockchain, commonApi = transactionsApi).route) Get(routePath(s"/info/${transaction.id()}")) ~> route ~> check { responseAs[JsObject] should matchJson(s"""{ | "type" : 18, @@ -510,7 +510,7 @@ class TransactionsRouteSpec Some(LeaseDetails(lease.sender, lease.recipient, lease.amount.value, LeaseDetails.Status.Cancelled(2, Some(leaseCancel.id())), lease.id(), 1)) ) - val route = transactionsApiRoute.copy(blockchain = blockchain, commonApi = transactionsApi).route + val route = transactionsApiRoute.copy(blockchain = () => blockchain, commonApi = transactionsApi).route Get(routePath(s"/info/${leaseCancel.id()}")) ~> route ~> check { val json = responseAs[JsObject] json shouldBe Json.parse(s"""{ diff --git a/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala b/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala index 33fc9b7a737..a14e8c89a00 100644 --- a/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala +++ b/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala @@ -114,7 +114,7 @@ class EvaluatedPBSerializationTest restAPISettings, d.transactionsApi, d.wallet, - d.blockchain, + () => d.blockchain, () => d.utxPool.size, (_, _) => Future.successful(TracedResult(Right(true))), ntpTime diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala index 71798f7c5a2..91cf148610d 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala @@ -94,7 +94,7 @@ class BalancesV4Test extends PropSpec with WithState { rideV4Activated ) { case (d, s) => - val apiBalance = com.wavesplatform.api.common.CommonAccountsApi(() => d, db, s).balanceDetails(acc1.toAddress).explicitGet() + val apiBalance = com.wavesplatform.api.common.CommonAccountsApi(() => d, db, () => s).balanceDetails(acc1.toAddress).explicitGet() val data = d.accountData(dapp.toAddress) data.data("available") shouldBe IntegerDataEntry("available", apiBalance.available) apiBalance.available shouldBe 16 * Constants.UnitsInWave From e2903d2fe176bf1e95f7f364530a359488d484f9 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 23 Aug 2022 14:45:08 +0300 Subject: [PATCH 08/28] Add test for account script --- .../mining/DiscardedMicroBlocksDiffTest.scala | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 414314e7b3a..61c3c5808ac 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -3,6 +3,8 @@ package com.wavesplatform.mining import com.wavesplatform.block.Block.ProtoBlockVersion import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance +import com.wavesplatform.lang.directives.values.V6 +import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.state.IntegerDataEntry import com.wavesplatform.state.appender.BlockAppender import com.wavesplatform.test.DomainPresets.RideV6 @@ -92,4 +94,37 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { data() shouldBe Some(1) } } + + property("consistent interim account script") { + val waitInterimState = new CountDownLatch(1) + val getOutOfInterimState = new CountDownLatch(1) + withDomain( + RideV6, + AddrWithBalance.enoughBalances(defaultSigner), + beforeSetPriorityDiffs = { () => + waitInterimState.countDown() + getOutOfInterimState.await() + } + ) { d => + val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ + + val setScriptTx = setScript(defaultSigner, TestCompiler(V6).compileExpression("true")) + def hasScript() = d.accountsApi.script(defaultAddress).nonEmpty + + val previousBlockId = d.appendBlock().id() + d.appendMicroBlock(setScriptTx) + hasScript() shouldBe true + + val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) + val appendKeyBlock = appendBlock(keyBlock).runToFuture + + waitInterimState.await() + val check = Future(hasScript() shouldBe true) + getOutOfInterimState.countDown() + + Await.result(check, Inf) + Await.result(appendKeyBlock, Inf) + hasScript() shouldBe true + } + } } From 1a603ec0201095595ac2c868140d1efef67c8762 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 23 Aug 2022 15:08:06 +0300 Subject: [PATCH 09/28] Remove unspecified test from EvaluatorV2Test --- .../lang/evaluator/EvaluatorV2Test.scala | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala index d94e28fb3e6..ce64e2b170b 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala @@ -1243,15 +1243,4 @@ class EvaluatorV2Test extends PropSpec with Inside { evalOld(script2, 100) shouldBe ((TRUE, "true", 5)) // 3 conditions + ref twice evalNew(script2, 100) shouldBe ((TRUE, "true", 3)) // 3 function call } - - property("test") { - val expr = BLOCK( - LET("n", CONST_LONG(26)), - LET_BLOCK( - LET("complexInt1", CONST_LONG(1973)), - LET_BLOCK(LET("res", CONST_STRING("Unit").explicitGet()), IF(CONST_BOOLEAN(true), REF("nil"), FUNCTION_CALL(Native(2), List(CONST_STRING("Strict value is not equal to itself.").explicitGet())))) - ) - ) - evalOld(expr, 1) shouldBe 1 - } } From 8b1a39aa6810ecbc4f2b2029bc621c1b6176ca4e Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 23 Aug 2022 16:52:26 +0300 Subject: [PATCH 10/28] Correct use of lambdas --- .../scala/com/wavesplatform/Application.scala | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/Application.scala b/node/src/main/scala/com/wavesplatform/Application.scala index 228a72e6d2b..9786c2a10c6 100644 --- a/node/src/main/scala/com/wavesplatform/Application.scala +++ b/node/src/main/scala/com/wavesplatform/Application.scala @@ -227,7 +227,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con override val transactionsApi: CommonTransactionsApi = CommonTransactionsApi( blockchainUpdater.bestLiquidDiff.map(diff => Height(blockchainUpdater.height) -> diff), db, - blockchainWithDiscardedDiffs, + () => blockchainWithDiscardedDiffs(), utxStorage, tx => transactionPublisher.validateAndBroadcast(tx, None), loadBlockAt(db, blockchainUpdater) @@ -235,8 +235,9 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con override val blocksApi: CommonBlocksApi = CommonBlocksApi(blockchainUpdater, loadBlockMetaAt(db, blockchainUpdater), loadBlockInfoAt(db, blockchainUpdater)) override val accountsApi: CommonAccountsApi = - CommonAccountsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainWithDiscardedDiffs) - override val assetsApi: CommonAssetsApi = CommonAssetsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, blockchainWithDiscardedDiffs) + CommonAccountsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, () => blockchainWithDiscardedDiffs()) + override val assetsApi: CommonAssetsApi = + CommonAssetsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, () => blockchainWithDiscardedDiffs()) } extensions = settings.extensions.map { extensionClassName => @@ -340,14 +341,14 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con ) val apiRoutes = Seq( - EthRpcRoute(blockchainWithDiscardedDiffs, extensionContext.transactionsApi, time), + EthRpcRoute(() => blockchainWithDiscardedDiffs(), extensionContext.transactionsApi, time), NodeApiRoute(settings.restAPISettings, blockchainUpdater, () => shutdown()), BlocksApiRoute(settings.restAPISettings, extensionContext.blocksApi, time), TransactionsApiRoute( settings.restAPISettings, extensionContext.transactionsApi, wallet, - blockchainWithDiscardedDiffs, + () => blockchainWithDiscardedDiffs(), () => utxStorage.size, transactionPublisher, time @@ -364,7 +365,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con AddressApiRoute( settings.restAPISettings, wallet, - blockchainWithDiscardedDiffs, + () => blockchainWithDiscardedDiffs(), transactionPublisher, time, limitedScheduler, @@ -397,7 +398,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con settings.restAPISettings, wallet, transactionPublisher, - blockchainWithDiscardedDiffs, + () => blockchainWithDiscardedDiffs(), time, extensionContext.accountsApi, extensionContext.assetsApi, @@ -405,7 +406,14 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con ), ActivationApiRoute(settings.restAPISettings, settings.featuresSettings, blockchainUpdater), LeaseApiRoute(settings.restAPISettings, wallet, transactionPublisher, time, extensionContext.accountsApi), - AliasApiRoute(settings.restAPISettings, extensionContext.transactionsApi, wallet, transactionPublisher, time, blockchainWithDiscardedDiffs), + AliasApiRoute( + settings.restAPISettings, + extensionContext.transactionsApi, + wallet, + transactionPublisher, + time, + () => blockchainWithDiscardedDiffs() + ), RewardApiRoute(blockchainUpdater) ) From edef565a7cd74f370659449d24531f1046c21dea Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 23 Aug 2022 23:25:59 +0300 Subject: [PATCH 11/28] Better naming --- .../mining/DiscardedMicroBlocksDiffTest.scala | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 61c3c5808ac..7fc41555b3b 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -21,13 +21,13 @@ import scala.concurrent.{Await, Future} class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { property("consistent interim balance") { Seq(true, false).foreach { useAsset => - val waitInterimState = new CountDownLatch(1) - val getOutOfInterimState = new CountDownLatch(1) + val waitInterimState = new CountDownLatch(1) + val endInterimState = new CountDownLatch(1) withDomain( RideV6, beforeSetPriorityDiffs = { () => waitInterimState.countDown() - getOutOfInterimState.await() + endInterimState.await() } ) { d => val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ @@ -53,7 +53,7 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { waitInterimState.await() val check = Future(balanceDiff() shouldBe 123) - getOutOfInterimState.countDown() + endInterimState.countDown() Await.result(check, Inf) Await.result(appendKeyBlock, Inf) @@ -63,14 +63,14 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { } property("consistent interim account data") { - val waitInterimState = new CountDownLatch(1) - val getOutOfInterimState = new CountDownLatch(1) + val waitInterimState = new CountDownLatch(1) + val endInterimState = new CountDownLatch(1) withDomain( RideV6, AddrWithBalance.enoughBalances(defaultSigner), beforeSetPriorityDiffs = { () => waitInterimState.countDown() - getOutOfInterimState.await() + endInterimState.await() } ) { d => val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ @@ -87,7 +87,7 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { waitInterimState.await() val check = Future(data() shouldBe Some(1)) - getOutOfInterimState.countDown() + endInterimState.countDown() Await.result(check, Inf) Await.result(appendKeyBlock, Inf) @@ -96,14 +96,14 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { } property("consistent interim account script") { - val waitInterimState = new CountDownLatch(1) - val getOutOfInterimState = new CountDownLatch(1) + val waitInterimState = new CountDownLatch(1) + val endInterimState = new CountDownLatch(1) withDomain( RideV6, AddrWithBalance.enoughBalances(defaultSigner), beforeSetPriorityDiffs = { () => waitInterimState.countDown() - getOutOfInterimState.await() + endInterimState.await() } ) { d => val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ @@ -120,7 +120,7 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { waitInterimState.await() val check = Future(hasScript() shouldBe true) - getOutOfInterimState.countDown() + endInterimState.countDown() Await.result(check, Inf) Await.result(appendKeyBlock, Inf) From f2e217ba43dfcd33d15a194f1b6306a79ec1566f Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 23 Aug 2022 23:26:26 +0300 Subject: [PATCH 12/28] Add test for active leases --- .../mining/DiscardedMicroBlocksDiffTest.scala | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 7fc41555b3b..769d480e9f8 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -127,4 +127,37 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { hasScript() shouldBe true } } + + property("consistent interim active leases") { + val waitInterimState = new CountDownLatch(1) + val endInterimState = new CountDownLatch(1) + withDomain( + RideV6, + AddrWithBalance.enoughBalances(defaultSigner), + beforeSetPriorityDiffs = { () => + waitInterimState.countDown() + endInterimState.await() + } + ) { d => + val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ + + val leaseTx = lease() + def activeLeases() = Await.result(d.accountsApi.activeLeases(defaultAddress).toListL.runToFuture, Inf) + + val previousBlockId = d.appendBlock().id() + d.appendMicroBlock(leaseTx) + activeLeases().size shouldBe 1 + + val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) + val appendKeyBlock = appendBlock(keyBlock).runToFuture + + waitInterimState.await() + val check = Future(activeLeases().size shouldBe 1) + endInterimState.countDown() + + Await.result(check, Inf) + Await.result(appendKeyBlock, Inf) + activeLeases().size shouldBe 1 + } + } } From 6ea11b1bd35e1f2c5410f1d3a5edda4e4385869d Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 23 Aug 2022 23:58:07 +0300 Subject: [PATCH 13/28] Unify tests --- .../mining/DiscardedMicroBlocksDiffTest.scala | 155 +++++------------- 1 file changed, 41 insertions(+), 114 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 769d480e9f8..094baa16fc5 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -3,132 +3,64 @@ package com.wavesplatform.mining import com.wavesplatform.block.Block.ProtoBlockVersion import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance +import com.wavesplatform.history.Domain import com.wavesplatform.lang.directives.values.V6 import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.state.IntegerDataEntry import com.wavesplatform.state.appender.BlockAppender import com.wavesplatform.test.DomainPresets.RideV6 import com.wavesplatform.test.{PropSpec, TestTime} -import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} +import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.Transaction import com.wavesplatform.transaction.TxHelpers.* import monix.execution.Scheduler import monix.execution.Scheduler.Implicits.global +import org.scalatest.Assertion import java.util.concurrent.CountDownLatch import scala.concurrent.duration.Duration.Inf import scala.concurrent.{Await, Future} class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { - property("consistent interim balance") { - Seq(true, false).foreach { useAsset => - val waitInterimState = new CountDownLatch(1) - val endInterimState = new CountDownLatch(1) - withDomain( - RideV6, - beforeSetPriorityDiffs = { () => - waitInterimState.countDown() - endInterimState.await() - } - ) { d => - val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ - - val recipient = secondAddress - val issueTx = issue() - val issuedAsset = IssuedAsset(issueTx.id()) - val asset = if (useAsset) issuedAsset else Waves - val startBalance = d.balance(recipient, asset) - val transferTx = transfer(amount = 123, asset = asset) - def balanceDiff() = - if (useAsset) - d.accountsApi.assetBalance(recipient, issuedAsset) - startBalance - else - d.accountsApi.balance(recipient) - startBalance - - val previousBlockId = d.appendBlock(issueTx).id() - d.appendMicroBlock(transferTx) - balanceDiff() shouldBe 123 - - val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) - val appendKeyBlock = appendBlock(keyBlock).runToFuture - - waitInterimState.await() - val check = Future(balanceDiff() shouldBe 123) - endInterimState.countDown() - - Await.result(check, Inf) - Await.result(appendKeyBlock, Inf) - balanceDiff() shouldBe 123 - } - } + property("interim waves balance") { + testInterimState( + transfer(amount = 123), + _.accountsApi.balance(secondAddress) shouldBe 123 + ) } - property("consistent interim account data") { - val waitInterimState = new CountDownLatch(1) - val endInterimState = new CountDownLatch(1) - withDomain( - RideV6, - AddrWithBalance.enoughBalances(defaultSigner), - beforeSetPriorityDiffs = { () => - waitInterimState.countDown() - endInterimState.await() - } - ) { d => - val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ - - val dataTx = dataEntry(defaultSigner, IntegerDataEntry("key", 1)) - def data() = d.accountsApi.data(defaultAddress, "key").map(_.value) - - val previousBlockId = d.appendBlock().id() - d.appendMicroBlock(dataTx) - data() shouldBe Some(1) - - val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) - val appendKeyBlock = appendBlock(keyBlock).runToFuture - - waitInterimState.await() - val check = Future(data() shouldBe Some(1)) - endInterimState.countDown() - - Await.result(check, Inf) - Await.result(appendKeyBlock, Inf) - data() shouldBe Some(1) - } + property("interim asset balance") { + val issueTx = issue() + val issuedAsset = IssuedAsset(issueTx.id()) + testInterimState( + transfer(amount = 123, asset = issuedAsset), + _.accountsApi.assetBalance(secondAddress, issuedAsset) shouldBe 123, + preconditions = Seq(issueTx) + ) } - property("consistent interim account script") { - val waitInterimState = new CountDownLatch(1) - val endInterimState = new CountDownLatch(1) - withDomain( - RideV6, - AddrWithBalance.enoughBalances(defaultSigner), - beforeSetPriorityDiffs = { () => - waitInterimState.countDown() - endInterimState.await() - } - ) { d => - val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ - - val setScriptTx = setScript(defaultSigner, TestCompiler(V6).compileExpression("true")) - def hasScript() = d.accountsApi.script(defaultAddress).nonEmpty - - val previousBlockId = d.appendBlock().id() - d.appendMicroBlock(setScriptTx) - hasScript() shouldBe true - - val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) - val appendKeyBlock = appendBlock(keyBlock).runToFuture + property("interim account data") { + testInterimState( + dataEntry(defaultSigner, IntegerDataEntry("key", 1)), + _.accountsApi.data(defaultAddress, "key").map(_.value) shouldBe Some(1) + ) + } - waitInterimState.await() - val check = Future(hasScript() shouldBe true) - endInterimState.countDown() + property("interim account script") { + testInterimState( + setScript(defaultSigner, TestCompiler(V6).compileExpression("true")), + _.accountsApi.script(defaultAddress) should not be empty + ) + } - Await.result(check, Inf) - Await.result(appendKeyBlock, Inf) - hasScript() shouldBe true - } + property("interim active leases") { + testInterimState( + lease(), + d => Await.result(d.accountsApi.activeLeases(defaultAddress).toListL.runToFuture, Inf) should not be empty + ) } - property("consistent interim active leases") { + private def testInterimState(tx: Transaction, assert: Domain => Assertion, preconditions: Seq[Transaction] = Nil): Assertion = { val waitInterimState = new CountDownLatch(1) val endInterimState = new CountDownLatch(1) withDomain( @@ -139,25 +71,20 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { endInterimState.await() } ) { d => - val appendBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false) _ - - val leaseTx = lease() - def activeLeases() = Await.result(d.accountsApi.activeLeases(defaultAddress).toListL.runToFuture, Inf) - - val previousBlockId = d.appendBlock().id() - d.appendMicroBlock(leaseTx) - activeLeases().size shouldBe 1 + val previousBlockId = d.appendBlock(preconditions: _*).id() + d.appendMicroBlock(tx) + assert(d) val keyBlock = d.createBlock(ProtoBlockVersion, Nil, Some(previousBlockId)) - val appendKeyBlock = appendBlock(keyBlock).runToFuture + val appendKeyBlock = BlockAppender(d.blockchain, TestTime(), d.utxPool, d.posSelector, Scheduler.global, verify = false)(keyBlock).runToFuture waitInterimState.await() - val check = Future(activeLeases().size shouldBe 1) + val check = Future(assert(d)) endInterimState.countDown() Await.result(check, Inf) Await.result(appendKeyBlock, Inf) - activeLeases().size shouldBe 1 + assert(d) } } } From 4ea47a1e4e8beca04347ab9e52fdf7b5e4afdf3c Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 00:44:02 +0300 Subject: [PATCH 14/28] Combine discarded mircoblocks diff with liquid diff --- .../scala/com/wavesplatform/Application.scala | 20 +++-- .../state/reader/CompositeBlockchain.scala | 8 -- .../com/wavesplatform/utx/UtxPoolImpl.scala | 5 +- .../api/eth/EthRpcRouteSpec.scala | 4 +- .../com/wavesplatform/history/Domain.scala | 85 +++++++++---------- .../http/TransactionsRouteSpec.scala | 4 +- .../EthereumTransactionStateChangesSpec.scala | 8 +- 7 files changed, 63 insertions(+), 71 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/Application.scala b/node/src/main/scala/com/wavesplatform/Application.scala index 9786c2a10c6..15da763afd9 100644 --- a/node/src/main/scala/com/wavesplatform/Application.scala +++ b/node/src/main/scala/com/wavesplatform/Application.scala @@ -17,6 +17,7 @@ import com.wavesplatform.api.http.assets.AssetsApiRoute import com.wavesplatform.api.http.eth.EthRpcRoute import com.wavesplatform.api.http.leasing.LeaseApiRoute import com.wavesplatform.common.state.ByteStr +import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.consensus.PoSSelector import com.wavesplatform.database.{DBExt, Keys, openDB} import com.wavesplatform.events.{BlockchainUpdateTriggers, UtxEvent} @@ -126,8 +127,15 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con val utxStorage = new UtxPoolImpl(time, blockchainUpdater, settings.utxSettings, settings.minerSettings.enable, utxEvents.onNext) maybeUtx = Some(utxStorage) - def blockchainWithDiscardedDiffs(): CompositeBlockchain = - utxStorage.priorityPool.optimisticRead(CompositeBlockchain(blockchainUpdater, utxStorage.priorityPool.validPriorityDiffs))(_ => true) + def blockchainWithDiscardedDiffs(): CompositeBlockchain = { + def blockchain = CompositeBlockchain(blockchainUpdater, utxStorage.discardedMicrosDiff()) + utxStorage.priorityPool.optimisticRead(blockchain)(_ => true) + } + + def totalLiquidDiff(): Diff = { + val liquidDiff = blockchainUpdater.bestLiquidDiff.getOrElse(Diff()) + liquidDiff.combineE(utxStorage.discardedMicrosDiff()).explicitGet() + } val timer = new HashedWheelTimer() val utxSynchronizerLogger = LoggerFacade(LoggerFactory.getLogger(classOf[TransactionPublisher])) @@ -225,7 +233,7 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con override def utxEvents: Observable[UtxEvent] = app.utxEvents override val transactionsApi: CommonTransactionsApi = CommonTransactionsApi( - blockchainUpdater.bestLiquidDiff.map(diff => Height(blockchainUpdater.height) -> diff), + Some(Height(blockchainUpdater.height) -> totalLiquidDiff()), db, () => blockchainWithDiscardedDiffs(), utxStorage, @@ -234,10 +242,8 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con ) override val blocksApi: CommonBlocksApi = CommonBlocksApi(blockchainUpdater, loadBlockMetaAt(db, blockchainUpdater), loadBlockInfoAt(db, blockchainUpdater)) - override val accountsApi: CommonAccountsApi = - CommonAccountsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, () => blockchainWithDiscardedDiffs()) - override val assetsApi: CommonAssetsApi = - CommonAssetsApi(() => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), db, () => blockchainWithDiscardedDiffs()) + override val accountsApi: CommonAccountsApi = CommonAccountsApi(() => totalLiquidDiff(), db, () => blockchainWithDiscardedDiffs()) + override val assetsApi: CommonAssetsApi = CommonAssetsApi(() => totalLiquidDiff(), db, () => blockchainWithDiscardedDiffs()) } extensions = settings.extensions.map { extensionClassName => diff --git a/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala b/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala index d075cc84d10..a6006ad155d 100644 --- a/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala @@ -185,14 +185,6 @@ object CompositeBlockchain { case _ => new CompositeBlockchain(inner, Some(diff)) } - def apply(inner: Blockchain, diff: Seq[Diff]): CompositeBlockchain = { - val acc = inner match { - case cb: CompositeBlockchain => cb - case _ => new CompositeBlockchain(inner, None) - } - diff.foldLeft(acc)(_.appendDiff(_)) - } - def apply( inner: Blockchain, diff: Diff, diff --git a/node/src/main/scala/com/wavesplatform/utx/UtxPoolImpl.scala b/node/src/main/scala/com/wavesplatform/utx/UtxPoolImpl.scala index cad0bf2b50b..e0cdc51f9a2 100644 --- a/node/src/main/scala/com/wavesplatform/utx/UtxPoolImpl.scala +++ b/node/src/main/scala/com/wavesplatform/utx/UtxPoolImpl.scala @@ -3,10 +3,10 @@ package com.wavesplatform.utx import java.time.Duration import java.time.temporal.ChronoUnit import java.util.concurrent.ConcurrentHashMap - import com.wavesplatform.ResponsivenessLogs import com.wavesplatform.account.Address import com.wavesplatform.common.state.ByteStr +import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.consensus.TransactionsOrdering import com.wavesplatform.events.UtxEvent import com.wavesplatform.lang.ValidationError @@ -174,6 +174,9 @@ class UtxPoolImpl( def resetPriorityPool(): Unit = priorityPool.setPriorityDiffs(Seq.empty) + def discardedMicrosDiff(): Diff = + priorityPool.validPriorityDiffs.fold(Diff())(_.combineE(_).explicitGet()) + private[this] def removeFromOrdPool(txId: ByteStr): Option[Transaction] = { for (tx <- Option(transactions.remove(txId))) yield { PoolMetrics.removeTransaction(tx) diff --git a/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala b/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala index d0d8a6df586..ff4403b5b89 100644 --- a/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala @@ -25,7 +25,7 @@ class EthRpcRouteSpec extends RouteSpec("/eth") with WithDomain with EthHelpers Post( routePath(""), Json.obj("method" -> method, "params" -> Json.arr(params*), "id" -> "test") - ) ~> EthRpcRoute(() => d.blockchain, d.commonApi.transactions, ntpTime).route ~> check(body) + ) ~> EthRpcRoute(() => d.blockchain, d.transactionsApi, ntpTime).route ~> check(body) } "eth_chainId" in withDomain(DefaultWavesSettings) { d => @@ -195,7 +195,7 @@ class EthRpcRouteSpec extends RouteSpec("/eth") with WithDomain with EthHelpers val issue3 = d.blockchain.accountData(randomKP.toAddress, "assetId").get.asInstanceOf[BinaryDataEntry].value - EthRpcRoute(() => d.blockchain, d.commonApi.transactions, ntpTime).route + EthRpcRoute(() => d.blockchain, d.transactionsApi, ntpTime).route .anyParamTest(routePath("/assets"), "id")( EthEncoding.toHexString(issue1.id().arr.take(20)), EthEncoding.toHexString(issue2.id().arr.take(20)), diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index 1f99e1a617e..0b47c240302 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -24,7 +24,7 @@ import com.wavesplatform.transaction.smart.script.trace.TracedResult import com.wavesplatform.transaction.{BlockchainUpdater, *} import com.wavesplatform.utils.{EthEncoding, SystemTime} import com.wavesplatform.wallet.Wallet -import com.wavesplatform.{Application, TestValues, database} +import com.wavesplatform.{TestValues, database} import monix.execution.Scheduler.Implicits.global import org.iq80.leveldb.DB import org.scalatest.matchers.should.Matchers.* @@ -61,6 +61,11 @@ case class Domain( lazy val utxPool = new TestUtxPool(SystemTime, blockchain, settings.utxSettings, settings.minerSettings.enable, beforeSetPriorityDiffs) lazy val wallet: Wallet = Wallet(settings.walletSettings.copy(file = None)) + def blockchainWithDiscardedDiffs(): CompositeBlockchain = { + def bc = CompositeBlockchain(blockchain, utxPool.discardedMicrosDiff()) + utxPool.priorityPool.optimisticRead(bc)(_ => true) + } + object commonApi { /** @return @@ -69,7 +74,7 @@ case class Domain( * [[com.wavesplatform.state.diffs.FeeValidation#getMinFee(com.wavesplatform.state.Blockchain, com.wavesplatform.transaction.Transaction)]] */ def calculateFee(tx: Transaction): (Asset, Long, Long) = - transactions.calculateFee(tx).explicitGet() + transactionsApi.calculateFee(tx).explicitGet() def calculateWavesFee(tx: Transaction): Long = { val (Waves, _, feeInWaves) = calculateFee(tx): @unchecked @@ -77,7 +82,7 @@ case class Domain( } def transactionMeta(transactionId: ByteStr): TransactionMeta = - transactions + transactionsApi .transactionById(transactionId) .getOrElse(throw new NoSuchElementException(s"No meta for $transactionId")) @@ -88,16 +93,7 @@ case class Domain( } def addressTransactions(address: Address): Seq[TransactionMeta] = - transactions.transactionsByAddress(address, None, Set.empty, None).toListL.runSyncUnsafe() - - lazy val transactions: CommonTransactionsApi = CommonTransactionsApi( - blockchainUpdater.bestLiquidDiff.map(diff => Height(blockchainUpdater.height) -> diff), - db, - () => blockchain, - utxPool, - tx => Future.successful(utxPool.putIfNew(tx)), - Application.loadBlockAt(db, blockchain) - ) + transactionsApi.transactionsByAddress(address, None, Set.empty, None).toListL.runSyncUnsafe() } def liquidState: Option[NgState] = { @@ -140,8 +136,10 @@ case class Domain( .getOrElse(TestBlock.create(Nil)) } - def liquidDiff: Diff = - blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty) + def liquidDiff: Diff = { + val liquidDiff = blockchainUpdater.bestLiquidDiff.getOrElse(Diff()) + liquidDiff.combineE(utxPool.discardedMicrosDiff()).explicitGet() + } def microBlocks: Vector[MicroBlock] = blockchain.microblockIds.reverseIterator.flatMap(blockchain.microBlock).to(Vector) @@ -164,24 +162,28 @@ case class Domain( def nftList(address: Address): Seq[(IssuedAsset, AssetDescription)] = db.withResource { resource => AddressPortfolio - .nftIterator(resource, address, blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), None, blockchainUpdater.assetDescription) + .nftIterator(resource, address, liquidDiff, None, blockchainUpdater.assetDescription) .toSeq } def addressTransactions(address: Address, from: Option[ByteStr] = None): Seq[(Height, Transaction)] = - AddressTransactions - .allAddressTransactions( - db, - blockchainUpdater.bestLiquidDiff.map(diff => Height(blockchainUpdater.height) -> diff), - address, - None, - Set.empty, - from - ) - .map { case (m, tx) => m.height -> tx } - .toSeq - - def portfolio(address: Address): Seq[(IssuedAsset, Long)] = Domain.portfolio(address, db, blockchainUpdater) + transactionsApi + .transactionsByAddress(address, None, Set(), from) + .map(meta => (meta.height, meta.transaction)) + .toListL + .runSyncUnsafe() + + def portfolio(address: Address): Seq[(IssuedAsset, Long)] = + db.withResource { resource => + AddressPortfolio + .assetBalanceIterator( + resource, + address, + liquidDiff, + id => blockchainUpdater.assetDescription(id).exists(!_.nft) + ) + .toSeq + } def appendAndAssertSucceed(txs: Transaction*): Block = { val block = createBlock(Block.PlainBlockVersion, txs) @@ -390,24 +392,24 @@ case class Domain( } val transactionsApi: CommonTransactionsApi = CommonTransactionsApi( - blockchainUpdater.bestLiquidDiff.map(Height(blockchainUpdater.height) -> _), + Some(Height(blockchainUpdater.height) -> liquidDiff), db, - () => blockchain, + () => blockchainWithDiscardedDiffs(), utxPool, - _ => Future.successful(TracedResult(Right(true))), + tx => Future.successful(utxPool.putIfNew(tx)), h => blocksApi.blockAtHeight(h) ) val accountsApi: CommonAccountsApi = CommonAccountsApi( - () => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), + () => liquidDiff, db, - () => utxPool.priorityPool.optimisticRead(CompositeBlockchain(blockchainUpdater, utxPool.priorityPool.validPriorityDiffs))(_ => true) + () => blockchainWithDiscardedDiffs() ) val assetsApi: CommonAssetsApi = CommonAssetsApi( - () => blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), + () => liquidDiff, db, - () => blockchain + () => blockchainWithDiscardedDiffs() ) } @@ -416,15 +418,4 @@ object Domain { def processBlock(block: Block): Either[ValidationError, Seq[Diff]] = bcu.processBlock(block, block.header.generationSignature) } - - def portfolio(address: Address, db: DB, blockchainUpdater: BlockchainUpdaterImpl): Seq[(IssuedAsset, Long)] = db.withResource { resource => - AddressPortfolio - .assetBalanceIterator( - resource, - address, - blockchainUpdater.bestLiquidDiff.getOrElse(Diff.empty), - id => blockchainUpdater.assetDescription(id).exists(!_.nft) - ) - .toSeq - } } diff --git a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala index 2b19c64ea6a..c140c952259 100644 --- a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala @@ -121,11 +121,11 @@ class TransactionsRouteSpec seal( new TransactionsApiRoute( restAPISettings, - d.commonApi.transactions, + d.transactionsApi, testWallet, () => d.blockchain, () => 0, - (t, _) => d.commonApi.transactions.broadcastTransaction(t), + (t, _) => d.transactionsApi.broadcastTransaction(t), ntpTime ).route ) diff --git a/node/src/test/scala/com/wavesplatform/transaction/smart/EthereumTransactionStateChangesSpec.scala b/node/src/test/scala/com/wavesplatform/transaction/smart/EthereumTransactionStateChangesSpec.scala index 125b5e19bbb..6fa644963b5 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/smart/EthereumTransactionStateChangesSpec.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/smart/EthereumTransactionStateChangesSpec.scala @@ -77,7 +77,7 @@ class EthereumTransactionStateChangesSpec extends FlatSpec with WithDomain with d.appendBlock(invoke) d.liquidAndSolidAssert { () => - d.commonApi.transactions.transactionById(invoke.id()) match { + d.transactionsApi.transactionById(invoke.id()) match { case Some(meta: TransactionMeta.Ethereum) => assert(!meta.succeeded, "should fail") Json.toJson(meta.invokeScriptResult) should matchJson(""" @@ -130,7 +130,7 @@ class EthereumTransactionStateChangesSpec extends FlatSpec with WithDomain with d.liquidAndSolidAssert { () => d.blockchain.accountData(dApp.toAddress, "test") shouldBe Some(StringDataEntry("test", "foo")) - d.commonApi.transactions.transactionById(invoke.id()) match { + d.transactionsApi.transactionById(invoke.id()) match { case Some(meta: TransactionMeta.Ethereum) => assert(meta.succeeded, "should succeed") Json.toJson(meta.invokeScriptResult) should matchJson("""{ @@ -191,7 +191,7 @@ class EthereumTransactionStateChangesSpec extends FlatSpec with WithDomain with d.appendBlock(invoke) d.liquidAndSolidAssert { () => - d.commonApi.transactions.transactionById(invoke.id()) match { + d.transactionsApi.transactionById(invoke.id()) match { case Some(meta: TransactionMeta.Ethereum) => assert(!meta.succeeded, "should fail") Json.toJson(meta.invokeScriptResult) should matchJson(""" @@ -283,7 +283,7 @@ class EthereumTransactionStateChangesSpec extends FlatSpec with WithDomain with d.blockchain.accountData(dApp.toAddress, "test") shouldBe Some(StringDataEntry("test", "foo")) d.blockchain.accountData(nestedDApp.toAddress, "test1") shouldBe Some(StringDataEntry("test1", "bar")) - d.commonApi.transactions.transactionById(invoke.id()) match { + d.transactionsApi.transactionById(invoke.id()) match { case Some(meta: TransactionMeta.Ethereum) => assert(meta.succeeded, "should succeed") Json.toJson(meta.invokeScriptResult) should matchJson("""{ From d2c2d8bbe06b59f5eb61e8950bc6ca8ea1dd6659 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 11:33:37 +0300 Subject: [PATCH 15/28] Remove unused import --- .../scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala index ce64e2b170b..7f59414a923 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala @@ -7,7 +7,6 @@ import com.wavesplatform.lang.directives.DirectiveSet import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.utils.lazyContexts import com.wavesplatform.lang.v1.FunctionHeader -import com.wavesplatform.lang.v1.FunctionHeader.Native import com.wavesplatform.lang.v1.compiler.Terms.{CONST_LONG, *} import com.wavesplatform.lang.v1.compiler.{Decompiler, ExpressionCompiler} import com.wavesplatform.lang.v1.evaluator.{EvaluatorV2, FunctionIds} From a23fde7fda9a1d9818c248ce168fbdfa1a3dd105 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 12:11:20 +0300 Subject: [PATCH 16/28] Reduce code --- .../com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 094baa16fc5..6f6dab5735e 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -56,7 +56,7 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { property("interim active leases") { testInterimState( lease(), - d => Await.result(d.accountsApi.activeLeases(defaultAddress).toListL.runToFuture, Inf) should not be empty + _.accountsApi.activeLeases(defaultAddress).toListL.runSyncUnsafe() should not be empty ) } From 5eb7db62ce654e5c280a050e93f76d9272dec6f5 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 17:30:37 +0300 Subject: [PATCH 17/28] Use UTX priority pool lock for liquid diff --- node/src/main/scala/com/wavesplatform/Application.scala | 3 ++- node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala | 1 + node/src/test/scala/com/wavesplatform/history/Domain.scala | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/Application.scala b/node/src/main/scala/com/wavesplatform/Application.scala index 15da763afd9..187c4e9f9cb 100644 --- a/node/src/main/scala/com/wavesplatform/Application.scala +++ b/node/src/main/scala/com/wavesplatform/Application.scala @@ -134,7 +134,8 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con def totalLiquidDiff(): Diff = { val liquidDiff = blockchainUpdater.bestLiquidDiff.getOrElse(Diff()) - liquidDiff.combineE(utxStorage.discardedMicrosDiff()).explicitGet() + val totalDiff = liquidDiff.combineE(utxStorage.discardedMicrosDiff()).explicitGet() + utxStorage.priorityPool.optimisticRead(totalDiff)(_ => true) } val timer = new HashedWheelTimer() diff --git a/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala b/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala index d7fe32cf124..b77dcc8ff1a 100644 --- a/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala +++ b/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala @@ -6,6 +6,7 @@ import com.wavesplatform.utx.UtxPoolImpl class TestUtxPool(time: Time, blockchain: Blockchain, utxSettings: UtxSettings, isMiningEnabled: Boolean, beforeSetPriorityDiffs: () => Unit) extends UtxPoolImpl(time, blockchain, utxSettings, isMiningEnabled) { + override def setPriorityDiffs(discDiffs: Seq[Diff]): Unit = { beforeSetPriorityDiffs() super.setPriorityDiffs(discDiffs) diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index 0b47c240302..9d23c609dcb 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -138,7 +138,8 @@ case class Domain( def liquidDiff: Diff = { val liquidDiff = blockchainUpdater.bestLiquidDiff.getOrElse(Diff()) - liquidDiff.combineE(utxPool.discardedMicrosDiff()).explicitGet() + val totalDiff = liquidDiff.combineE(utxPool.discardedMicrosDiff()).explicitGet() + utxPool.priorityPool.optimisticRead(totalDiff)(_ => true) } def microBlocks: Vector[MicroBlock] = blockchain.microblockIds.reverseIterator.flatMap(blockchain.microBlock).to(Vector) From 509dfe9b4fa0e5b7d29189eaa033569aa41494ee Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 17:30:55 +0300 Subject: [PATCH 18/28] Test diff transaction --- .../mining/DiscardedMicroBlocksDiffTest.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 6f6dab5735e..68f1803d67b 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -12,6 +12,7 @@ import com.wavesplatform.test.DomainPresets.RideV6 import com.wavesplatform.test.{PropSpec, TestTime} import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.Transaction +import com.wavesplatform.transaction.TransactionType.{Lease, Transfer} import com.wavesplatform.transaction.TxHelpers.* import monix.execution.Scheduler import monix.execution.Scheduler.Implicits.global @@ -60,6 +61,18 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { ) } + property("interim transaction") { + val tx = transfer() + testInterimState( + tx, + _.transactionsApi.transactionsByAddress(defaultAddress, None, Set(Transfer)).toListL.runSyncUnsafe().map(_.transaction) shouldBe Seq(tx) + ) + testInterimState( + tx, + _.transactionsApi.transactionById(tx.id()).map(_.transaction) shouldBe Some(tx) + ) + } + private def testInterimState(tx: Transaction, assert: Domain => Assertion, preconditions: Seq[Transaction] = Nil): Assertion = { val waitInterimState = new CountDownLatch(1) val endInterimState = new CountDownLatch(1) From ec889357f78838c8953b86a6d024af0b5ab9ddfa Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 17:41:51 +0300 Subject: [PATCH 19/28] Test asset description --- .../mining/DiscardedMicroBlocksDiffTest.scala | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 68f1803d67b..b33771ce52a 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -12,7 +12,7 @@ import com.wavesplatform.test.DomainPresets.RideV6 import com.wavesplatform.test.{PropSpec, TestTime} import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.Transaction -import com.wavesplatform.transaction.TransactionType.{Lease, Transfer} +import com.wavesplatform.transaction.TransactionType.Transfer import com.wavesplatform.transaction.TxHelpers.* import monix.execution.Scheduler import monix.execution.Scheduler.Implicits.global @@ -31,11 +31,11 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { } property("interim asset balance") { - val issueTx = issue() - val issuedAsset = IssuedAsset(issueTx.id()) + val issueTx = issue() + val asset = IssuedAsset(issueTx.id()) testInterimState( - transfer(amount = 123, asset = issuedAsset), - _.accountsApi.assetBalance(secondAddress, issuedAsset) shouldBe 123, + transfer(amount = 123, asset = asset), + _.accountsApi.assetBalance(secondAddress, asset) shouldBe 123, preconditions = Seq(issueTx) ) } @@ -73,6 +73,20 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { ) } + property("interim asset description") { + val issueTx = issue() + val asset = IssuedAsset(issueTx.id()) + testInterimState( + issueTx, + _.assetsApi.description(asset) should not be empty + ) + testInterimState( + reissue(asset, amount = 1), + _.assetsApi.description(asset).get.totalVolume shouldBe issueTx.quantity.value + 1, + preconditions = Seq(issueTx) + ) + } + private def testInterimState(tx: Transaction, assert: Domain => Assertion, preconditions: Seq[Transaction] = Nil): Assertion = { val waitInterimState = new CountDownLatch(1) val endInterimState = new CountDownLatch(1) From 048ca9a6c736849459f4988cbab58e138b9610f1 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 17:50:49 +0300 Subject: [PATCH 20/28] Remove unused LeaseActionInfo --- node/src/main/scala/com/wavesplatform/state/Diff.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/state/Diff.scala b/node/src/main/scala/com/wavesplatform/state/Diff.scala index 1f197676119..ac442526f6b 100755 --- a/node/src/main/scala/com/wavesplatform/state/Diff.scala +++ b/node/src/main/scala/com/wavesplatform/state/Diff.scala @@ -145,8 +145,6 @@ case class NewTransactionInfo(transaction: Transaction, affected: Set[Address], case class NewAssetInfo(static: AssetStaticInfo, dynamic: AssetInfo, volume: AssetVolumeInfo) -case class LeaseActionInfo(invokeId: ByteStr, dAppPublicKey: PublicKey, recipient: AddressOrAlias, amount: Long) - case class Diff( transactions: collection.Map[ByteStr, NewTransactionInfo] = VectorMap.empty, portfolios: Map[Address, Portfolio] = Map.empty, From 239fa0b64b3d8537df3d71a4cef98a49e4e2fd45 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 18:07:55 +0300 Subject: [PATCH 21/28] More tests --- .../mining/DiscardedMicroBlocksDiffTest.scala | 51 ++++++++++++++++--- .../wavesplatform/transaction/TxHelpers.scala | 2 +- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index b33771ce52a..ce8c8d470b6 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -1,5 +1,6 @@ package com.wavesplatform.mining +import com.wavesplatform.account.Alias import com.wavesplatform.block.Block.ProtoBlockVersion import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance @@ -9,7 +10,7 @@ import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.state.IntegerDataEntry import com.wavesplatform.state.appender.BlockAppender import com.wavesplatform.test.DomainPresets.RideV6 -import com.wavesplatform.test.{PropSpec, TestTime} +import com.wavesplatform.test.{NumericExt, PropSpec, TestTime} import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.Transaction import com.wavesplatform.transaction.TransactionType.Transfer @@ -65,21 +66,25 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { val tx = transfer() testInterimState( tx, - _.transactionsApi.transactionsByAddress(defaultAddress, None, Set(Transfer)).toListL.runSyncUnsafe().map(_.transaction) shouldBe Seq(tx) - ) - testInterimState( - tx, - _.transactionsApi.transactionById(tx.id()).map(_.transaction) shouldBe Some(tx) + { d => + d.transactionsApi.transactionsByAddress(defaultAddress, None, Set(Transfer)).toListL.runSyncUnsafe().map(_.transaction) shouldBe Seq(tx) + d.transactionsApi.transactionById(tx.id()).map(_.transaction) shouldBe Some(tx) + } ) } - property("interim asset description") { + property("interim asset issue") { val issueTx = issue() val asset = IssuedAsset(issueTx.id()) testInterimState( issueTx, _.assetsApi.description(asset) should not be empty ) + } + + property("interim asset reissue") { + val issueTx = issue() + val asset = IssuedAsset(issueTx.id()) testInterimState( reissue(asset, amount = 1), _.assetsApi.description(asset).get.totalVolume shouldBe issueTx.quantity.value + 1, @@ -87,6 +92,38 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { ) } + property("interim asset script") { + val exprTrue = TestCompiler(V6).compileExpression("true") + val exprFalse = TestCompiler(V6).compileExpression("false") + val issueTx = issue(script = Some(exprTrue)) + val asset = IssuedAsset(issueTx.id()) + testInterimState( + setAssetScript(asset = asset, script = exprFalse, fee = 1.waves), + _.assetsApi.description(asset).flatMap(_.script).map(_.script) shouldBe Some(exprFalse), + preconditions = Seq(issueTx) + ) + } + + property("interim alias") { + testInterimState( + createAlias("alias"), + { d => + d.accountsApi.resolveAlias(Alias('T', "alias")) shouldBe Right(defaultAddress) + d.transactionsApi.aliasesOfAddress(defaultAddress).toListL.runSyncUnsafe().map(_._2.aliasName) shouldBe Seq("alias") + } + ) + } + + property("interim sponsorship") { + val issueTx = issue() + val asset = IssuedAsset(issueTx.id()) + testInterimState( + sponsor(asset, Some(123)), + _.assetsApi.description(asset).map(_.sponsorship) shouldBe Some(123), + preconditions = Seq(issueTx) + ) + } + private def testInterimState(tx: Transaction, assert: Domain => Assertion, preconditions: Seq[Transaction] = Nil): Assertion = { val waitInterimState = new CountDownLatch(1) val endInterimState = new CountDownLatch(1) diff --git a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala index fa536c87c30..54bbc831087 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala @@ -340,7 +340,7 @@ object TxHelpers { } def setAssetScript( - acc: KeyPair, + acc: KeyPair = defaultSigner, asset: IssuedAsset, script: Script, fee: Long = FeeConstants(TransactionType.SetAssetScript) * FeeUnit, From 6b2f7965aca8179b4626963797794d9c519ab659 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 19:51:43 +0300 Subject: [PATCH 22/28] Correct blocking liquid diff --- node/src/main/scala/com/wavesplatform/Application.scala | 4 ++-- node/src/test/scala/com/wavesplatform/history/Domain.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/Application.scala b/node/src/main/scala/com/wavesplatform/Application.scala index 187c4e9f9cb..2d5e6096112 100644 --- a/node/src/main/scala/com/wavesplatform/Application.scala +++ b/node/src/main/scala/com/wavesplatform/Application.scala @@ -133,8 +133,8 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con } def totalLiquidDiff(): Diff = { - val liquidDiff = blockchainUpdater.bestLiquidDiff.getOrElse(Diff()) - val totalDiff = liquidDiff.combineE(utxStorage.discardedMicrosDiff()).explicitGet() + def liquidDiff = blockchainUpdater.bestLiquidDiff.getOrElse(Diff()) + def totalDiff = liquidDiff.combineE(utxStorage.discardedMicrosDiff()).explicitGet() utxStorage.priorityPool.optimisticRead(totalDiff)(_ => true) } diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index 9d23c609dcb..d9a93541dc1 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -137,8 +137,8 @@ case class Domain( } def liquidDiff: Diff = { - val liquidDiff = blockchainUpdater.bestLiquidDiff.getOrElse(Diff()) - val totalDiff = liquidDiff.combineE(utxPool.discardedMicrosDiff()).explicitGet() + def liquidDiff = blockchainUpdater.bestLiquidDiff.getOrElse(Diff()) + def totalDiff = liquidDiff.combineE(utxPool.discardedMicrosDiff()).explicitGet() utxPool.priorityPool.optimisticRead(totalDiff)(_ => true) } From 14ce46bc694e9b9463c901ef05460997280b47e9 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 24 Aug 2022 23:27:39 +0300 Subject: [PATCH 23/28] Optimize imports --- node/src/main/scala/com/wavesplatform/state/Diff.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/main/scala/com/wavesplatform/state/Diff.scala b/node/src/main/scala/com/wavesplatform/state/Diff.scala index ac442526f6b..6fa63647d99 100755 --- a/node/src/main/scala/com/wavesplatform/state/Diff.scala +++ b/node/src/main/scala/com/wavesplatform/state/Diff.scala @@ -6,7 +6,7 @@ import cats.implicits.{catsSyntaxSemigroup, toFlatMapOps, toFunctorOps} import cats.kernel.{Monoid, Semigroup} import cats.syntax.either.* import com.google.protobuf.ByteString -import com.wavesplatform.account.{Address, AddressOrAlias, Alias, PublicKey} +import com.wavesplatform.account.{Address, Alias, PublicKey} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.database.protobuf.EthereumTransactionMeta import com.wavesplatform.features.BlockchainFeatures From dd7914c1075823aa89d60a60dbc6e2b7840587c2 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 20 Oct 2022 17:54:18 +0300 Subject: [PATCH 24/28] Fixes after merge --- .../scala/com/wavesplatform/api/http/eth/EthRpcRoute.scala | 4 ++-- .../scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/api/http/eth/EthRpcRoute.scala b/node/src/main/scala/com/wavesplatform/api/http/eth/EthRpcRoute.scala index aa4dffa4115..3899796eef0 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/eth/EthRpcRoute.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/eth/EthRpcRoute.scala @@ -71,7 +71,7 @@ case class EthRpcRoute(blockchain: () => Blockchain, transactionsApi: CommonTran extractParam1[String](jso) { str => val blockNumberOpt = str match { case "earliest" => Some(1).asRight - case "latest" => Some(blockchain.height).asRight + case "latest" => Some(bc.height).asRight case "pending" => None.asRight case _ => Try(Some(Integer.parseInt(str.drop(2), 16))) @@ -162,7 +162,7 @@ case class EthRpcRoute(blockchain: () => Blockchain, transactionsApi: CommonTran Json.obj( "hash" -> toHexString(tm.transaction.id().arr), "nonce" -> "0x1", - "blockHash" -> toHexString(blockchain.lastBlockId.get.arr), + "blockHash" -> toHexString(bc.lastBlockId.get.arr), "blockNumber" -> toHexString(BigInteger.valueOf(tm.height)), "transactionIndex" -> "0x1", "from" -> toHexString(tx.senderAddress().publicKeyHash), diff --git a/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala b/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala index e6a711047d3..887baa2e50f 100644 --- a/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/api/eth/EthRpcRouteSpec.scala @@ -324,13 +324,13 @@ class EthRpcRouteSpec extends RouteSpec("/eth") with WithDomain with EthHelpers "absence of id" in withDomain() { d => Post(routePath(""), Json.obj("method" -> "eth_chainId")) - ~> new EthRpcRoute(d.blockchain, d.commonApi.transactions, ntpTime).route + ~> EthRpcRoute(() => d.blockchain, d.transactionsApi, ntpTime).route ~> check { responseAs[JsObject] shouldBe Json.obj("id" -> null, "jsonrpc" -> "2.0", "result" -> "0x54") } } "absence of method" in withDomain() { d => Post(routePath(""), Json.obj()) - ~> new EthRpcRoute(d.blockchain, d.commonApi.transactions, ntpTime).route + ~> EthRpcRoute(() => d.blockchain, d.transactionsApi, ntpTime).route ~> check { responseAs[JsObject] shouldBe Json.obj() } } From fbc64814831df006d34744d384b0632921c15c21 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 20 Oct 2022 18:22:20 +0300 Subject: [PATCH 25/28] Add tests --- .../mining/DiscardedMicroBlocksDiffTest.scala | 40 ++++++++++++++++++- .../wavesplatform/transaction/TxHelpers.scala | 2 +- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index ce8c8d470b6..4ebe8b65b2e 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -7,8 +7,8 @@ import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.history.Domain import com.wavesplatform.lang.directives.values.V6 import com.wavesplatform.lang.v1.compiler.TestCompiler -import com.wavesplatform.state.IntegerDataEntry import com.wavesplatform.state.appender.BlockAppender +import com.wavesplatform.state.{EmptyDataEntry, IntegerDataEntry, StringDataEntry} import com.wavesplatform.test.DomainPresets.RideV6 import com.wavesplatform.test.{NumericExt, PropSpec, TestTime} import com.wavesplatform.transaction.Asset.IssuedAsset @@ -48,6 +48,14 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { ) } + property("interim account data delete") { + testInterimState( + dataEntry(defaultSigner, EmptyDataEntry("key")), + _.accountsApi.data(defaultAddress, "key") shouldBe None, + preconditions = Seq(dataEntry(defaultSigner, StringDataEntry("key", "value"))) + ) + } + property("interim account script") { testInterimState( setScript(defaultSigner, TestCompiler(V6).compileExpression("true")), @@ -62,6 +70,15 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { ) } + property("interim cancel leases") { + val leaseTx = lease() + testInterimState( + leaseCancel(leaseTx.id()), + _.accountsApi.activeLeases(defaultAddress).toListL.runSyncUnsafe() shouldBe empty, + Seq(leaseTx) + ) + } + property("interim transaction") { val tx = transfer() testInterimState( @@ -92,6 +109,16 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { ) } + property("interim asset burn") { + val issueTx = issue() + val asset = IssuedAsset(issueTx.id()) + testInterimState( + burn(asset, amount = 1), + _.assetsApi.description(asset).get.totalVolume shouldBe issueTx.quantity.value - 1, + preconditions = Seq(issueTx) + ) + } + property("interim asset script") { val exprTrue = TestCompiler(V6).compileExpression("true") val exprFalse = TestCompiler(V6).compileExpression("false") @@ -124,6 +151,17 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { ) } + property("interim sponsorship cancel") { + val issueTx = issue() + val asset = IssuedAsset(issueTx.id()) + val sponsorTx = sponsor(asset, Some(123)) + testInterimState( + sponsor(asset, None), + _.assetsApi.description(asset).map(_.sponsorship) shouldBe Some(0), + preconditions = Seq(issueTx, sponsorTx) + ) + } + private def testInterimState(tx: Transaction, assert: Domain => Assertion, preconditions: Seq[Transaction] = Nil): Assertion = { val waitInterimState = new CountDownLatch(1) val endInterimState = new CountDownLatch(1) diff --git a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala index f6b32556a21..8081e306811 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala @@ -139,7 +139,7 @@ object TxHelpers { .explicitGet() def dataEntry(account: KeyPair, value: DataEntry[?]): DataTransaction = - DataTransaction.selfSigned(TxVersion.V1, account, Seq(value), TestValues.fee * 3, timestamp).explicitGet() + DataTransaction.selfSigned(TxVersion.V2, account, Seq(value), TestValues.fee * 3, timestamp).explicitGet() def dataSingle(account: KeyPair = defaultSigner, key: String = "test", value: String = "test"): DataTransaction = data(account, Seq(StringDataEntry(key, value))) From 9f55303a271e8847f68cd5178f47c419de23884e Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 20 Oct 2022 18:23:50 +0300 Subject: [PATCH 26/28] Unify --- .../com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala index 4ebe8b65b2e..1f25b61dbc4 100644 --- a/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/DiscardedMicroBlocksDiffTest.scala @@ -75,7 +75,7 @@ class DiscardedMicroBlocksDiffTest extends PropSpec with WithDomain { testInterimState( leaseCancel(leaseTx.id()), _.accountsApi.activeLeases(defaultAddress).toListL.runSyncUnsafe() shouldBe empty, - Seq(leaseTx) + preconditions = Seq(leaseTx) ) } From cc1ccd815f77443b52f016b3962b868e8387b076 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Fri, 21 Oct 2022 13:48:26 +0300 Subject: [PATCH 27/28] Adapt tests --- .../src/test/scala/com/wavesplatform/state/RollbackSpec.scala | 2 +- .../test/scala/com/wavesplatform/transaction/TxHelpers.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/state/RollbackSpec.scala b/node/src/test/scala/com/wavesplatform/state/RollbackSpec.scala index cfb2b30c499..44b906eded4 100644 --- a/node/src/test/scala/com/wavesplatform/state/RollbackSpec.scala +++ b/node/src/test/scala/com/wavesplatform/state/RollbackSpec.scala @@ -374,7 +374,7 @@ class RollbackSpec extends FreeSpec with WithDomain { TestBlock.create( nextTs, genesisBlockId, - Seq(TxHelpers.dataEntry(sender, dataEntry)) + Seq(TxHelpers.dataEntry(sender, dataEntry, TxVersion.V1)) ) ) diff --git a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala index 8081e306811..7807cc84fb1 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala @@ -138,8 +138,8 @@ object TxHelpers { .selfSigned(version, sender, asset, amount, reissuable = reissuable, fee, timestamp, chainId) .explicitGet() - def dataEntry(account: KeyPair, value: DataEntry[?]): DataTransaction = - DataTransaction.selfSigned(TxVersion.V2, account, Seq(value), TestValues.fee * 3, timestamp).explicitGet() + def dataEntry(account: KeyPair, value: DataEntry[?], version: TxVersion = TxVersion.V2): DataTransaction = + DataTransaction.selfSigned(version, account, Seq(value), TestValues.fee * 3, timestamp).explicitGet() def dataSingle(account: KeyPair = defaultSigner, key: String = "test", value: String = "test"): DataTransaction = data(account, Seq(StringDataEntry(key, value))) From 557636fa5805a18f2d32f96a706141c54ec06d6c Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Fri, 21 Oct 2022 13:56:37 +0300 Subject: [PATCH 28/28] Fixes after merge --- .../test/scala/com/wavesplatform/db/TestUtxPool.scala | 10 ++++++++-- .../test/scala/com/wavesplatform/history/Domain.scala | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala b/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala index b77dcc8ff1a..4fb67c12883 100644 --- a/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala +++ b/node/src/test/scala/com/wavesplatform/db/TestUtxPool.scala @@ -4,8 +4,14 @@ import com.wavesplatform.state.{Blockchain, Diff} import com.wavesplatform.utils.Time import com.wavesplatform.utx.UtxPoolImpl -class TestUtxPool(time: Time, blockchain: Blockchain, utxSettings: UtxSettings, isMiningEnabled: Boolean, beforeSetPriorityDiffs: () => Unit) - extends UtxPoolImpl(time, blockchain, utxSettings, isMiningEnabled) { +class TestUtxPool( + time: Time, + blockchain: Blockchain, + utxSettings: UtxSettings, + maxTxErrorLogSize: Int, + isMiningEnabled: Boolean, + beforeSetPriorityDiffs: () => Unit +) extends UtxPoolImpl(time, blockchain, utxSettings, maxTxErrorLogSize, isMiningEnabled) { override def setPriorityDiffs(discDiffs: Seq[Diff]): Unit = { beforeSetPriorityDiffs() diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index 65a5f56c2c0..eef3a965b85 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -58,7 +58,7 @@ case class Domain( def createDiffE(tx: Transaction): Either[ValidationError, Diff] = transactionDiffer(tx).resultE def createDiff(tx: Transaction): Diff = createDiffE(tx).explicitGet() - lazy val utxPool = + lazy val utxPool = new TestUtxPool(SystemTime, blockchain, settings.utxSettings, settings.maxTxErrorLogSize, settings.minerSettings.enable, beforeSetPriorityDiffs) lazy val wallet: Wallet = Wallet(settings.walletSettings.copy(file = None))