From b9c794dd8b73c27214807050165195dcf3b714fb Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 7 Dec 2023 13:46:14 +0300 Subject: [PATCH] NODE-2632 Delayed Light Node block structure (#3917) --- .../api/grpc/test/AccountsApiGrpcSpec.scala | 3 +- .../api/grpc/test/BlocksApiGrpcSpec.scala | 4 +- .../grpc/test/TransactionsApiGrpcSpec.scala | 3 +- .../events/BlockchainUpdatesSpec.scala | 2 +- .../{ => lightnode}/BlockChallengeSuite.scala | 3 +- .../LightNodeBroadcastSuite.scala | 4 +- .../sync/lightnode/LightNodeMiningSuite.scala | 27 ++-- .../LightNodeRollbackSuite.scala | 4 +- node/src/main/resources/custom-defaults.conf | 4 + .../api/http/BlocksApiRoute.scala | 4 +- .../com/wavesplatform/crypto/package.scala | 3 +- .../mining/BlockChallenger.scala | 38 ++--- .../com/wavesplatform/mining/Miner.scala | 54 ++++--- .../microblocks/MicroBlockMinerImpl.scala | 2 +- .../settings/BlockchainSettings.scala | 3 +- .../com/wavesplatform/state/Blockchain.scala | 4 + .../state/appender/package.scala | 5 +- .../state/diffs/BlockDiffer.scala | 16 +- .../com/wavesplatform/db/WithState.scala | 12 +- .../com/wavesplatform/history/Domain.scala | 6 +- .../wavesplatform/mining/BlockV5Test.scala | 63 ++------ .../mining/LightNodeBlockFieldsTest.scala | 153 ++++++++++++++++++ .../com/wavesplatform/mining/package.scala | 48 +++++- .../BlockchainSettingsSpecification.scala | 2 + .../state/BlockChallengeTest.scala | 16 +- .../wavesplatform/state/LightNodeTest.scala | 8 +- .../state/diffs/BlockDifferTest.scala | 11 +- .../predef/TransactionBindingsTest.scala | 11 +- 28 files changed, 352 insertions(+), 161 deletions(-) rename node-it/src/test/scala/com/wavesplatform/it/sync/{ => lightnode}/BlockChallengeSuite.scala (97%) rename node-it/src/test/scala/com/wavesplatform/it/sync/{ => lightnode}/LightNodeBroadcastSuite.scala (97%) rename node-it/src/test/scala/com/wavesplatform/it/sync/{ => lightnode}/LightNodeRollbackSuite.scala (97%) create mode 100644 node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala diff --git a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/AccountsApiGrpcSpec.scala b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/AccountsApiGrpcSpec.scala index 3af310c397e..cbfe2c42682 100644 --- a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/AccountsApiGrpcSpec.scala +++ b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/AccountsApiGrpcSpec.scala @@ -19,6 +19,7 @@ import com.wavesplatform.transaction.TxHelpers import com.wavesplatform.utils.DiffMatchers import monix.execution.Scheduler.Implicits.global import org.scalatest.{Assertion, BeforeAndAfterAll} +import com.wavesplatform.test.DomainPresets.* import scala.concurrent.Await import scala.concurrent.duration.{DurationInt, FiniteDuration} @@ -186,7 +187,7 @@ class AccountsApiGrpcSpec extends FreeSpec with BeforeAndAfterAll with DiffMatch val sender = TxHelpers.signer(1) val challengedMiner = TxHelpers.signer(2) - withDomain(DomainPresets.TransactionStateSnapshot, balances = AddrWithBalance.enoughBalances(sender)) { d => + withDomain(TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), balances = AddrWithBalance.enoughBalances(sender)) { d => val grpcApi = getGrpcApi(d) val challengingMiner = d.wallet.generateNewAccount().get diff --git a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/BlocksApiGrpcSpec.scala b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/BlocksApiGrpcSpec.scala index bb210dd8014..a53a5c157fb 100644 --- a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/BlocksApiGrpcSpec.scala +++ b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/BlocksApiGrpcSpec.scala @@ -253,7 +253,7 @@ class BlocksApiGrpcSpec extends FreeSpec with BeforeAndAfterAll with DiffMatcher "NODE-922. GetBlock should return correct data for challenging block" in { val sender = TxHelpers.signer(1) - withDomain(DomainPresets.TransactionStateSnapshot, balances = AddrWithBalance.enoughBalances(sender, defaultSigner)) { d => + withDomain(TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), balances = AddrWithBalance.enoughBalances(sender, defaultSigner)) { d => val grpcApi = getGrpcApi(d) val challengingMiner = d.wallet.generateNewAccount().get @@ -304,7 +304,7 @@ class BlocksApiGrpcSpec extends FreeSpec with BeforeAndAfterAll with DiffMatcher "NODE-922. GetBlockRange should return correct data for challenging block" in { val sender = TxHelpers.signer(1) - withDomain(DomainPresets.TransactionStateSnapshot, balances = AddrWithBalance.enoughBalances(sender, defaultSigner)) { d => + withDomain(TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), balances = AddrWithBalance.enoughBalances(sender, defaultSigner)) { d => val grpcApi = getGrpcApi(d) val challengingMiner = d.wallet.generateNewAccount().get diff --git a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/TransactionsApiGrpcSpec.scala b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/TransactionsApiGrpcSpec.scala index cf946e27ba3..5d3f4311af8 100644 --- a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/TransactionsApiGrpcSpec.scala +++ b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/TransactionsApiGrpcSpec.scala @@ -13,6 +13,7 @@ import com.wavesplatform.history.Domain import com.wavesplatform.protobuf.transaction.{PBTransactions, Recipient} import com.wavesplatform.state.TxMeta import com.wavesplatform.test.* +import com.wavesplatform.test.DomainPresets.* import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.{TxHelpers, TxVersion} import com.wavesplatform.transaction.assets.exchange.{ExchangeTransaction, Order, OrderType} @@ -142,7 +143,7 @@ class TransactionsApiGrpcSpec extends FreeSpec with BeforeAndAfterAll with DiffM val challengedMiner = TxHelpers.signer(2) val resender = TxHelpers.signer(3) val recipient = TxHelpers.signer(4) - withDomain(DomainPresets.TransactionStateSnapshot, balances = AddrWithBalance.enoughBalances(sender)) { d => + withDomain(TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), balances = AddrWithBalance.enoughBalances(sender)) { d => val grpcApi = getGrpcApi(d) val challengingMiner = d.wallet.generateNewAccount().get diff --git a/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala b/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala index 4c7db9915cb..8806ba561cb 100644 --- a/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala +++ b/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala @@ -803,7 +803,7 @@ class BlockchainUpdatesSpec extends FreeSpec with WithBUDomain with ScalaFutures val sender = TxHelpers.signer(3) val recipient = TxHelpers.signer(4) - withDomainAndRepo(settings = DomainPresets.TransactionStateSnapshot) { case (d, repo) => + withDomainAndRepo(settings = TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0))) { case (d, repo) => val challengingMiner = d.wallet.generateNewAccount().get val initSenderBalance = 100000.waves diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/BlockChallengeSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala similarity index 97% rename from node-it/src/test/scala/com/wavesplatform/it/sync/BlockChallengeSuite.scala rename to node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala index e6a8eccb07f..47dfef0f061 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/BlockChallengeSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala @@ -1,4 +1,4 @@ -package com.wavesplatform.it.sync +package com.wavesplatform.it.sync.lightnode import com.typesafe.config.Config import com.wavesplatform.account.KeyPair @@ -35,6 +35,7 @@ class BlockChallengeSuite extends BaseFunSuite with TransferSending { BlockchainFeatures.LightNode.id.toInt -> 0 ) ) + .overrideBase(_.raw("waves.blockchain.custom.functionality.light-node-block-fields-absence-interval = 0")) .withDefault(1) .withSpecial(1, _.lightNode) .withSpecial(2, _.nonMiner) diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/LightNodeBroadcastSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeBroadcastSuite.scala similarity index 97% rename from node-it/src/test/scala/com/wavesplatform/it/sync/LightNodeBroadcastSuite.scala rename to node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeBroadcastSuite.scala index d34eb7090f3..f1f72f61580 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/LightNodeBroadcastSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeBroadcastSuite.scala @@ -1,10 +1,10 @@ -package com.wavesplatform.it.sync +package com.wavesplatform.it.sync.lightnode import com.google.common.primitives.Ints import com.typesafe.config.Config import com.wavesplatform.account.{Address, PublicKey} -import com.wavesplatform.it.{BaseFunSuite, NodeConfigs, TransferSending} import com.wavesplatform.it.api.SyncHttpApi.* +import com.wavesplatform.it.{BaseFunSuite, NodeConfigs, TransferSending} class LightNodeBroadcastSuite extends BaseFunSuite with TransferSending { override def nodeConfigs: Seq[Config] = diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeMiningSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeMiningSuite.scala index c8b8ef4510d..8ed66b0e31e 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeMiningSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeMiningSuite.scala @@ -1,26 +1,31 @@ package com.wavesplatform.it.sync.lightnode import com.typesafe.config.Config +import com.wavesplatform.features.BlockchainFeatures.LightNode import com.wavesplatform.it.api.SyncHttpApi.* import com.wavesplatform.it.{BaseFunSuite, NodeConfigs, TransferSending} +import com.wavesplatform.test.NumericExt class LightNodeMiningSuite extends BaseFunSuite with TransferSending { override def nodeConfigs: Seq[Config] = NodeConfigs.newBuilder - .overrideBase(_.lightNode) - .withDefault(2) + .overrideBase(_.preactivatedFeatures(LightNode.id.toInt -> 2)) + .overrideBase(_.raw("waves.blockchain.custom.functionality.light-node-block-fields-absence-interval = 2")) + .withDefault(1) + .withSpecial(1, _.lightNode) .buildNonConflicting() - test("nodes can mine in light mode") { - val first = nodes.head - val second = nodes.last + test("node can mine in light mode after light-node-block-fields-absence-interval") { + val lightNode = nodes.find(_.settings.enableLightMode).get + val fullNode = nodes.find(!_.settings.enableLightMode).get + val lightNodeAddress = lightNode.keyPair.toAddress.toString + val fullNodeAddress = fullNode.keyPair.toAddress.toString - val tx1 = first.transfer(first.keyPair, second.address, 1, waitForTx = true) - nodes.waitForHeightArise() - second.transactionStatus(tx1.id).applicationStatus.get shouldBe "succeeded" + nodes.waitForHeight(5) + fullNode.transfer(fullNode.keyPair, lightNodeAddress, fullNode.balance(fullNodeAddress).balance - 1.waves) + lightNode.blockSeq(2, 5).foreach(_.generator shouldBe fullNodeAddress) - val tx2 = second.transfer(second.keyPair, first.address, 1, waitForTx = true) - nodes.waitForHeightArise() - first.transactionStatus(tx2.id).applicationStatus.get shouldBe "succeeded" + lightNode.waitForHeight(6) + lightNode.blockAt(6).generator shouldBe lightNodeAddress } } diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/LightNodeRollbackSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeRollbackSuite.scala similarity index 97% rename from node-it/src/test/scala/com/wavesplatform/it/sync/LightNodeRollbackSuite.scala rename to node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeRollbackSuite.scala index e83bea8612d..8d11bc91f24 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/LightNodeRollbackSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/LightNodeRollbackSuite.scala @@ -1,10 +1,10 @@ -package com.wavesplatform.it.sync +package com.wavesplatform.it.sync.lightnode import com.google.common.primitives.Ints import com.typesafe.config.Config import com.wavesplatform.account.{Address, PublicKey} -import com.wavesplatform.it.{BaseFunSuite, NodeConfigs, TransferSending} import com.wavesplatform.it.api.SyncHttpApi.* +import com.wavesplatform.it.{BaseFunSuite, NodeConfigs, TransferSending} class LightNodeRollbackSuite extends BaseFunSuite with TransferSending { override def nodeConfigs: Seq[Config] = diff --git a/node/src/main/resources/custom-defaults.conf b/node/src/main/resources/custom-defaults.conf index 08e837a92ce..41a3a66626d 100644 --- a/node/src/main/resources/custom-defaults.conf +++ b/node/src/main/resources/custom-defaults.conf @@ -21,6 +21,10 @@ functionality { lease-expiration = 1000000 min-block-time = 15s delay-delta = 8 + + # Fill new block header fields (state hash and challenged header) + # only specified number of blocks after the Light Node blockchain feature activation + light-node-block-fields-absence-interval = 1000 } # Block rewards settings diff --git a/node/src/main/scala/com/wavesplatform/api/http/BlocksApiRoute.scala b/node/src/main/scala/com/wavesplatform/api/http/BlocksApiRoute.scala index ad0162d8a47..a4dd1b57830 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/BlocksApiRoute.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/BlocksApiRoute.scala @@ -128,9 +128,9 @@ case class BlocksApiRoute(settings: RestAPISettings, commonApi: CommonBlocksApi, val predictedHeight = (lowerBound + offset).max(lowerBound).min(upperBound) val timestamp = timestampOf(predictedHeight) - val rightTimestmap = timestampOf(predictedHeight + 1, Long.MaxValue) + val rightTimestamp = timestampOf(predictedHeight + 1, Long.MaxValue) val leftHit = timestamp <= target - val rightHit = rightTimestmap <= target + val rightHit = rightTimestamp <= target val (newLower, newUpper) = { if (!leftHit) (lowerBound, (predictedHeight - 1).max(lowerBound)) diff --git a/node/src/main/scala/com/wavesplatform/crypto/package.scala b/node/src/main/scala/com/wavesplatform/crypto/package.scala index c32998ca6f0..aba16b5ebbf 100644 --- a/node/src/main/scala/com/wavesplatform/crypto/package.scala +++ b/node/src/main/scala/com/wavesplatform/crypto/package.scala @@ -1,7 +1,5 @@ package com.wavesplatform -import java.lang.reflect.Constructor - import com.wavesplatform.account.{PrivateKey, PublicKey} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.lang.ValidationError @@ -9,6 +7,7 @@ import com.wavesplatform.transaction.TxValidationError.GenericError import com.wavesplatform.utils.* import org.whispersystems.curve25519.OpportunisticCurve25519Provider +import java.lang.reflect.Constructor import scala.util.Try package object crypto { diff --git a/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala b/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala index d52c4acb413..b13fe31c252 100644 --- a/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala +++ b/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala @@ -46,7 +46,8 @@ class BlockChallengerImpl( settings: WavesSettings, timeService: Time, pos: PoSSelector, - appendBlock: Block => Task[Either[ValidationError, BlockApplyResult]] + appendBlock: Block => Task[Either[ValidationError, BlockApplyResult]], + timeDrift: Long = MaxTimeDrift ) extends BlockChallenger with ScorexLogging { @@ -179,8 +180,6 @@ class BlockChallengerImpl( blockchainUpdater.parentHeader(prevBlockHeader, 2).map(_.timestamp), blockTime ) - - initialBlockSnapshot <- BlockDiffer.createInitialBlockSnapshot(blockchainUpdater, challengedBlock.header.reference, acc.toAddress) blockWithoutChallengeAndStateHash <- Block.buildAndSign( challengedBlock.header.version, blockTime, @@ -204,6 +203,7 @@ class BlockChallengerImpl( blockchainUpdater.computeNextReward, None ) + initialBlockSnapshot <- BlockDiffer.createInitialBlockSnapshot(blockchainUpdater, challengedBlock.header.reference, acc.toAddress) stateHash <- TxStateSnapshotHashBuilder .computeStateHash( txs, @@ -227,26 +227,28 @@ class BlockChallengerImpl( acc, blockFeatures(blockchainUpdater, settings), blockRewardVote(settings), - Some(stateHash), - Some( - ChallengedHeader( - challengedBlock.header.timestamp, - challengedBlock.header.baseTarget, - challengedBlock.header.generationSignature, - challengedBlock.header.featureVotes, - challengedBlock.header.generator, - challengedBlock.header.rewardVote, - challengedStateHash, - challengedSignature + if (blockchainWithNewBlock.supportsLightNodeBlockFields()) Some(stateHash) else None, + if (blockchainWithNewBlock.supportsLightNodeBlockFields()) + Some( + ChallengedHeader( + challengedBlock.header.timestamp, + challengedBlock.header.baseTarget, + challengedBlock.header.generationSignature, + challengedBlock.header.featureVotes, + challengedBlock.header.generator, + challengedBlock.header.rewardVote, + challengedStateHash, + challengedSignature + ) ) - ) + else None ) } yield { log.debug(s"Forged challenging block $challengingBlock") challengingBlock } }.flatMap { - case res @ Right(block) => waitForTimeAlign(block.header.timestamp).map(_ => res) + case res @ Right(block) => waitForTimeAlign(block.header.timestamp, timeDrift).map(_ => res) case err @ Left(_) => Task(err) } @@ -262,10 +264,10 @@ class BlockChallengerImpl( private def blockRewardVote(settings: WavesSettings): Long = settings.rewardsSettings.desired.getOrElse(-1L) - private def waitForTimeAlign(blockTime: Long): Task[Unit] = + private def waitForTimeAlign(blockTime: Long, timeDrift: Long): Task[Unit] = Task { val currentTime = timeService.correctedTime() - blockTime - currentTime - MaxTimeDrift + blockTime - currentTime - timeDrift }.flatMap { timeDiff => if (timeDiff > 0) { Task.sleep(timeDiff.millis) diff --git a/node/src/main/scala/com/wavesplatform/mining/Miner.scala b/node/src/main/scala/com/wavesplatform/mining/Miner.scala index 5ef98f4f374..77163abde07 100644 --- a/node/src/main/scala/com/wavesplatform/mining/Miner.scala +++ b/node/src/main/scala/com/wavesplatform/mining/Miner.scala @@ -1,6 +1,5 @@ package com.wavesplatform.mining -import java.time.LocalTime import cats.syntax.either.* import com.wavesplatform.account.{Address, KeyPair, PKKeyPair} import com.wavesplatform.block.Block.* @@ -18,11 +17,11 @@ import com.wavesplatform.state.* import com.wavesplatform.state.BlockchainUpdaterImpl.BlockApplyResult.{Applied, Ignored} import com.wavesplatform.state.appender.BlockAppender import com.wavesplatform.state.diffs.BlockDiffer -import com.wavesplatform.transaction.TxValidationError.BlockFromFuture import com.wavesplatform.transaction.* +import com.wavesplatform.transaction.TxValidationError.BlockFromFuture import com.wavesplatform.utils.{ScorexLogging, Time} -import com.wavesplatform.utx.UtxPool.PackStrategy import com.wavesplatform.utx.UtxPool +import com.wavesplatform.utx.UtxPool.PackStrategy import com.wavesplatform.wallet.Wallet import io.netty.channel.group.ChannelGroup import kamon.Kamon @@ -31,6 +30,7 @@ import monix.execution.cancelables.{CompositeCancelable, SerialCancelable} import monix.execution.schedulers.SchedulerService import monix.reactive.Observable +import java.time.LocalTime import scala.concurrent.duration.* trait Miner { @@ -60,7 +60,8 @@ class MinerImpl( pos: PoSSelector, val minerScheduler: SchedulerService, val appenderScheduler: SchedulerService, - transactionAdded: Observable[Unit] + transactionAdded: Observable[Unit], + maxTimeDrift: Long = appender.MaxTimeDrift ) extends Miner with MinerDebugInfo with ScorexLogging { @@ -90,27 +91,28 @@ class MinerImpl( def getNextBlockGenerationOffset(account: KeyPair): Either[String, FiniteDuration] = this.nextBlockGenOffsetWithConditions(account, blockchainUpdater) - def scheduleMining(tempBlockchain: Option[Blockchain]): Unit = { - Miner.blockMiningStarted.increment() - - val accounts = if (settings.minerSettings.privateKeys.nonEmpty) { - settings.minerSettings.privateKeys.map(PKKeyPair(_)) - } else { - wallet.privateKeyAccounts + def scheduleMining(tempBlockchain: Option[Blockchain]): Unit = + if (!settings.enableLightMode || blockchainUpdater.supportsLightNodeBlockFields()) { + Miner.blockMiningStarted.increment() + + val accounts = if (settings.minerSettings.privateKeys.nonEmpty) { + settings.minerSettings.privateKeys.map(PKKeyPair(_)) + } else { + wallet.privateKeyAccounts + } + + val hasAllowedForMiningScriptsAccounts = + accounts.filter(kp => hasAllowedForMiningScript(kp.toAddress, tempBlockchain.getOrElse(blockchainUpdater))) + scheduledAttempts := CompositeCancelable.fromSet(hasAllowedForMiningScriptsAccounts.map { account => + generateBlockTask(account, tempBlockchain) + .onErrorHandle(err => log.warn(s"Error mining Block", err)) + .runAsyncLogErr(appenderScheduler) + }.toSet) + microBlockAttempt := SerialCancelable() + + debugStateRef = MinerDebugInfo.MiningBlocks } - val hasAllowedForMiningScriptsAccounts = - accounts.filter(kp => hasAllowedForMiningScript(kp.toAddress, tempBlockchain.getOrElse(blockchainUpdater))) - scheduledAttempts := CompositeCancelable.fromSet(hasAllowedForMiningScriptsAccounts.map { account => - generateBlockTask(account, tempBlockchain) - .onErrorHandle(err => log.warn(s"Error mining Block", err)) - .runAsyncLogErr(appenderScheduler) - }.toSet) - microBlockAttempt := SerialCancelable() - - debugStateRef = MinerDebugInfo.MiningBlocks - } - override def state: MinerDebugInfo.State = debugStateRef private def checkAge(parentHeight: Int, parentTimestamp: Long): Either[String, Unit] = @@ -184,11 +186,11 @@ class MinerImpl( currentTime - 1.minute.toMillis ) _ <- Either.cond( - blockTime <= currentTime + appender.MaxTimeDrift, + blockTime <= currentTime + maxTimeDrift, log.debug( s"Forging with ${account.toAddress}, balance $balance, prev block $reference at $height with target ${lastBlockHeader.baseTarget}" ), - s"Block time $blockTime is from the future: current time is $currentTime, MaxTimeDrift = ${appender.MaxTimeDrift}" + s"Block time $blockTime is from the future: current time is $currentTime, MaxTimeDrift = $maxTimeDrift" ) consensusData <- consensusData(height, account, lastBlockHeader, blockTime) prevStateHash = @@ -207,7 +209,7 @@ class MinerImpl( account, blockFeatures(version), blockRewardVote(version), - stateHash, + if (blockchainUpdater.supportsLightNodeBlockFields(height + 1)) stateHash else None, None ) .leftMap(_.err) diff --git a/node/src/main/scala/com/wavesplatform/mining/microblocks/MicroBlockMinerImpl.scala b/node/src/main/scala/com/wavesplatform/mining/microblocks/MicroBlockMinerImpl.scala index de83f87765d..c40f3213772 100644 --- a/node/src/main/scala/com/wavesplatform/mining/microblocks/MicroBlockMinerImpl.scala +++ b/node/src/main/scala/com/wavesplatform/mining/microblocks/MicroBlockMinerImpl.scala @@ -171,7 +171,7 @@ class MicroBlockMinerImpl( signer = account, featureVotes = accumulatedBlock.header.featureVotes, rewardVote = accumulatedBlock.header.rewardVote, - stateHash = stateHash, + stateHash = if (blockchainUpdater.supportsLightNodeBlockFields()) stateHash else None, challengedHeader = None ) .leftMap(BlockBuildError) diff --git a/node/src/main/scala/com/wavesplatform/settings/BlockchainSettings.scala b/node/src/main/scala/com/wavesplatform/settings/BlockchainSettings.scala index 0b37e2be9b6..5ac37553bde 100644 --- a/node/src/main/scala/com/wavesplatform/settings/BlockchainSettings.scala +++ b/node/src/main/scala/com/wavesplatform/settings/BlockchainSettings.scala @@ -76,7 +76,8 @@ case class FunctionalitySettings( ethInvokePaymentsCheckHeight: Int = 0, daoAddress: Option[String] = None, xtnBuybackAddress: Option[String] = None, - xtnBuybackRewardPeriod: Int = Int.MaxValue + xtnBuybackRewardPeriod: Int = Int.MaxValue, + lightNodeBlockFieldsAbsenceInterval: Int = 1000 ) { val allowLeasedBalanceTransferUntilHeight: Int = blockVersion3AfterHeight val allowTemporaryNegativeUntil: Long = lastTimeBasedForkParameter diff --git a/node/src/main/scala/com/wavesplatform/state/Blockchain.scala b/node/src/main/scala/com/wavesplatform/state/Blockchain.scala index f0b949ecde4..a040d362261 100644 --- a/node/src/main/scala/com/wavesplatform/state/Blockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/Blockchain.scala @@ -5,6 +5,7 @@ import com.wavesplatform.block.Block.* import com.wavesplatform.block.{Block, BlockHeader, SignedBlockHeader} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.consensus.GeneratingBalanceProvider +import com.wavesplatform.features.BlockchainFeatures.LightNode import com.wavesplatform.features.{BlockchainFeature, BlockchainFeatureStatus, BlockchainFeatures} import com.wavesplatform.lang.ValidationError import com.wavesplatform.lang.script.ContractScript @@ -221,5 +222,8 @@ object Blockchain { def hasBannedEffectiveBalance(address: Address, height: Int = blockchain.height): Boolean = blockchain.effectiveBalanceBanHeights(address).contains(height) + + def supportsLightNodeBlockFields(height: Int = blockchain.height): Boolean = + blockchain.featureActivationHeight(LightNode.id).exists(height >= _ + blockchain.settings.functionalitySettings.lightNodeBlockFieldsAbsenceInterval) } } diff --git a/node/src/main/scala/com/wavesplatform/state/appender/package.scala b/node/src/main/scala/com/wavesplatform/state/appender/package.scala index cc9339a6399..b4a78165cdd 100644 --- a/node/src/main/scala/com/wavesplatform/state/appender/package.scala +++ b/node/src/main/scala/com/wavesplatform/state/appender/package.scala @@ -5,7 +5,6 @@ import com.wavesplatform.block.Block.BlockId import com.wavesplatform.block.{Block, BlockSnapshot} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.consensus.PoSSelector -import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.lang.ValidationError import com.wavesplatform.metrics.* import com.wavesplatform.mining.Miner @@ -209,7 +208,7 @@ package object appender { private def validateChallengedHeader(block: Block, blockchain: Blockchain): Either[ValidationError, Unit] = for { _ <- Either.cond( - block.header.challengedHeader.isEmpty || blockchain.isFeatureActivated(BlockchainFeatures.LightNode, blockchain.height + 1), + block.header.challengedHeader.isEmpty || blockchain.supportsLightNodeBlockFields(blockchain.height + 1), (), BlockAppendError("Challenged header is not supported yet", block) ) @@ -222,7 +221,7 @@ package object appender { private def validateStateHash(block: Block, blockchain: Blockchain): Either[ValidationError, Unit] = Either.cond( - block.header.stateHash.isEmpty || blockchain.isFeatureActivated(BlockchainFeatures.LightNode, blockchain.height + 1), + block.header.stateHash.isEmpty || blockchain.supportsLightNodeBlockFields(blockchain.height + 1), (), BlockAppendError("Block state hash is not supported yet", block) ) diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala b/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala index c4402a8966d..5b6b69f867d 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala @@ -12,7 +12,6 @@ import com.wavesplatform.state.* import com.wavesplatform.state.StateSnapshot.monoid import com.wavesplatform.state.TxStateSnapshotHashBuilder.TxStatusInfo import com.wavesplatform.state.patch.* -import com.wavesplatform.state.SnapshotBlockchain import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxValidationError.* import com.wavesplatform.transaction.assets.exchange.ExchangeTransaction @@ -184,6 +183,7 @@ object BlockDiffer { _ <- TracedResult(Either.cond(!verify || block.signatureValid(), (), GenericError(s"Block $block has invalid signature"))) initSnapshot <- TracedResult(initSnapshotE.leftMap(GenericError(_))) prevStateHash = maybePrevBlock.flatMap(_.header.stateHash).getOrElse(blockchain.lastStateHash(None)) + hasChallenge = block.header.challengedHeader.isDefined r <- snapshot match { case Some(BlockSnapshot(_, txSnapshots)) => TracedResult.wrapValue( @@ -197,7 +197,7 @@ object BlockDiffer { prevStateHash, initSnapshot, stateHeight >= ngHeight, - block.header.challengedHeader.isDefined, + hasChallenge, block.transactionData, loadCacheData, verify = verify, @@ -348,7 +348,6 @@ object BlockDiffer { prepareCaches(blockGenerator, txs, loadCacheData) val initStateHash = computeInitialStateHash(blockchain, initSnapshot, prevStateHash) - txs .foldLeft(TracedResult(Result(initSnapshot, 0L, 0L, initConstraint, initSnapshot, initStateHash).asRight[ValidationError])) { case (acc @ TracedResult(Left(_), _, _), _) => acc @@ -419,7 +418,6 @@ object BlockDiffer { txSnapshots: Seq[(StateSnapshot, TxMeta.Status)] ): Result = { val initStateHash = computeInitialStateHash(blockchain, initSnapshot, prevStateHash) - txs.zip(txSnapshots).foldLeft(Result(initSnapshot, 0L, 0L, MiningConstraint.Unlimited, initSnapshot, initStateHash)) { case (Result(currSnapshot, carryFee, currTotalFee, currConstraint, keyBlockSnapshot, prevStateHash), (tx, (txSnapshot, txStatus))) => val currBlockchain = SnapshotBlockchain(blockchain, currSnapshot) @@ -496,11 +494,9 @@ object BlockDiffer { blockStateHash: Option[ByteStr], computedStateHash: ByteStr ): TracedResult[ValidationError, Unit] = - TracedResult( - Either.cond( - !blockchain.isFeatureActivated(BlockchainFeatures.LightNode) || blockStateHash.contains(computedStateHash), - (), - InvalidStateHash(blockStateHash) - ) + Either.cond( + !blockchain.supportsLightNodeBlockFields() || blockStateHash.contains(computedStateHash), + (), + InvalidStateHash(blockStateHash) ) } diff --git a/node/src/test/scala/com/wavesplatform/db/WithState.scala b/node/src/test/scala/com/wavesplatform/db/WithState.scala index 66532bd1417..229d1c8e0ff 100644 --- a/node/src/test/scala/com/wavesplatform/db/WithState.scala +++ b/node/src/test/scala/com/wavesplatform/db/WithState.scala @@ -11,7 +11,6 @@ import com.wavesplatform.database.{KeyTags, RDB, RocksDBWriter, TestStorageFacto import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.events.BlockchainUpdateTriggers import com.wavesplatform.features.BlockchainFeatures -import com.wavesplatform.features.BlockchainFeatures.LightNode import com.wavesplatform.history.Domain import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lagonaki.mocks.TestBlock.BlockWithSigner @@ -21,9 +20,8 @@ import com.wavesplatform.lang.directives.values.* import com.wavesplatform.mining.MiningConstraint import com.wavesplatform.settings.{TestFunctionalitySettings as TFS, *} import com.wavesplatform.state.diffs.{BlockDiffer, ENOUGH_AMT} -import com.wavesplatform.state.SnapshotBlockchain import com.wavesplatform.state.utils.TestRocksDB -import com.wavesplatform.state.{Blockchain, BlockchainUpdaterImpl, NgState, StateSnapshot, TxStateSnapshotHashBuilder} +import com.wavesplatform.state.{Blockchain, BlockchainUpdaterImpl, NgState, SnapshotBlockchain, StateSnapshot, TxStateSnapshotHashBuilder} import com.wavesplatform.test.* import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.TxHelpers.defaultAddress @@ -321,7 +319,7 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit signer: KeyPair, blockchain: BlockchainUpdater & Blockchain ): TracedResult[ValidationError, Block] = { - (if (blockchain.isFeatureActivated(LightNode, blockchain.height + 1)) { + (if (blockchain.supportsLightNodeBlockFields(blockchain.height + 1)) { val compBlockchain = SnapshotBlockchain(blockchain, StateSnapshot.empty, blockWithoutStateHash, ByteStr.empty, 0, blockchain.computeNextReward, None) val prevStateHash = blockchain.lastStateHash(Some(blockWithoutStateHash.header.reference)) @@ -404,7 +402,7 @@ trait WithDomain extends WithState { _: Suite => domain.appendBlock( createGenesisWithStateHash( genesis, - bcu.isFeatureActivated(BlockchainFeatures.LightNode, 1), + fillStateHash = blockchain.supportsLightNodeBlockFields(), Some(settings.blockchainSettings.genesisSettings.initialBaseTarget) ) ) @@ -428,7 +426,7 @@ trait WithDomain extends WithState { _: Suite => .filter(v => v >= from && v <= to) .foreach(v => withDomain(DomainPresets.settingsForRide(v), balances)(assertion(v, _))) - def createGenesisWithStateHash(txs: Seq[GenesisTransaction], txStateSnapshotActivated: Boolean, baseTarget: Option[Long] = None): Block = { + def createGenesisWithStateHash(txs: Seq[GenesisTransaction], fillStateHash: Boolean, baseTarget: Option[Long] = None): Block = { val timestamp = txs.map(_.timestamp).max val genesisSettings = GenesisSettings( timestamp, @@ -461,7 +459,7 @@ trait WithDomain extends WithState { _: Suite => GenesisGenerator, Seq.empty, -1, - Option.when(txStateSnapshotActivated)(TxStateSnapshotHashBuilder.createGenesisStateHash(txs)), + Option.when(fillStateHash)(TxStateSnapshotHashBuilder.createGenesisStateHash(txs)), None ) } yield block).explicitGet() diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index eacb987fda1..58def9c43cc 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -14,7 +14,7 @@ import com.wavesplatform.consensus.{PoSCalculator, PoSSelector} import com.wavesplatform.database.{DBExt, Keys, RDB, RocksDBWriter} import com.wavesplatform.events.BlockchainUpdateTriggers import com.wavesplatform.features.BlockchainFeatures -import com.wavesplatform.features.BlockchainFeatures.{BlockV5, LightNode, RideV6} +import com.wavesplatform.features.BlockchainFeatures.{BlockV5, RideV6} import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lang.ValidationError import com.wavesplatform.lang.script.Script @@ -297,7 +297,7 @@ case class Domain(rdb: RDB, blockchainUpdater: BlockchainUpdaterImpl, rocksDBWri ): Either[ValidationError, MicroBlock] = { val lastBlock = this.lastBlock val blockSigner = signer.getOrElse(defaultSigner) - val stateHashE = if (blockchain.isFeatureActivated(BlockchainFeatures.LightNode)) { + val stateHashE = if (blockchain.supportsLightNodeBlockFields()) { stateHash .map(Right(_)) .getOrElse( @@ -444,7 +444,7 @@ case class Domain(rdb: RDB, blockchainUpdater: BlockchainUpdaterImpl, rocksDBWri challengedHeader = challengedHeader ) resultStateHash <- stateHash.map(Right(_)).getOrElse { - if (blockchain.isFeatureActivated(LightNode, blockchain.height + 1)) { + if (blockchain.supportsLightNodeBlockFields(blockchain.height + 1)) { val hitSource = posSelector.validateGenerationSignature(blockWithoutStateHash).getOrElse(blockWithoutStateHash.header.generationSignature) val blockchainWithNewBlock = SnapshotBlockchain(blockchain, StateSnapshot.empty, blockWithoutStateHash, hitSource, 0, blockchain.computeNextReward, None) diff --git a/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala b/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala index 12625590359..113611503fd 100644 --- a/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala +++ b/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala @@ -1,6 +1,5 @@ package com.wavesplatform.mining -import java.util.concurrent.atomic.AtomicReference import com.typesafe.config.ConfigFactory import com.wavesplatform.account.{AddressOrAlias, KeyPair} import com.wavesplatform.block.serialization.{BlockHeaderSerializer, BlockSerializer} @@ -8,37 +7,26 @@ import com.wavesplatform.block.validation.Validators import com.wavesplatform.block.{Block, BlockHeader, SignedBlockHeader} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.consensus.PoSSelector import com.wavesplatform.crypto.DigestLength import com.wavesplatform.db.WithDomain import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.history.{chainBaseAndMicro, defaultSigner} import com.wavesplatform.lagonaki.mocks.TestBlock -import com.wavesplatform.lang.ValidationError import com.wavesplatform.protobuf.block.PBBlocks -import com.wavesplatform.settings.{Constants, FunctionalitySettings, TestFunctionalitySettings, WalletSettings, WavesSettings} -import com.wavesplatform.state.BlockchainUpdaterImpl.BlockApplyResult +import com.wavesplatform.settings.{Constants, FunctionalitySettings, TestFunctionalitySettings, WavesSettings} import com.wavesplatform.state.BlockchainUpdaterImpl.BlockApplyResult.Applied -import com.wavesplatform.state.appender.BlockAppender import com.wavesplatform.state.{Blockchain, BlockchainUpdaterImpl, NG, diffs} import com.wavesplatform.test.{FlatSpec, *} import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.transfer.TransferTransaction import com.wavesplatform.transaction.{BlockchainUpdater, GenesisTransaction, Transaction, TxHelpers, TxVersion} import com.wavesplatform.utils.Time -import com.wavesplatform.utx.UtxPoolImpl -import com.wavesplatform.wallet.Wallet import com.wavesplatform.{BlocksTransactionsHelpers, crypto, protobuf} -import io.netty.channel.group.DefaultChannelGroup -import io.netty.util.concurrent.GlobalEventExecutor -import monix.eval.Task -import monix.execution.Scheduler -import monix.reactive.Observable import org.scalacheck.Gen import org.scalatest.* import org.scalatest.enablers.Length -import scala.concurrent.Await +import java.util.concurrent.atomic.AtomicReference import scala.concurrent.duration.* class BlockV5Test extends FlatSpec with WithDomain with OptionValues with EitherValues with BlocksTransactionsHelpers { import BlockV5Test.* @@ -138,14 +126,14 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either val disabledFeatures = new AtomicReference(Set[Short]()) withBlockchain(disabledFeatures, testTime) { blockchain => blockchain.processBlock(genesis, genesis.header.generationSignature, None) should beRight - withMiner(blockchain, testTime) { case (miner, appender, scheduler) => + withMiner(blockchain, testTime, testSettings) { case (miner, append) => for (h <- 2 until BlockV5ActivationHeight) { shiftTime(miner, minerAcc1) val forge = miner.forgeBlock(minerAcc1) val block = forge.explicitGet()._1 - Await.result(appender(block).runToFuture(scheduler), 10.seconds).explicitGet() shouldBe an[Applied] + append(block).explicitGet() shouldBe an[Applied] blockchain.height shouldBe h } @@ -157,7 +145,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either val blockAtActivationHeight = forgedAtActivationHeight.explicitGet()._1 blockAtActivationHeight.header.version shouldBe Block.ProtoBlockVersion - Await.result(appender(blockAtActivationHeight).runToFuture(scheduler), 10.seconds).explicitGet() shouldBe an[Applied] + append(blockAtActivationHeight).explicitGet() shouldBe an[Applied] blockchain.height shouldBe BlockV5ActivationHeight blockchain.lastBlockHeader.value.header.version shouldBe Block.ProtoBlockVersion blockAtActivationHeight.signature shouldBe blockchain.lastBlockHeader.value.signature @@ -180,7 +168,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either val blockAfterActivationHeight = forgedAfterActivationHeight.explicitGet()._1 blockAfterActivationHeight.header.version shouldBe Block.ProtoBlockVersion - Await.result(appender(blockAfterActivationHeight).runToFuture(scheduler), 10.seconds).explicitGet() shouldBe an[Applied] + append(blockAfterActivationHeight).explicitGet() shouldBe an[Applied] blockchain.height shouldBe BlockV5ActivationHeight + 1 blockchain.lastBlockHeader.value.header.version shouldBe Block.ProtoBlockVersion blockAfterActivationHeight.signature shouldBe blockchain.lastBlockHeader.value.signature @@ -200,7 +188,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either val blockAfterVRFUsing = forgedAfterVRFUsing.explicitGet()._1 blockAfterVRFUsing.header.version shouldBe Block.ProtoBlockVersion - Await.result(appender(blockAfterVRFUsing).runToFuture(scheduler), 10.seconds).explicitGet() shouldBe an[Applied] + append(blockAfterVRFUsing).explicitGet() shouldBe an[Applied] blockchain.height shouldBe BlockV5ActivationHeight + 2 blockchain.lastBlockHeader.value.header.version shouldBe Block.ProtoBlockVersion blockAfterVRFUsing.signature shouldBe blockchain.lastBlockHeader.value.signature @@ -230,7 +218,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either oldVersionBlock.header.version shouldBe Block.RewardBlockVersion disabledFeatures.set(Set()) - Await.result(appender(oldVersionBlock).runToFuture(scheduler), 10.seconds).left.value + append(oldVersionBlock).left.value for (h <- blockchain.height to 110) { @@ -240,7 +228,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either val block = forged.explicitGet()._1 block.header.version shouldBe Block.ProtoBlockVersion - Await.result(appender(block).runToFuture(scheduler), 10.seconds).explicitGet() shouldBe an[Applied] + append(block).explicitGet() shouldBe an[Applied] blockchain.height shouldBe (h + 1) val hitSource = blockchain.hitSource(if (h > 100) h - 100 else h).value @@ -262,7 +250,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either "Miner" should "generate valid blocks when feature pre-activated" in forAll(genesis) { case (minerAcc1, _, genesis) => withBlockchain(new AtomicReference(Set()), testTime, preActivatedTestSettings) { blockchain => blockchain.processBlock(genesis, genesis.header.generationSignature, None) should beRight - withMiner(blockchain, testTime) { case (miner, appender, scheduler) => + withMiner(blockchain, testTime, testSettings) { case (miner, append) => for (h <- blockchain.height to 110) { shiftTime(miner, minerAcc1) @@ -271,7 +259,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either val block = forged.explicitGet()._1 block.header.version shouldBe Block.ProtoBlockVersion - Await.result(appender(block).runToFuture(scheduler), 10.seconds).explicitGet() shouldBe an[Applied] + append(block).explicitGet() shouldBe an[Applied] blockchain.height shouldBe (h + 1) } } @@ -282,7 +270,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either val disabledFeatures = new AtomicReference(Set.empty[Short]) withBlockchain(disabledFeatures, testTime) { blockchain => blockchain.processBlock(genesis, genesis.header.generationSignature, None) should beRight - withMiner(blockchain, testTime) { case (miner, appender, scheduler) => + withMiner(blockchain, testTime, testSettings) { case (miner, append) => def forge(): Block = { val forge = miner.forgeBlock(minerAcc) forge.explicitGet()._1 @@ -291,7 +279,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either def forgeAppendAndValidate(version: Byte, height: Int): Unit = { val block = forge() block.header.version shouldBe version - Await.result(appender(block).runToFuture(scheduler), 10.seconds).explicitGet() shouldBe an[Applied] + append(block).explicitGet() shouldBe an[Applied] blockchain.height shouldBe height } @@ -313,7 +301,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either val badNgBlock = forge() badNgBlock.header.version shouldBe Block.NgBlockVersion disabledFeatures.set(Set()) - Await.result(appender(badNgBlock).runToFuture(scheduler), 10.seconds).left.value + append(badNgBlock).left.value shiftTime(miner, minerAcc) forgeAppendAndValidate(Block.RewardBlockVersion, BlockRewardActivationHeight + 1) @@ -328,8 +316,8 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either val badRewardBlock = forge() badRewardBlock.header.version shouldBe Block.RewardBlockVersion disabledFeatures.set(Set()) - Await.result(appender(badNgBlock).runToFuture(scheduler), 10.seconds).left.value - Await.result(appender(badRewardBlock).runToFuture(scheduler), 10.seconds).left.value + append(badNgBlock).left.value + append(badRewardBlock).left.value shiftTime(miner, minerAcc) forgeAppendAndValidate(Block.ProtoBlockVersion, BlockV5ActivationHeight) @@ -461,25 +449,6 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either finally bcu.shutdown() } } - - type Appender = Block => Task[Either[ValidationError, BlockApplyResult]] - - private def withMiner(blockchain: Blockchain & BlockchainUpdater & NG, time: Time, settings: WavesSettings = testSettings)( - f: (MinerImpl, Appender, Scheduler) => Unit - ): Unit = { - val pos = PoSSelector(blockchain, settings.synchronizationSettings.maxBaseTarget) - val allChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE) - val wallet = Wallet(WalletSettings(None, Some("123"), None)) - val utxPool = new UtxPoolImpl(time, blockchain, settings.utxSettings, settings.maxTxErrorLogSize, settings.minerSettings.enable) - val minerScheduler = Scheduler.singleThread("miner") - val appenderScheduler = Scheduler.singleThread("appender") - val miner = new MinerImpl(allChannels, blockchain, settings, time, utxPool, wallet, pos, minerScheduler, appenderScheduler, Observable.empty) - val blockAppender = BlockAppender(blockchain, time, utxPool, pos, appenderScheduler)(_, None) - f(miner, blockAppender, appenderScheduler) - appenderScheduler.shutdown() - minerScheduler.shutdown() - utxPool.close() - } } object BlockV5Test { diff --git a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala new file mode 100644 index 00000000000..34ef6e178c9 --- /dev/null +++ b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala @@ -0,0 +1,153 @@ +package com.wavesplatform.mining + +import com.wavesplatform.account.SeedKeyPair +import com.wavesplatform.block.Block.ProtoBlockVersion +import com.wavesplatform.common.state.ByteStr +import com.wavesplatform.common.utils.EitherExt2 +import com.wavesplatform.crypto.DigestLength +import com.wavesplatform.db.WithDomain +import com.wavesplatform.db.WithState.AddrWithBalance +import com.wavesplatform.features.BlockchainFeatures.LightNode +import com.wavesplatform.mining.MultiDimensionalMiningConstraint.Unlimited +import com.wavesplatform.mining.microblocks.MicroBlockMinerImpl +import com.wavesplatform.test.DomainPresets.* +import com.wavesplatform.test.{PropSpec, produce} +import com.wavesplatform.transaction.TxHelpers.{defaultSigner, secondSigner, transfer} +import com.wavesplatform.transaction.TxValidationError.GenericError +import io.netty.channel.group.DefaultChannelGroup +import io.netty.util.concurrent.GlobalEventExecutor +import monix.eval.Task +import monix.execution.Scheduler.Implicits.global +import monix.reactive.Observable + +import scala.concurrent.duration.DurationInt + +class LightNodeBlockFieldsTest extends PropSpec with WithDomain { + private val invalidStateHash = Some(Some(ByteStr.fill(DigestLength)(1))) + + property("new block fields appear `lightNodeBlockFieldsAbsenceInterval` blocks after LightNode activation") { + withDomain( + TransactionStateSnapshot.setFeaturesHeight(LightNode -> 2).configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 10)), + AddrWithBalance.enoughBalances(defaultSigner, secondSigner) + ) { d => + withMiner( + d.blockchain, + d.testTime, + d.settings.copy(minerSettings = d.settings.minerSettings.copy(quorum = 0, minMicroBlockAge = 0.seconds)), + verify = false, + timeDrift = Int.MaxValue + ) { case (miner, append) => + val microBlockMiner = new MicroBlockMinerImpl( + _ => (), + null, + d.blockchainUpdater, + d.utxPool, + d.settings.minerSettings, + miner.minerScheduler, + miner.appenderScheduler, + Observable.empty, + identity + ) + val challenger = new BlockChallengerImpl( + d.blockchain, + new DefaultChannelGroup(GlobalEventExecutor.INSTANCE), + d.wallet, + d.settings, + d.testTime, + d.posSelector, + b => Task.now(append(b)), + timeDrift = Int.MaxValue + ) { + override def pickBestAccount(accounts: Seq[(SeedKeyPair, Long)]): Either[GenericError, (SeedKeyPair, Long)] = Right((defaultSigner, 0)) + } + def block(height: Int) = d.blocksApi.blockAtHeight(height).get._1.header + def appendBlock() = append(miner.forgeBlock(defaultSigner).explicitGet()._1).explicitGet() + def appendMicro() = { + d.utxPool.putIfNew(transfer()).resultE.explicitGet() + microBlockMiner.generateOneMicroBlockTask(defaultSigner, d.lastBlock, Unlimited, 0).runSyncUnsafe() + } + def challengeBlock() = { + val invalidBlock = d.createBlock(ProtoBlockVersion, Seq(), strictTime = true, stateHash = invalidStateHash) + challenger.challengeBlock(invalidBlock, null).runSyncUnsafe() + } + + appendBlock() + d.blockchain.height shouldBe 2 + d.blockchain.isFeatureActivated(LightNode) shouldBe true + block(2).stateHash shouldBe None + + appendMicro() + block(2).stateHash shouldBe None + + challengeBlock() + d.blockchain.height shouldBe 3 + block(3).stateHash shouldBe None + block(3).challengedHeader shouldBe None + + (1 to 8).foreach(_ => appendBlock()) + d.blockchain.height shouldBe 11 + block(11).stateHash shouldBe None + + appendMicro() + block(11).stateHash shouldBe None + + appendBlock() + d.blockchain.height shouldBe 12 + val hash1 = block(12).stateHash + hash1 shouldBe defined + + appendMicro() + val hash2 = block(12).stateHash + hash2 shouldBe defined + hash2 should not be hash1 + + d.rollbackTo(10) + challengeBlock() + block(11).stateHash shouldBe None + block(11).challengedHeader shouldBe None + + challengeBlock() + block(12).stateHash shouldBe defined + block(12).challengedHeader shouldBe defined + } + } + } + + property( + "blocks with challenged header or state hash should be allowed only `lightNodeBlockFieldsAbsenceInterval` blocks after LightNode activation" + ) { + withDomain( + TransactionStateSnapshot.setFeaturesHeight(LightNode -> 2).configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 10)), + AddrWithBalance.enoughBalances(defaultSigner, secondSigner) + ) { d => + withMiner(d.blockchain, d.testTime, d.settings) { case (_, append) => + (1 to 9).foreach(_ => d.appendBlock()) + d.blockchain.height shouldBe 10 + val challengedBlock = d.createBlock(ProtoBlockVersion, Nil, strictTime = true, stateHash = invalidStateHash) + val challengingBlock = d.createChallengingBlock(secondSigner, challengedBlock, strictTime = true) + val blockWithOnlyChallengingHeader = { + val challengedHeader = challengingBlock.header.challengedHeader.map(_.copy(stateHash = None)) + val block = d.createBlock(ProtoBlockVersion, Nil, strictTime = true, challengedHeader = challengedHeader) + block.copy(header = block.header.copy(stateHash = None)) + } + d.testTime.setTime(challengingBlock.header.timestamp) + append(challengedBlock) should produce("Block state hash is not supported yet") + append(challengingBlock) should produce("Block state hash is not supported yet") + append(blockWithOnlyChallengingHeader) should produce("Challenged header is not supported yet") + + d.appendBlock() + d.blockchain.height shouldBe 11 + val correctBlockWithStateHash = d.createBlock(ProtoBlockVersion, Nil, strictTime = true) + correctBlockWithStateHash.header.stateHash shouldBe defined + d.testTime.setTime(correctBlockWithStateHash.header.timestamp) + append(correctBlockWithStateHash) shouldBe a[Right[?, ?]] + + d.rollbackTo(11) + val invalidBlock = d.createBlock(ProtoBlockVersion, Nil, stateHash = invalidStateHash, strictTime = true) + val challengingBlock2 = d.createChallengingBlock(secondSigner, invalidBlock, strictTime = true) + d.testTime.setTime(challengingBlock2.header.timestamp) + append(challengingBlock2) shouldBe a[Right[?, ?]] + } + } + } +} diff --git a/node/src/test/scala/com/wavesplatform/mining/package.scala b/node/src/test/scala/com/wavesplatform/mining/package.scala index 6909afcfa69..eb1e7320dad 100644 --- a/node/src/test/scala/com/wavesplatform/mining/package.scala +++ b/node/src/test/scala/com/wavesplatform/mining/package.scala @@ -1,7 +1,23 @@ package com.wavesplatform -import com.wavesplatform.state.{Blockchain, StateSnapshot} -import com.wavesplatform.transaction.Transaction +import com.wavesplatform.block.Block +import com.wavesplatform.consensus.PoSSelector +import com.wavesplatform.lang.ValidationError +import com.wavesplatform.settings.{WalletSettings, WavesSettings} +import com.wavesplatform.state.BlockchainUpdaterImpl.BlockApplyResult +import com.wavesplatform.state.appender.BlockAppender +import com.wavesplatform.state.{Blockchain, NG, StateSnapshot, appender} +import com.wavesplatform.transaction.{BlockchainUpdater, Transaction} +import com.wavesplatform.utils.Time +import com.wavesplatform.utx.UtxPoolImpl +import com.wavesplatform.wallet.Wallet +import io.netty.channel.group.DefaultChannelGroup +import io.netty.util.concurrent.GlobalEventExecutor +import monix.execution.Scheduler +import monix.reactive.Observable + +import scala.concurrent.Await +import scala.concurrent.duration.Duration.Inf package object mining { private[mining] def createConstConstraint(maxSize: Long, transactionSize: => Long, description: String) = OneDimensionalMiningConstraint( @@ -13,4 +29,32 @@ package object mining { }, description ) + + type Appender = Block => Either[ValidationError, BlockApplyResult] + + def withMiner( + blockchain: Blockchain & BlockchainUpdater & NG, + time: Time, + settings: WavesSettings, + verify: Boolean = true, + timeDrift: Long = appender.MaxTimeDrift + )( + f: (MinerImpl, Appender) => Unit + ): Unit = { + val pos = PoSSelector(blockchain, settings.synchronizationSettings.maxBaseTarget) + val channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE) + val wallet = Wallet(WalletSettings(None, Some("123"), None)) + val utxPool = new UtxPoolImpl(time, blockchain, settings.utxSettings, settings.maxTxErrorLogSize, settings.minerSettings.enable) + val minerScheduler = Scheduler.singleThread("miner") + val appenderScheduler = Scheduler.singleThread("appender") + val miner = new MinerImpl(channels, blockchain, settings, time, utxPool, wallet, pos, minerScheduler, appenderScheduler, Observable(), timeDrift) + def appendBlock(b: Block) = { + val appendTask = BlockAppender(blockchain, time, utxPool, pos, appenderScheduler, verify)(b, None) + Await.result(appendTask.runToFuture(appenderScheduler), Inf) + } + f(miner, appendBlock) + appenderScheduler.shutdown() + minerScheduler.shutdown() + utxPool.close() + } } diff --git a/node/src/test/scala/com/wavesplatform/settings/BlockchainSettingsSpecification.scala b/node/src/test/scala/com/wavesplatform/settings/BlockchainSettingsSpecification.scala index d3d17179ed1..2aa32030826 100644 --- a/node/src/test/scala/com/wavesplatform/settings/BlockchainSettingsSpecification.scala +++ b/node/src/test/scala/com/wavesplatform/settings/BlockchainSettingsSpecification.scala @@ -30,6 +30,7 @@ class BlockchainSettingsSpecification extends FlatSpec { | max-transaction-time-back-offset = 55s | max-transaction-time-forward-offset = 12d | lease-expiration = 1000000 + | light-node-block-fields-absence-interval = 123 | } | rewards { | term = 100000 @@ -66,6 +67,7 @@ class BlockchainSettingsSpecification extends FlatSpec { settings.functionalitySettings.doubleFeaturesPeriodsAfterHeight should be(21) settings.functionalitySettings.maxTransactionTimeBackOffset should be(55.seconds) settings.functionalitySettings.maxTransactionTimeForwardOffset should be(12.days) + settings.functionalitySettings.lightNodeBlockFieldsAbsenceInterval shouldBe 123 settings.rewardsSettings.initial should be(600000000) settings.rewardsSettings.minIncrement should be(50000000) settings.rewardsSettings.term should be(100000) diff --git a/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala b/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala index ec22a04b94a..7c5b1fbc2c9 100644 --- a/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala @@ -32,7 +32,7 @@ import com.wavesplatform.state.appender.{BlockAppender, ExtensionAppender, Micro import com.wavesplatform.state.diffs.BlockDiffer import com.wavesplatform.state.diffs.BlockDiffer.CurrentBlockFeePart import com.wavesplatform.test.* -import com.wavesplatform.test.DomainPresets.WavesSettingsOps +import com.wavesplatform.test.DomainPresets.{TransactionStateSnapshot, WavesSettingsOps} import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.TxValidationError.{BlockAppendError, GenericError, InvalidStateHash, MicroBlockAppendError} import com.wavesplatform.transaction.assets.exchange.OrderType @@ -66,7 +66,9 @@ class BlockChallengeTest implicit val appenderScheduler: SchedulerService = Scheduler.singleThread("appender") val settings: WavesSettings = - DomainPresets.TransactionStateSnapshot.addFeatures(BlockchainFeatures.SmallerMinimalGeneratingBalance) + TransactionStateSnapshot + .addFeatures(BlockchainFeatures.SmallerMinimalGeneratingBalance) + .configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)) val testTime: TestTime = TestTime() val invalidStateHash: ByteStr = ByteStr.fill(DigestLength)(1) @@ -449,7 +451,7 @@ class BlockChallengeTest val recipientEth = TxHelpers.signer(4).toEthKeyPair val dApp = TxHelpers.signer(5) withDomain( - DomainPresets.TransactionStateSnapshot.configure(_.copy(minAssetInfoUpdateInterval = 0)), + TransactionStateSnapshot.configure(_.copy(minAssetInfoUpdateInterval = 0, lightNodeBlockFieldsAbsenceInterval = 0)), balances = AddrWithBalance.enoughBalances(sender, dApp) ) { d => val challengingMiner = d.wallet.generateNewAccount().get @@ -553,7 +555,7 @@ class BlockChallengeTest val buyer = TxHelpers.signer(6) val matcher = TxHelpers.signer(7) withDomain( - DomainPresets.TransactionStateSnapshot, + DomainPresets.TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), balances = AddrWithBalance.enoughBalances(sender, dApp, invoker, buyer, matcher) ) { d => val challengingMiner = d.wallet.generateNewAccount().get @@ -1049,7 +1051,8 @@ class BlockChallengeTest withDomain( DomainPresets.BlockRewardDistribution .addFeatures(BlockchainFeatures.SmallerMinimalGeneratingBalance) - .setFeaturesHeight(BlockchainFeatures.LightNode -> 1003), + .setFeaturesHeight(BlockchainFeatures.LightNode -> 1003) + .configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), balances = AddrWithBalance.enoughBalances(defaultSigner) ) { d => val challengingMiner = d.wallet.generateNewAccount().get @@ -1120,7 +1123,8 @@ class BlockChallengeTest withDomain( DomainPresets.BlockRewardDistribution .addFeatures(BlockchainFeatures.SmallerMinimalGeneratingBalance) - .setFeaturesHeight(BlockchainFeatures.LightNode -> 1008), + .setFeaturesHeight(BlockchainFeatures.LightNode -> 1008) + .configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), balances = AddrWithBalance.enoughBalances(sender) ) { d => val challengingMiner = d.wallet.generateNewAccount().get diff --git a/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala b/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala index 409c6a7a783..4825ec5dd00 100644 --- a/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala @@ -10,6 +10,7 @@ import com.wavesplatform.mining.MiningConstraint import com.wavesplatform.network.{BlockSnapshotResponse, ExtensionBlocks, InvalidBlockStorage, PeerDatabase} import com.wavesplatform.protobuf.PBSnapshots import com.wavesplatform.settings.WavesSettings +import com.wavesplatform.test.DomainPresets.WavesSettingsOps import com.wavesplatform.state.BlockchainUpdaterImpl.BlockApplyResult.Applied import com.wavesplatform.state.appender.{BlockAppender, ExtensionAppender} import com.wavesplatform.state.diffs.BlockDiffer @@ -30,7 +31,7 @@ class LightNodeTest extends PropSpec with WithDomain { property("NODE-1148. Light node shouldn't apply block when its state hash differs from snapshot state hash") { val sender = TxHelpers.signer(1) val recipient = TxHelpers.address(2) - withDomain(settings, AddrWithBalance.enoughBalances(sender)) { d => + withDomain(settings.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), AddrWithBalance.enoughBalances(sender)) { d => val prevBlock = d.lastBlock val txs = Seq(TxHelpers.transfer(sender, recipient, amount = 10.waves), TxHelpers.transfer(sender, recipient, amount = 100.waves)) val validBlock = d.createBlock(Block.ProtoBlockVersion, txs) @@ -167,7 +168,10 @@ class LightNodeTest extends PropSpec with WithDomain { val recipient = TxHelpers.address(2) val challengingMiner = TxHelpers.signer(3) - withDomain(settings, AddrWithBalance.enoughBalances(challengingMiner, TxHelpers.defaultSigner, sender)) { d => + withDomain( + settings.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), + AddrWithBalance.enoughBalances(challengingMiner, TxHelpers.defaultSigner, sender) + ) { d => val txs = Seq(TxHelpers.transfer(sender, recipient, amount = 1.waves), TxHelpers.transfer(sender, recipient, amount = 2.waves)) val invalidBlock = d.createBlock(Block.ProtoBlockVersion, txs, strictTime = true, stateHash = Some(Some(invalidStateHash))) val challengingBlock = d.createChallengingBlock(challengingMiner, invalidBlock, strictTime = true) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala index 68b26d83418..798a74369b8 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala @@ -15,6 +15,7 @@ import com.wavesplatform.settings.FunctionalitySettings import com.wavesplatform.state.diffs.BlockDiffer.Result import com.wavesplatform.state.{Blockchain, SnapshotBlockchain, StateSnapshot, TxStateSnapshotHashBuilder} import com.wavesplatform.test.* +import com.wavesplatform.test.DomainPresets.{TransactionStateSnapshot, WavesSettingsOps} import com.wavesplatform.test.node.* import com.wavesplatform.transaction.TxValidationError.InvalidStateHash import com.wavesplatform.transaction.{TxHelpers, TxVersion} @@ -109,8 +110,8 @@ class BlockDifferTest extends FreeSpec with WithDomain { "genesis block" in { val txs = (1 to 10).map(idx => TxHelpers.genesis(TxHelpers.address(idx), 100.waves)) ++ (1 to 5).map(idx => TxHelpers.genesis(TxHelpers.address(idx), 1.waves)) - withDomain(DomainPresets.TransactionStateSnapshot) { d => - val block = createGenesisWithStateHash(txs, txStateSnapshotActivated = true) + withDomain(TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0))) { d => + val block = createGenesisWithStateHash(txs, fillStateHash = true) block.header.stateHash shouldBe defined BlockDiffer @@ -118,7 +119,7 @@ class BlockDifferTest extends FreeSpec with WithDomain { } withDomain(DomainPresets.RideV6) { d => - val block = createGenesisWithStateHash(txs, txStateSnapshotActivated = false) + val block = createGenesisWithStateHash(txs, fillStateHash = false) block.header.stateHash shouldBe None BlockDiffer @@ -127,8 +128,8 @@ class BlockDifferTest extends FreeSpec with WithDomain { } "arbitrary block/microblock" in - withDomain(DomainPresets.TransactionStateSnapshot) { d => - val genesis = createGenesisWithStateHash(Seq(TxHelpers.genesis(TxHelpers.address(1))), txStateSnapshotActivated = true) + withDomain(TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0))) { d => + val genesis = createGenesisWithStateHash(Seq(TxHelpers.genesis(TxHelpers.address(1))), fillStateHash = true) d.appendBlock(genesis) val txs = (1 to 10).map(idx => TxHelpers.transfer(TxHelpers.signer(idx), TxHelpers.address(idx + 1), (100 - idx).waves)) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/TransactionBindingsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/TransactionBindingsTest.scala index c9eeaf2bc27..abc2a820aa8 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/TransactionBindingsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/TransactionBindingsTest.scala @@ -13,15 +13,14 @@ import com.wavesplatform.lang.directives.values.{Asset as AssetType, *} import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet} import com.wavesplatform.lang.script.v1.ExprScript import com.wavesplatform.lang.script.v1.ExprScript.ExprScriptImpl -import com.wavesplatform.lang.v1.compiler.ExpressionCompiler +import com.wavesplatform.lang.v1.compiler import com.wavesplatform.lang.v1.compiler.Terms.* -import com.wavesplatform.lang.v1.compiler.TestCompiler +import com.wavesplatform.lang.v1.compiler.{ExpressionCompiler, TestCompiler} import com.wavesplatform.lang.v1.evaluator.EvaluatorV1 import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.{FieldNames, WavesContext} import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, GlobalValNames, PureContext} import com.wavesplatform.lang.v1.parser.Parser import com.wavesplatform.lang.v1.traits.Environment -import com.wavesplatform.lang.v1.compiler import com.wavesplatform.lang.{Common, Global} import com.wavesplatform.settings.WavesSettings import com.wavesplatform.state.* @@ -31,8 +30,8 @@ import com.wavesplatform.test.DomainPresets.* import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.assets.exchange.{Order, OrderType} import com.wavesplatform.transaction.smart.BlockchainContext.In -import com.wavesplatform.transaction.smart.{InvokeExpressionTransaction, InvokeScriptTransaction, WavesEnvironment, buildThisValue} import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment +import com.wavesplatform.transaction.smart.{InvokeExpressionTransaction, InvokeScriptTransaction, WavesEnvironment, buildThisValue} import com.wavesplatform.transaction.{Asset, DataTransaction, Proofs, TxHelpers, TxVersion} import com.wavesplatform.utils.EmptyBlockchain import monix.eval.Coeval @@ -835,7 +834,9 @@ class TransactionBindingsTest extends PropSpec with PathMockFactory with EitherV TestCompiler(v).compileExpressionE(script) should produce("Undefined field `attachment` of variable of type `Order`") withDomain( - DomainPresets.BlockRewardDistribution.setFeaturesHeight(BlockchainFeatures.LightNode -> Int.MaxValue), + DomainPresets.BlockRewardDistribution + .setFeaturesHeight(BlockchainFeatures.LightNode -> Int.MaxValue) + .configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), AddrWithBalance.enoughBalances(issuer, matcher, buyer) ) { d => d.appendBlock(issue)