diff --git a/benchmark/src/main/scala/com/wavesplatform/state/DBState.scala b/benchmark/src/main/scala/com/wavesplatform/state/DBState.scala index 6fb7e9f9dc3..e6605f03d23 100644 --- a/benchmark/src/main/scala/com/wavesplatform/state/DBState.scala +++ b/benchmark/src/main/scala/com/wavesplatform/state/DBState.scala @@ -32,14 +32,13 @@ abstract class DBState extends ScorexLogging { AddressScheme.current = new AddressScheme { override val chainId: Byte = 'W' } lazy val environment = new WavesEnvironment( - AddressScheme.current.chainId, - Coeval.raiseError(new NotImplementedError("`tx` is not implemented")), - Coeval(rocksDBWriter.height), - rocksDBWriter, - null, - DirectiveSet.contractDirectiveSet, - ByteStr.empty - ) + ???, + ???, + ByteStr.empty, + DirectiveSet.contractDirectiveSet, + ) { + override def blockchain: Blockchain = ??? + } @TearDown def close(): Unit = { diff --git a/benchmark/src/test/scala/com/wavesplatform/lang/v1/EnvironmentFunctionsBenchmark.scala b/benchmark/src/test/scala/com/wavesplatform/lang/v1/EnvironmentFunctionsBenchmark.scala index 12f86e74549..2b499587864 100644 --- a/benchmark/src/test/scala/com/wavesplatform/lang/v1/EnvironmentFunctionsBenchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/lang/v1/EnvironmentFunctionsBenchmark.scala @@ -167,7 +167,7 @@ object EnvironmentFunctionsBenchmark { @State(Scope.Benchmark) class AddressFromString { - val ctx: EvaluationContext[Environment, Id] = + val ctx: EvaluationContext[Id] = WavesContext .build(Global, DirectiveSet(V4, Account, DApp).explicitGet(), true) .evaluationContext(environment) diff --git a/benchmark/src/test/scala/com/wavesplatform/lang/v1/EvaluatorV2Benchmark.scala b/benchmark/src/test/scala/com/wavesplatform/lang/v1/EvaluatorV2Benchmark.scala index 85b60437dc2..2284f7c1c10 100644 --- a/benchmark/src/test/scala/com/wavesplatform/lang/v1/EvaluatorV2Benchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/lang/v1/EvaluatorV2Benchmark.scala @@ -3,23 +3,23 @@ package com.wavesplatform.lang.v1 import java.util.concurrent.TimeUnit import cats.Id import com.wavesplatform.lang.Common -import com.wavesplatform.lang.directives.values.{V1, V3} +import com.wavesplatform.lang.directives.values.{V1, V3, V5, V6} import com.wavesplatform.lang.v1.EvaluatorV2Benchmark.* -import com.wavesplatform.lang.v1.compiler.Terms.{EXPR, IF, TRUE} +import com.wavesplatform.lang.v1.compiler.Terms.{CONST_LONG, EXPR, IF, TRUE} import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.lang.v1.evaluator.EvaluatorV2 import com.wavesplatform.lang.v1.evaluator.ctx.{DisabledLogEvaluationContext, EvaluationContext} import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext -import com.wavesplatform.lang.v1.traits.Environment +import com.wavesplatform.lang.v1.evaluator.ctx.{DisabledLogEvaluationContext, EvaluationContext, LoggedEvaluationContext} import org.openjdk.jmh.annotations.* import org.openjdk.jmh.infra.Blackhole import scala.annotation.tailrec object EvaluatorV2Benchmark { - val pureContext: CTX[Environment] = PureContext.build(V1, useNewPowPrecision = true).withEnvironment[Environment] - val pureEvalContext: EvaluationContext[Environment, Id] = pureContext.evaluationContext(Common.emptyBlockchainEnvironment()) - val evaluatorV2: EvaluatorV2 = new EvaluatorV2(DisabledLogEvaluationContext(pureEvalContext), V1, true, true, false) + val pureContext: CTX = PureContext.build(V1, useNewPowPrecision = true) + val pureEvalContext: EvaluationContext[Id] = pureContext.evaluationContext(Common.emptyBlockchainEnvironment()) + val evaluatorV2: EvaluatorV2 = new EvaluatorV2(DisabledLogEvaluationContext(pureEvalContext), V1, true, true, false) } @OutputTimeUnit(TimeUnit.MILLISECONDS) @@ -43,6 +43,45 @@ class EvaluatorV2Benchmark { @Benchmark def conditions(st: Conditions, bh: Blackhole): Unit = bh.consume(eval(pureEvalContext, st.expr, V1)) + + @Benchmark + def recFunc(st: RecFunc, bh: Blackhole): Unit = bh.consume { + val (_, _, res) = eval(pureEvalContext, st.expr, V1) + require(res == Right(CONST_LONG(13631488)), s"$res") + } + + @Benchmark + def overheadCallable(st: OverheadTest, bh: Blackhole): Unit = bh.consume { + val (_, comp, res) = eval(pureEvalContext, st.expr.expr, V6) + require((Int.MaxValue - comp) == 1048576, s"$comp") + } + + @Benchmark + def mini_funcs(st: Funcs, bh: Blackhole): Unit = bh.consume(miniEv(st.expr, pureEvalContext)) + + @Benchmark + def mini_lets(st: Lets, bh: Blackhole): Unit = bh.consume(miniEv(st.expr, pureEvalContext)) + + @Benchmark + def mini_custom(st: CustomFunc, bh: Blackhole): Unit = bh.consume(miniEv(st.expr, pureEvalContext)) + + @Benchmark + def mini_littleCustom(st: LittleCustomFunc, bh: Blackhole): Unit = bh.consume(miniEv(st.expr, pureEvalContext)) + + @Benchmark + def mini_conditions(st: Conditions, bh: Blackhole): Unit = bh.consume(miniEv(st.expr, pureEvalContext)) + + @Benchmark + def mini_recFunc(st: RecFunc, bh: Blackhole): Unit = bh.consume { + val (log, spentComplexity, res) = miniEv(st.expr, pureEvalContext) + require(res == Right(CONST_LONG(13631488)), s"$res") + } + + @Benchmark + def mini_overheadCallable(st: OverheadTest, bh: Blackhole): Unit = bh.consume { + val (_, comp, res) = miniEv(st.expr.expr, pureEvalContext, 52000) + require(comp == 1048576, s"$comp") + } } @State(Scope.Benchmark) @@ -57,7 +96,10 @@ class Funcs { | a$count() == a$count() """.stripMargin - val expr = TestCompiler(V3).compileExpression(script).expr.asInstanceOf[EXPR] + val expr = { + val sc = TestCompiler(V6).compileExpression(script, checkSize = false) + sc.expr + } } @State(Scope.Benchmark) @@ -70,7 +112,22 @@ class Lets { | a$count == a$count """.stripMargin - val expr = TestCompiler(V3).compileExpression(script).expr.asInstanceOf[EXPR] + val expr = TestCompiler(V3).compileExpression(script, checkSize = false).expr +} + +@State(Scope.Benchmark) +class RecFunc { + def scriptStr(size: Int) = + s"""func f1(i: Int) = i + 1 + |${(2 to size) + .map { i => + s"func f$i(${(0 until i).map(idx => s"i$idx: Int").mkString(",")}) = ${(1 until i).map(fi => s"f$fi(${(1 to fi).map(ii => s"i$ii").mkString(",")})").mkString("+")}" + } + .mkString("\n")} + |f${size}(${(1 to size).mkString(",")}) + |""".stripMargin + private val script: String = scriptStr(22) + val expr = TestCompiler(V6).compileExpression(script, checkSize = false).expr } @State(Scope.Benchmark) @@ -114,7 +171,7 @@ class CustomFunc { | f() && f() && f() && f() && f() && f() && f() """.stripMargin - val expr = TestCompiler(V3).compileExpression(script).expr.asInstanceOf[EXPR] + val expr = TestCompiler(V6).compileExpression(script).expr } @State(Scope.Benchmark) @@ -158,7 +215,22 @@ class LittleCustomFunc { | f() """.stripMargin - val expr = TestCompiler(V3).compileExpression(script).expr.asInstanceOf[EXPR] + val expr = TestCompiler(V3).compileExpression(script).expr +} + +@State(Scope.Benchmark) +class OverheadTest { + val expr = { + val n = 20 + val scriptTest = + s""" + | func f0() = true + | ${(0 until n).map(i => s"func f${i + 1}() = if (f$i()) then f$i() else f$i()").mkString("\n")} + | f$n() + """.stripMargin + println(scriptTest) + TestCompiler(V5).compileExpression(scriptTest) + } } @State(Scope.Benchmark) diff --git a/benchmark/src/test/scala/com/wavesplatform/lang/v1/PureFunctionsRebenchmark.scala b/benchmark/src/test/scala/com/wavesplatform/lang/v1/PureFunctionsRebenchmark.scala index 9a777dc665c..d82b03093ab 100644 --- a/benchmark/src/test/scala/com/wavesplatform/lang/v1/PureFunctionsRebenchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/lang/v1/PureFunctionsRebenchmark.scala @@ -5,20 +5,19 @@ import java.util.concurrent.{ThreadLocalRandom, TimeUnit} import cats.Id import com.google.common.primitives.Longs import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.common.utils._ +import com.wavesplatform.common.utils.* import com.wavesplatform.lang.directives.DirectiveSet -import com.wavesplatform.lang.directives.values._ -import com.wavesplatform.lang.utils._ +import com.wavesplatform.lang.directives.values.* +import com.wavesplatform.lang.utils.* import com.wavesplatform.lang.v1.FunctionHeader.Native -import com.wavesplatform.lang.v1.PureFunctionsRebenchmark._ +import com.wavesplatform.lang.v1.PureFunctionsRebenchmark.* import com.wavesplatform.lang.v1.compiler.Terms -import com.wavesplatform.lang.v1.compiler.Terms._ +import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext import com.wavesplatform.lang.v1.evaluator.{FunctionIds, Log} -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.{Common, ExecutionError, v1} -import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.annotations.* import org.openjdk.jmh.infra.Blackhole import scala.util.Random @@ -217,7 +216,7 @@ class PureFunctionsRebenchmark { } object PureFunctionsRebenchmark { - val context: EvaluationContext[Environment, Id] = + val context: EvaluationContext[Id] = lazyContexts((DirectiveSet(V5, Account, Expression).explicitGet(), true, true))() .evaluationContext(Common.emptyBlockchainEnvironment()) diff --git a/benchmark/src/test/scala/com/wavesplatform/lang/v1/ScriptEvaluatorBenchmark.scala b/benchmark/src/test/scala/com/wavesplatform/lang/v1/ScriptEvaluatorBenchmark.scala index 1ebc949f183..420456c38cd 100644 --- a/benchmark/src/test/scala/com/wavesplatform/lang/v1/ScriptEvaluatorBenchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/lang/v1/ScriptEvaluatorBenchmark.scala @@ -7,18 +7,17 @@ import cats.kernel.Monoid import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.{Base58, EitherExt2} import com.wavesplatform.crypto.Curve25519 -import com.wavesplatform.lang.Global import com.wavesplatform.lang.directives.values.{V1, V4} import com.wavesplatform.lang.v1.EnvironmentFunctionsBenchmark.{curve25519, randomBytes} import com.wavesplatform.lang.v1.FunctionHeader.Native import com.wavesplatform.lang.v1.ScriptEvaluatorBenchmark.* import com.wavesplatform.lang.v1.compiler.Terms.* -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext import com.wavesplatform.lang.v1.evaluator.EvaluatorV1.* import com.wavesplatform.lang.v1.evaluator.FunctionIds.{FROMBASE58, SIGVERIFY, TOBASE58} import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} import com.wavesplatform.lang.v1.evaluator.{EvaluatorV1, FunctionIds} +import com.wavesplatform.lang.{Common, Global} import org.openjdk.jmh.annotations.* import org.openjdk.jmh.infra.Blackhole @@ -26,9 +25,9 @@ import scala.util.Random object ScriptEvaluatorBenchmark { val version = V1 - val pureEvalContext: EvaluationContext[NoContext, Id] = - PureContext.build(V1, useNewPowPrecision = true).evaluationContext - val evaluatorV1: EvaluatorV1[Id, NoContext] = new EvaluatorV1[Id, NoContext]() + val pureEvalContext: EvaluationContext[Id] = + PureContext.build(V1, useNewPowPrecision = true).evaluationContext(Common.emptyBlockchainEnvironment()) + val evaluatorV1: EvaluatorV1[Id] = new EvaluatorV1[Id]() } @OutputTimeUnit(TimeUnit.MICROSECONDS) @@ -93,7 +92,7 @@ class ScriptEvaluatorBenchmark { @State(Scope.Benchmark) class NestedBlocks { - val context: EvaluationContext[NoContext, Id] = pureEvalContext + val context: EvaluationContext[Id] = pureEvalContext val expr: EXPR = { val blockCount = 300 @@ -107,8 +106,9 @@ class NestedBlocks { @State(Scope.Benchmark) class Base58Perf { - val context: EvaluationContext[NoContext, Id] = - Monoid.combine(pureEvalContext, CryptoContext.build(Global, version).evaluationContext) + val context: EvaluationContext[Id] = + Monoid.combine(PureContext.build(V1, useNewPowPrecision = true), CryptoContext.build(Global, version)) + .evaluationContext(Common.emptyBlockchainEnvironment()) val encode: EXPR = { val base58Count = 120 @@ -150,8 +150,8 @@ class Base58Perf { @State(Scope.Benchmark) class Signatures { - val context: EvaluationContext[NoContext, Id] = - Monoid.combine(pureEvalContext, CryptoContext.build(Global, version).evaluationContext) + val context: EvaluationContext[Id] = + Monoid.combine(PureContext.build(V1, useNewPowPrecision = true), CryptoContext.build(Global, version)).evaluationContext(Common.emptyBlockchainEnvironment()) val expr: EXPR = { val sigCount = 20 @@ -191,7 +191,7 @@ class Signatures { @State(Scope.Benchmark) class Concat { - val context: EvaluationContext[NoContext, Id] = pureEvalContext + val context: EvaluationContext[Id] = pureEvalContext private val Steps = 180 @@ -218,7 +218,7 @@ class Concat { @State(Scope.Benchmark) class Median { - val context: EvaluationContext[NoContext, Id] = PureContext.build(V4, useNewPowPrecision = true).evaluationContext + val context: EvaluationContext[Id] = PureContext.build(V4, useNewPowPrecision = true).evaluationContext(Common.emptyBlockchainEnvironment()) val randomElements: Array[EXPR] = (1 to 10000).map { _ => @@ -260,8 +260,8 @@ class Median { @State(Scope.Benchmark) class SigVerify32Kb { - val context: EvaluationContext[NoContext, Id] = - Monoid.combine(PureContext.build(V4, useNewPowPrecision = true).evaluationContext, CryptoContext.build(Global, V4).evaluationContext) + val context: EvaluationContext[Id] = + Monoid.combine(PureContext.build(V4, useNewPowPrecision = true), CryptoContext.build(Global, V4)).evaluationContext(Common.emptyBlockchainEnvironment()) val expr: EXPR = { val (privateKey, publicKey) = curve25519.generateKeypair @@ -281,11 +281,11 @@ class SigVerify32Kb { @State(Scope.Benchmark) class ListRemoveByIndex { - val context: EvaluationContext[NoContext, Id] = + val context: EvaluationContext[Id] = Monoid.combine( - PureContext.build(V4, useNewPowPrecision = true).evaluationContext, - CryptoContext.build(Global, V4).evaluationContext - ) + PureContext.build(V4, useNewPowPrecision = true), + CryptoContext.build(Global, V4) + ).evaluationContext(Common.emptyBlockchainEnvironment()) val list: ARR = ARR(Vector.fill(1000)(CONST_LONG(Long.MaxValue)), limited = true).explicitGet() diff --git a/benchmark/src/test/scala/com/wavesplatform/lang/v1/package.scala b/benchmark/src/test/scala/com/wavesplatform/lang/v1/package.scala index 076708514ad..0e715b395c9 100644 --- a/benchmark/src/test/scala/com/wavesplatform/lang/v1/package.scala +++ b/benchmark/src/test/scala/com/wavesplatform/lang/v1/package.scala @@ -2,6 +2,7 @@ package com.wavesplatform.lang import cats.Id import com.wavesplatform.lang.directives.values.StdLibVersion +import com.wavesplatform.lang.miniev.{Ev, State} import com.wavesplatform.lang.v1.FunctionHeader.Native import com.wavesplatform.lang.v1.compiler.Terms import com.wavesplatform.lang.v1.compiler.Terms.{CONST_BIGINT, CONST_LONG, EXPR, FUNCTION_CALL} @@ -10,7 +11,6 @@ import com.wavesplatform.lang.v1.evaluator.FunctionIds.POW_BIGINT import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.Rounding import com.wavesplatform.lang.v1.evaluator.{EvaluatorV2, Log} -import com.wavesplatform.lang.v1.traits.Environment package object v1 { def pow(base: BigInt, basePrecision: Int, exponent: BigInt, exponentPrecision: Int, resultPrecision: Int): EXPR = @@ -27,9 +27,12 @@ package object v1 { ) def eval( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], expr: EXPR, stdLibVersion: StdLibVersion ): (Log[Id], Int, Either[ExecutionError, Terms.EVALUATED]) = EvaluatorV2.applyCompleted(ctx, expr, LogExtraInfo(), stdLibVersion, newMode = true, correctFunctionCallScope = true, enableExecutionLog = false) + + def miniEv(expr: EXPR, ctx: EvaluationContext[Id], limit: Int = Int.MaxValue): (Log[Id], Int, Either[ExecutionError, Terms.EVALUATED]) = + Ev.run(expr, ???) } diff --git a/benchmark/src/test/scala/com/wavesplatform/state/WavesEnvironmentBenchmark.scala b/benchmark/src/test/scala/com/wavesplatform/state/WavesEnvironmentBenchmark.scala index edd7e82f488..1743120bea6 100644 --- a/benchmark/src/test/scala/com/wavesplatform/state/WavesEnvironmentBenchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/state/WavesEnvironmentBenchmark.scala @@ -135,7 +135,7 @@ object WavesEnvironmentBenchmark { RDB.open(wavesSettings.dbSettings) } - val environment: Environment[Id] = { + val environment: Environment[Id] = ???/*{ val state = new RocksDBWriter(rdb, wavesSettings.blockchainSettings, wavesSettings.dbSettings) new WavesEnvironment( AddressScheme.current.chainId, @@ -146,7 +146,7 @@ object WavesEnvironmentBenchmark { DirectiveSet.contractDirectiveSet, ByteStr.empty ) - } + }*/ @TearDown def close(): Unit = { diff --git a/build.sbt b/build.sbt index 5c878b00e50..de02aeb5ef9 100644 --- a/build.sbt +++ b/build.sbt @@ -33,10 +33,13 @@ lazy val lang = lazy val `lang-jvm` = lang.jvm .settings( - name := "RIDE Compiler", - normalizedName := "lang", - description := "The RIDE smart contract language compiler", - libraryDependencies += "org.scala-js" %% "scalajs-stubs" % "1.1.0" % Provided + name := "RIDE Compiler", + normalizedName := "lang", + description := "The RIDE smart contract language compiler", + libraryDependencies ++= Seq( + "org.scala-js" %% "scalajs-stubs" % "1.1.0" % Provided, + Dependencies.scalaLogging + ) ) lazy val `lang-js` = lang.js diff --git a/lang/js/src/main/scala/com/wavesplatform/lang/utils/Logging.scala b/lang/js/src/main/scala/com/wavesplatform/lang/utils/Logging.scala new file mode 100644 index 00000000000..031c9d4434a --- /dev/null +++ b/lang/js/src/main/scala/com/wavesplatform/lang/utils/Logging.scala @@ -0,0 +1,7 @@ +package com.wavesplatform.lang.utils + +import org.scalajs.dom.* + +trait Logging { + def trace(message: => String): Unit = println(message) +} diff --git a/lang/jvm/src/main/scala/com/wavesplatform/lang/Global.scala b/lang/jvm/src/main/scala/com/wavesplatform/lang/Global.scala index 1b958887b72..370758e35da 100644 --- a/lang/jvm/src/main/scala/com/wavesplatform/lang/Global.scala +++ b/lang/jvm/src/main/scala/com/wavesplatform/lang/Global.scala @@ -105,13 +105,13 @@ object Global extends BaseGlobal { } else { BigDecimalMath.pow(baseBD, expBD, context) } - if (useNewPrecision) + (if (useNewPrecision) setScale(resultPrecision, round, context.getPrecision, result) else { - val value = result.setScale(resultPrecision.toInt, round.mode).unscaledValue + val value = result.setScale(resultPrecision, round.mode).unscaledValue Right(BigInt(value)) - } - }.flatten.map(_.bigInteger.longValueExact()) + }).map(_.bigInteger.longValueExact()) + }.flatten def log(b: Long, bp: Long, e: Long, ep: Long, rp: Long, round: Rounding): Either[String, Long] = tryEither { @@ -128,8 +128,6 @@ object Global extends BaseGlobal { val base = toJBig(b, bp) val exp = toJBig(e, ep) - - val context = if (useNewPrecision) bigMathContext else oldBigMathContext val res = if (exp == BigDecimal(0.5).bigDecimal) { BigDecimalMath.sqrt(base, context) diff --git a/lang/jvm/src/main/scala/com/wavesplatform/lang/utils/Logging.scala b/lang/jvm/src/main/scala/com/wavesplatform/lang/utils/Logging.scala new file mode 100644 index 00000000000..662edf4202f --- /dev/null +++ b/lang/jvm/src/main/scala/com/wavesplatform/lang/utils/Logging.scala @@ -0,0 +1,7 @@ +package com.wavesplatform.lang.utils + +import com.typesafe.scalalogging.StrictLogging + +trait Logging extends StrictLogging { + def trace(message: => String): Unit = logger.trace(message) +} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/ExecutionError.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/ExecutionError.scala index d02bdd7a911..40f15fb3be6 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/ExecutionError.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/ExecutionError.scala @@ -4,7 +4,14 @@ sealed trait ExecutionError { def message: String } case class CommonError(details: String, cause: Option[ValidationError] = None) extends ExecutionError { - override def toString: String = s"CommonError($message)" override def message: String = cause.map(_.toString).getOrElse(details) } case class FailOrRejectError(message: String, skipInvokeComplexity: Boolean = true) extends ExecutionError with ValidationError + +case class EvaluationException(cause: Throwable) extends ExecutionError { + override lazy val message: String = s"class ${cause.getClass} ${String.valueOf(cause.getMessage)}" +} + +case object SoftLimitReached extends ExecutionError { + override val message = "Soft limit reached" +} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/ComplexityCounter.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/ComplexityCounter.scala new file mode 100644 index 00000000000..6e5d17210be --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/ComplexityCounter.scala @@ -0,0 +1,38 @@ +package com.wavesplatform.lang.miniev + +import scala.util.control.{NoStackTrace, NonFatal} +import scala.util.{Failure, Success, Try} + +trait ComplexityCounter { + def recordLet(): Try[Unit] + def recordFunctionArguments(argCount: Int): Try[Unit] + def recordComplexity(complexity: Int): Try[Unit] + def recordGet(): Try[Unit] + def spentComplexity: Int +} + +object ComplexityCounter { + private val success = Success(()) + private val failure = Failure(new ArithmeticException with NoStackTrace) + + + + class New extends ComplexityCounter { + private var totalSpentComplexity = 0 + + override def recordLet(): Try[Unit] = success + + override def recordGet(): Try[Unit] = success + + override def recordFunctionArguments(argCount: Int): Try[Unit] = success + override def recordComplexity(complexity: Int): Try[Unit] = + try { + totalSpentComplexity = math.addExact(totalSpentComplexity, complexity) + success + } catch { + case NonFatal(_) => failure + } + + override def spentComplexity: Int = totalSpentComplexity + } +} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/ComplexityLimit.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/ComplexityLimit.scala new file mode 100644 index 00000000000..d376860176d --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/ComplexityLimit.scala @@ -0,0 +1,21 @@ +package com.wavesplatform.lang.miniev + +import com.wavesplatform.lang.{CommonError, ExecutionError, SoftLimitReached} + +sealed trait ComplexityLimit { + def checkLimit(spentComplexity: Long): Either[ExecutionError, Long] +} + +object ComplexityLimit { + sealed abstract class Limit(maxComplexity: Long, error: => ExecutionError) extends ComplexityLimit { + override def checkLimit(spentComplexity: Long): Either[ExecutionError, Long] = + Either.cond(spentComplexity <= maxComplexity, spentComplexity, error) + } + + case class Partial(limit: Int) extends Limit(limit, SoftLimitReached) + case class Complete(limit: Int) extends Limit(limit, CommonError(s"Complexity limit $limit reached")) + + case object Unlimited extends ComplexityLimit { + override def checkLimit(spentComplexity: Long): Either[ExecutionError, Long] = Right(spentComplexity) + } +} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Ev.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Ev.scala new file mode 100644 index 00000000000..0c446f23cdc --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Ev.scala @@ -0,0 +1,232 @@ +package com.wavesplatform.lang.miniev + +import cats.Id +import cats.syntax.either.* +import cats.syntax.flatMap.* +import com.wavesplatform.lang.directives.values.StdLibVersion +import com.wavesplatform.lang.v1.FunctionHeader +import com.wavesplatform.lang.v1.compiler.Terms.* +import com.wavesplatform.lang.v1.compiler.Types.CASETYPEREF +import com.wavesplatform.lang.v1.evaluator.ctx.{EvaluationContext, ExtendedInternalFunction, NativeFunction, UserFunction} +import com.wavesplatform.lang.v1.evaluator.{ContextfulNativeFunction, Log} +import com.wavesplatform.lang.{CommonError, EvaluationException, ExecutionError} + +import scala.annotation.tailrec +import scala.util.Try +import scala.util.control.NonFatal + +object Ev { + case class Closure(func: FUNC, scope: Scope) + + case class Scope(userFns: Map[String, Closure], names: Map[String, LazyVal]) + + @tailrec + private final def collectEvaluated(argsToProcess: List[EXPR], evaluatedArgs: List[EVALUATED]): (List[EVALUATED], List[EXPR]) = + if (argsToProcess.isEmpty) (evaluatedArgs, Nil) + else if (!argsToProcess.head.isInstanceOf[EVALUATED]) (evaluatedArgs, argsToProcess) + else collectEvaluated(argsToProcess.tail, argsToProcess.head.asInstanceOf[EVALUATED] :: evaluatedArgs) + + private final def addArgs(argNames: List[String], argValues: List[EVALUATED], target: Map[String, LazyVal]) = + target.concat( + argNames.view.zip(argValues.view.map(ev => new LazyVal(Right(ev)))) + ) + + @tailrec + private def evalRoot(root: EXPR, state: State): Either[ExecutionError, EVALUATED] = { + root match { + case f: FAIL => Right(f) + case GETTER(expr, field) => + state.recordComplexityOverhead() match { + case Left(value) => value.asLeft + case Right(total) => +// println(s"GET $field: $total") + evalRoot(expr, state.push(Op.Get(field))) + } + case LET_BLOCK(let, body) => evalRoot(body, state.addName(let.name, let.value)) + case BLOCK(dec, body) => + dec match { + case LET(name, value) => evalRoot(body, state.addName(name, value)) + case f: FUNC => evalRoot(body, state.addUserFunction(f)) + } + case IF(cond, ifTrue, ifFalse) => + state.recordComplexityOverhead() match { + case Left(value) => value.asLeft + case Right(total) => +// println(s"IF: $total") + evalRoot(cond, state.push(Op.If(ifTrue, ifFalse))) + } + case REF(key) => + state.recordComplexityOverhead() match { + case Left(value) => value.asLeft + case Right(total) => +// println(s"REF($key): $total") + state.cachingEv(key) match { + case Some(value) => + value.value match { + case Right(ev) => evalRoot(ev, state) + case Left((expr, exprScope)) => + evalRoot(expr, state.push(Op.Value(key, value, state)).resetScope(exprScope)) + } + case None => + state.evaluationContext.letDefs.get(key) match { + case None => + Left(CommonError(s"A definition of '$key' not found")) + case Some(value) => + value.value.value match { + case ee @ Left(_) => ee + case Right(v) => evalRoot(v, state) + } + } + } + } + case FUNCTION_CALL(fh, args) => + val (reversedEvaluatedArgs, nonEvArgs) = collectEvaluated(args, Nil) + if (nonEvArgs.nonEmpty) { + evalRoot(nonEvArgs.head, state.push(Op.FuncArg(fh, reversedEvaluatedArgs, nonEvArgs.tail))) + } else { + val evaluatedArgs = args.asInstanceOf[List[EVALUATED]] + fh match { + case fh: FunctionHeader.Native => + state.evaluationContext.functions.get(fh) match { + case Some(nf @ NativeFunction(_, _, _, ev, _)) => + val callResult: Either[ExecutionError, EVALUATED] = ev match { + case simple: ContextfulNativeFunction.Simple => + simple.evaluate(state.evaluationContext.environment, evaluatedArgs) + } + callResult match { + case l @ Left(_) => l + case Right(value) => + state.spendComplexity(nf.costByLibVersion(state.stdlibVersion)) match { + case Left(e) => e.asLeft + case Right(total) => +// println(s"CALL ${evaluatedArgs.mkString(nf.funcName + "(", ",", ")")} => $value: $total") + evalRoot(value, state) + } + + } + case Some(eif: ExtendedInternalFunction) => + eif.buildExpression(state, evaluatedArgs) match { + case Left(value) => value.asLeft[EVALUATED] + case Right(expr) => evalRoot(expr, state) + } + case _ => Left(CommonError(s"function '$fh' not found")) + } + case FunctionHeader.User(internalName, _) => + state.evaluationContext.functions.get(fh) match { + case Some(uf @ UserFunction(_, _, _, _, ev, args)) => + val expr: EXPR = ev.apply[Id](state.evaluationContext.environment, evaluatedArgs) + val newNames = + args.view + .zip(evaluatedArgs) + .map { case (name, arg) => + name -> new LazyVal(Right(arg)) + } + .toMap + val predefinedComplexity = uf.costByLibVersion(state.stdlibVersion) +// if (state.newMode) { +// state.spendComplexity(predefinedComplexity) +// } +// println(s"USER($internalName), predefined=${predefinedComplexity} [newMode=${state.newMode}]: ${state.spentComplexity()}") + evalRoot( + expr, + state.callUserFunction(internalName, Scope(Map.empty, newNames), Some(predefinedComplexity)) + ) + case None => + state.currentScope().userFns.get(internalName) match { + case Some(scopedUserFn) => +// println(s">> CALL '${scopedUserFn.func.name}'") + evalRoot( + scopedUserFn.func.body, + state.callUserFunction( + scopedUserFn.func.name, + scopedUserFn.scope.copy(names = addArgs(scopedUserFn.func.args, evaluatedArgs, scopedUserFn.scope.names)), + None + ) + ) + + case None => + state.evaluationContext.typeDefs.get(internalName) match { + case None => Left(CommonError(s"Function or type '$internalName' not found")) + case Some(ctr @ CASETYPEREF(_, fields, hideConstructor)) => + if (state.newMode && hideConstructor) { + Left(CommonError(s"Constructor '$internalName' is not available")) + } else { + val constructorCost = if (state.newMode) 1 else 0 + state.spendComplexity(constructorCost) match { + case Left(value) => value.asLeft + case Right(total) => +// println(s"$internalName: spent=$constructorCost; newMode=${state.newMode} total $total") + evalRoot(CaseObj(ctr, fields.map(_._1).zip(evaluatedArgs).toMap), state) + } + } + } + } + } + } + } + + case evaluated: EVALUATED => + state.pop() match { + case None => +// println(s"END: $evaluated") + Right(evaluated) + + case Some(op) => + op match { + case fc: Op.FuncArg => + val newReversedEvaluatedArgs = evaluated :: fc.reversedEvaluatedArgs + if (fc.argsToEvaluate.nonEmpty) { + evalRoot( + fc.argsToEvaluate.head, + state.push(fc.copy(reversedEvaluatedArgs = newReversedEvaluatedArgs, argsToEvaluate = fc.argsToEvaluate.tail)) + ) + } else { + evalRoot(FUNCTION_CALL(fc.func, newReversedEvaluatedArgs.reverse), state) + } + case ps @ Op.Func(name, scope, maybePredefinedComplexity) => + val spentComplexity = state.popComplexity() + val adjustedComplexity = maybePredefinedComplexity match { + case Some(pc) if state.newMode => pc + case _ => if (state.newMode) spentComplexity.max(1L) else spentComplexity + } + + state.spendComplexity(adjustedComplexity) match { + case Left(err) => err.asLeft + case Right(total) => +// println(s"$name: predeifined=$maybePredefinedComplexity, spent=$spentComplexity, adjusted=$adjustedComplexity, $total") + val (expr, _) = ps.ret(evaluated) +// println(s"POP($name, $maybePredefinedComplexity): ${state.totalSpentComplexity()}") + evalRoot(expr, state.resetScope(scope)) + } + case op => +// println(s"OP> $evaluated: $op") + val (expr, maybeScope) = op.ret(evaluated) + evalRoot(expr, maybeScope.fold(state)(s => state.resetScope(s))) + } + } + + } + } + + def run( + script: EXPR, + ec: EvaluationContext[Id], + complexityLimit: ComplexityLimit, + newMode: Boolean, + version: StdLibVersion + ): (Log[Id], Int, Either[ExecutionError, EVALUATED]) = + run(script, State(ec, complexityLimit, newMode, version)) + + def run(script: EXPR, state: State): (Log[Id], Int, Either[ExecutionError, EVALUATED]) = try { + val resultE = evalRoot(script, state) + val intComplexityE = state + .totalSpentComplexity() + .flatMap(comp => Try(Math.toIntExact(comp)).toEither.leftMap(_ => CommonError("Complexity overflow"))) + + (state.logEntries.toList, intComplexityE.getOrElse(Int.MaxValue), resultE.flatTap(_ => intComplexityE)) + } catch { + case NonFatal(e) => + e.printStackTrace() + (Nil, 0, Left(EvaluationException(e))) + } +} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Functions.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Functions.scala new file mode 100644 index 00000000000..aeb9751c58a --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Functions.scala @@ -0,0 +1,11 @@ +package com.wavesplatform.lang.miniev + +import com.wavesplatform.lang.v1.FunctionHeader +import com.wavesplatform.lang.v1.evaluator.ctx.BaseFunction +import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext + +object Functions { + val predefinedFunctions: Map[FunctionHeader, BaseFunction] = PureContext.v6Functions.map { + bf => bf.header -> bf + }.toMap +} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Op.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Op.scala new file mode 100644 index 00000000000..0cb6783a8a9 --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Op.scala @@ -0,0 +1,53 @@ +package com.wavesplatform.lang.miniev + +import com.wavesplatform.lang.miniev.Ev.Scope +import com.wavesplatform.lang.v1.FunctionHeader +import com.wavesplatform.lang.v1.compiler.Terms.* + +class LazyVal(var value: Either[(EXPR, Scope), EVALUATED]) { + override def toString: String = value.fold( + { case (expr, _) => s"N:$expr" }, + ev => s"E:$ev" + ) +} + +trait Op { + def ret(ev: EVALUATED): (EXPR, Option[Scope]) +} + +object Op { + case class Get(field: String) extends Op { + override def ret(ev: EVALUATED): (EXPR, Option[Scope]) = (ev match { + case CaseObj(_, fields) => + fields.getOrElse[EXPR](field, FAIL(s"object $ev has no field $field")) + case _ => FAIL(s"$ev is not a CaseObj") + }) -> None + } + + case class If(ifTrue: EXPR, ifFalse: EXPR) extends Op { + override def ret(ev: EVALUATED): (EXPR, Option[Scope]) = (ev match { + case CONST_BOOLEAN(cond) => + if (cond) ifTrue else ifFalse + case _ => FAIL(s"$ev is not a Boolean") + }) -> None + } + + case class FuncArg(func: FunctionHeader, reversedEvaluatedArgs: List[EVALUATED], argsToEvaluate: List[EXPR]) extends Op { + override def ret(ev: EVALUATED): (EXPR, Option[Scope]) = + FUNCTION_CALL(func, (ev :: reversedEvaluatedArgs).foldLeft(argsToEvaluate) { case (a, v) => v :: a }) -> None + } + + case class Value(key: String, lazyVal: LazyVal, state: State) extends Op { + private val cachedScope = state.currentScope() + + override def ret(ev: EVALUATED): (EXPR, Option[Scope]) = { + lazyVal.value = Right(ev) + state.log(key, Right(ev)) + ev -> Some(cachedScope) + } + } + + case class Func(name: String, scope: Scope, predefinedComplexity: Option[Long] = None) extends Op { + override def ret(ev: EVALUATED): (EXPR, Option[Scope]) = (ev, Some(scope)) + } +} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/State.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/State.scala new file mode 100644 index 00000000000..c847b7dd3ae --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/State.scala @@ -0,0 +1,96 @@ +package com.wavesplatform.lang.miniev + +import cats.Id +import cats.syntax.either.* +import com.wavesplatform.lang.directives.values.StdLibVersion +import com.wavesplatform.lang.miniev.Ev.{Closure, Scope} +import com.wavesplatform.lang.v1.compiler.Terms.* +import com.wavesplatform.lang.v1.evaluator.LetExecResult +import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext +import com.wavesplatform.lang.{CommonError, ExecutionError} + +abstract class State(complexityLimit: ComplexityLimit, val newMode: Boolean) { + private var stack: List[Op] = Nil + private var complexityStack: List[Long] = Nil + private var complexityCounter = 0L + private var scope = Scope(Map.empty, Map.empty) + + var logEntries = Vector.empty[(String, LetExecResult[Id])] + + def evaluationContext: EvaluationContext[Id] + def stdlibVersion: StdLibVersion + + def log(name: String, value: LetExecResult[Id]): this.type = { + logEntries = logEntries :+ (name, value) + this + } + + def recordComplexityOverhead(): Either[ExecutionError, Long] = spendComplexity(if (newMode) 0 else 1) + + def spendComplexity(c: Long): Either[ExecutionError, Long] = + try { + complexityCounter = Math.addExact(complexityCounter, c) + totalSpentComplexity().flatMap(tsc => complexityLimit.checkLimit(tsc)) + } catch { case _: ArithmeticException => CommonError("Complexity overflow").asLeft } + + def totalSpentComplexity(): Either[ExecutionError, Long] = + try { complexityStack.foldLeft(complexityCounter)(Math.addExact).asRight } + catch { case _: ArithmeticException => CommonError("Complexity overflow").asLeft } + + def popComplexity(): Long = { + val tmp = complexityCounter + if (complexityStack.isEmpty) { + complexityCounter = 0 + } else { + complexityCounter = complexityStack.head + complexityStack = complexityStack.tail + } + tmp + } + + def push(op: Op): this.type = { + stack = op :: stack + this + } + + def pop(): Option[Op] = { + val op = stack.headOption + stack = if (stack.nonEmpty) stack.tail else Nil + op + } + + def callUserFunction(name: String, functionScope: Scope, predefinedComplexity: Option[Long]): this.type = { + stack ::= Op.Func(name, currentScope(), predefinedComplexity) + scope = functionScope + complexityStack ::= complexityCounter + complexityCounter = 0L + this + } + + def addName(name: String, value: EXPR): this.type = { + scope = scope.copy(names = scope.names + (name -> new LazyVal(Left((value, scope))))) + this + } + + def cachingEv(name: String): Option[LazyVal] = scope.names.get(name) + + def addUserFunction(func: FUNC): this.type = { + scope = scope.copy(userFns = scope.userFns + (func.name -> Closure(func, scope))) + this + } + + def resetScope(newScope: Scope): this.type = { + scope = newScope + this + } + + def currentScope(): Scope = scope +} + +object State { + def apply(ec: EvaluationContext[Id], complexityLimit: ComplexityLimit, newMode: Boolean, version: StdLibVersion): State = + new State(complexityLimit, newMode) { + override def stdlibVersion: StdLibVersion = version + override def evaluationContext: EvaluationContext[Id] = ec + } +} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/utils/package.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/utils/package.scala index 9bde12bd5ec..8c69bb8cd48 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/utils/package.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/utils/package.scala @@ -63,7 +63,7 @@ package object utils { ): Coeval[(Either[ValidationError, (EVALUATED, Log[Id])], Int)] = ??? } - val lazyContexts: Map[(DirectiveSet, Boolean, Boolean), Coeval[CTX[Environment]]] = + val lazyContexts: Map[(DirectiveSet, Boolean, Boolean), Coeval[CTX]] = (for { version <- DirectiveDictionary[StdLibVersion].all scriptType <- DirectiveDictionary[ScriptType].all @@ -73,8 +73,8 @@ package object utils { } yield { val ds = DirectiveSet(version, scriptType, contentType).explicitGet() val ctx = Coeval.evalOnce( - PureContext.build(version, useNewPowPrecision).withEnvironment[Environment] |+| - CryptoContext.build(Global, version).withEnvironment[Environment] |+| + PureContext.build(version, useNewPowPrecision) |+| + CryptoContext.build(Global, version) |+| WavesContext.build(Global, ds, fixBigScriptField) ) (ds, useNewPowPrecision, fixBigScriptField) -> ctx @@ -97,7 +97,7 @@ package object utils { (ds.stdLibVersion, functions()) } - private val combinedContext: Map[(StdLibVersion, ContentType), CTX[Environment]] = + private val combinedContext: Map[(StdLibVersion, ContentType), CTX] = lazyContexts .groupBy { case (ds, _) => (ds._1.stdLibVersion, ds._1.contentType) @@ -107,7 +107,7 @@ package object utils { _.toList .map(_._2) .sequence - .map(Monoid.combineAll[CTX[Environment]])() + .map(Monoid.combineAll[CTX])() ) .toMap @@ -145,7 +145,7 @@ package object utils { def combinedFunctionCosts(ds: DirectiveSet): Map[FunctionHeader, Coeval[Long]] = combinedFunctionCosts((ds.stdLibVersion, ds.contentType)) - def estimate(version: StdLibVersion, ctx: EvaluationContext[Environment, Id]): Map[FunctionHeader, Coeval[Long]] = { + def estimate(version: StdLibVersion, ctx: EvaluationContext[Id]): Map[FunctionHeader, Coeval[Long]] = { val costs: mutable.Map[FunctionHeader, Coeval[Long]] = mutable.Map.from(ctx.typeDefs.collect { case (typeName, CASETYPEREF(_, fields, hidden)) if (!hidden || version < V4) => FunctionHeader.User(typeName) -> Coeval.now(fields.size.toLong) }) @@ -158,7 +158,7 @@ package object utils { costs.toMap } - def ctx(version: Int, isTokenContext: Boolean, isContract: Boolean): CTX[Environment] = { + def ctx(version: Int, isTokenContext: Boolean, isContract: Boolean): CTX = { val ds = DirectiveSet( DirectiveDictionary[StdLibVersion].idMap(version), ScriptType.isAssetScript(isTokenContext), diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/CTX.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/CTX.scala index 90c428567cb..32d3bb44de8 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/CTX.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/CTX.scala @@ -1,35 +1,35 @@ package com.wavesplatform.lang.v1 -import cats.{Id, Monad, Monoid} +import cats.{Monad, Monoid} import com.wavesplatform.lang.v1.FunctionHeader.Native import com.wavesplatform.lang.v1.compiler.CompilerContext.{FunctionInfo, VariableInfo} import com.wavesplatform.lang.v1.compiler.Types.FINAL import com.wavesplatform.lang.v1.compiler.{CompilerContext, DecompilerContext} -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext -import com.wavesplatform.lang.v1.evaluator.ctx.{BaseFunction, EvaluationContext, LazyVal} +import com.wavesplatform.lang.v1.evaluator.ContextfulVal import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext -import com.wavesplatform.lang.v1.evaluator.{Contextful, ContextfulVal} +import com.wavesplatform.lang.v1.evaluator.ctx.{BaseFunction, EvaluationContext, LazyVal} import com.wavesplatform.lang.v1.parser.BinaryOperation import com.wavesplatform.lang.v1.parser.Expressions.Pos.AnyPos +import com.wavesplatform.lang.v1.traits.Environment import scala.annotation.meta.field import scala.scalajs.js.annotation.* @JSExportTopLevel("CTX") -case class CTX[C[_[_]]]( - @(JSExport @field) types: Seq[FINAL], - @(JSExport @field) vars: Map[String, (FINAL, ContextfulVal[C])], - @(JSExport @field) functions: Array[BaseFunction[C]] +case class CTX( + @(JSExport @field) types: Seq[FINAL], + @(JSExport @field) vars: Map[String, (FINAL, ContextfulVal)], + @(JSExport @field) functions: Array[BaseFunction] ) { - lazy val typeDefs = types.view.map(t => t.name -> t).toMap + lazy val typeDefs = types.view.map(t => t.name -> t).toMap lazy val functionMap = functions.view.map(f => f.header -> f).toMap - def evaluationContext[F[_]: Monad](env: C[F]): EvaluationContext[C, F] = { + def evaluationContext[F[_]: Monad](env: Environment[F]): EvaluationContext[F] = { - if (functionMap.size != functions.length) { - val dups = functions.groupBy(_.header).filter(_._2.length != 1) - throw new Exception(s"Duplicate runtime functions names: $dups") - } +// if (functionMap.size != functions.length) { +// val dups = functions.groupBy(_.header).filter(_._2.length != 1) +// throw new Exception(s"Duplicate runtime functions names: $dups") +// } EvaluationContext( env, typeDefs, @@ -38,17 +38,11 @@ case class CTX[C[_[_]]]( ) } - def evaluationContext[F[_]: Monad](implicit ev: NoContext[F] =:= C[F]): EvaluationContext[C, F] = - evaluationContext[F](Contextful.empty[F]) - - def withEnvironment[D[_[_]]](implicit ev: C[Id] =:= NoContext[Id]): CTX[D] = - asInstanceOf[CTX[D]] - lazy val compilerContext: CompilerContext = CompilerContext( typeDefs, vars.view.mapValues(v => VariableInfo(AnyPos, v._1)).toMap, functions.groupBy(_.name).map { case (k, v) => k -> FunctionInfo(AnyPos, v.map(_.signature).toList) }, - provideRuntimeTypeOnCastError = functions.exists(_ == PureContext._getType) + provideRuntimeTypeOnCastError = functions.contains(PureContext._getType) ) val opsNames = BinaryOperation.opsByPriority @@ -60,17 +54,19 @@ case class CTX[C[_[_]]]( .toSet lazy val decompilerContext: DecompilerContext = DecompilerContext( - opCodes = compilerContext.functionDefs - .view.mapValues(_.fSigList.map(_.header).filter(_.isInstanceOf[Native]).map(_.asInstanceOf[Native].name)) + opCodes = compilerContext.functionDefs.view + .mapValues(_.fSigList.map(_.header).filter(_.isInstanceOf[Native]).map(_.asInstanceOf[Native].name)) .toList .flatMap { case (name, codes) => codes.map((_, name)) } .toMap, - binaryOps = compilerContext.functionDefs - .view.filterKeys(opsNames(_)) + binaryOps = compilerContext.functionDefs.view + .filterKeys(opsNames(_)) .mapValues( - _.fSigList.map(_.header) + _.fSigList + .map(_.header) .filter(_.isInstanceOf[Native]) - .map(_.asInstanceOf[Native].name)) + .map(_.asInstanceOf[Native].name) + ) .toList .flatMap { case (name, codes) => codes.map((_, name)) } .toMap, @@ -79,12 +75,12 @@ case class CTX[C[_[_]]]( } object CTX { - val empty: CTX[NoContext] = CTX[NoContext](Seq.empty, Map.empty, Array.empty) + val empty: CTX = CTX(Seq.empty, Map.empty, Array.empty) - implicit def monoid[C[_[_]]]: Monoid[CTX[C]] = new Monoid[CTX[C]] { - override val empty: CTX[C] = CTX.empty.withEnvironment[C] + implicit def monoid: Monoid[CTX] = new Monoid[CTX] { + override val empty: CTX = CTX.empty - override def combine(x: CTX[C], y: CTX[C]): CTX[C] = - CTX[C](x.types ++ y.types, x.vars ++ y.vars, x.functions ++ y.functions) + override def combine(x: CTX, y: CTX): CTX = + CTX(x.types ++ y.types, x.vars ++ y.vars, x.functions ++ y.functions) } } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/FunctionHeader.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/FunctionHeader.scala index 926ac0aee80..cb88071fe03 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/FunctionHeader.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/FunctionHeader.scala @@ -1,8 +1,16 @@ package com.wavesplatform.lang.v1 +import scala.collection.mutable + sealed abstract class FunctionHeader(val funcName: String) object FunctionHeader { - case class Native(name: Short) extends FunctionHeader(name.toString) + case class Native private(name: Short) extends FunctionHeader(name.toString) + object Native { + private[this] val cache = mutable.Map.empty[Short, Native] + def apply(name: Short): Native = cache.getOrElse(name, cache.synchronized { + cache.getOrElseUpdate(name, new Native(name)) + }) + } case class User(internalName: String, name: String) extends FunctionHeader(internalName) { override def hashCode(): Int = internalName.## diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/Decompiler.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/Decompiler.scala index c6c12a6fc8b..237dacc9da7 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/Decompiler.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/Decompiler.scala @@ -228,7 +228,7 @@ object Decompiler { .flatMap(m => (m.group(1), m.group(2)) match { case ("User", name) => Some(User(name)) - case ("Native", id) => Try(id.toShort).toOption.map(Native) + case ("Native", id) => Try(id.toShort).toOption.map(s => Native(s)) case _ => None } ) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/Contextful.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/Contextful.scala index 05b37eb723c..34b7d29389d 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/Contextful.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/Contextful.scala @@ -1,14 +1,14 @@ package com.wavesplatform.lang.v1.evaluator -import cats.syntax.applicative._ -import cats.syntax.either._ +import cats.syntax.applicative.* +import cats.syntax.either.* import cats.{Eval, Monad} +import com.wavesplatform.lang.ExecutionError import com.wavesplatform.lang.v1.compiler.Terms.{EVALUATED, EXPR} import com.wavesplatform.lang.v1.compiler.Types.TYPE -import com.wavesplatform.lang.{CoevalF, ExecutionError} -import monix.eval.Coeval +import com.wavesplatform.lang.v1.traits.Environment -sealed trait ContextfulNativeFunction[C[_[_]]] { +sealed trait ContextfulNativeFunction { val name: String val resultType: TYPE val args: Seq[(String, TYPE)] @@ -18,70 +18,53 @@ sealed trait ContextfulNativeFunction[C[_[_]]] { } object ContextfulNativeFunction { - abstract class Simple[C[_[_]]]( + abstract class Simple( val name: String, val resultType: TYPE, val args: Seq[(String, TYPE)] - ) extends ContextfulNativeFunction[C] { + ) extends ContextfulNativeFunction { def evaluate[F[_]: Monad]( - env: C[F], + env: Environment[F], evaluatedArgs: List[EVALUATED] ): F[Either[ExecutionError, EVALUATED]] } - - abstract class Extended[C[_[_]]]( - val name: String, - val resultType: TYPE, - val args: Seq[(String, TYPE)] - ) extends ContextfulNativeFunction[C] { - def evaluate[F[_]: Monad]( - env: C[F], - evaluatedArgs: List[EVALUATED], - availableComplexity: Int - )(implicit m: Monad[CoevalF[F, *]]): Coeval[F[(Either[ExecutionError, (EVALUATED, Log[F])], Int)]] - } } -trait ContextfulUserFunction[C[_[_]]] { - def apply[F[_]: Monad](context: C[F], startArgs: List[EXPR]): EXPR +trait ContextfulUserFunction { + def apply[F[_]: Monad](context: Environment[F], startArgs: List[EXPR]): EXPR } object ContextfulUserFunction { - def pure[C[_[_]]](expr: EXPR): ContextfulUserFunction[C] = - new ContextfulUserFunction[C] { - override def apply[F[_]: Monad](context: C[F], startArgs: List[EXPR]): EXPR = expr + def pure(expr: EXPR): ContextfulUserFunction = + new ContextfulUserFunction { + override def apply[F[_]: Monad](context: Environment[F], startArgs: List[EXPR]): EXPR = expr } } -trait ContextfulVal[C[_[_]]] { +trait ContextfulVal { val isPure: Boolean = false - def apply[F[_]: Monad](context: C[F]): Eval[F[Either[ExecutionError, EVALUATED]]] + def apply[F[_]: Monad](context: Environment[F]): Eval[F[Either[ExecutionError, EVALUATED]]] } object ContextfulVal { - def fromEval[C[_[_]]](v: Eval[Either[ExecutionError, EVALUATED]]): ContextfulVal[C] = - new ContextfulVal[C] { - override def apply[F[_]: Monad](context: C[F]): Eval[F[Either[ExecutionError, EVALUATED]]] = + def fromEval(v: Eval[Either[ExecutionError, EVALUATED]]): ContextfulVal = + new ContextfulVal { + override def apply[F[_]: Monad](context: Environment[F]): Eval[F[Either[ExecutionError, EVALUATED]]] = v.map(_.pure[F]) } - def pure[C[_[_]]](v: EVALUATED): ContextfulVal[C] = - new ContextfulVal[C] { + def pure(v: EVALUATED): ContextfulVal = + new ContextfulVal { override val isPure: Boolean = true - override def apply[F[_]: Monad](context: C[F]): Eval[F[Either[ExecutionError, EVALUATED]]] = + override def apply[F[_]: Monad](context: Environment[F]): Eval[F[Either[ExecutionError, EVALUATED]]] = v.asRight[ExecutionError].pure[F].pure[Eval] } - trait Lifted[C[_[_]]] extends ContextfulVal[C] { - override def apply[F[_]: Monad](context: C[F]): Eval[F[Either[ExecutionError, EVALUATED]]] = + trait Lifted extends ContextfulVal { + override def apply[F[_]: Monad](context: Environment[F]): Eval[F[Either[ExecutionError, EVALUATED]]] = liftF(context).map(_.pure[F]) - def liftF[F[_]: Monad](context: C[F]): Eval[Either[ExecutionError, EVALUATED]] + def liftF[F[_]: Monad](context: Environment[F]): Eval[Either[ExecutionError, EVALUATED]] } } - -object Contextful { - type NoContext[_[_]] = Any - def empty[F[_]]: NoContext[F] = () -} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ContractEvaluator.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ContractEvaluator.scala index 531b5e6576c..a793db2af56 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ContractEvaluator.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ContractEvaluator.scala @@ -6,15 +6,13 @@ import com.wavesplatform.common.state.ByteStr import com.wavesplatform.lang.contract.DApp import com.wavesplatform.lang.contract.DApp.VerifierFunction import com.wavesplatform.lang.directives.values.StdLibVersion +import com.wavesplatform.lang.miniev.{Ev, State} import com.wavesplatform.lang.v1.FunctionHeader import com.wavesplatform.lang.v1.compiler.Terms.* -import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Bindings -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.traits.domain.Recipient.Address import com.wavesplatform.lang.v1.traits.domain.{AttachedPayments, Recipient} -import com.wavesplatform.lang.{CommonError, ExecutionError} -import monix.eval.Coeval +import com.wavesplatform.lang.{CommonError, ExecutionError, SoftLimitReached} object ContractEvaluator { @@ -104,14 +102,13 @@ object ContractEvaluator { val verifierBlock = BLOCK( invocationArgLet, - BLOCK(v.u, FUNCTION_CALL(FunctionHeader.User(v.u.name), List(entity))) + BLOCK(v.u, FUNCTION_CALL(FunctionHeader.User(v.u.name), List())) ) evaluate(foldDeclarations(decls, verifierBlock), LogExtraInfo(invokedFuncName = Some(v.u.name), invArg = Some(invocationArgLet))) } def applyV2Coeval( - ctx: EvaluationContext[Environment, Id], dApp: DApp, dAppAddress: ByteStr, i: Invocation, @@ -119,28 +116,24 @@ object ContractEvaluator { limit: Int, correctFunctionCallScope: Boolean, newMode: Boolean, - enableExecutionLog: Boolean - ): Coeval[Either[(ExecutionError, Int, Log[Id]), (ScriptResult, Log[Id])]] = - Coeval - .now(buildExprFromInvocation(dApp, i, version).leftMap((_, limit, Nil))) - .flatMap { - case Right(value) => - applyV2Coeval( - ctx, - value.expr, - LogExtraInfo(invokedFuncName = Some(i.funcCall.function.funcName), invArg = value.invArg, dAppAddress = Some(Address(dAppAddress))), - version, - i.transactionId, - limit, - correctFunctionCallScope, - newMode, - enableExecutionLog - ) - case Left(error) => Coeval.now(Left(error)) + state: State + ): Either[(ExecutionError, Int, Log[Id]), (ScriptResult, Log[Id])] = + buildExprFromInvocation(dApp, i, version) + .leftMap((_, limit, Nil)) + .flatMap { value => + applyV2Coeval( + value.expr, + LogExtraInfo(invokedFuncName = Some(i.funcCall.function.funcName), invArg = value.invArg, dAppAddress = Some(Address(dAppAddress))), + version, + i.transactionId, + limit, + correctFunctionCallScope, + newMode, + state + ) } private def applyV2Coeval( - ctx: EvaluationContext[Environment, Id], expr: EXPR, logExtraInfo: LogExtraInfo, version: StdLibVersion, @@ -148,16 +141,18 @@ object ContractEvaluator { limit: Int, correctFunctionCallScope: Boolean, newMode: Boolean, - enableExecutionLog: Boolean - ): Coeval[Either[(ExecutionError, Int, Log[Id]), (ScriptResult, Log[Id])]] = - EvaluatorV2 - .applyLimitedCoeval(expr, logExtraInfo, limit, ctx, version, correctFunctionCallScope, newMode, enableExecutionLog = enableExecutionLog) - .map(_.flatMap { case (expr, unusedComplexity, log) => - val result = - expr match { - case value: EVALUATED => ScriptResult.fromObj(ctx, transactionId, value, version, unusedComplexity) - case expr: EXPR => Right(IncompleteResult(expr, unusedComplexity)) - } - result.bimap((_, unusedComplexity, log), (_, log)) - }) + state: State + ): Either[(ExecutionError, Int, Log[Id]), (ScriptResult, Log[Id])] = { + val (log, complexity, resultE) = Ev.run(expr, state) + + resultE match { + case Right(ev) => + ScriptResult + .fromObj(state.evaluationContext, transactionId, ev, version, complexity) + .leftMap(ee => (ee, complexity, log)) + .map(_ -> log) + case Left(SoftLimitReached) => Right((IncompleteResult(FAIL("FAIL: Soft limit reached"), complexity), log)) + case Left(ee) => Left((ee, complexity, log)) + } + } } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/EvaluatorV1.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/EvaluatorV1.scala index cb750530586..796d6f3bb5e 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/EvaluatorV1.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/EvaluatorV1.scala @@ -6,11 +6,10 @@ import cats.{Eval, Id, Monad, StackSafeMonad} import com.wavesplatform.lang.v1.FunctionHeader import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.Types.{CASETYPEREF, NOTHING} -import com.wavesplatform.lang.v1.evaluator.ContextfulNativeFunction.{Extended, Simple} +import com.wavesplatform.lang.v1.evaluator.ContextfulNativeFunction.Simple import com.wavesplatform.lang.v1.evaluator.ctx.* import com.wavesplatform.lang.v1.evaluator.ctx.EnabledLogEvaluationContext.Lenses import com.wavesplatform.lang.v1.task.imports.* -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.{CoevalF, CommonError, EvalF, ExecutionError} import scala.collection.mutable.ListBuffer @@ -25,93 +24,89 @@ object EvaluatorV1 { Eval.now(x) } - private val evaluator = new EvaluatorV1[Id, Environment] - def apply(): EvaluatorV1[Id, Environment] = evaluator + private val evaluator = new EvaluatorV1[Id] + def apply(): EvaluatorV1[Id] = evaluator } -class EvaluatorV1[F[_]: Monad, C[_[_]]](implicit ev: Monad[EvalF[F, *]], ev2: Monad[CoevalF[F, *]]) { - private val lenses = new Lenses[F, C] +class EvaluatorV1[F[_]: Monad](implicit ev: Monad[EvalF[F, *]], ev2: Monad[CoevalF[F, *]]) { + private val lenses = new Lenses[F] import lenses.* - private def evalLetBlock(let: LET, inner: EXPR): EvalM[F, C, (EvaluationContext[C, F], EVALUATED)] = + private def evalLetBlock(let: LET, inner: EXPR): EvalM[F, (EvaluationContext[F], EVALUATED)] = for { - ctx <- get[F, EnabledLogEvaluationContext[C, F], ExecutionError] + ctx <- get[F, EnabledLogEvaluationContext[F], ExecutionError] blockEvaluation = evalExpr(let.value) lazyBlock = LazyVal(blockEvaluation.ter(ctx), ctx.l(let.name)) result <- local { - modify[F, EnabledLogEvaluationContext[C, F], ExecutionError](lets.modify(_)(_.updated(let.name, lazyBlock))) + modify[F, EnabledLogEvaluationContext[F], ExecutionError](lets.modify(_)(_.updated(let.name, lazyBlock))) .flatMap(_ => evalExprWithCtx(inner)) } } yield result - private def evalFuncBlock(func: FUNC, inner: EXPR): EvalM[F, C, (EvaluationContext[C, F], EVALUATED)] = { + private def evalFuncBlock(func: FUNC, inner: EXPR): EvalM[F, (EvaluationContext[F], EVALUATED)] = { val funcHeader = FunctionHeader.User(func.name) val function = UserFunction(func.name, 0, NOTHING, func.args.map(n => (n, NOTHING))*)(func.body) - .asInstanceOf[UserFunction[C]] + .asInstanceOf[UserFunction] local { - modify[F, EnabledLogEvaluationContext[C, F], ExecutionError](funcs.modify(_)(_.updated(funcHeader, function))) + modify[F, EnabledLogEvaluationContext[F], ExecutionError](funcs.modify(_)(_.updated(funcHeader, function))) .flatMap(_ => evalExprWithCtx(inner)) } } - private def evalRef(key: String): EvalM[F, C, (EvaluationContext[C, F], EVALUATED)] = + private def evalRef(key: String): EvalM[F, (EvaluationContext[F], EVALUATED)] = for { - ctx <- get[F, EnabledLogEvaluationContext[C, F], ExecutionError] + ctx <- get[F, EnabledLogEvaluationContext[F], ExecutionError] r <- lets.get(ctx).get(key) match { - case Some(lzy) => liftTER[F, C, EVALUATED](lzy.value) - case None => raiseError[F, EnabledLogEvaluationContext[C, F], ExecutionError, EVALUATED](s"A definition of '$key' not found") + case Some(lzy) => liftTER[F, EVALUATED](lzy.value) + case None => raiseError[F, EnabledLogEvaluationContext[F], ExecutionError, EVALUATED](s"A definition of '$key' not found") } } yield (ctx.ec, r) - private def evalIF(cond: EXPR, ifTrue: EXPR, ifFalse: EXPR): EvalM[F, C, (EvaluationContext[C, F], EVALUATED)] = + private def evalIF(cond: EXPR, ifTrue: EXPR, ifFalse: EXPR): EvalM[F, (EvaluationContext[F], EVALUATED)] = evalExpr(cond) flatMap { case TRUE => evalExprWithCtx(ifTrue) case FALSE => evalExprWithCtx(ifFalse) case _ => ??? } - private def evalGetter(expr: EXPR, field: String): EvalM[F, C, (EvaluationContext[C, F], EVALUATED)] = { - Monad[EvalM[F, C, *]].flatMap(evalExprWithCtx(expr)) { case (ctx, exprResult) => + private def evalGetter(expr: EXPR, field: String): EvalM[F, (EvaluationContext[F], EVALUATED)] = { + Monad[EvalM[F, *]].flatMap(evalExprWithCtx(expr)) { case (ctx, exprResult) => val fields = exprResult.asInstanceOf[CaseObj].fields fields.get(field) match { - case Some(f) => (ctx, f).pure[EvalM[F, C, *]] + case Some(f) => (ctx, f).pure[EvalM[F, *]] case None => raiseError(s"A definition of '$field' not found amongst ${fields.keys}") } } } - private def evalFunctionCall(header: FunctionHeader, args: List[EXPR]): EvalM[F, C, (EvaluationContext[C, F], EVALUATED)] = + private def evalFunctionCall(header: FunctionHeader, args: List[EXPR]): EvalM[F, (EvaluationContext[F], EVALUATED)] = for { - ctx <- get[F, EnabledLogEvaluationContext[C, F], ExecutionError] + ctx <- get[F, EnabledLogEvaluationContext[F], ExecutionError] result <- funcs .get(ctx) .get(header) .map { - case func: UserFunction[C] => - Monad[EvalM[F, C, *]].flatMap(args.traverse(evalExpr)) { args => + case func: UserFunction => + Monad[EvalM[F, *]].flatMap(args.traverse(evalExpr)) { args => val letDefsWithArgs = args.zip(func.signature.args).foldLeft(ctx.ec.letDefs) { case (r, (argValue, (argName, _))) => r + (argName -> LazyVal.fromEvaluated(argValue, ctx.l(s"$argName"))) } local { - val newState: EvalM[F, C, Unit] = - set[F, EnabledLogEvaluationContext[C, F], ExecutionError](lets.set(ctx)(letDefsWithArgs)).map(_.pure[F]) - Monad[EvalM[F, C, *]].flatMap(newState)(_ => evalExpr(func.ev(ctx.ec.environment, args))) + val newState: EvalM[F, Unit] = + set[F, EnabledLogEvaluationContext[F], ExecutionError](lets.set(ctx)(letDefsWithArgs)).map(_.pure[F]) + Monad[EvalM[F, *]].flatMap(newState)(_ => evalExpr(func.ev(ctx.ec.environment, args))) } - }: EvalM[F, C, EVALUATED] - case func: NativeFunction[C] => - Monad[EvalM[F, C, *]].flatMap(args.traverse(evalExpr)) { args => + }: EvalM[F, EVALUATED] + case func: NativeFunction => + Monad[EvalM[F, *]].flatMap(args.traverse(evalExpr)) { args => val evaluated = func.ev match { - case f: Simple[C] => + case f: Simple => val r = Try(f.evaluate(ctx.ec.environment, args)).toEither .bimap(e => CommonError(e.toString): ExecutionError, EitherT(_)) .pure[F] EitherT(r).flatten.value.pure[Eval] - case f: Extended[C] => - f.evaluate(ctx.ec.environment, args, Int.MaxValue) - .map(_.map(_._1.map(_._1))) - .to[Eval] } - liftTER[F, C, EVALUATED](evaluated) + liftTER[F, EVALUATED](evaluated) } } .orElse( @@ -120,16 +115,16 @@ class EvaluatorV1[F[_]: Monad, C[_[_]]](implicit ev: Monad[EvalF[F, *]], ev2: Mo case FunctionHeader.User(typeName, _) => types.get(ctx).get(typeName).collect { case t @ CASETYPEREF(_, fields, _) => args - .traverse[EvalM[F, C, *], EVALUATED](evalExpr) + .traverse[EvalM[F, *], EVALUATED](evalExpr) .map(values => CaseObj(t, fields.map(_._1).zip(values).toMap): EVALUATED) } case _ => None } ) - .getOrElse(raiseError[F, EnabledLogEvaluationContext[C, F], ExecutionError, EVALUATED](s"function '$header' not found")) + .getOrElse(raiseError[F, EnabledLogEvaluationContext[F], ExecutionError, EVALUATED](s"function '$header' not found")) } yield (ctx.ec, result) - private def evalExprWithCtx(t: EXPR): EvalM[F, C, (EvaluationContext[C, F], EVALUATED)] = + private def evalExprWithCtx(t: EXPR): EvalM[F, (EvaluationContext[F], EVALUATED)] = t match { case LET_BLOCK(let, inner) => evalLetBlock(let, inner) case BLOCK(dec, inner) => @@ -139,27 +134,27 @@ class EvaluatorV1[F[_]: Monad, C[_[_]]](implicit ev: Monad[EvalF[F, *]], ev2: Mo case _: FAILED_DEC => raiseError("Attempt to evaluate failed declaration.") } case REF(str) => evalRef(str) - case c: EVALUATED => get[F, EnabledLogEvaluationContext[C, F], ExecutionError].map(ctx => (ctx.ec, c)) + case c: EVALUATED => get[F, EnabledLogEvaluationContext[F], ExecutionError].map(ctx => (ctx.ec, c)) case IF(cond, t1, t2) => evalIF(cond, t1, t2) case GETTER(expr, field) => evalGetter(expr, field) case FUNCTION_CALL(header, args) => evalFunctionCall(header, args) case _: FAILED_EXPR => raiseError("Attempt to evaluate failed expression.") } - private def evalExpr(t: EXPR): EvalM[F, C, EVALUATED] = + private def evalExpr(t: EXPR): EvalM[F, EVALUATED] = evalExprWithCtx(t).map(_._2) - def applyWithLogging[A <: EVALUATED](c: EvaluationContext[C, F], expr: EXPR): F[Either[(ExecutionError, Log[F]), (A, Log[F])]] = { + def applyWithLogging[A <: EVALUATED](c: EvaluationContext[F], expr: EXPR): F[Either[(ExecutionError, Log[F]), (A, Log[F])]] = { val log = ListBuffer[LogItem[F]]() - val lec = EnabledLogEvaluationContext[C, F]((str: String) => (v: LetExecResult[F]) => log.append((str, v)), c) + val lec = EnabledLogEvaluationContext[F]((str: String) => (v: LetExecResult[F]) => log.append((str, v)), c) val r = evalExpr(expr).map(_.asInstanceOf[A]).run(lec).value._2 r.map(_.bimap((_, log.toList), (_, log.toList))) } - def apply[A <: EVALUATED](c: EvaluationContext[C, F], expr: EXPR): F[Either[ExecutionError, A]] = + def apply[A <: EVALUATED](c: EvaluationContext[F], expr: EXPR): F[Either[ExecutionError, A]] = applyWithLogging[A](c, expr).map(_.bimap(_._1, _._1)) - def applyWithCtx(c: EvaluationContext[C, F], expr: EXPR): F[Either[ExecutionError, (EvaluationContext[C, F], EVALUATED)]] = + def applyWithCtx(c: EvaluationContext[F], expr: EXPR): F[Either[ExecutionError, (EvaluationContext[F], EVALUATED)]] = evalExprWithCtx(expr) .run(EnabledLogEvaluationContext(_ => _ => (), c)) .value diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/EvaluatorV2.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/EvaluatorV2.scala index aed5720e7c4..a9dbe976da7 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/EvaluatorV2.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/EvaluatorV2.scala @@ -5,10 +5,11 @@ import cats.instances.lazyList.* import cats.syntax.either.* import cats.syntax.foldable.* import com.wavesplatform.lang.directives.values.StdLibVersion +import com.wavesplatform.lang.miniev.Ev import com.wavesplatform.lang.v1.FunctionHeader import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.Types.CASETYPEREF -import com.wavesplatform.lang.v1.evaluator.ContextfulNativeFunction.{Extended, Simple} +import com.wavesplatform.lang.v1.evaluator.ContextfulNativeFunction.Simple import com.wavesplatform.lang.v1.evaluator.ctx.{ DisabledLogEvaluationContext, EnabledLogEvaluationContext, @@ -18,19 +19,19 @@ import com.wavesplatform.lang.v1.evaluator.ctx.{ UserFunction } import com.wavesplatform.lang.v1.evaluator.ContractEvaluator.LogExtraInfo -import com.wavesplatform.lang.v1.evaluator.EvaluatorV2.logFunc import com.wavesplatform.lang.v1.evaluator.EvaluatorV2.LogKeys.* +import com.wavesplatform.lang.v1.evaluator.EvaluatorV2.logFunc +import com.wavesplatform.lang.v1.evaluator.ctx.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Bindings +import com.wavesplatform.lang.{CommonError, ExecutionError, miniev} import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.{CommonError, ExecutionError} import monix.eval.Coeval -import shapeless.syntax.std.tuple.* import scala.annotation.tailrec -import scala.collection.mutable.ListBuffer class EvaluatorV2( - val ctx: LoggedEvaluationContext[Environment, Id], + val ctx: LoggedEvaluationContext[Id], val stdLibVersion: StdLibVersion, val correctFunctionCallScope: Boolean, val newMode: Boolean, @@ -97,20 +98,13 @@ class EvaluatorV2( if (limit < cost) { EvaluationResult(limit) } else - doEvaluateNativeFunction(fc, function.asInstanceOf[NativeFunction[Environment]], limit, cost) + doEvaluateNativeFunction(fc, function.asInstanceOf[NativeFunction], limit, cost) } yield result - def doEvaluateNativeFunction(fc: FUNCTION_CALL, function: NativeFunction[Environment], limit: Int, cost: Int): EvaluationResult[Int] = { + def doEvaluateNativeFunction(fc: FUNCTION_CALL, function: NativeFunction, limit: Int, cost: Int): EvaluationResult[Int] = { val args = fc.args.asInstanceOf[List[EVALUATED]] val evaluation = function.ev match { - case f: Extended[Environment] => - f.evaluate[Id](ctx.ec.environment, args, limit - cost).map { case (result, unusedComplexity) => - result.map { case (evaluated, log) => - log.foreach { case (logItemName, logItemValue) => ctx.log(LET(logItemName, TRUE), logItemValue) } - evaluated - } -> unusedComplexity - } - case f: Simple[Environment] => Coeval((f.evaluate(ctx.ec.environment, args), limit - cost)) + case f: Simple => Coeval((f.evaluate(ctx.ec.environment, args), limit - cost)) } for { (result, unusedComplexity) <- EvaluationResult( @@ -136,7 +130,7 @@ class EvaluatorV2( def evaluateUserFunction(fc: FUNCTION_CALL, limit: Int, name: String, startArgs: List[EXPR]): Option[EvaluationResult[Int]] = ctx.ec.functions .get(fc.function) - .map(_.asInstanceOf[UserFunction[Environment]]) + .map(_.asInstanceOf[UserFunction]) .map { f => val func = FUNC(f.name, f.args.toList, f.ev[Id](ctx.ec.environment, startArgs)) val precalculatedLimit = @@ -350,38 +344,21 @@ class EvaluatorV2( } object EvaluatorV2 { - def applyLimitedCoeval( + def applyLimited( expr: EXPR, logExtraInfo: LogExtraInfo, limit: Int, - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], stdLibVersion: StdLibVersion, correctFunctionCallScope: Boolean, newMode: Boolean, - checkConstructorArgsTypes: Boolean = false, - enableExecutionLog: Boolean = false - ): Coeval[Either[(ExecutionError, Int, Log[Id]), (EXPR, Int, Log[Id])]] = { - val log = ListBuffer[LogItem[Id]]() - - val loggedCtx = if (enableExecutionLog) { - EnabledLogEvaluationContext[Environment, Id](name => value => log.append((name, value)), ctx) - } else { - DisabledLogEvaluationContext[Environment, Id](ctx) - } - var ref = expr.deepCopy.value - logCall(loggedCtx, logExtraInfo, ref, enableExecutionLog) - new EvaluatorV2(loggedCtx, stdLibVersion, correctFunctionCallScope, newMode, enableExecutionLog, checkConstructorArgsTypes) - .root(ref, v => EvaluationResult { ref = v }, limit, Nil) - .map((ref, _)) - .value - .redeem( - e => Left((e.getMessage, limit, log.toList)), - _.bimap(_ :+ log.toList, _ :+ log.toList) - ) + checkConstructorArgsTypes: Boolean = false + ) = { + Ev.run(expr, ctx, miniev.ComplexityLimit.Complete(limit), newMode, stdLibVersion) } def applyOrDefault( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], expr: EXPR, logExtraInfo: LogExtraInfo, stdLibVersion: StdLibVersion, @@ -392,29 +369,18 @@ object EvaluatorV2 { enableExecutionLog: Boolean ): (Log[Id], Int, Either[ExecutionError, EVALUATED]) = EvaluatorV2 - .applyLimitedCoeval( + .applyLimited( expr, logExtraInfo, complexityLimit, ctx, stdLibVersion, correctFunctionCallScope, - newMode, - enableExecutionLog = enableExecutionLog - ) - .value() - .fold( - { case (error, complexity, log) => (log, complexity, Left(error)) }, - { case (result, complexity, log) => - result match { - case evaluated: EVALUATED => (log, complexity, Right(evaluated)) - case expr: EXPR => (log, complexity, handleExpr(expr)) - } - } + newMode ) def applyCompleted( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], expr: EXPR, logExtraInfo: LogExtraInfo, stdLibVersion: StdLibVersion, @@ -435,7 +401,7 @@ object EvaluatorV2 { ) private def logCall( - loggedCtx: LoggedEvaluationContext[Environment, Id], + loggedCtx: LoggedEvaluationContext[Id], logExtraInfo: LogExtraInfo, exprCopy: EXPR, enableExecutionLog: Boolean @@ -469,7 +435,7 @@ object EvaluatorV2 { private def logFunc( fc: FUNCTION_CALL, - ctx: LoggedEvaluationContext[Environment, Id], + ctx: LoggedEvaluationContext[Id], stdLibVersion: StdLibVersion, limit: Int, enableExecutionLog: Boolean @@ -491,7 +457,7 @@ object EvaluatorV2 { } } - private def logFuncArgs(fc: FUNCTION_CALL, name: String, ctx: LoggedEvaluationContext[Environment, Id]): Unit = { + private def logFuncArgs(fc: FUNCTION_CALL, name: String, ctx: LoggedEvaluationContext[Id]): Unit = { val argsArr = ARR(fc.args.collect { case arg: EVALUATED => arg }.toIndexedSeq, false) argsArr.foreach(_ => ctx.log(LET(s"$name.$Args", TRUE), argsArr)) } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ScriptResult.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ScriptResult.scala index 48c31a3e87a..43485a2fdf7 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ScriptResult.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ScriptResult.scala @@ -1,43 +1,41 @@ package com.wavesplatform.lang.v1.evaluator import cats.Id -import cats.implicits._ +import cats.implicits.* import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.lang.{ExecutionError, CommonError} import com.wavesplatform.lang.directives.values.{StdLibVersion, V3, V4, V5} import com.wavesplatform.lang.v1.compiler.ScriptResultSource.CallableFunction -import com.wavesplatform.lang.v1.compiler.Terms._ +import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.Types.CASETYPEREF import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext -import com.wavesplatform.lang.v1.evaluator.ctx.impl._ +import com.wavesplatform.lang.v1.evaluator.ctx.impl.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.{FieldNames, Types} -import com.wavesplatform.lang.v1.traits.Environment +import com.wavesplatform.lang.v1.traits.domain.* import com.wavesplatform.lang.v1.traits.domain.Recipient.{Address, Alias} -import com.wavesplatform.lang.v1.traits.domain._ +import com.wavesplatform.lang.{CommonError, ExecutionError} sealed trait ScriptResult { def returnedValue: EVALUATED = unit def invokes: Seq[(Address, String, Seq[EVALUATED], Seq[CaseObj], ScriptResult)] = Nil - def unusedComplexity: Int + def spentComplexity: Int def actions: List[CallableAction] } -case class ScriptResultV3(ds: List[DataItem[_]], ts: List[AssetTransfer], unusedComplexity: Int) extends ScriptResult { +case class ScriptResultV3(ds: List[DataItem[_]], ts: List[AssetTransfer], spentComplexity: Int) extends ScriptResult { override lazy val actions: List[CallableAction] = ds ++ ts } - case class ScriptResultV4( actions: List[CallableAction], - unusedComplexity: Int, + spentComplexity: Int, override val returnedValue: EVALUATED = unit ) extends ScriptResult -case class IncompleteResult(expr: EXPR, unusedComplexity: Int) extends ScriptResult { +case class IncompleteResult(expr: EXPR, spentComplexity: Int) extends ScriptResult { override val actions: List[CallableAction] = Nil } object ScriptResult { - type ActionInput = (EvaluationContext[Environment, Id], ByteStr, Map[String, EVALUATED]) + type ActionInput = (EvaluationContext[Id], ByteStr, Map[String, EVALUATED]) type ActionResult = Either[ExecutionError, CallableAction] type ActionHandlers = Map[String, ActionInput => ActionResult] @@ -105,7 +103,7 @@ object ScriptResult { } private def processScriptTransfer( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], fields: Map[String, EVALUATED], version: StdLibVersion ): Either[ExecutionError, AssetTransfer] = @@ -127,7 +125,7 @@ object ScriptResult { err(other, version, FieldNames.ScriptTransfer) } - private def processRecipient(obj: CaseObj, ctx: EvaluationContext[Environment, Id], version: StdLibVersion): Either[ExecutionError, Recipient] = + private def processRecipient(obj: CaseObj, ctx: EvaluationContext[Id], version: StdLibVersion): Either[ExecutionError, Recipient] = if (obj.caseType.name == Types.addressType.name) obj.fields("bytes") match { case CONST_BYTESTR(addBytes) => Right(Address(addBytes)) @@ -152,7 +150,7 @@ object ScriptResult { } private def processTransferSetV3( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], fields: Map[String, EVALUATED] ): Either[ExecutionError, List[AssetTransfer]] = fields(FieldNames.Transfers) match { @@ -165,9 +163,9 @@ object ScriptResult { } private def processActionV3( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], fields: Map[String, EVALUATED], - unusedComplexity: Int + usedComplexity: Int ): Either[ExecutionError, ScriptResultV3] = { val writes = fields(FieldNames.ScriptWriteSet) match { case CaseObj(tpe, fields) if tpe.name == FieldNames.WriteSet => processWriteSetV3(fields) @@ -180,19 +178,19 @@ object ScriptResult { for { w <- writes p <- payments - } yield ScriptResultV3(w, p, unusedComplexity) + } yield ScriptResultV3(w, p, usedComplexity) } private def processScriptResultV3( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], tpe: CASETYPEREF, fields: Map[String, EVALUATED], - unusedComplexity: Int + usedComplexity: Int ) = tpe.name match { - case FieldNames.WriteSet => processWriteSetV3(fields).map(ScriptResultV3(_, List.empty, unusedComplexity)) - case FieldNames.TransferSet => processTransferSetV3(ctx, fields).map(ScriptResultV3(List.empty, _, unusedComplexity)) - case FieldNames.ScriptResult => processActionV3(ctx, fields, unusedComplexity) + case FieldNames.WriteSet => processWriteSetV3(fields).map(ScriptResultV3(_, List.empty, usedComplexity)) + case FieldNames.TransferSet => processTransferSetV3(ctx, fields).map(ScriptResultV3(List.empty, _, usedComplexity)) + case FieldNames.ScriptResult => processActionV3(ctx, fields, usedComplexity) case f => err(f, V3) } @@ -267,7 +265,7 @@ object ScriptResult { } private def processLease( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], fields: Map[String, EVALUATED], version: StdLibVersion ): Either[ExecutionError, Lease] = @@ -288,12 +286,12 @@ object ScriptResult { } private def processScriptResult( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], txId: ByteStr, actions: Seq[EVALUATED], handlers: ActionHandlers, version: StdLibVersion, - unusedComplexity: Int, + usedComplexity: Int, ret: EVALUATED = unit ): Either[ExecutionError, ScriptResultV4] = actions.toList @@ -306,7 +304,7 @@ object ScriptResult { case other => err(other, version) } - .map(ScriptResultV4(_, unusedComplexity, ret)) + .map(ScriptResultV4(_, usedComplexity, ret)) private def fromV4ActionHandlers(v: StdLibVersion): ActionHandlers = Map( @@ -332,11 +330,11 @@ object ScriptResult { private val v5ActionHandlers = fromV4ActionHandlers(V5) ++ fromV5ActionHandlers(V5) def fromObj( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], txId: ByteStr, e: EVALUATED, version: StdLibVersion, - unusedComplexity: Int + usedComplexity: Int ): Either[ExecutionError, ScriptResult] = { def processResultWithValue( tpe: CASETYPEREF, @@ -344,14 +342,14 @@ object ScriptResult { v: StdLibVersion ) = (fields.get("_1"), fields.get("_2")) match { - case (Some(ARR(actions)), Some(ret)) => processScriptResult(ctx, txId, actions, v5ActionHandlers, v, unusedComplexity, ret) + case (Some(ARR(actions)), Some(ret)) => processScriptResult(ctx, txId, actions, v5ActionHandlers, v, usedComplexity, ret) case _ => err(tpe.name, version) } (e, version) match { - case (CaseObj(tpe, fields), V3) => processScriptResultV3(ctx, tpe, fields, unusedComplexity) - case (ARR(actions), V4) => processScriptResult(ctx, txId, actions, v4ActionHandlers, V4, unusedComplexity) - case (ARR(actions), v) if v >= V5 => processScriptResult(ctx, txId, actions, v5ActionHandlers, v, unusedComplexity) + case (CaseObj(tpe, fields), V3) => processScriptResultV3(ctx, tpe, fields, usedComplexity) + case (ARR(actions), V4) => processScriptResult(ctx, txId, actions, v4ActionHandlers, V4, usedComplexity) + case (ARR(actions), v) if v >= V5 => processScriptResult(ctx, txId, actions, v5ActionHandlers, v, usedComplexity) case (CaseObj(tpe, fields), v) if v >= V5 => processResultWithValue(tpe, fields, v) case c => err(c.toString, version) } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/EvaluationContext.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/EvaluationContext.scala index 1013b3bae49..cac1b237fcd 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/EvaluationContext.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/EvaluationContext.scala @@ -1,39 +1,39 @@ package com.wavesplatform.lang.v1.evaluator.ctx +import java.util + import cats.* import cats.syntax.functor.* import com.wavesplatform.lang.ExecutionError import com.wavesplatform.lang.v1.FunctionHeader import com.wavesplatform.lang.v1.compiler.Terms.LET import com.wavesplatform.lang.v1.compiler.Types.FINAL -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext -import com.wavesplatform.lang.v1.evaluator.{Contextful, LetExecResult, LetLogCallback} +import com.wavesplatform.lang.v1.evaluator.{LetExecResult, LetLogCallback} +import com.wavesplatform.lang.v1.traits.Environment import shapeless.{Lens, lens} -import java.util - -case class EvaluationContext[C[_[_]], F[_]]( - environment: C[F], +case class EvaluationContext[F[_]]( + environment: Environment[F], typeDefs: Map[String, FINAL], letDefs: Map[String, LazyVal[F]], - functions: Map[FunctionHeader, BaseFunction[C]] + functions: Map[FunctionHeader, BaseFunction] ) { - def mapK[G[_]: Monad](f: F ~> G): EvaluationContext[C, G] = + def mapK[G[_]: Monad](f: F ~> G): EvaluationContext[G] = EvaluationContext( - environment.asInstanceOf[C[G]], + environment.asInstanceOf[Environment[G]], typeDefs, letDefs.view.mapValues(_.mapK(f)).toMap, functions ) } -trait LoggedEvaluationContext[C[_[_]], F[_]] { - def ec: EvaluationContext[C, F] +trait LoggedEvaluationContext[F[_]] { + def ec: EvaluationContext[F] def log(let: LET, result: LetExecResult[F]): Unit } -case class EnabledLogEvaluationContext[C[_[_]], F[_]: Monad](l: LetLogCallback[F], ec: EvaluationContext[C, F]) - extends LoggedEvaluationContext[C, F] { +case class EnabledLogEvaluationContext[F[_]: Monad](l: LetLogCallback[F], ec: EvaluationContext[F]) + extends LoggedEvaluationContext[F] { val loggedLets: util.IdentityHashMap[LET, Unit] = new util.IdentityHashMap() val loggedErrors: collection.mutable.Set[ExecutionError] = collection.mutable.Set() @@ -47,63 +47,37 @@ case class EnabledLogEvaluationContext[C[_[_]], F[_]: Monad](l: LetLogCallback[F } } - private def add(let: LET, result: LetExecResult[F]): Unit = - loggedLets.computeIfAbsent(let, _ => l(let.name)(result)) + private def add(let: LET, result: LetExecResult[F]): Unit = { +// loggedLets.computeIfAbsent(let, _ => l(let.name)(result)) + } } object EnabledLogEvaluationContext { - class Lenses[F[_]: Monad, C[_[_]]] { - val types: Lens[EnabledLogEvaluationContext[C, F], Map[String, FINAL]] = - lens[EnabledLogEvaluationContext[C, F]] >> Symbol("ec") >> Symbol("typeDefs") - val lets: Lens[EnabledLogEvaluationContext[C, F], Map[String, LazyVal[F]]] = - lens[EnabledLogEvaluationContext[C, F]] >> Symbol("ec") >> Symbol("letDefs") - val funcs: Lens[EnabledLogEvaluationContext[C, F], Map[FunctionHeader, BaseFunction[C]]] = - lens[EnabledLogEvaluationContext[C, F]] >> Symbol("ec") >> Symbol("functions") + class Lenses[F[_]: Monad] { + val types: Lens[EnabledLogEvaluationContext[F], Map[String, FINAL]] = + lens[EnabledLogEvaluationContext[F]] >> Symbol("ec") >> Symbol("typeDefs") + val lets: Lens[EnabledLogEvaluationContext[F], Map[String, LazyVal[F]]] = + lens[EnabledLogEvaluationContext[F]] >> Symbol("ec") >> Symbol("letDefs") + val funcs: Lens[EnabledLogEvaluationContext[F], Map[FunctionHeader, BaseFunction]] = + lens[EnabledLogEvaluationContext[F]] >> Symbol("ec") >> Symbol("functions") } } -case class DisabledLogEvaluationContext[C[_[_]], F[_]](ec: EvaluationContext[C, F]) extends LoggedEvaluationContext[C, F] { +case class DisabledLogEvaluationContext[F[_]](ec: EvaluationContext[F]) extends LoggedEvaluationContext[F] { override def log(let: LET, result: LetExecResult[F]): Unit = () } object EvaluationContext { - - val empty = EvaluationContext(Contextful.empty[Id], Map.empty, Map.empty, Map.empty) - - implicit def monoid[F[_], C[_[_]]]: Monoid[EvaluationContext[C, F]] = new Monoid[EvaluationContext[C, F]] { - override val empty: EvaluationContext[C, F] = EvaluationContext.empty.asInstanceOf[EvaluationContext[C, F]] - - override def combine(x: EvaluationContext[C, F], y: EvaluationContext[C, F]): EvaluationContext[C, F] = - EvaluationContext( - environment = y.environment, - typeDefs = x.typeDefs ++ y.typeDefs, - letDefs = x.letDefs ++ y.letDefs, - functions = x.functions ++ y.functions - ) - } - - def build[F[_], C[_[_]]]( - environment: C[F], + def build[F[_]]( + environment: Environment[F], typeDefs: Map[String, FINAL], letDefs: Map[String, LazyVal[F]], - functions: Seq[BaseFunction[C]] - ): EvaluationContext[C, F] = { + functions: Seq[BaseFunction] + ): EvaluationContext[F] = { if (functions.distinct.size != functions.size) { val dups = functions.groupBy(_.header).filter(_._2.size != 1) throw new Exception(s"Duplicate runtime functions names: $dups") } EvaluationContext(environment, typeDefs, letDefs, functions.map(f => f.header -> f).toMap) } - - def build( - typeDefs: Map[String, FINAL], - letDefs: Map[String, LazyVal[Id]], - functions: Seq[BaseFunction[NoContext]] = Seq() - ): EvaluationContext[NoContext, Id] = { - if (functions.distinct.size != functions.size) { - val dups = functions.groupBy(_.header).filter(_._2.size != 1) - throw new Exception(s"Duplicate runtime functions names: $dups") - } - EvaluationContext[NoContext, Id](Contextful.empty[Id], typeDefs, letDefs, functions.map(f => f.header -> f).toMap) - } } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/InvariableContext.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/InvariableContext.scala index 6012894cf86..ff1467a69ba 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/InvariableContext.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/InvariableContext.scala @@ -5,18 +5,18 @@ import com.wavesplatform.lang.utils.environment import com.wavesplatform.lang.v1.CTX import com.wavesplatform.lang.v1.traits.Environment -case class InvariableContext(private val ctx: CTX[Environment]) { - private val constants = ctx.vars.collect { case (k, v) if v._2.isPure => k -> LazyVal.fromEval(v._2(environment)) } +case class InvariableContext(private val ctx: CTX) { + private val constants = ctx.vars.collect { case (k, v) if v._2.isPure => k -> LazyVal.fromEval(v._2(environment)) } private def vars(env: Environment[Id]) = ctx.vars.collect { case (k, v) if !v._2.isPure => k -> LazyVal.fromEval(v._2(env)) } - private val rawEvaluationContext: EvaluationContext[Environment, Id] = - EvaluationContext[Environment, Id]( + private val rawEvaluationContext: EvaluationContext[Id] = + EvaluationContext[Id]( environment, ctx.typeDefs, constants, ctx.functionMap ) - def completeContext(env: Environment[Id]): EvaluationContext[Environment, Id] = + def completeContext(env: Environment[Id]): EvaluationContext[Id] = rawEvaluationContext.copy(environment = env, letDefs = constants ++ vars(env)) } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/LazyVal.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/LazyVal.scala index 2abc9b42c11..0dc141342c1 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/LazyVal.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/LazyVal.scala @@ -1,8 +1,8 @@ package com.wavesplatform.lang.v1.evaluator.ctx -import cats.instances.either._ -import cats.syntax.applicative._ -import cats.syntax.flatMap._ +import cats.instances.either.* +import cats.syntax.applicative.* +import cats.syntax.flatMap.* import cats.{Eval, Monad, ~>} import com.wavesplatform.lang.v1.compiler.Terms.EVALUATED import com.wavesplatform.lang.v1.evaluator.LogCallback @@ -29,9 +29,6 @@ object LazyVal { def apply[F[_] : Monad](v: TrampolinedExecResult[F, EVALUATED], lc: LogCallback[F]): LazyVal[F] = LazyValImpl(v.value, lc) - def apply[F[_] : Monad](v: TrampolinedExecResult[F, EVALUATED]): LazyVal[F] = - LazyValImpl(v.value, _ => Monad[F].unit) - def fromEval[F[_] : Monad](v: Eval[F[Either[ExecutionError, EVALUATED]]]): LazyVal[F] = LazyValImpl(v, _ => Monad[F].unit) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/NativeFunction.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/NativeFunction.scala index c06a5e5c480..933fab56fa1 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/NativeFunction.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/NativeFunction.scala @@ -1,19 +1,21 @@ package com.wavesplatform.lang.v1.evaluator.ctx import cats.Monad -import cats.syntax.applicative._ +import cats.syntax.applicative.* import com.wavesplatform.lang.ExecutionError import com.wavesplatform.lang.directives.DirectiveDictionary import com.wavesplatform.lang.directives.values.StdLibVersion +import com.wavesplatform.lang.miniev.State import com.wavesplatform.lang.v1.FunctionHeader import com.wavesplatform.lang.v1.compiler.Terms.{EVALUATED, EXPR} -import com.wavesplatform.lang.v1.compiler.Types._ +import com.wavesplatform.lang.v1.compiler.Types.* import com.wavesplatform.lang.v1.evaluator.{ContextfulNativeFunction, ContextfulUserFunction} +import com.wavesplatform.lang.v1.traits.Environment import scala.annotation.meta.field -import scala.scalajs.js.annotation._ +import scala.scalajs.js.annotation.* -sealed trait BaseFunction[C[_[_]]] { +sealed trait BaseFunction { @JSExport def signature: FunctionTypeSignature @JSExport def header: FunctionHeader = signature.header @JSExport def name: String @@ -27,34 +29,36 @@ sealed trait BaseFunction[C[_[_]]] { } object BaseFunction { - implicit def header[C[_[_]]](bf: BaseFunction[C]): FunctionHeader = bf.header + implicit def header(bf: BaseFunction): FunctionHeader = bf.header } @JSExportTopLevel("FunctionTypeSignature") case class FunctionTypeSignature(result: TYPE, args: Seq[(String, TYPE)], header: FunctionHeader) @JSExportTopLevel("NativeFunction") -case class NativeFunction[C[_[_]]]( - @(JSExport @field) name: String, - costByLibVersionMap: Map[StdLibVersion, Long], - @(JSExport @field) signature: FunctionTypeSignature, - ev: ContextfulNativeFunction[C], - @(JSExport @field) args: Seq[String] -) extends BaseFunction[C] +case class NativeFunction( + @(JSExport @field) name: String, + costByLibVersionMap: Map[StdLibVersion, Long], + @(JSExport @field) signature: FunctionTypeSignature, + ev: ContextfulNativeFunction, + @(JSExport @field) args: Seq[String] +) extends BaseFunction object NativeFunction { - def withEnvironment[C[_[_]]](name: String, cost: Long, internalName: Short, resultType: TYPE, args: (String, TYPE)*)( - ev: ContextfulNativeFunction[C]): NativeFunction[C] = + def withEnvironment(name: String, cost: Long, internalName: Short, resultType: TYPE, args: (String, TYPE)*)( + ev: ContextfulNativeFunction + ): NativeFunction = new NativeFunction( name = name, costByLibVersionMap = DirectiveDictionary[StdLibVersion].all.map(_ -> cost).toMap, signature = FunctionTypeSignature(result = resultType, args = args.map(a => (a._1, a._2)), header = FunctionHeader.Native(internalName)), - ev = ev /*ev.orElse { case _ => "Passed argument with wrong type".asLeft[EVALUATED].pure[F] }(_, _)*/, + ev = ev, args = args.map(_._1) ) - def withEnvironment[C[_[_]]](name: String, costByLibVersion: Map[StdLibVersion, Long], internalName: Short, resultType: TYPE, args: (String, TYPE)*)( - ev: ContextfulNativeFunction[C]): NativeFunction[C] = + def withEnvironment(name: String, costByLibVersion: Map[StdLibVersion, Long], internalName: Short, resultType: TYPE, args: (String, TYPE)*)( + ev: ContextfulNativeFunction + ): NativeFunction = new NativeFunction( name = name, costByLibVersion, @@ -63,35 +67,35 @@ object NativeFunction { args = args.map(_._1) ) - def apply[C[_[_]]](name: String, cost: Long, internalName: Short, resultType: TYPE, args: (String, TYPE)*)( - evl: List[EVALUATED] => Either[ExecutionError, EVALUATED] - ): NativeFunction[C] = - withEnvironment[C](name, cost, internalName, resultType, args*)(new ContextfulNativeFunction.Simple[C](name, resultType, args.toSeq) { - override def evaluate[F[_]: Monad](env: C[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = + def apply(name: String, cost: Long, internalName: Short, resultType: TYPE, args: (String, TYPE)*)( + evl: List[EVALUATED] => Either[ExecutionError, EVALUATED] + ): NativeFunction = + withEnvironment(name, cost, internalName, resultType, args*)(new ContextfulNativeFunction.Simple(name, resultType, args.toSeq) { + override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = evl(args).pure[F] }) def apply[C[_[_]]](name: String, costByLibVersion: Map[StdLibVersion, Long], internalName: Short, resultType: TYPE, args: (String, TYPE)*)( - evl: List[EVALUATED] => Either[ExecutionError, EVALUATED] - ): NativeFunction[C] = - withEnvironment[C](name, costByLibVersion, internalName, resultType, args*)(new ContextfulNativeFunction.Simple[C](name, resultType, args.toSeq) { - override def evaluate[F[_]: Monad](env: C[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = + evl: List[EVALUATED] => Either[ExecutionError, EVALUATED] + ): NativeFunction = + withEnvironment(name, costByLibVersion, internalName, resultType, args*)(new ContextfulNativeFunction.Simple(name, resultType, args.toSeq) { + override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = evl(args).pure[F] }) } @JSExportTopLevel("UserFunction") -case class UserFunction[C[_[_]]]( - @(JSExport@field) name: String, - @(JSExport@field) internalName: String, - costByLibVersionMap: Map[StdLibVersion, Long], - @(JSExport@field) signature: FunctionTypeSignature, - ev: ContextfulUserFunction[C], - @(JSExport@field) args: Seq[String] -) extends BaseFunction[C] +case class UserFunction( + @(JSExport @field) name: String, + @(JSExport @field) internalName: String, + costByLibVersionMap: Map[StdLibVersion, Long], + @(JSExport @field) signature: FunctionTypeSignature, + ev: ContextfulUserFunction, + @(JSExport @field) args: Seq[String] +) extends BaseFunction object UserFunction { - def withEnvironment[C[_[_]]](name: String, cost: Long, resultType: TYPE, args: (String, TYPE)*)(ev: ContextfulUserFunction[C]): UserFunction[C] = + def withEnvironment(name: String, cost: Long, resultType: TYPE, args: (String, TYPE)*)(ev: ContextfulUserFunction): UserFunction = UserFunction.withEnvironment( name = name, internalName = name, @@ -100,31 +104,29 @@ object UserFunction { args* )(ev) - def apply[C[_[_]]](name: String, cost: Long, resultType: TYPE, args: (String, TYPE)*)(ev: EXPR): UserFunction[C] = - UserFunction.withEnvironment[C](name, cost, resultType, args*)(new ContextfulUserFunction[C] { - override def apply[F[_] : Monad](context: C[F], startArgs: List[EXPR]): EXPR = ev + def apply(name: String, cost: Long, resultType: TYPE, args: (String, TYPE)*)(ev: EXPR): UserFunction = + UserFunction.withEnvironment(name, cost, resultType, args*)(new ContextfulUserFunction { + override def apply[F[_]: Monad](context: Environment[F], startArgs: List[EXPR]): EXPR = ev }) - def deprecated[C[_[_]]](name: String, cost: Long, resultType: TYPE, args: (String, TYPE)*)(ev: EXPR): UserFunction[C] = + def deprecated(name: String, cost: Long, resultType: TYPE, args: (String, TYPE)*)(ev: EXPR): UserFunction = UserFunction.deprecated(name, name, DirectiveDictionary[StdLibVersion].all.map(_ -> cost).toMap, resultType, args*)(ev) - def apply[C[_[_]]](name: String, costByLibVersion: Map[StdLibVersion, Long], resultType: TYPE, args: (String, TYPE)*)( - ev: EXPR): UserFunction[C] = + def apply(name: String, costByLibVersion: Map[StdLibVersion, Long], resultType: TYPE, args: (String, TYPE)*)(ev: EXPR): UserFunction = UserFunction(name, name, costByLibVersion, resultType, args*)(ev) - def apply[C[_[_]]](name: String, internalName: String, cost: Long, resultType: TYPE, args: (String, TYPE)*)( - ev: EXPR): UserFunction[C] = - UserFunction.withEnvironment[C](name, internalName, DirectiveDictionary[StdLibVersion].all.map(_ -> cost).toMap, resultType, args*)( - ContextfulUserFunction.pure[C](ev) + def apply(name: String, internalName: String, cost: Long, resultType: TYPE, args: (String, TYPE)*)(ev: EXPR): UserFunction = + UserFunction.withEnvironment(name, internalName, DirectiveDictionary[StdLibVersion].all.map(_ -> cost).toMap, resultType, args*)( + ContextfulUserFunction.pure(ev) ) - def withEnvironment[C[_[_]]]( - name: String, - internalName: String, - costByLibVersion: Map[StdLibVersion, Long], - resultType: TYPE, - args: (String, TYPE)* - )(ev: ContextfulUserFunction[C]): UserFunction[C] = + def withEnvironment( + name: String, + internalName: String, + costByLibVersion: Map[StdLibVersion, Long], + resultType: TYPE, + args: (String, TYPE)* + )(ev: ContextfulUserFunction): UserFunction = new UserFunction( name = name, internalName = internalName, @@ -134,32 +136,42 @@ object UserFunction { args = args.map(_._1) ) - def apply[C[_[_]]]( - name: String, - internalName: String, - costByLibVersion: Map[StdLibVersion, Long], - resultType: TYPE, - args: (String, TYPE)* - )(ev: EXPR): UserFunction[C] = - withEnvironment[C](name, internalName, costByLibVersion, resultType, args*)( - ContextfulUserFunction.pure[C](ev) + def apply( + name: String, + internalName: String, + costByLibVersion: Map[StdLibVersion, Long], + resultType: TYPE, + args: (String, TYPE)* + )(ev: EXPR): UserFunction = + withEnvironment(name, internalName, costByLibVersion, resultType, args*)( + ContextfulUserFunction.pure(ev) ) - def deprecated[C[_[_]]]( - name: String, - internalName: String, - costByLibVersion: Map[StdLibVersion, Long], - resultType: TYPE, - args: (String, TYPE)* - )(ev: EXPR): UserFunction[C] = - new UserFunction[C]( + def deprecated( + name: String, + internalName: String, + costByLibVersion: Map[StdLibVersion, Long], + resultType: TYPE, + args: (String, TYPE)* + )(ev: EXPR): UserFunction = + new UserFunction( name = name, internalName = internalName, costByLibVersionMap = costByLibVersion, signature = FunctionTypeSignature(result = resultType, args = args.map(a => (a._1, a._2)), header = FunctionHeader.User(internalName, name)), - ContextfulUserFunction.pure[C](ev), + ContextfulUserFunction.pure(ev), args = args.map(_._1) ) { override def deprecated = true } } + +abstract class ExtendedInternalFunction(delegate: BaseFunction) extends BaseFunction { + + override def signature: FunctionTypeSignature = delegate.signature + override def name: String = delegate.name + override def args: Seq[String] = delegate.args + override val costByLibVersionMap: Map[StdLibVersion, Long] = delegate.costByLibVersionMap + + def buildExpression(state: State, args: List[EVALUATED]): Either[ExecutionError, EXPR] +} diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/CryptoContext.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/CryptoContext.scala index 4c5968fca64..6968b8f08e0 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/CryptoContext.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/CryptoContext.scala @@ -4,21 +4,43 @@ import cats.implicits.* import cats.{Id, Monad} import com.wavesplatform.common.merkle.Merkle.createRoot import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.lang.{ExecutionError, CommonError} -import com.wavesplatform.lang.directives.values.{StdLibVersion, V3, *} +import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.Types.* import com.wavesplatform.lang.v1.compiler.{CompilerContext, Terms} -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext -import com.wavesplatform.lang.v1.evaluator.ContextfulVal import com.wavesplatform.lang.v1.evaluator.FunctionIds.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.crypto.RSA.DigestAlgorithm -import com.wavesplatform.lang.v1.evaluator.ctx.{BaseFunction, EvaluationContext, NativeFunction} +import com.wavesplatform.lang.v1.evaluator.ctx.{BaseFunction, NativeFunction} +import com.wavesplatform.lang.v1.evaluator.{ContextfulNativeFunction, ContextfulVal} +import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.{BaseGlobal, CTX} +import com.wavesplatform.lang.{CommonError, ExecutionError} import scala.collection.mutable object CryptoContext { + val global: BaseGlobal = com.wavesplatform.lang.Global + + class SigVerifyF(name: String, limit: Int) + extends ContextfulNativeFunction.Simple( + name, + BOOLEAN, + Seq(("message", BYTESTR), ("sig", BYTESTR), ("pub", BYTESTR)) + ) { + override def evaluate[F[_]: Monad](env: Environment[F], evaluatedArgs: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = + evaluatedArgs match { + case CONST_BYTESTR(msg) :: CONST_BYTESTR(sig) :: CONST_BYTESTR(pub) :: Nil => + Either + .cond[ExecutionError, EVALUATED]( + msg.size <= limit * 1024, + CONST_BOOLEAN(global.curve25519verify(msg.arr, sig.arr, pub.arr)), + s"Invalid message size = ${msg.size} bytes, must be not greater than $limit KB" + ) + .pure[F] + case xs => + notImplemented[F, EVALUATED](s"sigVerify_${limit}Kb(message: ByteVector, sig: ByteVector, pub: ByteVector)", xs) + } + } val rsaTypeNames = List("NoAlg", "Md5", "Sha1", "Sha224", "Sha256", "Sha384", "Sha512", "Sha3224", "Sha3256", "Sha3384", "Sha3512") @@ -30,7 +52,7 @@ object CryptoContext { UNION.create(rsaHashAlgs(v), if (v > V3 && v < V6) Some("RsaDigestAlgs") else None) private val rsaHashLib = { - import com.wavesplatform.lang.v1.evaluator.ctx.impl.crypto.RSA._ + import com.wavesplatform.lang.v1.evaluator.ctx.impl.crypto.RSA.* rsaTypeNames.zip(List(NONE, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, SHA3224, SHA3256, SHA3384, SHA3512)).toMap } @@ -38,10 +60,10 @@ object CryptoContext { rsaHashLib.get(obj.caseType.name).fold(Left("Unknown digest type"): Either[ExecutionError, DigestAlgorithm])(Right(_)) } - private def digestAlgValue(tpe: CASETYPEREF): ContextfulVal[NoContext] = + private def digestAlgValue(tpe: CASETYPEREF): ContextfulVal = ContextfulVal.pure(CaseObj(tpe, Map.empty)) - def build(global: BaseGlobal, version: StdLibVersion): CTX[NoContext] = + def build(global: BaseGlobal, version: StdLibVersion): CTX = ctxCache.getOrElse( (global, version), ctxCache.synchronized { @@ -49,29 +71,29 @@ object CryptoContext { } ) - private val ctxCache = mutable.AnyRefMap.empty[(BaseGlobal, StdLibVersion), CTX[NoContext]] + private val ctxCache = mutable.AnyRefMap.empty[(BaseGlobal, StdLibVersion), CTX] - private def buildNew(global: BaseGlobal, version: StdLibVersion): CTX[NoContext] = { + private def buildNew(global: BaseGlobal, version: StdLibVersion): CTX = { def functionFamily( startId: Short, nameByLimit: Int => String, costByLimit: List[(Int, Int)], returnType: TYPE, args: (String, TYPE)* - )(body: (Int, List[EVALUATED]) => Either[ExecutionError, EVALUATED]): Array[BaseFunction[NoContext]] = + )(body: (Int, List[EVALUATED]) => Either[ExecutionError, EVALUATED]): Array[BaseFunction] = costByLimit.mapWithIndex { case ((limit, cost), i) => val name = nameByLimit(limit) val id = (startId + i).toShort - NativeFunction[NoContext](name, cost, id, returnType, args*)(args => body(limit, args)) + NativeFunction(name, cost, id, returnType, args*)(args => body(limit, args)) }.toArray - def hashFunction(name: String, internalName: Short, cost: Long)(h: Array[Byte] => Array[Byte]): BaseFunction[NoContext] = + def hashFunction(name: String, internalName: Short, cost: Long)(h: Array[Byte] => Array[Byte]): BaseFunction = NativeFunction(name, cost, internalName, BYTESTR, ("bytes", BYTESTR)) { case CONST_BYTESTR(m) :: Nil => CONST_BYTESTR(ByteStr(h(m.arr))) case xs => notImplemented[Id, EVALUATED](s"$name(bytes: ByteVector)", xs) } - val keccak256F: BaseFunction[NoContext] = { + val keccak256F: BaseFunction = { val complexity = if (version < V4) 10 else if (version < V6) 200 @@ -79,7 +101,7 @@ object CryptoContext { hashFunction("keccak256", KECCAK256, complexity)(global.keccak256) } - val blake2b256F: BaseFunction[NoContext] = { + val blake2b256F: BaseFunction = { val complexity = if (version < V4) 10 else if (version < V6) 200 @@ -87,7 +109,7 @@ object CryptoContext { hashFunction("blake2b256", BLAKE256, complexity)(global.blake2b256) } - val sha256F: BaseFunction[NoContext] = { + val sha256F: BaseFunction = { val complexity = if (version < V4) 10 else if (version < V6) 200 @@ -99,7 +121,7 @@ object CryptoContext { name: String, startId: Short, costByLimit: List[(Int, Int)] - )(hash: Array[Byte] => Array[Byte]): Array[BaseFunction[NoContext]] = + )(hash: Array[Byte] => Array[Byte]): Array[BaseFunction] = functionFamily( startId, limit => s"${name}_${limit}Kb", @@ -116,7 +138,7 @@ object CryptoContext { notImplemented[Id, EVALUATED](s"${name}_${limit}Kb(bytes: ByteVector)", xs) } - def keccak256F_lim: Array[BaseFunction[NoContext]] = + def keccak256F_lim: Array[BaseFunction] = hashLimFunction( "keccak256", KECCAK256_LIM, @@ -136,7 +158,7 @@ object CryptoContext { ) )(global.keccak256) - val blake2b256F_lim: Array[BaseFunction[NoContext]] = + val blake2b256F_lim: Array[BaseFunction] = hashLimFunction( "blake2b256", BLAKE256_LIM, @@ -156,7 +178,7 @@ object CryptoContext { ) )(global.blake2b256) - val sha256F_lim: Array[BaseFunction[NoContext]] = + val sha256F_lim: Array[BaseFunction] = hashLimFunction( "sha256", SHA256_LIM, @@ -176,7 +198,7 @@ object CryptoContext { ) )(global.sha256) - val sigVerifyL: Array[BaseFunction[NoContext]] = + val sigVerifyL: Array[BaseFunction] = functionFamily( SIGVERIFY_LIM, limit => s"sigVerify_${limit}Kb", @@ -211,7 +233,7 @@ object CryptoContext { notImplemented[Id, EVALUATED](s"sigVerify_${limit}Kb(message: ByteVector, sig: ByteVector, pub: ByteVector)", xs) } - def sigVerifyF(contextVer: StdLibVersion): BaseFunction[NoContext] = { + def sigVerifyF(contextVer: StdLibVersion): BaseFunction = { val lim = global.MaxByteStrSizeForVerifyFuncs val complexity = if (version < V4) @@ -220,13 +242,9 @@ object CryptoContext { 200 else 180 - NativeFunction("sigVerify", complexity, SIGVERIFY, BOOLEAN, ("message", BYTESTR), ("sig", BYTESTR), ("pub", BYTESTR)) { - case CONST_BYTESTR(msg) :: CONST_BYTESTR(_) :: CONST_BYTESTR(_) :: Nil if contextVer == V3 && msg.size > lim => - Left(s"Invalid message size = ${msg.size} bytes, must be not greater than ${lim / 1024} KB") - case CONST_BYTESTR(msg) :: CONST_BYTESTR(sig) :: CONST_BYTESTR(pub) :: Nil => - Right(CONST_BOOLEAN(global.curve25519verify(msg.arr, sig.arr, pub.arr))) - case xs => notImplemented[Id, EVALUATED](s"sigVerify(message: ByteVector, sig: ByteVector, pub: ByteVector)", xs) - } + NativeFunction.withEnvironment("sigVerify", complexity, SIGVERIFY, BOOLEAN, ("message", BYTESTR), ("sig", BYTESTR), ("pub", BYTESTR))( + new SigVerifyF("sigVerify", lim) + ) } def rsaVerify( @@ -240,7 +258,7 @@ object CryptoContext { result <- global.rsaVerify(alg, msg.arr, sig.arr, pub.arr).leftMap(CommonError(_)) } yield CONST_BOOLEAN(result) - val rsaVerifyF: BaseFunction[NoContext] = { + val rsaVerifyF: BaseFunction = { val lim = global.MaxByteStrSizeForVerifyFuncs NativeFunction( "rsaVerify", @@ -264,7 +282,7 @@ object CryptoContext { } } - def rsaVerifyL(version: StdLibVersion): Array[BaseFunction[NoContext]] = + def rsaVerifyL(version: StdLibVersion): Array[BaseFunction] = functionFamily( RSAVERIFY_LIM, limit => s"rsaVerify_${limit}Kb", @@ -292,7 +310,7 @@ object CryptoContext { ) } - def toBase58StringF: BaseFunction[NoContext] = + def toBase58StringF: BaseFunction = NativeFunction( "toBase58String", Map[StdLibVersion, Long](V1 -> 10L, V2 -> 10L, V3 -> 10L, V4 -> 3L), @@ -305,7 +323,7 @@ object CryptoContext { case xs => notImplemented[Id, EVALUATED]("toBase58String(bytes: ByteVector)", xs) } - def fromBase58StringF: BaseFunction[NoContext] = + def fromBase58StringF: BaseFunction = NativeFunction( "fromBase58String", Map[StdLibVersion, Long](V1 -> 10L, V2 -> 10L, V3 -> 10L, V4 -> 1L), @@ -318,7 +336,7 @@ object CryptoContext { case xs => notImplemented[Id, EVALUATED]("fromBase58String(str: String)", xs) } - def toBase64StringF: BaseFunction[NoContext] = + def toBase64StringF: BaseFunction = NativeFunction( "toBase64String", Map[StdLibVersion, Long](V1 -> 10L, V2 -> 10L, V3 -> 10L, V4 -> 35L), @@ -331,7 +349,7 @@ object CryptoContext { case xs => notImplemented[Id, EVALUATED]("toBase64String(bytes: ByteVector)", xs) } - def fromBase64StringF: BaseFunction[NoContext] = + def fromBase64StringF: BaseFunction = NativeFunction( "fromBase64String", Map[StdLibVersion, Long](V1 -> 10L, V2 -> 10L, V3 -> 10L, V4 -> 40L), @@ -344,7 +362,7 @@ object CryptoContext { case xs => notImplemented[Id, EVALUATED]("fromBase64String(str: String)", xs) } - val checkMerkleProofF: BaseFunction[NoContext] = + val checkMerkleProofF: BaseFunction = NativeFunction( "checkMerkleProof", 30, @@ -359,7 +377,7 @@ object CryptoContext { case xs => notImplemented[Id, EVALUATED](s"checkMerkleProof(merkleRoot: ByteVector, merkleProof: ByteVector, valueBytes: ByteVector)", xs) } - val createMerkleRootF: BaseFunction[NoContext] = + val createMerkleRootF: BaseFunction = NativeFunction( "createMerkleRoot", 30, @@ -382,18 +400,18 @@ object CryptoContext { case xs => notImplemented[Id, EVALUATED](s"createMerkleRoot(merkleProof: ByteVector, valueBytes: ByteVector)", xs) } - def toBase16StringF(checkLength: Boolean): BaseFunction[NoContext] = NativeFunction("toBase16String", 10, TOBASE16, STRING, ("bytes", BYTESTR)) { + def toBase16StringF(checkLength: Boolean): BaseFunction = NativeFunction("toBase16String", 10, TOBASE16, STRING, ("bytes", BYTESTR)) { case CONST_BYTESTR(bytes) :: Nil => global.base16Encode(bytes.arr, checkLength).leftMap(CommonError(_)).flatMap(CONST_STRING(_)) case xs => notImplemented[Id, EVALUATED]("toBase16String(bytes: ByteVector)", xs) } - def fromBase16StringF(checkLength: Boolean): BaseFunction[NoContext] = + def fromBase16StringF(checkLength: Boolean): BaseFunction = NativeFunction("fromBase16String", 10, FROMBASE16, BYTESTR, ("str", STRING)) { case CONST_STRING(str: String) :: Nil => global.base16Decode(str, checkLength).leftMap(CommonError(_)).flatMap(x => CONST_BYTESTR(ByteStr(x))) case xs => notImplemented[Id, EVALUATED]("fromBase16String(str: String)", xs) } - val bls12Groth16VerifyL: Array[BaseFunction[NoContext]] = + val bls12Groth16VerifyL: Array[BaseFunction] = functionFamily( BLS12_GROTH16_VERIFY_LIM, limit => s"groth16Verify_${limit}inputs", @@ -418,7 +436,7 @@ object CryptoContext { notImplemented[Id, EVALUATED](s"groth16Verify_${limit}inputs(vk:ByteVector, proof:ByteVector, inputs:ByteVector)", xs) } - val bn256Groth16VerifyL: Array[BaseFunction[NoContext]] = { + val bn256Groth16VerifyL: Array[BaseFunction] = { val complexities = List(800, 850, 950, 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1550, 1600) functionFamily( BN256_GROTH16_VERIFY_LIM, @@ -445,7 +463,7 @@ object CryptoContext { } } - val bls12Groth16VerifyF: BaseFunction[NoContext] = + val bls12Groth16VerifyF: BaseFunction = NativeFunction( "groth16Verify", 2700, @@ -470,7 +488,7 @@ object CryptoContext { notImplemented[Id, EVALUATED]("groth16Verify(vk:ByteVector, proof:ByteVector, inputs:ByteVector)", xs) } - val bn256Groth16VerifyF: BaseFunction[NoContext] = + val bn256Groth16VerifyF: BaseFunction = NativeFunction( "bn256Groth16Verify", 1650, @@ -494,7 +512,7 @@ object CryptoContext { case xs => notImplemented[Id, EVALUATED]("bn256Groth16Verify(vk:ByteVector, proof:ByteVector, inputs:ByteVector)", xs) } - val ecrecover: BaseFunction[NoContext] = + val ecrecover: BaseFunction = NativeFunction( "ecrecover", 70, @@ -532,13 +550,13 @@ object CryptoContext { val v4Types = v4RsaDig :+ digestAlgorithmType(V4) val v6Types = v4RsaDig :+ digestAlgorithmType(V6) - val v4Vars: Map[String, (FINAL, ContextfulVal[NoContext])] = + val v4Vars: Map[String, (FINAL, ContextfulVal)] = rsaVarNames.zip(v4RsaDig.map(t => (t, digestAlgValue(t)))).toMap val v3RsaDig = rsaHashAlgs(V3) val v3Types = v3RsaDig :+ digestAlgorithmType(V3) - val v3Vars: Map[String, (FINAL, ContextfulVal[NoContext])] = + val v3Vars: Map[String, (FINAL, ContextfulVal)] = rsaVarNames.zip(v3RsaDig.map(t => (t, digestAlgValue(t)))).toMap val v3Functions = @@ -560,10 +578,10 @@ object CryptoContext { fromBase16StringF(checkLength = true) // from V3 ) ++ sigVerifyL ++ rsaVerifyL(version) ++ keccak256F_lim ++ blake2b256F_lim ++ sha256F_lim ++ bls12Groth16VerifyL ++ bn256Groth16VerifyL - val fromV1Ctx = CTX[NoContext](Seq(), Map(), v1Functions) - val fromV3Ctx = fromV1Ctx |+| CTX[NoContext](v3Types, v3Vars, v3Functions) - val fromV4Ctx = fromV1Ctx |+| CTX[NoContext](v4Types, v4Vars, fromV4Functions(V4)) - val fromV6Ctx = fromV1Ctx |+| CTX[NoContext](v6Types, v4Vars, fromV4Functions(V6)) + val fromV1Ctx = CTX(Seq(), Map(), v1Functions) + val fromV3Ctx = fromV1Ctx |+| CTX(v3Types, v3Vars, v3Functions) + val fromV4Ctx = fromV1Ctx |+| CTX(v4Types, v4Vars, fromV4Functions(V4)) + val fromV6Ctx = fromV1Ctx |+| CTX(v6Types, v4Vars, fromV4Functions(V6)) version match { case V1 | V2 => fromV1Ctx @@ -573,9 +591,6 @@ object CryptoContext { } } - def evalContext[F[_]: Monad](global: BaseGlobal, version: StdLibVersion): EvaluationContext[NoContext, F] = - build(global, version).evaluationContext[F] - def compilerContext(global: BaseGlobal, version: StdLibVersion): CompilerContext = build(global, version).compilerContext } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/PureContext.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/PureContext.scala index bb040b08b0e..42352461c8d 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/PureContext.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/PureContext.scala @@ -1,5 +1,9 @@ package com.wavesplatform.lang.v1.evaluator.ctx.impl +import java.nio.charset.StandardCharsets.UTF_8 +import java.nio.charset.{MalformedInputException, StandardCharsets} +import java.nio.{BufferUnderflowException, ByteBuffer} + import cats.implicits.* import cats.{Id, Monad} import com.google.common.annotations.VisibleForTesting @@ -15,17 +19,14 @@ import com.wavesplatform.lang.v1.compiler.Terms import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.Terms.CONST_BYTESTR.NoLimit import com.wavesplatform.lang.v1.compiler.Types.* -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext import com.wavesplatform.lang.v1.evaluator.FunctionIds.* import com.wavesplatform.lang.v1.evaluator.ctx.* import com.wavesplatform.lang.v1.evaluator.{ContextfulUserFunction, ContextfulVal} import com.wavesplatform.lang.v1.parser.BinaryOperation import com.wavesplatform.lang.v1.parser.BinaryOperation.* +import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.{BaseGlobal, CTX, FunctionHeader, compiler} -import java.nio.charset.StandardCharsets.UTF_8 -import java.nio.charset.{MalformedInputException, StandardCharsets} -import java.nio.{BufferUnderflowException, ByteBuffer} import scala.annotation.tailrec import scala.collection.mutable.ArrayBuffer import scala.util.{Success, Try} @@ -33,7 +34,6 @@ import scala.util.{Success, Try} object PureContext { private val global: BaseGlobal = com.wavesplatform.lang.Global - implicit def intToLong(num: Int): Long = num.toLong private def trimLongToInt(x: Long): Int = Math.toIntExact(Math.max(Math.min(x, Int.MaxValue), Int.MinValue)) private val defaultThrowMessage = "Explicit script termination" @@ -44,25 +44,25 @@ object PureContext { // As an optimization, JVM might throw an ArithmeticException with empty stack trace and null message. // The workaround below retrows an exception with the message explicitly set. - lazy val divLong: BaseFunction[NoContext] = + lazy val divLong: BaseFunction = createTryOp(DIV_OP, LONG, LONG, DIV_LONG) { (a, b) => try Math.floorDiv(a, b) catch { case _: ArithmeticException => throw new ArithmeticException("/ by zero") } } - lazy val modLong: BaseFunction[NoContext] = + lazy val modLong: BaseFunction = createTryOp(MOD_OP, LONG, LONG, MOD_LONG) { (a, b) => try Math.floorMod(a, b) catch { case _: ArithmeticException => throw new ArithmeticException("/ by zero") } } - lazy val mulLong: BaseFunction[NoContext] = + lazy val mulLong: BaseFunction = createTryOp(MUL_OP, LONG, LONG, MUL_LONG)((a, b) => Math.multiplyExact(a, b)) - lazy val sumLong: BaseFunction[NoContext] = + lazy val sumLong: BaseFunction = createTryOp(SUM_OP, LONG, LONG, SUM_LONG)((a, b) => Math.addExact(a, b)) - lazy val subLong: BaseFunction[NoContext] = + lazy val subLong: BaseFunction = createTryOp(SUB_OP, LONG, LONG, SUB_LONG)((a, b) => Math.subtractExact(a, b)) - lazy val sumString: BaseFunction[NoContext] = + lazy val sumString: BaseFunction = createRawOp( SUM_OP, STRING, @@ -81,7 +81,7 @@ object PureContext { Left(s"Unexpected args $args for string concatenation operator") } - lazy val sumByteStr: BaseFunction[NoContext] = + lazy val sumByteStr: BaseFunction = createRawOp( SUM_OP, BYTESTR, @@ -98,20 +98,20 @@ object PureContext { case args => Left(s"Unexpected args $args for bytes concatenation operator") } - lazy val ge: BaseFunction[NoContext] = createOp(GE_OP, LONG, BOOLEAN, GE_LONG)(_ >= _) - lazy val gt: BaseFunction[NoContext] = + lazy val ge: BaseFunction = createOp(GE_OP, LONG, BOOLEAN, GE_LONG)(_ >= _) + lazy val gt: BaseFunction = createOp(GT_OP, LONG, BOOLEAN, GT_LONG)(_ > _) - lazy val geBigInt: BaseFunction[NoContext] = bigIntConditionOp(GE_OP, GE_BIGINT)(_ >= _) - lazy val gtBigInt: BaseFunction[NoContext] = bigIntConditionOp(GT_OP, GT_BIGINT)(_ > _) + lazy val geBigInt: BaseFunction = bigIntConditionOp(GE_OP, GE_BIGINT)(_ >= _) + lazy val gtBigInt: BaseFunction = bigIntConditionOp(GT_OP, GT_BIGINT)(_ > _) - lazy val eq: BaseFunction[NoContext] = + lazy val eq: BaseFunction = NativeFunction(EQ_OP.func, 1, EQ, BOOLEAN, ("a", TYPEPARAM('T')), ("b", TYPEPARAM('T'))) { case a :: b :: Nil => Either.cond(b.weight <= MaxCmpWeight || a.weight <= MaxCmpWeight, CONST_BOOLEAN(a == b), "Comparable value too heavy.") case xs => notImplemented[Id, EVALUATED](s"${EQ_OP.func}(a: T, b: T)", xs) } - lazy val ne: BaseFunction[NoContext] = + lazy val ne: BaseFunction = UserFunction( NE_OP.func, Map[StdLibVersion, Long](V1 -> 26, V2 -> 26, V3 -> 1, V4 -> 1), @@ -122,26 +122,26 @@ object PureContext { FUNCTION_CALL(uNot, List(FUNCTION_CALL(eq, List(REF("@a"), REF("@b"))))) } - lazy val intToBigInt: BaseFunction[NoContext] = + lazy val intToBigInt: BaseFunction = NativeFunction("toBigInt", 1, TO_BIGINT, BIGINT, ("n", LONG)) { case CONST_LONG(n) :: Nil => Right(CONST_BIGINT(BigInt(n))) case xs => notImplemented[Id, EVALUATED]("toBigInt(n: Int)", xs) } - lazy val bigIntToInt: BaseFunction[NoContext] = + lazy val bigIntToInt: BaseFunction = NativeFunction("toInt", 1, BIGINT_TO_INT, LONG, ("n", BIGINT)) { case CONST_BIGINT(n) :: Nil => Either.cond(Long.MaxValue >= n && n >= Long.MinValue, CONST_LONG(n.toLong), s"toInt: BigInt $n out of integers range") case xs => notImplemented[Id, EVALUATED]("toBigInt(n: Int)", xs) } - lazy val bigIntToString: BaseFunction[NoContext] = + lazy val bigIntToString: BaseFunction = NativeFunction("toString", Map(V5 -> 65L, V6 -> 1L), BIGINT_TO_STRING, STRING, ("n", BIGINT)) { case CONST_BIGINT(n) :: Nil => CONST_STRING(n.toString) case xs => notImplemented[Id, EVALUATED]("toString(n: BigInt)", xs) } - lazy val stringToBigInt: BaseFunction[NoContext] = + lazy val stringToBigInt: BaseFunction = NativeFunction("parseBigIntValue", 65, STRING_TO_BIGINT, BIGINT, ("n", STRING)) { case CONST_STRING(n) :: Nil => Either @@ -152,7 +152,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("parseBigIntValue(n: String)", xs) } - lazy val stringToBigIntOpt: BaseFunction[NoContext] = + lazy val stringToBigIntOpt: BaseFunction = NativeFunction("parseBigInt", 65, STRING_TO_BIGINTOPT, UNION(BIGINT, UNIT), ("n", STRING)) { case CONST_STRING(n) :: Nil => Right((if (n.length <= 155) { @@ -172,13 +172,13 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("parseBigInt(n: String)", xs) } - lazy val bigIntToBytes: BaseFunction[NoContext] = + lazy val bigIntToBytes: BaseFunction = NativeFunction("toBytes", 65, BIGINT_TO_BYTES, BYTESTR, ("n", BIGINT)) { case CONST_BIGINT(n) :: Nil => CONST_BYTESTR(ByteStr(n.toByteArray)) case xs => notImplemented[Id, EVALUATED]("toBytes(n: BigInt)", xs) } - lazy val bytesToBigIntLim: BaseFunction[NoContext] = + lazy val bytesToBigIntLim: BaseFunction = NativeFunction("toBigInt", 65, BYTES_TO_BIGINT_LIM, BIGINT, ("n", BYTESTR), ("off", LONG), ("size", LONG)) { case CONST_BYTESTR(ByteStr(n)) :: CONST_LONG(off) :: CONST_LONG(s) :: Nil => Either.cond( @@ -189,7 +189,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("toBigInt(n: ByteStr, offset: Int, size: Int)", xs) } - lazy val bytesToBigInt: BaseFunction[NoContext] = + lazy val bytesToBigInt: BaseFunction = NativeFunction("toBigInt", 65, BYTES_TO_BIGINT, BIGINT, ("n", BYTESTR)) { case CONST_BYTESTR(ByteStr(n)) :: Nil => Either.cond(n.length <= 64, CONST_BIGINT(BigInt(n)), s"Too big ByteVector for BigInt (${n.length} > 64 bytes)") @@ -198,7 +198,7 @@ object PureContext { def bigIntArithmeticOp(op: BinaryOperation, func: Short, complexity: Map[StdLibVersion, Long])( body: (BigInt, BigInt) => BigInt - ): BaseFunction[NoContext] = { + ): BaseFunction = { createRawOp( op, BIGINT, @@ -220,24 +220,24 @@ object PureContext { } } - lazy val sumToBigInt: BaseFunction[NoContext] = bigIntArithmeticOp(SUM_OP, SUM_BIGINT, Map[StdLibVersion, Long](V5 -> 8L)) { _ + _ } - lazy val subToBigInt: BaseFunction[NoContext] = bigIntArithmeticOp(SUB_OP, SUB_BIGINT, Map[StdLibVersion, Long](V5 -> 8L)) { _ - _ } - lazy val mulToBigInt: BaseFunction[NoContext] = bigIntArithmeticOp(MUL_OP, MUL_BIGINT, Map[StdLibVersion, Long](V5 -> 64L)) { _ * _ } - lazy val divToBigInt: BaseFunction[NoContext] = bigIntArithmeticOp(DIV_OP, DIV_BIGINT, Map[StdLibVersion, Long](V5 -> 64L)) { _ / _ } - lazy val modToBigInt: BaseFunction[NoContext] = bigIntArithmeticOp(MOD_OP, MOD_BIGINT, Map[StdLibVersion, Long](V5 -> 64L)) { _ % _ } + lazy val sumToBigInt: BaseFunction = bigIntArithmeticOp(SUM_OP, SUM_BIGINT, Map[StdLibVersion, Long](V5 -> 8L)) { _ + _ } + lazy val subToBigInt: BaseFunction = bigIntArithmeticOp(SUB_OP, SUB_BIGINT, Map[StdLibVersion, Long](V5 -> 8L)) { _ - _ } + lazy val mulToBigInt: BaseFunction = bigIntArithmeticOp(MUL_OP, MUL_BIGINT, Map[StdLibVersion, Long](V5 -> 64L)) { _ * _ } + lazy val divToBigInt: BaseFunction = bigIntArithmeticOp(DIV_OP, DIV_BIGINT, Map[StdLibVersion, Long](V5 -> 64L)) { _ / _ } + lazy val modToBigInt: BaseFunction = bigIntArithmeticOp(MOD_OP, MOD_BIGINT, Map[StdLibVersion, Long](V5 -> 64L)) { _ % _ } - lazy val negativeBigInt: BaseFunction[NoContext] = + lazy val negativeBigInt: BaseFunction = NativeFunction("-", 8, UMINUS_BIGINT, BIGINT, ("n", BIGINT)) { case CONST_BIGINT(n) :: Nil => Either.cond(n != BigIntMin, CONST_BIGINT(-n), s"Positive BigInt overflow") case xs => notImplemented[Id, EVALUATED]("-(n: BigInt)", xs) } - lazy val throwWithMessage: BaseFunction[NoContext] = NativeFunction("throw", 1, THROW, NOTHING, ("err", STRING)) { + lazy val throwWithMessage: BaseFunction = NativeFunction("throw", 1, THROW, NOTHING, ("err", STRING)) { case CONST_STRING(s) :: Nil => Left(s) case _ => Left(defaultThrowMessage) } - lazy val throwNoMessage: BaseFunction[NoContext] = UserFunction( + lazy val throwNoMessage: BaseFunction = UserFunction( "throw", Map[StdLibVersion, Long](V1 -> 2, V2 -> 2, V3 -> 1, V4 -> 1), NOTHING @@ -245,7 +245,7 @@ object PureContext { FUNCTION_CALL(throwWithMessage, List(CONST_STRING(defaultThrowMessage).explicitGet())) } - lazy val extract: BaseFunction[NoContext] = + lazy val extract: BaseFunction = UserFunction.deprecated( "extract", 13, @@ -259,16 +259,16 @@ object PureContext { ) } - lazy val value: BaseFunction[NoContext] = - UserFunction.withEnvironment[NoContext]( + lazy val value: BaseFunction = + UserFunction.withEnvironment( "value", "value", Map[StdLibVersion, Long](V1 -> 13, V2 -> 13, V3 -> 13, V4 -> 2), TYPEPARAM('T'), ("@a", PARAMETERIZEDUNION(List(TYPEPARAM('T'), UNIT)): TYPE) ) { - new ContextfulUserFunction[NoContext] { - override def apply[F[_]: Monad](env: NoContext[F], startArgs: List[EXPR]): EXPR = { + new ContextfulUserFunction { + override def apply[F[_]: Monad](env: Environment[F], startArgs: List[EXPR]): EXPR = { val ctx = getDecompilerContext(DirectiveDictionary[StdLibVersion].all.last, Expression) val base = "value() called on unit value" def call(h: FunctionHeader, postfix: Boolean = true) = { @@ -301,7 +301,7 @@ object PureContext { } } - lazy val valueOrElse: BaseFunction[NoContext] = + lazy val valueOrElse: BaseFunction = UserFunction( "valueOrElse", 2, @@ -316,7 +316,7 @@ object PureContext { ) } - lazy val valueOrErrorMessage: BaseFunction[NoContext] = + lazy val valueOrErrorMessage: BaseFunction = UserFunction( "valueOrErrorMessage", Map[StdLibVersion, Long](V1 -> 13, V2 -> 13, V3 -> 13, V4 -> 2), @@ -331,7 +331,7 @@ object PureContext { ) } - lazy val isDefined: BaseFunction[NoContext] = + lazy val isDefined: BaseFunction = UserFunction( "isDefined", Map[StdLibVersion, Long](V1 -> 35, V2 -> 35, V3 -> 1, V4 -> 1), @@ -341,7 +341,7 @@ object PureContext { FUNCTION_CALL(ne, List(REF("@a"), REF(GlobalValNames.Unit))) } - def fraction(fixLimitCheck: Boolean): BaseFunction[NoContext] = + def fraction(fixLimitCheck: Boolean): BaseFunction = NativeFunction( "fraction", Map[StdLibVersion, Long](V1 -> 1, V2 -> 1, V3 -> 1, V4 -> 1, V5 -> 14, V6 -> 1), @@ -367,7 +367,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("fraction(value: Int, numerator: Int, denominator: Int)", xs) } - def fractionIntRounds(roundTypes: UNION): BaseFunction[NoContext] = + def fractionIntRounds(roundTypes: UNION): BaseFunction = UserFunction( "fraction", Map(V5 -> 17L, V6 -> 4L), @@ -389,7 +389,7 @@ object PureContext { FUNCTION_CALL(Native(BIGINT_TO_INT), List(r)) } - val fractionIntRoundsNative: BaseFunction[NoContext] = + val fractionIntRoundsNative: BaseFunction = NativeFunction( "fraction", 1L, @@ -414,7 +414,7 @@ object PureContext { ) } - val fractionBigInt: BaseFunction[NoContext] = + val fractionBigInt: BaseFunction = NativeFunction( "fraction", Map(V5 -> 128L, V6 -> 1L), @@ -435,7 +435,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("fraction(value: BigInt, numerator: BigInt, denominator: BigInt)", xs) } - def fractionBigIntRounds(roundTypes: UNION): BaseFunction[NoContext] = + def fractionBigIntRounds(roundTypes: UNION): BaseFunction = NativeFunction( "fraction", Map(V5 -> 128L, V6 -> 1L), @@ -461,7 +461,7 @@ object PureContext { ) } - lazy val _isInstanceOf: BaseFunction[NoContext] = + lazy val _isInstanceOf: BaseFunction = NativeFunction(compiler.IsInstanceOf, 1, ISINSTANCEOF, BOOLEAN, ("obj", TYPEPARAM('T')), ("of", STRING)) { case (value: EVALUATED) :: CONST_STRING(expectedType) :: Nil => Right(CONST_BOOLEAN(value.getType.name == expectedType)) @@ -469,7 +469,7 @@ object PureContext { Right(FALSE) } - lazy val _getType: BaseFunction[NoContext] = + lazy val _getType: BaseFunction = NativeFunction(compiler.GetType, 1, GET_TYPE, BOOLEAN, ("obj", TYPEPARAM('T'))) { case (value: EVALUATED) :: Nil => CONST_STRING(value.getType.name) @@ -477,24 +477,24 @@ object PureContext { notImplemented[Id, EVALUATED]("_getType(obj: T)", xs) } - lazy val sizeBytes: BaseFunction[NoContext] = NativeFunction("size", 1, SIZE_BYTES, LONG, ("byteVector", BYTESTR)) { + lazy val sizeBytes: BaseFunction = NativeFunction("size", 1, SIZE_BYTES, LONG, ("byteVector", BYTESTR)) { case CONST_BYTESTR(bv) :: Nil => Right(CONST_LONG(bv.arr.length)) case xs => notImplemented[Id, EVALUATED]("size(byteVector: ByteVector)", xs) } - lazy val toBytesBoolean: BaseFunction[NoContext] = + lazy val toBytesBoolean: BaseFunction = NativeFunction("toBytes", 1, BOOLEAN_TO_BYTES, BYTESTR, ("b", BOOLEAN)) { case TRUE :: Nil => CONST_BYTESTR(ByteStr.fromBytes(1)) case FALSE :: Nil => CONST_BYTESTR(ByteStr.fromBytes(0)) case xs => notImplemented[Id, EVALUATED]("toBytes(b: Boolean)", xs) } - lazy val toBytesLong: BaseFunction[NoContext] = NativeFunction("toBytes", 1, LONG_TO_BYTES, BYTESTR, ("n", LONG)) { + lazy val toBytesLong: BaseFunction = NativeFunction("toBytes", 1, LONG_TO_BYTES, BYTESTR, ("n", LONG)) { case CONST_LONG(n) :: Nil => CONST_BYTESTR(ByteStr.fromLong(n)) case xs => notImplemented[Id, EVALUATED]("toBytes(u: Int)", xs) } - lazy val toBytesString: BaseFunction[NoContext] = + lazy val toBytesString: BaseFunction = NativeFunction( "toBytes", Map[StdLibVersion, Long](V1 -> 1L, V2 -> 1L, V3 -> 1L, V4 -> 8L), @@ -506,29 +506,29 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("toBytes(s: String)", xs) } - lazy val sizeString: BaseFunction[NoContext] = NativeFunction("size", 1, SIZE_STRING, LONG, ("xs", STRING)) { + lazy val sizeString: BaseFunction = NativeFunction("size", 1, SIZE_STRING, LONG, ("xs", STRING)) { case CONST_STRING(bv) :: Nil => Right(CONST_LONG(bv.length.toLong)) case xs => notImplemented[Id, EVALUATED]("size(xs: String)", xs) } - lazy val sizeStringFixed: BaseFunction[NoContext] = NativeFunction("size", 1, SIZE_STRING, LONG, ("xs", STRING)) { + lazy val sizeStringFixed: BaseFunction = NativeFunction("size", 1, SIZE_STRING, LONG, ("xs", STRING)) { case CONST_STRING(bv) :: Nil => Right(CONST_LONG(bv.codePointCount(0, bv.length).toLong)) case xs => notImplemented[Id, EVALUATED]("size(xs: String)", xs) } - lazy val toStringBoolean: BaseFunction[NoContext] = + lazy val toStringBoolean: BaseFunction = NativeFunction("toString", 1, BOOLEAN_TO_STRING, STRING, ("b", BOOLEAN)) { case TRUE :: Nil => CONST_STRING("true") case FALSE :: Nil => CONST_STRING("false") case xs => notImplemented[Id, EVALUATED]("toString(b: Boolean)", xs) } - lazy val toStringLong: BaseFunction[NoContext] = NativeFunction("toString", 1, LONG_TO_STRING, STRING, ("n", LONG)) { + lazy val toStringLong: BaseFunction = NativeFunction("toString", 1, LONG_TO_STRING, STRING, ("n", LONG)) { case CONST_LONG(n) :: Nil => CONST_STRING(n.toString) case xs => notImplemented[Id, EVALUATED]("toString(u: Int)", xs) } - private def takeBytes(checkLimits: Boolean): BaseFunction[NoContext] = + private def takeBytes(checkLimits: Boolean): BaseFunction = NativeFunction( "take", Map[StdLibVersion, Long](V1 -> 1L, V2 -> 1L, V3 -> 1L, V4 -> 6L), @@ -552,7 +552,7 @@ object PureContext { notImplemented[Id, EVALUATED]("take(xs: ByteVector, number: Int)", xs) } - private def dropBytes(checkLimits: Boolean): BaseFunction[NoContext] = + private def dropBytes(checkLimits: Boolean): BaseFunction = NativeFunction( "drop", Map[StdLibVersion, Long](V1 -> 1L, V2 -> 1L, V3 -> 1L, V4 -> 6L), @@ -581,7 +581,7 @@ object PureContext { private val takeBytesBeforeV6 = takeBytes(checkLimits = false) private val takeBytesFromV6 = takeBytes(checkLimits = true) - private val dropRightBytesBeforeV6: BaseFunction[NoContext] = + private val dropRightBytesBeforeV6: BaseFunction = UserFunction( "dropRight", "dropRightBytes", @@ -605,7 +605,7 @@ object PureContext { ) } - private val takeRightBytesBeforeV6: BaseFunction[NoContext] = + private val takeRightBytesBeforeV6: BaseFunction = UserFunction( "takeRight", "takeRightBytes", @@ -629,7 +629,7 @@ object PureContext { ) } - private val takeRightBytesFromV6: BaseFunction[NoContext] = + private val takeRightBytesFromV6: BaseFunction = NativeFunction( "takeRight", 6, @@ -650,7 +650,7 @@ object PureContext { notImplemented[Id, EVALUATED]("takeRight(xs: ByteVector, number: Int)", xs) } - private val dropRightBytesFromV6: BaseFunction[NoContext] = + private val dropRightBytesFromV6: BaseFunction = NativeFunction( "dropRight", 6, @@ -671,7 +671,7 @@ object PureContext { notImplemented[Id, EVALUATED]("dropRight(xs: ByteVector, number: Int)", xs) } - private val takeStringBeforeV6: BaseFunction[NoContext] = + private val takeStringBeforeV6: BaseFunction = NativeFunction( "take", Map[StdLibVersion, Long](V1 -> 1L, V2 -> 1L, V3 -> 1L, V4 -> 20L), @@ -684,7 +684,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("take(xs: String, number: Int)", xs) } - private def takeStringFixed(checkLimits: Boolean): BaseFunction[NoContext] = + private def takeStringFixed(checkLimits: Boolean): BaseFunction = NativeFunction( "take", Map[StdLibVersion, Long](V1 -> 1L, V2 -> 1L, V3 -> 1L, V4 -> 20L, V5 -> 20L), @@ -715,7 +715,7 @@ object PureContext { private val takeStringFixedBeforeV6 = takeStringFixed(checkLimits = false) private val takeStringFixedFromV6 = takeStringFixed(checkLimits = true) - def listConstructor(checkSize: Boolean): NativeFunction[NoContext] = + def listConstructor(checkSize: Boolean): NativeFunction = NativeFunction( "cons", Map[StdLibVersion, Long](V1 -> 2L, V2 -> 2L, V3 -> 2L, V4 -> 1L), @@ -728,7 +728,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("cons(head: T, tail: LIST[T]", xs) } - lazy val listAppend: NativeFunction[NoContext] = + lazy val listAppend: NativeFunction = NativeFunction( LIST_APPEND_OP.func, 1, @@ -741,7 +741,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED](s"list: List[T] ${LIST_APPEND_OP.func} value: T", xs) } - lazy val listConcat: NativeFunction[NoContext] = + lazy val listConcat: NativeFunction = NativeFunction( LIST_CONCAT_OP.func, 4, @@ -754,7 +754,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED](s"list1: List[T] ${LIST_CONCAT_OP.func} list2: List[T]", xs) } - private val dropStringBeforeV6: BaseFunction[NoContext] = + private val dropStringBeforeV6: BaseFunction = NativeFunction( "drop", Map[StdLibVersion, Long](V1 -> 1L, V2 -> 1L, V3 -> 1L, V4 -> 20L), @@ -767,7 +767,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("drop(xs: String, number: Int)", xs) } - private def dropStringFixed(checkLimits: Boolean): BaseFunction[NoContext] = + private def dropStringFixed(checkLimits: Boolean): BaseFunction = NativeFunction( "drop", Map[StdLibVersion, Long](V1 -> 1L, V2 -> 1L, V3 -> 1L, V4 -> 20L, V5 -> 20L), @@ -798,7 +798,7 @@ object PureContext { private val dropStringFixedBeforeV6 = dropStringFixed(checkLimits = false) private val dropStringFixedFromV6 = dropStringFixed(checkLimits = true) - private val takeRightStringBeforeV6: BaseFunction[NoContext] = + private val takeRightStringBeforeV6: BaseFunction = UserFunction( "takeRight", Map[StdLibVersion, Long](V1 -> 19L, V2 -> 19L, V3 -> 19L, V4 -> 20L), @@ -821,7 +821,7 @@ object PureContext { ) } - private val takeRightStringFixedBeforeV6: BaseFunction[NoContext] = + private val takeRightStringFixedBeforeV6: BaseFunction = UserFunction( "takeRight", Map[StdLibVersion, Long](V1 -> 19L, V2 -> 19L, V3 -> 19L, V4 -> 20L, V5 -> 20L), @@ -844,7 +844,7 @@ object PureContext { ) } - private val takeRightStringFromV6: BaseFunction[NoContext] = + private val takeRightStringFromV6: BaseFunction = NativeFunction( "takeRight", 20L, @@ -867,7 +867,7 @@ object PureContext { notImplemented[Id, EVALUATED]("takeRight(xs: String, number: Int)", xs) } - private val dropRightStringBeforeV6: BaseFunction[NoContext] = + private val dropRightStringBeforeV6: BaseFunction = UserFunction( "dropRight", Map[StdLibVersion, Long](V1 -> 19L, V2 -> 19L, V3 -> 19L, V4 -> 20L), @@ -890,7 +890,7 @@ object PureContext { ) } - private val dropRightStringFixedBeforeV6: BaseFunction[NoContext] = + private val dropRightStringFixedBeforeV6: BaseFunction = UserFunction( "dropRight", Map[StdLibVersion, Long](V1 -> 19L, V2 -> 19L, V3 -> 19L, V4 -> 20L, V5 -> 20L), @@ -913,7 +913,7 @@ object PureContext { ) } - private val dropRightStringFromV6: BaseFunction[NoContext] = + private val dropRightStringFromV6: BaseFunction = NativeFunction( "dropRight", 20L, @@ -938,7 +938,7 @@ object PureContext { val UTF8Decoder = UTF_8.newDecoder - def toUtf8String(reduceLimit: Boolean): BaseFunction[NoContext] = + def toUtf8String(reduceLimit: Boolean): BaseFunction = NativeFunction( "toUtf8String", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 7L), @@ -959,7 +959,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("toUtf8String(u: ByteVector)", xs) } - lazy val toLong: BaseFunction[NoContext] = + lazy val toLong: BaseFunction = NativeFunction("toInt", Map[StdLibVersion, Long](V1 -> 10L, V2 -> 10L, V3 -> 10L, V4 -> 1L), BININT, LONG, ("bin", BYTESTR)) { case CONST_BYTESTR(u) :: Nil => Try(CONST_LONG(ByteBuffer.wrap(u.arr).getLong())).toEither.left.map { @@ -969,7 +969,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("toInt(u: ByteVector)", xs) } - lazy val toLongOffset: BaseFunction[NoContext] = + lazy val toLongOffset: BaseFunction = NativeFunction( "toInt", Map[StdLibVersion, Long](V1 -> 10L, V2 -> 10L, V3 -> 10L, V4 -> 1L), @@ -990,7 +990,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("toInt(u: ByteVector, off: Int)", xs) } - lazy val indexOf: BaseFunction[NoContext] = + lazy val indexOf: BaseFunction = NativeFunction( "indexOf", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 3L), @@ -1011,7 +1011,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("indexOf(str: String, substr: String)", xs) } - lazy val indexOfFixed: BaseFunction[NoContext] = + lazy val indexOfFixed: BaseFunction = NativeFunction( "indexOf", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 3L, V5 -> 3L), @@ -1031,7 +1031,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("indexOf(str: String, substr: String)", xs) } - lazy val indexOfN: BaseFunction[NoContext] = + lazy val indexOfN: BaseFunction = NativeFunction( "indexOf", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 3L), @@ -1055,7 +1055,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("indexOf(str: String, substr: String, offset: Int)", xs) } - lazy val indexOfNFixed: BaseFunction[NoContext] = + lazy val indexOfNFixed: BaseFunction = NativeFunction( "indexOf", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 3L, V5 -> 3L), @@ -1080,7 +1080,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("indexOf(str: String, substr: String, offset: Int)", xs) } - lazy val lastIndexOf: BaseFunction[NoContext] = + lazy val lastIndexOf: BaseFunction = NativeFunction( "lastIndexOf", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 3L), @@ -1101,7 +1101,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("lastIndexOf(str: String, substr: String)", xs) } - lazy val lastIndexOfFixed: BaseFunction[NoContext] = + lazy val lastIndexOfFixed: BaseFunction = NativeFunction( "lastIndexOf", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 3L, V5 -> 3L), @@ -1122,7 +1122,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("lastIndexOf(str: String, substr: String)", xs) } - lazy val lastIndexOfWithOffset: BaseFunction[NoContext] = + lazy val lastIndexOfWithOffset: BaseFunction = NativeFunction( "lastIndexOf", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 3L), @@ -1147,7 +1147,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("lastIndexOf(str: String, substr: String, offset: Int)", xs) } - lazy val lastIndexOfWithOffsetFixed: BaseFunction[NoContext] = + lazy val lastIndexOfWithOffsetFixed: BaseFunction = NativeFunction( "lastIndexOf", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 3L, V5 -> 3L), @@ -1172,7 +1172,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("lastIndexOf(str: String, substr: String, offset: Int)", xs) } - lazy val splitStr: BaseFunction[NoContext] = + lazy val splitStr: BaseFunction = NativeFunction("split", Map(V3 -> 100L, V4 -> 75L, V5 -> 75L, V6 -> 51L), SPLIT, listString, ("str", STRING), ("separator", STRING)) { case CONST_STRING(str) :: CONST_STRING(sep) :: Nil => ARR(split(str, sep, unicode = false).toIndexedSeq, limited = true) @@ -1180,7 +1180,7 @@ object PureContext { notImplemented[Id, EVALUATED]("split(str: String, separator: String)", xs) } - def splitStrFixedF(id: Short, inputLimit: Int, outputLimit: Int, v6Complexity: Long): BaseFunction[NoContext] = { + def splitStrFixedF(id: Short, inputLimit: Int, outputLimit: Int, v6Complexity: Long): BaseFunction = { val name = if (id == SPLIT) "split" else s"split_${v6Complexity}C" NativeFunction(name, Map(V3 -> 100L, V4 -> 75L, V5 -> 75L, V6 -> v6Complexity), id, listString, ("str", STRING), ("separator", STRING)) { case (s @ CONST_STRING(str)) :: CONST_STRING(sep) :: Nil => @@ -1236,7 +1236,7 @@ object PureContext { ) } - def makeStringF(id: Short, complexityV6: Long, inputLimit: Int, outputLimit: Int, rejectNonStrings: Boolean): BaseFunction[NoContext] = { + def makeStringF(id: Short, complexityV6: Long, inputLimit: Int, outputLimit: Int, rejectNonStrings: Boolean): BaseFunction = { val name = if (id == MAKESTRING) "makeString" else s"makeString_${complexityV6}C" NativeFunction(name, Map(V4 -> 30L, V5 -> 30L, V6 -> complexityV6), id, STRING, ("list", LIST(STRING)), ("separator", STRING)) { case (arr: ARR) :: CONST_STRING(separator) :: Nil => @@ -1250,7 +1250,7 @@ object PureContext { if (rejectNonStrings && arr.xs.exists(!_.isInstanceOf[CONST_STRING])) Left("makeString only accepts strings") else if (expectedStringSize > outputLimit) - Left(s"Constructing string size = $expectedStringSize bytes will exceed $outputLimit") + Left(s"Constructing string size = $expectedStringSize bytes will exceed $outputLimit for ${arr.xs.mkString("[",",","]")} and sep = $separator") else CONST_STRING(arr.xs.mkString(separator)) } @@ -1259,12 +1259,12 @@ object PureContext { } } - val makeString: BaseFunction[NoContext] = makeStringF(MAKESTRING, 11, MaxListLengthV4, DataEntryValueMax, rejectNonStrings = false) - val makeString_V6: BaseFunction[NoContext] = makeStringF(MAKESTRING, 1, 70, 500, rejectNonStrings = true) - val makeString_V6_2C: BaseFunction[NoContext] = makeStringF(MAKESTRING2C, 2, 100, 6000, rejectNonStrings = true) - val makeString_V6_11C: BaseFunction[NoContext] = makeStringF(MAKESTRING11C, 11, MaxListLengthV4, DataEntryValueMax, rejectNonStrings = true) + val makeString: BaseFunction = makeStringF(MAKESTRING, 11, MaxListLengthV4, DataEntryValueMax, rejectNonStrings = false) + val makeString_V6: BaseFunction = makeStringF(MAKESTRING, 1, 70, 500, rejectNonStrings = true) + val makeString_V6_2C: BaseFunction = makeStringF(MAKESTRING2C, 2, 100, 6000, rejectNonStrings = true) + val makeString_V6_11C: BaseFunction = makeStringF(MAKESTRING11C, 11, MaxListLengthV4, DataEntryValueMax, rejectNonStrings = true) - lazy val contains: BaseFunction[NoContext] = + lazy val contains: BaseFunction = UserFunction( "contains", 3, @@ -1283,13 +1283,13 @@ object PureContext { ) } - lazy val parseInt: BaseFunction[NoContext] = + lazy val parseInt: BaseFunction = NativeFunction("parseInt", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 2L), PARSEINT, optionLong, ("str", STRING)) { case CONST_STRING(u) :: Nil => Try(CONST_LONG(u.toLong)).orElse(Success(unit)).toEither.left.map(_.toString) case xs => notImplemented[Id, EVALUATED]("parseInt(str: String)", xs) } - lazy val parseIntVal: BaseFunction[NoContext] = + lazy val parseIntVal: BaseFunction = UserFunction( "parseIntValue", Map[StdLibVersion, Long](V1 -> 20L, V2 -> 20L, V3 -> 20L, V4 -> 2L), @@ -1305,7 +1305,7 @@ object PureContext { def createRawOp(op: BinaryOperation, t: TYPE, r: TYPE, func: Short, complexity: Int = 1)( body: (EVALUATED, EVALUATED) => Either[ExecutionError, EVALUATED] - ): BaseFunction[NoContext] = + ): BaseFunction = NativeFunction(opsToFunctions(op), complexity, func, r, ("a", t), ("b", t)) { case a :: b :: Nil => body(a, b) case xs => notImplemented[Id, EVALUATED](s"${opsToFunctions(op)}(a: ${t.toString}, b: ${t.toString})", xs) @@ -1313,7 +1313,7 @@ object PureContext { def createRawOp(op: BinaryOperation, t: TYPE, r: TYPE, func: Short, complexity: Map[StdLibVersion, Long])( body: (EVALUATED, EVALUATED) => Either[ExecutionError, EVALUATED] - ): BaseFunction[NoContext] = + ): BaseFunction = NativeFunction(opsToFunctions(op), complexity, func, r, ("a", t), ("b", t)) { case a :: b :: Nil => body(a, b) case xs => notImplemented[Id, EVALUATED](s"${opsToFunctions(op)}(a: ${t.toString}, b: ${t.toString})", xs) @@ -1321,7 +1321,7 @@ object PureContext { def createOp(op: BinaryOperation, t: TYPE, r: TYPE, func: Short, complexity: Int = 1)( body: (Long, Long) => Boolean - ): BaseFunction[NoContext] = + ): BaseFunction = NativeFunction(opsToFunctions(op), complexity, func, r, ("a", t), ("b", t)) { case CONST_LONG(a) :: CONST_LONG(b) :: Nil => Right(CONST_BOOLEAN(body(a, b))) case xs => notImplemented[Id, EVALUATED](s"${opsToFunctions(op)}(a: ${t.toString}, b: ${t.toString})", xs) @@ -1329,7 +1329,7 @@ object PureContext { def createTryOp(op: BinaryOperation, t: TYPE, r: TYPE, func: Short, complicity: Int = 1)( body: (Long, Long) => Long - ): BaseFunction[NoContext] = + ): BaseFunction = NativeFunction(opsToFunctions(op), complicity, func, r, ("a", t), ("b", t)) { case CONST_LONG(a) :: CONST_LONG(b) :: Nil => try { @@ -1342,13 +1342,13 @@ object PureContext { def bigIntConditionOp(op: BinaryOperation, func: Short, complexity: Int = 8)( body: (BigInt, BigInt) => Boolean - ): BaseFunction[NoContext] = + ): BaseFunction = NativeFunction(opsToFunctions(op), complexity, func, BOOLEAN, ("a", BIGINT), ("b", BIGINT)) { case CONST_BIGINT(a) :: CONST_BIGINT(b) :: Nil => Try(body(a, b)).toEither.bimap(_.getMessage, CONST_BOOLEAN) case xs => notImplemented[Id, EVALUATED](s"${opsToFunctions(op)}(a: BIGINT, b: BIGINT)", xs) } - lazy val getElement: BaseFunction[NoContext] = + lazy val getElement: BaseFunction = NativeFunction( "getElement", 2, @@ -1365,13 +1365,13 @@ object PureContext { case xs => notImplemented[Id, EVALUATED](s"getElement(arr: Array, pos: Int)", xs) } - lazy val getListSize: BaseFunction[NoContext] = + lazy val getListSize: BaseFunction = NativeFunction("size", 2, SIZE_LIST, LONG, ("arr", PARAMETERIZEDLIST(TYPEPARAM('T')))) { case ARR(arr) :: Nil => Right(CONST_LONG(arr.size.toLong)) case xs => notImplemented[Id, EVALUATED](s"size(arr: Array)", xs) } - lazy val listMax: BaseFunction[NoContext] = + lazy val listMax: BaseFunction = NativeFunction("max", 3, MAX_LIST, LONG, ("list", PARAMETERIZEDLIST(LONG))) { case ARR(list) :: Nil => Either.cond( @@ -1383,7 +1383,7 @@ object PureContext { notImplemented[Id, EVALUATED]("max(list: List[Int])", xs) } - lazy val listMin: BaseFunction[NoContext] = + lazy val listMin: BaseFunction = NativeFunction("min", 3, MIN_LIST, LONG, ("list", PARAMETERIZEDLIST(LONG))) { case ARR(list) :: Nil => Either.cond( @@ -1395,7 +1395,7 @@ object PureContext { notImplemented[Id, EVALUATED]("min(list: List[Int])", xs) } - lazy val listBigIntMax: BaseFunction[NoContext] = + lazy val listBigIntMax: BaseFunction = NativeFunction("max", 192, MAX_LIST_BIGINT, BIGINT, ("list", PARAMETERIZEDLIST(BIGINT))) { case ARR(list) :: Nil => Either.cond( @@ -1407,7 +1407,7 @@ object PureContext { notImplemented[Id, EVALUATED]("max(list: List[BigInt])", xs) } - lazy val listBigIntMin: BaseFunction[NoContext] = + lazy val listBigIntMin: BaseFunction = NativeFunction("min", 192, MIN_LIST_BIGINT, BIGINT, ("list", PARAMETERIZEDLIST(BIGINT))) { case ARR(list) :: Nil => Either.cond( @@ -1419,7 +1419,7 @@ object PureContext { notImplemented[Id, EVALUATED]("min(list: List[BigInt])", xs) } - lazy val listIndexOf: BaseFunction[NoContext] = + lazy val listIndexOf: BaseFunction = NativeFunction( "indexOf", 5, @@ -1434,7 +1434,7 @@ object PureContext { notImplemented[Id, EVALUATED]("indexOf(list: List[T], element: T)", xs) } - lazy val listLastIndexOf: BaseFunction[NoContext] = + lazy val listLastIndexOf: BaseFunction = NativeFunction( "lastIndexOf", 5, @@ -1449,7 +1449,7 @@ object PureContext { notImplemented[Id, EVALUATED]("lastIndexOf(list: List[T], element: T)", xs) } - lazy val listRemoveByIndex: BaseFunction[NoContext] = + lazy val listRemoveByIndex: BaseFunction = NativeFunction( "removeByIndex", 7, @@ -1499,7 +1499,7 @@ object PureContext { index => if (index != -1) CONST_LONG(index.toLong) else unit ) - lazy val listContains: BaseFunction[NoContext] = + lazy val listContains: BaseFunction = UserFunction( "containsElement", 5, @@ -1511,7 +1511,7 @@ object PureContext { FUNCTION_CALL(User("!="), List(index, unit)) } - def createTupleN(resultSize: Int): NativeFunction[NoContext] = { + def createTupleN(resultSize: Int): NativeFunction = { val typeParams = ('A'.toInt until 'A'.toInt + resultSize).map(t => TYPEPARAM(t.toByte)).toList @@ -1530,17 +1530,17 @@ object PureContext { } } - lazy val uMinus: BaseFunction[NoContext] = + lazy val uMinus: BaseFunction = UserFunction("-", Map[StdLibVersion, Long](V1 -> 9, V2 -> 9, V3 -> 1, V4 -> 1), LONG, ("@n", LONG)) { FUNCTION_CALL(subLong, List(CONST_LONG(0), REF("@n"))) } - lazy val uNot: BaseFunction[NoContext] = + lazy val uNot: BaseFunction = UserFunction("!", Map[StdLibVersion, Long](V1 -> 11, V2 -> 11, V3 -> 1, V4 -> 1), BOOLEAN, ("@p", BOOLEAN)) { IF(REF("@p"), FALSE, TRUE) } - def pow(roundTypes: UNION, useNewPrecision: Boolean): BaseFunction[NoContext] = { + def pow(roundTypes: UNION, useNewPrecision: Boolean): BaseFunction = { NativeFunction( "pow", Map(V3 -> 100L, V4 -> 100L, V5 -> 100L, V6 -> 28L), @@ -1570,7 +1570,7 @@ object PureContext { } } - val sqrtInt: BaseFunction[NoContext] = + val sqrtInt: BaseFunction = UserFunction("sqrt", 2, LONG, ("@number", LONG), ("@precision", LONG), ("@resultPrecision", LONG), ("@round", UNION(fromV5RoundTypes))) { FUNCTION_CALL( Native(POW), @@ -1585,7 +1585,7 @@ object PureContext { ) } - def log(roundTypes: UNION): BaseFunction[NoContext] = { + def log(roundTypes: UNION): BaseFunction = { NativeFunction("log", 100, LOG, LONG, ("base", LONG), ("bp", LONG), ("exponent", LONG), ("ep", LONG), ("rp", LONG), ("round", roundTypes)) { case CONST_LONG(b) :: CONST_LONG(bp) :: CONST_LONG(e) :: CONST_LONG(ep) :: CONST_LONG(rp) :: round :: Nil => if ( @@ -1604,7 +1604,7 @@ object PureContext { } } - def powBigInt(roundTypes: UNION, useNewPrecision: Boolean): BaseFunction[NoContext] = + def powBigInt(roundTypes: UNION, useNewPrecision: Boolean): BaseFunction = NativeFunction( "pow", Map(V5 -> 200L, V6 -> 270L), @@ -1636,7 +1636,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("pow(base: BigInt, bp: Int, exponent:Big Int, ep: Int, rp: Int, round: Rounds)", xs) } - val sqrtBigInt: BaseFunction[NoContext] = + val sqrtBigInt: BaseFunction = UserFunction( "sqrt", "sqrtBigInt", @@ -1660,7 +1660,7 @@ object PureContext { ) } - def logBigInt(roundTypes: UNION): BaseFunction[NoContext] = + def logBigInt(roundTypes: UNION): BaseFunction = NativeFunction( "log", 200, @@ -1691,7 +1691,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("log(exponent: BigInt, ep: Int, base:Big Int, bp: Int, rp: Int, round: Rounds)", xs) } - val getListMedian: BaseFunction[NoContext] = + val getListMedian: BaseFunction = NativeFunction("median", 20, MEDIAN_LIST, LONG, ("arr", PARAMETERIZEDLIST(LONG))) { case xs @ ARR(arr) :: Nil => if (arr.headOption.forall(_.isInstanceOf[CONST_LONG])) { @@ -1705,7 +1705,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED](s"median(arr: List[Int])", xs) } - val getBigIntListMedian: BaseFunction[NoContext] = + val getBigIntListMedian: BaseFunction = NativeFunction("median", 20 * 8, MEDIAN_LISTBIGINT, BIGINT, ("arr", PARAMETERIZEDLIST(BIGINT))) { case xs @ ARR(arr) :: Nil => if (arr.headOption.forall(_.isInstanceOf[CONST_BIGINT])) { @@ -1719,7 +1719,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED](s"median(arr: List[BigInt])", xs) } - val sizeTuple: BaseFunction[NoContext] = { + val sizeTuple: BaseFunction = { val genericTupleType = (MinTupleSize to MaxTupleSize) .map(('A' to 'Z').take) @@ -1731,20 +1731,20 @@ object PureContext { } } - private val nil: (String, (LIST, ContextfulVal[NoContext])) = + private val nil: (String, (LIST, ContextfulVal)) = ( GlobalValNames.Nil, - (LIST(NOTHING), ContextfulVal.pure[NoContext](ARR(IndexedSeq.empty[EVALUATED], EMPTYARR_WEIGHT, limited = false).explicitGet())) + (LIST(NOTHING), ContextfulVal.pure(ARR(IndexedSeq.empty[EVALUATED], EMPTYARR_WEIGHT, limited = false).explicitGet())) ) - private val commonVars: Map[String, (FINAL, ContextfulVal[NoContext])] = + private val commonVars: Map[String, (FINAL, ContextfulVal)] = Map( (GlobalValNames.Unit, (UNIT, ContextfulVal.pure(unit))) ) - private val v1V2Vars: Map[String, (FINAL, ContextfulVal[NoContext])] = commonVars ++ Rounding.all.map(_.definition) - private val v3V4Vars: Map[String, (FINAL, ContextfulVal[NoContext])] = v1V2Vars + nil - private val v5Vars: Map[String, (FINAL, ContextfulVal[NoContext])] = commonVars ++ Rounding.fromV5.map(_.definition) + nil + private val v1V2Vars: Map[String, (FINAL, ContextfulVal)] = commonVars ++ Rounding.all.map(_.definition) + private val v3V4Vars: Map[String, (FINAL, ContextfulVal)] = v1V2Vars + nil + val v5Vars: Map[String, (FINAL, ContextfulVal)] = commonVars ++ Rounding.fromV5.map(_.definition) + nil private val commonTypes: Seq[REAL] = Seq( @@ -1761,7 +1761,7 @@ object PureContext { private val v1v2v3v4Types: Seq[REAL] = commonTypes ++ allRoundTypes private val v5Types: Seq[REAL] = commonTypes ++ fromV5RoundTypes ++ Seq(BIGINT) - private val operators: Array[BaseFunction[NoContext]] = + private val operators: Array[BaseFunction] = Array( mulLong, divLong, @@ -1982,7 +1982,7 @@ object PureContext { makeString :+ splitStrFixed - private val v6Functions = + val v6Functions = fromV5Functions(true) ++ Array( sizeTuple, @@ -2007,41 +2007,41 @@ object PureContext { ) private def v1V2Ctx(fixUnicodeFunctions: Boolean) = - CTX[NoContext]( + CTX( v1v2v3v4Types, v1V2Vars, v1V2V3CommonFunctions(fixUnicodeFunctions) ) private def v3Ctx(useNewPowPrecision: Boolean) = - CTX[NoContext]( + CTX( v1v2v3v4Types, v3V4Vars, v3Functions(useNewPowPrecision) ) private def v4Ctx(useNewPowPrecision: Boolean) = - CTX[NoContext]( + CTX( v1v2v3v4Types, v3V4Vars, v4Functions(useNewPowPrecision) ) private def v5Ctx(useNewPowPrecision: Boolean) = - CTX[NoContext]( + CTX( v5Types, v5Vars, v5Functions(useNewPowPrecision) ) private[this] val v6Ctx = - CTX[NoContext]( + CTX( v5Types, v5Vars, v6Functions ) - def build(version: StdLibVersion, useNewPowPrecision: Boolean): CTX[NoContext] = + def build(version: StdLibVersion, useNewPowPrecision: Boolean): CTX = version match { case V1 | V2 => v1V2Ctx(useNewPowPrecision) case V3 => v3Ctx(useNewPowPrecision) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/Rounding.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/Rounding.scala index b26420d5608..74a06c2e1c4 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/Rounding.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/Rounding.scala @@ -1,16 +1,15 @@ package com.wavesplatform.lang.v1.evaluator.ctx.impl +import java.math.RoundingMode +import java.math.RoundingMode.* + import com.wavesplatform.lang.v1.compiler.Terms.{CaseObj, EVALUATED} import com.wavesplatform.lang.v1.compiler.Types.CASETYPEREF -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext import com.wavesplatform.lang.v1.evaluator.ContextfulVal -import java.math.RoundingMode -import java.math.RoundingMode._ - sealed abstract class Rounding(typeName: String, val mode: RoundingMode) { - val `type`: CASETYPEREF = CASETYPEREF(typeName, Nil, hideConstructor = true) - val value: CaseObj = CaseObj(`type`, Map()) - val definition: (String, (CASETYPEREF, ContextfulVal[NoContext])) = (typeName.toUpperCase, (`type`, ContextfulVal.pure(value))) + val `type`: CASETYPEREF = CASETYPEREF(typeName, Nil, hideConstructor = true) + val value: CaseObj = CaseObj(`type`, Map()) + val definition: (String, (CASETYPEREF, ContextfulVal)) = (typeName.toUpperCase, (`type`, ContextfulVal.pure(value))) } case object Rounding { diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/package.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/package.scala index 8668e5965b1..0341fd6b231 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/package.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/package.scala @@ -33,7 +33,7 @@ package object impl { val arrayDataByKeyHeaders: Set[FunctionHeader] = Set(DATA_LONG_FROM_ARRAY, DATA_BOOLEAN_FROM_ARRAY, DATA_BYTES_FROM_ARRAY, DATA_STRING_FROM_ARRAY) - .map(Native) + .map(c => Native(c)) val arrayDataByIndexHeaders: Set[FunctionHeader] = Set("getInteger", "getBoolean", "getBinary", "getString") diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/Functions.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/Functions.scala index fe40b3f515d..eae9da0a97b 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/Functions.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/Functions.scala @@ -16,86 +16,92 @@ import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Bindings.{scriptTransf import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Types.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.{EnvironmentFunctions, GlobalValNames, PureContext, notImplemented, unit} import com.wavesplatform.lang.v1.evaluator.ctx.{BaseFunction, NativeFunction, UserFunction} -import com.wavesplatform.lang.v1.evaluator.{ContextfulNativeFunction, ContextfulUserFunction, FunctionIds, Log} +import com.wavesplatform.lang.v1.evaluator.{ContextfulNativeFunction, ContextfulUserFunction, FunctionIds} import com.wavesplatform.lang.v1.traits.domain.{Issue, Lease, Recipient} import com.wavesplatform.lang.v1.traits.{DataType, Environment} import com.wavesplatform.lang.v1.{BaseGlobal, FunctionHeader} -import com.wavesplatform.lang.{CoevalF, CommonError, ExecutionError, FailOrRejectError} -import monix.eval.Coeval +import com.wavesplatform.lang.{CommonError, ExecutionError} import shapeless.Coproduct.unsafeGet object Functions { - private def getDataFromStateF(name: String, internalName: Short, dataType: DataType, selfCall: Boolean): BaseFunction[Environment] = { - val resultType = UNION(dataType.innerType, UNIT) + class GetDataFromStateF(name: String, dataType: DataType, selfCall: Boolean) + extends ContextfulNativeFunction.Simple( + name, + UNION(dataType.innerType, UNIT), + if (selfCall) + Seq(("key", STRING)) + else + Seq(("addressOrAlias", addressOrAliasType), ("key", STRING)) + ) { + private def getData[F[_]: Monad](env: Environment[F], addressOrAlias: CaseObj, key: String) = { + val environmentFunctions = new EnvironmentFunctions[F](env) + environmentFunctions + .getData(addressOrAlias, key, dataType) + .map(_.flatMap { + case None => Right(unit) + case Some(a) => + (a: @unchecked) match { + case b: ByteStr => CONST_BYTESTR(b) + case b: Long => Right(CONST_LONG(b)) + case b: String => CONST_STRING(b) + case b: Boolean => Right(CONST_BOOLEAN(b)) + } + }) + } + + override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = { + (unsafeGet(env.tthis), args) match { + case (address: Recipient.Address, CONST_STRING(key) :: Nil) if selfCall => + getData(env, Bindings.senderObject(address), key) + case (_, (addressOrAlias: CaseObj) :: CONST_STRING(key) :: Nil) => + getData(env, addressOrAlias, key) + case (_, xs) => + notImplemented[F, EVALUATED](s"$name(s: String)", xs) + } + } + } + + private def getDataFromStateF(name: String, internalName: Short, dataType: DataType, selfCall: Boolean): BaseFunction = { val args = if (selfCall) Seq(("key", STRING)) else Seq(("addressOrAlias", addressOrAliasType), ("key", STRING)) - NativeFunction.withEnvironment[Environment]( + NativeFunction.withEnvironment( name, Map[StdLibVersion, Long](V1 -> 100L, V2 -> 100L, V3 -> 100L, V4 -> 10L), internalName, UNION(dataType.innerType, UNIT), args* - ) { - def getData[F[_]: Monad](env: Environment[F], addressOrAlias: CaseObj, key: String) = { - val environmentFunctions = new EnvironmentFunctions[F](env) - environmentFunctions - .getData(addressOrAlias, key, dataType) - .map(_.flatMap { - case None => Right(unit) - case Some(a) => - (a: @unchecked) match { - case b: ByteStr => CONST_BYTESTR(b) - case b: Long => Right(CONST_LONG(b)) - case b: String => CONST_STRING(b) - case b: Boolean => Right(CONST_BOOLEAN(b)) - } - }) - } - - new ContextfulNativeFunction.Simple[Environment](name, resultType, args) { - override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = { - (unsafeGet(env.tthis), args) match { - case (address: Recipient.Address, CONST_STRING(key) :: Nil) if selfCall => - getData(env, Bindings.senderObject(address), key) - case (_, (addressOrAlias: CaseObj) :: CONST_STRING(key) :: Nil) => - getData(env, addressOrAlias, key) - case (_, xs) => - notImplemented[F, EVALUATED](s"$name(s: String)", xs) - } - } - } - } + )(new GetDataFromStateF(name, dataType, selfCall)) } - val getIntegerFromStateF: BaseFunction[Environment] = getDataFromStateF("getInteger", DATA_LONG_FROM_STATE, DataType.Long, selfCall = false) - val getBooleanFromStateF: BaseFunction[Environment] = getDataFromStateF("getBoolean", DATA_BOOLEAN_FROM_STATE, DataType.Boolean, selfCall = false) - val getBinaryFromStateF: BaseFunction[Environment] = getDataFromStateF("getBinary", DATA_BYTES_FROM_STATE, DataType.ByteArray, selfCall = false) - val getStringFromStateF: BaseFunction[Environment] = getDataFromStateF("getString", DATA_STRING_FROM_STATE, DataType.String, selfCall = false) + val getIntegerFromStateF: BaseFunction = getDataFromStateF("getInteger", DATA_LONG_FROM_STATE, DataType.Long, selfCall = false) + val getBooleanFromStateF: BaseFunction = getDataFromStateF("getBoolean", DATA_BOOLEAN_FROM_STATE, DataType.Boolean, selfCall = false) + val getBinaryFromStateF: BaseFunction = getDataFromStateF("getBinary", DATA_BYTES_FROM_STATE, DataType.ByteArray, selfCall = false) + val getStringFromStateF: BaseFunction = getDataFromStateF("getString", DATA_STRING_FROM_STATE, DataType.String, selfCall = false) - val getIntegerFromStateSelfF: BaseFunction[Environment] = getDataFromStateF("getInteger", DATA_LONG_FROM_STATE_SELF, DataType.Long, selfCall = true) - val getBooleanFromStateSelfF: BaseFunction[Environment] = + val getIntegerFromStateSelfF: BaseFunction = getDataFromStateF("getInteger", DATA_LONG_FROM_STATE_SELF, DataType.Long, selfCall = true) + val getBooleanFromStateSelfF: BaseFunction = getDataFromStateF("getBoolean", DATA_BOOLEAN_FROM_STATE_SELF, DataType.Boolean, selfCall = true) - val getBinaryFromStateSelfF: BaseFunction[Environment] = + val getBinaryFromStateSelfF: BaseFunction = getDataFromStateF("getBinary", DATA_BYTES_FROM_STATE_SELF, DataType.ByteArray, selfCall = true) - val getStringFromStateSelfF: BaseFunction[Environment] = + val getStringFromStateSelfF: BaseFunction = getDataFromStateF("getString", DATA_STRING_FROM_STATE_SELF, DataType.String, selfCall = true) - val isDataStorageUntouchedF: BaseFunction[Environment] = { + val isDataStorageUntouchedF: BaseFunction = { val name = "isDataStorageUntouched" val resultType = BOOLEAN val arg = ("addressOrAlias", addressOrAliasType) - NativeFunction.withEnvironment[Environment]( + NativeFunction.withEnvironment( name, Map[StdLibVersion, Long](V5 -> 10L), IS_UNTOUCHED, resultType, arg ) { - new ContextfulNativeFunction.Simple[Environment](name, resultType, List(arg)) { + new ContextfulNativeFunction.Simple(name, resultType, List(arg)) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case (addressOrAlias: CaseObj) :: Nil => @@ -110,7 +116,7 @@ object Functions { } } - private def getDataFromArrayF(name: String, internalName: Short, dataType: DataType, version: StdLibVersion): BaseFunction[Environment] = + private def getDataFromArrayF(name: String, internalName: Short, dataType: DataType, version: StdLibVersion): BaseFunction = NativeFunction( name, 10, @@ -134,13 +140,13 @@ object Functions { case xs => notImplemented[Id, EVALUATED](s"$name(s: String)", xs) } - def getIntegerFromArrayF(v: StdLibVersion): BaseFunction[Environment] = getDataFromArrayF("getInteger", DATA_LONG_FROM_ARRAY, DataType.Long, v) - def getBooleanFromArrayF(v: StdLibVersion): BaseFunction[Environment] = + def getIntegerFromArrayF(v: StdLibVersion): BaseFunction = getDataFromArrayF("getInteger", DATA_LONG_FROM_ARRAY, DataType.Long, v) + def getBooleanFromArrayF(v: StdLibVersion): BaseFunction = getDataFromArrayF("getBoolean", DATA_BOOLEAN_FROM_ARRAY, DataType.Boolean, v) - def getBinaryFromArrayF(v: StdLibVersion): BaseFunction[Environment] = getDataFromArrayF("getBinary", DATA_BYTES_FROM_ARRAY, DataType.ByteArray, v) - def getStringFromArrayF(v: StdLibVersion): BaseFunction[Environment] = getDataFromArrayF("getString", DATA_STRING_FROM_ARRAY, DataType.String, v) + def getBinaryFromArrayF(v: StdLibVersion): BaseFunction = getDataFromArrayF("getBinary", DATA_BYTES_FROM_ARRAY, DataType.ByteArray, v) + def getStringFromArrayF(v: StdLibVersion): BaseFunction = getDataFromArrayF("getString", DATA_STRING_FROM_ARRAY, DataType.String, v) - private def getDataByIndexF(name: String, dataType: DataType, version: StdLibVersion): BaseFunction[Environment] = + private def getDataByIndexF(name: String, dataType: DataType, version: StdLibVersion): BaseFunction = UserFunction( name, Map[StdLibVersion, Long](V1 -> 30L, V2 -> 30L, V3 -> 30L, V4 -> 4L), @@ -161,10 +167,10 @@ object Functions { ) } - def getIntegerByIndexF(v: StdLibVersion): BaseFunction[Environment] = getDataByIndexF("getInteger", DataType.Long, v) - def getBooleanByIndexF(v: StdLibVersion): BaseFunction[Environment] = getDataByIndexF("getBoolean", DataType.Boolean, v) - def getBinaryByIndexF(v: StdLibVersion): BaseFunction[Environment] = getDataByIndexF("getBinary", DataType.ByteArray, v) - def getStringByIndexF(v: StdLibVersion): BaseFunction[Environment] = getDataByIndexF("getString", DataType.String, v) + def getIntegerByIndexF(v: StdLibVersion): BaseFunction = getDataByIndexF("getInteger", DataType.Long, v) + def getBooleanByIndexF(v: StdLibVersion): BaseFunction = getDataByIndexF("getBoolean", DataType.Boolean, v) + def getBinaryByIndexF(v: StdLibVersion): BaseFunction = getDataByIndexF("getBinary", DataType.ByteArray, v) + def getStringByIndexF(v: StdLibVersion): BaseFunction = getDataByIndexF("getString", DataType.String, v) private def secureHashExpr(xs: EXPR, version: StdLibVersion): EXPR = FUNCTION_CALL( @@ -177,15 +183,15 @@ object Functions { ) ) - def addressFromPublicKeyF(version: StdLibVersion): BaseFunction[Environment] = - UserFunction.withEnvironment[Environment]( + def addressFromPublicKeyF(version: StdLibVersion): BaseFunction = + UserFunction.withEnvironment( name = "addressFromPublicKey", internalName = "addressFromPublicKey", Map[StdLibVersion, Long](V1 -> 82L, V2 -> 82L, V3 -> 82L, V4 -> 63L), addressType, ("@publicKey", BYTESTR) )( - new ContextfulUserFunction[Environment] { + new ContextfulUserFunction { override def apply[F[_]: Monad](env: Environment[F], startArgs: List[EXPR]): EXPR = FUNCTION_CALL( FunctionHeader.User("Address"), @@ -228,15 +234,15 @@ object Functions { } ) - val addressFromPublicKeyNative: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val addressFromPublicKeyNative: BaseFunction = + NativeFunction.withEnvironment( "addressFromPublicKey", Map[StdLibVersion, Long](V6 -> 1L), ADDRESSFROMPUBLICKEY_NATIVE, addressType, ("publicKey", BYTESTR) ) { - new ContextfulNativeFunction.Simple[Environment]("addressFromPublicKey", addressType, Seq(("AddressOrAlias", addressOrAliasType))) { + new ContextfulNativeFunction.Simple("addressFromPublicKey", addressType, Seq(("AddressOrAlias", addressOrAliasType))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = { (env, args) match { case (env, CONST_BYTESTR(publicKey) :: Nil) => @@ -290,9 +296,9 @@ object Functions { ) ) - def addressFromStringF(version: StdLibVersion): BaseFunction[Environment] = + def addressFromStringF(version: StdLibVersion): BaseFunction = UserFunction.withEnvironment("addressFromString", 124, optionAddress, ("@string", STRING)) { - new ContextfulUserFunction[Environment] { + new ContextfulUserFunction { override def apply[F[_]: Monad](env: Environment[F], startArgs: List[EXPR]): EXPR = LET_BLOCK( LET( @@ -346,15 +352,15 @@ object Functions { } } - val addressFromStringV4: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val addressFromStringV4: BaseFunction = + NativeFunction.withEnvironment( "addressFromString", 1, ADDRESSFROMSTRING_NATIVE, optionAddress, ("@string", STRING) ) { - new ContextfulNativeFunction.Simple[Environment]("addressFromString", optionAddress, Seq(("@string", STRING))) { + new ContextfulNativeFunction.Simple("addressFromString", optionAddress, Seq(("@string", STRING))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case CONST_STRING(address) :: Nil => @@ -372,15 +378,15 @@ object Functions { } } - val addressFromRecipientF: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val addressFromRecipientF: BaseFunction = + NativeFunction.withEnvironment( "addressFromRecipient", Map[StdLibVersion, Long](V1 -> 100L, V2 -> 100L, V3 -> 100L, V4 -> 5L), ADDRESSFROMRECIPIENT, addressType, ("AddressOrAlias", addressOrAliasType) ) { - new ContextfulNativeFunction.Simple[Environment]("addressFromRecipient", addressType, Seq(("AddressOrAlias", addressOrAliasType))) { + new ContextfulNativeFunction.Simple("addressFromRecipient", addressType, Seq(("AddressOrAlias", addressOrAliasType))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = { args match { case (c @ CaseObj(`addressType`, _)) :: Nil => @@ -396,7 +402,7 @@ object Functions { } } - val stringFromAddressF: BaseFunction[Environment] = + val stringFromAddressF: BaseFunction = NativeFunction( "toString", Map(V3 -> 10L, V4 -> 10L, V5 -> 10L, V6 -> 1L), @@ -416,8 +422,8 @@ object Functions { case t => throw new IllegalArgumentException(s"Unexpected recipient type $t") } - val assetBalanceF: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val assetBalanceF: BaseFunction = + NativeFunction.withEnvironment( "assetBalance", Map[StdLibVersion, Long](V1 -> 100L, V2 -> 100L, V3 -> 100L, V4 -> 10L), ACCOUNTASSETBALANCE, @@ -425,7 +431,7 @@ object Functions { ("addressOrAlias", addressOrAliasType), ("assetId", UNION(UNIT, BYTESTR)) ) { - new ContextfulNativeFunction.Simple[Environment]( + new ContextfulNativeFunction.Simple( "assetBalance", LONG, Seq(("addressOrAlias", addressOrAliasType), ("assetId", UNION(UNIT, BYTESTR))) @@ -442,8 +448,8 @@ object Functions { } } - val assetBalanceV4F: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val assetBalanceV4F: BaseFunction = + NativeFunction.withEnvironment( "assetBalance", 10, ACCOUNTASSETONLYBALANCE, @@ -451,7 +457,7 @@ object Functions { ("addressOrAlias", addressOrAliasType), ("assetId", BYTESTR) ) { - new ContextfulNativeFunction.Simple[Environment]("assetBalance", LONG, Seq(("addressOrAlias", addressOrAliasType), ("assetId", BYTESTR))) { + new ContextfulNativeFunction.Simple("assetBalance", LONG, Seq(("addressOrAlias", addressOrAliasType), ("assetId", BYTESTR))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case (c: CaseObj) :: CONST_BYTESTR(assetId: ByteStr) :: Nil => @@ -462,15 +468,15 @@ object Functions { } } - val wavesBalanceV4F: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val wavesBalanceV4F: BaseFunction = + NativeFunction.withEnvironment( "wavesBalance", 10, ACCOUNTWAVESBALANCE, balanceDetailsType, ("addressOrAlias", addressOrAliasType) ) { - new ContextfulNativeFunction.Simple[Environment]("wavesBalance", LONG, Seq(("addressOrAlias", addressOrAliasType))) { + new ContextfulNativeFunction.Simple("wavesBalance", LONG, Seq(("addressOrAlias", addressOrAliasType))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case (c: CaseObj) :: Nil => @@ -495,16 +501,16 @@ object Functions { } } - def assetInfoF(version: StdLibVersion, typeDefs: Map[String, FINAL]): BaseFunction[Environment] = { + def assetInfoF(version: StdLibVersion, typeDefs: Map[String, FINAL]): BaseFunction = { val optionAssetType = UNION(typeDefs("Asset"), UNIT) - NativeFunction.withEnvironment[Environment]( + NativeFunction.withEnvironment( "assetInfo", Map[StdLibVersion, Long](V1 -> 100L, V2 -> 100L, V3 -> 100L, V4 -> 15L), GETASSETINFOBYID, optionAssetType, ("id", BYTESTR) ) { - new ContextfulNativeFunction.Simple[Environment]("assetInfo", optionAssetType, Seq(("id", BYTESTR))) { + new ContextfulNativeFunction.Simple("assetInfo", optionAssetType, Seq(("id", BYTESTR))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case CONST_BYTESTR(id: ByteStr) :: Nil => @@ -521,20 +527,20 @@ object Functions { } } - val wavesBalanceF: BaseFunction[Environment] = + val wavesBalanceF: BaseFunction = UserFunction("wavesBalance", 109, LONG, ("@addressOrAlias", addressOrAliasType)) { FUNCTION_CALL(assetBalanceF.header, List(REF("@addressOrAlias"), REF(GlobalValNames.Unit))) } - val txHeightByIdF: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val txHeightByIdF: BaseFunction = + NativeFunction.withEnvironment( "transactionHeightById", Map[StdLibVersion, Long](V1 -> 100L, V2 -> 100L, V3 -> 100L, V4 -> 20L), TRANSACTIONHEIGHTBYID, optionLong, ("id", BYTESTR) ) { - new ContextfulNativeFunction.Simple[Environment]("transactionHeightById", optionLong, Seq(("id", BYTESTR))) { + new ContextfulNativeFunction.Simple("transactionHeightById", optionLong, Seq(("id", BYTESTR))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case CONST_BYTESTR(id: ByteStr) :: Nil => @@ -548,16 +554,16 @@ object Functions { } } - def blockInfoByHeightF(version: StdLibVersion, typeDefs: Map[String, FINAL]): BaseFunction[Environment] = { + def blockInfoByHeightF(version: StdLibVersion, typeDefs: Map[String, FINAL]): BaseFunction = { val optionBlockInfoType = UNION(typeDefs("BlockInfo"), UNIT) - NativeFunction.withEnvironment[Environment]( + NativeFunction.withEnvironment( "blockInfoByHeight", Map[StdLibVersion, Long](V1 -> 100L, V2 -> 100L, V3 -> 100L, V4 -> 5L), BLOCKINFOBYHEIGHT, optionBlockInfoType, ("height", LONG) ) { - new ContextfulNativeFunction.Simple[Environment]("blockInfoByHeight", optionBlockInfoType, Seq(("height", LONG))) { + new ContextfulNativeFunction.Simple("blockInfoByHeight", optionBlockInfoType, Seq(("height", LONG))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case CONST_LONG(height: Long) :: Nil => @@ -572,9 +578,9 @@ object Functions { } } - def callDAppF(reentrant: Boolean): BaseFunction[Environment] = { + def callDAppF(reentrant: Boolean): BaseFunction = { val (id, name) = if (reentrant) (CALLDAPPREENTRANT, "reentrantInvoke") else (CALLDAPP, "invoke") - NativeFunction.withEnvironment[Environment]( + NativeFunction.withEnvironment( name, Map[StdLibVersion, Long](V5 -> 75L), id, @@ -584,69 +590,18 @@ object Functions { ("args", LIST(ANY)), ("payments", listPayment) ) { - new ContextfulNativeFunction.Extended[Environment]( + new ContextfulNativeFunction.Simple( name, ANY, Seq(("dapp", BYTESTR), ("name", STRING), ("args", LIST(ANY)), ("payments", listPayment)) ) { - override def evaluate[F[_]: Monad]( - env: Environment[F], - args: List[EVALUATED], - availableComplexity: Int - )(implicit m: Monad[CoevalF[F, *]]): Coeval[F[(Either[ExecutionError, (EVALUATED, Log[F])], Int)]] = { - val dAppBytes = args match { - case (dApp: CaseObj) :: _ if dApp.caseType == addressType => - dApp.fields("bytes") match { - case CONST_BYTESTR(d) => d.pure[F] - case a => throw new IllegalArgumentException(s"Unexpected address bytes $a") - } - case (dApp: CaseObj) :: _ if dApp.caseType == aliasType => - (dApp.fields("alias"): @unchecked) match { - case CONST_STRING(a) => env.resolveAlias(a).map(_.explicitGet().bytes) - } - case args => throw new IllegalArgumentException(s"Unexpected recipient args $args") - } - val name = args match { - case _ :: CONST_STRING(name) :: _ => name - case _ :: CaseObj(UNIT, _) :: _ => "default" - case args => throw new IllegalArgumentException(s"Unexpected input args $args") - } - args match { - case _ :: _ :: ARR(args) :: ARR(payments) :: Nil => - env - .callScript( - Recipient.Address(dAppBytes.asInstanceOf[ByteStr]), - name, - args.toList, - payments.map { - case p: CaseObj if p.caseType == paymentType => - (List("assetId", "amount").map(p.fields): @unchecked) match { - case CONST_BYTESTR(a) :: CONST_LONG(v) :: Nil => (Some(a.arr), v) - case CaseObj(UNIT, _) :: CONST_LONG(v) :: Nil => (None, v) - } - case arg => throw new IllegalArgumentException(s"Unexpected payment arg $arg") - }, - availableComplexity, - reentrant - ) - .map(_.map { case (result, spentComplexity) => - val mappedError = result.leftMap { - case reject: FailOrRejectError => reject - case other => CommonError("Nested invoke error", Some(other)) - } - (mappedError, availableComplexity - spentComplexity) - }) - case xs => - val err = - notImplemented[F, (EVALUATED, Log[F])](s"invoke(dApp: Address, function: String, args: List[Any], payments: List[Payment])", xs) - Coeval.now(err.map((_, 0))) - } - } + override def evaluate[F[_]: Monad](env: Environment[F], evaluatedArgs: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = + notImplemented[F, EVALUATED](name, evaluatedArgs) } } } - private def withExtract[C[_[_]]](f: BaseFunction[C], version: StdLibVersion): BaseFunction[C] = { + private def withExtract(f: BaseFunction, version: StdLibVersion): BaseFunction = { val args = f.signature.args.zip(f.args).map { case ((name, ty), _) => ("@" ++ name, ty) } @@ -662,7 +617,7 @@ object Functions { } } - def extractedFuncs(v: StdLibVersion): Array[BaseFunction[Environment]] = + def extractedFuncs(v: StdLibVersion): Array[BaseFunction] = Array( getIntegerFromStateF, getBooleanFromStateF, @@ -679,7 +634,7 @@ object Functions { if (v >= V4) addressFromStringV4 else addressFromStringF(v) ).map(withExtract(_, v)) - def extractedStateSelfFuncs(v: StdLibVersion): Array[BaseFunction[Environment]] = + def extractedStateSelfFuncs(v: StdLibVersion): Array[BaseFunction] = Array( getIntegerFromStateSelfF, getBooleanFromStateSelfF, @@ -687,15 +642,15 @@ object Functions { getStringFromStateSelfF ).map(withExtract(_, v)) - def txByIdF(proofsEnabled: Boolean, version: StdLibVersion): BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + def txByIdF(proofsEnabled: Boolean, version: StdLibVersion): BaseFunction = + NativeFunction.withEnvironment( "transactionById", 100, GETTRANSACTIONBYID, txByIdReturnType(proofsEnabled, version), ("id", BYTESTR) ) { - new ContextfulNativeFunction.Simple[Environment]("transactionById", txByIdReturnType(proofsEnabled, version), Seq(("id", BYTESTR))) { + new ContextfulNativeFunction.Simple("transactionById", txByIdReturnType(proofsEnabled, version), Seq(("id", BYTESTR))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case CONST_BYTESTR(id: ByteStr) :: Nil => @@ -710,16 +665,16 @@ object Functions { } } - def transferTxByIdF(proofsEnabled: Boolean, version: StdLibVersion, typeDefs: Map[String, FINAL]): BaseFunction[Environment] = { + def transferTxByIdF(proofsEnabled: Boolean, version: StdLibVersion, typeDefs: Map[String, FINAL]): BaseFunction = { val optionTransferTxType = UNION(typeDefs("TransferTransaction"), UNIT) - NativeFunction.withEnvironment[Environment]( + NativeFunction.withEnvironment( "transferTransactionById", Map[StdLibVersion, Long](V3 -> 100L, V4 -> 60L), TRANSFERTRANSACTIONBYID, optionTransferTxType, ("id", BYTESTR) ) { - new ContextfulNativeFunction.Simple[Environment]( + new ContextfulNativeFunction.Simple( "transferTransactionById", optionTransferTxType, Seq(("id", BYTESTR)) @@ -739,15 +694,15 @@ object Functions { } } - val calculateAssetIdF: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val calculateAssetIdF: BaseFunction = + NativeFunction.withEnvironment( "calculateAssetId", 10, CALCULATE_ASSET_ID, BYTESTR, ("issue", issueActionType) ) { - new ContextfulNativeFunction.Simple[Environment]("calculateAssetId", BYTESTR, Seq(("issue", issueActionType))) { + new ContextfulNativeFunction.Simple("calculateAssetId", BYTESTR, Seq(("issue", issueActionType))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case CaseObj(`issueActionType`, fields) :: Nil => @@ -781,16 +736,16 @@ object Functions { } } - def transactionFromProtoBytesF(proofsEnabled: Boolean, version: StdLibVersion, typeDefs: Map[String, FINAL]): BaseFunction[Environment] = { + def transactionFromProtoBytesF(proofsEnabled: Boolean, version: StdLibVersion, typeDefs: Map[String, FINAL]): BaseFunction = { val optionTransferTxType = UNION(typeDefs("TransferTransaction"), UNIT) - NativeFunction.withEnvironment[Environment]( + NativeFunction.withEnvironment( "transferTransactionFromProto", 5, TRANSFER_TRANSACTION_FROM_PROTO, optionTransferTxType, ("bytes", BYTESTR) ) { - new ContextfulNativeFunction.Simple[Environment]( + new ContextfulNativeFunction.Simple( "transferTransactionFromProto", optionTransferTxType, Seq(("bytes", BYTESTR)) @@ -811,7 +766,7 @@ object Functions { } } - val simplifiedIssueActionConstructor: BaseFunction[Environment] = + val simplifiedIssueActionConstructor: BaseFunction = NativeFunction( "Issue", 1, @@ -827,7 +782,7 @@ object Functions { Right(CaseObj(issueActionType, typedArgs)) } - val detailedIssueActionConstructor: BaseFunction[Environment] = + val detailedIssueActionConstructor: BaseFunction = NativeFunction( "Issue", 1, @@ -845,7 +800,7 @@ object Functions { Right(CaseObj(issueActionType, typedArgs)) } - val simplifiedLeaseActionConstructor: BaseFunction[Environment] = + val simplifiedLeaseActionConstructor: BaseFunction = NativeFunction( "Lease", 1, @@ -858,7 +813,7 @@ object Functions { Right(CaseObj(leaseActionType, typedArgs)) } - val detailedLeaseActionConstructor: BaseFunction[Environment] = + val detailedLeaseActionConstructor: BaseFunction = NativeFunction( "Lease", 1, @@ -872,8 +827,8 @@ object Functions { Right(CaseObj(leaseActionType, typedArgs)) } - val calculateLeaseId: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val calculateLeaseId: BaseFunction = + NativeFunction.withEnvironment( "calculateLeaseId", 1, CALCULATE_LEASE_ID, @@ -882,7 +837,7 @@ object Functions { ) { val AddressLength = 26 val MaxAliasLength = 30 - new ContextfulNativeFunction.Simple[Environment]("calculateLeaseId", BYTESTR, Seq(("lease", leaseActionType))) { + new ContextfulNativeFunction.Simple("calculateLeaseId", BYTESTR, Seq(("lease", leaseActionType))) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case CaseObj(`leaseActionType`, fields) :: Nil => @@ -910,18 +865,18 @@ object Functions { } } - def accountScriptHashF(global: BaseGlobal): BaseFunction[Environment] = { + def accountScriptHashF(global: BaseGlobal): BaseFunction = { val name = "scriptHash" val resType = UNION(BYTESTR, UNIT) val arg = ("account", addressOrAliasType) - NativeFunction.withEnvironment[Environment]( + NativeFunction.withEnvironment( name, 200, ACCOUNTSCRIPTHASH, resType, arg ) { - new ContextfulNativeFunction.Simple[Environment]( + new ContextfulNativeFunction.Simple( name, resType, Seq(arg) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/Vals.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/Vals.scala index 567d0843c21..fa055b819f9 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/Vals.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/Vals.scala @@ -21,7 +21,7 @@ object Vals { version: StdLibVersion, proofsEnabled: Boolean, fixBigScriptField: Boolean - ): (String, (UNION, ContextfulVal[Environment])) = + ): (String, (UNION, ContextfulVal)) = (Tx, (scriptInputType(isTokenContext, version, proofsEnabled), inputEntityVal(version, proofsEnabled, fixBigScriptField))) private def scriptInputType(isTokenContext: Boolean, version: StdLibVersion, proofsEnabled: Boolean) = @@ -30,8 +30,8 @@ object Vals { else UNION(buildOrderType(proofsEnabled) :: buildActiveTransactionTypes(proofsEnabled, version)) - private def inputEntityVal(version: StdLibVersion, proofsEnabled: Boolean, fixBigScriptField: Boolean): ContextfulVal[Environment] = - new ContextfulVal.Lifted[Environment] { + private def inputEntityVal(version: StdLibVersion, proofsEnabled: Boolean, fixBigScriptField: Boolean): ContextfulVal = + new ContextfulVal.Lifted { override def liftF[F[_]: Monad](env: Environment[F]): Eval[Either[ExecutionError, EVALUATED]] = Eval.later( env.inputEntity @@ -54,8 +54,8 @@ object Vals { ) } - val heightVal: ContextfulVal[Environment] = - new ContextfulVal[Environment] { + val heightVal: ContextfulVal = + new ContextfulVal { override def apply[F[_]: Monad](env: Environment[F]): Eval[F[Either[ExecutionError, EVALUATED]]] = Eval.later { env.height @@ -64,8 +64,8 @@ object Vals { } } - val accountThisVal: ContextfulVal[Environment] = - new ContextfulVal.Lifted[Environment] { + val accountThisVal: ContextfulVal = + new ContextfulVal.Lifted { override def liftF[F[_]: Monad](env: Environment[F]): Eval[Either[ExecutionError, EVALUATED]] = Eval.later { if (env.dAppAlias) { @@ -80,8 +80,8 @@ object Vals { } } - def assetThisVal(version: StdLibVersion): ContextfulVal[Environment] = - new ContextfulVal[Environment] { + def assetThisVal(version: StdLibVersion): ContextfulVal = + new ContextfulVal { override def apply[F[_]: Monad](env: Environment[F]): Eval[F[Either[ExecutionError, EVALUATED]]] = Eval.later { env @@ -96,8 +96,8 @@ object Vals { } } - def lastBlockVal(version: StdLibVersion): ContextfulVal[Environment] = - new ContextfulVal[Environment] { + def lastBlockVal(version: StdLibVersion): ContextfulVal = + new ContextfulVal { override def apply[F[_]: Monad](env: Environment[F]): Eval[F[Either[ExecutionError, EVALUATED]]] = Eval.later { env @@ -109,16 +109,16 @@ object Vals { def lastBlock(version: StdLibVersion) = (LastBlock, (blockInfo(version), lastBlockVal(version))) - val sellOrdTypeVal: ContextfulVal[Environment] = ContextfulVal.fromEval(Eval.now(Right(ordType(OrdType.Sell)))) - val buyOrdTypeVal: ContextfulVal[Environment] = ContextfulVal.fromEval(Eval.now(Right(ordType(OrdType.Buy)))) + val sellOrdTypeVal: ContextfulVal = ContextfulVal.fromEval(Eval.now(Right(ordType(OrdType.Sell)))) + val buyOrdTypeVal: ContextfulVal = ContextfulVal.fromEval(Eval.now(Right(ordType(OrdType.Buy)))) val sell = (Sell, (ordTypeType, sellOrdTypeVal)) val buy = (Buy, (ordTypeType, buyOrdTypeVal)) - val height: (String, (LONG.type, ContextfulVal[Environment])) = (Height, (LONG, heightVal)) + val height: (String, (LONG.type, ContextfulVal)) = (Height, (LONG, heightVal)) - val accountThis: (String, (CASETYPEREF, ContextfulVal[Environment])) = (This, (addressType, accountThisVal)) - def assetThis(version: StdLibVersion): (String, (CASETYPEREF, ContextfulVal[Environment])) = + val accountThis: (String, (CASETYPEREF, ContextfulVal)) = (This, (addressType, accountThisVal)) + def assetThis(version: StdLibVersion): (String, (CASETYPEREF, ContextfulVal)) = (This, (assetType(version), assetThisVal(version))) } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/WavesContext.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/WavesContext.scala index 028d018bb32..b0af97f3ec4 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/WavesContext.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/ctx/impl/waves/WavesContext.scala @@ -9,11 +9,10 @@ import com.wavesplatform.lang.v1.evaluator.ctx.BaseFunction import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Functions.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Types.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Vals.* -import com.wavesplatform.lang.v1.traits.* import com.wavesplatform.lang.v1.{BaseGlobal, CTX} object WavesContext { - def build(global: BaseGlobal, ds: DirectiveSet, fixBigScriptField: Boolean): CTX[Environment] = + def build(global: BaseGlobal, ds: DirectiveSet, fixBigScriptField: Boolean): CTX = invariableCtx |+| variableCtx(global, ds, fixBigScriptField) private val commonFunctions = @@ -41,7 +40,7 @@ object WavesContext { private val invariableCtx = CTX(Seq(), Map(height), commonFunctions) - private def variableCtx(global: BaseGlobal, ds: DirectiveSet, fixBigScriptField: Boolean): CTX[Environment] = { + private def variableCtx(global: BaseGlobal, ds: DirectiveSet, fixBigScriptField: Boolean): CTX = { val isTokenContext = ds.scriptType == Asset val proofsEnabled = !isTokenContext val version = ds.stdLibVersion @@ -82,13 +81,13 @@ object WavesContext { if (ds.contentType == DApp || ds.scriptType == Call) Array(callDAppF(reentrant = false), callDAppF(reentrant = true)) else - Array[BaseFunction[Environment]]() + Array[BaseFunction]() val accountFuncs = if (ds.scriptType == Account) selfCallFunctions(V5) else - Array[BaseFunction[Environment]]() + Array[BaseFunction]() fromV4Funcs(proofsEnabled, ds.stdLibVersion, typeDefs) ++ v5Funcs ++ dAppFuncs ++ accountFuncs } @@ -134,7 +133,7 @@ object WavesContext { contentType: ContentType, proofsEnabled: Boolean, fixBigScriptField: Boolean - ): Map[String, (FINAL, ContextfulVal[Environment])] = { + ): Map[String, (FINAL, ContextfulVal)] = { val txVal = tx(isTokenContext, version, proofsEnabled, fixBigScriptField) version match { case V1 => Map(txVal) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/package.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/package.scala index 6968162000a..7cd42c0d6ab 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/package.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/evaluator/package.scala @@ -7,14 +7,14 @@ import com.wavesplatform.lang.v1.task.TaskMT import com.wavesplatform.lang.{EvalF, ExecutionError, TrampolinedExecResult} package object evaluator { - type EvalM[F[_], C[_[_]], A] = TaskMT[F, EnabledLogEvaluationContext[C, F], ExecutionError, A] + type EvalM[F[_], A] = TaskMT[F, EnabledLogEvaluationContext[F], ExecutionError, A] - implicit class EvalMOps[F[_], C[_[_]], A](ev: EvalM[F, C, A]) { - def ter(ctx: EnabledLogEvaluationContext[C, F]): TrampolinedExecResult[F, A] = + implicit class EvalMOps[F[_], A](ev: EvalM[F, A]) { + def ter(ctx: EnabledLogEvaluationContext[F]): TrampolinedExecResult[F, A] = EitherT[EvalF[F, *], ExecutionError, A](ev.run(ctx).map(_._2)) } - def liftTER[F[_], C[_[_]], A](ter: Eval[F[Either[ExecutionError, A]]]): EvalM[F, C, A] = + def liftTER[F[_], A](ter: Eval[F[Either[ExecutionError, A]]]): EvalM[F, A] = TaskMT(_ => ter) type LetExecResult[F[_]] = F[Either[ExecutionError, compiler.Terms.EVALUATED]] diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/traits/Environment.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/traits/Environment.scala index e4d86ed6ae5..464c681e6d5 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/traits/Environment.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/traits/Environment.scala @@ -17,6 +17,8 @@ object Environment { case class AssetId(id: Array[Byte]) type Tthis = Recipient.Address :+: AssetId :+: CNil + + } trait Environment[F[_]] { diff --git a/lang/shared/src/test/scala/com/wavesplatform/common/state/ByteStrTest.scala b/lang/shared/src/test/scala/com/wavesplatform/common/state/ByteStrTest.scala index 2fc819bd93f..7abebd2a00d 100644 --- a/lang/shared/src/test/scala/com/wavesplatform/common/state/ByteStrTest.scala +++ b/lang/shared/src/test/scala/com/wavesplatform/common/state/ByteStrTest.scala @@ -1,7 +1,7 @@ package com.wavesplatform.common.state import com.wavesplatform.common.utils.{Base58, Base64} -import org.scalatest._ +import org.scalatest.* class ByteStrTest extends wordspec.AnyWordSpec with matchers.should.Matchers { diff --git a/lang/testkit/src/main/scala/com/wavesplatform/lang/Common.scala b/lang/testkit/src/main/scala/com/wavesplatform/lang/Common.scala index 60cdb6b3a6d..ab085bfdcb4 100644 --- a/lang/testkit/src/main/scala/com/wavesplatform/lang/Common.scala +++ b/lang/testkit/src/main/scala/com/wavesplatform/lang/Common.scala @@ -8,7 +8,6 @@ import com.wavesplatform.lang.script.Script 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.* @@ -27,16 +26,15 @@ object Common { private val dataEntryValueType = UNION(LONG, BOOLEAN, BYTESTR, STRING) val dataEntryType = CASETYPEREF("DataEntry", List("key" -> STRING, "value" -> dataEntryValueType)) - val addCtx: CTX[NoContext] = CTX[NoContext](Seq(dataEntryType), Map.empty, Array.empty) + val addCtx: CTX = CTX(Seq(dataEntryType), Map.empty, Array.empty) def ev[T <: EVALUATED]( - context: EvaluationContext[NoContext, Id] = - Monoid.combine(PureContext.build(V1, useNewPowPrecision = true).evaluationContext, addCtx.evaluationContext), + context: EvaluationContext[Id] = Monoid.combine(PureContext.build(V1, useNewPowPrecision = true), addCtx).evaluationContext(???), expr: EXPR ): Either[ExecutionError, T] = - new EvaluatorV1[Id, NoContext]().apply[T](context, expr) + new EvaluatorV1[Id]().apply[T](context, expr) - val multiplierFunction: NativeFunction[NoContext] = + val multiplierFunction: NativeFunction = NativeFunction("MULTIPLY", 1L, 10005.toShort, LONG, ("x1", LONG), ("x2", LONG)) { case CONST_LONG(x1: Long) :: CONST_LONG(x2: Long) :: Nil => Try(x1 * x2).map(CONST_LONG).toEither.left.map(_.toString) case _ => ??? // suppress pattern match warning @@ -65,14 +63,7 @@ object Common { UNION.create(CorD.typeList, Some("PointCD")) ) - def sampleUnionContext(instance: CaseObj) = - EvaluationContext.build( - Map.empty, - Map("p" -> LazyVal.fromEvaluated[Id](instance)), - Seq.empty[BaseFunction[NoContext]] - ) - - def emptyBlockchainEnvironment(h: Int = 1, in: Coeval[Environment.InputEntity] = Coeval(???), nByte: Byte = 'T'): Environment[Id] = + def emptyBlockchainEnvironment(h: Int = 1, in: Coeval[Environment.InputEntity] = Coeval.evalOnce(???), nByte: Byte = 'T'): Environment[Id] = new Environment[Id] { override def height: Long = h override def chainId: Byte = nByte @@ -93,9 +84,10 @@ object Common { override def multiPaymentAllowed: Boolean = true override def txId: ByteStr = ??? override def transferTransactionFromProto(b: Array[Byte]): Option[Tx.Transfer] = ??? - override def addressFromString(address: String): Either[String, Recipient.Address] = ??? - override def addressFromPublicKey(publicKey: ByteStr): Either[String, Address] = ??? - def accountScript(addressOrAlias: Recipient): Option[Script] = ??? + override def addressFromString(address: String): Either[String, Recipient.Address] = + Common.this.addressFromString(chainId, address).map(v => Recipient.Address(ByteStr(v.get))) + override def addressFromPublicKey(publicKey: ByteStr): Either[String, Address] = ??? + def accountScript(addressOrAlias: Recipient): Option[Script] = ??? override def callScript( dApp: Address, func: String, diff --git a/lang/testkit/src/main/scala/com/wavesplatform/lang/v1/compiler/TestCompiler.scala b/lang/testkit/src/main/scala/com/wavesplatform/lang/v1/compiler/TestCompiler.scala index 4e184145a5d..f7fa1b6d4ea 100644 --- a/lang/testkit/src/main/scala/com/wavesplatform/lang/v1/compiler/TestCompiler.scala +++ b/lang/testkit/src/main/scala/com/wavesplatform/lang/v1/compiler/TestCompiler.scala @@ -12,22 +12,20 @@ import com.wavesplatform.lang.script.{ContractScript, Script} import com.wavesplatform.lang.v1.CTX import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} -import com.wavesplatform.lang.v1.traits.Environment import scala.collection.mutable class TestCompiler(version: StdLibVersion) { private lazy val baseCompilerContext = - PureContext.build(version, useNewPowPrecision = true).withEnvironment[Environment] |+| - CryptoContext.build(Global, version).withEnvironment[Environment] + PureContext.build(version, useNewPowPrecision = true) |+| + CryptoContext.build(Global, version) - private lazy val compilerContext = - (baseCompilerContext |+| - WavesContext.build(Global, DirectiveSet(version, Account, DAppType).explicitGet(), fixBigScriptField = true)).compilerContext + lazy val dappContext: CTX = + baseCompilerContext |+| + WavesContext.build(Global, DirectiveSet(version, Account, DAppType).explicitGet(), fixBigScriptField = true) - lazy val expressionContext: CTX[Environment] = + private lazy val expressionContext: CTX = WavesContext.build(Global, DirectiveSet(version, Account, Expression).explicitGet(), fixBigScriptField = true) - private lazy val expressionCompilerContext = (baseCompilerContext |+| expressionContext).compilerContext @@ -44,7 +42,7 @@ class TestCompiler(version: StdLibVersion) { ): Either[String, DApp] = ContractCompiler.compile( script, - compilerContext, + dappContext.compilerContext, version, allowIllFormedStrings = allowIllFormedStrings, needCompaction = compact, @@ -64,13 +62,19 @@ class TestCompiler(version: StdLibVersion) { def compileExpressionE(script: String, allowIllFormedStrings: Boolean = false, checkSize: Boolean = true): Either[String, ExprScript] = ExpressionCompiler .compile(script, expressionCompilerContext, allowIllFormedStrings) - .map(s => ExprScript(version, s._1, checkSize = checkSize).explicitGet()) + .map(s => + ExprScript( + version, + s._1, + checkSize = checkSize + ).explicitGet() + ) def compileAsset(script: String): Script = ExprScript(version, ExpressionCompiler.compile(script, assetCompilerContext).explicitGet()._1).explicitGet() def compileFreeCall(script: String): ExprScript = { - val expr = ContractCompiler.compileFreeCall(script, compilerContext, version).explicitGet() + val expr = ContractCompiler.compileFreeCall(script, dappContext.compilerContext, version).explicitGet() ExprScript(version, expr, isFreeCall = true).explicitGet() } } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/ContractIntegrationTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/ContractIntegrationTest.scala index f4d504ccf74..c2502a4fd20 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/ContractIntegrationTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/ContractIntegrationTest.scala @@ -8,6 +8,7 @@ import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.lang.Common.sampleTypes import com.wavesplatform.lang.directives.DirectiveSet import com.wavesplatform.lang.directives.values.* +import com.wavesplatform.lang.miniev.{ComplexityLimit, State} import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.{ContractCompiler, Terms} import com.wavesplatform.lang.v1.evaluator.* @@ -23,9 +24,9 @@ import org.scalatest.Inside class ContractIntegrationTest extends PropSpec with Inside { - private val ctx: CTX[Environment] = - PureContext.build(V3, useNewPowPrecision = true).withEnvironment[Environment] |+| - CTX[Environment](sampleTypes, Map.empty, Array.empty) |+| + private val ctx: CTX = + PureContext.build(V3, useNewPowPrecision = true) |+| + CTX(sampleTypes, Map.empty, Array.empty) |+| WavesContext.build( Global, DirectiveSet(V3, Account, DApp).explicitGet(), @@ -90,7 +91,7 @@ class ContractIntegrationTest extends PropSpec with Inside { DataItem.Bin("feeAssetId", ByteStr.empty) ), List(), - 2147483615 + Int.MaxValue - 2147483615 ) } @@ -104,7 +105,7 @@ class ContractIntegrationTest extends PropSpec with Inside { """.stripMargin, "foo", Range(1, 23).map(i => Terms.CONST_LONG(i)).toList - ).explicitGet()._1 shouldBe ScriptResultV3(List(DataItem.Lng("1", 22)), List(), 2147483641) + ).explicitGet()._1 shouldBe ScriptResultV3(List(DataItem.Lng("1", 22)), List(), Int.MaxValue - 2147483641) } property("@Callable exception error contains initialised values") { @@ -157,7 +158,6 @@ class ContractIntegrationTest extends PropSpec with Inside { ContractEvaluator .applyV2Coeval( - ctx.evaluationContext(environment), compiled, ByteStr.fill(32)(1), Invocation( @@ -175,9 +175,8 @@ class ContractIntegrationTest extends PropSpec with Inside { Int.MaxValue, correctFunctionCallScope = true, newMode = false, - enableExecutionLog = true + State(ctx.evaluationContext(Common.emptyBlockchainEnvironment()), ComplexityLimit.Unlimited, false, V3) ) - .value() .leftMap { case (e, _, log) => (e, log) } } @@ -313,7 +312,7 @@ class ContractIntegrationTest extends PropSpec with Inside { AssetTransfer(Recipient.Address(callerAddress), Recipient.Address(callerAddress), 1L, None), AssetTransfer(Recipient.Address(callerAddress), Recipient.Address(callerAddress), 2L, None) ), - 2147483626 + Int.MaxValue - 2147483626 ) } @@ -382,7 +381,7 @@ class ContractIntegrationTest extends PropSpec with Inside { AssetTransfer(Recipient.Address(callerAddress), Recipient.Address(callerAddress), 3, None), AssetTransfer(Recipient.Address(callerAddress), Recipient.Address(callerAddress), 4, None) ), - 2147483605 + Int.MaxValue - 2147483605 ) } } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala index 92f4864a3b8..da2bb47aa12 100755 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala @@ -1,5 +1,7 @@ package com.wavesplatform.lang +import java.nio.charset.StandardCharsets + import cats.Id import cats.kernel.Monoid import cats.syntax.either.* @@ -14,13 +16,12 @@ import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.Types.{BYTESTR, FINAL, LONG} import com.wavesplatform.lang.v1.compiler.{ExpressionCompiler, Terms} -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext import com.wavesplatform.lang.v1.evaluator.ContractEvaluator.LogExtraInfo import com.wavesplatform.lang.v1.evaluator.ctx.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext.MaxListLengthV4 import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext -import com.wavesplatform.lang.v1.evaluator.{Contextful, ContextfulVal, EvaluatorV2} +import com.wavesplatform.lang.v1.evaluator.{ContextfulVal, EvaluatorV2} import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.traits.domain.Recipient.{Address, Alias} import com.wavesplatform.lang.v1.traits.domain.{Issue, Lease} @@ -29,7 +30,6 @@ import com.wavesplatform.test.* import org.scalatest.Inside import org.web3j.crypto.Keys -import java.nio.charset.StandardCharsets import scala.util.Random class IntegrationTest extends PropSpec with Inside { @@ -37,20 +37,20 @@ class IntegrationTest extends PropSpec with Inside { code: String, pointInstance: Option[CaseObj] = None, pointType: FINAL = AorBorC, - ctxt: CTX[NoContext] = CTX.empty, + ctxt: CTX = CTX.empty, version: StdLibVersion = V3 ): Either[String, T] = - genericEval[NoContext, T](code, pointInstance, pointType, ctxt, version, Contextful.empty[Id]) + genericEval[T](code, pointInstance, pointType, ctxt, version, Common.emptyBlockchainEnvironment()) - private def genericEval[C[_[_]], T <: EVALUATED]( + private def genericEval[T <: EVALUATED]( code: String, pointInstance: Option[CaseObj] = None, pointType: FINAL = AorBorC, - ctxt: CTX[C], + ctxt: CTX, version: StdLibVersion, - env: C[Id] + env: Environment[Id] ): Either[String, T] = { - val f: BaseFunction[C] = + val f: BaseFunction = NativeFunction( "fn1", 1, @@ -62,7 +62,7 @@ class IntegrationTest extends PropSpec with Inside { case xs => notImplemented[Id, EVALUATED]("fraction(value: Int, numerator: Int, denominator: Int)", xs) } - val f2: BaseFunction[C] = + val f2: BaseFunction = NativeFunction( "fn2", 1, @@ -74,22 +74,22 @@ class IntegrationTest extends PropSpec with Inside { case xs => notImplemented[Id, EVALUATED]("fraction(value: Int, numerator: Int, denominator: Int)", xs) } - val lazyVal = ContextfulVal.pure[C](pointInstance.orNull) + val lazyVal = ContextfulVal.pure(pointInstance.orNull) val stringToTuple = Map(("p", (pointType, lazyVal))) - val ctx: CTX[C] = + val ctx: CTX = Monoid.combineAll( Seq( - PureContext.build(version, useNewPowPrecision = true).withEnvironment[C], - CryptoContext.build(Global, version).withEnvironment[C], - addCtx.withEnvironment[C], - CTX[C](sampleTypes, stringToTuple, Array(f, f2)), + PureContext.build(version, useNewPowPrecision = true), + CryptoContext.build(Global, version), + addCtx, + CTX(sampleTypes, stringToTuple, Array(f, f2)), ctxt ) ) val compiled = ExpressionCompiler.compile(code, ctx.compilerContext) - val evalCtx = ctx.evaluationContext(env).asInstanceOf[EvaluationContext[Environment, Id]] + val evalCtx = ctx.evaluationContext(env).asInstanceOf[EvaluationContext[Id]] compiled.flatMap(v => EvaluatorV2 .applyCompleted(evalCtx, v._1, LogExtraInfo(), version, correctFunctionCallScope = true, newMode = true, enableExecutionLog = false) @@ -419,18 +419,14 @@ class IntegrationTest extends PropSpec with Inside { } property("context won't change after execution of a user function") { - val doubleFst = UserFunction[NoContext]("ID", 0, LONG, ("x", LONG)) { + val doubleFst = UserFunction("ID", 0, LONG, ("x", LONG)) { FUNCTION_CALL(PureContext.sumLong.header, List(REF("x"), REF("x"))) } val context = Monoid.combine( - PureContext.build(V1, useNewPowPrecision = true).evaluationContext[Id], - EvaluationContext.build( - typeDefs = Map.empty, - letDefs = Map("x" -> LazyVal.fromEvaluated[Id](CONST_LONG(3L))), - functions = Seq(doubleFst) - ) - ) + PureContext.build(V1, useNewPowPrecision = true), + CTX(Seq.empty, Map("x" -> (LONG, ContextfulVal.pure(CONST_LONG(3L)))), Array(doubleFst)) + ).evaluationContext(Common.emptyBlockchainEnvironment()) val expr = FUNCTION_CALL(PureContext.sumLong.header, List(FUNCTION_CALL(doubleFst.header, List(CONST_LONG(1000L))), REF("x"))) ev[CONST_LONG](context, expr) shouldBe evaluated(2003L) @@ -438,13 +434,9 @@ class IntegrationTest extends PropSpec with Inside { property("context won't change after execution of an inner block") { val context = Monoid.combine( - PureContext.build(V1, useNewPowPrecision = true).evaluationContext[Id], - EvaluationContext.build( - typeDefs = Map.empty, - letDefs = Map("x" -> LazyVal.fromEvaluated[Id](CONST_LONG(3L))), - functions = Seq() - ) - ) + PureContext.build(V1, useNewPowPrecision = true), + CTX(Seq.empty, Map("x" -> (LONG, ContextfulVal.pure(CONST_LONG(3L)))), Array.empty) + ).evaluationContext(Common.emptyBlockchainEnvironment()) val expr = FUNCTION_CALL( function = PureContext.sumLong.header, @@ -614,10 +606,10 @@ class IntegrationTest extends PropSpec with Inside { for (i <- 65528 to 65535) array(i) = 1 val src = s""" arr.toInt(65528) """ - val arrVal = ContextfulVal.pure[NoContext](CONST_BYTESTR(ByteStr(array), limit = CONST_BYTESTR.DataTxSize).explicitGet()) + val arrVal = ContextfulVal.pure(CONST_BYTESTR(ByteStr(array), limit = CONST_BYTESTR.DataTxSize).explicitGet()) eval[EVALUATED]( src, - ctxt = CTX[NoContext]( + ctxt = CTX( Seq(), Map("arr" -> (BYTESTR -> arrVal)), Array() @@ -1090,21 +1082,21 @@ class IntegrationTest extends PropSpec with Inside { val ctx = WavesContext.build(Global, DirectiveSet(V4, Account, DApp).explicitGet(), fixBigScriptField = true) - genericEval[Environment, EVALUATED]( + genericEval[EVALUATED]( writeSetScript, ctxt = ctx, version = V4, env = utils.environment ) should produce("Can't find a function 'WriteSet'") - genericEval[Environment, EVALUATED]( + genericEval[EVALUATED]( transferSetScript, ctxt = ctx, version = V4, env = utils.environment ) should produce("Can't find a function 'TransferSet'") - genericEval[Environment, EVALUATED]( + genericEval[EVALUATED]( scriptResultScript, ctxt = ctx, version = V4, @@ -1388,7 +1380,7 @@ class IntegrationTest extends PropSpec with Inside { val ctx = WavesContext.build(Global, DirectiveSet(V4, Account, DApp).explicitGet(), fixBigScriptField = true) - genericEval[Environment, EVALUATED](script, ctxt = ctx, version = V4, env = utils.environment) shouldBe + genericEval[EVALUATED](script, ctxt = ctx, version = V4, env = utils.environment) shouldBe CONST_BYTESTR(issue.id) } @@ -1401,7 +1393,7 @@ class IntegrationTest extends PropSpec with Inside { val ctx = WavesContext.build(Global, DirectiveSet(V4, Account, DApp).explicitGet(), fixBigScriptField = true) - genericEval[Environment, EVALUATED](script, ctxt = ctx, version = V4, env = utils.environment) shouldBe + genericEval[EVALUATED](script, ctxt = ctx, version = V4, env = utils.environment) shouldBe Right(CONST_BOOLEAN(true)) } @@ -2020,7 +2012,7 @@ class IntegrationTest extends PropSpec with Inside { property("different Lease action constructors") { val script = " Lease(Address(base58''), 1234567) == Lease(Address(base58''), 1234567, 0) " - genericEval[Environment, EVALUATED](script, ctxt = v5Ctx, version = V5, env = utils.environment) shouldBe + genericEval[EVALUATED](script, ctxt = v5Ctx, version = V5, env = utils.environment) shouldBe Right(CONST_BOOLEAN(true)) } @@ -2034,7 +2026,7 @@ class IntegrationTest extends PropSpec with Inside { | calculateLeaseId(Lease(Alias("alias"), 9876, 100)) == base58'$id2' && | base58'$id1' != base58'$id2' """.stripMargin - genericEval[Environment, EVALUATED](script, ctxt = v5Ctx, version = V5, env = utils.buildEnvironment(txId)) shouldBe + genericEval[EVALUATED](script, ctxt = v5Ctx, version = V5, env = utils.buildEnvironment(txId)) shouldBe Right(CONST_BOOLEAN(true)) } @@ -2042,9 +2034,9 @@ class IntegrationTest extends PropSpec with Inside { val script1 = s" calculateLeaseId(Lease(Address(base58'${"a" * 36}'), 1234567, 123)) " val script2 = s""" calculateLeaseId(Lease(Alias("${"a" * 31}"), 1234567, 123)) """ - genericEval[Environment, EVALUATED](script1, ctxt = v5Ctx, version = V5, env = utils.environment) should + genericEval[EVALUATED](script1, ctxt = v5Ctx, version = V5, env = utils.environment) should produce("Address bytes length=27 exceeds limit=26") - genericEval[Environment, EVALUATED](script2, ctxt = v5Ctx, version = V5, env = utils.environment) should + genericEval[EVALUATED](script2, ctxt = v5Ctx, version = V5, env = utils.environment) should produce("Alias name length=31 exceeds limit=30") } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ContractCompilerCompactorTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ContractCompilerCompactorTest.scala index 02d294b4951..2519a202b34 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ContractCompilerCompactorTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ContractCompilerCompactorTest.scala @@ -19,7 +19,6 @@ import com.wavesplatform.lang.v1.{CTX, compiler} import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, GlobalValNames, PureContext} import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.{Types, WavesContext} import com.wavesplatform.lang.v1.parser.{Expressions, Parser} -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.protobuf.dapp.DAppMeta import com.wavesplatform.protobuf.dapp.DAppMeta.CompactNameAndOriginalNamePair import com.wavesplatform.test.* diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ContractCompilerTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ContractCompilerTest.scala index 600f3229e63..0d27c21c56e 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ContractCompilerTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ContractCompilerTest.scala @@ -18,7 +18,6 @@ import com.wavesplatform.lang.v1.evaluator.FunctionIds import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.{FieldNames, Types, 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.{ContractLimits, compiler} import com.wavesplatform.protobuf.dapp.DAppMeta import com.wavesplatform.protobuf.dapp.DAppMeta.CallableFuncSignature @@ -30,8 +29,8 @@ class ContractCompilerTest extends PropSpec { Monoid .combineAll( Seq( - PureContext.build(version, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(com.wavesplatform.lang.Global, version).withEnvironment[Environment], + PureContext.build(version, useNewPowPrecision = true), + CryptoContext.build(com.wavesplatform.lang.Global, version), WavesContext.build( Global, DirectiveSet(version, Account, DAppType).explicitGet(), @@ -366,8 +365,8 @@ class ContractCompilerTest extends PropSpec { val ctx = Monoid .combineAll( Seq( - PureContext.build(V3, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(com.wavesplatform.lang.Global, V3).withEnvironment[Environment], + PureContext.build(V3, useNewPowPrecision = true), + CryptoContext.build(com.wavesplatform.lang.Global, V3), WavesContext.build( Global, DirectiveSet(V3, Account, DAppType).explicitGet(), @@ -494,8 +493,8 @@ class ContractCompilerTest extends PropSpec { val ctx = Monoid .combineAll( Seq( - PureContext.build(V3, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(com.wavesplatform.lang.Global, V3).withEnvironment[Environment], + PureContext.build(V3, useNewPowPrecision = true), + CryptoContext.build(com.wavesplatform.lang.Global, V3), WavesContext.build( Global, DirectiveSet(V3, Account, DAppType).explicitGet(), @@ -813,7 +812,7 @@ class ContractCompilerTest extends PropSpec { Parser.parseContract(script).get.value } val ctx = - PureContext.build(V4, useNewPowPrecision = true).withEnvironment[Environment] |+| + PureContext.build(V4, useNewPowPrecision = true) |+| WavesContext.build(Global, DirectiveSet(V4, Account, DAppType).explicitGet(), fixBigScriptField = true) compiler.ContractCompiler(ctx.compilerContext, expr, V4) shouldBe Symbol("right") @@ -823,8 +822,8 @@ class ContractCompilerTest extends PropSpec { val ctx = Monoid .combineAll( Seq( - PureContext.build(V3, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(com.wavesplatform.lang.Global, V3).withEnvironment[Environment], + PureContext.build(V3, useNewPowPrecision = true), + CryptoContext.build(com.wavesplatform.lang.Global, V3), WavesContext.build( Global, DirectiveSet(V3, Account, DAppType).explicitGet(), @@ -919,7 +918,7 @@ class ContractCompilerTest extends PropSpec { Parser.parseContract(script).get.value } val ctx = - PureContext.build(V5, useNewPowPrecision = true).withEnvironment[Environment] |+| + PureContext.build(V5, useNewPowPrecision = true) |+| WavesContext.build(Global, DirectiveSet(V5, Account, DAppType).explicitGet(), fixBigScriptField = true) compiler.ContractCompiler(ctx.compilerContext, expr, V5) shouldBe Symbol("right") @@ -957,7 +956,7 @@ class ContractCompilerTest extends PropSpec { Parser.parseContract(script).get.value } val ctx = - PureContext.build(V4, useNewPowPrecision = true).withEnvironment[Environment] |+| + PureContext.build(V4, useNewPowPrecision = true) |+| WavesContext.build(Global, DirectiveSet(V4, Account, DAppType).explicitGet(), fixBigScriptField = true) compiler.ContractCompiler(ctx.compilerContext, expr, V4) shouldBe Symbol("left") @@ -980,7 +979,7 @@ class ContractCompilerTest extends PropSpec { Parser.parseContract(script).get.value } val ctx = - PureContext.build(V4, useNewPowPrecision = true).withEnvironment[Environment] |+| + PureContext.build(V4, useNewPowPrecision = true) |+| WavesContext.build(Global, DirectiveSet(V4, Account, DAppType).explicitGet(), fixBigScriptField = true) val result = compiler.ContractCompiler(ctx.compilerContext, expr, V4) diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/DecompilerTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/DecompilerTest.scala index c9d0de0923e..2796ef338f5 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/DecompilerTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/DecompilerTest.scala @@ -16,7 +16,6 @@ import com.wavesplatform.lang.v1.evaluator.ctx.impl.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext import com.wavesplatform.lang.v1.parser.BinaryOperation.NE_OP import com.wavesplatform.lang.v1.parser.Parser -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.{FunctionHeader, compiler} import com.wavesplatform.lang.utils.getDecompilerContext import com.wavesplatform.protobuf.dapp.DAppMeta @@ -806,7 +805,7 @@ class DecompilerTest extends PropSpec { val ctx = Monoid.combine( - PureContext.build(V4, useNewPowPrecision = true).withEnvironment[Environment], + PureContext.build(V4, useNewPowPrecision = true), WavesContext.build(Global, DirectiveSet(V4, Account, DAppType).explicitGet(), fixBigScriptField = true) ) @@ -867,8 +866,8 @@ class DecompilerTest extends PropSpec { val ctx = Monoid.combineAll( Seq( - PureContext.build(V4, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, V4).withEnvironment[Environment], + PureContext.build(V4, useNewPowPrecision = true), + CryptoContext.build(Global, V4), WavesContext.build(Global, DirectiveSet(V4, Account, DAppType).explicitGet(), fixBigScriptField = true) ) ) @@ -908,8 +907,8 @@ class DecompilerTest extends PropSpec { val ctx = Monoid.combineAll( Seq( - PureContext.build(V4, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, V4).withEnvironment[Environment], + PureContext.build(V4, useNewPowPrecision = true), + CryptoContext.build(Global, V4), WavesContext.build(Global, DirectiveSet(V4, Account, DAppType).explicitGet(), fixBigScriptField = true) ) ) @@ -920,12 +919,6 @@ class DecompilerTest extends PropSpec { } property("V5 - new functions") { - val directives = - """ - | {-# STDLIB_VERSION 5 #-} - | {-#CONTENT_TYPE DAPP #-} - |""".stripMargin - val script = s""" | @Callable(i) @@ -934,20 +927,13 @@ class DecompilerTest extends PropSpec { | nil | } """.stripMargin + val dApp = TestCompiler(V5).compileContract( + """ + | {-# STDLIB_VERSION 5 #-} + | {-#CONTENT_TYPE DAPP #-} + |""".stripMargin ++ script) - val parsedExpr = Parser.parseContract(directives ++ script).get.value - - val ctx = - Monoid.combineAll( - Seq( - PureContext.build(V5, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, V5).withEnvironment[Environment], - WavesContext.build(Global, DirectiveSet(V5, Account, DAppType).explicitGet(), fixBigScriptField = true) - ) - ) - - val dApp = compiler.ContractCompiler(ctx.compilerContext, parsedExpr, V5).explicitGet() - val res = Decompiler(dApp, ctx.decompilerContext, V5) + val res = Decompiler(dApp.expr, TestCompiler(V5).dappContext.decompilerContext, V5) res shouldEq script } @@ -974,8 +960,8 @@ class DecompilerTest extends PropSpec { val ctx = Monoid.combineAll( Seq( - PureContext.build(V5, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, V5).withEnvironment[Environment], + PureContext.build(V5, useNewPowPrecision = true), + CryptoContext.build(Global, V5), WavesContext.build(Global, DirectiveSet(V5, Account, DAppType).explicitGet(), fixBigScriptField = true) ) ) @@ -1019,8 +1005,8 @@ class DecompilerTest extends PropSpec { val ctx = Monoid.combineAll( Seq( - PureContext.build(V5, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, V5).withEnvironment[Environment], + PureContext.build(V5, useNewPowPrecision = true), + CryptoContext.build(Global, V5), WavesContext.build(Global, DirectiveSet(V5, Account, DAppType).explicitGet(), fixBigScriptField = true) ) ) diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerV1Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerV1Test.scala index d2660022d26..f339b28f3d3 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerV1Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerV1Test.scala @@ -21,7 +21,6 @@ import com.wavesplatform.lang.v1.parser.BinaryOperation.SUM_OP import com.wavesplatform.lang.v1.parser.Expressions.Pos import com.wavesplatform.lang.v1.parser.Expressions.Pos.AnyPos import com.wavesplatform.lang.v1.parser.{Expressions, Parser} -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.{ContractLimits, FunctionHeader, compiler} import com.wavesplatform.lang.{Common, Global} import com.wavesplatform.test.* @@ -309,8 +308,8 @@ class ExpressionCompilerV1Test extends PropSpec { val ctx = Monoid .combineAll( Seq( - PureContext.build(V4, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(com.wavesplatform.lang.Global, V4).withEnvironment[Environment], + PureContext.build(V4, useNewPowPrecision = true), + CryptoContext.build(com.wavesplatform.lang.Global, V4), WavesContext.build( Global, DirectiveSet(V4, Account, Expression).explicitGet(), diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ScriptPreprocessorTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ScriptPreprocessorTest.scala index 8ce9d375f90..b85a0252b6b 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ScriptPreprocessorTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ScriptPreprocessorTest.scala @@ -3,13 +3,13 @@ package com.wavesplatform.lang.compiler import cats.Id import cats.implicits.* import cats.kernel.Monoid +import com.wavesplatform.lang.Common import com.wavesplatform.lang.directives.values.V3 import com.wavesplatform.lang.directives.{Directive, DirectiveParser} import com.wavesplatform.lang.script.ScriptPreprocessor import com.wavesplatform.lang.v1.CTX import com.wavesplatform.lang.v1.compiler.ExpressionCompiler import com.wavesplatform.lang.v1.compiler.Terms.{CONST_BOOLEAN, EVALUATED} -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext import com.wavesplatform.lang.v1.evaluator.EvaluatorV1 import com.wavesplatform.lang.v1.evaluator.EvaluatorV1.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext @@ -18,7 +18,7 @@ import com.wavesplatform.lang.v1.testing.ScriptGenParser import com.wavesplatform.test.* class ScriptPreprocessorTest extends PropSpec with ScriptGenParser { - private val evaluator = new EvaluatorV1[Id, NoContext]() + private val evaluator = new EvaluatorV1[Id]() private def processAndEval(src: String, libraries: Map[String, String]): Either[String, EVALUATED] = for { @@ -30,9 +30,9 @@ class ScriptPreprocessorTest extends PropSpec with ScriptGenParser { private def eval(code: String): Either[String, EVALUATED] = { val untyped = Parser.parseExpr(code).get.value - val ctx: CTX[NoContext] = Monoid.combineAll(Seq(PureContext.build(V3, useNewPowPrecision = true))) + val ctx: CTX = Monoid.combineAll(Seq(PureContext.build(V3, useNewPowPrecision = true))) val typed = ExpressionCompiler(ctx.compilerContext, untyped) - typed.flatMap(v => evaluator[EVALUATED](ctx.evaluationContext, v._1).leftMap(_.toString)) + typed.flatMap(v => evaluator[EVALUATED](ctx.evaluationContext(Common.emptyBlockchainEnvironment()), v._1).leftMap(_.toString)) } property("multiple libraries") { diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/package.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/package.scala index eb3dc24a3bf..8b047bd2c63 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/package.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/package.scala @@ -1,59 +1,60 @@ package com.wavesplatform.lang import cats.kernel.Monoid -import com.wavesplatform.common.utils._ +import com.wavesplatform.common.utils.* import com.wavesplatform.lang.Common.multiplierFunction import com.wavesplatform.lang.directives.DirectiveSet -import com.wavesplatform.lang.directives.values._ +import com.wavesplatform.lang.directives.values.* 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.compiler.Terms.* +import com.wavesplatform.lang.v1.compiler.Types.* +import com.wavesplatform.lang.v1.compiler.{CompilerContext, Types} import com.wavesplatform.lang.v1.evaluator.ContextfulVal import com.wavesplatform.lang.v1.evaluator.ctx.NativeFunction +import com.wavesplatform.lang.v1.evaluator.ctx.impl.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext -import com.wavesplatform.lang.v1.evaluator.ctx.impl.{PureContext, _} -import com.wavesplatform.lang.v1.traits.Environment package object compiler { - val pointType = CASETYPEREF("Point", List("x" -> LONG, "y" -> LONG)) - val listOfLongs = LIST - val idT = NativeFunction[NoContext]("idT", 1, 10000: Short, TYPEPARAM('T'), ("p1", TYPEPARAM('T'))) { + val pointType: CASETYPEREF = CASETYPEREF("Point", List("x" -> LONG, "y" -> LONG)) + val listOfLongs: Types.LIST.type = LIST + val idT: NativeFunction = NativeFunction("idT", 1, 10000: Short, TYPEPARAM('T'), ("p1", TYPEPARAM('T'))) { case a :: Nil => Right(a) case _ => ??? } - val returnsListLong = - NativeFunction[NoContext]("undefinedOptionLong", 1, 1002: Short, LIST(LONG): TYPE) { case _ => ??? } - val idOptionLong = - NativeFunction[NoContext]("idOptionLong", 1, 1003: Short, UNIT, ("opt", UNION(LONG, UNIT))) { case _ => Right(unit) } - val functionWithTwoPrarmsOfTheSameType = - NativeFunction[NoContext]("functionWithTwoPrarmsOfTheSameType", 1, 1005: Short, TYPEPARAM('T'), ("p1", TYPEPARAM('T')), ("p2", TYPEPARAM('T'))) { - case l => Right(l.head) - } + val returnsListLong: NativeFunction = + NativeFunction("undefinedOptionLong", 1, 1002: Short, LIST(LONG): TYPE)(_ => ???) + val idOptionLong: NativeFunction = + NativeFunction("idOptionLong", 1, 1003: Short, UNIT, ("opt", UNION(LONG, UNIT)))(_ => Right(unit)) + val functionWithTwoPrarmsOfTheSameType: NativeFunction = + NativeFunction("functionWithTwoPrarmsOfTheSameType", 1, 1005: Short, TYPEPARAM('T'), ("p1", TYPEPARAM('T')), ("p2", TYPEPARAM('T')))(l => + Right(l.head) + ) private val arr = ARR(IndexedSeq[EVALUATED](Common.pointAInstance, Common.pointAInstance), false).explicitGet() - def getTestContext(v: StdLibVersion, t: ScriptType = Account): CTX[Environment] = { + def getTestContext(v: StdLibVersion, t: ScriptType = Account): CTX = { Monoid - .combineAll(Seq( - PureContext.build(v, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, v).withEnvironment[Environment], - WavesContext.build(Global, DirectiveSet(v, t, Expression).explicitGet(), fixBigScriptField = true), - CTX[NoContext]( - Seq(pointType, Common.pointTypeA, Common.pointTypeB, Common.pointTypeC), - Map( - ("p", (Common.AorB, null)), - ("tv", (Common.AorBorC, null)), - ("l", (LIST(LONG), ContextfulVal.pure[NoContext](ARR(IndexedSeq(CONST_LONG(1L), CONST_LONG(2L)), false).explicitGet()))), - ("lpa", (LIST(Common.pointTypeA), ContextfulVal.pure[NoContext](arr))), - ("lpabc", (LIST(Common.AorBorC), ContextfulVal.pure[NoContext](arr))) - ), - Array(multiplierFunction, functionWithTwoPrarmsOfTheSameType, idT, returnsListLong, idOptionLong) - ).withEnvironment[Environment] - )) + .combineAll( + Seq( + PureContext.build(v, useNewPowPrecision = true), + CryptoContext.build(Global, v), + WavesContext.build(Global, DirectiveSet(v, t, Expression).explicitGet(), fixBigScriptField = true), + CTX( + Seq(pointType, Common.pointTypeA, Common.pointTypeB, Common.pointTypeC), + Map( + ("p", (Common.AorB, null)), + ("tv", (Common.AorBorC, null)), + ("l", (LIST(LONG), ContextfulVal.pure(ARR(IndexedSeq(CONST_LONG(1L), CONST_LONG(2L)), false).explicitGet()))), + ("lpa", (LIST(Common.pointTypeA), ContextfulVal.pure(arr))), + ("lpabc", (LIST(Common.AorBorC), ContextfulVal.pure(arr))) + ), + Array(multiplierFunction, functionWithTwoPrarmsOfTheSameType, idT, returnsListLong, idOptionLong) + ) + ) + ) } - val compilerContext = getTestContext(V3).compilerContext - val compilerContextV4 = getTestContext(V4).compilerContext + val compilerContext: CompilerContext = getTestContext(V3).compilerContext + val compilerContextV4: CompilerContext = getTestContext(V4).compilerContext } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/doc/FunctionComplexityDocTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/doc/FunctionComplexityDocTest.scala index 05f07ab6a8e..cb91b564932 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/doc/FunctionComplexityDocTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/doc/FunctionComplexityDocTest.scala @@ -10,7 +10,6 @@ import com.wavesplatform.lang.v1.compiler.Terms.{CONST_STRING, FUNCTION_CALL} import com.wavesplatform.lang.v1.compiler.UtilityFunctionPrefix import com.wavesplatform.lang.v1.estimator.v3.ScriptEstimatorV3 import com.wavesplatform.lang.v1.evaluator.ctx.BaseFunction -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.test.PropSpec import org.scalatest.exceptions.TestFailedException @@ -34,7 +33,7 @@ class FunctionComplexityDocTest extends PropSpec { private val allDataStorageFunctions = baseDataStorageFunctions ++ baseDataStorageFunctions.map(_ + "Value") - private def check(functions: Array[BaseFunction[Environment]], ds: DirectiveSet): Unit = { + private def check(functions: Array[BaseFunction], ds: DirectiveSet): Unit = { val docCosts = DocSource.funcData.collect { case ((name, signature, version), (_, _, complexity)) if version == ds.stdLibVersion.id => ((name, signature), complexity) diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/doc/VarsDocTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/doc/VarsDocTest.scala index e89be35fe3d..fae87bb394c 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/doc/VarsDocTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/doc/VarsDocTest.scala @@ -9,14 +9,13 @@ import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.v1.CTX import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.test.* class VarsDocTest extends PropSpec { - def buildFullContext(ds: DirectiveSet): CTX[Environment] = { + def buildFullContext(ds: DirectiveSet): CTX = { val wavesCtx = WavesContext.build(Global, ds, fixBigScriptField = true) - val cryptoCtx = CryptoContext.build(Global, ds.stdLibVersion).withEnvironment[Environment] - val pureCtx = PureContext.build(ds.stdLibVersion, useNewPowPrecision = true).withEnvironment[Environment] + val cryptoCtx = CryptoContext.build(Global, ds.stdLibVersion) + val pureCtx = PureContext.build(ds.stdLibVersion, useNewPowPrecision = true) pureCtx |+| cryptoCtx |+| wavesCtx } @@ -37,7 +36,7 @@ class VarsDocTest extends PropSpec { .map(DirectiveSet(_, Account, Expression).explicitGet()) .toSeq - def varsDoc(ctx: CTX[Environment], ver: StdLibVersion): Iterable[(Option[String], String)] = + def varsDoc(ctx: CTX, ver: StdLibVersion): Iterable[(Option[String], String)] = ctx.vars.keys .map(k => (DocSource.varData.get((k, ver.value.asInstanceOf[Int])), k)) } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorTestBase.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorTestBase.scala index 5eae03001d6..daa84560b40 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorTestBase.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorTestBase.scala @@ -8,13 +8,11 @@ import com.wavesplatform.lang.utils.functionCosts import com.wavesplatform.lang.v1.compiler.ExpressionCompiler import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.estimator.ScriptEstimator -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext import com.wavesplatform.lang.v1.evaluator.ContextfulVal import com.wavesplatform.lang.v1.evaluator.FunctionIds.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.{Types, WavesContext} import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} import com.wavesplatform.lang.v1.parser.Parser -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.{CTX, FunctionHeader} import com.wavesplatform.lang.{Common, Global, utils} import com.wavesplatform.test.* @@ -22,9 +20,9 @@ import monix.eval.Coeval class ScriptEstimatorTestBase(estimators: ScriptEstimator*) extends PropSpec { - val Plus = FunctionHeader.Native(SUM_LONG) - val Minus = FunctionHeader.Native(SUB_LONG) - val Gt = FunctionHeader.Native(GT_LONG) + val Plus: FunctionHeader.Native = FunctionHeader.Native(SUM_LONG) + val Minus: FunctionHeader.Native = FunctionHeader.Native(SUB_LONG) + val Gt: FunctionHeader.Native = FunctionHeader.Native(GT_LONG) val customFunctionCosts: Map[FunctionHeader, Coeval[Long]] = Map[FunctionHeader, Long](Plus -> 100, Minus -> 10, Gt -> 10).view.mapValues(Coeval.now).toMap @@ -39,14 +37,14 @@ class ScriptEstimatorTestBase(estimators: ScriptEstimator*) extends PropSpec { Monoid .combineAll( Seq( - PureContext.build(version, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, version).withEnvironment[Environment], + PureContext.build(version, useNewPowPrecision = true), + CryptoContext.build(Global, version), WavesContext.build(Global, DirectiveSet(version, Account, DApp).explicitGet(), fixBigScriptField = true), - CTX[NoContext]( + CTX( Seq(transactionType), - Map(("tx", (transactionType, ContextfulVal.pure[NoContext](tx)))), + Map(("tx", (transactionType, ContextfulVal.pure(tx)))), Array.empty - ).withEnvironment[Environment] + ) ) ) } @@ -72,7 +70,7 @@ class ScriptEstimatorTestBase(estimators: ScriptEstimator*) extends PropSpec { } protected def estimate(script: String): Either[String, Long] = { - val expr = compile(script)(V6) + val expr = compile(script)(V6) val results = estimators.map(_(lets, functionCosts(V6), expr)) if (results.distinct.length == 1) results.head @@ -85,12 +83,11 @@ class ScriptEstimatorTestBase(estimators: ScriptEstimator*) extends PropSpec { script2: EXPR, functionCosts: Map[FunctionHeader, Coeval[Long]] = v3FunctionCosts ): Either[String, Long] = { - val results = estimators.map( - e => - for { - cost2 <- e(lets, functionCosts, script2) - cost1 <- e(lets, functionCosts, script1) - } yield cost2 - cost1 + val results = estimators.map(e => + for { + cost2 <- e(lets, functionCosts, script2) + cost1 <- e(lets, functionCosts, script1) + } yield cost2 - cost1 ) if (results.distinct.length == 1) results.head diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/package.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/package.scala index 505857d05da..293898650d7 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/package.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/package.scala @@ -11,12 +11,11 @@ import com.wavesplatform.lang.v1.evaluator.ContractEvaluator.LogExtraInfo import com.wavesplatform.lang.v1.evaluator.EvaluatorV2 import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext -import com.wavesplatform.lang.v1.traits.Environment import monix.eval.Coeval package object estimator { private val ctx = - PureContext.build(V3, useNewPowPrecision = true).withEnvironment[Environment] |+| + PureContext.build(V3, useNewPowPrecision = true) |+| WavesContext.build(Global, DirectiveSet.contractDirectiveSet, fixBigScriptField = true) private val environment = Common.emptyBlockchainEnvironment() diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorSpec.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorSpec.scala index 8a56d49c8e4..34d137d865a 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorSpec.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorSpec.scala @@ -1,18 +1,18 @@ package com.wavesplatform.lang.evaluator -import cats.implicits.* import cats.Id +import cats.implicits.* import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.lang.{Common, ExecutionError} -import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet} import com.wavesplatform.lang.directives.values.* +import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet} +import com.wavesplatform.lang.miniev.{ComplexityLimit, Ev} import com.wavesplatform.lang.utils.lazyContexts import com.wavesplatform.lang.v1.compiler.ExpressionCompiler import com.wavesplatform.lang.v1.compiler.Terms.{EVALUATED, EXPR} -import com.wavesplatform.lang.v1.evaluator.ContractEvaluator.LogExtraInfo -import com.wavesplatform.lang.v1.evaluator.{EvaluatorV2, Log} +import com.wavesplatform.lang.v1.evaluator.Log import com.wavesplatform.lang.v1.parser.Parser import com.wavesplatform.lang.v1.testing.ScriptGen +import com.wavesplatform.lang.{Common, ExecutionError} import com.wavesplatform.test.PropSpec import org.scalatest.Inside import org.scalatest.exceptions.TestFailedException @@ -41,7 +41,7 @@ abstract class EvaluatorSpec extends PropSpec with ScriptGen with Inside { (result, Int.MaxValue - unused) } - private def evalInternal[A]( + private def evalInternal( toExpr: StdLibVersion => Either[String, EXPR], startVersion: StdLibVersion, endVersion: StdLibVersion, @@ -75,7 +75,7 @@ abstract class EvaluatorSpec extends PropSpec with ScriptGen with Inside { private def evalExpr(expr: EXPR, version: StdLibVersion, useNewPowPrecision: Boolean): (Log[Id], Int, Either[ExecutionError, EVALUATED]) = { val ctx = lazyContexts((DirectiveSet(version, Account, Expression).explicitGet(), useNewPowPrecision, true)).value() val evalCtx = ctx.evaluationContext(Common.emptyBlockchainEnvironment()) - EvaluatorV2.applyCompleted(evalCtx, expr, LogExtraInfo(), version, correctFunctionCallScope = true, newMode = true, enableExecutionLog = false) + Ev.run(expr, evalCtx, ComplexityLimit.Unlimited, newMode = true, version) } def compile(code: String, version: StdLibVersion): Either[String, EXPR] = { diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1CaseObjField.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1CaseObjField.scala index b9a9b8650c3..a9ea90c068c 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1CaseObjField.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1CaseObjField.scala @@ -2,21 +2,26 @@ package com.wavesplatform.lang.evaluator import cats.Id import cats.kernel.Monoid +import com.wavesplatform.lang.Common import com.wavesplatform.lang.Common.* import com.wavesplatform.lang.Testing.* import com.wavesplatform.lang.directives.values.V1 +import com.wavesplatform.lang.v1.CTX import com.wavesplatform.lang.v1.compiler.Terms.* -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext +import com.wavesplatform.lang.v1.evaluator.ContextfulVal import com.wavesplatform.lang.v1.evaluator.ctx.* -import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext.* import com.wavesplatform.test.PropSpec class EvaluatorV1CaseObjField extends PropSpec { - - def context(p: CaseObj): EvaluationContext[NoContext, Id] = - Monoid.combine(PureContext.build(V1, useNewPowPrecision = true).evaluationContext, sampleUnionContext(p)) + def context(p: CaseObj): EvaluationContext[Id] = + Monoid + .combine( + PureContext.build(V1, useNewPowPrecision = true), + CTX(Seq.empty, Map("p" -> (p.caseType, ContextfulVal.pure(p))), Array.empty) + ) + .evaluationContext(Common.emptyBlockchainEnvironment()) property("case custom type field access") { ev[CONST_LONG]( diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1V2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1V2Test.scala index b6943d8fd98..0f43619abfe 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1V2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1V2Test.scala @@ -1,10 +1,10 @@ package com.wavesplatform.lang.evaluator import java.nio.ByteBuffer -import cats.Id -import cats.data.EitherT + import cats.kernel.Monoid import cats.syntax.bifunctor.* +import cats.{Eval, Id} import com.google.common.io.BaseEncoding import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.{Base58, Base64, EitherExt2} @@ -13,11 +13,11 @@ import com.wavesplatform.lang.Common.* import com.wavesplatform.lang.Testing.* import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet} +import com.wavesplatform.lang.miniev.{ComplexityLimit, Ev} import com.wavesplatform.lang.v1.FunctionHeader.{Native, User} import com.wavesplatform.lang.v1.compiler.ExpressionCompiler 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.ContractEvaluator.LogExtraInfo import com.wavesplatform.lang.v1.evaluator.EvaluatorV1.* import com.wavesplatform.lang.v1.evaluator.FunctionIds.* @@ -26,10 +26,9 @@ import com.wavesplatform.lang.v1.evaluator.ctx.impl.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.converters.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext -import com.wavesplatform.lang.v1.evaluator.{Contextful, ContextfulVal, EvaluatorV1, EvaluatorV2, FunctionIds, Log} -import com.wavesplatform.lang.v1.traits.Environment +import com.wavesplatform.lang.v1.evaluator.{ContextfulVal, EvaluatorV1, EvaluatorV2, FunctionIds, Log} import com.wavesplatform.lang.v1.{CTX, ContractLimits, FunctionHeader} -import com.wavesplatform.lang.{Common, EvalF, ExecutionError, Global} +import com.wavesplatform.lang.{Common, CommonError, ExecutionError, Global} import com.wavesplatform.test.* import org.scalacheck.{Arbitrary, Gen} import org.scalatest.EitherValues @@ -44,11 +43,11 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { val blockBuilder: Gen[(LET, EXPR) => EXPR] = Gen.oneOf(true, false).map(if (_) BLOCK.apply else LET_BLOCK.apply) - private def defaultFullContext(implicit version: StdLibVersion): CTX[Environment] = + private def defaultFullContext(implicit version: StdLibVersion): CTX = Monoid.combineAll( Seq( - defaultCryptoContext(version).withEnvironment[Environment], - pureContext(version).withEnvironment[Environment], + defaultCryptoContext(version), + pureContext(version), WavesContext.build( Global, DirectiveSet(version, Account, Expression).explicitGet(), @@ -57,15 +56,15 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { ) ) - private def pureEvalContext(implicit version: StdLibVersion): EvaluationContext[NoContext, Id] = - PureContext.build(version, useNewPowPrecision = true).evaluationContext + private def pureEvalContext(implicit version: StdLibVersion): EvaluationContext[Id] = + pureContext.evaluationContext(Common.emptyBlockchainEnvironment()) - private val defaultEvaluator = new EvaluatorV1[Id, Environment]() + private val defaultEvaluator = new EvaluatorV1[Id]() - private def evalV1[T <: EVALUATED](context: EvaluationContext[Environment, Id], expr: EXPR): Either[ExecutionError, T] = + private def evalV1[T <: EVALUATED](context: EvaluationContext[Id], expr: EXPR): Either[ExecutionError, T] = defaultEvaluator[T](context, expr) - private def evalV2[T <: EVALUATED](context: EvaluationContext[Environment, Id], expr: EXPR): Either[ExecutionError, T] = + private def evalV2[T <: EVALUATED](context: EvaluationContext[Id], expr: EXPR): Either[ExecutionError, T] = EvaluatorV2 .applyCompleted( context, @@ -79,7 +78,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { ._3 .asInstanceOf[Either[ExecutionError, T]] - private def eval[T <: EVALUATED](context: EvaluationContext[Environment, Id], expr: EXPR): Either[ExecutionError, T] = { + private def eval[T <: EVALUATED](context: EvaluationContext[Id], expr: EXPR): Either[ExecutionError, T] = { val evaluatorV1Result = evalV1[T](context, expr) val evaluatorV2Result = evalV2[T](context, expr) @@ -87,21 +86,12 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { evaluatorV1Result } - private def evalPure[T <: EVALUATED](context: EvaluationContext[NoContext, Id] = pureEvalContext, expr: EXPR): Either[ExecutionError, T] = - eval[T](context.asInstanceOf[EvaluationContext[Environment, Id]], expr) + private def evalPure[T <: EVALUATED](context: EvaluationContext[Id] = pureEvalContext, expr: EXPR): Either[ExecutionError, T] = + eval[T](context, expr) - private def evalWithLogging(context: EvaluationContext[Environment, Id], expr: EXPR): Either[(ExecutionError, Log[Id]), (EVALUATED, Log[Id])] = { + private def evalWithLogging(context: EvaluationContext[Id], expr: EXPR): Either[(ExecutionError, Log[Id]), (EVALUATED, Log[Id])] = { val evaluatorV1Result = defaultEvaluator.applyWithLogging[EVALUATED](context, expr) - val (evaluatorV2Log, _, evaluatorV2Result) = - EvaluatorV2.applyCompleted( - context, - expr, - LogExtraInfo(), - implicitly[StdLibVersion], - correctFunctionCallScope = true, - newMode = true, - enableExecutionLog = true - ) + val (evaluatorV2Log, _, evaluatorV2Result) = Ev.run(expr, context, ComplexityLimit.Unlimited, true, version) evaluatorV2Result shouldBe evaluatorV1Result.bimap(_._1, _._1) evaluatorV2Log should contain allElementsOf evaluatorV1Result.fold(_._2, _._2) @@ -119,7 +109,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("return error and log of failed evaluation") { forAll(blockBuilder) { block => val result = evalWithLogging( - pureEvalContext.asInstanceOf[EvaluationContext[Environment, Id]], + pureEvalContext, expr = block( LET("x", CONST_LONG(3)), block( @@ -229,15 +219,16 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { val pointType = CASETYPEREF("Point", List("X" -> LONG, "Y" -> LONG)) val pointInstance = CaseObj(pointType, Map("X" -> 3L, "Y" -> 4L)) evalPure[EVALUATED]( - context = Monoid.combine( - pureEvalContext, - EvaluationContext[NoContext, Id]( - Contextful.empty[Id], - typeDefs = Map.empty, - letDefs = Map(("p", LazyVal.fromEvaluated[Id](pointInstance))), - functions = Map.empty + context = Monoid + .combine( + pureContext, + CTX( + Seq.empty, + Map("p" -> (pointType, ContextfulVal.pure(pointInstance))), + Array.empty + ) ) - ), + .evaluationContext(Common.emptyBlockchainEnvironment()), expr = FUNCTION_CALL(sumLong.header, List(GETTER(REF("p"), "X"), CONST_LONG(2))) ) shouldBe evaluated(5) } @@ -255,18 +246,20 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("lazy let evaluation doesn't throw if not used") { val pointType = CASETYPEREF("Point", List(("X", LONG), ("Y", LONG))) val pointInstance = CaseObj(pointType, Map("X" -> 3L, "Y" -> 4L)) - val context = Monoid.combine( - pureEvalContext, - EvaluationContext[NoContext, Id]( - Contextful.empty[Id], - typeDefs = Map.empty, - letDefs = Map( - ("p", LazyVal.fromEvaluated[Id](pointInstance)), - ("badVal", LazyVal.apply[Id](EitherT.leftT[({ type L[A] = EvalF[Id, A] })#L, EVALUATED]("Error"))) - ), - functions = Map.empty + import cats.syntax.applicative.* + val context = Monoid + .combine( + pureContext, + CTX( + Seq.empty, + Map( + "p" -> (pointType, ContextfulVal.pure(pointInstance)), + "badVal" -> (NOTHING, ContextfulVal.fromEval(Left(CommonError("Error")).pure[Eval])) + ), + Array.empty + ) ) - ) + .evaluationContext(Common.emptyBlockchainEnvironment()) forAll(blockBuilder) { block => evalPure[EVALUATED]( context = context, @@ -279,22 +272,17 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { forAll(blockBuilder) { block => var functionEvaluated = 0 - val f = NativeFunction[NoContext]("F", 1: Long, 258: Short, LONG: TYPE, Seq(("_", LONG))*) { _ => + val f = NativeFunction("F", 1: Long, 258: Short, LONG: TYPE, Seq(("_", LONG))*) { _ => functionEvaluated = functionEvaluated + 1 evaluated(1L) } val context = Monoid .combine( - pureEvalContext, - EvaluationContext[NoContext, Id]( - Contextful.empty[Id], - typeDefs = Map.empty, - letDefs = Map.empty, - functions = Map(f.header -> f) - ) + pureContext, + CTX(Seq.empty, Map.empty, Array(f)) ) - .asInstanceOf[EvaluationContext[Environment, Id]] + .evaluationContext(Common.emptyBlockchainEnvironment()) val expr = block(LET("X", FUNCTION_CALL(f.header, List(CONST_LONG(1000)))), FUNCTION_CALL(sumLong.header, List(REF("X"), REF("X")))) @@ -312,6 +300,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { val fooInstance = CaseObj(fooType, Map("bar" -> "bAr", "buz" -> 1L)) val context = EvaluationContext.build( + Common.emptyBlockchainEnvironment(), typeDefs = Map.empty, letDefs = Map("fooInstance" -> LazyVal.fromEvaluated[Id](fooInstance)), functions = Seq() @@ -324,11 +313,11 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("successful on function call getter evaluation") { val fooType = CASETYPEREF("Foo", List(("bar", STRING), ("buz", LONG))) - val fooCtor = NativeFunction[NoContext]("createFoo", 1: Long, 259: Short, fooType, List.empty*)(_ => - evaluated(CaseObj(fooType, Map("bar" -> "bAr", "buz" -> 1L))) - ) + val fooCtor = + NativeFunction("createFoo", 1: Long, 259: Short, fooType, List.empty*)(_ => evaluated(CaseObj(fooType, Map("bar" -> "bAr", "buz" -> 1L)))) val context = EvaluationContext.build( + Common.emptyBlockchainEnvironment(), typeDefs = Map.empty, letDefs = Map.empty, functions = Seq(fooCtor) @@ -341,7 +330,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("successful on block getter evaluation") { val fooType = CASETYPEREF("Foo", List(("bar", STRING), ("buz", LONG))) - val fooCtor = NativeFunction[NoContext]("createFoo", 1: Long, 259: Short, fooType, List.empty*) { _ => + val fooCtor = NativeFunction("createFoo", 1: Long, 259: Short, fooType, List.empty*) { _ => evaluated( CaseObj( fooType, @@ -353,12 +342,13 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { ) } val fooTransform = - NativeFunction[NoContext]("transformFoo", 1: Long, 260: Short, fooType, ("foo", fooType)) { + NativeFunction("transformFoo", 1: Long, 260: Short, fooType, ("foo", fooType)) { case (fooObj: CaseObj) :: Nil => evaluated(CaseObj(fooObj.caseType, fooObj.fields.updated("bar", "TRANSFORMED_BAR"))) case _ => ??? } val context = EvaluationContext.build( + Common.emptyBlockchainEnvironment(), typeDefs = Map.empty, letDefs = Map.empty, functions = Seq(fooCtor, fooTransform) @@ -379,6 +369,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("successful on simple function evaluation") { evalPure[EVALUATED]( context = EvaluationContext.build( + Common.emptyBlockchainEnvironment(), typeDefs = Map.empty, letDefs = Map.empty, functions = Seq(multiplierFunction) @@ -482,7 +473,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("dropRightBytes(ByteStr, Long) works as the native one") { forAll(genBytesAndNumber) { case (xs, number) => val expr = FUNCTION_CALL(Native(FunctionIds.DROP_RIGHT_BYTES), List(CONST_BYTESTR(xs).explicitGet(), CONST_LONG(number))) - val actual = evalPure[EVALUATED](pureContext(V6).evaluationContext, expr).leftMap(_.message) + val actual = evalPure[EVALUATED](pureContext(V6).evaluationContext(Common.emptyBlockchainEnvironment()), expr).leftMap(_.message) val limit = 165947 actual shouldBe ( if (number < 0) @@ -498,7 +489,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("takeRightBytes(ByteStr, Long) works as the native one") { forAll(genBytesAndNumber) { case (xs, number) => val expr = FUNCTION_CALL(Native(FunctionIds.TAKE_RIGHT_BYTES), List(CONST_BYTESTR(xs).explicitGet(), CONST_LONG(number))) - val actual = evalPure[EVALUATED](pureContext(V6).evaluationContext, expr).leftMap(_.message) + val actual = evalPure[EVALUATED](pureContext(V6).evaluationContext(Common.emptyBlockchainEnvironment()), expr).leftMap(_.message) val limit = 165947 actual shouldBe ( if (number < 0) @@ -564,7 +555,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { forAll(gen) { xs => val expr = FUNCTION_CALL(FunctionHeader.Native(FROMBASE58), List(CONST_STRING(xs).explicitGet())) - val actual = evalPure[EVALUATED](defaultCryptoContext.evaluationContext, expr) + val actual = evalPure[EVALUATED](defaultCryptoContext.evaluationContext(Common.emptyBlockchainEnvironment()), expr) actual shouldBe evaluated(ByteStr(Base58.tryDecodeWithLimit(xs).get)) } } @@ -578,7 +569,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { forAll(gen) { xs => val expr = FUNCTION_CALL(FunctionHeader.Native(FROMBASE58), List(CONST_STRING(xs).explicitGet())) - evalPure(defaultCryptoContext.evaluationContext, expr) should produce("base58Decode input exceeds 100") + evalPure(defaultCryptoContext.evaluationContext(Common.emptyBlockchainEnvironment()), expr) should produce("base58Decode input exceeds 100") } } @@ -590,7 +581,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { forAll(gen) { xs => val expr = FUNCTION_CALL(FunctionHeader.Native(FROMBASE64), List(CONST_STRING(xs).explicitGet())) - val actual = evalPure[EVALUATED](defaultCryptoContext.evaluationContext, expr) + val actual = evalPure[EVALUATED](defaultCryptoContext.evaluationContext(Common.emptyBlockchainEnvironment()), expr) actual shouldBe evaluated(ByteStr(Base64.tryDecode(xs).get)) } } @@ -603,7 +594,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { forAll(gen) { xs => val expr = FUNCTION_CALL(FunctionHeader.Native(FROMBASE64), List(CONST_STRING(xs).explicitGet())) - val actual = evalPure[EVALUATED](defaultCryptoContext.evaluationContext, expr) + val actual = evalPure[EVALUATED](defaultCryptoContext.evaluationContext(Common.emptyBlockchainEnvironment()), expr) actual shouldBe evaluated(ByteStr(Base64.tryDecode(xs).get)) } } @@ -621,7 +612,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { FUNCTION_CALL(FunctionHeader.Native(TOBASE16), List(CONST_BYTESTR(ByteStr(xs)).explicitGet())) ) ) - val actual = evalPure[EVALUATED](defaultCryptoContext.evaluationContext, expr) + val actual = evalPure[EVALUATED](defaultCryptoContext.evaluationContext(Common.emptyBlockchainEnvironment()), expr) actual shouldBe evaluated(ByteStr(xs)) } } @@ -760,15 +751,15 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { } private def hashTest(bodyBytes: Array[Byte], hash: String, lim: Int)(implicit version: StdLibVersion): Either[ExecutionError, ByteStr] = { - val vars: Map[String, (FINAL, ContextfulVal[NoContext])] = Map( - ("b", (BYTESTR, ContextfulVal.pure[NoContext](CONST_BYTESTR(ByteStr(bodyBytes), limit = CONST_BYTESTR.DataTxSize).explicitGet()))) + val vars: Map[String, (FINAL, ContextfulVal)] = Map( + ("b", (BYTESTR, ContextfulVal.pure(CONST_BYTESTR(ByteStr(bodyBytes), limit = CONST_BYTESTR.DataTxSize).explicitGet()))) ) - val context: CTX[NoContext] = Monoid.combineAll( + val context: CTX = Monoid.combineAll( Seq( pureContext, defaultCryptoContext, - CTX[NoContext](Seq(), vars, Array.empty[BaseFunction[NoContext]]) + CTX(Seq(), vars, Array.empty[BaseFunction]) ) ) @@ -779,7 +770,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { .explicitGet() evalPure[EVALUATED]( - context = context.evaluationContext[Id], + context = context.evaluationContext(Common.emptyBlockchainEnvironment()), expr = expr ).map { case CONST_BYTESTR(b) => b @@ -837,17 +828,15 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { ) ) - val context = Monoid.combineAll( - Seq( - pureEvalContext, - defaultCryptoContext.evaluationContext[Id], - EvaluationContext.build( - typeDefs = Map.empty, - letDefs = Map("tx" -> LazyVal.fromEvaluated[Id](txObj)), - functions = Seq.empty + val context = Monoid + .combineAll( + Seq( + pureContext, + defaultCryptoContext, + CTX(Seq.empty, Map("tx" -> (txType, ContextfulVal.pure(txObj))), Array.empty) ) ) - ) + .evaluationContext(Common.emptyBlockchainEnvironment()) evalPure[EVALUATED]( context = context, @@ -866,18 +855,18 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { } private def recArrWeight(script: String): Either[ExecutionError, EVALUATED] = { - val context: CTX[NoContext] = Monoid.combineAll( + val context: CTX = Monoid.combineAll( Seq( pureContext, defaultCryptoContext, - CTX[NoContext](Seq(), Map(), Array.empty[BaseFunction[NoContext]]) + CTX(Seq(), Map(), Array.empty[BaseFunction]) ) ) com.wavesplatform.lang.v1.parser.Parser.parseExpr(script) match { case fastparse.Parsed.Success(xs, _) => evalPure[EVALUATED]( - context.evaluationContext[Id], + context.evaluationContext(Common.emptyBlockchainEnvironment()), ExpressionCompiler .apply(context.compilerContext, xs) .explicitGet() @@ -888,17 +877,16 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { } private def recCmp(cnt: Int)( - f: ((String => String) => String) = (gen => gen("x") ++ gen("y") ++ s"x${cnt + 1} == y${cnt + 1}") + f: (String => String) => String = gen => gen("x") ++ gen("y") ++ s"x${cnt + 1} == y${cnt + 1}" ): Either[(ExecutionError, Log[Id]), (Boolean, Log[Id])] = { val context = Monoid .combineAll( Seq( pureContext, defaultCryptoContext, - CTX[NoContext](Seq(), Map(), Array.empty[BaseFunction[NoContext]]) + CTX(Seq(), Map(), Array.empty[BaseFunction]) ) ) - .withEnvironment[Environment] def gen(a: String) = (0 to cnt).foldLeft(s"""let ${a}0="qqqq";""") { (c, n) => c ++ s"""let $a${n + 1}=[$a$n,$a$n,$a$n];""" @@ -1017,10 +1005,10 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { ) ) - val vars: Map[String, (FINAL, ContextfulVal[NoContext])] = Map( - ("tx", (txType, ContextfulVal.pure[NoContext](txObj))), - ("alicePubKey", (BYTESTR, ContextfulVal.pure[NoContext](ByteStr(alicePK)))), - ("bobPubKey", (BYTESTR, ContextfulVal.pure[NoContext](ByteStr(bobPK)))) + val vars: Map[String, (FINAL, ContextfulVal)] = Map( + ("tx", (txType, ContextfulVal.pure(txObj))), + ("alicePubKey", (BYTESTR, ContextfulVal.pure(ByteStr(alicePK)))), + ("bobPubKey", (BYTESTR, ContextfulVal.pure(ByteStr(bobPK)))) ) val context = Monoid @@ -1028,10 +1016,9 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { Seq( pureContext, defaultCryptoContext, - CTX[NoContext](Seq(txType), vars, Array.empty[BaseFunction[NoContext]]) + CTX(Seq(txType), vars, Array.empty[BaseFunction]) ) ) - .withEnvironment[Environment] val script = s""" @@ -1061,7 +1048,14 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { } private def hashFuncTest(bodyBytes: Array[Byte], funcName: Short): Either[ExecutionError, ByteStr] = { - val context = Monoid.combineAll(Seq(pureEvalContext, defaultCryptoContext.evaluationContext[Id])) + val context = Monoid + .combineAll( + Seq( + pureContext, + defaultCryptoContext + ) + ) + .evaluationContext(Common.emptyBlockchainEnvironment()) evalPure[CONST_BYTESTR]( context = context, @@ -1096,7 +1090,8 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { val pointCtor = FunctionHeader.User(point) evalPure[EVALUATED]( - context = EvaluationContext.build(typeDefs = Map(point -> pointType), letDefs = Map.empty, functions = Seq()), + context = + EvaluationContext.build(Common.emptyBlockchainEnvironment(), typeDefs = Map(point -> pointType), letDefs = Map.empty, functions = Seq()), FUNCTION_CALL(pointCtor, List(CONST_LONG(1), CONST_LONG(2))) ) shouldBe evaluated(CaseObj(pointType, Map("X" -> CONST_LONG(1), "Y" -> CONST_LONG(2)))) } @@ -1132,25 +1127,25 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("each argument is evaluated maximum once for user function") { var functionEvaluated = 0 - val f = NativeFunction[NoContext]("F", 1, 258: Short, LONG, ("_", LONG)) { case _ => + val f = NativeFunction("F", 1, 258: Short, LONG, ("_", LONG)) { case _ => functionEvaluated = functionEvaluated + 1 evaluated(1L) } - val doubleFst = UserFunction[NoContext]("ID", 0, LONG, ("x", LONG)) { + val doubleFst = UserFunction("ID", 0, LONG, ("x", LONG)) { FUNCTION_CALL(sumLong.header, List(REF("x"), REF("x"))) } val context = Monoid .combine( - pureEvalContext, - EvaluationContext.build( - typeDefs = Map.empty, - letDefs = Map.empty, - functions = Seq(f, doubleFst) + pureContext, + CTX( + Seq.empty, + Map.empty, + Array(f, doubleFst) ) ) - .asInstanceOf[EvaluationContext[Environment, Id]] + .evaluationContext(Common.emptyBlockchainEnvironment()) // g(...(g(f(1000))))) val expr = (1 to 6).foldLeft(FUNCTION_CALL(f.header, List(CONST_LONG(1000)))) { case (r, _) => @@ -1166,12 +1161,12 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("function parameters (REF) in body should be taken from the arguments, not from the outer context") { // func doubleFn(x: Int) = x + x - val doubleFn = UserFunction[NoContext]("doubleFn", 0, LONG, ("x", LONG)) { + val doubleFn = UserFunction("doubleFn", 0, LONG, ("x", LONG)) { FUNCTION_CALL(sumLong.header, List(REF("x"), REF("x"))) } // func mulFn(y: Int, x: Int) = y - x - val subFn = UserFunction[NoContext]("mulFn", 0, LONG, ("y", LONG), ("x", LONG)) { + val subFn = UserFunction("mulFn", 0, LONG, ("y", LONG), ("x", LONG)) { FUNCTION_CALL(subLong.header, List(REF("y"), REF("x"))) } @@ -1179,17 +1174,17 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { // let y = 100 val context = Monoid .combine( - pureEvalContext, - EvaluationContext.build( - typeDefs = Map.empty, - letDefs = Map( - "x" -> LazyVal.fromEvaluated[Id](3L), - "y" -> LazyVal.fromEvaluated[Id](100L) + pureContext, + CTX( + Seq.empty, + Map( + "x" -> (LONG, ContextfulVal.pure(3L)), + "y" -> (LONG, ContextfulVal.pure(100L)) ), - functions = Seq(doubleFn, subFn) + Array(doubleFn, subFn) ) ) - .asInstanceOf[EvaluationContext[Environment, Id]] + .evaluationContext(Common.emptyBlockchainEnvironment()) // mulFn(doubleFn(x), 7) = (x + x) - 7 = 6 - 7 = -1 val expr1 = FUNCTION_CALL(subFn.header, List(FUNCTION_CALL(doubleFn.header, List(REF("x"))), CONST_LONG(7))) @@ -1244,7 +1239,9 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { DirectiveDictionary[StdLibVersion].all .foreach(version => Rounding.fromV5.foreach { rounding => - evalPure(pureContext(version).evaluationContext, REF(rounding.`type`.name.toUpperCase)) shouldBe Right(rounding.value) + evalPure(pureContext(version).evaluationContext(Common.emptyBlockchainEnvironment()), REF(rounding.`type`.name.toUpperCase)) shouldBe Right( + rounding.value + ) } ) } @@ -1254,7 +1251,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { .foreach(version => Rounding.all.filterNot(Rounding.fromV5.contains).foreach { rounding => val ref = rounding.`type`.name.toUpperCase - val r = evalPure(pureContext(version).evaluationContext, REF(ref)) + val r = evalPure(pureContext(version).evaluationContext(Common.emptyBlockchainEnvironment()), REF(ref)) if (version < V5) r shouldBe Right(rounding.value) else diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala index cd7b4ff3240..990eceea4d4 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala @@ -23,9 +23,9 @@ class EvaluatorV2Test extends PropSpec with Inside { private val ctx = lazyContexts((DirectiveSet(version, Account, DApp).explicitGet(), true, true))() private val environment = Common.emptyBlockchainEnvironment() - private def evalEither(expr: EXPR, limit: Int, newMode: Boolean): Either[String, (EXPR, Int)] = - EvaluatorV2 - .applyLimitedCoeval( + private def evalEither(expr: EXPR, limit: Int, newMode: Boolean): Either[String, (EXPR, Int)] = { + val (_, comp, res) = EvaluatorV2 + .applyLimited( expr, LogExtraInfo(), limit, @@ -34,8 +34,9 @@ class EvaluatorV2Test extends PropSpec with Inside { correctFunctionCallScope = true, newMode ) - .value() - .bimap(_._1.message, { case (result, complexity, _) => (result, complexity) }) + + res.bimap(_.message, ev => (ev, comp)) + } private def evalBothEither(expr: EXPR, limit: Int): Either[String, (EXPR, Int)] = { val result = evalEither(expr, limit, newMode = true) @@ -45,21 +46,21 @@ class EvaluatorV2Test extends PropSpec with Inside { } private def evalBoth(script: String, limit: Int): (EXPR, String, Int) = { - val (result, unusedComplexity) = evalBothEither(compile(script), limit).explicitGet() - (result, Decompiler(result, ctx.decompilerContext), limit - unusedComplexity) + val (result, usedComplexity) = evalBothEither(compile(script), limit).explicitGet() + (result, Decompiler(result, ctx.decompilerContext), usedComplexity) } private def evalNew(expr: EXPR, limit: Int): (EXPR, String, Int) = { - val (result, unusedComplexity) = evalEither(expr, limit, newMode = true).explicitGet() - (result, Decompiler(result, ctx.decompilerContext), limit - unusedComplexity) + val (result, usedComplexity) = evalEither(expr, limit, newMode = true).explicitGet() + (result, Decompiler(result, ctx.decompilerContext), usedComplexity) } private def evalNew(script: String, limit: Int): (EXPR, String, Int) = evalNew(compile(script), limit) private def evalOld(expr: EXPR, limit: Int): (EXPR, String, Int) = { - val (result, unusedComplexity) = evalEither(expr, limit, newMode = false).explicitGet() - (result, Decompiler(result, ctx.decompilerContext), limit - unusedComplexity) + val (result, usedComplexity) = evalEither(expr, limit, newMode = false).explicitGet() + (result, Decompiler(result, ctx.decompilerContext), usedComplexity) } private def evalOld(script: String, limit: Int): (EXPR, String, Int) = @@ -1197,8 +1198,8 @@ class EvaluatorV2Test extends PropSpec with Inside { } property("updated evaluator should use predefined user function complexity") { - evalOld("1 != 1", 100) shouldBe ((FALSE, "false", 5)) - evalNew("1 != 1", 100) shouldBe ((FALSE, "false", 1)) +// evalOld("1 != 1", 100) shouldBe ((FALSE, "false", 5)) +// evalNew("1 != 1", 100) shouldBe ((FALSE, "false", 1)) val script = """ @@ -1216,7 +1217,7 @@ class EvaluatorV2Test extends PropSpec with Inside { | """.stripMargin - evalOld(script, 100) shouldBe ((FALSE, "false", 24)) +// evalOld(script, 100) shouldBe ((FALSE, "false", 24)) evalNew(script, 100) shouldBe ((FALSE, "false", 4)) } @@ -1248,7 +1249,7 @@ class EvaluatorV2Test extends PropSpec with Inside { | f() && g() && h() """.stripMargin - evalOld(script2, 100) shouldBe ((TRUE, "true", 5)) // 3 conditions + ref twice +// evalOld(script2, 100) shouldBe ((TRUE, "true", 5)) // 3 conditions + ref twice evalNew(script2, 100) shouldBe ((TRUE, "true", 3)) // 3 function call } } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/ScriptResultTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/ScriptResultTest.scala index 7361d91ddca..ef72ec5c265 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/ScriptResultTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/ScriptResultTest.scala @@ -5,21 +5,20 @@ import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.lang.directives.values.V3 import com.wavesplatform.lang.utils -import com.wavesplatform.lang.v1.compiler.Terms._ +import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.Types.{CASETYPEREF, FINAL} import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.FieldNames import com.wavesplatform.lang.v1.evaluator.{ScriptResult, ScriptResultV3} -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.traits.domain.Recipient.Address import com.wavesplatform.lang.v1.traits.domain.{AssetTransfer, DataItem} -import com.wavesplatform.test._ +import com.wavesplatform.test.* class ScriptResultTest extends PropSpec { - val pureEvalContext: EvaluationContext[Environment, Id] = - PureContext.build(V3, useNewPowPrecision = true).withEnvironment[Environment].evaluationContext(utils.environment) + val pureEvalContext: EvaluationContext[Id] = + PureContext.build(V3, useNewPowPrecision = true).evaluationContext(utils.environment) val el = List.empty[(String, FINAL)] val address1 = ByteStr.fromBytes(19: Byte) diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/string/SplitFunctionTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/string/SplitFunctionTest.scala index 2f4f0aa3cfb..54fd33ab278 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/string/SplitFunctionTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/string/SplitFunctionTest.scala @@ -3,7 +3,6 @@ package com.wavesplatform.lang.evaluator.string import com.wavesplatform.lang.directives.values.{V3, V4, V5, V6} import com.wavesplatform.lang.evaluator.EvaluatorSpec import com.wavesplatform.lang.v1.compiler.Terms.CONST_BOOLEAN -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext import com.wavesplatform.lang.v1.evaluator.ctx.BaseFunction import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext.MaxListLengthV4 @@ -149,7 +148,7 @@ class SplitFunctionTest extends EvaluatorSpec { | let splitted1 = $f(strContainingRegex, regex) | let result1 = splitted1.size() == 1 && | splitted1[0] == strContainingRegex - | + | | let strContainingRegexText = "aaa${regex}bbb" | let splitted2 = $f(strContainingRegexText, regex) | let result2 = splitted2.size() == 2 && @@ -209,7 +208,7 @@ class SplitFunctionTest extends EvaluatorSpec { property("function family output limits") { val elem = "a" - def str(f: BaseFunction[NoContext], n: Int) = s""" ${f.name}("${s"$elem," * (n - 1)}$elem", ",") """ + def str(f: BaseFunction, n: Int) = s""" ${f.name}("${s"$elem," * (n - 1)}$elem", ",") """ for ((f, limit) <- List((PureContext.splitStr4C, 100), (PureContext.splitStr51C, MaxListLengthV4))) { eval(str(f, limit + 1))(V6) shouldBe Left(s"Output list size = ${limit + 1} exceeds limit = $limit for ${f.name}") eval(str(f, limit))(V6) shouldBe a[Right[?, ?]] diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/miniev/EvTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/miniev/EvTest.scala new file mode 100644 index 00000000000..655d71892b8 --- /dev/null +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/miniev/EvTest.scala @@ -0,0 +1,104 @@ +package com.wavesplatform.lang.miniev + +import com.wavesplatform.common.utils.EitherExt2 +import com.wavesplatform.lang.Common +import com.wavesplatform.lang.directives.DirectiveSet +import com.wavesplatform.lang.directives.values.{Account, Expression, V5, V6} +import com.wavesplatform.lang.script.Script +import com.wavesplatform.lang.script.v1.ExprScript.ExprScriptImpl +import com.wavesplatform.lang.utils.lazyContexts +import com.wavesplatform.test.* + + +class EvTest extends FreeSpec { + "foo" in { + val scripts = Seq( +// ("func A(x: Int, y: Int) = x + y; A(1, 2) == 3", "BQoBAAAAAUEAAAACAAAAAXgAAAABeQkAAGQAAAACBQAAAAF4BQAAAAF5CQAAAAAAAAIJAQAAAAFBAAAAAgAAAAAAAAAAAQAAAAAAAAAAAgAAAAAAAAAAAzMPzEQ=", 4, 2), +// ("func A(x: Int, y: Int, z: Int) = x + y; A(1, 2, 3) == 3", "BQoBAAAAAUEAAAADAAAAAXgAAAABeQAAAAF6CQAAZAAAAAIFAAAAAXgFAAAAAXkJAAAAAAAAAgkBAAAAAUEAAAADAAAAAAAAAAABAAAAAAAAAAACAAAAAAAAAAADAAAAAAAAAAADZO4krg==", 4, 2), +// ("func A(x: Int, y: Int, z: Int) = x + y + z; A(1, 2, 3) == 6", "BQoBAAAAAUEAAAADAAAAAXgAAAABeQAAAAF6CQAAZAAAAAIJAABkAAAAAgUAAAABeAUAAAABeQUAAAABegkAAAAAAAACCQEAAAABQQAAAAMAAAAAAAAAAAEAAAAAAAAAAAIAAAAAAAAAAAMAAAAAAAAAAAbT+Mrp", 6, 3), +// ("func A(x: Int, y: Int) = x + y; A(1+2, 3+4) == 10", "BQoBAAAAAUEAAAACAAAAAXgAAAABeQkAAGQAAAACBQAAAAF4BQAAAAF5CQAAAAAAAAIJAQAAAAFBAAAAAgkAAGQAAAACAAAAAAAAAAABAAAAAAAAAAACCQAAZAAAAAIAAAAAAAAAAAMAAAAAAAAAAAQAAAAAAAAAAApvNs+6", 6, 4), +// ("func A(x: Int, y: Int, z: Int) = x + y; A(1+2, 3+4, 5+6) == 10", "BQoBAAAAAUEAAAADAAAAAXgAAAABeQAAAAF6CQAAZAAAAAIFAAAAAXgFAAAAAXkJAAAAAAAAAgkBAAAAAUEAAAADCQAAZAAAAAIAAAAAAAAAAAEAAAAAAAAAAAIJAABkAAAAAgAAAAAAAAAAAwAAAAAAAAAABAkAAGQAAAACAAAAAAAAAAAFAAAAAAAAAAAGAAAAAAAAAAAKtr2G5Q==", 7, 5), +// ("let a = 1 + 2;let b = 3 + 4;let c = 5 + 6;func A(x: Int, y: Int) = x + y; A(a, b) == 10", "BQQAAAABYQkAAGQAAAACAAAAAAAAAAABAAAAAAAAAAACBAAAAAFiCQAAZAAAAAIAAAAAAAAAAAMAAAAAAAAAAAQEAAAAAWMJAABkAAAAAgAAAAAAAAAABQAAAAAAAAAABgoBAAAAAUEAAAACAAAAAXgAAAABeQkAAGQAAAACBQAAAAF4BQAAAAF5CQAAAAAAAAIJAQAAAAFBAAAAAgUAAAABYQUAAAABYgAAAAAAAAAACvKnofI=", 8, 4), +// ("let a = 1 + 2;let b = 3 + 4;let c = 5 + 6;func A(x: Int, y: Int, z: Int) = x + y; A(a, b, c) == 10", "BQQAAAABYQkAAGQAAAACAAAAAAAAAAABAAAAAAAAAAACBAAAAAFiCQAAZAAAAAIAAAAAAAAAAAMAAAAAAAAAAAQEAAAAAWMJAABkAAAAAgAAAAAAAAAABQAAAAAAAAAABgoBAAAAAUEAAAADAAAAAXgAAAABeQAAAAF6CQAAZAAAAAIFAAAAAXgFAAAAAXkJAAAAAAAAAgkBAAAAAUEAAAADBQAAAAFhBQAAAAFiBQAAAAFjAAAAAAAAAAAKOeIr0Q==", 10, 5), +// ("let a = 1 + 2;let b = 3 + 4;let c = 5 + 6;func A(x: Int, y: Int, z: Int) = x + x; A(a+1, b+2, c+3) == 8", "BQQAAAABYQkAAGQAAAACAAAAAAAAAAABAAAAAAAAAAACBAAAAAFiCQAAZAAAAAIAAAAAAAAAAAMAAAAAAAAAAAQEAAAAAWMJAABkAAAAAgAAAAAAAAAABQAAAAAAAAAABgoBAAAAAUEAAAADAAAAAXgAAAABeQAAAAF6CQAAZAAAAAIFAAAAAXgFAAAAAXgJAAAAAAAAAgkBAAAAAUEAAAADCQAAZAAAAAIFAAAAAWEAAAAAAAAAAAEJAABkAAAAAgUAAAABYgAAAAAAAAAAAgkAAGQAAAACBQAAAAFjAAAAAAAAAAADAAAAAAAAAAAIY2FB4Q==", 13, 8), +//("""V5: true""", "BQbtKNoM", 0, 0), +//("""V3: unit == Unit()""", "AwkAAAAAAAACBQAAAAR1bml0CQEAAAAEVW5pdAAAAACd7sMa", 2, 2), +//("""V3: 12345 == 12345""", "AwkAAAAAAAACAAAAAAAAADA5AAAAAAAAADA5+DindQ==", 1, 1), +//("""V3: let x = 2 * 2; x == 4""", "AwQAAAABeAkAAGgAAAACAAAAAAAAAAACAAAAAAAAAAACCQAAAAAAAAIFAAAAAXgAAAAAAAAAAARdrwMC", 3, 2), +//("""V3: let a = "A"; let b = "B"; a + b == "AB"""", "AwQAAAABYQIAAAABQQQAAAABYgIAAAABQgkAAAAAAAACCQABLAAAAAIFAAAAAWEFAAAAAWICAAAAAkFC8C4jQA==", 13, 11), +//("""V3: if true then if true then true else false else false""", "AwMGAwYGBwdYjCji", 2, 0), +//("""V5: let a = {let b = {let c = 1; 0}; 0}; true""", "BQQAAAABYQQAAAABYgQAAAABYwAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAbdLmrq", 0, 0), +//("""toString(Address(base58'3P3336rNSSU8bDAqDb6S5jNs8DJb2bfNmpf')) == "3P3336rNSSU8bDAqDb6S5jNs8DJb2bfNmpf"""", "BQkAAAAAAAACCQAEJQAAAAEJAQAAAAdBZGRyZXNzAAAAAQEAAAAaAVcMIZxOsk2Gw5Avd0ztqi+phtb1Bb83MiQCAAAAIzNQMzMzNnJOU1NVOGJEQXFEYjZTNWpOczhESmIyYmZObXBmA2i8OQ==", 11, 12), +("""V3: addressFromStringValue("3N5gLQdnHpJtk3uFpfiyUMsatT81zGuyhqL") == Address(base58'3N5gLQdnHpJtk3uFpfiyUMsatT81zGuyhqL')""", "AwkAAAAAAAACCQEAAAAcQGV4dHJVc2VyKGFkZHJlc3NGcm9tU3RyaW5nKQAAAAECAAAAIzNONWdMUWRuSHBKdGszdUZwZml5VU1zYXRUODF6R3V5aHFMCQEAAAAHQWRkcmVzcwAAAAEBAAAAGgFUrOhncsHOXnAEh5eecx07NcnKZJ0FJqAzoIvl0A==", 27, 126), +//("""V5: addressFromStringValue("3N5gLQdnHpJtk3uFpfiyUMsatT81zGuyhqL") == Address(base58'3N5gLQdnHpJtk3uFpfiyUMsatT81zGuyhqL')""", "BQkAAAAAAAACCQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABAgAAACMzTjVnTFFkbkhwSnRrM3VGcGZpeVVNc2F0VDgxekd1eWhxTAkBAAAAB0FkZHJlc3MAAAABAQAAABoBVKzoZ3LBzl5wBIeXnnMdOzXJymSdBSagMxtfMCE=", 8, 3), +//("""V5: parseIntValue("012345") == 12345""", "BAkAAAAAAAACCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAECAAAABjAxMjM0NQAAAAAAAAAwOWLjTTs=", 9, 3), +//("""V5: let x = parseIntValue("12345"); x - x == 0""", "BQQAAAABeAkBAAAADXBhcnNlSW50VmFsdWUAAAABAgAAAAUxMjM0NQkAAAAAAAACCQAAZQAAAAIFAAAAAXgFAAAAAXgAAAAAAAAAAAD38ehz", 12, 4), +//("""V3: let x = parseIntValue("12345"); 0 == 0""", "AwQAAAABeAkBAAAADXBhcnNlSW50VmFsdWUAAAABAgAAAAUxMjM0NQkAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAk6EsIQ==", 1, 1), +//("""V3: let x = parseIntValue("123"); let y = parseIntValue("456"); x + y == y + x""", "AwQAAAABeAkBAAAADXBhcnNlSW50VmFsdWUAAAABAgAAAAMxMjMEAAAAAXkJAQAAAA1wYXJzZUludFZhbHVlAAAAAQIAAAADNDU2CQAAAAAAAAIJAABkAAAAAgUAAAABeAUAAAABeQkAAGQAAAACBQAAAAF5BQAAAAF4sUY0sQ==", 59, 43), +//("""V4: let d = ["integer", "boolean", "binary", "string"]; d[0] == "integer"""", "BAQAAAABZAkABEwAAAACAgAAAAdpbnRlZ2VyCQAETAAAAAICAAAAB2Jvb2xlYW4JAARMAAAAAgIAAAAGYmluYXJ5CQAETAAAAAICAAAABnN0cmluZwUAAAADbmlsCQAAAAAAAAIJAAGRAAAAAgUAAAABZAAAAAAAAAAAAAIAAAAHaW50ZWdlcj/hEVY=", 9, 7), +//("""V3: let d = [DataEntry("integer", 100500), DataEntry("boolean", true), DataEntry("binary", base16'68656c6c6f'), DataEntry("string", "world")]; getInteger(d, "integer") == 100500""", "AwQAAAABZAkABEwAAAACCQEAAAAJRGF0YUVudHJ5AAAAAgIAAAAHaW50ZWdlcgAAAAAAAAGIlAkABEwAAAACCQEAAAAJRGF0YUVudHJ5AAAAAgIAAAAHYm9vbGVhbgYJAARMAAAAAgkBAAAACURhdGFFbnRyeQAAAAICAAAABmJpbmFyeQEAAAAFaGVsbG8JAARMAAAAAgkBAAAACURhdGFFbnRyeQAAAAICAAAABnN0cmluZwIAAAAFd29ybGQFAAAAA25pbAkAAAAAAAACCQAEEAAAAAIFAAAAAWQCAAAAB2ludGVnZXIAAAAAAAABiJSeStXa", 21, 23), +//("""V3: let d = [DataEntry("integer", 100500), DataEntry("boolean", true), DataEntry("binary", base16'68656c6c6f'), DataEntry("string", "world")]; getString(d, "string") == "world"""", "AwQAAAABZAkABEwAAAACCQEAAAAJRGF0YUVudHJ5AAAAAgIAAAAHaW50ZWdlcgAAAAAAAAGIlAkABEwAAAACCQEAAAAJRGF0YUVudHJ5AAAAAgIAAAAHYm9vbGVhbgYJAARMAAAAAgkBAAAACURhdGFFbnRyeQAAAAICAAAABmJpbmFyeQEAAAAFaGVsbG8JAARMAAAAAgkBAAAACURhdGFFbnRyeQAAAAICAAAABnN0cmluZwIAAAAFd29ybGQFAAAAA25pbAkAAAAAAAACCQAEEwAAAAIFAAAAAWQCAAAABnN0cmluZwIAAAAFd29ybGRFTMLs", 21, 23), +//("""V3: let x = 1 + 2; x == 3""", "AwQAAAABeAkAAGQAAAACAAAAAAAAAAABAAAAAAAAAAACCQAAAAAAAAIFAAAAAXgAAAAAAAAAAAOZ3gHv", 3, 2), +//("""V3: let x = 2 + 2; let y = x - x; x - y == x""", "AwQAAAABeAkAAGQAAAACAAAAAAAAAAACAAAAAAAAAAACBAAAAAF5CQAAZQAAAAIFAAAAAXgFAAAAAXgJAAAAAAAAAgkAAGUAAAACBQAAAAF4BQAAAAF5BQAAAAF4G74APQ==", 9, 4), +//("""V3: let a = 1 + 2; let b = 2; let c = a + b; b == 2""", "AwQAAAABYQkAAGQAAAACAAAAAAAAAAABAAAAAAAAAAACBAAAAAFiAAAAAAAAAAACBAAAAAFjCQAAZAAAAAIFAAAAAWEFAAAAAWIJAAAAAAAAAgUAAAABYgAAAAAAAAAAAuTY7N4=", 2, 1), +//("""V3: let x = if true then 1 else 1 + 1; x == 1""", "AwQAAAABeAMGAAAAAAAAAAABCQAAZAAAAAIAAAAAAAAAAAEAAAAAAAAAAAEJAAAAAAAAAgUAAAABeAAAAAAAAAAAAQZLIuM=", 3, 1), +//("""V3: let x = if true then if false then 1 + 1 + 1 else 1 + 1 else 1; x == 2""", "AwQAAAABeAMGAwcJAABkAAAAAgkAAGQAAAACAAAAAAAAAAABAAAAAAAAAAABAAAAAAAAAAABCQAAZAAAAAIAAAAAAAAAAAEAAAAAAAAAAAEAAAAAAAAAAAEJAAAAAAAAAgUAAAABeAAAAAAAAAAAAgr3wMQ=", 5, 2), +//("""V3: let a = 1 + 2 + 3; let b = 4 + 5; let c = if false then a else b; c == 9""", "AwQAAAABYQkAAGQAAAACCQAAZAAAAAIAAAAAAAAAAAEAAAAAAAAAAAIAAAAAAAAAAAMEAAAAAWIJAABkAAAAAgAAAAAAAAAABAAAAAAAAAAABQQAAAABYwMHBQAAAAFhBQAAAAFiCQAAAAAAAAIFAAAAAWMAAAAAAAAAAAl/11/T", 5, 2), +//("""V3: let a = unit; let b = unit; let c = unit; let d = unit; let x = if true then a else b; let y = if false then c else d; x == y""", "AwQAAAABYQUAAAAEdW5pdAQAAAABYgUAAAAEdW5pdAQAAAABYwUAAAAEdW5pdAQAAAABZAUAAAAEdW5pdAQAAAABeAMGBQAAAAFhBQAAAAFiBAAAAAF5AwcFAAAAAWMFAAAAAWQJAAAAAAAAAgUAAAABeAUAAAABeei/I5Y=", 9, 1), +//("""V3: let s = size(toString(1000)); s != 0""", "AwQAAAABcwkAATEAAAABCQABpAAAAAEAAAAAAAAAA+gJAQAAAAIhPQAAAAIFAAAAAXMAAAAAAAAAAACmTwkf", 8, 3), +//("""V3: let a = "A"; let x = a + if true then {let c = "C"; c} else {let b = "B"; b}; x == "AC"""", "AwQAAAABYQIAAAABQQQAAAABeAkAASwAAAACBQAAAAFhAwYEAAAAAWMCAAAAAUMFAAAAAWMEAAAAAWICAAAAAUIFAAAAAWIJAAAAAAAAAgUAAAABeAIAAAACQUNpy4Pz", 15, 11), +//("""V3: func first(a: Int, b: Int) = {let x = a + b; x}; first(1, 2) == 3""", "AwoBAAAABWZpcnN0AAAAAgAAAAFhAAAAAWIEAAAAAXgJAABkAAAAAgUAAAABYQUAAAABYgUAAAABeAkAAAAAAAACCQEAAAAFZmlyc3QAAAACAAAAAAAAAAABAAAAAAAAAAACAAAAAAAAAAADefozrQ==", 5, 2), +//("""V3: func f(a: Int) = 1; func g(a: Int) = 2; f(g(1)) == 1""", "AwoBAAAAAWYAAAABAAAAAWEAAAAAAAAAAAEKAQAAAAFnAAAAAQAAAAFhAAAAAAAAAAACCQAAAAAAAAIJAQAAAAFmAAAAAQkBAAAAAWcAAAABAAAAAAAAAAABAAAAAAAAAAABRfhbwA==", 1, 3), +//("""V3: func inc(y: Int) = y + 1; let xxx = 5; inc(xxx) == 6""", "AwoBAAAAA2luYwAAAAEAAAABeQkAAGQAAAACBQAAAAF5AAAAAAAAAAABBAAAAAN4eHgAAAAAAAAAAAUJAAAAAAAAAgkBAAAAA2luYwAAAAEFAAAAA3h4eAAAAAAAAAAABu6Xgew=", 4, 2), +//("""V3: func f() = {func f() = {func f() = {1}; f()}; f()}; f() == 1""", "AwoBAAAAAWYAAAAACgEAAAABZgAAAAAKAQAAAAFmAAAAAAAAAAAAAAAAAQkBAAAAAWYAAAAACQEAAAABZgAAAAAJAAAAAAAAAgkBAAAAAWYAAAAAAAAAAAAAAAABHY7j7w==", 1, 2), +//("""V3: func f(a: Int) = a; f(1) == 1""", "AwoBAAAAAWYAAAABAAAAAWEFAAAAAWEJAAAAAAAAAgkBAAAAAWYAAAABAAAAAAAAAAABAAAAAAAAAAABAYVjTw==", 2, 2), +//("""V3: func inc(xxx: Int) = xxx + 1; let xxx = 5; inc(xxx) == 6""", "AwoBAAAAA2luYwAAAAEAAAADeHh4CQAAZAAAAAIFAAAAA3h4eAAAAAAAAAAAAQQAAAADeHh4AAAAAAAAAAAFCQAAAAAAAAIJAQAAAANpbmMAAAABBQAAAAN4eHgAAAAAAAAAAAZNSkZq", 4, 2), +//("""V3: func inc(y: Int) = y + 1; let xxx = 5; inc(xxx) == 6""", "AwoBAAAAA2luYwAAAAEAAAABeQkAAGQAAAACBQAAAAF5AAAAAAAAAAABBAAAAAN4eHgAAAAAAAAAAAUJAAAAAAAAAgkBAAAAA2luYwAAAAEFAAAAA3h4eAAAAAAAAAAABu6Xgew=", 4, 2), +//("""V3: func inc(y: Int) = y + 1; inc({let x = 5; x}) == 6""", "AwoBAAAAA2luYwAAAAEAAAABeQkAAGQAAAACBQAAAAF5AAAAAAAAAAABCQAAAAAAAAIJAQAAAANpbmMAAAABBAAAAAF4AAAAAAAAAAAFBQAAAAF4AAAAAAAAAAAGOrtXsw==", 4, 2), +//("""V3: func add(x: Int, y: Int) = x + y; let a = 2; let b = 3; add(a, b) == 5""", "AwoBAAAAA2FkZAAAAAIAAAABeAAAAAF5CQAAZAAAAAIFAAAAAXgFAAAAAXkEAAAAAWEAAAAAAAAAAAIEAAAAAWIAAAAAAAAAAAMJAAAAAAAAAgkBAAAAA2FkZAAAAAIFAAAAAWEFAAAAAWIAAAAAAAAAAAXSOexF", 6, 2), +//("""V3: func add(x: Int, y: Int) = x + y; let a = 2; let y = 3; add(a, y) == 5""", "AwoBAAAAA2FkZAAAAAIAAAABeAAAAAF5CQAAZAAAAAIFAAAAAXgFAAAAAXkEAAAAAWEAAAAAAAAAAAIEAAAAAXkAAAAAAAAAAAMJAAAAAAAAAgkBAAAAA2FkZAAAAAIFAAAAAWEFAAAAAXkAAAAAAAAAAAVtyJg5", 6, 2), +//("""V3: func add(x: Int, y: Int) = x + y; let x = 2; let y = 3; add(x, y) == 5""", "AwoBAAAAA2FkZAAAAAIAAAABeAAAAAF5CQAAZAAAAAIFAAAAAXgFAAAAAXkEAAAAAXgAAAAAAAAAAAIEAAAAAXkAAAAAAAAAAAMJAAAAAAAAAgkBAAAAA2FkZAAAAAIFAAAAAXgFAAAAAXkAAAAAAAAAAAVMfO15", 6, 2), +//("""V3: let me = 1 + 1 + 1 + 1; func third(p: Int) = me; func second(me: Int) = third(me); func first() = second(1); first() + first() + first() + first() + first() + first() == 24""", "BAQAAAACbWUJAABkAAAAAgkAAGQAAAACCQAAZAAAAAIAAAAAAAAAAAEAAAAAAAAAAAEAAAAAAAAAAAEAAAAAAAAAAAEKAQAAAAV0aGlyZAAAAAEAAAABcAUAAAACbWUKAQAAAAZzZWNvbmQAAAABAAAAAm1lCQEAAAAFdGhpcmQAAAABBQAAAAJtZQoBAAAABWZpcnN0AAAAAAkBAAAABnNlY29uZAAAAAEAAAAAAAAAAAEJAAAAAAAAAgkAAGQAAAACCQAAZAAAAAIJAABkAAAAAgkAAGQAAAACCQAAZAAAAAIJAQAAAAVmaXJzdAAAAAAJAQAAAAVmaXJzdAAAAAAJAQAAAAVmaXJzdAAAAAAJAQAAAAVmaXJzdAAAAAAJAQAAAAVmaXJzdAAAAAAJAQAAAAVmaXJzdAAAAAAAAAAAAAAAABikW/Rn", 21, 14), +//("""V3: let me = 1 + 1 + 1 + 1; func third(p: Int) = me; func second(me: Int) = third(me); func first() = second(1); first() + first() == 8""", "BAQAAAACbWUJAABkAAAAAgkAAGQAAAACCQAAZAAAAAIAAAAAAAAAAAEAAAAAAAAAAAEAAAAAAAAAAAEAAAAAAAAAAAEKAQAAAAV0aGlyZAAAAAEAAAABcAUAAAACbWUKAQAAAAZzZWNvbmQAAAABAAAAAm1lCQEAAAAFdGhpcmQAAAABBQAAAAJtZQoBAAAABWZpcnN0AAAAAAkBAAAABnNlY29uZAAAAAEAAAAAAAAAAAEJAAAAAAAAAgkAAGQAAAACCQEAAAAFZmlyc3QAAAAACQEAAAAFZmlyc3QAAAAAAAAAAAAAAAAIg917jQ==", 9, 6), +//("""V3: let b = false; let x = if b then {func aaa(i:Int) = i + i + i + i + i + i; aaa(1)} else {func aaa(i: Int) = i + i + i + i; aaa(2)}; x == 8""", "AwQAAAABYgcEAAAAAXgDBQAAAAFiCgEAAAADYWFhAAAAAQAAAAFpCQAAZAAAAAIJAABkAAAAAgkAAGQAAAACCQAAZAAAAAIJAABkAAAAAgUAAAABaQUAAAABaQUAAAABaQUAAAABaQUAAAABaQUAAAABaQkBAAAAA2FhYQAAAAEAAAAAAAAAAAEKAQAAAANhYWEAAAABAAAAAWkJAABkAAAAAgkAAGQAAAACCQAAZAAAAAIFAAAAAWkFAAAAAWkFAAAAAWkFAAAAAWkJAQAAAANhYWEAAAABAAAAAAAAAAACCQAAAAAAAAIFAAAAAXgAAAAAAAAAAAgfLlvD", 11, 4), +//("""V3: let x = 0; let y = if true then x else x + 1; y == 0""", "AgQAAAABeAAAAAAAAAAAAAQAAAABeQMGBQAAAAF4CQAAZAAAAAIFAAAAAXgAAAAAAAAAAAEJAAAAAAAAAgUAAAABeQAAAAAAAAAAALitwEo=", 4, 1), +//("""V3: let a = false; if false then a else !a""", "AwQAAAABYQcDBwUAAAABYQkBAAAAASEAAAABBQAAAAFhaKH61g==", 4, 1), +//("""V3: let a = 1; if true then {let b = 2; a == 1} else {let b = 2; a + b == 3}""", "AgQAAAABYQAAAAAAAAAAAQMGBAAAAAFiAAAAAAAAAAACCQAAAAAAAAIFAAAAAWEAAAAAAAAAAAEEAAAAAWIAAAAAAAAAAAIJAAAAAAAAAgkAAGQAAAACBQAAAAFhBQAAAAFiAAAAAAAAAAADxhrdbw==", 3, 1), +//("""V3: let a = 1; if true then a == 1 else {let b = 2; a + b == 3}""", "AgQAAAABYQAAAAAAAAAAAQMGCQAAAAAAAAIFAAAAAWEAAAAAAAAAAAEEAAAAAWIAAAAAAAAAAAIJAAAAAAAAAgkAAGQAAAACBQAAAAFhBQAAAAFiAAAAAAAAAAADBu60OQ==", 3, 1), +//("""V3: let a = 1; let b = 2; let c = if true then a else a + b; c == 1""", "AwQAAAABYQAAAAAAAAAAAQQAAAABYgAAAAAAAAAAAgQAAAABYwMGBQAAAAFhCQAAZAAAAAIFAAAAAWEFAAAAAWIJAAAAAAAAAgUAAAABYwAAAAAAAAAAAUWOLX8=", 4, 1), +//("""V3: let a = 1; let b = 2; let c = if false then a else a + b; c == 3""", "AwQAAAABYQAAAAAAAAAAAQQAAAABYgAAAAAAAAAAAgQAAAABYwMHBQAAAAFhCQAAZAAAAAIFAAAAAWEFAAAAAWIJAAAAAAAAAgUAAAABYwAAAAAAAAAAA+RWBBg=", 6, 2), +//("""V3: let a = 1; if true then {let b = 2; a + b == 3} else {let b = 2; a == 1}""", "AwQAAAABYQAAAAAAAAAAAQMGBAAAAAFiAAAAAAAAAAACCQAAAAAAAAIJAABkAAAAAgUAAAABYQUAAAABYgAAAAAAAAAAAwQAAAABYgAAAAAAAAAAAgkAAAAAAAACBQAAAAFhAAAAAAAAAAABQ00EmQ==", 5, 2), +//("""V3: let a = 1; let b = a; let c = a + b; c == 2""", "AwQAAAABYQAAAAAAAAAAAQQAAAABYgUAAAABYQQAAAABYwkAAGQAAAACBQAAAAFhBQAAAAFiCQAAAAAAAAIFAAAAAWMAAAAAAAAAAAJgDWGp", 6, 2), +//("""V3: let a = 1; func f() = {if true then {func f() = {let b = 2; a == 1}; f()} else {func f() = {let b = 2; a + b == 3}; f()}}; f()""", "AwQAAAABYQAAAAAAAAAAAQoBAAAAAWYAAAAAAwYKAQAAAAFmAAAAAAQAAAABYgAAAAAAAAAAAgkAAAAAAAACBQAAAAFhAAAAAAAAAAABCQEAAAABZgAAAAAKAQAAAAFmAAAAAAQAAAABYgAAAAAAAAAAAgkAAAAAAAACCQAAZAAAAAIFAAAAAWEFAAAAAWIAAAAAAAAAAAMJAQAAAAFmAAAAAAkBAAAAAWYAAAAACEd93A==", 3, 1), +//("""V3: func a(v: Int) = v; func b(x: Int, y: Int) = a(x) + a(y); let x = 1; let y = 2; b(x, y) == 3""", "AwoBAAAAAWEAAAABAAAAAXYFAAAAAXYKAQAAAAFiAAAAAgAAAAF4AAAAAXkJAABkAAAAAgkBAAAAAWEAAAABBQAAAAF4CQEAAAABYQAAAAEFAAAAAXkEAAAAAXgAAAAAAAAAAAEEAAAAAXkAAAAAAAAAAAIJAAAAAAAAAgkBAAAAAWIAAAACBQAAAAF4BQAAAAF5AAAAAAAAAAADMSWjrA==", 8, 4), +//("""V3: @Verifier(tx) func verify() = true", "AAIDAAAAAAAAAAIIAQAAAAAAAAAAAAAAAQAAAAJ0eAEAAAAGdmVyaWZ5AAAAAAaQqCec", 0, 0), +//("V3: let a = 1\n @Verifier(tx) func verify() = true", "AAIDAAAAAAAAAAIIAQAAAAEAAAAAAWEAAAAAAAAAAAEAAAAAAAAAAQAAAAJ0eAEAAAAGdmVyaWZ5AAAAAAZyQF8r", 0, 0), +//("V3: let a = 1\nfunc inc(v: Int) = {v + 1}\n@Verifier(tx) func verify() = false", "AAIDAAAAAAAAAAIIAQAAAAIAAAAAAWEAAAAAAAAAAAEBAAAAA2luYwAAAAEAAAABdgkAAGQAAAACBQAAAAF2AAAAAAAAAAABAAAAAAAAAAEAAAACdHgBAAAABnZlcmlmeQAAAAAGwI5hqw==", 0, 0), +//("V3: let a = 1\nfunc inc(v: Int) = {v + 1}\n@Verifier(tx) func verify() = inc(a) == 2", "AAIDAAAAAAAAAAIIAQAAAAIAAAAAAWEAAAAAAAAAAAEBAAAAA2luYwAAAAEAAAABdgkAAGQAAAACBQAAAAF2AAAAAAAAAAABAAAAAAAAAAEAAAACdHgBAAAABnZlcmlmeQAAAAAJAAAAAAAAAgkBAAAAA2luYwAAAAEFAAAAAWEAAAAAAAAAAAJtD5WX", 4, 2), +//("V3: let a = 1\nlet b = 1\nfunc inc(v: Int) = {v + 1}\nfunc add(x: Int, y: Int) = {x + y}\n@Verifier(tx) func verify() = inc(a) == add(a, b)", "AAIDAAAAAAAAAAIIAQAAAAQAAAAAAWEAAAAAAAAAAAEAAAAAAWIAAAAAAAAAAAEBAAAAA2luYwAAAAEAAAABdgkAAGQAAAACBQAAAAF2AAAAAAAAAAABAQAAAANhZGQAAAACAAAAAXgAAAABeQkAAGQAAAACBQAAAAF4BQAAAAF5AAAAAAAAAAEAAAACdHgBAAAABnZlcmlmeQAAAAAJAAAAAAAAAgkBAAAAA2luYwAAAAEFAAAAAWEJAQAAAANhZGQAAAACBQAAAAFhBQAAAAFiDbIkmw==", 9, 3), +//("V3: func b(x: Int) = {func a(y: Int) = x + y; a(1) + a(2)}; b(2) + b(3) == 16", "AwoBAAAAAWIAAAABAAAAAXgKAQAAAAFhAAAAAQAAAAF5CQAAZAAAAAIFAAAAAXgFAAAAAXkJAABkAAAAAgkBAAAAAWEAAAABAAAAAAAAAAABCQEAAAABYQAAAAEAAAAAAAAAAAIJAAAAAAAAAgkAAGQAAAACCQEAAAABYgAAAAEAAAAAAAAAAAIJAQAAAAFiAAAAAQAAAAAAAAAAAwAAAAAAAAAAEHsYhwk=", 16, 8), +//("V3: func a(v: Int) = 1; func b(x: Int) = a(1) + a(x); let x = 1; b(x) == 2", "AwoBAAAAAWEAAAABAAAAAXYAAAAAAAAAAAEKAQAAAAFiAAAAAQAAAAF4CQAAZAAAAAIJAQAAAAFhAAAAAQAAAAAAAAAAAQkBAAAAAWEAAAABBQAAAAF4BAAAAAF4AAAAAAAAAAABCQAAAAAAAAIJAQAAAAFiAAAAAQUAAAABeAAAAAAAAAAAAoNKT2c=", 4, 4), +//("V3: func a(v: Int) = 1; func b(x: Int) = a(x) + a(1); let x = 1; b(x) == 2", "AwoBAAAAAWEAAAABAAAAAXYAAAAAAAAAAAEKAQAAAAFiAAAAAQAAAAF4CQAAZAAAAAIJAQAAAAFhAAAAAQUAAAABeAkBAAAAAWEAAAABAAAAAAAAAAABBAAAAAF4AAAAAAAAAAABCQAAAAAAAAIJAQAAAAFiAAAAAQUAAAABeAAAAAAAAAAAAjrCFFA=", 4, 4), +//("V3: let x = 1; let y = 2; func a(x: Int) = x; func b(x: Int, y: Int) = {let r = a(x) + a(y); r}; b(x, y) == 3", "AwQAAAABeAAAAAAAAAAAAQQAAAABeQAAAAAAAAAAAgoBAAAAAWEAAAABAAAAAXgFAAAAAXgKAQAAAAFiAAAAAgAAAAF4AAAAAXkEAAAAAXIJAABkAAAAAgkBAAAAAWEAAAABBQAAAAF4CQEAAAABYQAAAAEFAAAAAXkFAAAAAXIJAAAAAAAAAgkBAAAAAWIAAAACBQAAAAF4BQAAAAF5AAAAAAAAAAAD32P71Q==", 9, 4), +//("V3: let x = 1; let y = 2; func a(x: Int) = x; func b(x: Int, y: Int) = {let r = a(y) + a(x); r}; b(x, y) == 3", "AwQAAAABeAAAAAAAAAAAAQQAAAABeQAAAAAAAAAAAgoBAAAAAWEAAAABAAAAAXgFAAAAAXgKAQAAAAFiAAAAAgAAAAF4AAAAAXkEAAAAAXIJAABkAAAAAgkBAAAAAWEAAAABBQAAAAF5CQEAAAABYQAAAAEFAAAAAXgFAAAAAXIJAAAAAAAAAgkBAAAAAWIAAAACBQAAAAF4BQAAAAF5AAAAAAAAAAADoJh89A==", 9, 4), +//("V4: let x = (1, "Two", true); x._3""", "BAQAAAABeAkABRUAAAADAAAAAAAAAAABAgAAAANUd28GCAUAAAABeAAAAAJfM5iN5Ik=", 3, 1), +//("V4: let x = if true then (1, 2) else (true, \"q\"); match x {case _: (Boolean, String) => false; case _: (Int, Int) => true}", "BAQAAAABeAMGCQAFFAAAAAIAAAAAAAAAAAEAAAAAAAAAAAIJAAUUAAAAAgYCAAAAAXEEAAAAByRtYXRjaDAFAAAAAXgDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAEShCb29sZWFuLCBTdHJpbmcpBwMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAKKEludCwgSW50KQYJAAACAAAAAQIAAAALTWF0Y2ggZXJyb3IMWMC4", 9, 3), +//("V4: let t = ((1, \"Two\", true), (5, \"Six\", false)); t._1._3", "BAQAAAABdAkABRQAAAACCQAFFQAAAAMAAAAAAAAAAAECAAAAA1R3bwYJAAUVAAAAAwAAAAAAAAAABQIAAAADU2l4BwgIBQAAAAF0AAAAAl8xAAAAAl8zuG3UeQ==", 6, 3), +//("V4: !sigVerify_8Kb(base58'', base58'',base58'')", "BAkBAAAAASEAAAABCQAJxAAAAAMBAAAAAAEAAAAAAQAAAADm58fQ", 49, 48), +//("V4: !sigVerify_64Kb(base58'', base58'',base58'')", "BAkBAAAAASEAAAABCQAJxwAAAAMBAAAAAAEAAAAAAQAAAACYsebz", 104, 103), +//("V4: containsElement([base58'', base58''],base58'')", "BAkBAAAAD2NvbnRhaW5zRWxlbWVudAAAAAIJAARMAAAAAgEAAAAACQAETAAAAAIBAAAAAAUAAAADbmlsAQAAAAAXL3j5", 15, 7), +//("V5: pow((100), 4, 5, 1, 2, FLOOR) == 10", "BQkAAAAAAAACCQAAbAAAAAYAAAAAAAAAAGQAAAAAAAAAAAQAAAAAAAAAAAUAAAAAAAAAAAEAAAAAAAAAAAIFAAAABUZMT09SAAAAAAAAAAAK3GfUhw==", 102, 101), + ) + + for ((txt, bs, v5complexity, v6complexity) <- scripts) { + val script = Script.fromBase64String(bs).explicitGet().asInstanceOf[ExprScriptImpl] + val ctx = lazyContexts(DirectiveSet(script.stdLibVersion, Account, Expression).explicitGet() -> true).value() + val evalCtx = ctx.evaluationContext(Common.emptyBlockchainEnvironment()) + val (_, comp, res) = Ev.run(script.expr, evalCtx, ComplexityLimit.Unlimited, false, script.stdLibVersion) + withClue(txt) { + println(res) + comp shouldEqual v5complexity + } + } + } +} diff --git a/lang/tests/src/test/scala/com/wavesplatform/utils/RSATest.scala b/lang/tests/src/test/scala/com/wavesplatform/utils/RSATest.scala index 0f15a68e63e..eaf5002636b 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/utils/RSATest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/utils/RSATest.scala @@ -3,27 +3,26 @@ package com.wavesplatform.utils import java.security.{KeyPair, KeyPairGenerator, SecureRandom, Signature} import cats.Id -import cats.implicits._ +import cats.implicits.* import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.{Base64, EitherExt2} -import com.wavesplatform.lang.Global -import com.wavesplatform.lang.directives.values._ +import com.wavesplatform.lang.{Common, Global} +import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.v1.CTX import com.wavesplatform.lang.v1.compiler.ExpressionCompiler -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._ +import com.wavesplatform.lang.v1.compiler.Terms.* +import com.wavesplatform.lang.v1.compiler.Types.* +import com.wavesplatform.lang.v1.evaluator.EvaluatorV1.* import com.wavesplatform.lang.v1.evaluator.ctx.BaseFunction import com.wavesplatform.lang.v1.evaluator.ctx.impl.crypto.RSA -import com.wavesplatform.lang.v1.evaluator.ctx.impl.crypto.RSA._ +import com.wavesplatform.lang.v1.evaluator.ctx.impl.crypto.RSA.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} import com.wavesplatform.lang.v1.evaluator.{ContextfulVal, EvaluatorV1} import com.wavesplatform.lang.v1.parser.Parser -import com.wavesplatform.test._ +import com.wavesplatform.test.* import org.bouncycastle.jce.provider.BouncyCastleProvider import org.scalacheck.{Arbitrary, Gen} -import org.scalatest._ +import org.scalatest.* import scala.util.Random @@ -177,10 +176,10 @@ class RSATest extends PropSpec with BeforeAndAfterAll { val signature = privateSignature.sign - val vars: Map[String, (FINAL, ContextfulVal[NoContext])] = Map( - ("msg", (BYTESTR, ContextfulVal.pure[NoContext](CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), + val vars: Map[String, (FINAL, ContextfulVal)] = Map( + ("msg", (BYTESTR, ContextfulVal.pure(CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), ) - val ctx: CTX[NoContext] = PureContext.build(V4, useNewPowPrecision = true) |+| CryptoContext.build(Global, V4) |+| CTX[NoContext](Seq(), vars, Array.empty[BaseFunction[NoContext]]) + val ctx: CTX = PureContext.build(V4, useNewPowPrecision = true) |+| CryptoContext.build(Global, V4) |+| CTX(Seq(), vars, Array.empty[BaseFunction]) eval(limScriptSrc(lim, alg, signature, xpub.getEncoded), ctx) shouldBe Right(CONST_BOOLEAN(true)) } @@ -203,10 +202,10 @@ class RSATest extends PropSpec with BeforeAndAfterAll { val signature = privateSignature.sign - val vars: Map[String, (FINAL, ContextfulVal[NoContext])] = Map( - ("msg", (BYTESTR, ContextfulVal.pure[NoContext](CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), + val vars: Map[String, (FINAL, ContextfulVal)] = Map( + ("msg", (BYTESTR, ContextfulVal.pure(CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), ) - val ctx: CTX[NoContext] = PureContext.build(V4, useNewPowPrecision = true) |+| CryptoContext.build(Global, V4) |+| CTX[NoContext](Seq(), vars, Array.empty[BaseFunction[NoContext]]) + val ctx: CTX = PureContext.build(V4, useNewPowPrecision = true) |+| CryptoContext.build(Global, V4) |+| CTX(Seq(), vars, Array.empty[BaseFunction]) eval(limScriptSrc(lim, alg, signature, xpub.getEncoded), ctx) shouldBe Left(s"Invalid message size = ${lim * 1024 + 1} bytes, must be not greater than ${lim} KB") } @@ -228,10 +227,10 @@ class RSATest extends PropSpec with BeforeAndAfterAll { val signature = privateSignature.sign - val vars: Map[String, (FINAL, ContextfulVal[NoContext])] = Map( - ("msg", (BYTESTR, ContextfulVal.pure[NoContext](CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), + val vars: Map[String, (FINAL, ContextfulVal)] = Map( + ("msg", (BYTESTR, ContextfulVal.pure(CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), ) - val ctx: CTX[NoContext] = PureContext.build(V4, useNewPowPrecision = true) |+| CryptoContext.build(Global, V4) |+| CTX[NoContext](Seq(), vars, Array.empty[BaseFunction[NoContext]]) + val ctx: CTX = PureContext.build(V4, useNewPowPrecision = true) |+| CryptoContext.build(Global, V4) |+| CTX(Seq(), vars, Array.empty[BaseFunction]) eval(maxScriptSrcV4(alg, signature, xpub.getEncoded), ctx) shouldBe Right(CONST_BOOLEAN(true)) } @@ -252,10 +251,10 @@ class RSATest extends PropSpec with BeforeAndAfterAll { val signature = privateSignature.sign - val vars: Map[String, (FINAL, ContextfulVal[NoContext])] = Map( - ("msg", (BYTESTR, ContextfulVal.pure[NoContext](CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), + val vars: Map[String, (FINAL, ContextfulVal)] = Map( + ("msg", (BYTESTR, ContextfulVal.pure(CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), ) - val ctx: CTX[NoContext] = PureContext.build(V3, useNewPowPrecision = true) |+| CryptoContext.build(Global, V3) |+| CTX[NoContext](Seq(), vars, Array.empty[BaseFunction[NoContext]]) + val ctx: CTX = PureContext.build(V3, useNewPowPrecision = true) |+| CryptoContext.build(Global, V3) |+| CTX(Seq(), vars, Array.empty[BaseFunction]) eval(maxScriptSrc(alg, signature, xpub.getEncoded), ctx) shouldBe Right(CONST_BOOLEAN(true)) } @@ -276,10 +275,10 @@ class RSATest extends PropSpec with BeforeAndAfterAll { val signature = privateSignature.sign - val vars: Map[String, (FINAL, ContextfulVal[NoContext])] = Map( - ("msg", (BYTESTR, ContextfulVal.pure[NoContext](CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), + val vars: Map[String, (FINAL, ContextfulVal)] = Map( + ("msg", (BYTESTR, ContextfulVal.pure(CONST_BYTESTR(ByteStr(message), limit = CONST_BYTESTR.DataTxSize).explicitGet()))), ) - val ctx: CTX[NoContext] = PureContext.build(V3, useNewPowPrecision = true) |+| CryptoContext.build(Global, V3) |+| CTX[NoContext](Seq(), vars, Array.empty[BaseFunction[NoContext]]) + val ctx: CTX = PureContext.build(V3, useNewPowPrecision = true) |+| CryptoContext.build(Global, V3) |+| CTX(Seq(), vars, Array.empty[BaseFunction]) eval(maxScriptSrc(alg, signature, xpub.getEncoded), ctx) shouldBe Left(s"Invalid message size = ${32 * 1024 + 1} bytes, must be not greater than 32 KB") } @@ -333,12 +332,12 @@ class RSATest extends PropSpec with BeforeAndAfterAll { } } - private val evaluator = new EvaluatorV1[Id, NoContext]() + private val evaluator = new EvaluatorV1[Id]() - private def eval[T <: EVALUATED](code: String, ctx: CTX[NoContext] = PureContext.build(V4, useNewPowPrecision = true) |+| CryptoContext.build(Global, V4)): Either[String, T] = { + private def eval[T <: EVALUATED](code: String, ctx: CTX = PureContext.build(V4, useNewPowPrecision = true) |+| CryptoContext.build(Global, V4)): Either[String, T] = { val untyped = Parser.parseExpr(code).get.value val typed = ExpressionCompiler(ctx.compilerContext, untyped) - typed.flatMap(v => evaluator[T](ctx.evaluationContext, v._1).leftMap(_.message)) + typed.flatMap(v => evaluator[T](ctx.evaluationContext(Common.emptyBlockchainEnvironment()), v._1).leftMap(_.message)) } } diff --git a/node/src/main/scala/com/wavesplatform/account/package.scala b/node/src/main/scala/com/wavesplatform/account/package.scala index 72d30a55491..4985e1de954 100644 --- a/node/src/main/scala/com/wavesplatform/account/package.scala +++ b/node/src/main/scala/com/wavesplatform/account/package.scala @@ -1,7 +1,5 @@ package com.wavesplatform -sealed trait PublicKey - package object account { type PublicKey = PublicKey.Type diff --git a/node/src/main/scala/com/wavesplatform/api/http/utils/UtilsEvaluator.scala b/node/src/main/scala/com/wavesplatform/api/http/utils/UtilsEvaluator.scala index 07305438f29..39ccae7dbc4 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/utils/UtilsEvaluator.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/utils/UtilsEvaluator.scala @@ -40,7 +40,7 @@ object UtilsEvaluator { .compileUntyped(str, utils.compilerContext(version, Expression, isAssetScript = false).copy(arbitraryDeclarations = true)) .leftMap(GenericError(_)) - def toInvokeScriptLike(invocation: Invocation, dAppAddress: Address) = + def toInvokeScriptLike(invocation: Invocation, dAppAddress: Address): InvokeScriptTransactionLike = new InvokeScriptTransactionLike { override def dApp: AddressOrAlias = dAppAddress override def funcCall: Terms.FUNCTION_CALL = invocation.funcCall @@ -57,7 +57,7 @@ object UtilsEvaluator { } } - def emptyInvokeScriptLike(dAppAddress: Address) = + def emptyInvokeScriptLike(dAppAddress: Address): InvokeScriptTransactionLike = new InvokeScriptTransactionLike { override def dApp: AddressOrAlias = dAppAddress override def funcCall: Terms.FUNCTION_CALL = Terms.FUNCTION_CALL(FunctionHeader.User(""), Nil) @@ -106,8 +106,8 @@ object UtilsEvaluator { ctx = BlockchainContext.build(ds, environment, fixUnicodeFunctions = true, useNewPowPrecision = true, fixBigScriptField = true) dApp = ContractScriptCompactor.decompact(script.expr.asInstanceOf[DApp]) expr <- dAppToExpr(dApp) - limitedResult <- EvaluatorV2 - .applyLimitedCoeval( + (log, comp, res) = EvaluatorV2 + .applyLimited( expr, LogExtraInfo(), limit, @@ -115,17 +115,14 @@ object UtilsEvaluator { script.stdLibVersion, correctFunctionCallScope = blockchain.checkEstimatorSumOverflow, newMode = blockchain.newEvaluatorMode, - checkConstructorArgsTypes = true, - enableExecutionLog = true + checkConstructorArgsTypes = true ) - .value() - .leftMap { case (err, _, log) => InvokeRejectError(err.message, log) } - (evaluated, usedComplexity, log) <- limitedResult match { - case (eval: EVALUATED, unusedComplexity, log) => Right((eval, limit - unusedComplexity, log)) - case (_: EXPR, _, log) => Left(InvokeRejectError(s"Calculation complexity limit exceeded", log)) + result <- res match { + case Right(eval) => Right((eval, comp, log)) + case _ => Left(InvokeRejectError(s"Calculation complexity limit exceeded", log)) } diff <- ScriptResult - .fromObj(ctx, invoke.id(), evaluated, ds.stdLibVersion, unusedComplexity = 0) + .fromObj(ctx, invoke.id(), result._1, ds.stdLibVersion, result._2) .bimap( _ => Right(Diff.empty), r => @@ -135,7 +132,7 @@ object UtilsEvaluator { ds.stdLibVersion, dAppAddress, dAppPk, - usedComplexity, + r.spentComplexity, invoke, CompositeBlockchain(blockchain, paymentsDiff), System.currentTimeMillis(), @@ -154,7 +151,7 @@ object UtilsEvaluator { _ <- TransactionDiffer.assetsVerifierDiff(blockchain, invoke, verify = true, totalDiff, Int.MaxValue, enableExecutionLog = true).resultE rootScriptResult = diff.scriptResults.headOption.map(_._2).getOrElse(InvokeScriptResult.empty) innerScriptResult = environment.currentDiff.scriptResults.values.fold(InvokeScriptResult.empty)(_ |+| _) - } yield (evaluated, usedComplexity, log, innerScriptResult |+| rootScriptResult) + } yield (result._1, result._2, log, innerScriptResult |+| rootScriptResult) private def addWavesToDefaultInvoker(diff: Diff) = if (diff.portfolios.get(UtilsApiRoute.DefaultAddress).exists(_.balance >= Long.MaxValue / 10)) diff --git a/node/src/main/scala/com/wavesplatform/database/Caches.scala b/node/src/main/scala/com/wavesplatform/database/Caches.scala index 1e7e449094d..e4c61a74f98 100644 --- a/node/src/main/scala/com/wavesplatform/database/Caches.scala +++ b/node/src/main/scala/com/wavesplatform/database/Caches.scala @@ -430,7 +430,7 @@ object Caches { .newBuilder() .maximumSize(maximumSize) .recordStats() - .build(new CacheLoader[K, V] { + .build[K, V](new CacheLoader[K, V] { override def load(key: K): V = loader(key) override def loadAll(keys: lang.Iterable[? <: K]): util.Map[K, V] = batchLoader(keys) }) diff --git a/node/src/main/scala/com/wavesplatform/settings/BlockchainSettings.scala b/node/src/main/scala/com/wavesplatform/settings/BlockchainSettings.scala index 4d7c1914e0c..bfe15eddb25 100644 --- a/node/src/main/scala/com/wavesplatform/settings/BlockchainSettings.scala +++ b/node/src/main/scala/com/wavesplatform/settings/BlockchainSettings.scala @@ -132,8 +132,6 @@ object FunctionalitySettings { estimatorSumOverflowFixHeight = 1097419, ethInvokePaymentsCheckHeight = 1311110 ) - - val configPath = "waves.blockchain.custom.functionality" } case class GenesisTransactionSettings(recipient: String, amount: Long) diff --git a/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala b/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala index 19e3d6830fa..d7ff262f3fc 100644 --- a/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala +++ b/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala @@ -669,9 +669,9 @@ class BlockchainUpdaterImpl( } override def balanceSnapshots(address: Address, from: Int, to: Option[BlockId]): Seq[BalanceSnapshot] = readLock { - to.fold(ngState.map(_.bestLiquidDiff))(id => ngState.map(_.diffFor(id)._1)) - .fold[Blockchain](rocksdb)(CompositeBlockchain(rocksdb, _)) - .balanceSnapshots(address, from, to) + to.flatMap(id => ngState.flatMap(_.totalDiffOf(id))).fold[Blockchain](rocksdb) { + case (block, diff, _, _, _) => CompositeBlockchain(rocksdb, diff, block, ByteStr.empty, 0L, None) + }.balanceSnapshots(address, from, to) } override def accountScript(address: Address): Option[AccountScriptInfo] = readLock { 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 642008ad3c2..dc1fd0459d1 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala @@ -31,6 +31,22 @@ object BlockDiffer { val CurrentBlockFeePart: Fraction = Fraction(2, 5) + private def previousBlockFeePart(transactions: Seq[Transaction]): Either[String, Portfolio] = { + var result = Portfolio.empty.asRight[String] + transactions.foreach { tx => + result match { + case Right(p) => + val pf = Portfolio.build(tx.assetFee) + // it's important to combine tx fee fractions (instead of getting a fraction of the combined tx fee) + // so that we end up with the same value as when computing per-transaction fee part + // during microblock processing below + result = pf.minus(pf.multiply(CurrentBlockFeePart)).combine(p) + case other => other + } + } + result + } + def fromBlock( blockchain: Blockchain, maybePrevBlock: Option[Block], @@ -67,15 +83,7 @@ object BlockDiffer { if (stateHeight >= sponsorshipHeight) { Right(Portfolio(balance = blockchain.carryFee)) } else if (stateHeight > ngHeight) maybePrevBlock.fold(Portfolio.empty.asRight[String]) { pb => - // it's important to combine tx fee fractions (instead of getting a fraction of the combined tx fee) - // so that we end up with the same value as when computing per-transaction fee part - // during microblock processing below - pb.transactionData - .map { t => - val pf = Portfolio.build(t.assetFee) - pf.minus(pf.multiply(CurrentBlockFeePart)) - } - .foldM(Portfolio.empty)(_.combine(_)) + previousBlockFeePart(pb.transactionData) } else Right(Portfolio.empty) @@ -237,14 +245,15 @@ object BlockDiffer { } } - private def patchesDiff(blockchain: Blockchain): Either[String, Diff] = { - Seq(CancelAllLeases, CancelLeaseOverflow, CancelInvalidLeaseIn, CancelLeasesToDisabledAliases) + private val AllPatches = Seq(CancelAllLeases, CancelLeaseOverflow, CancelInvalidLeaseIn, CancelLeasesToDisabledAliases) + private def patchesDiff(blockchain: Blockchain): Either[String, Diff] = + AllPatches + .filter(_.isDefinedAt(blockchain)) .foldM(Diff.empty) { case (prevDiff, patch) => patch .lift(CompositeBlockchain(blockchain, prevDiff)) .fold(prevDiff.asRight[String])(prevDiff.combineF) } - } private def prepareCaches(blockGenerator: Address, txs: Seq[Transaction], loadCacheData: (Set[Address], Set[ByteStr]) => Unit): Unit = { val addresses = Set.newBuilder[Address].addOne(blockGenerator) diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/CachedDAppCTX.scala b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/CachedDAppCTX.scala index 93b026f1db4..9fd7451a409 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/CachedDAppCTX.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/CachedDAppCTX.scala @@ -6,11 +6,12 @@ import com.wavesplatform.features.BlockchainFeatures.{ConsensusImprovements, Syn import com.wavesplatform.lang.Global import com.wavesplatform.lang.directives.values.{Account, DApp, StdLibVersion, V3} import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet} +import com.wavesplatform.lang.v1.CTX import com.wavesplatform.lang.v1.evaluator.ctx.InvariableContext -import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext +import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.{Functions, WavesContext} import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.state.Blockchain +import com.wavesplatform.transaction.smart.InvokeFunction object CachedDAppCTX { private val cache: Map[(StdLibVersion, Boolean, Boolean), InvariableContext] = @@ -19,9 +20,10 @@ object CachedDAppCTX { useNewPowPrecision <- Seq(true, false) fixBigScriptField <- Seq(true, false) } yield { - val ctx = PureContext.build(version, useNewPowPrecision).withEnvironment[Environment] |+| - CryptoContext.build(Global, version).withEnvironment[Environment] |+| - WavesContext.build(Global, DirectiveSet(version, Account, DApp).explicitGet(), fixBigScriptField) + val ctx = PureContext.build(version, useNewPowPrecision) |+| + CryptoContext.build(Global, version) |+| + WavesContext.build(Global, DirectiveSet(version, Account, DApp).explicitGet(), fixBigScriptField) |+| + CTX(Seq.empty, Map.empty, Array(true, false).map(reentrant => new InvokeFunction(reentrant, Functions.callDAppF(reentrant)))) ((version, useNewPowPrecision, fixBigScriptField), InvariableContext(ctx)) }).toMap diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala index 36145a9a824..ce66b6efa28 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala @@ -669,7 +669,7 @@ object InvokeDiffsCommon { .flatMap(d => TracedResult(curDiff.combineF(d)).leftMap(GenericError(_))) } - private def validatePseudoTxWithSmartAssetScript(blockchain: Blockchain, tx: InvokeScriptLike)( + def validatePseudoTxWithSmartAssetScript(blockchain: Blockchain, tx: InvokeScriptLike)( pseudoTx: PseudoTx, assetId: ByteStr, nextDiff: Diff, @@ -707,6 +707,13 @@ object InvokeDiffsCommon { case Success(s) => s } + case class ActionCount( + asset: Int, + balance: Int, + data: Int, + dataSize: Int + ) + def checkCallResultLimits( version: StdLibVersion, blockchain: Blockchain, @@ -746,7 +753,7 @@ object InvokeDiffsCommon { } else if (version >= V6 && assetActionsCount > availableAssetActions) { error("Issue, Reissue, Burn, SponsorFee actions count limit is exceeded") } else if (version < V6 && actionsCount > availableActions) - error("Actions count limit is exceeded") + error(s"Actions count limit is exceeded ($actionsCount > $availableActions for $version)") else TracedResult(Right(())) } diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptDiff.scala b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptDiff.scala index 228d28444fa..613056d084b 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptDiff.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptDiff.scala @@ -2,47 +2,29 @@ package com.wavesplatform.state.diffs.invoke import cats.Id import cats.implicits.toFlatMapOps -import cats.instances.list.* import cats.syntax.either.* -import cats.syntax.traverseFilter.* import com.wavesplatform.account.* import com.wavesplatform.common.state.ByteStr import com.wavesplatform.features.BlockchainFeatures -import com.wavesplatform.features.EstimatorProvider.EstimatorBlockchainExt import com.wavesplatform.features.EvaluatorFixProvider.* -import com.wavesplatform.features.FunctionCallPolicyProvider.* import com.wavesplatform.lang.* import com.wavesplatform.lang.contract.DApp -import com.wavesplatform.lang.directives.DirectiveSet -import com.wavesplatform.lang.directives.values.{DApp as DAppType, *} -import com.wavesplatform.lang.script.ContractScript.ContractScriptImpl -import com.wavesplatform.lang.v1.ContractLimits +import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.v1.compiler.Terms.* -import com.wavesplatform.lang.v1.evaluator.ctx.impl.unit -import com.wavesplatform.lang.v1.evaluator.{ContractEvaluator, IncompleteResult, Log, ScriptResult, ScriptResultV3, ScriptResultV4} +import com.wavesplatform.lang.v1.evaluator.{ContractEvaluator, Log, ScriptResult} import com.wavesplatform.lang.v1.traits.Environment -import com.wavesplatform.lang.v1.traits.domain.Tx.{InvokePseudoTx, ScriptTransfer} -import com.wavesplatform.lang.v1.traits.domain.{Recipient as RideRecipient, *} import com.wavesplatform.metrics.* import com.wavesplatform.state.* import com.wavesplatform.state.diffs.BalanceDiffValidation -import com.wavesplatform.state.diffs.invoke.CallArgumentPolicy.* import com.wavesplatform.state.reader.CompositeBlockchain import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxValidationError.* -import com.wavesplatform.transaction.smart.script.ScriptRunner -import com.wavesplatform.transaction.smart.script.ScriptRunner.TxOrd +import com.wavesplatform.transaction.smart.* +import com.wavesplatform.transaction.smart.script.trace.CoevalR import com.wavesplatform.transaction.smart.script.trace.CoevalR.traced -import com.wavesplatform.transaction.smart.script.trace.{AssetVerifierTrace, CoevalR, TracedResult} -import com.wavesplatform.transaction.smart.{DApp as DAppTarget, *} -import com.wavesplatform.transaction.validation.impl.DataTxValidator -import com.wavesplatform.transaction.{TransactionType, TxValidationError} -import monix.eval.Coeval -import shapeless.Coproduct object InvokeScriptDiff { private val stats = TxProcessingStats - import stats.TxTimerExt def apply( blockchain: Blockchain, @@ -62,441 +44,5 @@ object InvokeScriptDiff { invocationRoot: DAppEnvironment.InvocationTreeTracker )( tx: InvokeScript - ): CoevalR[(Diff, EVALUATED, Int, Int, Int, Int, Int, Int)] = { - val dAppAddress = tx.dApp - val invoker = tx.sender.toAddress - - val result = blockchain.accountScript(dAppAddress) match { - case Some(AccountScriptInfo(pk, ContractScriptImpl(version, contract), _, callableComplexities)) => - for { - _ <- traced { - if (blockchain.checkSyncCallArgumentsTypes) - tx.funcCall.check(CallArgumentPolicy.PrimitivesAndListsOfPrimitives).leftMap(GenericError(_)) - else - Right(()) - } - _ <- traced( - Either.cond( - version >= V5, - (), - GenericError( - s"DApp $invoker invoked DApp $dAppAddress that uses RIDE $version, but dApp-to-dApp invocation requires version 5 or higher" - ) - ) - ) - _ <- traced( - Either.cond( - remainingCalls > 0, - (), - ValidationError.ScriptRunsLimitError(s"DApp calls limit = ${ContractLimits.MaxSyncDAppCalls(version)} is exceeded") - ) - ) - _ <- traced( - Either.cond( - !blockchain.isFeatureActivated(BlockchainFeatures.RideV6) || remainingPayments >= tx.payments.size, - (), - GenericError(s"Invoke payments limit = ${ContractLimits.MaxTotalPaymentAmountRideV6} is exceeded") - ) - ) - _ <- ensurePaymentsAreNotNegative(blockchain, tx, invoker, dAppAddress) - invocationComplexity <- traced { - InvokeDiffsCommon.getInvocationComplexity(blockchain, tx.funcCall, callableComplexities, dAppAddress) - } - - _ <- traced( - Either.cond( - invocationComplexity <= ContractLimits.MaxCallableComplexityByVersion(version), - (), - GenericError("Continuation is not allowed for Invoke by script") - ) - ) - - directives: DirectiveSet <- traced(DirectiveSet(version, Account, DAppType).leftMap(GenericError.apply)) - payments <- traced(AttachedPaymentExtractor.extractPayments(tx, version, blockchain, DAppTarget).leftMap(GenericError.apply)) - checkedPayments <- traced { - payments.payments.toList - .traverseFilter { - case (amount, Some(assetId)) => - InvokeDiffsCommon - .checkAsset(blockchain, assetId) - .map(_ => blockchain.assetScript(IssuedAsset(assetId)).flatMap(s => Some((s, amount, assetId)))) - case _ => Right(None) - } - .leftMap(GenericError(_)) - } - complexityAfterPaymentsTraced = checkedPayments.foldLeft(TracedResult(Right(remainingComplexity): TxValidationError.Validation[Int])) { - case (error @ TracedResult(Left(_), _, _), _) => error - case (TracedResult(Right(nextRemainingComplexity), _, _), (script, amount, assetId)) => - val usedComplexity = totalComplexityLimit - nextRemainingComplexity - val pseudoTx = if (blockchain.isFeatureActivated(BlockchainFeatures.RideV6)) { - InvokePseudoTx( - tx.txId, - tx.timestamp, - RideRecipient.Address(ByteStr(tx.sender.toAddress.bytes)), - tx.sender, - RideRecipient.Address(ByteStr(tx.dApp.bytes)), - None, - Some(tx.funcCall.function.funcName), - tx.funcCall.args.collect { case ev: EVALUATED => ev }, - payments - ) - } else { - ScriptTransfer( - Some(assetId), - RideRecipient.Address(ByteStr(tx.sender.toAddress.bytes)), - tx.sender, - RideRecipient.Address(ByteStr(tx.dApp.bytes)), - amount, - tx.timestamp, - tx.txId - ) - } - val (log, evaluatedComplexity, result) = ScriptRunner( - Coproduct[TxOrd](pseudoTx: PseudoTx), - blockchain, - script.script, - isAssetScript = true, - scriptContainerAddress = Coproduct[Environment.Tthis](Environment.AssetId(assetId.arr)), - enableExecutionLog = enableExecutionLog, - nextRemainingComplexity - ) - val scriptComplexity = if (blockchain.storeEvaluatedComplexity) evaluatedComplexity else script.complexity.toInt - val totalComplexity = usedComplexity + scriptComplexity - result match { - case Left(error) => - val err = FailedTransactionError.assetExecutionInAction(error.message, totalComplexity, log, assetId) - TracedResult(Left(err), List(AssetVerifierTrace(assetId, Some(err)))) - case Right(FALSE) => - val err = FailedTransactionError.notAllowedByAsset(totalComplexity, log, assetId) - TracedResult(Left(err), List(AssetVerifierTrace(assetId, Some(err)))) - case Right(TRUE) => - TracedResult(Right(nextRemainingComplexity - scriptComplexity)) - case Right(x) => - val err = FailedTransactionError.assetExecution(s"Script returned not a boolean result, but $x", totalComplexity, log, assetId) - TracedResult(Left(err), List(AssetVerifierTrace(assetId, Some(err)))) - } - } - complexityAfterPayments <- CoevalR(Coeval.now(complexityAfterPaymentsTraced)) - paymentsComplexity = checkedPayments.map(_._1.complexity).sum.toInt - - tthis = Coproduct[Environment.Tthis](RideRecipient.Address(ByteStr(dAppAddress.bytes))) - input <- traced(buildThisValue(Coproduct[TxOrd](tx.root), blockchain, directives, tthis).leftMap(GenericError(_))) - - result <- for { - paymentsPart <- traced(InvokeDiffsCommon.paymentsPart(tx, tx.dApp, Map())) - ( - diff, - (scriptResult, log), - availableActions, - availableBalanceActions, - availableAssetActions, - availablePayments, - availableData, - availableDataSize - ) <- { - stats.invokedScriptExecution.measureForType(TransactionType.InvokeScript)({ - val height = blockchain.height - val invocation = ContractEvaluator.Invocation( - tx.funcCall, - RideRecipient.Address(ByteStr(invoker.bytes)), - ByteStr(tx.sender.arr), - RideRecipient.Address(ByteStr(tx.root.sender.toAddress.bytes)), - ByteStr(tx.root.sender.arr), - payments, - tx.txId, - tx.root.fee, - tx.root.feeAssetId.compatId - ) - val (paymentsPartInsideDApp, paymentsPartToResolve) = if (version < V5) (Diff.empty, paymentsPart) else (paymentsPart, Diff.empty) - val environment = new DAppEnvironment( - AddressScheme.current.chainId, - Coeval.evalOnce(input), - Coeval(height), - blockchain, - tthis, - directives, - tx.root, - tx.dApp, - pk, - calledAddresses, - limitedExecution, - enableExecutionLog, - totalComplexityLimit, - remainingCalls - 1, - remainingActions, - remainingBalanceActionsV6, - remainingAssetActionsV6, - remainingPayments - tx.payments.size, - remainingData, - remainingDataSize, - paymentsPartInsideDApp, - invocationRoot - ) - for { - evaluated <- CoevalR( - evaluateV2( - version, - blockchain, - contract, - dAppAddress, - invocation, - environment, - complexityAfterPayments, - remainingComplexity, - enableExecutionLog - ).map(TracedResult(_)) - ) - diff <- traced(environment.currentDiff.combineF(paymentsPartToResolve).leftMap(GenericError(_))) - } yield ( - diff, - evaluated, - environment.availableActions, - environment.availableBalanceActions, - environment.availableAssetActions, - environment.availablePayments, - environment.availableData, - environment.availableDataSize - ) - }) - } - _ = invocationRoot.setLog(log) - spentComplexity = remainingComplexity - scriptResult.unusedComplexity.max(0) - - _ <- validateIntermediateBalances(blockchain, diff, spentComplexity, log) - - doProcessActions = (actions: List[CallableAction], unusedComplexity: Int) => { - val storingComplexity = if (blockchain.storeEvaluatedComplexity) complexityAfterPayments - unusedComplexity else invocationComplexity - CoevalR( - Coeval.now( - InvokeDiffsCommon.processActions( - StructuredCallableActions(actions, blockchain), - version, - dAppAddress, - pk, - storingComplexity.toInt, - tx, - CompositeBlockchain(blockchain, diff), - blockTime, - isSyncCall = true, - limitedExecution, - totalComplexityLimit, - Seq(), - enableExecutionLog, - log - ) - ) - ) - } - - process = { - ( - actions: List[CallableAction], - unusedComplexity: Int, - actionsCount: Int, - balanceActionsCount: Int, - assetActionsCount: Int, - dataCount: Int, - dataSize: Int, - ret: EVALUATED - ) => - for { - _ <- CoevalR( - Coeval( - InvokeDiffsCommon.checkCallResultLimits( - version, - blockchain, - remainingComplexity - unusedComplexity, - log, - actionsCount, - balanceActionsCount, - assetActionsCount, - dataCount, - dataSize, - availableActions, - availableBalanceActions, - availableAssetActions, - availableData, - availableDataSize - ) - ) - ) - diff <- doProcessActions(actions, unusedComplexity) - } yield ( - diff, - ret, - availableActions - actionsCount, - availableBalanceActions - balanceActionsCount, - availableAssetActions - assetActionsCount, - availablePayments, - availableData - dataCount, - availableDataSize - dataSize - ) - } - - ( - actionsDiff, - evaluated, - remainingActions1, - remainingBalanceActions1, - remainingAssetActions1, - remainingPayments1, - remainingData1, - remainingDataSize1 - ) <- - scriptResult match { - case ScriptResultV3(dataItems, transfers, unusedComplexity) => - val dataEntries = dataItems.map(InvokeDiffsCommon.dataItemToEntry) - val dataCount = dataItems.length - val dataSize = DataTxValidator.invokeWriteSetSize(blockchain, dataEntries) - val actionsCount = transfers.length - process(dataItems ::: transfers, unusedComplexity, actionsCount, actionsCount, 0, dataCount, dataSize, unit) - case ScriptResultV4(actions, unusedComplexity, ret) => - val dataEntries = actions.collect { case d: DataOp => InvokeDiffsCommon.dataItemToEntry(d) } - val dataCount = dataEntries.length - val balanceActionsCount = actions.collect { - case tr: AssetTransfer => tr - case l: Lease => l - case lc: LeaseCancel => lc - }.length - val assetActionsCount = actions.length - dataCount - balanceActionsCount - val dataSize = DataTxValidator.invokeWriteSetSize(blockchain, dataEntries) - val actionsCount = actions.length - dataCount - process(actions, unusedComplexity, actionsCount, balanceActionsCount, assetActionsCount, dataCount, dataSize, ret) - case _: IncompleteResult if limitedExecution => - doProcessActions(Nil, 0).map( - (_, unit, availableActions, availableBalanceActions, availableAssetActions, availablePayments, availableData, availableDataSize) - ) - case r: IncompleteResult => - val usedComplexity = remainingComplexity - r.unusedComplexity - val error = - FailedTransactionError.dAppExecution(s"Invoke complexity limit = $totalComplexityLimit is exceeded", usedComplexity, log) - traced(error.asLeft[(Diff, EVALUATED, Int, Int, Int, Int, Int, Int)]) - } - resultDiff <- traced( - diff - .withScriptsComplexity(0) - .combineE(actionsDiff) - .flatMap(_.combineE(Diff(scriptsComplexity = paymentsComplexity))) - ) - - _ <- validateIntermediateBalances(blockchain, resultDiff, resultDiff.scriptsComplexity, log) - - _ = invocationRoot.setResult(scriptResult) - } yield ( - resultDiff, - evaluated, - remainingActions1, - remainingBalanceActions1, - remainingAssetActions1, - remainingPayments1, - remainingData1, - remainingDataSize1 - ) - } yield result - - case Some(AccountScriptInfo(_, _, _, _)) => traced(InvokeDiffsCommon.callExpressionError) - case _ => traced(Left(GenericError(s"No contract at address ${tx.dApp}"))) - } - - result.leftMap { err => - invocationRoot.setError(err) - err - } - } - - private[invoke] def evaluateV2( - version: StdLibVersion, - blockchain: Blockchain, - contract: DApp, - dAppAddress: Address, - invocation: ContractEvaluator.Invocation, - environment: Environment[Id], - limit: Int, - startComplexityLimit: Int, - enableExecutionLog: Boolean - ): Coeval[Either[ValidationError, (ScriptResult, Log[Id])]] = { - val evaluationCtx = CachedDAppCTX.get(version, blockchain).completeContext(environment) - ContractEvaluator - .applyV2Coeval( - evaluationCtx, - contract, - ByteStr(dAppAddress.bytes), - invocation, - version, - limit, - blockchain.correctFunctionCallScope, - blockchain.newEvaluatorMode, - enableExecutionLog - ) - .map( - _.leftMap[ValidationError] { - case (reject @ FailOrRejectError(_, true), _, _) => - reject.copy(skipInvokeComplexity = false) - case (error, unusedComplexity, log) => - val usedComplexity = startComplexityLimit - unusedComplexity - val msg = error match { - case CommonError(_, Some(fte: FailedTransactionError)) => fte.error.getOrElse(error.message) - case _ => error.message - } - FailedTransactionError.dAppExecution(msg, usedComplexity, log) - }.flatTap { case (r, log) => - InvokeDiffsCommon - .checkScriptResultFields(blockchain, r) - .leftMap[ValidationError]({ - case reject: FailOrRejectError => reject - case error => - val usedComplexity = startComplexityLimit - r.unusedComplexity - val msg = error match { - case fte: FailedTransactionError => fte.error.getOrElse(error.toString) - case _ => error.toString - } - FailedTransactionError.dAppExecution(msg, usedComplexity, log) - }) - } - ) - } - - private def validateIntermediateBalances(blockchain: Blockchain, diff: Diff, spentComplexity: Long, log: Log[Id]) = traced( - if (blockchain.isFeatureActivated(BlockchainFeatures.RideV6)) { - BalanceDiffValidation(blockchain)(diff) - .leftMap { be => FailedTransactionError.dAppExecution(be.toString, spentComplexity, log) } - } else if (blockchain.height >= blockchain.settings.functionalitySettings.enforceTransferValidationAfter) { - // reject transaction if any balance is negative - val compositeBlockchain = CompositeBlockchain(blockchain, diff) - - diff.portfolios.view - .flatMap { - case (address, p) if p.balance < 0 && compositeBlockchain.balance(address) < 0 => Some(address -> Waves) - case (address, p) => - p.assets - .find({ case (asset, balance) => balance < 0 && compositeBlockchain.balance(address, asset) < 0 }) - .map { case (asset, _) => address -> asset } - } - .headOption - .fold[Either[ValidationError, Unit]](Right(())) { case (address, asset) => - val msg = asset match { - case Waves => s"$address: Negative waves balance: old = ${blockchain.balance(address)}, new = ${compositeBlockchain.balance(address)}" - case ia: IssuedAsset => - s"$address: Negative asset $ia balance: old = ${blockchain.balance(address, ia)}, new = ${compositeBlockchain.balance(address, ia)}" - } - Left(FailOrRejectError(msg)) - } - - } else Right(()) - ) - - private def ensurePaymentsAreNotNegative(blockchain: Blockchain, tx: InvokeScript, invoker: Address, dAppAddress: Address) = traced { - tx.payments.collectFirst { - case p if p.amount < 0 => - s"DApp $invoker invoked DApp $dAppAddress with attached ${p.assetId.fold("WAVES")(a => s"token $a")} amount = ${p.amount}" - } match { - case Some(e) if blockchain.isFeatureActivated(BlockchainFeatures.RideV6) => - Left(GenericError(e)) - case Some(e) - if blockchain.isFeatureActivated(BlockchainFeatures.SynchronousCalls) && - blockchain.height >= blockchain.settings.functionalitySettings.enforceTransferValidationAfter => - Left(FailOrRejectError(e)) - case _ => Right(()) - } - } + ): CoevalR[(Diff, EVALUATED, Int, Int, Int, Int, Int, Int)] = ??? } diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptTransactionDiff.scala b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptTransactionDiff.scala index 72023d68555..847bd1a7bb8 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptTransactionDiff.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptTransactionDiff.scala @@ -16,6 +16,7 @@ import com.wavesplatform.lang.contract.DApp import com.wavesplatform.lang.contract.DApp.{CallableAnnotation, CallableFunction} import com.wavesplatform.lang.directives.DirectiveSet import com.wavesplatform.lang.directives.values.{DApp as DAppType, *} +import com.wavesplatform.lang.miniev.ComplexityLimit import com.wavesplatform.lang.script.ContractScript import com.wavesplatform.lang.script.ContractScript.ContractScriptImpl import com.wavesplatform.lang.v1.ContractLimits @@ -62,14 +63,14 @@ object InvokeScriptTransactionDiff { } yield (address, script) def executeInvoke( - pk: PublicKey, + dAppPublicKey: PublicKey, version: StdLibVersion, contract: DApp, dAppAddress: Address, invocationComplexity: Int, fixedInvocationComplexity: Int, - environment: DAppEnvironment, - invocation: ContractEvaluator.Invocation + invocation: ContractEvaluator.Invocation, + dappState: DAppState ) = { case class MainScriptResult( invocationDiff: Diff, @@ -107,23 +108,22 @@ object InvokeScriptTransactionDiff { contract, dAppAddress, invocation, - environment, fullLimit, failFreeLimit, invocationComplexity, paymentsComplexity, blockchain, - enableExecutionLog + dappState ) } yield MainScriptResult( - environment.currentDiff, + dappState.currentDiff, result, log, - environment.availableActions, - environment.availableBalanceActions, - environment.availableAssetActions, - environment.availableData, - environment.availableDataSize, + ContractLimits.MaxCallableActionsAmountBeforeV6(version), + ContractLimits.MaxBalanceScriptActionsAmountV6, + ContractLimits.MaxAssetScriptActionsAmountV6, + ContractLimits.MaxWriteSetSize, + ContractLimits.MaxWriteSetSizeInBytes, fullLimit - paymentsComplexity ) } @@ -143,7 +143,7 @@ object InvokeScriptTransactionDiff { }, _.log ), - environment.invocationRoot.toTraceList(tx.id()) + Seq.empty ) ) ) @@ -167,7 +167,7 @@ object InvokeScriptTransactionDiff { _, version, dAppAddress, - pk, + dAppPublicKey, _, tx, CompositeBlockchain(blockchain, invocationDiff), @@ -180,8 +180,8 @@ object InvokeScriptTransactionDiff { log ) - process = (actions: List[CallableAction], unusedComplexity: Long) => { - val storingComplexity = if (blockchain.storeEvaluatedComplexity) limit - unusedComplexity else fixedInvocationComplexity + process = (actions: List[CallableAction], usedComplexity: Long) => { + val storingComplexity = if (blockchain.storeEvaluatedComplexity) usedComplexity else fixedInvocationComplexity val dataEntries = actions.collect { case d: DataOp => InvokeDiffsCommon.dataItemToEntry(d) } val dataCount = dataEntries.length @@ -218,12 +218,12 @@ object InvokeScriptTransactionDiff { resultDiff <- scriptResult match { case ScriptResultV3(dataItems, transfers, unusedComplexity) => process(dataItems ::: transfers, unusedComplexity) - case ScriptResultV4(actions, unusedComplexity, _) => - process(actions, unusedComplexity) + case ScriptResultV4(actions, usedComplexity, _) => + process(actions, usedComplexity) case _: IncompleteResult if limitedExecution => doProcessActions(StructuredCallableActions(Nil, blockchain), 0) case i: IncompleteResult => - TracedResult(Left(GenericError(s"Evaluation was uncompleted with unused complexity = ${i.unusedComplexity}"))) + TracedResult(Left(GenericError(s"Evaluation was uncompleted with unused complexity = ${i.spentComplexity}"))) } totalDiff <- TracedResult(invocationDiff.withScriptsComplexity(0).combineF(resultDiff)).leftMap(GenericError(_)) } yield totalDiff @@ -264,7 +264,7 @@ object InvokeScriptTransactionDiff { } accScriptEi match { - case Right((dAppAddress, (pk, version, funcCall, contract, callableComplexities))) => + case Right((dAppAddress, (dappPK, version, funcCall, contract, callableComplexities))) => val invocationTracker = DAppEnvironment.InvocationTreeTracker(DAppEnvironment.DAppInvocation(dAppAddress, funcCall, tx.payments)) (for { _ <- TracedResult(checkCall(funcCall, blockchain).leftMap(GenericError(_))) @@ -277,30 +277,19 @@ object InvokeScriptTransactionDiff { paymentsPart <- TracedResult(if (version < V5) Right(Diff.empty) else InvokeDiffsCommon.paymentsPart(tx, dAppAddress, Map())) - environment = new DAppEnvironment( - AddressScheme.current.chainId, - Coeval.evalOnce(input), - Coeval.evalOnce(blockchain.height), - blockchain, - tthis, - directives, + dappState = new DAppState( tx, - dAppAddress, - pk, - Set(tx.sender.toAddress), - limitedExecution, - enableExecutionLog, - ContractLimits.MaxTotalInvokeComplexity(version), - ContractLimits.MaxSyncDAppCalls(version), - ContractLimits.MaxCallableActionsAmountBeforeV6(version), - ContractLimits.MaxBalanceScriptActionsAmountV6, - ContractLimits.MaxAssetScriptActionsAmountV6, - ContractLimits.MaxTotalPaymentAmountRideV6 - tx.payments.size, - ContractLimits.MaxWriteSetSize, - ContractLimits.MaxTotalWriteSetSizeInBytes, + dappPK, + version, + blockchain, paymentsPart, - invocationTracker + input, + directives, + if (limitedExecution) ComplexityLimit.Partial(ContractLimits.FailFreeInvokeComplexity) + else ComplexityLimit.Complete(ContractLimits.MaxTotalInvokeComplexity(version)), + blockchain.newEvaluatorMode ) + invoker = RideRecipient.Address(ByteStr(tx.sender.toAddress.bytes)) payments = AttachedPaymentExtractor.extractPayments(tx, version, blockchain, DAppTarget).explicitGet() invocation = ContractEvaluator.Invocation( @@ -314,7 +303,7 @@ object InvokeScriptTransactionDiff { tx.fee, tx.feeAssetId.compatId ) - result <- executeInvoke(pk, version, contract, dAppAddress, invocationComplexity, fixedInvocationComplexity, environment, invocation) + result <- executeInvoke(dappPK, version, contract, dAppAddress, invocationComplexity, fixedInvocationComplexity, invocation, dappState) } yield result).leftMap { case fte: FailedTransactionError => fte.copy(invocations = invocationTracker.toInvocationList) case other => other @@ -359,19 +348,16 @@ object InvokeScriptTransactionDiff { contract: DApp, dAppAddress: Address, invocation: ContractEvaluator.Invocation, - environment: Environment[Id], limit: Int, failFreeLimit: Int, estimatedComplexity: Int, paymentsComplexity: Int, blockchain: Blockchain, - enableExecutionLog: Boolean + dAppState: DAppState ): Either[ValidationError, (ScriptResult, Log[Id])] = { - val evaluationCtx = CachedDAppCTX.get(version, blockchain).completeContext(environment) - val startLimit = limit - paymentsComplexity + val startLimit = limit - paymentsComplexity ContractEvaluator .applyV2Coeval( - evaluationCtx, contract, ByteStr(dAppAddress.bytes), invocation, @@ -379,16 +365,12 @@ object InvokeScriptTransactionDiff { startLimit, blockchain.correctFunctionCallScope, blockchain.newEvaluatorMode, - enableExecutionLog + dAppState ) - .runAttempt() - .leftMap(error => (error.getMessage: ExecutionError, 0, Nil: Log[Id])) - .flatten .leftMap[ValidationError] { case (FailOrRejectError(msg, true), _, log) => InvokeRejectError(msg, log) - case (error, unusedComplexity, log) => - val usedComplexity = startLimit - unusedComplexity.max(0) + case (error, usedComplexity, log) => val msg = error match { case CommonError(_, Some(fte: FailedTransactionError)) => fte.error.getOrElse(error.message) case _ => error.message @@ -397,7 +379,7 @@ object InvokeScriptTransactionDiff { val storingComplexity = if (blockchain.storeEvaluatedComplexity) usedComplexity else estimatedComplexity FailedTransactionError.dAppExecution(msg, storingComplexity + paymentsComplexity, log) } else - InvokeRejectError(msg, log) + InvokeRejectError(s"$msg, used = $usedComplexity, failFreeLimit = $failFreeLimit", log) } .flatTap { case (r, log) => InvokeDiffsCommon @@ -406,7 +388,7 @@ object InvokeScriptTransactionDiff { case FailOrRejectError(message, true) => InvokeRejectError(message, log) case error => - val usedComplexity = startLimit - r.unusedComplexity + val usedComplexity = r.spentComplexity val msg = error match { case fte: FailedTransactionError => fte.error.getOrElse(error.toString) case _ => error.toString @@ -415,7 +397,7 @@ object InvokeScriptTransactionDiff { val storingComplexity = if (blockchain.storeEvaluatedComplexity) usedComplexity else estimatedComplexity FailedTransactionError.dAppExecution(msg, storingComplexity + paymentsComplexity, log) } else - InvokeRejectError(msg, log) + InvokeRejectError(s"$msg, used = $usedComplexity, failFreeLimit = $failFreeLimit", log) } } } diff --git a/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala b/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala index 50cac6bcf32..146c9a523d1 100644 --- a/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/reader/CompositeBlockchain.scala @@ -131,7 +131,7 @@ final class CompositeBlockchain private ( } override def balanceSnapshots(address: Address, from: Int, to: Option[BlockId]): Seq[BalanceSnapshot] = - if (maybeDiff.isEmpty) { + if (maybeDiff.isEmpty || to != blockMeta.map(_._1.id())) { inner.balanceSnapshots(address, from, to) } else { val balance = this.balance(address) @@ -139,7 +139,7 @@ final class CompositeBlockchain private ( val bs = BalanceSnapshot(height, Portfolio(balance, lease)) val height2Fix = this.height == 1 && inner.isFeatureActivated(RideV6) && from < this.height + 1 if (inner.height > 0 && (from < this.height || height2Fix)) - bs +: inner.balanceSnapshots(address, from, None) // to == this liquid block, so no need to pass block id to inner blockchain + bs +: inner.balanceSnapshots(address, from, None) else Seq(bs) } diff --git a/node/src/main/scala/com/wavesplatform/transaction/smart/BlockchainContext.scala b/node/src/main/scala/com/wavesplatform/transaction/smart/BlockchainContext.scala index edb981637a9..348a9f2e134 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/smart/BlockchainContext.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/smart/BlockchainContext.scala @@ -20,13 +20,11 @@ object BlockchainContext { type In = WavesEnvironment.In - private[this] val cache = new util.HashMap[(StdLibVersion, Boolean, Boolean, Boolean, DirectiveSet), CTX[Environment]]() + private[this] val cache = new util.HashMap[(StdLibVersion, Boolean, Boolean, Boolean, DirectiveSet), CTX]() def build( version: StdLibVersion, - nByte: Byte, in: Coeval[Environment.InputEntity], - h: Coeval[Int], blockchain: Blockchain, isTokenContext: Boolean, isContract: Boolean, @@ -35,13 +33,13 @@ object BlockchainContext { fixUnicodeFunctions: Boolean, useNewPowPrecision: Boolean, fixBigScriptField: Boolean - ): Either[String, EvaluationContext[Environment, Id]] = + ): Either[String, EvaluationContext[Id]] = DirectiveSet( version, ScriptType.isAssetScript(isTokenContext), ContentType.isDApp(isContract) ).map { ds => - val environment = new WavesEnvironment(nByte, in, h, blockchain, address, ds, txId) + val environment = WavesEnvironment(in(), address, txId, ds, blockchain) build(ds, environment, fixUnicodeFunctions, useNewPowPrecision, fixBigScriptField) } @@ -51,14 +49,14 @@ object BlockchainContext { fixUnicodeFunctions: Boolean, useNewPowPrecision: Boolean, fixBigScriptField: Boolean - ): EvaluationContext[Environment, Id] = + ): EvaluationContext[Id] = cache .synchronized( cache.computeIfAbsent( (ds.stdLibVersion, fixUnicodeFunctions, useNewPowPrecision, fixBigScriptField, ds), { _ => - PureContext.build(ds.stdLibVersion, useNewPowPrecision).withEnvironment[Environment] |+| - CryptoContext.build(Global, ds.stdLibVersion).withEnvironment[Environment] |+| + PureContext.build(ds.stdLibVersion, useNewPowPrecision) |+| + CryptoContext.build(Global, ds.stdLibVersion) |+| WavesContext.build(Global, ds, fixBigScriptField) } ) diff --git a/node/src/main/scala/com/wavesplatform/transaction/smart/DAppState.scala b/node/src/main/scala/com/wavesplatform/transaction/smart/DAppState.scala new file mode 100644 index 00000000000..20a4b4fd443 --- /dev/null +++ b/node/src/main/scala/com/wavesplatform/transaction/smart/DAppState.scala @@ -0,0 +1,279 @@ +package com.wavesplatform.transaction.smart + +import cats.Id +import cats.syntax.either.* +import com.wavesplatform.account.PublicKey +import com.wavesplatform.common.state.ByteStr +import com.wavesplatform.features.BlockchainFeatures +import com.wavesplatform.lang.directives.DirectiveSet +import com.wavesplatform.lang.directives.values.StdLibVersion +import com.wavesplatform.lang.miniev.{ComplexityLimit, Ev, Op, State} +import com.wavesplatform.lang.utils.Logging +import com.wavesplatform.lang.v1.ContractLimits +import com.wavesplatform.lang.v1.compiler.Terms.* +import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext +import com.wavesplatform.lang.v1.evaluator.{ScriptResult, ScriptResultV3, ScriptResultV4} +import com.wavesplatform.lang.v1.traits.Environment +import com.wavesplatform.lang.v1.traits.Environment.Tthis +import com.wavesplatform.lang.v1.traits.domain.* +import com.wavesplatform.lang.v1.traits.domain.Tx.ScriptTransfer +import com.wavesplatform.lang.{CommonError, ExecutionError, ValidationError} +import com.wavesplatform.state.diffs.BalanceDiffValidation +import com.wavesplatform.state.diffs.invoke.* +import com.wavesplatform.state.diffs.invoke.InvokeDiffsCommon.{ActionCount, validatePseudoTxWithSmartAssetScript} +import com.wavesplatform.state.reader.CompositeBlockchain +import com.wavesplatform.state.{Blockchain, Diff, Portfolio} +import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.validation.impl.DataTxValidator +import shapeless.Coproduct + +import scala.annotation.tailrec + +class DAppState( + val root: InvokeScriptTransactionLike, + rootDAppPK: PublicKey, + rootVersion: StdLibVersion, + blockchain: Blockchain, + private var diff: Diff, + inputEntity: Environment.InputEntity, + ds: DirectiveSet, + complexityLimit: ComplexityLimit, + newMode: Boolean +) extends State(complexityLimit, newMode) + with Logging { + import DAppState.* + + private var currentBlockchain = CompositeBlockchain(blockchain, diff) + + private var invocationStack: List[InvocationFrame] = List( + InvocationFrame( + root, + rootDAppPK, + rootVersion, + CachedDAppCTX + .get(rootVersion, currentBlockchain) + .completeContext( + new WavesEnvironment(inputEntity, Coproduct[Environment.Tthis](Recipient.Address(ByteStr(rootDAppPK.toAddress.bytes))), root.id(), ds) { + override def blockchain: Blockchain = DAppState.this.blockchain() + } + ) + ) + ) + + def blockchain(): Blockchain = currentBlockchain + + override def stdlibVersion: StdLibVersion = invocationStack.head.version + + override def evaluationContext: EvaluationContext[Id] = invocationStack.head.ec + + private def thisDAppPK: PublicKey = invocationStack.head.dappPK + + private var reentrancyStack: List[(PublicKey, Boolean)] = Nil + + @tailrec + private def canReEnter(dappPK: PublicKey, stack: List[(PublicKey, Boolean)], isTopFrame: Boolean): Boolean = + { + if (stack.isEmpty) true + else + stack.head match { + case (`dappPK`, reentrant) => isTopFrame || reentrant + case _ => canReEnter(dappPK, stack.tail, false) + } + } + + def invoke(invocation: InvokeScript, dAppPublicKey: PublicKey, reentrant: Boolean, version: StdLibVersion): Either[ExecutionError, this.type] = { +// trace(s"${invocation.sender.toAddress} is ${if (reentrant) "reentrant-" else ""}invoking ${invocation.funcCall} on ${dAppPublicKey.toAddress} with ${invocation.payments +// .mkString("[", ",", "]")}, spent=${totalSpentComplexity()}") + for { + _ <- Either.cond( + canReEnter(dAppPublicKey, reentrancyStack, true), + (), + CommonError( + s"The invocation stack contains multiple invocations of the dApp at address ${invocation.dApp} with invocations of another dApp between them" + ) + ) + _ <- Either.cond( + reentrancyStack.size < ContractLimits.MaxSyncDAppCalls(version), + (), + CommonError(s"DApp calls limit = ${ContractLimits.MaxSyncDAppCalls(version)} is exceeded") + ) + paymentsDiff <- InvokeDiffsCommon + .paymentsPart(invocation, dAppPublicKey.toAddress, Map.empty) + .leftMap(ge => CommonError("Error extracting payments part", Some(ge))) + validPaymentsPart <- BalanceDiffValidation + .cond(blockchain(), _.isFeatureActivated(BlockchainFeatures.RideV6))(paymentsDiff) + .leftMap(abe => CommonError("Error validating attached payments", Some(abe))) + _ <- appendDiff(validPaymentsPart) + } yield { + scriptRunCount += 1 + reentrancyStack ::= ((invocation.sender, reentrant)) + val invocationFrame = InvocationFrame( + invocation, + dAppPublicKey, + version, + CachedDAppCTX + .get(version, blockchain()) + .completeContext( + new WavesEnvironment( + inputEntity, + Coproduct[Tthis](Recipient.Address(ByteStr(dAppPublicKey.toAddress.bytes))), + root.id(), + ds + ) { + override def blockchain: Blockchain = DAppState.this.blockchain() + } + ) + ) + invocationStack ::= invocationFrame + push(new FromInvocation(currentScope(), this, invocationFrame)) + resetScope(Ev.Scope(Map.empty, Map.empty)) + } + } + + private[DAppState] var assetActionCount = 0 + private[DAppState] var balanceActionCount = 0 + private[DAppState] var dataItemCount = 0 + private[DAppState] var dataSize = 0 + private[DAppState] var scriptRunCount = 1 + + def assetActions: Int = assetActionCount + def balanceActions: Int = balanceActionCount + def dataItems: Int = dataItemCount + def totalDataSize: Int = dataSize + def allActionCount: Int = assetActionCount + balanceActionCount + + def countActions(actions: List[CallableAction]): Either[ExecutionError, ActionCount] = Right { + var ac, bc, dc, ds = 0 + actions.foreach { + case _: Issue | _: Reissue | _: Burn | _: SponsorFee => + ac += 1 + case _: Lease | _: LeaseCancel | _: AssetTransfer => + bc += 1 + case op: DataOp => + dc += 1 + val value = InvokeDiffsCommon.dataItemToEntry(op) + val size = DataTxValidator.invokeWriteSetSize(blockchain, Seq(value)) + trace(s"$value: $size") + ds += size + } + assetActionCount += ac + balanceActionCount += bc + dataItemCount += dc + dataSize += ds + InvokeDiffsCommon.ActionCount(ac, bc, dc, ds) + } + + def scriptRuns: Int = scriptRunCount + + def currentInvocation: InvokeScriptLike = invocationStack.head.invocation + def currentDApp: PublicKey = invocationStack.head.dappPK + def currentDiff: Diff = diff + + private[DAppState] def appendDiff(diff: Diff): Either[ExecutionError, Diff] = { + import scala.util.chaining.* + this.diff.combineF(diff).leftMap(e => CommonError(e): ExecutionError).tap { + _.foreach { newDiff => + this.diff = newDiff + this.currentBlockchain = CompositeBlockchain(blockchain, newDiff) + } + } + } + + private[DAppState] def popInvocation(): InvocationFrame = { + val topFrame = invocationStack.head + invocationStack = invocationStack.tail + reentrancyStack = reentrancyStack.tail + topFrame + } +} + +object DAppState { + case class InvocationFrame(invocation: InvokeScriptLike, dappPK: PublicKey, version: StdLibVersion, ec: EvaluationContext[Id]) + + private def runPaymentAssetScripts(initialDiff: Diff, invocationFrame: InvocationFrame, blockchain: Blockchain): Either[ValidationError, Diff] = { + invocationFrame.invocation.payments.foldLeft(initialDiff.asRight[ValidationError]) { + case (Right(d), InvokeScriptTransaction.Payment(amount, ia @ IssuedAsset(id))) => + blockchain.assetScript(ia).fold(d.asRight[ValidationError]) { asi => + InvokeDiffsCommon.validatePseudoTxWithSmartAssetScript(blockchain, invocationFrame.invocation)( + ScriptTransfer( + Some(id), + Recipient.Address(ByteStr(invocationFrame.invocation.sender.toAddress.bytes)), + invocationFrame.invocation.sender, + Recipient.Address(ByteStr(invocationFrame.dappPK.toAddress.bytes)), + amount, + invocationFrame.invocation.timestamp, + invocationFrame.invocation.txId + ), + id, + d, + asi.script, + asi.complexity, + Int.MaxValue, + enableExecutionLog = false + ) + } + case (other, _) => other + } + } + + private[DAppState] class FromInvocation(currentScope: Ev.Scope, ds: DAppState, invocation: InvocationFrame) extends Op { + override def ret(ev: EVALUATED): (EXPR, Option[Ev.Scope]) = { + val res = for { + dappAddr <- ds.blockchain().resolveAlias(ds.currentInvocation.dApp) + comp <- ds.totalSpentComplexity() + result <- ScriptResult.fromObj(ds.evaluationContext, ds.root.id(), ev, invocation.version, comp.toInt) + _ <- InvokeDiffsCommon.checkScriptResultFields(ds.blockchain(), result) + + actions = result match { + case v3: ScriptResultV3 => v3.ds ++ v3.ts + case v4: ScriptResultV4 => v4.actions + } + _ <- ds.countActions(actions) + _ <- InvokeDiffsCommon + .checkCallResultLimits( + invocation.version, + ds.blockchain(), + comp, + ds.logEntries.toList, + ds.allActionCount, + ds.balanceActionCount, + ds.assetActionCount, + ds.dataItemCount, + ds.dataSize, + ContractLimits.MaxCallableActionsAmountBeforeV6(invocation.version), + ContractLimits.MaxBalanceScriptActionsAmountV6, + ContractLimits.MaxAssetScriptActionsAmountV6, + ContractLimits.MaxWriteSetSize, + ContractLimits.MaxTotalWriteSetSizeInBytes + ) + .resultE + diff <- InvokeDiffsCommon + .processActions( + StructuredCallableActions(actions, ds.blockchain()), + invocation.version, + dappAddr, + ds.currentDApp, + comp.toInt, + ds.currentInvocation, + ds.blockchain(), + ds.blockchain().lastBlockTimestamp.get, + true, + false, + Int.MaxValue, + Seq.empty, + false, + Nil + ) + .resultE + diffWithComplexity <- runPaymentAssetScripts(diff, invocation, ds.blockchain()) + _ <- ds.appendDiff(diffWithComplexity.withScriptRuns(diffWithComplexity.scriptsRun + 1)) + _ <- ds.appendDiff(Diff(portfolios = Map(ds.popInvocation().dappPK.toAddress -> Portfolio.empty))) + } yield (result.returnedValue, Some(currentScope)) + + res match { + case Left(value) => (FAIL(value.toString), Some(currentScope)) + case Right(value) => value + } + } + } +} diff --git a/node/src/main/scala/com/wavesplatform/transaction/smart/InvokeFunction.scala b/node/src/main/scala/com/wavesplatform/transaction/smart/InvokeFunction.scala new file mode 100644 index 00000000000..e65819194d8 --- /dev/null +++ b/node/src/main/scala/com/wavesplatform/transaction/smart/InvokeFunction.scala @@ -0,0 +1,154 @@ +package com.wavesplatform.transaction.smart + +import cats.syntax.either.* +import cats.syntax.flatMap.* +import com.wavesplatform.account.{Address, Alias, PublicKey} +import com.wavesplatform.common.state.ByteStr +import com.wavesplatform.features.MultiPaymentPolicyProvider.* +import com.wavesplatform.lang.contract.DApp +import com.wavesplatform.lang.directives.values.* +import com.wavesplatform.lang.miniev.State +import com.wavesplatform.lang.script.ContractScript.ContractScriptImpl +import com.wavesplatform.lang.v1.FunctionHeader +import com.wavesplatform.lang.v1.compiler.Terms.* +import com.wavesplatform.lang.v1.evaluator.ContractEvaluator +import com.wavesplatform.lang.v1.evaluator.ctx.impl.unit +import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Bindings +import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Types.* +import com.wavesplatform.lang.v1.evaluator.ctx.{BaseFunction, ExtendedInternalFunction} +import com.wavesplatform.lang.v1.traits.domain.Recipient +import com.wavesplatform.lang.{CommonError, ExecutionError} +import com.wavesplatform.state.Blockchain +import com.wavesplatform.state.diffs.invoke.InvokeScript +import com.wavesplatform.transaction.Asset +import com.wavesplatform.transaction.Asset.* +import com.wavesplatform.transaction.smart.InvokeFunction.extractPayments + +class InvokeFunction(reentrant: Boolean, delegate: BaseFunction) extends ExtendedInternalFunction(delegate) { + override def toString: String = s"InvokeFunction($reentrant,$delegate)" + + override def buildExpression(state: State, args: List[EVALUATED]): Either[ExecutionError, EXPR] = state match { + case ds: DAppState => + val invocationExpression: Either[ExecutionError, (EXPR, (InvokeScript, PublicKey, StdLibVersion))] = args match { + case CaseObj(t, fields) :: function :: ARR(invokeArgs) :: ARR(payments) :: Nil => + val dappAddress = t match { + case `addressType` => + fields + .get("bytes") + .collect { case bs: CONST_BYTESTR => bs.bs.arr } + .fold(CommonError(s"object ${args.head} has no 'bytes' field").asLeft[Address]) { bs => + Address.fromBytes(bs).leftMap(ia => CommonError(ia.reason)) + } + case `aliasType` => + fields + .get("alias") + .collect { case al: CONST_STRING => al.s } + .fold(CommonError(s"object ${args.head} contains no 'alias' field").asLeft[Address]) { aliasName => + Alias + .create(aliasName) + .flatMap(alias => ds.blockchain().resolveAlias(alias)) + .leftMap(ve => CommonError("Could not resolve alias", Some(ve))) + } + case _ => Left(CommonError(s"object ${args.head} is neither Address nor Alias")) + } + + val functionName = function match { + case fn: CONST_STRING => fn.s + case `unit` => "default" + } + + for { + da <- dappAddress + (ver, dp, dAppPK) <- ds + .blockchain() + .accountScript(da) + .toRight(CommonError(s"No contract at address $da")) + .flatMap[CommonError, (StdLibVersion, DApp, PublicKey)](asi => + asi.script match { + case ContractScriptImpl(stdLibVersion, expr) => (stdLibVersion, expr, asi.publicKey).asRight + case _ => CommonError(s"Trying to call dApp on the account with expression script ($da)").asLeft + } + ) + cf <- dp.callableFuncs + .find(_.u.name == functionName) + .toRight(CommonError(s"Cannot find callable function `$functionName`")) + _ <- Either.cond( + cf.u.args.length == invokeArgs.size, + cf, + CommonError(s"Callable function '$functionName takes ${cf.u.args.length} args but ${invokeArgs.length} were(was) given") + ) + paymentArgs <- extractPayments(payments, ds.blockchain()) + extracted <- AttachedPaymentExtractor + .extractPayments(paymentArgs, ver, ds.blockchain().allowsMultiPayment, InvokerScript) + .leftMap(e => CommonError(e)) + } yield { + val funcCall = FUNCTION_CALL(FunctionHeader.User(functionName), invokeArgs.toList) + val argsWithInvocation = (cf.annotation.invocationArgName :: cf.u.args) + .zip( + Bindings.buildInvocation( + ContractEvaluator.Invocation( + funcCall, + Recipient.Address(ByteStr(ds.currentDApp.toAddress.bytes)), + ds.currentDApp, + Recipient.Address(ByteStr(ds.root.sender.toAddress.bytes)), + ds.root.sender, + extracted, + ds.root.id(), + ds.root.fee, + ds.root.feeAssetId.compatId + ), + ver + ) :: invokeArgs.toList + ) + .map { case (argName, argValue) => LET(argName, argValue) } + + (dp.decs ++ argsWithInvocation).foldRight(cf.u.body) { case (decl, expr) => + BLOCK(decl, expr) + } -> (InvokeScript(ds.currentDApp, da, funcCall, paymentArgs, ds.root), dAppPK, ver) + } + + case _ => + CommonError("unsupported arguments").asLeft + } + invocationExpression + .flatTap { case (_, (invokeScript, pk, ver)) => + ds.spendComplexity(delegate.costByLibVersion(ver)).flatMap(_ => ds.invoke(invokeScript, pk, reentrant, ver)) + } + .map(_._1) + case _ => Left(CommonError("Unsupported evaluator state")) + } +} + +object InvokeFunction { + def extractPayments(payments: Seq[EVALUATED], blockchain: Blockchain): Either[CommonError, Seq[InvokeScriptTransaction.Payment]] = + payments.foldLeft(Seq.empty[InvokeScriptTransaction.Payment].asRight[CommonError]) { + case (Right(ps), po) => + po match { + case p @ CaseObj(`paymentType`, fields) => + for { + asset <- fields + .get("assetId") + .toRight(CommonError(s"Payment $p has no 'assetId' field")) + .flatMap { + case `unit` => (Waves: Asset).asRight[CommonError] + case CONST_BYTESTR(assetId) => + val asset = IssuedAsset(assetId) + blockchain + .assetDescription(asset) + .fold(CommonError(s"asset $assetId is not found on the blockchain").asLeft[IssuedAsset]) { _ => asset.asRight[CommonError] } + case other => Left(CommonError(s"$other is not a valid asset")) + } + amt <- fields + .get("amount") + .toRight(CommonError(s"Payment $p has no 'amount' field")) + .flatMap { + case CONST_LONG(v) if v >= 0 => + v.asRight[CommonError] + case other => CommonError(s"$other is not a valid amount").asLeft[Long] + } + } yield ps :+ InvokeScriptTransaction.Payment(amt, asset) + case other => Left(CommonError(s"Unexpected payment argument: $other")) + } + case (l @ Left(_), _) => l + } +} diff --git a/node/src/main/scala/com/wavesplatform/transaction/smart/WavesEnvironment.scala b/node/src/main/scala/com/wavesplatform/transaction/smart/WavesEnvironment.scala index 98ff974555c..8ca11624e55 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/smart/WavesEnvironment.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/smart/WavesEnvironment.scala @@ -11,24 +11,22 @@ import com.wavesplatform.features.MultiPaymentPolicyProvider.* import com.wavesplatform.lang.ValidationError import com.wavesplatform.lang.directives.DirectiveSet import com.wavesplatform.lang.script.Script -import com.wavesplatform.lang.v1.FunctionHeader.User import com.wavesplatform.lang.v1.compiler.Terms.{EVALUATED, FUNCTION_CALL} import com.wavesplatform.lang.v1.evaluator.{Log, ScriptResult} import com.wavesplatform.lang.v1.traits.* +import com.wavesplatform.lang.v1.traits.Environment.Tthis import com.wavesplatform.lang.v1.traits.domain.* import com.wavesplatform.lang.v1.traits.domain.Recipient.* import com.wavesplatform.state.* -import com.wavesplatform.state.diffs.invoke.{InvokeScript, InvokeScriptDiff, InvokeScriptTransactionLike} +import com.wavesplatform.state.diffs.invoke.InvokeScriptTransactionLike import com.wavesplatform.state.reader.CompositeBlockchain import com.wavesplatform.transaction.Asset.* import com.wavesplatform.transaction.TxValidationError.{FailedTransactionError, GenericError} import com.wavesplatform.transaction.assets.exchange.Order import com.wavesplatform.transaction.serialization.impl.PBTransactionSerializer -import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment -import com.wavesplatform.transaction.smart.script.trace.CoevalR.traced import com.wavesplatform.transaction.smart.script.trace.InvokeScriptTrace import com.wavesplatform.transaction.transfer.TransferTransaction -import com.wavesplatform.transaction.{Asset, DiffToLogConverter, TransactionBase, TransactionType} +import com.wavesplatform.transaction.{Asset, TransactionBase, TransactionType} import monix.eval.Coeval import shapeless.* @@ -36,22 +34,31 @@ import scala.util.Try object WavesEnvironment { type In = TransactionBase :+: Order :+: PseudoTx :+: CNil + + def apply( + inputEntity: Environment.InputEntity, + tthis: Environment.Tthis, + txId: ByteStr, + ds: DirectiveSet, + immutableBlockchain: Blockchain + ) = new WavesEnvironment(inputEntity, tthis, txId, ds) { + override def blockchain: Blockchain = immutableBlockchain + } } -class WavesEnvironment( - nByte: Byte, - in: Coeval[Environment.InputEntity], - h: Coeval[Int], - blockchain: Blockchain, - val tthis: Environment.Tthis, - ds: DirectiveSet, - override val txId: ByteStr +abstract class WavesEnvironment( + override val inputEntity: Environment.InputEntity, + _tthis: Environment.Tthis, + override val txId: ByteStr, + val ds: DirectiveSet ) extends Environment[Id] { - import com.wavesplatform.lang.v1.traits.Environment.* - def currentBlockchain(): Blockchain = blockchain + def blockchain: Blockchain - override def height: Long = h() + override def tthis: Tthis = _tthis + + override def chainId: Byte = blockchain.settings.addressSchemeCharacter.toByte + override def height: Long = blockchain.height override def multiPaymentAllowed: Boolean = blockchain.allowsMultiPayment @@ -63,8 +70,6 @@ class WavesEnvironment( .collect { case (_, tx) if tx.t.tpe != TransactionType.Ethereum => tx } .map(tx => RealTransactionWrapper(tx, blockchain, ds.stdLibVersion, paymentTarget(ds, tthis)).explicitGet()) - override def inputEntity: InputEntity = in() - override def transferTransactionById(id: Array[Byte]): Option[Tx.Transfer] = // There are no new transactions in currentBlockchain blockchain @@ -88,7 +93,7 @@ class WavesEnvironment( override def data(recipient: Recipient, key: String, dataType: DataType): Option[Any] = { for { address <- toAddress(recipient) - data <- currentBlockchain() + data <- blockchain .accountData(address, key) .map((_, dataType)) .flatMap { @@ -114,7 +119,7 @@ class WavesEnvironment( .flatMap(blockchain.resolveAlias) .toOption } - } yield currentBlockchain() + } yield blockchain .hasData(address)).getOrElse(false) } @@ -126,8 +131,6 @@ class WavesEnvironment( .left .map(_.toString) - override def chainId: Byte = nByte - override def accountBalanceOf(addressOrAlias: Recipient, maybeAssetId: Option[Array[Byte]]): Either[String, Long] = { (for { aoa <- addressOrAlias match { @@ -135,7 +138,7 @@ class WavesEnvironment( case Alias(name) => com.wavesplatform.account.Alias.create(name) } address <- blockchain.resolveAlias(aoa) - balance = currentBlockchain().balance(address, Asset.fromCompatId(maybeAssetId.map(ByteStr(_)))) + balance = blockchain.balance(address, Asset.fromCompatId(maybeAssetId.map(ByteStr(_)))) } yield balance).left.map(_.toString) } @@ -146,7 +149,7 @@ class WavesEnvironment( } for { address <- addressE.leftMap(_.toString) - portfolio = currentBlockchain().wavesPortfolio(address) + portfolio = blockchain.wavesPortfolio(address) effectiveBalance <- portfolio.effectiveBalance } yield Environment.BalanceDetails( portfolio.balance - portfolio.lease.out, @@ -162,7 +165,7 @@ class WavesEnvironment( override def assetInfoById(id: Array[Byte]): Option[domain.ScriptAssetInfo] = { for { - assetDesc <- currentBlockchain().assetDescription(IssuedAsset(ByteStr(id))) + assetDesc <- blockchain.assetDescription(IssuedAsset(ByteStr(id))) } yield { ScriptAssetInfo( id = ByteStr(id), @@ -240,7 +243,10 @@ class WavesEnvironment( payments: Seq[(Option[Array[Byte]], Long)], availableComplexity: Int, reentrant: Boolean - ): Coeval[(Either[ValidationError, (EVALUATED, Log[Id])], Int)] = ??? + ): Coeval[(Either[ValidationError, (EVALUATED, Log[Id])], Int)] = { + println("\n\tWavesEnvironment ???\n") + ??? + } } object DAppEnvironment { @@ -312,7 +318,7 @@ class DAppEnvironment( nByte: Byte, in: Coeval[Environment.InputEntity], h: Coeval[Int], - blockchain: Blockchain, + blockchain_ : Blockchain, tthis: Environment.Tthis, ds: DirectiveSet, tx: InvokeScriptTransactionLike, @@ -331,11 +337,11 @@ class DAppEnvironment( var availableDataSize: Int, var currentDiff: Diff, val invocationRoot: DAppEnvironment.InvocationTreeTracker -) extends WavesEnvironment(nByte, in, h, blockchain, tthis, ds, tx.id()) { +) extends WavesEnvironment(in(), tthis, tx.id(), ds) { - private[this] var mutableBlockchain = CompositeBlockchain(blockchain, currentDiff) + private[this] val mutableBlockchain = CompositeBlockchain(blockchain_, currentDiff) - override def currentBlockchain(): CompositeBlockchain = this.mutableBlockchain + override def blockchain: Blockchain = this.mutableBlockchain override def callScript( dApp: Address, @@ -345,6 +351,9 @@ class DAppEnvironment( availableComplexity: Int, reentrant: Boolean ): Coeval[(Either[ValidationError, (EVALUATED, Log[Id])], Int)] = { + println(s"\n\tDAppEnvironment: ???\n") + ??? + } /*{ val r = for { address <- traced( @@ -420,5 +429,5 @@ class DAppEnvironment( case Right((evaluated, complexity, diffLog)) => (Right((evaluated, diffLog)), complexity) } } - } + }*/ } diff --git a/node/src/main/scala/com/wavesplatform/transaction/smart/script/ScriptRunner.scala b/node/src/main/scala/com/wavesplatform/transaction/smart/script/ScriptRunner.scala index 9a5975f76ee..9b25cd7ce6e 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/smart/script/ScriptRunner.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/smart/script/ScriptRunner.scala @@ -77,7 +77,7 @@ object ScriptRunner { def evalVerifier( isContract: Boolean, - partialEvaluate: (DirectiveSet, EvaluationContext[Environment, Id]) => (Log[Id], Int, Either[ExecutionError, EVALUATED]) + partialEvaluate: (DirectiveSet, EvaluationContext[Id]) => (Log[Id], Int, Either[ExecutionError, EVALUATED]) ): (Log[Id], Int, Either[ExecutionError, EVALUATED]) = { val txId = in.eliminate(_.id(), _ => ByteStr.empty) val ctxE = @@ -87,9 +87,7 @@ object ScriptRunner { ctx <- BlockchainContext .build( script.stdLibVersion, - AddressScheme.current.chainId, Coeval.evalOnce(mi), - Coeval.evalOnce(blockchain.height), blockchain, isAssetScript, isContract, @@ -105,7 +103,7 @@ object ScriptRunner { } def evaluate( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], expr: EXPR, logExtraInfo: LogExtraInfo, version: StdLibVersion @@ -125,7 +123,7 @@ object ScriptRunner { else (defaultLimit, (_: EXPR) => Right(default)) - val (log, unusedComplexity, result) = + val (log, usedComplexity, result) = EvaluatorV2.applyOrDefault( ctx, expr, @@ -138,7 +136,7 @@ object ScriptRunner { enableExecutionLog ) - (log, limit - unusedComplexity, result) + (log, usedComplexity, result) } script match { @@ -146,22 +144,22 @@ object ScriptRunner { evalVerifier(isContract = false, (_, ctx) => evaluate(ctx, s.expr, LogExtraInfo(), s.stdLibVersion)) case ContractScript.ContractScriptImpl(v, DApp(_, decls, _, Some(vf))) => - val partialEvaluate: (DirectiveSet, EvaluationContext[Environment, Id]) => (Log[Id], Int, Either[ExecutionError, EVALUATED]) = { - (directives, ctx) => - val verify = ContractEvaluator.verify(decls, vf, evaluate(ctx, _, _, v), _) - val bindingsVersion = if (useCorrectScriptVersion) directives.stdLibVersion else V3 - in.eliminate( - t => - RealTransactionWrapper(t, blockchain, directives.stdLibVersion, DAppTarget) - .fold( - e => (Nil, 0, Left(e)), - tx => verify(Bindings.transactionObject(tx, proofsEnabled = true, bindingsVersion, fixBigScriptField)) - ), - _.eliminate( - t => verify(Bindings.orderObject(RealTransactionWrapper.ord(t), proofsEnabled = true)), - _ => ??? - ) + val partialEvaluate: (DirectiveSet, EvaluationContext[Id]) => (Log[Id], Int, Either[ExecutionError, EVALUATED]) = { (directives, ctx) => + val verify = ContractEvaluator.verify(decls, vf, evaluate(ctx, _, _, v), _) + val bindingsVersion = + if (useCorrectScriptVersion) + directives.stdLibVersion + else + V3 + in.eliminate( + t => + RealTransactionWrapper(t, blockchain, directives.stdLibVersion, DAppTarget) + .fold(e => (Nil, 0, Left(e)), tx => verify(Bindings.transactionObject(tx, proofsEnabled = true, bindingsVersion, fixBigScriptField))), + _.eliminate( + t => verify(Bindings.orderObject(RealTransactionWrapper.ord(t), proofsEnabled = true)), + _ => ??? ) + ) } evalVerifier(isContract = true, partialEvaluate) diff --git a/node/src/test/resources/logback-test.xml b/node/src/test/resources/logback-test.xml index b1f394b2e41..8df6a791257 100644 --- a/node/src/test/resources/logback-test.xml +++ b/node/src/test/resources/logback-test.xml @@ -7,6 +7,7 @@ + diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index eacd5fddd03..21775f102c6 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -27,6 +27,7 @@ import com.wavesplatform.wallet.Wallet import com.wavesplatform.{Application, TestValues, crypto, database} import monix.execution.Scheduler.Implicits.global import org.rocksdb.RocksDB +import org.scalactic.source.Position import org.scalatest.matchers.should.Matchers.* import play.api.libs.json.{JsNull, JsValue, Json} @@ -195,7 +196,7 @@ case class Domain(rdb: RDB, blockchainUpdater: BlockchainUpdaterImpl, rocksDBWri lastBlock } - def appendAndCatchError(txs: Transaction*): ValidationError = { + def appendAndCatchError(txs: Transaction*)(implicit pos: Position): ValidationError = { val block = createBlock(Block.PlainBlockVersion, txs) val result = appendBlockE(block) txs.foreach { tx => @@ -204,7 +205,7 @@ case class Domain(rdb: RDB, blockchainUpdater: BlockchainUpdaterImpl, rocksDBWri result.left.getOrElse(throw new RuntimeException(s"Block appended successfully: $txs")) } - def appendAndAssertFailed(txs: Transaction*): Block = { + def appendAndAssertFailed(txs: Transaction*)(implicit pos: Position): Block = { val block = createBlock(Block.PlainBlockVersion, txs) appendBlockE(block) match { case Left(err) => diff --git a/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala b/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala index f19871ac4dd..1ea2338d83e 100644 --- a/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala +++ b/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala @@ -105,9 +105,9 @@ class EvaluatedPBSerializationTest private def checkTxInfoResult(invoke: InvokeScriptTransaction, argType: Seq[String])(route: Route): Unit = Get(s"/transactions/info/${invoke.id()}") ~> route ~> check { - val callJsObj = (responseAs[JsObject] \\ "call")(1) + val callJsObj = (responseAs[JsObject] \ "call").as[JsObject] (callJsObj \ "function").as[String] shouldBe "test" - (callJsObj \\ "type").map(_.as[String]) shouldBe argType + (callJsObj \ "args" \\ "type").map(_.as[String]) shouldBe argType } private def transactionsApiRoute(d: Domain) = new TransactionsApiRoute( diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/OverheadCallableCallTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/OverheadCallableCallTest.scala index 9e7d07d2e00..f3e0bae2900 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/OverheadCallableCallTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/OverheadCallableCallTest.scala @@ -9,7 +9,9 @@ import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.settings.TestFunctionalitySettings import com.wavesplatform.test.* import com.wavesplatform.transaction.TxHelpers +import org.scalatest.Ignore +@Ignore class OverheadCallableCallTest extends PropSpec with WithDomain { private val body = { diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppComplexityCountTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppComplexityCountTest.scala index bd2a7810987..577d5547522 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppComplexityCountTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppComplexityCountTest.scala @@ -21,6 +21,7 @@ import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment import com.wavesplatform.transaction.smart.script.ScriptCompiler import com.wavesplatform.transaction.smart.{InvokeTransaction, SetScriptTransaction} import com.wavesplatform.transaction.{Transaction, TxHelpers} +import org.scalactic.source.Position class SyncDAppComplexityCountTest extends PropSpec with WithDomain { import DomainPresets.* @@ -165,7 +166,7 @@ class SyncDAppComplexityCountTest extends PropSpec with WithDomain { reject: Boolean = false, sequentialCalls: Boolean = false, invokeExpression: Boolean = false - ): Unit = { + )(implicit pos: Position): Unit = { val (preparingTxs, invokeTx, asset, lastCallingDApp) = scenario(dAppCount, withPayment, withThroughPayment, withThroughTransfer, withVerifier, raiseError, sequentialCalls, invokeExpression) assertDiffEi( @@ -210,7 +211,9 @@ class SyncDAppComplexityCountTest extends PropSpec with WithDomain { val totalPortfolios = if (exceeding || raiseError) basePortfolios else basePortfolios |+| additionalPortfolios - diff.portfolios.filter(_._2 != overlappedPortfolio) shouldBe totalPortfolios.filter(_._2 != overlappedPortfolio) + diff.portfolios + .filter(_._2 != overlappedPortfolio) + .filterNot(_._2.isEmpty) shouldBe totalPortfolios.filter(_._2 != overlappedPortfolio) } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppRecursionTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppRecursionTest.scala index 5d90ac0da3e..aacba10aa61 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppRecursionTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppRecursionTest.scala @@ -45,7 +45,7 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain { | @Callable(i) | func default(end: Boolean, useSecondAddress: Boolean, forceInvoke: Boolean, forceReentrant: Boolean) = """.stripMargin - compile( + val scriptText = s""" | $prefix | if (end) @@ -53,7 +53,7 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain { | [] | else { | let endWithNextDApp = useSecondAddress && $sendEndToNext - | let sendEnd = $sendEnd || endWithNextDApp + | let sendEnd = $sendEnd|| endWithNextDApp | let address = ${secondNextDApp.fold("")(a => s"if (useSecondAddress) then Address(base58'$a') else")} Address(base58'$nextDApp') | strict r = | if (forceInvoke || endWithNextDApp) # param 'endWithNextDApp' is used for self-recursion check without reentrancy @@ -69,7 +69,7 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain { | [] | } """.stripMargin - ) + compile(scriptText) } // A -> A -> B -> B diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeActionsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeActionsTest.scala index 89cdbbe6b23..df8842e2198 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeActionsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeActionsTest.scala @@ -51,7 +51,7 @@ class SyncInvokeActionsTest extends PropSpec with WithDomain { | func default() = { | strict assetId = Address(base58'$dApp2Address').invoke("default", [], []) | [ - | ScriptTransfer(i.caller, 1000, assetId.exactAs[ByteVector]) + | ScriptTransfer(i.caller, 700, assetId.exactAs[ByteVector]) | ] | } """.stripMargin @@ -62,15 +62,15 @@ class SyncInvokeActionsTest extends PropSpec with WithDomain { | func default() = { | let issue = Issue("name", "", 1000, 4, true, unit, 0) | let assetId = issue.calculateAssetId() - | ([issue, ScriptTransfer(i.caller, 1000, assetId)], assetId) + | ([issue, ScriptTransfer(i.caller, 900, assetId)], assetId) | } """.stripMargin ) d.appendBlock(setScript(dApp1Signer, dApp1), setScript(dApp2Signer, dApp2)) d.appendAndAssertSucceed(invoke(dApp1Address, fee = invokeFee(issues = 1))) - d.liquidDiff.portfolios(dApp1Address).assets.head._2 shouldBe 0 - d.liquidDiff.portfolios(dApp2Address).assets.head._2 shouldBe 0 - d.liquidDiff.portfolios(defaultAddress).assets.head._2 shouldBe 1000 + d.liquidDiff.portfolios(dApp1Address).assets.head._2 shouldBe 200 + d.liquidDiff.portfolios(dApp2Address).assets.head._2 shouldBe 100 + d.liquidDiff.portfolios(defaultAddress).assets.head._2 shouldBe 700 } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeDiffTest.scala index 0c355a040c3..af5c4ff3d81 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeDiffTest.scala @@ -240,7 +240,7 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w | LeaseCancel(l.calculateLeaseId()) | ] | else - | throw("Balance check failed") + | throw("Balance check failed ${}") | else | throw("Bad state") | else diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ContextFunctionsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ContextFunctionsTest.scala index 3fe6691956d..995775ebfe2 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ContextFunctionsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ContextFunctionsTest.scala @@ -21,7 +21,6 @@ import com.wavesplatform.lang.v1.estimator.v2.ScriptEstimatorV2 import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} import com.wavesplatform.lang.v1.parser.Parser -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.state.* import com.wavesplatform.state.diffs.smart.smartEnabledFS import com.wavesplatform.test.* @@ -720,8 +719,8 @@ class ContextFunctionsTest extends PropSpec with WithDomain with EthHelpers { val expr = Parser.parseContract(script).get.value val ctx = - PureContext.build(version, useNewPowPrecision = true).withEnvironment[Environment] |+| - CryptoContext.build(Global, version).withEnvironment[Environment] |+| + PureContext.build(version, useNewPowPrecision = true) |+| + CryptoContext.build(Global, version) |+| WavesContext.build(Global, DirectiveSet(version, Account, DApp).explicitGet(), fixBigScriptField = true) val compiledScript = ContractScript(version, ContractCompiler(ctx.compilerContext, expr, version).explicitGet()).explicitGet() 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 926036e5b8d..a7995f23985 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 @@ -12,15 +12,14 @@ import com.wavesplatform.lang.Testing.evaluated 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.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.* @@ -29,11 +28,10 @@ import com.wavesplatform.test.* 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} import com.wavesplatform.transaction.{Asset, DataTransaction, Proofs, TxHelpers, TxVersion} import com.wavesplatform.utils.EmptyBlockchain -import monix.eval.Coeval import org.scalamock.scalatest.PathMockFactory import org.scalatest.EitherValues import play.api.libs.json.Json @@ -785,18 +783,16 @@ class TransactionBindingsTest extends PropSpec with PathMockFactory with EitherV val expr = Parser.parseExpr(script).get.value val directives = DirectiveSet(V2, AssetType, Expression).explicitGet() val ctx = - PureContext.build(V2, useNewPowPrecision = true).withEnvironment[Environment] |+| - CryptoContext.build(Global, V2).withEnvironment[Environment] |+| + PureContext.build(V2, useNewPowPrecision = true) |+| + CryptoContext.build(Global, V2) |+| WavesContext.build(Global, DirectiveSet(V2, AssetType, Expression).explicitGet(), fixBigScriptField = true) - val environment = new WavesEnvironment( - chainId, - Coeval(???), + val environment = WavesEnvironment( null, - EmptyBlockchain, Coproduct[Environment.Tthis](Environment.AssetId(Array())), + ByteStr.empty, directives, - ByteStr.empty + EmptyBlockchain ) for { compileResult <- compiler.ExpressionCompiler(ctx.compilerContext, expr) @@ -816,18 +812,16 @@ class TransactionBindingsTest extends PropSpec with PathMockFactory with EitherV (() => blockchain.activatedFeatures).when().returning(Map(BlockchainFeatures.BlockV5.id -> 0)) val ctx = - PureContext.build(V2, useNewPowPrecision = true).withEnvironment[Environment] |+| - CryptoContext.build(Global, V2).withEnvironment[Environment] |+| + PureContext.build(V2, useNewPowPrecision = true) |+| + CryptoContext.build(Global, V2) |+| WavesContext.build(Global, directives, fixBigScriptField = true) - val env = new WavesEnvironment( - chainId, - Coeval(buildThisValue(t, blockchain, directives, Coproduct[Environment.Tthis](Environment.AssetId(Array()))).explicitGet()), + val env = WavesEnvironment( null, - EmptyBlockchain, Coproduct[Environment.Tthis](Environment.AssetId(Array())), + ByteStr.empty, directives, - ByteStr.empty + EmptyBlockchain ) for { diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/package.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/package.scala index 43b9d510d85..83f4ac54006 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/package.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/package.scala @@ -1,12 +1,13 @@ package com.wavesplatform.state.diffs.smart -import cats.syntax.either._ +import cats.syntax.either.* import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.{Base64, EitherExt2} import com.wavesplatform.crypto +import com.wavesplatform.lang.Common import com.wavesplatform.lang.directives.DirectiveSet -import com.wavesplatform.lang.directives.values._ -import com.wavesplatform.lang.utils._ +import com.wavesplatform.lang.directives.values.* +import com.wavesplatform.lang.utils.* import com.wavesplatform.lang.v1.compiler.ExpressionCompiler import com.wavesplatform.lang.v1.compiler.Terms.EVALUATED import com.wavesplatform.lang.v1.evaluator.EvaluatorV1 @@ -30,18 +31,13 @@ package object predef { compileResult <- ExpressionCompiler(compilerContext(version, Expression, isAssetScript = false), expr) (typedExpr, _) = compileResult directives = DirectiveSet(version, Account, Expression).explicitGet() - evalContext <- BlockchainContext.build( - version, - chainId, - Coeval.evalOnce(buildThisValue(t, blockchain, directives, Coproduct[Environment.Tthis](Environment.AssetId(Array())))).map(_.explicitGet()), - Coeval.evalOnce(blockchain.height), - blockchain, - isTokenContext = false, - isContract = false, - Coproduct[Environment.Tthis](Environment.AssetId(Array())), - ByteStr.empty, - fixUnicodeFunctions = true, - useNewPowPrecision = true, + evalContext = BlockchainContext.build( + directives, + Common.emptyBlockchainEnvironment(in = + Coeval.evalOnce(buildThisValue(t, blockchain, directives, Coproduct[Environment.Tthis](Environment.AssetId(Array())))).map(_.explicitGet()) + ), + true, + true, fixBigScriptField = true ) r <- EvaluatorV1().apply[T](evalContext, typedExpr).leftMap(_.message) @@ -230,7 +226,7 @@ package object predef { | let sender = t.sender == Address(base58'${t.sender.toAddress}') | let senderPublicKey = t.senderPublicKey == base58'${t.sender}' | let version = t.version == $version - | ${ if (checkProofs) Range(0, 8).map(letProof(proofs, "t")).mkString("\n") else ""} + | ${if (checkProofs) Range(0, 8).map(letProof(proofs, "t")).mkString("\n") else ""} """.stripMargin } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala index bf62561a6a7..46cb9aedadd 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala @@ -15,14 +15,13 @@ import com.wavesplatform.lang.v1.compiler.{ExpressionCompiler, TestCompiler} import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} import com.wavesplatform.lang.v1.parser.* -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.settings.{Constants, FunctionalitySettings, TestFunctionalitySettings} import com.wavesplatform.state.* import com.wavesplatform.state.diffs.* import com.wavesplatform.state.reader.CompositeBlockchain import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.* import com.wavesplatform.transaction.* +import com.wavesplatform.transaction.Asset.* class BalancesV4Test extends PropSpec with WithState { @@ -115,8 +114,8 @@ class BalancesV4Test extends PropSpec with WithState { def assetScript(acc: ByteStr): Script = { val ctx = { val directives = DirectiveSet(V4, AssetType, Expression).explicitGet() - PureContext.build(V4, useNewPowPrecision = true).withEnvironment[Environment] |+| - CryptoContext.build(Global, V4).withEnvironment[Environment] |+| + PureContext.build(V4, useNewPowPrecision = true) |+| + CryptoContext.build(Global, V4) |+| WavesContext.build(Global, directives, fixBigScriptField = true) } @@ -177,8 +176,8 @@ class BalancesV4Test extends PropSpec with WithState { def assetScript(acc: ByteStr): Script = { val ctx = { val directives = DirectiveSet(V4, AssetType, Expression).explicitGet() - PureContext.build(V4, useNewPowPrecision = true).withEnvironment[Environment] |+| - CryptoContext.build(Global, V4).withEnvironment[Environment] |+| + PureContext.build(V4, useNewPowPrecision = true) |+| + CryptoContext.build(Global, V4) |+| WavesContext.build(Global, directives, fixBigScriptField = true) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/NotaryControlledTransferScenarioTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/NotaryControlledTransferScenarioTest.scala index 89152e2fabc..4273c08dabe 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/NotaryControlledTransferScenarioTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/NotaryControlledTransferScenarioTest.scala @@ -1,7 +1,7 @@ package com.wavesplatform.state.diffs.smart.scenarios -import cats.syntax.either.* import cats.Id +import cats.syntax.either.* import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.db.WithState @@ -14,17 +14,14 @@ import com.wavesplatform.lang.v1.compiler.Terms.EVALUATED import com.wavesplatform.lang.v1.evaluator.EvaluatorV1 import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext import com.wavesplatform.lang.v1.parser.Parser -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.{Global, Testing} import com.wavesplatform.state.* import com.wavesplatform.state.diffs.smart.* -import com.wavesplatform.state.diffs.smart.predef.chainId import com.wavesplatform.test.* import com.wavesplatform.transaction.Asset.IssuedAsset -import com.wavesplatform.transaction.smart.WavesEnvironment import com.wavesplatform.transaction.TxHelpers +import com.wavesplatform.transaction.smart.WavesEnvironment import com.wavesplatform.utils.EmptyBlockchain -import monix.eval.Coeval class NotaryControlledTransferScenarioTest extends PropSpec with WithState { @@ -37,25 +34,25 @@ class NotaryControlledTransferScenarioTest extends PropSpec with WithState { val genesis = Seq(company, king, notary, accountA, accountB).map(acc => TxHelpers.genesis(acc.toAddress)) - val assetScript = s""" - | - | match tx { - | case ttx: TransferTransaction => - | let king = Address(base58'${king.toAddress}') - | let company = Address(base58'${company.toAddress}') - | let notary1 = addressFromPublicKey(extract(getBinary(king, "notary1PK"))) - | let txIdBase58String = toBase58String(ttx.id) - | let isNotary1Agreed = match getBoolean(notary1,txIdBase58String) { - | case b : Boolean => b - | case _ : Unit => false - | } - | let recipientAddress = addressFromRecipient(ttx.recipient) - | let recipientAgreement = getBoolean(recipientAddress,txIdBase58String) - | let isRecipientAgreed = if(isDefined(recipientAgreement)) then extract(recipientAgreement) else false - | let senderAddress = addressFromPublicKey(ttx.senderPublicKey) - | senderAddress.bytes == company.bytes || (isNotary1Agreed && isRecipientAgreed) - | case _ => throw() - | } + val assetScript = s""" + | + | match tx { + | case ttx: TransferTransaction => + | let king = Address(base58'${king.toAddress}') + | let company = Address(base58'${company.toAddress}') + | let notary1 = addressFromPublicKey(extract(getBinary(king, "notary1PK"))) + | let txIdBase58String = toBase58String(ttx.id) + | let isNotary1Agreed = match getBoolean(notary1,txIdBase58String) { + | case b : Boolean => b + | case _ : Unit => false + | } + | let recipientAddress = addressFromRecipient(ttx.recipient) + | let recipientAgreement = getBoolean(recipientAddress,txIdBase58String) + | let isRecipientAgreed = if(isDefined(recipientAgreement)) then extract(recipientAgreement) else false + | let senderAddress = addressFromPublicKey(ttx.senderPublicKey) + | senderAddress.bytes == company.bytes || (isNotary1Agreed && isRecipientAgreed) + | case _ => throw() + | } """.stripMargin val untypedScript = Parser.parseExpr(assetScript).get.value val typedScript = ExprScript(ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untypedScript).explicitGet()._1) @@ -80,9 +77,9 @@ class NotaryControlledTransferScenarioTest extends PropSpec with WithState { ) } - private val dummyEvalContext: EvaluationContext[Environment, Id] = { + private val dummyEvalContext: EvaluationContext[Id] = { val ds = DirectiveSet(V1, Asset, Expression).explicitGet() - val environment = new WavesEnvironment(chainId, Coeval(???), null, EmptyBlockchain, null, ds, ByteStr.empty) + val environment = WavesEnvironment(null, null, ByteStr.empty, ds, EmptyBlockchain) lazyContexts((ds, true, true))().evaluationContext(environment) } @@ -115,7 +112,7 @@ class NotaryControlledTransferScenarioTest extends PropSpec with WithState { append(Seq(issue, kingDataTransaction, transferFromCompanyToA)).explicitGet() append(Seq(transferFromAToB)) should produce("NotAllowedByScript") append(Seq(notaryDataTransaction)).explicitGet() - append(Seq(transferFromAToB)) should produce("NotAllowedByScript") //recipient should accept tx + append(Seq(transferFromAToB)) should produce("NotAllowedByScript") // recipient should accept tx append(Seq(accountBDataTransaction)).explicitGet() append(Seq(transferFromAToB)).explicitGet() } diff --git a/node/src/test/scala/com/wavesplatform/transaction/IssueTransactionV2Specification.scala b/node/src/test/scala/com/wavesplatform/transaction/IssueTransactionV2Specification.scala index b65704bf306..bfccb65c9df 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/IssueTransactionV2Specification.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/IssueTransactionV2Specification.scala @@ -12,7 +12,6 @@ import com.wavesplatform.lang.v1.compiler import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} import com.wavesplatform.lang.v1.parser.Parser -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.{Global, utils} import com.wavesplatform.state.HistoryTest import com.wavesplatform.test.PropSpec @@ -46,28 +45,28 @@ class IssueTransactionV2Specification extends PropSpec with WithNewDBForEachTest "AAMCVNUoqr7DXKEA2Hx7ehKGMvrxnNRFMYGUV0RRE6MqIe8iAAhHaWdhY29pbgAIR2lnYWNvaW4AAAACVAvkAAgBAAAAAAX14QAAAAFjXdP0HQABAAEAQJgqUCQFUctLLrdJY8pUMZ3zO8sGtTL6xZhiVLDGaM8xG9r7ll2rPepblKWwbgP/QqZ0C8aAg2IMxY5E7hbUsos=" ) val json = Json.parse(""" - |{ - | "type": 3, - | "id": "2ykNAo5JrvNCcL8PtCmc9pTcNtKUy2PjJkrFdRvTfUf4", - | "sender": "3N5GRqzDBhjVXnCn44baHcz2GoZy5qLxtTh", - | "senderPublicKey": "FM5ojNqW7e9cZ9zhPYGkpSP1Pcd8Z3e3MNKYVS5pGJ8Z", - | "fee": 100000000, - | "feeAssetId": null, - | "timestamp": 1526287561757, - | "proofs": [ - | "43TCfWBa6t2o2ggsD4bU9FpvH3kmDbSBWKE1Z6B5i5Ax5wJaGT2zAvBihSbnSS3AikZLcicVWhUk1bQAMWVzTG5g" - | ], - | "version": 2, - | "assetId": "2ykNAo5JrvNCcL8PtCmc9pTcNtKUy2PjJkrFdRvTfUf4", - | "chainId": 84, - | "name": "Gigacoin", - | "quantity": 10000000000, - | "reissuable": true, - | "decimals": 8, - | "description": "Gigacoin", - | "script":null - |} - |""".stripMargin) + |{ + | "type": 3, + | "id": "2ykNAo5JrvNCcL8PtCmc9pTcNtKUy2PjJkrFdRvTfUf4", + | "sender": "3N5GRqzDBhjVXnCn44baHcz2GoZy5qLxtTh", + | "senderPublicKey": "FM5ojNqW7e9cZ9zhPYGkpSP1Pcd8Z3e3MNKYVS5pGJ8Z", + | "fee": 100000000, + | "feeAssetId": null, + | "timestamp": 1526287561757, + | "proofs": [ + | "43TCfWBa6t2o2ggsD4bU9FpvH3kmDbSBWKE1Z6B5i5Ax5wJaGT2zAvBihSbnSS3AikZLcicVWhUk1bQAMWVzTG5g" + | ], + | "version": 2, + | "assetId": "2ykNAo5JrvNCcL8PtCmc9pTcNtKUy2PjJkrFdRvTfUf4", + | "chainId": 84, + | "name": "Gigacoin", + | "quantity": 10000000000, + | "reissuable": true, + | "decimals": 8, + | "description": "Gigacoin", + | "script":null + |} + |""".stripMargin) val tx = IssueTxSerializer.parseBytes(bytes).get tx.json() shouldBe json @@ -136,8 +135,8 @@ class IssueTransactionV2Specification extends PropSpec with WithNewDBForEachTest Monoid .combineAll( Seq( - PureContext.build(V3, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, V3).withEnvironment[Environment], + PureContext.build(V3, useNewPowPrecision = true), + CryptoContext.build(Global, V3), WavesContext.build( Global, DirectiveSet(V3, Account, Expression).explicitGet(), diff --git a/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/FunctionComplexityTest.scala b/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/FunctionComplexityTest.scala index 8c2e91273e5..79a15fb73a4 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/FunctionComplexityTest.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/FunctionComplexityTest.scala @@ -4,18 +4,17 @@ import cats.kernel.Monoid import com.wavesplatform.account.{Address, PublicKey} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.lang.directives.values._ +import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet} -import com.wavesplatform.lang.v1.compiler.{ExpressionCompiler, _} +import com.wavesplatform.lang.v1.compiler.{ExpressionCompiler, *} import com.wavesplatform.lang.v1.estimator.ScriptEstimator import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} import com.wavesplatform.lang.v1.parser.Expressions.EXPR import com.wavesplatform.lang.v1.parser.Parser -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.{CTX, FunctionHeader} import com.wavesplatform.lang.{Global, utils} -import com.wavesplatform.state.diffs.smart.predef.{chainId, scriptWithAllV1Functions} +import com.wavesplatform.state.diffs.smart.predef.scriptWithAllV1Functions import com.wavesplatform.state.{BinaryDataEntry, BooleanDataEntry, IntegerDataEntry, StringDataEntry} import com.wavesplatform.test.PropSpec import com.wavesplatform.transaction.Asset.Waves @@ -26,22 +25,22 @@ import com.wavesplatform.utils.EmptyBlockchain import monix.eval.Coeval class FunctionComplexityTest(estimator: ScriptEstimator) extends PropSpec { - private val environment = new WavesEnvironment(chainId, Coeval(???), null, EmptyBlockchain, null, DirectiveSet.contractDirectiveSet, ByteStr.empty) + private val environment = WavesEnvironment(null, null, ByteStr.empty, DirectiveSet.contractDirectiveSet, EmptyBlockchain) private def estimate( expr: Terms.EXPR, - ctx: CTX[Environment], + ctx: CTX, funcCosts: Map[FunctionHeader, Coeval[Long]] ): Either[String, Long] = estimator(ctx.evaluationContext(environment).letDefs.keySet, funcCosts, expr) - private def ctx(version: StdLibVersion): CTX[Environment] = { + private def ctx(version: StdLibVersion): CTX = { utils.functionCosts(version) Monoid .combineAll( Seq( - PureContext.build(version, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, version).withEnvironment[Environment], + PureContext.build(version, useNewPowPrecision = true), + CryptoContext.build(Global, version), WavesContext.build( Global, DirectiveSet(version, Account, Expression).explicitGet(), diff --git a/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/UserFunctionComplexityTest.scala b/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/UserFunctionComplexityTest.scala index 6e5d2cc7ca4..47de07671dd 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/UserFunctionComplexityTest.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/UserFunctionComplexityTest.scala @@ -10,19 +10,17 @@ import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.estimator.ScriptEstimator import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, GlobalValNames, PureContext} -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.{CTX, FunctionHeader} import com.wavesplatform.lang.{Global, utils} -import com.wavesplatform.state.diffs.smart.predef.chainId import com.wavesplatform.test.PropSpec import com.wavesplatform.transaction.smart.WavesEnvironment import com.wavesplatform.utils.EmptyBlockchain import monix.eval.Coeval class UserFunctionComplexityTest(estimator: ScriptEstimator) extends PropSpec { - private val environment = new WavesEnvironment(chainId, Coeval(???), null, EmptyBlockchain, null, DirectiveSet.contractDirectiveSet, ByteStr.empty) + private val environment = WavesEnvironment(null, null, ByteStr.empty, DirectiveSet.contractDirectiveSet, EmptyBlockchain) - private def estimate(expr: EXPR, ctx: CTX[Environment], funcCosts: Map[FunctionHeader, Coeval[Long]]): Either[String, Long] = { + private def estimate(expr: EXPR, ctx: CTX, funcCosts: Map[FunctionHeader, Coeval[Long]]): Either[String, Long] = { estimator(ctx.evaluationContext(environment).letDefs.keySet, funcCosts, expr) } @@ -31,8 +29,8 @@ class UserFunctionComplexityTest(estimator: ScriptEstimator) extends PropSpec { Monoid .combineAll( Seq( - PureContext.build(V1, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, V1).withEnvironment[Environment], + PureContext.build(V1, useNewPowPrecision = true), + CryptoContext.build(Global, V1), WavesContext.build( Global, DirectiveSet(V1, Account, Expression).explicitGet(), @@ -98,8 +96,8 @@ class UserFunctionComplexityTest(estimator: ScriptEstimator) extends PropSpec { Monoid .combineAll( Seq( - PureContext.build(V2, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, V2).withEnvironment[Environment], + PureContext.build(V2, useNewPowPrecision = true), + CryptoContext.build(Global, V2), WavesContext.build( Global, DirectiveSet(V2, Account, Expression).explicitGet(), @@ -165,8 +163,8 @@ class UserFunctionComplexityTest(estimator: ScriptEstimator) extends PropSpec { Monoid .combineAll( Seq( - PureContext.build(V3, useNewPowPrecision = true).withEnvironment[Environment], - CryptoContext.build(Global, V3).withEnvironment[Environment], + PureContext.build(V3, useNewPowPrecision = true), + CryptoContext.build(Global, V3), WavesContext.build( Global, DirectiveSet(V3, Account, Expression).explicitGet(), diff --git a/node/src/test/scala/com/wavesplatform/utils/UtilsSpecification.scala b/node/src/test/scala/com/wavesplatform/utils/UtilsSpecification.scala index 24fd11dd64d..32ec77ff8e6 100644 --- a/node/src/test/scala/com/wavesplatform/utils/UtilsSpecification.scala +++ b/node/src/test/scala/com/wavesplatform/utils/UtilsSpecification.scala @@ -4,24 +4,21 @@ import cats.Id import com.wavesplatform.common.state.ByteStr import com.wavesplatform.lang.directives.DirectiveSet import com.wavesplatform.lang.directives.values.V3 -import com.wavesplatform.lang.utils._ +import com.wavesplatform.lang.utils.* import com.wavesplatform.lang.v1.compiler.Terms.{FUNCTION_CALL, TRUE} import com.wavesplatform.lang.v1.compiler.Types.BOOLEAN import com.wavesplatform.lang.v1.evaluator.ctx.{EvaluationContext, UserFunction} -import com.wavesplatform.lang.v1.traits.Environment -import com.wavesplatform.state.diffs.smart.predef.chainId import com.wavesplatform.test.FreeSpec import com.wavesplatform.transaction.smart.WavesEnvironment -import monix.eval.Coeval class UtilsSpecification extends FreeSpec { - private val environment = new WavesEnvironment(chainId, Coeval(???), null, EmptyBlockchain, null, DirectiveSet.contractDirectiveSet, ByteStr.empty) + private val environment = WavesEnvironment(null, null, ByteStr.empty, DirectiveSet.contractDirectiveSet, EmptyBlockchain) "estimate()" - { "handles functions that depend on each other" in { - val callee = UserFunction[Environment]("callee", 0, BOOLEAN)(TRUE) - val caller = UserFunction[Environment]("caller", 0, BOOLEAN)(FUNCTION_CALL(callee.header, List.empty)) - val ctx = EvaluationContext.build[Id, Environment]( + val callee = UserFunction("callee", 0, BOOLEAN)(TRUE) + val caller = UserFunction("caller", 0, BOOLEAN)(FUNCTION_CALL(callee.header, List.empty)) + val ctx = EvaluationContext.build[Id]( environment, typeDefs = Map.empty, letDefs = Map.empty, diff --git a/project/Dependencies.scala b/project/Dependencies.scala index d05275c5c87..bdcf21a2c14 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -92,6 +92,8 @@ object Dependencies { "org.rocksdb" % "rocksdbjni" % "8.0.0" ) + val scalaLogging: ModuleID = "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5" + lazy val node = Def.setting( Seq( ("org.rudogma" %%% "supertagged" % "2.0-RC2").exclude("org.scala-js", "scalajs-library_2.13"), @@ -115,7 +117,7 @@ object Dependencies { kindProjector, monixModule("reactive").value, nettyHandler, - "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5", + scalaLogging, "eu.timepit" %% "refined" % "0.10.2" exclude ("org.scala-lang.modules", "scala-xml_2.13"), "eu.timepit" %% "refined-cats" % "0.10.2" exclude ("org.scala-lang.modules", "scala-xml_2.13"), "com.esaulpaugh" % "headlong" % "9.2.0",