Skip to content

Commit

Permalink
NODE-2638 Improved RIDE calculateDelay() (#3922)
Browse files Browse the repository at this point in the history
  • Loading branch information
xrtm000 authored Dec 6, 2023
1 parent 05430ea commit a518c18
Show file tree
Hide file tree
Showing 13 changed files with 64 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ object EnvironmentFunctionsBenchmark {

override def accountScript(addressOrAlias: Recipient): Option[Script] = ???

override def calculateDelay(hitSource: ByteStr, baseTarget: Long, generator: ByteStr, balance: Long): Long = ???
override def calculateDelay(generator: ByteStr, balance: Long): Long = ???

def callScript(
dApp: Address,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,24 @@ class CalculateDelayBenchmark {

@Benchmark
def calculateDelay1(bh: Blackhole, st: St): Unit =
bh.consume(st.environment.calculateDelay(ByteStr.empty, 0, ByteStr.empty, 0))
bh.consume(st.environment.calculateDelay(ByteStr.empty, 0))

@Benchmark
def calculateDelay2(bh: Blackhole, st: St): Unit =
bh.consume(
st.environment.calculateDelay(ByteStr.fill(96)(127), Long.MaxValue, ByteStr.fill(26)(127), Long.MaxValue)
st.environment.calculateDelay(ByteStr.fill(26)(127), Long.MaxValue)
)

@Benchmark
def calculateDelay3(bh: Blackhole, st: St): Unit =
bh.consume(
st.environment.calculateDelay(ByteStr.fill(96)(-128), Long.MinValue, ByteStr.fill(26)(-128), Long.MinValue)
st.environment.calculateDelay(ByteStr.fill(26)(-128), Long.MinValue)
)

@Benchmark
def calculateDelay4(bh: Blackhole, st: St): Unit =
bh.consume(
st.environment.calculateDelay(ByteStr.fill(32)(32), 123456, ByteStr.fill(26)(32), 100_000_000)
st.environment.calculateDelay(ByteStr.fill(26)(32), 100_000_000)
)
}

Expand Down
4 changes: 2 additions & 2 deletions lang/doc/v8/funcs/blockchain-functions.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@
}
{
name: "calculateDelay"
params: [ "ByteVector", "Int", "Address", "Int" ]
params: [ "Address", "Int" ]
doc: "Calculates mining delay using Fair PoS calculator."
paramsDoc: [ "hit source", "base target", "generator address", "generator balance" ]
paramsDoc: [ "generator address", "generator balance" ]
complexity: 1
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ package object utils {
override def addressFromString(address: String): Either[String, Recipient.Address] = ???
override def addressFromPublicKey(publicKey: ByteStr): Either[String, Address] = ???
override def accountScript(addressOrAlias: Recipient): Option[Script] = ???
override def calculateDelay(hs: ByteStr, bt: Long, gt: ByteStr, b: Long): Long = ???
override def calculateDelay(gt: ByteStr, b: Long): Long = ???
override def callScript(
dApp: Address,
func: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1002,8 +1002,6 @@ object Functions {
val calculateDelay: NativeFunction[Environment] = {
val args =
Seq(
("hit source", BYTESTR),
("base target", LONG),
("generator", addressType),
("balance", LONG)
)
Expand All @@ -1014,24 +1012,23 @@ object Functions {
LONG,
args*
) {
val MaxHitSourceLength = 96
new ContextfulNativeFunction.Simple[Environment]("calculateDelay", LONG, args) {
override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] =
args match {
case CONST_BYTESTR(hitSource) :: CONST_LONG(baseTarget) :: CaseObj(`addressType`, fields) :: CONST_LONG(balance) :: Nil =>
case CaseObj(`addressType`, fields) :: CONST_LONG(balance) :: Nil =>
val addressBytes = fields("bytes").asInstanceOf[CONST_BYTESTR].bs
if (addressBytes.size > AddressLength) {
val error = CommonError(s"Address bytes length = ${addressBytes.size} exceeds limit = $AddressLength")
(error: ExecutionError).asLeft[EVALUATED].pure[F]
} else if (hitSource.size > MaxHitSourceLength) {
val error = CommonError(s"Hit source bytes length = ${hitSource.size} exceeds limit = $MaxHitSourceLength")
} else if (balance <= 0) {
val error = CommonError(s"Unexpected non-positive balance = $balance")
(error: ExecutionError).asLeft[EVALUATED].pure[F]
} else {
val delay = env.calculateDelay(hitSource, baseTarget, addressBytes, balance)
val delay = env.calculateDelay(addressBytes, balance)
(CONST_LONG(delay): EVALUATED).asRight[ExecutionError].pure[F]
}
case xs =>
notImplemented[Id, EVALUATED]("calculateDelay(hitSource: ByteVector, baseTarget: ByteVector, generator: Address, balance: Long)", xs)
notImplemented[Id, EVALUATED]("calculateDelay(generator: Address, balance: Long)", xs)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ trait Environment[F[_]] {
availableComplexity: Int,
reentrant: Boolean
): Coeval[F[(Either[ValidationError, (EVALUATED, Log[F])], Int)]]
def calculateDelay(hitSource: ByteStr, baseTarget: Long, generator: ByteStr, balance: Long): Long
def calculateDelay(generator: ByteStr, balance: Long): Long
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import com.wavesplatform.lang.v1.CTX
import com.wavesplatform.lang.v1.compiler.Terms.*
import com.wavesplatform.lang.v1.compiler.Types.*
import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext
import com.wavesplatform.lang.v1.evaluator.{EvaluatorV1, Log}
import com.wavesplatform.lang.v1.evaluator.EvaluatorV1.*
import com.wavesplatform.lang.v1.evaluator.ctx.*
import com.wavesplatform.lang.v1.evaluator.ctx.impl.{EnvironmentFunctions, PureContext, *}
import com.wavesplatform.lang.v1.evaluator.ctx.impl.*
import com.wavesplatform.lang.v1.evaluator.{EvaluatorV1, Log}
import com.wavesplatform.lang.v1.traits.domain.Recipient.Address
import com.wavesplatform.lang.v1.traits.domain.{BlockInfo, Recipient, ScriptAssetInfo, Tx}
import com.wavesplatform.lang.v1.traits.{DataType, Environment}
Expand Down Expand Up @@ -96,7 +96,7 @@ object Common {
def addressFromString(address: String): Either[String, Recipient.Address] = ???
def addressFromPublicKey(publicKey: ByteStr): Either[String, Address] = ???
def accountScript(addressOrAlias: Recipient): Option[Script] = ???
def calculateDelay(hs: ByteStr, bt: Long, gt: ByteStr, b: Long): Long = ???
def calculateDelay(gt: ByteStr, b: Long): Long = ???
def callScript(
dApp: Address,
func: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,7 @@ class DecompilerTest extends PropSpec {
}

property("calculateDelay()") {
val script = "calculateDelay(base58'aaa', 123, Address(base58'bbb'), 456)"
val script = "calculateDelay(Address(base58'bbb'), 456)"
assertDecompile(script, script, V8)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.wavesplatform.transaction.smart

import cats.implicits.catsSyntaxSemigroup
import cats.Id
import cats.implicits.catsSyntaxSemigroup
import cats.syntax.either.*
import com.wavesplatform.account
import com.wavesplatform.account.{AddressOrAlias, PublicKey}
Expand All @@ -26,7 +26,6 @@ import com.wavesplatform.state.*
import com.wavesplatform.state.BlockRewardCalculator.CurrentBlockRewardPart
import com.wavesplatform.state.diffs.invoke.InvokeScriptDiff.validateIntermediateBalances
import com.wavesplatform.state.diffs.invoke.{InvokeScript, InvokeScriptDiff, InvokeScriptTransactionLike}
import com.wavesplatform.state.SnapshotBlockchain
import com.wavesplatform.transaction.Asset.*
import com.wavesplatform.transaction.TxValidationError.{FailedTransactionError, GenericError}
import com.wavesplatform.transaction.assets.exchange.Order
Expand Down Expand Up @@ -268,9 +267,15 @@ class WavesEnvironment(
reentrant: Boolean
): Coeval[(Either[ValidationError, (EVALUATED, Log[Id])], Int)] = ???

override def calculateDelay(hitSource: ByteStr, baseTarget: Long, generator: ByteStr, balance: Long): Long = {
override def calculateDelay(generator: ByteStr, balance: Long): Long = {
val baseTarget = blockchain.lastBlockHeader.map(_.header.baseTarget).getOrElse(0L)
val hitSource =
blockchain
.vrf(blockchain.height)
.orElse(blockchain.lastBlockHeader.map(_.header.generationSignature))
.getOrElse(ByteStr.empty)
val hit = Global.blake2b256(hitSource.arr ++ generator.arr).take(PoSCalculator.HitSize)
FairPoSCalculator.V2.calculateDelay(BigInt(1, hit), baseTarget, balance)
FairPoSCalculator(0, 0).calculateDelay(BigInt(1, hit), baseTarget, balance)
}

private def getRewards(generator: PublicKey, height: Int): Seq[(Address, Long)] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.db.WithDomain
import com.wavesplatform.db.WithState.AddrWithBalance
import com.wavesplatform.lang.directives.values.V8
import com.wavesplatform.lang.v1.compiler.Terms.CONST_LONG
import com.wavesplatform.lang.v1.compiler.TestCompiler
import com.wavesplatform.state.IntegerDataEntry
import com.wavesplatform.test.*
import com.wavesplatform.transaction.TxHelpers.*

Expand All @@ -20,25 +22,34 @@ class CalculateDelayTest extends PropSpec with WithDomain {
| let address1 = i.caller
| let address2 = Address(base58'${signer(2).toAddress}')
| let address3 = Address(base58'${signer(3).toAddress}')
| let lowest = calculateDelay(hitSource, lastBlock.baseTarget, address1, 10 * 1000 * 1000)
| let medium = calculateDelay(hitSource, lastBlock.baseTarget, address2, 30 * 1000 * 1000)
| let largest = calculateDelay(hitSource, lastBlock.baseTarget, address3, 90 * 1000 * 1000)
| let lowest = calculateDelay(address1, 10 * 1000 * 1000)
| let medium = calculateDelay(address2, 30 * 1000 * 1000)
| let largest = calculateDelay(address3, 90 * 1000 * 1000)
| [
| IntegerEntry("lowest", lowest),
| IntegerEntry("medium", medium),
| IntegerEntry("largest", largest)
| ]
| }
|
| @Callable(i)
| func results() =
| [
| IntegerEntry("1", calculateDelay(Address(base58''), 1)),
| IntegerEntry("2", calculateDelay(Address(base58'${ByteStr.fill(26)(1)}'), ${Int.MaxValue})),
| IntegerEntry("3", calculateDelay(Address(base58'${ByteStr.fill(26)(1)}'), ${100_000L * Int.MaxValue})),
| IntegerEntry("4", calculateDelay(Address(base58'${ByteStr.fill(26)(1)}'), ${200_000L * Int.MaxValue}))
| ]
|
| @Callable(i)
| func error1() = {
| strict r = calculateDelay(base58'${ByteStr.fill(97)(1)}', 0, i.caller, 0)
| strict r = calculateDelay(Address(base58'${ByteStr.fill(27)(1)}'), 1)
| []
| }
|
| @Callable(i)
| func error2() = {
| strict r = calculateDelay(lastBlock.generationSignature, 0, Address(base58'${ByteStr.fill(27)(1)}'), 0)
| func error2(balance: Int) = {
| strict r = calculateDelay(Address(base58''), balance)
| []
| }
""".stripMargin
Expand All @@ -61,11 +72,25 @@ class CalculateDelayTest extends PropSpec with WithDomain {
}
}

property("results") {
withDomain(TransactionStateSnapshot, AddrWithBalance.enoughBalances(secondSigner)) { d =>
d.appendBlock(setScript(secondSigner, contract))
d.appendBlock(invoke(func = Some("results")))
d.liquidSnapshot.accountData.head._2.values.toSeq shouldBe Seq(
IntegerDataEntry("1", 1418883),
IntegerDataEntry("2", 70064),
IntegerDataEntry("3", 1),
IntegerDataEntry("4", 0)
)
}
}

property("errors of calculateDelay()") {
withDomain(TransactionStateSnapshot, AddrWithBalance.enoughBalances(secondSigner)) { d =>
d.appendBlock(setScript(secondSigner, contract))
d.appendBlockE(invoke(func = Some("error1"))) should produce("Hit source bytes length = 97 exceeds limit = 96")
d.appendBlockE(invoke(func = Some("error2"))) should produce("Address bytes length = 27 exceeds limit = 26")
d.appendBlockE(invoke(func = Some("error1"))) should produce("Address bytes length = 27 exceeds limit = 26")
d.appendBlockE(invoke(func = Some("error2"), args = Seq(CONST_LONG(-1)))) should produce("Unexpected non-positive balance = -1")
d.appendBlockE(invoke(func = Some("error2"), args = Seq(CONST_LONG(0)))) should produce("Unexpected non-positive balance = 0")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ case class ErrorMessageEnvironment[F[_]](message: String) extends Environment[F]
override def addressFromString(address: String): Either[String, Recipient.Address] = unavailable
override def addressFromPublicKey(publicKey: ByteStr): Either[String, Address] = unavailable
override def accountScript(addressOrAlias: Recipient): F[Option[Script]] = unavailable
override def calculateDelay(hitSource: ByteStr, bt: Long, generator: ByteStr, balance: Long): Long = unavailable
override def calculateDelay(generator: ByteStr, balance: Long): Long = unavailable
override def callScript(
dApp: Address,
func: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private[repl] case class WebEnvironment(settings: NodeConnectionSettings, client

override def accountScript(addressOrAlias: Recipient): Future[Option[Script]] = ???

override def calculateDelay(hitSource: ByteStr, baseTarget: Long, generator: ByteStr, balance: Long): Long = ???
override def calculateDelay(generator: ByteStr, balance: Long): Long = ???

override def callScript(
dApp: Address,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class TrackedDAppEnvironment(underlying: DAppEnvironment, tracker: DAppEnvironme

override def invocationRoot: DAppEnvironment.InvocationTreeTracker = underlying.invocationRoot

override def calculateDelay(hitSource: ByteStr, baseTarget: Long, generator: ByteStr, balance: Long): Long =
underlying.calculateDelay(hitSource, baseTarget, generator, balance)
override def calculateDelay(generator: ByteStr, balance: Long): Long =
underlying.calculateDelay(generator, balance)

// Functions those need Blockchain
override def height: Id[Long] = {
Expand Down

0 comments on commit a518c18

Please sign in to comment.