Skip to content

Commit

Permalink
added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
phearnot committed Jun 28, 2024
1 parent 703a533 commit b36eddb
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -623,8 +623,8 @@ object BlockAppended {

// updatedWavesAmount can change as a result of either genesis transactions or miner rewards
val wavesAmount = blockchainBeforeWithReward.wavesAmount(height).toLong
val updatedWavesAmount = wavesAmount + reward.filter(_ => height > 0).getOrElse(0L)

val rewardBoost = if (blockchainBeforeWithReward.isBlockRewardBoostActive(height + 1)) BlockRewardCalculator.RewardBoost else 1
val updatedWavesAmount = wavesAmount + reward.filter(_ => height > 0).getOrElse(0L) * rewardBoost
val activatedFeatures = blockchainBeforeWithReward.activatedFeatures.collect {
case (id, activationHeight) if activationHeight == height + 1 => id.toInt
}.toSeq
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import com.wavesplatform.lang.v1.compiler.Terms.FUNCTION_CALL
import com.wavesplatform.lang.v1.compiler.TestCompiler
import com.wavesplatform.protobuf.*
import com.wavesplatform.protobuf.block.PBBlocks
import com.wavesplatform.protobuf.transaction.{DataEntry, InvokeScriptResult}
import com.wavesplatform.protobuf.transaction.InvokeScriptResult.{Call, Invocation, Payment}
import com.wavesplatform.protobuf.transaction.{DataEntry, InvokeScriptResult}
import com.wavesplatform.settings.{Constants, WavesSettings}
import com.wavesplatform.state.{AssetDescription, BlockRewardCalculator, EmptyDataEntry, Height, LeaseBalance, StringDataEntry}
import com.wavesplatform.test.*
Expand Down Expand Up @@ -1093,6 +1093,33 @@ class BlockchainUpdatesSpec extends FreeSpec with WithBUDomain with ScalaFutures
)
}
}

"should return correct updated_waves_amount when reward boost is active" in {
val settings = ConsensusImprovements
.setFeaturesHeight(
BlockchainFeatures.BlockReward -> 0,
BlockchainFeatures.BlockRewardDistribution -> 0,
BlockchainFeatures.BoostBlockReward -> 5
)
.configure(fs =>
fs.copy(blockRewardBoostPeriod = 10)
)

withDomainAndRepo(settings) { case (d, repo) =>
d.appendBlock()
val subscription = repo.createFakeObserver(SubscribeRequest.of(1, 0))

(1 to 15).foreach(_ => d.appendBlock())


subscription
.fetchAllEvents(d.blockchain)
.map(_.getUpdate.getAppend.getBlock.updatedWavesAmount) shouldBe
(2 to 16).scanLeft(100_000_000.waves) { (total, height) => total + 6.waves * (if (d.blockchain.isBlockRewardBoostActive(height)) BlockRewardCalculator.RewardBoost else 1) }


}
}
}

private def assertCommon(rollback: RollbackResult): Assertion = {
Expand Down
6 changes: 5 additions & 1 deletion node/src/main/scala/com/wavesplatform/Application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,11 @@ object Application extends ScorexLogging {
.orElse(db.get(Keys.blockMetaAt(Height(height))).flatMap(BlockMeta.fromPb))
.map { blockMeta =>
val rewardShares = BlockRewardCalculator.getSortedBlockRewardShares(height, blockMeta.header.generator.toAddress, blockchainUpdater)
blockMeta.copy(rewardShares = rewardShares)
val rewardBoost = if (blockchainUpdater.isBlockRewardBoostActive(height)) BlockRewardCalculator.RewardBoost else 1
blockMeta.copy(
rewardShares = rewardShares,
reward = blockMeta.reward.map(_ * rewardBoost)
)
}

def main(args: Array[String]): Unit = {
Expand Down
3 changes: 2 additions & 1 deletion node/src/main/scala/com/wavesplatform/database/Caches.scala
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ abstract class Caches extends Blockchain with Storage {
reward.getOrElse(0),
if (block.header.version >= Block.ProtoBlockVersion) ByteString.copyFrom(hitSource.arr) else ByteString.EMPTY,
ByteString.copyFrom(newScore.toByteArray),
current.meta.fold(settings.genesisSettings.initialBalance)(_.totalWavesAmount) + reward.getOrElse(0L)
current.meta.fold(settings.genesisSettings.initialBalance)(_.totalWavesAmount) +
(reward.getOrElse(0L) * (if (this.isBlockRewardBoostActive(newHeight)) BlockRewardCalculator.RewardBoost else 1))
)
current = CurrentBlockInfo(Height(newHeight), Some(newMeta), block.transactionData)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,7 @@ object BlockRewardCalculator {
calculateRewards(fullBlockReward, CurrentBlockRewardPart.apply(fullBlockReward), daoAddress, modifiedXtnBuybackAddress)
}
} else BlockRewardShares(fullBlockReward, 0, 0)
}.multiply(
if (
blockchain
.featureActivationHeight(BlockchainFeatures.BoostBlockReward.id)
.exists { boostHeight =>
boostHeight <= height && height < boostHeight + blockchain.settings.functionalitySettings.blockRewardBoostPeriod
}
) RewardBoost
else 1
)
}.multiply(if (blockchain.isBlockRewardBoostActive(height)) RewardBoost else 1)

def getSortedBlockRewardShares(height: Int, fullBlockReward: Long, generator: Address, blockchain: Blockchain): Seq[(Address, Long)] = {
val daoAddress = blockchain.settings.functionalitySettings.daoAddressParsed.toOption.flatten
Expand Down
7 changes: 7 additions & 0 deletions node/src/main/scala/com/wavesplatform/state/Blockchain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,12 @@ object Blockchain {

def supportsLightNodeBlockFields(height: Int = blockchain.height): Boolean =
blockchain.featureActivationHeight(LightNode.id).exists(height >= _ + blockchain.settings.functionalitySettings.lightNodeBlockFieldsAbsenceInterval)

def isBlockRewardBoostActive(height: Int): Boolean =
blockchain
.featureActivationHeight(BlockchainFeatures.BoostBlockReward.id)
.exists { boostHeight =>
boostHeight <= height && height < boostHeight + blockchain.settings.functionalitySettings.blockRewardBoostPeriod
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,9 @@ class BlockchainUpdaterImpl(
)
miner.scheduleMining(Some(tempBlockchain))

log.trace(s"Persisting block ${referencedForgedBlock.id()}, discarded microblock refs: ${discarded.map(_._1.reference).mkString("[", ",", "]")}")
log.trace(
s"Persisting block ${referencedForgedBlock.id()}, discarded microblock refs: ${discarded.map(_._1.reference).mkString("[", ",", "]")}"
)

if (discarded.nonEmpty) {
blockchainUpdateTriggers.onMicroBlockRollback(this, block.header.reference)
Expand Down Expand Up @@ -629,7 +631,8 @@ class BlockchainUpdaterImpl(
override def wavesAmount(height: Int): BigInt = readLock {
ngState match {
case Some(ng) if this.height == height =>
rocksdb.wavesAmount(height - 1) + BigInt(ng.reward.getOrElse(0L))
rocksdb.wavesAmount(height - 1) +
BigInt(ng.reward.getOrElse(0L)) * (if (this.isBlockRewardBoostActive(height)) BlockRewardCalculator.RewardBoost else 1)
case _ =>
rocksdb.wavesAmount(height)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1423,5 +1423,12 @@ class BlockRewardSpec extends FreeSpec with WithDomain {
daoAddress -> rewardAfterDeactivation,
xtnBuybackAddress -> rewardAfterDeactivation
)

d.blockchain.wavesAmount(15) shouldBe
BigInt(100_000_000.waves + // 1: genesis
3 * 6.waves + // 2..4: before boost activation
5 * 60.waves + // 5..9: boosted reward before change
5 * (6.waves + rewardDelta) * 10 + // 10..14: boosted reward after change
6.waves + rewardDelta) // 15: non-boosted after change
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import com.wavesplatform.state.{BlockRewardCalculator, Blockchain}
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.{Order, OrderType}
import com.wavesplatform.transaction.{TxHelpers, TxVersion}
import com.wavesplatform.utils.{SharedSchedulerMixin, SystemTime}
import monix.reactive.Observable
import org.scalactic.source.Position
import org.scalamock.scalatest.PathMockFactory
import org.scalatest.Assertion
import play.api.libs.json.*
Expand Down Expand Up @@ -504,4 +505,78 @@ class BlocksApiRouteSpec
.toMap shouldBe heightToResult
}
}

"Boost block reward feature changes API response" in {
val miner = TxHelpers.signer(3001)
val daoAddress = TxHelpers.address(3002)
val xtnAddress = TxHelpers.address(3003)

val settings = DomainPresets.ConsensusImprovements
.setFeaturesHeight(
BlockchainFeatures.BlockRewardDistribution -> 0,
BlockchainFeatures.CappedReward -> 0,
BlockchainFeatures.BoostBlockReward -> 5,
BlockchainFeatures.CeaseXtnBuyback -> 0
)
.configure(fs =>
fs.copy(
xtnBuybackRewardPeriod = 10,
blockRewardBoostPeriod = 10,
xtnBuybackAddress = Some(xtnAddress.toString),
daoAddress = Some(daoAddress.toString)
)
)

withDomain(settings, Seq(AddrWithBalance(miner.toAddress, 100_000.waves))) { d =>
val route = new BlocksApiRoute(d.settings.restAPISettings, d.blocksApi, SystemTime, new RouteTimeout(60.seconds)(sharedScheduler)).route

def checkRewardAndShares(height: Int, expectedReward: Long, expectedMinerShare: Long, expectedDaoShare: Long, expectedXtnShare: Option[Long])(
implicit pos: Position
): Unit = {
Seq("/headers/at/", "/at/").foreach { prefix =>
val path = routePath(s"$prefix$height")
withClue(path) {
Get(path) ~> route ~> check {
val jsonResp = responseAs[JsObject]
withClue(" reward:") {
(jsonResp \ "reward").as[Long] shouldBe expectedReward
}
val shares = (jsonResp \ "rewardShares").as[JsObject]
withClue(" miner share: ") {
(shares \ miner.toAddress.toString).as[Long] shouldBe expectedMinerShare
}
withClue(" dao share: ") {
(shares \ daoAddress.toString).as[Long] shouldBe expectedDaoShare
}
withClue(" XTN share: ") {
(shares \ xtnAddress.toString).asOpt[Long] shouldBe expectedXtnShare
}
}
}
}
}

(1 to 3).foreach(_ => d.appendKeyBlock(miner))
d.blockchain.height shouldBe 4
(1 to 3).foreach { h =>
checkRewardAndShares(h + 1, 6.waves, 2.waves, 2.waves, Some(2.waves))
}

// reward boost activation
(1 to 5).foreach(_ => d.appendKeyBlock(miner))
(1 to 5).foreach { h =>
checkRewardAndShares(h + 4, 60.waves, 20.waves, 20.waves, Some(20.waves))
}

// cease XTN buyback
(1 to 5).foreach(_ => d.appendKeyBlock(miner))
(1 to 5).foreach { h =>
checkRewardAndShares(h + 9, 60.waves, 40.waves, 20.waves, None)
}

d.appendKeyBlock(miner)
d.blockchain.height shouldBe 15
checkRewardAndShares(15, 6.waves, 4.waves, 2.waves, None)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.wavesplatform.lang.Testing.*
import com.wavesplatform.lang.directives.values.*
import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet}
import com.wavesplatform.lang.script.ContractScript
import com.wavesplatform.lang.v1.compiler.Terms.CONST_LONG
import com.wavesplatform.lang.v1.compiler.{ContractCompiler, TestCompiler}
import com.wavesplatform.lang.v1.estimator.v2.ScriptEstimatorV2
import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext
Expand All @@ -31,6 +32,7 @@ import com.wavesplatform.transaction.smart.script.ScriptCompiler
import com.wavesplatform.transaction.{TxHelpers, TxVersion}
import com.wavesplatform.utils.*
import org.scalatest.Assertion
import org.scalatest.OptionValues.convertOptionToValuable
import shapeless.Coproduct

class ContextFunctionsTest extends PropSpec with WithDomain with EthHelpers {
Expand Down Expand Up @@ -618,6 +620,84 @@ class ContextFunctionsTest extends PropSpec with WithDomain with EthHelpers {
}
}

property("Block reward boost is visible in dApp scripts") {
val daoAddress = TxHelpers.address(1003).toString
val xtnAddress = TxHelpers.address(1004).toString
val miner = TxHelpers.signer(1005)
val invoker = TxHelpers.signer(1006)
val dapp = TxHelpers.signer(1007)
val settings = ConsensusImprovements
.setFeaturesHeight(
BlockchainFeatures.BlockRewardDistribution -> 0,
BlockchainFeatures.CappedReward -> 0,
BlockchainFeatures.BoostBlockReward -> 5
)
.configure(fs =>
fs.copy(
blockRewardBoostPeriod = 10,
daoAddress = Some(daoAddress),
xtnBuybackAddress = Some(xtnAddress)
)
)

withDomain(
settings,
Seq(
AddrWithBalance(miner.toAddress, 100_000.waves),
AddrWithBalance(invoker.toAddress, 100.waves),
AddrWithBalance(dapp.toAddress, 100.waves)
)
) { d =>
d.appendBlock(
TxHelpers.setScript(
dapp,
TestCompiler(V7).compileContract(s"""
func findRewardsFor(rewards: List[(Address, Int)], addr: Address) = {
func check(prev: Int|Unit, next: (Address, Int)) = match prev {
case _: Unit =>
let (thisAddr, share) = next
if (thisAddr == addr) then share else unit
case _ => prev
}
FOLD<3>(rewards, unit, check)
}
@Callable(i)
func storeBlockInfo(height: Int) = {
let prefix = i.transactionId.toBase58String() + "_"
let blockInfo = blockInfoByHeight(height).value()
[
IntegerEntry(prefix + "miner", findRewardsFor(blockInfo.rewards, blockInfo.generator).valueOrElse(0)),
IntegerEntry(prefix + "dao", findRewardsFor(blockInfo.rewards, addressFromStringValue("$daoAddress")).valueOrElse(0)),
IntegerEntry(prefix + "xtn", findRewardsFor(blockInfo.rewards, addressFromStringValue("$xtnAddress")).valueOrElse(0))
]
}""")
)
)

def checkHeight(height: Int, minerShare: Long, daoShare: Long, xtnShare: Long): Unit = {
val invocation = TxHelpers.invoke(dapp.toAddress, Some("storeBlockInfo"), Seq(CONST_LONG(height)), invoker = invoker)

d.appendBlock(d.createBlock(Block.RewardBlockVersion, Seq(invocation), generator = miner))

d.blockchain.accountData(dapp.toAddress, invocation.id().toString + "_miner").value.value shouldBe minerShare
d.blockchain.accountData(dapp.toAddress, invocation.id().toString + "_dao").value.value shouldBe daoShare
d.blockchain.accountData(dapp.toAddress, invocation.id().toString + "_xtn").value.value shouldBe xtnShare
}

checkHeight(3, 2.waves, 2.waves, 2.waves)
d.appendBlock()
(1 to 10).foreach(i => checkHeight(i + 4, 20.waves, 20.waves, 20.waves))
checkHeight(15, 2.waves, 2.waves, 2.waves)

// check historic blocks again after deactivation
checkHeight(3, 2.waves, 2.waves, 2.waves)
checkHeight(5, 20.waves, 20.waves, 20.waves)
checkHeight(15, 2.waves, 2.waves, 2.waves)
}
}

property("transfer transaction by id") {
val (masterAcc, _, genesis, setScriptTransactions, dataTransaction, transferTx, transfer2) = preconditionsAndPayments
setScriptTransactions.foreach { setScriptTransaction =>
Expand Down

0 comments on commit b36eddb

Please sign in to comment.