From d687aeb851a2a059e7995fa5978b6474bc906600 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Fri, 17 Nov 2023 14:14:14 +0300 Subject: [PATCH 01/24] Directory for LightNode integration tests --- .../it/sync/{ => lightnode}/BlockChallengeSuite.scala | 8 ++++---- .../it/sync/{ => lightnode}/LightNodeBroadcastSuite.scala | 4 ++-- .../it/sync/{ => lightnode}/LightNodeRollbackSuite.scala | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename node-it/src/test/scala/com/wavesplatform/it/sync/{ => lightnode}/BlockChallengeSuite.scala (99%) 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%) 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 99% 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 05d16dbecf..d8dbd37318 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 @@ -6,12 +6,11 @@ import com.wavesplatform.block.Block import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.consensus.FairPoSCalculator -import com.wavesplatform.{TestValues, crypto} import com.wavesplatform.features.BlockchainFeatures -import com.wavesplatform.it.api.Block as ApiBlock -import com.wavesplatform.it.{BaseFunSuite, Node, NodeConfigs, TransferSending} import com.wavesplatform.it.api.AsyncNetworkApi.NodeAsyncNetworkApi +import com.wavesplatform.it.api.Block as ApiBlock import com.wavesplatform.it.api.SyncHttpApi.* +import com.wavesplatform.it.{BaseFunSuite, Node, NodeConfigs, TransferSending} import com.wavesplatform.lang.directives.values.V8 import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.network.RawBytes @@ -19,6 +18,7 @@ import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.assets.exchange.OrderType import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer import com.wavesplatform.transaction.{Transaction, TxHelpers, TxNonNegativeAmount} +import com.wavesplatform.{TestValues, crypto} import scala.concurrent.Await import scala.concurrent.duration.* 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 d34eb7090f..f1f72f6158 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/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 e83bea8612..8d11bc91f2 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] = From 9234c3d92e9204f15dace0d975ee2ad3fe95d9a8 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 22 Nov 2023 12:00:08 +0300 Subject: [PATCH 02/24] NODE-2632 Delayed Light Node block structure --- .../com/wavesplatform/crypto/package.scala | 2 +- .../mining/BlockChallenger.scala | 56 ++++----- .../com/wavesplatform/mining/Miner.scala | 15 +-- .../microblocks/MicroBlockMinerImpl.scala | 5 +- .../com/wavesplatform/state/Blockchain.scala | 4 + .../state/diffs/BlockDiffer.scala | 37 ++++-- .../com/wavesplatform/db/WithState.scala | 2 +- .../wavesplatform/mining/BlockV5Test.scala | 63 +++------- .../mining/LightNodeBlockFieldsTest.scala | 112 ++++++++++++++++++ .../com/wavesplatform/mining/package.scala | 48 +++++++- 10 files changed, 248 insertions(+), 96 deletions(-) create mode 100644 node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala diff --git a/node/src/main/scala/com/wavesplatform/crypto/package.scala b/node/src/main/scala/com/wavesplatform/crypto/package.scala index c32998ca6f..eba3da458e 100644 --- a/node/src/main/scala/com/wavesplatform/crypto/package.scala +++ b/node/src/main/scala/com/wavesplatform/crypto/package.scala @@ -47,7 +47,7 @@ package object crypto { for { _ <- Either.cond(!checkWeakPk || !isWeakPublicKey(publicKey.arr), (), GenericError("Could not verify VRF proof: weak public key is used")) result <- Try(ByteStr(provider.verifyVrfSignature(publicKey.arr, message, signature.arr))).toEither.left - .map(_ => GenericError("Could not verify VRF proof")) + .map(_ => throw new RuntimeException("Could not verify VRF proof")) } yield result } diff --git a/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala b/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala index 4facb60cd9..56e96972b6 100644 --- a/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala +++ b/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala @@ -46,14 +46,15 @@ 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 { private val processingTxs: ConcurrentHashMap[ByteStr, Transaction] = new ConcurrentHashMap() def challengeBlock(block: Block, ch: Channel): Task[Unit] = { - log.debug(s"Challenging block $block") + println(s"Challenging block $block") withProcessingTxs(block.transactionData) { (for { @@ -70,19 +71,19 @@ class BlockChallengerImpl( } yield applyResult -> challengingBlock).value }.map { case Right((Applied(_, _), challengingBlock)) => - log.debug(s"Successfully challenged $block with $challengingBlock") + println(s"Successfully challenged $block with $challengingBlock") BlockStats.challenged(challengingBlock, blockchainUpdater.height) if (blockchainUpdater.isLastBlockId(challengingBlock.id())) { allChannels.broadcast(BlockForged(challengingBlock), Some(ch)) } - case Right((_, challengingBlock)) => log.debug(s"Ignored challenging block $challengingBlock") - case Left(err) => log.debug(s"Could not challenge $block: $err") + case Right((_, challengingBlock)) => println(s"Ignored challenging block $challengingBlock") + case Left(err) => println(s"Could not challenge $block: $err") } } def challengeMicroblock(md: MicroblockData, ch: Channel): Task[Unit] = { val idStr = md.invOpt.map(_.totalBlockId.toString).getOrElse(s"(sig=${md.microBlock.totalResBlockSig})") - log.debug(s"Challenging microblock $idStr") + println(s"Challenging microblock $idStr") (for { discarded <- EitherT(Task(blockchainUpdater.removeAfter(blockchainUpdater.lastBlockHeader.get.header.reference))) @@ -105,16 +106,16 @@ class BlockChallengerImpl( } yield { applyResult match { case Applied(_, _) => - log.debug(s"Successfully challenged microblock $idStr with $challengingBlock") + println(s"Successfully challenged microblock $idStr with $challengingBlock") BlockStats.challenged(challengingBlock, blockchainUpdater.height) if (blockchainUpdater.isLastBlockId(challengingBlock.id())) { allChannels.broadcast(BlockForged(challengingBlock), Some(ch)) } case _ => - log.debug(s"Ignored challenging block $challengingBlock") + println(s"Ignored challenging block $challengingBlock") } }).fold( - err => log.debug(s"Could not challenge microblock $idStr: $err"), + err => println(s"Could not challenge microblock $idStr: $err"), identity ) } @@ -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") + println(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 ec84c72047..feca9168f7 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 { @@ -184,11 +185,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 +208,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 de83f87765..9438024128 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,10 @@ 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/state/Blockchain.scala b/node/src/main/scala/com/wavesplatform/state/Blockchain.scala index 448d0ef1f6..b5dc4edd42 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 >= _ + 1000) } } 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 310a6daa2f..c22a0a5861 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala @@ -347,7 +347,11 @@ object BlockDiffer { prepareCaches(blockGenerator, txs, loadCacheData) - val initStateHash = computeInitialStateHash(blockchain, initSnapshot, prevStateHash) + val initStateHash = + if (blockchain.supportsLightNodeBlockFields()) + computeInitialStateHash(blockchain, initSnapshot, prevStateHash) + else + ByteStr.empty txs .foldLeft(TracedResult(Result(initSnapshot, 0L, 0L, initConstraint, initSnapshot, initStateHash).asRight[ValidationError])) { @@ -390,9 +394,12 @@ object BlockDiffer { currTotalFee + txFeeInfo.wavesFee, updatedConstraint, newKeyBlockSnapshot, - TxStateSnapshotHashBuilder - .createHashFromSnapshot(resultTxSnapshot, Some(TxStatusInfo(txInfo.transaction.id(), txInfo.status))) - .createHash(prevStateHash) + if (blockchain.supportsLightNodeBlockFields()) + TxStateSnapshotHashBuilder + .createHashFromSnapshot(resultTxSnapshot, Some(TxStatusInfo(txInfo.transaction.id(), txInfo.status))) + .createHash(prevStateHash) + else + ByteStr.empty ) } } @@ -418,7 +425,11 @@ object BlockDiffer { txs: Seq[Transaction], txSnapshots: Seq[(StateSnapshot, TxMeta.Status)] ): Result = { - val initStateHash = computeInitialStateHash(blockchain, initSnapshot, prevStateHash) + val initStateHash = + if (blockchain.supportsLightNodeBlockFields()) + computeInitialStateHash(blockchain, initSnapshot, prevStateHash) + else + ByteStr.empty txs.zip(txSnapshots).foldLeft(Result(initSnapshot, 0L, 0L, MiningConstraint.Unlimited, initSnapshot, initStateHash)) { case (Result(currSnapshot, carryFee, currTotalFee, currConstraint, keyBlockSnapshot, prevStateHash), (tx, (txSnapshot, txStatus))) => @@ -433,7 +444,10 @@ object BlockDiffer { currTotalFee + txFeeInfo.map(_.wavesFee).getOrElse(0L), currConstraint, keyBlockSnapshot.withTransaction(nti), - TxStateSnapshotHashBuilder.createHashFromSnapshot(txSnapshot, Some(TxStatusInfo(tx.id(), txStatus))).createHash(prevStateHash) + if (blockchain.supportsLightNodeBlockFields()) + TxStateSnapshotHashBuilder.createHashFromSnapshot(txSnapshot, Some(TxStatusInfo(tx.id(), txStatus))).createHash(prevStateHash) + else + ByteStr.empty ) } } @@ -496,11 +510,14 @@ object BlockDiffer { blockStateHash: Option[ByteStr], computedStateHash: ByteStr ): TracedResult[ValidationError, Unit] = - TracedResult( - Either.cond( - !blockchain.isFeatureActivated(BlockchainFeatures.LightNode) || blockStateHash.contains(computedStateHash), + TracedResult { + val r = Either.cond( + !blockchain.supportsLightNodeBlockFields() || blockStateHash.contains(computedStateHash), (), InvalidStateHash(blockStateHash) ) - ) + if (r.isLeft) + println("here") + r + } } diff --git a/node/src/test/scala/com/wavesplatform/db/WithState.scala b/node/src/test/scala/com/wavesplatform/db/WithState.scala index 42bba1cb50..4290d8abaa 100644 --- a/node/src/test/scala/com/wavesplatform/db/WithState.scala +++ b/node/src/test/scala/com/wavesplatform/db/WithState.scala @@ -324,7 +324,7 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit TracedResult( BlockDiffer .createInitialBlockSnapshot( - blockchain, + blockchain.asInstanceOf[BlockchainUpdaterImpl], blockWithoutStateHash.header.reference, blockWithoutStateHash.header.generator.toAddress ) diff --git a/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala b/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala index 21cb7bc539..d48db55f4c 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 0000000000..04e82a2774 --- /dev/null +++ b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala @@ -0,0 +1,112 @@ +package com.wavesplatform.mining + +import com.wavesplatform.account.SeedKeyPair +import com.wavesplatform.block.Block +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 +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 { + property("new block fields appear 1000 blocks after LightNode activation") { + withDomain( + TransactionStateSnapshot.setFeaturesHeight(LightNode -> 2), + 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() = challenger.challengeBlock(miner.forgeBlock(defaultSigner).explicitGet()._1, 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 998).foreach(_ => appendBlock()) + d.blockchain.height shouldBe 1001 + block(1001).stateHash shouldBe None + appendMicro() + block(1001).stateHash shouldBe None + + appendBlock() + d.blockchain.height shouldBe 1002 + val hash1 = block(1002).stateHash + hash1 shouldBe defined + appendMicro() + val hash2 = block(1002).stateHash + hash2 shouldBe defined + hash2 should not be hash1 + + d.rollbackTo(1000) + challengeBlock() + block(1001).stateHash shouldBe None + block(1001).challengedHeader shouldBe None + + d.rollbackTo(1000) + val invalidBlock = d.createBlock(ProtoBlockVersion, Seq.empty, strictTime = true, stateHash = Some(Some(ByteStr.fill(DigestLength)(1)))) + append(invalidBlock).explicitGet() + challengeBlock() + block(1002).stateHash shouldBe defined + block(1002).challengedHeader shouldBe defined + } + } + } +} diff --git a/node/src/test/scala/com/wavesplatform/mining/package.scala b/node/src/test/scala/com/wavesplatform/mining/package.scala index 6909afcfa6..09587a145a 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.DurationInt 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), 10.seconds) + } + f(miner, appendBlock) + appenderScheduler.shutdown() + minerScheduler.shutdown() + utxPool.close() + } } From a1ef6c7b6ef40c67f7c6c2aa209d0df3d875962f Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 22 Nov 2023 13:48:06 +0300 Subject: [PATCH 03/24] Optimised imports --- .../com/wavesplatform/mining/LightNodeBlockFieldsTest.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala index 04e82a2774..eea8489119 100644 --- a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala @@ -1,7 +1,6 @@ package com.wavesplatform.mining import com.wavesplatform.account.SeedKeyPair -import com.wavesplatform.block.Block import com.wavesplatform.block.Block.ProtoBlockVersion import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 From 995e66db5bb456feafa9b7528a2dd53c90eb423a Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 22 Nov 2023 14:01:06 +0300 Subject: [PATCH 04/24] Restored log.debug --- .../wavesplatform/mining/BlockChallenger.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala b/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala index 56e96972b6..0b159d8949 100644 --- a/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala +++ b/node/src/main/scala/com/wavesplatform/mining/BlockChallenger.scala @@ -54,7 +54,7 @@ class BlockChallengerImpl( private val processingTxs: ConcurrentHashMap[ByteStr, Transaction] = new ConcurrentHashMap() def challengeBlock(block: Block, ch: Channel): Task[Unit] = { - println(s"Challenging block $block") + log.debug(s"Challenging block $block") withProcessingTxs(block.transactionData) { (for { @@ -71,19 +71,19 @@ class BlockChallengerImpl( } yield applyResult -> challengingBlock).value }.map { case Right((Applied(_, _), challengingBlock)) => - println(s"Successfully challenged $block with $challengingBlock") + log.debug(s"Successfully challenged $block with $challengingBlock") BlockStats.challenged(challengingBlock, blockchainUpdater.height) if (blockchainUpdater.isLastBlockId(challengingBlock.id())) { allChannels.broadcast(BlockForged(challengingBlock), Some(ch)) } - case Right((_, challengingBlock)) => println(s"Ignored challenging block $challengingBlock") - case Left(err) => println(s"Could not challenge $block: $err") + case Right((_, challengingBlock)) => log.debug(s"Ignored challenging block $challengingBlock") + case Left(err) => log.debug(s"Could not challenge $block: $err") } } def challengeMicroblock(md: MicroblockData, ch: Channel): Task[Unit] = { val idStr = md.invOpt.map(_.totalBlockId.toString).getOrElse(s"(sig=${md.microBlock.totalResBlockSig})") - println(s"Challenging microblock $idStr") + log.debug(s"Challenging microblock $idStr") (for { discarded <- EitherT(Task(blockchainUpdater.removeAfter(blockchainUpdater.lastBlockHeader.get.header.reference))) @@ -106,16 +106,16 @@ class BlockChallengerImpl( } yield { applyResult match { case Applied(_, _) => - println(s"Successfully challenged microblock $idStr with $challengingBlock") + log.debug(s"Successfully challenged microblock $idStr with $challengingBlock") BlockStats.challenged(challengingBlock, blockchainUpdater.height) if (blockchainUpdater.isLastBlockId(challengingBlock.id())) { allChannels.broadcast(BlockForged(challengingBlock), Some(ch)) } case _ => - println(s"Ignored challenging block $challengingBlock") + log.debug(s"Ignored challenging block $challengingBlock") } }).fold( - err => println(s"Could not challenge microblock $idStr: $err"), + err => log.debug(s"Could not challenge microblock $idStr: $err"), identity ) } @@ -244,7 +244,7 @@ class BlockChallengerImpl( else None ) } yield { - println(s"Forged challenging block $challengingBlock") + log.debug(s"Forged challenging block $challengingBlock") challengingBlock } }.flatMap { From 46a2ef25df60b737fdd6b94b22612b1752ad8080 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 23 Nov 2023 11:32:20 +0300 Subject: [PATCH 05/24] Typo --- .../scala/com/wavesplatform/api/http/BlocksApiRoute.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ad0162d8a4..a4dd1b5783 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)) From f40c9cccbcb123b82a5b8acb1456687cd114a5f1 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 23 Nov 2023 15:14:56 +0300 Subject: [PATCH 06/24] Adapted withMiner() --- node/src/test/scala/com/wavesplatform/mining/package.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/package.scala b/node/src/test/scala/com/wavesplatform/mining/package.scala index 09587a145a..eb1e7320da 100644 --- a/node/src/test/scala/com/wavesplatform/mining/package.scala +++ b/node/src/test/scala/com/wavesplatform/mining/package.scala @@ -17,7 +17,7 @@ import monix.execution.Scheduler import monix.reactive.Observable import scala.concurrent.Await -import scala.concurrent.duration.DurationInt +import scala.concurrent.duration.Duration.Inf package object mining { private[mining] def createConstConstraint(maxSize: Long, transactionSize: => Long, description: String) = OneDimensionalMiningConstraint( @@ -50,7 +50,7 @@ package object mining { 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), 10.seconds) + Await.result(appendTask.runToFuture(appenderScheduler), Inf) } f(miner, appendBlock) appenderScheduler.shutdown() From 54cd9df6cc25a007c2627133013fad2389d28ea9 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 23 Nov 2023 16:04:49 +0300 Subject: [PATCH 07/24] Corrected BlockDiffer --- .../com/wavesplatform/state/diffs/BlockDiffer.scala | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 c22a0a5861..4785b942b8 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala @@ -510,14 +510,11 @@ object BlockDiffer { blockStateHash: Option[ByteStr], computedStateHash: ByteStr ): TracedResult[ValidationError, Unit] = - TracedResult { - val r = Either.cond( + TracedResult( + Either.cond( !blockchain.supportsLightNodeBlockFields() || blockStateHash.contains(computedStateHash), (), InvalidStateHash(blockStateHash) ) - if (r.isLeft) - println("here") - r - } + ) } From b51a44501fb8f671606edd04baae02ed3dcb03ed Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 23 Nov 2023 16:17:55 +0300 Subject: [PATCH 08/24] Improved LightNodeBlockFieldsTest --- .../mining/LightNodeBlockFieldsTest.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala index eea8489119..99f4e940ee 100644 --- a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala @@ -58,19 +58,22 @@ class LightNodeBlockFieldsTest extends PropSpec with WithDomain { ) { 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() = challenger.challengeBlock(miner.forgeBlock(defaultSigner).explicitGet()._1, null).runSyncUnsafe() + def challengeBlock() = { + val invalidBlock = d.createBlock(ProtoBlockVersion, Seq(), strictTime = true, stateHash = Some(Some(ByteStr.fill(DigestLength)(1)))) + 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 @@ -82,6 +85,7 @@ class LightNodeBlockFieldsTest extends PropSpec with WithDomain { (1 to 998).foreach(_ => appendBlock()) d.blockchain.height shouldBe 1001 block(1001).stateHash shouldBe None + appendMicro() block(1001).stateHash shouldBe None @@ -89,6 +93,7 @@ class LightNodeBlockFieldsTest extends PropSpec with WithDomain { d.blockchain.height shouldBe 1002 val hash1 = block(1002).stateHash hash1 shouldBe defined + appendMicro() val hash2 = block(1002).stateHash hash2 shouldBe defined @@ -99,9 +104,6 @@ class LightNodeBlockFieldsTest extends PropSpec with WithDomain { block(1001).stateHash shouldBe None block(1001).challengedHeader shouldBe None - d.rollbackTo(1000) - val invalidBlock = d.createBlock(ProtoBlockVersion, Seq.empty, strictTime = true, stateHash = Some(Some(ByteStr.fill(DigestLength)(1)))) - append(invalidBlock).explicitGet() challengeBlock() block(1002).stateHash shouldBe defined block(1002).challengedHeader shouldBe defined From c7a841f29048f3090032ab115bb087636fef743a Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 23 Nov 2023 20:09:12 +0300 Subject: [PATCH 09/24] Restored GenericError --- node/src/main/scala/com/wavesplatform/crypto/package.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/crypto/package.scala b/node/src/main/scala/com/wavesplatform/crypto/package.scala index eba3da458e..aba16b5ebb 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 { @@ -47,7 +46,7 @@ package object crypto { for { _ <- Either.cond(!checkWeakPk || !isWeakPublicKey(publicKey.arr), (), GenericError("Could not verify VRF proof: weak public key is used")) result <- Try(ByteStr(provider.verifyVrfSignature(publicKey.arr, message, signature.arr))).toEither.left - .map(_ => throw new RuntimeException("Could not verify VRF proof")) + .map(_ => GenericError("Could not verify VRF proof")) } yield result } From 4c4d019519d3646bc1a214ac96a60648b345ddeb Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 23 Nov 2023 20:10:41 +0300 Subject: [PATCH 10/24] Better code --- .../mining/microblocks/MicroBlockMinerImpl.scala | 5 +---- node/src/test/scala/com/wavesplatform/db/WithState.scala | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) 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 9438024128..c40f321377 100644 --- a/node/src/main/scala/com/wavesplatform/mining/microblocks/MicroBlockMinerImpl.scala +++ b/node/src/main/scala/com/wavesplatform/mining/microblocks/MicroBlockMinerImpl.scala @@ -171,10 +171,7 @@ class MicroBlockMinerImpl( signer = account, featureVotes = accumulatedBlock.header.featureVotes, rewardVote = accumulatedBlock.header.rewardVote, - stateHash = if (blockchainUpdater.supportsLightNodeBlockFields()) - stateHash - else - None, + stateHash = if (blockchainUpdater.supportsLightNodeBlockFields()) stateHash else None, challengedHeader = None ) .leftMap(BlockBuildError) diff --git a/node/src/test/scala/com/wavesplatform/db/WithState.scala b/node/src/test/scala/com/wavesplatform/db/WithState.scala index 4290d8abaa..42bba1cb50 100644 --- a/node/src/test/scala/com/wavesplatform/db/WithState.scala +++ b/node/src/test/scala/com/wavesplatform/db/WithState.scala @@ -324,7 +324,7 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit TracedResult( BlockDiffer .createInitialBlockSnapshot( - blockchain.asInstanceOf[BlockchainUpdaterImpl], + blockchain, blockWithoutStateHash.header.reference, blockWithoutStateHash.header.generator.toAddress ) From 00e054f3831ff7864e71a73bb5c234a0d294d673 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 23 Nov 2023 20:37:18 +0300 Subject: [PATCH 11/24] Overflow protection --- node/src/main/scala/com/wavesplatform/state/Blockchain.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/main/scala/com/wavesplatform/state/Blockchain.scala b/node/src/main/scala/com/wavesplatform/state/Blockchain.scala index b5dc4edd42..8a63222265 100644 --- a/node/src/main/scala/com/wavesplatform/state/Blockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/Blockchain.scala @@ -224,6 +224,6 @@ object Blockchain { blockchain.effectiveBalanceBanHeights(address).contains(height) def supportsLightNodeBlockFields(height: Int = blockchain.height): Boolean = - blockchain.featureActivationHeight(LightNode.id).exists(height >= _ + 1000) + blockchain.featureActivationHeight(LightNode.id).exists(height >= _ + 1000L) } } From 98de17248d495c8dbedb8a53757611eee0de0088 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 23 Nov 2023 21:39:53 +0300 Subject: [PATCH 12/24] Adapted WithState --- node/src/test/scala/com/wavesplatform/db/WithState.scala | 6 +++--- .../com/wavesplatform/state/diffs/BlockDifferTest.scala | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/db/WithState.scala b/node/src/test/scala/com/wavesplatform/db/WithState.scala index 42bba1cb50..32af317b0f 100644 --- a/node/src/test/scala/com/wavesplatform/db/WithState.scala +++ b/node/src/test/scala/com/wavesplatform/db/WithState.scala @@ -400,7 +400,7 @@ trait WithDomain extends WithState { _: Suite => domain.appendBlock( createGenesisWithStateHash( genesis, - bcu.isFeatureActivated(BlockchainFeatures.LightNode, 1), + fillStateHash = blockchain.supportsLightNodeBlockFields(), Some(settings.blockchainSettings.genesisSettings.initialBaseTarget) ) ) @@ -424,7 +424,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, @@ -457,7 +457,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/state/diffs/BlockDifferTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala index d845315504..2e3ce78acf 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala @@ -111,7 +111,7 @@ class BlockDifferTest extends FreeSpec with WithDomain { 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) + val block = createGenesisWithStateHash(txs, fillStateHash = true) block.header.stateHash shouldBe defined BlockDiffer @@ -119,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 @@ -129,7 +129,7 @@ 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) + 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)) From 497e6e1ae2a0081a4ec87db04363bf84628b7b0a Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 23 Nov 2023 21:45:47 +0300 Subject: [PATCH 13/24] Experiment --- node/src/main/scala/com/wavesplatform/state/Blockchain.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/main/scala/com/wavesplatform/state/Blockchain.scala b/node/src/main/scala/com/wavesplatform/state/Blockchain.scala index 8a63222265..f6c27b1898 100644 --- a/node/src/main/scala/com/wavesplatform/state/Blockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/Blockchain.scala @@ -224,6 +224,6 @@ object Blockchain { blockchain.effectiveBalanceBanHeights(address).contains(height) def supportsLightNodeBlockFields(height: Int = blockchain.height): Boolean = - blockchain.featureActivationHeight(LightNode.id).exists(height >= _ + 1000L) + blockchain.isFeatureActivated(LightNode)//.exists(height >= _ + 1000L) } } From 9c3f28960f52b6cf930b42865ed103c445688d2c Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Fri, 24 Nov 2023 00:06:06 +0300 Subject: [PATCH 14/24] Configurable light-node-block-fields-absence-interval --- .../api/grpc/test/AccountsApiGrpcSpec.scala | 13 +++---------- .../api/grpc/test/BlocksApiGrpcSpec.scala | 4 ++-- .../grpc/test/TransactionsApiGrpcSpec.scala | 3 ++- .../events/BlockchainUpdatesSpec.scala | 2 +- .../sync/lightnode/BlockChallengeSuite.scala | 1 + node/src/main/resources/custom-defaults.conf | 4 ++++ .../settings/BlockchainSettings.scala | 3 ++- .../com/wavesplatform/state/Blockchain.scala | 2 +- .../BlockchainSettingsSpecification.scala | 2 ++ .../state/BlockChallengeTest.scala | 18 +++++++++++------- .../wavesplatform/state/LightNodeTest.scala | 3 ++- .../state/diffs/BlockDifferTest.scala | 3 ++- .../smart/predef/TransactionBindingsTest.scala | 11 ++++++----- 13 files changed, 39 insertions(+), 30 deletions(-) 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 61c1cb035f..2f6aa866fa 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 @@ -3,15 +3,7 @@ package com.wavesplatform.api.grpc.test import com.google.protobuf.ByteString import com.wavesplatform.TestValues import com.wavesplatform.account.{Address, KeyPair} -import com.wavesplatform.api.grpc.{ - AccountRequest, - AccountsApiGrpcImpl, - BalanceResponse, - BalancesRequest, - DataEntryResponse, - DataRequest, - LeaseResponse -} +import com.wavesplatform.api.grpc.{AccountRequest, AccountsApiGrpcImpl, BalanceResponse, BalancesRequest, DataEntryResponse, DataRequest, LeaseResponse} import com.wavesplatform.block.Block import com.wavesplatform.common.state.ByteStr import com.wavesplatform.crypto.DigestLength @@ -27,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} @@ -194,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 bb210dd801..a53a5c157f 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 cf946e27ba..5d3f4311af 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 34feeb4a0d..d04151e680 100644 --- a/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala +++ b/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala @@ -812,7 +812,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/lightnode/BlockChallengeSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala index d8dbd37318..9d9c10ed22 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala @@ -36,6 +36,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/src/main/resources/custom-defaults.conf b/node/src/main/resources/custom-defaults.conf index 08e837a92c..41a3a66626 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/settings/BlockchainSettings.scala b/node/src/main/scala/com/wavesplatform/settings/BlockchainSettings.scala index 0b37e2be9b..5ac37553bd 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 f6c27b1898..a94f90e02e 100644 --- a/node/src/main/scala/com/wavesplatform/state/Blockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/Blockchain.scala @@ -224,6 +224,6 @@ object Blockchain { blockchain.effectiveBalanceBanHeights(address).contains(height) def supportsLightNodeBlockFields(height: Int = blockchain.height): Boolean = - blockchain.isFeatureActivated(LightNode)//.exists(height >= _ + 1000L) + blockchain.featureActivationHeight(LightNode.id).exists(height >= _ + blockchain.settings.functionalitySettings.lightNodeBlockFieldsAbsenceInterval) } } diff --git a/node/src/test/scala/com/wavesplatform/settings/BlockchainSettingsSpecification.scala b/node/src/test/scala/com/wavesplatform/settings/BlockchainSettingsSpecification.scala index d3d17179ed..2aa3203082 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 2755cf490a..5fbe519c53 100644 --- a/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala @@ -5,8 +5,8 @@ import akka.http.scaladsl.server.Route import akka.http.scaladsl.testkit.* import com.wavesplatform.TestValues import com.wavesplatform.account.{Address, KeyPair, SeedKeyPair} -import com.wavesplatform.api.http.TransactionsApiRoute.{ApplicationStatus, Status} import com.wavesplatform.api.http.* +import com.wavesplatform.api.http.TransactionsApiRoute.{ApplicationStatus, Status} import com.wavesplatform.block.{Block, ChallengedHeader, MicroBlock} import com.wavesplatform.common.merkle.Merkle import com.wavesplatform.common.state.ByteStr @@ -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 @@ -60,7 +60,9 @@ class BlockChallengeTest extends PropSpec 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) @@ -440,7 +442,7 @@ class BlockChallengeTest extends PropSpec 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 @@ -544,7 +546,7 @@ class BlockChallengeTest extends PropSpec 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 @@ -1040,7 +1042,8 @@ class BlockChallengeTest extends PropSpec 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 @@ -1111,7 +1114,8 @@ class BlockChallengeTest extends PropSpec 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 807d67a5ff..805891f45b 100644 --- a/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala @@ -9,6 +9,7 @@ import com.wavesplatform.history.Domain import com.wavesplatform.mining.MiningConstraint import com.wavesplatform.network.{ExtensionBlocks, InvalidBlockStorage, PeerDatabase} 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 @@ -29,7 +30,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) 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 2e3ce78acf..b2b69512ea 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala @@ -16,6 +16,7 @@ import com.wavesplatform.state.diffs.BlockDiffer.Result import com.wavesplatform.state.reader.SnapshotBlockchain import com.wavesplatform.state.{Blockchain, Diff, StateSnapshot, TxStateSnapshotHashBuilder} import com.wavesplatform.test.* +import com.wavesplatform.test.DomainPresets.WavesSettingsOps import com.wavesplatform.test.node.* import com.wavesplatform.transaction.TxValidationError.InvalidStateHash import com.wavesplatform.transaction.{TxHelpers, TxVersion} @@ -128,7 +129,7 @@ class BlockDifferTest extends FreeSpec with WithDomain { } "arbitrary block/microblock" in - withDomain(DomainPresets.TransactionStateSnapshot) { d => + withDomain(DomainPresets.TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0))) { d => val genesis = createGenesisWithStateHash(Seq(TxHelpers.genesis(TxHelpers.address(1))), fillStateHash = true) d.appendBlock(genesis) 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 f307953d23..30ee4cfc51 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) From 317e39409fa39538f959f01f6b22f0e6c011f13f Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 30 Nov 2023 15:52:09 +0300 Subject: [PATCH 15/24] Imports --- .../wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala index 4da3091543..47dfef0f06 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/lightnode/BlockChallengeSuite.scala @@ -18,7 +18,6 @@ import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.assets.exchange.OrderType import com.wavesplatform.transaction.{Transaction, TxHelpers} import com.wavesplatform.{TestValues, crypto} -import com.wavesplatform.{TestValues, crypto} import scala.concurrent.Await import scala.concurrent.duration.* From 3a73598f89c99c6a31bb4189ff18e4ad8991f6e4 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 30 Nov 2023 18:25:16 +0300 Subject: [PATCH 16/24] Reduced blocks count in LightNodeBlockFieldsTest --- .../mining/LightNodeBlockFieldsTest.scala | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala index 99f4e940ee..8b7098e34d 100644 --- a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala @@ -23,9 +23,9 @@ import monix.reactive.Observable import scala.concurrent.duration.DurationInt class LightNodeBlockFieldsTest extends PropSpec with WithDomain { - property("new block fields appear 1000 blocks after LightNode activation") { + property("new block fields appear `lightNodeBlockFieldsAbsenceInterval` blocks after LightNode activation") { withDomain( - TransactionStateSnapshot.setFeaturesHeight(LightNode -> 2), + TransactionStateSnapshot.setFeaturesHeight(LightNode -> 2).configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 10)), AddrWithBalance.enoughBalances(defaultSigner, secondSigner) ) { d => withMiner( @@ -82,31 +82,31 @@ class LightNodeBlockFieldsTest extends PropSpec with WithDomain { block(3).stateHash shouldBe None block(3).challengedHeader shouldBe None - (1 to 998).foreach(_ => appendBlock()) - d.blockchain.height shouldBe 1001 - block(1001).stateHash shouldBe None + (1 to 8).foreach(_ => appendBlock()) + d.blockchain.height shouldBe 11 + block(11).stateHash shouldBe None appendMicro() - block(1001).stateHash shouldBe None + block(11).stateHash shouldBe None appendBlock() - d.blockchain.height shouldBe 1002 - val hash1 = block(1002).stateHash + d.blockchain.height shouldBe 12 + val hash1 = block(12).stateHash hash1 shouldBe defined appendMicro() - val hash2 = block(1002).stateHash + val hash2 = block(12).stateHash hash2 shouldBe defined hash2 should not be hash1 - d.rollbackTo(1000) + d.rollbackTo(10) challengeBlock() - block(1001).stateHash shouldBe None - block(1001).challengedHeader shouldBe None + block(11).stateHash shouldBe None + block(11).challengedHeader shouldBe None challengeBlock() - block(1002).stateHash shouldBe defined - block(1002).challengedHeader shouldBe defined + block(12).stateHash shouldBe defined + block(12).challengedHeader shouldBe defined } } } From e667dc91909f674e4aedccf376ec216c21813809 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 5 Dec 2023 01:18:19 +0300 Subject: [PATCH 17/24] Corrected fields checks --- .../state/appender/package.scala | 5 +- .../state/diffs/BlockDiffer.scala | 55 +++++++------------ .../transaction/TxValidationError.scala | 2 + 3 files changed, 25 insertions(+), 37 deletions(-) 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 cc9339a639..b4a78165cd 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 d4aec8e344..00ed6a5330 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, @@ -205,7 +205,7 @@ object BlockDiffer { txSignParCheck = txSignParCheck ) } - _ <- checkStateHash(blockchainWithNewBlock, block.header.stateHash, r.computedStateHash) + _ <- checkStateHash(blockchainWithNewBlock, block.header.stateHash, r.computedStateHash, hasChallenge) } yield r } @@ -272,7 +272,7 @@ object BlockDiffer { txSignParCheck = true ) } - _ <- checkStateHash(blockchain, micro.stateHash, r.computedStateHash) + _ <- checkStateHash(blockchain, micro.stateHash, r.computedStateHash, hasChallenge = false) } yield r } @@ -347,12 +347,7 @@ object BlockDiffer { prepareCaches(blockGenerator, txs, loadCacheData) - val initStateHash = - if (blockchain.supportsLightNodeBlockFields()) - computeInitialStateHash(blockchain, initSnapshot, prevStateHash) - else - ByteStr.empty - + val initStateHash = computeInitialStateHash(blockchain, initSnapshot, prevStateHash) txs .foldLeft(TracedResult(Result(initSnapshot, 0L, 0L, initConstraint, initSnapshot, initStateHash).asRight[ValidationError])) { case (acc @ TracedResult(Left(_), _, _), _) => acc @@ -394,12 +389,9 @@ object BlockDiffer { currTotalFee + txFeeInfo.wavesFee, updatedConstraint, newKeyBlockSnapshot, - if (blockchain.supportsLightNodeBlockFields()) - TxStateSnapshotHashBuilder - .createHashFromSnapshot(resultTxSnapshot, Some(TxStatusInfo(txInfo.transaction.id(), txInfo.status))) - .createHash(prevStateHash) - else - ByteStr.empty + TxStateSnapshotHashBuilder + .createHashFromSnapshot(resultTxSnapshot, Some(TxStatusInfo(txInfo.transaction.id(), txInfo.status))) + .createHash(prevStateHash) ) } } @@ -425,12 +417,7 @@ object BlockDiffer { txs: Seq[Transaction], txSnapshots: Seq[(StateSnapshot, TxMeta.Status)] ): Result = { - val initStateHash = - if (blockchain.supportsLightNodeBlockFields()) - computeInitialStateHash(blockchain, initSnapshot, prevStateHash) - else - ByteStr.empty - + 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) @@ -444,10 +431,7 @@ object BlockDiffer { currTotalFee + txFeeInfo.map(_.wavesFee).getOrElse(0L), currConstraint, keyBlockSnapshot.withTransaction(nti), - if (blockchain.supportsLightNodeBlockFields()) - TxStateSnapshotHashBuilder.createHashFromSnapshot(txSnapshot, Some(TxStatusInfo(tx.id(), txStatus))).createHash(prevStateHash) - else - ByteStr.empty + TxStateSnapshotHashBuilder.createHashFromSnapshot(txSnapshot, Some(TxStatusInfo(tx.id(), txStatus))).createHash(prevStateHash) ) } } @@ -508,13 +492,16 @@ object BlockDiffer { private def checkStateHash( blockchain: Blockchain, blockStateHash: Option[ByteStr], - computedStateHash: ByteStr + computedStateHash: ByteStr, + hasChallenge: Boolean ): TracedResult[ValidationError, Unit] = - TracedResult( - Either.cond( - !blockchain.supportsLightNodeBlockFields() || blockStateHash.contains(computedStateHash), - (), - InvalidStateHash(blockStateHash) - ) - ) + if (blockchain.supportsLightNodeBlockFields()) + if (blockStateHash.contains(computedStateHash)) + Right(()) + else + Left(InvalidStateHash(blockStateHash)) + else if (blockStateHash.isDefined || hasChallenge) + Left(UnexpectedLightNodeFields) + else + Right(()) } diff --git a/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala b/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala index ff057fd7f7..38ea50a5de 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala @@ -51,6 +51,8 @@ object TxValidationError { case class InvalidStateHash(blockStateHash: Option[ByteStr]) extends ValidationError + case object UnexpectedLightNodeFields extends ValidationError + sealed trait WithLog extends Product with Serializable { def log: Log[Id] def toStringWithLog(limit: Int): String From 510ca734e9c474024ee824f2db86c20efd9e480d Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 5 Dec 2023 01:23:45 +0300 Subject: [PATCH 18/24] Adapted tests --- node/src/test/scala/com/wavesplatform/history/Domain.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index eacb987fda..58def9c43c 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) From 054505e5189bbc146f39961ee76761a975f0e6ce Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 5 Dec 2023 01:50:31 +0300 Subject: [PATCH 19/24] Adapted tests --- node/src/test/scala/com/wavesplatform/db/WithState.scala | 6 ++---- .../test/scala/com/wavesplatform/state/LightNodeTest.scala | 5 ++++- .../com/wavesplatform/state/diffs/BlockDifferTest.scala | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/node/src/test/scala/com/wavesplatform/db/WithState.scala b/node/src/test/scala/com/wavesplatform/db/WithState.scala index 515edb7345..229d1c8e0f 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)) diff --git a/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala b/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala index afa6eb535f..4825ec5dd0 100644 --- a/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala @@ -168,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 d50637a1c4..798a74369b 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala @@ -15,7 +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.WavesSettingsOps +import com.wavesplatform.test.DomainPresets.{TransactionStateSnapshot, WavesSettingsOps} import com.wavesplatform.test.node.* import com.wavesplatform.transaction.TxValidationError.InvalidStateHash import com.wavesplatform.transaction.{TxHelpers, TxVersion} @@ -110,7 +110,7 @@ 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 => + withDomain(TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0))) { d => val block = createGenesisWithStateHash(txs, fillStateHash = true) block.header.stateHash shouldBe defined @@ -128,7 +128,7 @@ class BlockDifferTest extends FreeSpec with WithDomain { } "arbitrary block/microblock" in - withDomain(DomainPresets.TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0))) { d => + withDomain(TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0))) { d => val genesis = createGenesisWithStateHash(Seq(TxHelpers.genesis(TxHelpers.address(1))), fillStateHash = true) d.appendBlock(genesis) From 1fab9512ba327160f4c3b644033dd4e1271671a2 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 5 Dec 2023 02:21:47 +0300 Subject: [PATCH 20/24] Test that new fields are prohibited for an interval --- .../mining/LightNodeBlockFieldsTest.scala | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala index 8b7098e34d..81826ff74f 100644 --- a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala @@ -11,7 +11,7 @@ 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 +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 @@ -110,4 +110,33 @@ class LightNodeBlockFieldsTest extends PropSpec with WithDomain { } } } + + 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, verify = false, timeDrift = Int.MaxValue) { case (_, append) => + (1 to 9).foreach(_ => d.appendBlock()) + d.blockchain.height shouldBe 10 + val challengedBlock = d.createBlock(ProtoBlockVersion, Nil, stateHash = Some(Some(ByteStr.fill(DigestLength)(1)))) + val challengingBlock = d.createChallengingBlock(defaultSigner, challengedBlock) + append(challengedBlock) should produce("UnexpectedLightNodeFields") + append(challengingBlock) should produce("UnexpectedLightNodeFields") + + d.appendBlock() + d.blockchain.height shouldBe 11 + val correctBlockWithStateHash = d.createBlock(ProtoBlockVersion, Nil) + correctBlockWithStateHash.header.stateHash shouldBe defined + append(correctBlockWithStateHash) shouldBe a[Right[?, ?]] + + d.rollbackTo(11) + val invalidBlock = d.createBlock(ProtoBlockVersion, Nil, stateHash = Some(Some(ByteStr.fill(DigestLength)(1)))) + val challengingBlock2 = d.createChallengingBlock(defaultSigner, invalidBlock) + append(challengingBlock2) shouldBe a[Right[?, ?]] + } + } + } } From dd82095b3ace5fd0b8cbe937afd837a22a5f2169 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 5 Dec 2023 11:32:43 +0300 Subject: [PATCH 21/24] Adapted light node mining --- .../sync/lightnode/LightNodeMiningSuite.scala | 27 +++++++------ .../com/wavesplatform/mining/Miner.scala | 39 ++++++++++--------- 2 files changed, 36 insertions(+), 30 deletions(-) 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 c8b8ef4510..8ed66b0e31 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/src/main/scala/com/wavesplatform/mining/Miner.scala b/node/src/main/scala/com/wavesplatform/mining/Miner.scala index ad92e72409..77163abde0 100644 --- a/node/src/main/scala/com/wavesplatform/mining/Miner.scala +++ b/node/src/main/scala/com/wavesplatform/mining/Miner.scala @@ -91,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] = From 7900745e5feb706431be5574b0bdca23295c88a9 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Wed, 6 Dec 2023 14:00:19 +0300 Subject: [PATCH 22/24] Removed excessive validation and adapted test --- .../state/diffs/BlockDiffer.scala | 9 +++----- .../transaction/TxValidationError.scala | 2 -- .../mining/LightNodeBlockFieldsTest.scala | 23 +++++++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) 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 00ed6a5330..19ea2ff00d 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala @@ -205,7 +205,7 @@ object BlockDiffer { txSignParCheck = txSignParCheck ) } - _ <- checkStateHash(blockchainWithNewBlock, block.header.stateHash, r.computedStateHash, hasChallenge) + _ <- checkStateHash(blockchainWithNewBlock, block.header.stateHash, r.computedStateHash) } yield r } @@ -272,7 +272,7 @@ object BlockDiffer { txSignParCheck = true ) } - _ <- checkStateHash(blockchain, micro.stateHash, r.computedStateHash, hasChallenge = false) + _ <- checkStateHash(blockchain, micro.stateHash, r.computedStateHash) } yield r } @@ -492,16 +492,13 @@ object BlockDiffer { private def checkStateHash( blockchain: Blockchain, blockStateHash: Option[ByteStr], - computedStateHash: ByteStr, - hasChallenge: Boolean + computedStateHash: ByteStr ): TracedResult[ValidationError, Unit] = if (blockchain.supportsLightNodeBlockFields()) if (blockStateHash.contains(computedStateHash)) Right(()) else Left(InvalidStateHash(blockStateHash)) - else if (blockStateHash.isDefined || hasChallenge) - Left(UnexpectedLightNodeFields) else Right(()) } diff --git a/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala b/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala index 38ea50a5de..ff057fd7f7 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala @@ -51,8 +51,6 @@ object TxValidationError { case class InvalidStateHash(blockStateHash: Option[ByteStr]) extends ValidationError - case object UnexpectedLightNodeFields extends ValidationError - sealed trait WithLog extends Product with Serializable { def log: Log[Id] def toStringWithLog(limit: Int): String diff --git a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala index 81826ff74f..62455fab6c 100644 --- a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala @@ -23,6 +23,8 @@ 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)), @@ -65,7 +67,7 @@ class LightNodeBlockFieldsTest extends PropSpec with WithDomain { microBlockMiner.generateOneMicroBlockTask(defaultSigner, d.lastBlock, Unlimited, 0).runSyncUnsafe() } def challengeBlock() = { - val invalidBlock = d.createBlock(ProtoBlockVersion, Seq(), strictTime = true, stateHash = Some(Some(ByteStr.fill(DigestLength)(1)))) + val invalidBlock = d.createBlock(ProtoBlockVersion, Seq(), strictTime = true, stateHash = invalidStateHash) challenger.challengeBlock(invalidBlock, null).runSyncUnsafe() } @@ -118,23 +120,26 @@ class LightNodeBlockFieldsTest extends PropSpec with WithDomain { TransactionStateSnapshot.setFeaturesHeight(LightNode -> 2).configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 10)), AddrWithBalance.enoughBalances(defaultSigner, secondSigner) ) { d => - withMiner(d.blockchain, d.testTime, d.settings, verify = false, timeDrift = Int.MaxValue) { case (_, append) => + 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, stateHash = Some(Some(ByteStr.fill(DigestLength)(1)))) - val challengingBlock = d.createChallengingBlock(defaultSigner, challengedBlock) - append(challengedBlock) should produce("UnexpectedLightNodeFields") - append(challengingBlock) should produce("UnexpectedLightNodeFields") + val challengedBlock = d.createBlock(ProtoBlockVersion, Nil, strictTime = true, stateHash = invalidStateHash) + val challengingBlock = d.createChallengingBlock(secondSigner, challengedBlock, strictTime = true) + 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") d.appendBlock() d.blockchain.height shouldBe 11 - val correctBlockWithStateHash = d.createBlock(ProtoBlockVersion, Nil) + 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 = Some(Some(ByteStr.fill(DigestLength)(1)))) - val challengingBlock2 = d.createChallengingBlock(defaultSigner, invalidBlock) + 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[?, ?]] } } From 33005f4f1bca3202ec9cf2773e80bd783406ca4a Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 7 Dec 2023 11:26:07 +0300 Subject: [PATCH 23/24] Improved test --- .../com/wavesplatform/mining/LightNodeBlockFieldsTest.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala index 62455fab6c..34ef6e178c 100644 --- a/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/LightNodeBlockFieldsTest.scala @@ -125,9 +125,15 @@ class LightNodeBlockFieldsTest extends PropSpec with WithDomain { 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 From 3423863fff1c160fe506f7211f26a19b40a05dec Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Thu, 7 Dec 2023 11:28:21 +0300 Subject: [PATCH 24/24] Reduced code --- .../com/wavesplatform/state/diffs/BlockDiffer.scala | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 19ea2ff00d..5b6b69f867 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala @@ -494,11 +494,9 @@ object BlockDiffer { blockStateHash: Option[ByteStr], computedStateHash: ByteStr ): TracedResult[ValidationError, Unit] = - if (blockchain.supportsLightNodeBlockFields()) - if (blockStateHash.contains(computedStateHash)) - Right(()) - else - Left(InvalidStateHash(blockStateHash)) - else - Right(()) + Either.cond( + !blockchain.supportsLightNodeBlockFields() || blockStateHash.contains(computedStateHash), + (), + InvalidStateHash(blockStateHash) + ) }