From 3ad38d1e33f9d684da52b45ce4be92c507895beb Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Wed, 5 Apr 2023 13:36:56 +0400 Subject: [PATCH] Miniev implementation --- .../com/wavesplatform/state/DBState.scala | 16 +- .../v1/EnvironmentFunctionsBenchmark.scala | 2 +- .../lang/v1/EvaluatorV2Benchmark.scala | 92 +++- .../lang/v1/PureFunctionsRebenchmark.scala | 15 +- .../lang/v1/ScriptEvaluatorBenchmark.scala | 11 +- .../com/wavesplatform/lang/v1/package.scala | 7 +- .../state/WavesEnvironmentBenchmark.scala | 4 +- build.sbt | 11 +- .../wavesplatform/lang/utils/Logging.scala | 5 + .../scala/com/wavesplatform/lang/Global.scala | 8 +- .../wavesplatform/lang/utils/Logging.scala | 7 + .../wavesplatform/lang/ExecutionError.scala | 9 +- .../wavesplatform/lang/ValidationError.scala | 2 - .../lang/miniev/ComplexityCounter.scala | 38 ++ .../lang/miniev/ComplexityLimit.scala | 21 + .../com/wavesplatform/lang/miniev/Ev.scala | 244 +++++++++ .../wavesplatform/lang/miniev/Functions.scala | 11 + .../com/wavesplatform/lang/miniev/Op.scala | 60 +++ .../com/wavesplatform/lang/miniev/State.scala | 96 ++++ .../lang/script/ContractScript.scala | 10 +- .../wavesplatform/lang/utils/package.scala | 14 +- .../scala/com/wavesplatform/lang/v1/CTX.scala | 60 ++- .../lang/v1/FunctionHeader.scala | 10 +- .../lang/v1/compiler/Decompiler.scala | 4 +- .../lang/v1/compiler/ExpressionCompiler.scala | 22 +- .../lang/v1/compiler/Terms.scala | 4 +- .../v1/estimator/v2/ScriptEstimatorV2.scala | 16 +- .../v1/estimator/v3/ScriptEstimatorV3.scala | 4 +- .../lang/v1/evaluator/Contextful.scala | 65 +-- .../lang/v1/evaluator/ContractEvaluator.scala | 82 ++- .../lang/v1/evaluator/EvaluatorV1.scala | 94 ++-- .../lang/v1/evaluator/EvaluatorV2.scala | 124 +---- .../lang/v1/evaluator/ScriptResult.scala | 64 ++- .../v1/evaluator/ctx/EvaluationContext.scala | 89 +--- .../v1/evaluator/ctx/InvariableContext.scala | 10 +- .../lang/v1/evaluator/ctx/LazyVal.scala | 9 +- .../v1/evaluator/ctx/NativeFunction.scala | 152 +++--- .../v1/evaluator/ctx/impl/CryptoContext.scala | 115 +++-- .../v1/evaluator/ctx/impl/PureContext.scala | 284 +++++------ .../lang/v1/evaluator/ctx/impl/Rounding.scala | 13 +- .../lang/v1/evaluator/ctx/impl/package.scala | 2 +- .../evaluator/ctx/impl/waves/Functions.scala | 338 +++++-------- .../v1/evaluator/ctx/impl/waves/Vals.scala | 32 +- .../ctx/impl/waves/WavesContext.scala | 11 +- .../lang/v1/evaluator/package.scala | 8 +- .../lang/v1/serialization/SerdeV1.scala | 2 +- .../lang/v1/serialization/SerdeV2.scala | 2 +- .../lang/v1/traits/Environment.scala | 2 + .../common/state/ByteStrTest.scala | 2 +- .../scala/com/wavesplatform/lang/Common.scala | 61 +-- .../lang/v1/compiler/TestCompiler.scala | 26 +- .../lang/ContractIntegrationTest.scala | 20 +- .../wavesplatform/lang/IntegrationTest.scala | 78 ++- .../ContractCompilerCompactorTest.scala | 16 +- .../lang/compiler/ContractCompilerTest.scala | 25 +- .../lang/compiler/DecompilerTest.scala | 44 +- .../compiler/ExpressionCompilerV1Test.scala | 5 +- .../compiler/ScriptPreprocessorTest.scala | 12 +- .../wavesplatform/lang/compiler/package.scala | 75 +-- .../lang/doc/FunctionComplexityDocTest.scala | 3 +- .../wavesplatform/lang/doc/VarsDocTest.scala | 9 +- .../estimator/ScriptEstimatorTestBase.scala | 31 +- .../lang/estimator/package.scala | 3 +- .../lang/evaluator/EvaluatorSpec.scala | 17 +- .../evaluator/EvaluatorV1CaseObjField.scala | 15 +- .../lang/evaluator/EvaluatorV1V2Test.scala | 254 +++++----- .../lang/evaluator/EvaluatorV2Test.scala | 31 +- .../lang/evaluator/ScriptResultTest.scala | 9 +- .../evaluator/string/SplitFunctionTest.scala | 5 +- .../wavesplatform/lang/miniev/EvTest.scala | 104 ++++ .../com/wavesplatform/utils/RSATest.scala | 55 +- .../com/wavesplatform/account/package.scala | 2 - .../api/http/utils/UtilsEvaluator.scala | 18 +- .../com/wavesplatform/database/Caches.scala | 2 +- .../state/SnapshotBlockchain.scala | 16 +- .../state/diffs/BlockDiffer.scala | 30 +- .../state/diffs/invoke/CachedDAppCTX.scala | 12 +- .../diffs/invoke/InvokeDiffsCommon.scala | 18 +- .../state/diffs/invoke/InvokeScriptDiff.scala | 474 ------------------ .../invoke/InvokeScriptTransactionDiff.scala | 96 ++-- .../transaction/smart/BlockchainContext.scala | 14 +- .../transaction/smart/DAppState.scala | 323 ++++++++++++ .../transaction/smart/InvokeFunction.scala | 154 ++++++ .../transaction/smart/WavesEnvironment.scala | 82 ++- .../smart/script/ScriptRunner.scala | 41 +- node/src/test/resources/logback-test.xml | 1 + .../com/wavesplatform/history/Domain.scala | 5 +- .../EvaluatedPBSerializationTest.scala | 4 +- .../diffs/ci/OverheadCallableCallTest.scala | 2 + .../diffs/ci/sync/InvocationTrackerTest.scala | 56 +++ .../ci/sync/SyncDAppComplexityCountTest.scala | 3 +- .../diffs/ci/sync/SyncDAppRecursionTest.scala | 6 +- .../diffs/ci/sync/SyncInvokeActionsTest.scala | 4 +- .../diffs/ci/sync/SyncInvokeDiffTest.scala | 2 +- .../smart/predef/ContextFunctionsTest.scala | 5 +- .../predef/TransactionBindingsTest.scala | 22 +- .../state/diffs/smart/predef/package.scala | 28 +- .../smart/scenarios/BalancesV4Test.scala | 9 +- ...NotaryControlledTransferScenarioTest.scala | 51 +- .../IssueTransactionV2Specification.scala | 49 +- .../wavesplatform/transaction/TxHelpers.scala | 3 + .../estimator/FunctionComplexityTest.scala | 17 +- .../UserFunctionComplexityTest.scala | 18 +- .../utils/UtilsSpecification.scala | 15 +- project/Dependencies.scala | 4 +- .../com/wavesplatform/lang/v1/repl/Repl.scala | 31 +- .../lang/v1/repl/ReplEngine.scala | 13 +- .../wavesplatform/lang/v1/repl/package.scala | 6 +- 108 files changed, 2640 insertions(+), 2197 deletions(-) create mode 100644 lang/js/src/main/scala/com/wavesplatform/lang/utils/Logging.scala create mode 100644 lang/jvm/src/main/scala/com/wavesplatform/lang/utils/Logging.scala create mode 100644 lang/shared/src/main/scala/com/wavesplatform/lang/miniev/ComplexityCounter.scala create mode 100644 lang/shared/src/main/scala/com/wavesplatform/lang/miniev/ComplexityLimit.scala create mode 100644 lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Ev.scala create mode 100644 lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Functions.scala create mode 100644 lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Op.scala create mode 100644 lang/shared/src/main/scala/com/wavesplatform/lang/miniev/State.scala create mode 100644 lang/tests/src/test/scala/com/wavesplatform/lang/miniev/EvTest.scala delete mode 100644 node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptDiff.scala create mode 100644 node/src/main/scala/com/wavesplatform/transaction/smart/DAppState.scala create mode 100644 node/src/main/scala/com/wavesplatform/transaction/smart/InvokeFunction.scala create mode 100644 node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/InvocationTrackerTest.scala diff --git a/benchmark/src/main/scala/com/wavesplatform/state/DBState.scala b/benchmark/src/main/scala/com/wavesplatform/state/DBState.scala index 7f39682c733..b342f265665 100644 --- a/benchmark/src/main/scala/com/wavesplatform/state/DBState.scala +++ b/benchmark/src/main/scala/com/wavesplatform/state/DBState.scala @@ -8,7 +8,6 @@ import com.wavesplatform.lang.directives.DirectiveSet import com.wavesplatform.settings.WavesSettings import com.wavesplatform.transaction.smart.WavesEnvironment import com.wavesplatform.utils.ScorexLogging -import monix.eval.Coeval import org.openjdk.jmh.annotations.{Param, Scope, State, TearDown} import java.io.File @@ -33,14 +32,13 @@ abstract class DBState extends ScorexLogging { AddressScheme.current = new AddressScheme { override val chainId: Byte = 'W' } lazy val environment = 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 bf14f6b9b1d..e25444e17ae 100644 --- a/benchmark/src/test/scala/com/wavesplatform/lang/v1/EnvironmentFunctionsBenchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/lang/v1/EnvironmentFunctionsBenchmark.scala @@ -169,7 +169,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 f9770b1630c..a3b8c919495 100644 --- a/benchmark/src/test/scala/com/wavesplatform/lang/v1/EvaluatorV2Benchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/lang/v1/EvaluatorV2Benchmark.scala @@ -1,14 +1,14 @@ package com.wavesplatform.lang.v1 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 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 @@ -16,9 +16,9 @@ import java.util.concurrent.TimeUnit import scala.annotation.tailrec object EvaluatorV2Benchmark { - val pureContext = PureContext.build(V1, useNewPowPrecision = true).withEnvironment[Environment] - val pureEvalContext = pureContext.evaluationContext(Common.emptyBlockchainEnvironment()) - val evaluatorV2 = new EvaluatorV2(DisabledLogEvaluationContext(pureEvalContext), V1, Int.MaxValue, true, false, true, true, true) + 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) @@ -42,6 +42,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) @@ -56,7 +95,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) @@ -69,7 +111,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) @@ -113,7 +170,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) @@ -157,7 +214,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 b820b97c285..9dac6ac3b4b 100644 --- a/benchmark/src/test/scala/com/wavesplatform/lang/v1/ScriptEvaluatorBenchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/lang/v1/ScriptEvaluatorBenchmark.scala @@ -9,11 +9,13 @@ import com.wavesplatform.lang.v1.EnvironmentFunctionsBenchmark.{curve25519, rand 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.EvaluatorV1.* import com.wavesplatform.lang.v1.evaluator.FunctionIds import com.wavesplatform.lang.v1.evaluator.FunctionIds.{FROMBASE58, SIGVERIFY, TOBASE58} import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.{Common, Global} +import com.wavesplatform.lang.{Common, Global} import org.openjdk.jmh.annotations.* import org.openjdk.jmh.infra.Blackhole @@ -22,11 +24,10 @@ import scala.concurrent.duration.SECONDS import scala.util.Random object ScriptEvaluatorBenchmark { - val lastVersion = StdLibVersion.VersionDic.all.max - val context = - (PureContext.build(lastVersion, useNewPowPrecision = true) |+| CryptoContext.build(Global, lastVersion)) - .withEnvironment[Environment] - .evaluationContext(Common.emptyBlockchainEnvironment()) + val version = V1 + val pureEvalContext: EvaluationContext[Id] = + PureContext.build(V1, useNewPowPrecision = true).evaluationContext(Common.emptyBlockchainEnvironment()) + val evaluatorV1: EvaluatorV1[Id] = new EvaluatorV1[Id]() } @OutputTimeUnit(TimeUnit.MICROSECONDS) 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 eedd9024c08..bae35fab22e 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,7 +27,7 @@ package object v1 { ) def eval( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], expr: EXPR, stdLibVersion: StdLibVersion = StdLibVersion.VersionDic.all.max ): (Log[Id], Int, Either[ExecutionError, Terms.EVALUATED]) = @@ -41,4 +41,7 @@ package object v1 { enableExecutionLog = false, fixedThrownError = true ) + + 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 8e831b286c1..afe1bb15499 100644 --- a/benchmark/src/test/scala/com/wavesplatform/state/WavesEnvironmentBenchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/state/WavesEnvironmentBenchmark.scala @@ -134,7 +134,7 @@ object WavesEnvironmentBenchmark { RDB.open(wavesSettings.dbSettings) } - val environment: Environment[Id] = { + val environment: Environment[Id] = ???/*{ val state = new RocksDBWriter(rdb, wavesSettings.blockchainSettings, wavesSettings.dbSettings, wavesSettings.enableLightMode) WavesEnvironment( AddressScheme.current.chainId, @@ -145,7 +145,7 @@ object WavesEnvironmentBenchmark { DirectiveSet.contractDirectiveSet, ByteStr.empty ) - } + }*/ @TearDown def close(): Unit = { diff --git a/build.sbt b/build.sbt index 7f4f492311c..446812a9642 100644 --- a/build.sbt +++ b/build.sbt @@ -37,10 +37,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..ac8175b7cac --- /dev/null +++ b/lang/js/src/main/scala/com/wavesplatform/lang/utils/Logging.scala @@ -0,0 +1,5 @@ +package com.wavesplatform.lang.utils + +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 bc34adde7c3..627e6c279a7 100644 --- a/lang/jvm/src/main/scala/com/wavesplatform/lang/Global.scala +++ b/lang/jvm/src/main/scala/com/wavesplatform/lang/Global.scala @@ -103,13 +103,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 { 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 63dfdfea2ec..de84185f41d 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/ExecutionError.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/ExecutionError.scala @@ -4,8 +4,15 @@ 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 ThrownError(message: String) extends ExecutionError 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/ValidationError.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/ValidationError.scala index 4df5c9cccd1..81574622e01 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/ValidationError.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/ValidationError.scala @@ -6,6 +6,4 @@ object ValidationError { type Validation[T] = Either[ValidationError, T] case class ScriptParseError(m: String) extends ValidationError - case class ScriptRunsLimitError(m: String) extends ValidationError - } 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..e5c9f80695c --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Ev.scala @@ -0,0 +1,244 @@ +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 FAILED_DEC => Left(CommonError("Encountered failed declaration")) + } + 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 other => Left(CommonError(s"Could not find constructor for type $other")) + } + } + case other => Left(CommonError(s"Unexpected call to $other")) + } + } + } + + 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") + ps.ret(evaluated) match { + case (Left(err), _) => err.asLeft + case (Right(expr), maybeScope) => + evalRoot(expr, maybeScope.fold(state)(s => state.resetScope(s))) + } +// println(s"POP($name, $maybePredefinedComplexity): ${state.totalSpentComplexity()}") + + } + case op => +// println(s"OP> $evaluated: $op") + op.ret(evaluated) match { + case (Left(err), _) => err.asLeft + case (Right(expr), maybeScope) => + evalRoot(expr, maybeScope.fold(state)(s => state.resetScope(s))) + } + } + } + case FAILED_EXPR => Left(CommonError("Unexpected FAILED_EXPR")) + } + } + + 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"))) + +// println(s"\n\tComplexity: $intComplexityE, result: $resultE") + + (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..997f3ae7ee6 --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/miniev/Op.scala @@ -0,0 +1,60 @@ +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.* +import com.wavesplatform.lang.{ExecutionError, FailOrRejectError} + +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): Op.Result +} + +object Op { + type Result = (Either[ExecutionError, EXPR], Option[Scope]) + + def failOrReject(str: String): Result = Left(FailOrRejectError(str)) -> None + + case class Get(field: String) extends Op { + override def ret(ev: EVALUATED): Result = ev match { + case CaseObj(_, fields) => + fields.get(field) match { + case Some(v) => Right(v) -> None + case None => failOrReject(s"object $ev has no field $field") + } + case other => failOrReject(s"$other is not an object") + } + } + + case class If(ifTrue: EXPR, ifFalse: EXPR) extends Op { + override def ret(ev: EVALUATED): Result = ev match { + case CONST_BOOLEAN(cond) => Right(if (cond) ifTrue else ifFalse) -> None + case _ => failOrReject(s"$ev is not a Boolean") + } + } + + case class FuncArg(func: FunctionHeader, reversedEvaluatedArgs: List[EVALUATED], argsToEvaluate: List[EXPR]) extends Op { + override def ret(ev: EVALUATED): Result = + Right(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): Result = { + lazyVal.value = Right(ev) + state.log(key, Right(ev)) + Right(ev) -> Some(cachedScope) + } + } + + case class Func(name: String, scope: Scope, predefinedComplexity: Option[Long] = None) extends Op { + override def ret(ev: EVALUATED): Result = (Right(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/script/ContractScript.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/script/ContractScript.scala index f8ce332e144..d9700c7d650 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/script/ContractScript.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/script/ContractScript.scala @@ -50,9 +50,9 @@ object ContractScript { override val containsArray: Boolean = { val declExprs = expr.decs.map { - case l: LET => l.value - case f: FUNC => f.body - case _: FAILED_DEC => FAILED_EXPR() + case l: LET => l.value + case f: FUNC => f.body + case FAILED_DEC => FAILED_EXPR } val callableExprs = expr.callableFuncs.map(_.u.body) val verifierExpr = expr.verifierFuncOpt.map(_.u.body).toList @@ -138,8 +138,8 @@ object ContractScript { ) case FUNC(name, args, _) => FUNCTION_CALL(FunctionHeader.User(name), List.fill(args.size)(TRUE)) - case Terms.FAILED_DEC() => - FAILED_EXPR() + case Terms.FAILED_DEC => + FAILED_EXPR } val funcWithContext = annotationArgNameOpt.fold(declExpr)(argName => BLOCK(LET(argName, TRUE), declExpr)) dec.foldRight(funcWithContext)((declaration, expr) => BLOCK(declaration, expr)) 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 23200066f59..fa2d83d8d47 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 @@ -64,7 +64,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 @@ -74,8 +74,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 @@ -98,7 +98,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) @@ -108,7 +108,7 @@ package object utils { _.toList .map(_._2) .sequence - .map(Monoid.combineAll[CTX[Environment]])() + .map(Monoid.combineAll[CTX])() ) .toMap @@ -146,7 +146,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) }) @@ -159,7 +159,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 759af4e1dec..ba6afe12576 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 @@ -43,7 +43,7 @@ object Decompiler { ) case Terms.LET(name, value) => expr(pure(value), ctx, BracesWhenNeccessary, DontIndentFirstLine).map(e => out("let " + name + " = " + e, ctx.ident)) - case _: FAILED_DEC => Coeval.now("FAILED_DEC") + case FAILED_DEC => Coeval.now("FAILED_DEC") } private def extrTypes(Name: String, e: EXPR): Coeval[Option[List[String]]] = { @@ -229,7 +229,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/compiler/ExpressionCompiler.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala index 9a9ae79997e..4f46f276a02 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala @@ -34,14 +34,14 @@ class ExpressionCompiler(val version: StdLibVersion) { CONST_BYTESTR(b) .leftMap(e => CompilationError.Generic(expr.position.start, expr.position.end, e.message)) .map(CompilationStepResultExpr(ctx, _, BYTESTR, expr)) - .recover { case err => CompilationStepResultExpr(ctx, FAILED_EXPR(), NOTHING, expr, List(err)) } + .recover { case err => CompilationStepResultExpr(ctx, FAILED_EXPR, NOTHING, expr, List(err)) } def adjustStr(expr: Expressions.CONST_STRING, str: String): Either[CompilationError, CompilationStepResultExpr] = CONST_STRING(str) .filterOrElse(_ => allowIllFormedStrings || str.isWellFormed, CommonError(s"String '$str' contains ill-formed characters")) .leftMap(e => CompilationError.Generic(expr.position.start, expr.position.end, e.message)) .map(CompilationStepResultExpr(ctx, _, STRING, expr)) - .recover { case err => CompilationStepResultExpr(ctx, FAILED_EXPR(), NOTHING, expr, List(err)) } + .recover { case err => CompilationStepResultExpr(ctx, FAILED_EXPR, NOTHING, expr, List(err)) } expr match { case x: Expressions.CONST_LONG => CompilationStepResultExpr(ctx, CONST_LONG(x.value), LONG, x).pure[CompileM] @@ -53,7 +53,7 @@ class ExpressionCompiler(val version: StdLibVersion) { case x: Expressions.INVALID => CompilationStepResultExpr( ctx, - FAILED_EXPR(), + FAILED_EXPR, NOTHING, x: Expressions.EXPR, List(Generic(x.position.start, x.position.end, x.message)) @@ -122,7 +122,7 @@ class ExpressionCompiler(val version: StdLibVersion) { errorList ) } else { - CompilationStepResultExpr(ctx, FAILED_EXPR(), NOTHING, parseNodeExpr, errorList ++ condWithErr._2.map(List(_)).get) + CompilationStepResultExpr(ctx, FAILED_EXPR, NOTHING, parseNodeExpr, errorList ++ condWithErr._2.map(List(_)).get) } } yield result @@ -255,7 +255,7 @@ class ExpressionCompiler(val version: StdLibVersion) { } else { CompilationStepResultExpr( ctx, - FAILED_EXPR(), + FAILED_EXPR, NOTHING, Expressions.MATCH(p, typedExpr.parseNodeExpr, cases, ctxOpt = saveExprContext.toOption(ctx)), errorList ++ typedExpr.errors @@ -313,7 +313,7 @@ class ExpressionCompiler(val version: StdLibVersion) { if (errorList.isEmpty) { CompilationStepResultDec(ctx, LET(letNameWithErr._1.get, compiledLet.expr), letType, parseNodeDecl, compiledLet.errors) } else { - CompilationStepResultDec(ctx, FAILED_DEC(), letType, parseNodeDecl, errorList ++ compiledLet.errors) + CompilationStepResultDec(ctx, FAILED_DEC, letType, parseNodeDecl, errorList ++ compiledLet.errors) } } yield result @@ -362,7 +362,7 @@ class ExpressionCompiler(val version: StdLibVersion) { compiledFuncBody.errors ) } else { - CompilationStepResultDec(ctx, FAILED_DEC(), compiledFuncBody.t, parseNodeDecl, errorList ++ compiledFuncBody.errors) + CompilationStepResultDec(ctx, FAILED_DEC, compiledFuncBody.t, parseNodeDecl, errorList ++ compiledFuncBody.errors) } } yield (result, argTypesWithErr._1.map(_.map(nameAnfInfo => (nameAnfInfo._1, nameAnfInfo._2.vType))).getOrElse(List.empty)) } @@ -399,7 +399,7 @@ class ExpressionCompiler(val version: StdLibVersion) { if (!compLetResult.dec.isItFailed) { LET_BLOCK(compLetResult.dec.asInstanceOf[LET], compiledBody.expr) } else { - FAILED_EXPR() + FAILED_EXPR } } yield CompilationStepResultExpr(compiledBody.ctx, result, compiledBody.t, parseNodeExpr, compLetResult.errors ++ compiledBody.errors) @@ -454,7 +454,7 @@ class ExpressionCompiler(val version: StdLibVersion) { val (ctx, expr, t) = getterWithErr._1.get CompilationStepResultExpr(ctx, expr, t, parseNodeExpr.copy(resultType = Some(t)), compiledRef.errors) } else { - CompilationStepResultExpr(ctx, FAILED_EXPR(), NOTHING, parseNodeExpr, errorList ++ compiledRef.errors) + CompilationStepResultExpr(ctx, FAILED_EXPR, NOTHING, parseNodeExpr, errorList ++ compiledRef.errors) } } yield result @@ -501,7 +501,7 @@ class ExpressionCompiler(val version: StdLibVersion) { val (expr, t) = funcCallWithErr._1.get CompilationStepResultExpr(ctx, expr, t, parseNodeExpr, argErrorList) } else { - CompilationStepResultExpr(ctx, FAILED_EXPR(), NOTHING, parseNodeExpr, errorList ++ argErrorList) + CompilationStepResultExpr(ctx, FAILED_EXPR, NOTHING, parseNodeExpr, errorList ++ argErrorList) } } yield result @@ -543,7 +543,7 @@ class ExpressionCompiler(val version: StdLibVersion) { } else { CompilationStepResultExpr( ctx, - FAILED_EXPR(), + FAILED_EXPR, NOTHING, Expressions.REF(p, keyPart, ctxOpt = saveExprContext.toOption(ctx)), errorList diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/Terms.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/Terms.scala index bc7456e3ab1..b00d7efccfe 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/Terms.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/Terms.scala @@ -26,7 +26,7 @@ object Terms { override def toString: String = toStr() def isItFailed: Boolean = false } - case class FAILED_DEC() extends DECLARATION { + case object FAILED_DEC extends DECLARATION { def name = "NO_NAME" def toStr: Coeval[String] = Coeval.now("Error") override def isItFailed: Boolean = true @@ -60,7 +60,7 @@ object Terms { def isItFailed: Boolean = false } - case class FAILED_EXPR() extends EXPR { + case object FAILED_EXPR extends EXPR { def toStr: Coeval[String] = Coeval.now("error") override def isItFailed: Boolean = true diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/estimator/v2/ScriptEstimatorV2.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/estimator/v2/ScriptEstimatorV2.scala index b0e2124b935..0c651dda2ea 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/estimator/v2/ScriptEstimatorV2.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/estimator/v2/ScriptEstimatorV2.scala @@ -1,14 +1,14 @@ package com.wavesplatform.lang.v1.estimator.v2 -import cats.instances.list._ -import cats.syntax.traverse._ +import cats.instances.list.* +import cats.syntax.traverse.* import cats.{Id, Monad} import com.wavesplatform.lang.v1.FunctionHeader -import com.wavesplatform.lang.v1.compiler.Terms._ -import com.wavesplatform.lang.v1.estimator.{EstimationError, ScriptEstimator} +import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.estimator.v2.EstimatorContext.EvalM -import com.wavesplatform.lang.v1.estimator.v2.EstimatorContext.Lenses._ -import com.wavesplatform.lang.v1.task.imports._ +import com.wavesplatform.lang.v1.estimator.v2.EstimatorContext.Lenses.* +import com.wavesplatform.lang.v1.estimator.{EstimationError, ScriptEstimator} +import com.wavesplatform.lang.v1.task.imports.* import monix.eval.Coeval object ScriptEstimatorV2 extends ScriptEstimator { @@ -32,13 +32,13 @@ object ScriptEstimatorV2 extends ScriptEstimator { case LET_BLOCK(let, inner) => evalLetBlock(let, inner) case BLOCK(let: LET, inner) => evalLetBlock(let, inner) case BLOCK(f: FUNC, inner) => evalFuncBlock(f, inner) - case BLOCK(_: FAILED_DEC, _) => const(0) + case BLOCK(FAILED_DEC, _) => const(0) case REF(str) => evalRef(str) case _: EVALUATED => const(1) case IF(cond, t1, t2) => evalIF(cond, t1, t2) case GETTER(expr, _) => evalGetter(expr) case FUNCTION_CALL(header, args) => evalFuncCall(header, args) - case _: FAILED_EXPR => const(0) + case FAILED_EXPR => const(0) } private def evalLetBlock(let: LET, inner: EXPR): EvalM[Long] = diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/estimator/v3/ScriptEstimatorV3.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/estimator/v3/ScriptEstimatorV3.scala index 6d8dc073443..fa7ccb1fce9 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/estimator/v3/ScriptEstimatorV3.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/estimator/v3/ScriptEstimatorV3.scala @@ -51,13 +51,13 @@ case class ScriptEstimatorV3(fixOverflow: Boolean, overhead: Boolean, letFixes: case LET_BLOCK(let, inner) => evalLetBlock(let, inner, activeFuncArgs, globalDeclarationsMode) case BLOCK(let: LET, inner) => evalLetBlock(let, inner, activeFuncArgs, globalDeclarationsMode) case BLOCK(f: FUNC, inner) => evalFuncBlock(f, inner, activeFuncArgs, globalDeclarationsMode) - case BLOCK(_: FAILED_DEC, _) => zero + case BLOCK(FAILED_DEC, _) => zero case REF(str) => evalRef(str, activeFuncArgs) case _: EVALUATED => const(overheadCost) case IF(cond, t1, t2) => evalIF(cond, t1, t2, activeFuncArgs) case GETTER(expr, _) => evalGetter(expr, activeFuncArgs) case FUNCTION_CALL(header, args) => evalFuncCall(header, args, activeFuncArgs) - case _: FAILED_EXPR => zero + case FAILED_EXPR => zero } private def evalLetBlock(let: LET, nextExpr: EXPR, activeFuncArgs: Set[String], globalDeclarationsMode: Boolean): EvalM[Long] = 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 f66b0202f1f..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,30 +116,24 @@ object ContractEvaluator { limit: Int, correctFunctionCallScope: Boolean, newMode: Boolean, - enableExecutionLog: Boolean, - fixedThrownError: 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, - fixedThrownError - ) - 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, @@ -150,27 +141,18 @@ object ContractEvaluator { limit: Int, correctFunctionCallScope: Boolean, newMode: Boolean, - enableExecutionLog: Boolean, - fixedThrownError: Boolean - ): Coeval[Either[(ExecutionError, Int, Log[Id]), (ScriptResult, Log[Id])]] = - EvaluatorV2 - .applyLimitedCoeval( - expr, - logExtraInfo, - limit, - ctx, - version, - correctFunctionCallScope, - newMode, - enableExecutionLog = enableExecutionLog, - fixedThrownError = fixedThrownError - ) - .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..abe19f57d58 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,94 +24,91 @@ 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) } + case _ => ??? } .orElse( // no such function, try data constructor @@ -120,46 +116,46 @@ 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) => dec match { - case l: LET => evalLetBlock(l, inner) - case f: FUNC => evalFuncBlock(f, inner) - case _: FAILED_DEC => raiseError("Attempt to evaluate failed declaration.") + case l: LET => evalLetBlock(l, inner) + case f: FUNC => evalFuncBlock(f, inner) + 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.") + 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 77c16ee5f5b..b7f05943ad7 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,25 +5,22 @@ 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.ContractEvaluator.LogExtraInfo import com.wavesplatform.lang.v1.evaluator.EvaluatorV2.LogKeys.* import com.wavesplatform.lang.v1.evaluator.EvaluatorV2.logFunc -import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Bindings -import com.wavesplatform.lang.v1.evaluator.ctx.* -import com.wavesplatform.lang.v1.traits.Environment -import com.wavesplatform.lang.{CommonError, ExecutionError, ThrownError} +import com.wavesplatform.lang.v1.evaluator.ctx.{EvaluationContext, LoggedEvaluationContext, NativeFunction, UserFunction} +import com.wavesplatform.lang.{CommonError, ExecutionError, miniev} 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 limit: Int, val correctFunctionCallScope: Boolean, @@ -92,20 +89,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] => + case f: Simple => Coeval((f.evaluate(ctx.ec.environment, args), limit - cost)) } for { @@ -139,7 +129,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 = @@ -278,7 +268,7 @@ class EvaluatorV2( case evaluated: EVALUATED => update(evaluated).map(_ => limit) - case f: FAILED_EXPR => EvaluationResult(s"Unexpected $f", limit) + case FAILED_EXPR => EvaluationResult(s"Unexpected FAILED_EXPR", limit) } } @@ -353,48 +343,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, - fixedThrownError: Boolean - ): 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, - limit, - correctFunctionCallScope, - newMode, - enableExecutionLog, - checkConstructorArgsTypes, - fixedThrownError - ) - .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, @@ -406,30 +369,18 @@ object EvaluatorV2 { fixedThrownError: Boolean ): (Log[Id], Int, Either[ExecutionError, EVALUATED]) = EvaluatorV2 - .applyLimitedCoeval( + .applyLimited( expr, logExtraInfo, complexityLimit, ctx, stdLibVersion, correctFunctionCallScope, - newMode, - enableExecutionLog = enableExecutionLog, - fixedThrownError = fixedThrownError - ) - .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, @@ -451,42 +402,9 @@ object EvaluatorV2 { fixedThrownError ) - private def logCall( - loggedCtx: LoggedEvaluationContext[Environment, Id], - logExtraInfo: LogExtraInfo, - exprCopy: EXPR, - enableExecutionLog: Boolean - ): Unit = { - @tailrec - def findInvArgLet(expr: EXPR, let: LET): Option[LET] = { - expr match { - case BLOCK(res @ LET(let.name, value), _) if value == let.value => Some(res) - case BLOCK(_, body) => findInvArgLet(body, let) - case _ => None - } - } - - if (enableExecutionLog) { - logExtraInfo.dAppAddress.foreach { addr => - val addrObj = Bindings.senderObject(addr) - loggedCtx.log(LET(InvokedDApp, addrObj), addrObj.asRight[ExecutionError]) - } - - logExtraInfo.invokedFuncName.foreach { funcName => - val invokedFuncName = CONST_STRING(funcName) - invokedFuncName.foreach(name => loggedCtx.log(LET(InvokedFuncName, name), invokedFuncName)) - } - - logExtraInfo.invArg.flatMap(findInvArgLet(exprCopy, _)).foreach { - case let @ LET(_, obj: CaseObj) => loggedCtx.log(let, obj.asRight[ExecutionError]) - case _ => - } - } - } - private def logFunc( fc: FUNCTION_CALL, - ctx: LoggedEvaluationContext[Environment, Id], + ctx: LoggedEvaluationContext[Id], stdLibVersion: StdLibVersion, limit: Int, enableExecutionLog: Boolean @@ -508,7 +426,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..045146e2ed9 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,22 @@ 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], - typeDefs: Map[String, FINAL], - letDefs: Map[String, LazyVal[F]], - functions: Seq[BaseFunction[C]] - ): EvaluationContext[C, 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 52b0a4a6a9f..4bf863c9a51 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 @@ -8,11 +8,11 @@ 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, ThrownError} @@ -20,6 +20,28 @@ import scala.collection.mutable import scala.util.Try 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") @@ -39,10 +61,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 { @@ -50,29 +72,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 @@ -80,7 +102,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 @@ -88,7 +110,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 @@ -100,7 +122,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", @@ -117,7 +139,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, @@ -137,7 +159,7 @@ object CryptoContext { ) )(global.keccak256) - val blake2b256F_lim: Array[BaseFunction[NoContext]] = + val blake2b256F_lim: Array[BaseFunction] = hashLimFunction( "blake2b256", BLAKE256_LIM, @@ -157,7 +179,7 @@ object CryptoContext { ) )(global.blake2b256) - val sha256F_lim: Array[BaseFunction[NoContext]] = + val sha256F_lim: Array[BaseFunction] = hashLimFunction( "sha256", SHA256_LIM, @@ -177,7 +199,7 @@ object CryptoContext { ) )(global.sha256) - val sigVerifyL: Array[BaseFunction[NoContext]] = + val sigVerifyL: Array[BaseFunction] = functionFamily( SIGVERIFY_LIM, limit => s"sigVerify_${limit}Kb", @@ -212,7 +234,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) @@ -221,13 +243,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( @@ -241,7 +259,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", @@ -265,7 +283,7 @@ object CryptoContext { } } - def rsaVerifyL(version: StdLibVersion): Array[BaseFunction[NoContext]] = + def rsaVerifyL(version: StdLibVersion): Array[BaseFunction] = functionFamily( RSAVERIFY_LIM, limit => s"rsaVerify_${limit}Kb", @@ -293,7 +311,7 @@ object CryptoContext { ) } - def toBase58StringF: BaseFunction[NoContext] = + def toBase58StringF: BaseFunction = NativeFunction( "toBase58String", Map[StdLibVersion, Long](V1 -> 10L, V2 -> 10L, V3 -> 10L, V4 -> 3L), @@ -306,7 +324,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), @@ -319,7 +337,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), @@ -332,7 +350,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), @@ -345,7 +363,7 @@ object CryptoContext { case xs => notImplemented[Id, EVALUATED]("fromBase64String(str: String)", xs) } - val checkMerkleProofF: BaseFunction[NoContext] = + val checkMerkleProofF: BaseFunction = NativeFunction( "checkMerkleProof", 30, @@ -360,7 +378,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, @@ -383,18 +401,18 @@ object CryptoContext { case xs => notImplemented[Id, EVALUATED](s"createMerkleRoot(merkleProof: ByteVector, valueBytes: ByteVector, index: Int)", 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", @@ -419,7 +437,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, @@ -446,7 +464,7 @@ object CryptoContext { } } - val bls12Groth16VerifyF: BaseFunction[NoContext] = + val bls12Groth16VerifyF: BaseFunction = NativeFunction( "groth16Verify", 2700, @@ -471,7 +489,7 @@ object CryptoContext { notImplemented[Id, EVALUATED]("groth16Verify(vk:ByteVector, proof:ByteVector, inputs:ByteVector)", xs) } - val bn256Groth16VerifyF: BaseFunction[NoContext] = + val bn256Groth16VerifyF: BaseFunction = NativeFunction( "bn256Groth16Verify", 1650, @@ -495,7 +513,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, @@ -533,13 +551,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 = @@ -561,10 +579,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 @@ -574,9 +592,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 2fbb5dc0d91..25ddff8f384 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.wavesplatform.common.state.ByteStr @@ -14,17 +18,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} @@ -32,7 +33,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" @@ -43,25 +43,25 @@ object PureContext { // As an optimization, JVM might throw an ArithmeticException with empty stack trace and null message. // The workaround below rethrows 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, @@ -80,7 +80,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, @@ -97,20 +97,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), @@ -121,26 +121,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", Map(V5 -> 65L, V6 -> 65L, V7 -> 65L, V8 -> 1L), STRING_TO_BIGINT, BIGINT, ("n", STRING)) { case CONST_STRING(n) :: Nil => Either @@ -151,7 +151,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("parseBigIntValue(n: String)", xs) } - lazy val stringToBigIntOpt: BaseFunction[NoContext] = + lazy val stringToBigIntOpt: BaseFunction = NativeFunction("parseBigInt", Map(V5 -> 65L, V6 -> 65L, V7 -> 65L, V8 -> 1L), STRING_TO_BIGINTOPT, UNION(BIGINT, UNIT), ("n", STRING)) { case CONST_STRING(n) :: Nil => Right((if (n.length <= 155) { @@ -171,13 +171,13 @@ object PureContext { case xs => notImplemented[Id, EVALUATED]("parseBigInt(n: String)", xs) } - lazy val bigIntToBytes: BaseFunction[NoContext] = + lazy val bigIntToBytes: BaseFunction = NativeFunction("toBytes", Map(V5 -> 65L, V6 -> 65L, V7 -> 65L, V8 -> 1L), 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", Map(V5 -> 65L, V6 -> 65L, V7 -> 65L, V8 -> 1L), @@ -196,7 +196,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", Map(V5 -> 65L, V6 -> 65L, V7 -> 65L, V8 -> 1L), 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)") @@ -205,7 +205,7 @@ object PureContext { def bigIntArithmeticOp(op: BinaryOperation, func: Short, complexity: Map[StdLibVersion, Long])( body: (BigInt, BigInt) => BigInt - ): BaseFunction[NoContext] = { + ): BaseFunction = { createRawOp( op, BIGINT, @@ -227,29 +227,29 @@ object PureContext { } } - lazy val sumToBigInt: BaseFunction[NoContext] = + lazy val sumToBigInt: BaseFunction = bigIntArithmeticOp(SUM_OP, SUM_BIGINT, Map[StdLibVersion, Long](V5 -> 8L, V6 -> 8L, V7 -> 8L, V8 -> 1L)) { _ + _ } - lazy val subToBigInt: BaseFunction[NoContext] = + lazy val subToBigInt: BaseFunction = bigIntArithmeticOp(SUB_OP, SUB_BIGINT, Map[StdLibVersion, Long](V5 -> 8L, V6 -> 8L, V7 -> 8L, V8 -> 1L)) { _ - _ } - lazy val mulToBigInt: BaseFunction[NoContext] = + lazy val mulToBigInt: BaseFunction = bigIntArithmeticOp(MUL_OP, MUL_BIGINT, Map[StdLibVersion, Long](V5 -> 64L, V6 -> 64L, V7 -> 64L, V8 -> 1L)) { _ * _ } - lazy val divToBigInt: BaseFunction[NoContext] = + lazy val divToBigInt: BaseFunction = bigIntArithmeticOp(DIV_OP, DIV_BIGINT, Map[StdLibVersion, Long](V5 -> 64L, V6 -> 64L, V7 -> 64L, V8 -> 1L)) { _ / _ } - lazy val modToBigInt: BaseFunction[NoContext] = + lazy val modToBigInt: BaseFunction = bigIntArithmeticOp(MOD_OP, MOD_BIGINT, Map[StdLibVersion, Long](V5 -> 64L, V6 -> 64L, V7 -> 64L, V8 -> 1L)) { _ % _ } - lazy val negativeBigInt: BaseFunction[NoContext] = + lazy val negativeBigInt: BaseFunction = NativeFunction("-", Map(V5 -> 8L, V6 -> 8L, V7 -> 8L, V8 -> 1L), 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 @@ -257,7 +257,7 @@ object PureContext { FUNCTION_CALL(throwWithMessage, List(CONST_STRING(defaultThrowMessage).explicitGet())) } - lazy val extract: BaseFunction[NoContext] = + lazy val extract: BaseFunction = UserFunction.deprecated( "extract", 13, @@ -271,16 +271,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) = { @@ -313,7 +313,7 @@ object PureContext { } } - lazy val valueOrElse: BaseFunction[NoContext] = + lazy val valueOrElse: BaseFunction = UserFunction( "valueOrElse", 2, @@ -328,7 +328,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), @@ -343,7 +343,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), @@ -353,7 +353,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), @@ -379,7 +379,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), @@ -401,7 +401,7 @@ object PureContext { FUNCTION_CALL(Native(BIGINT_TO_INT), List(r)) } - val fractionIntRoundsNative: BaseFunction[NoContext] = + val fractionIntRoundsNative: BaseFunction = NativeFunction( "fraction", 1L, @@ -426,7 +426,7 @@ object PureContext { ) } - val fractionBigInt: BaseFunction[NoContext] = + val fractionBigInt: BaseFunction = NativeFunction( "fraction", Map(V5 -> 128L, V6 -> 1L), @@ -447,7 +447,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), @@ -473,7 +473,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)) @@ -481,7 +481,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) @@ -489,24 +489,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), @@ -518,29 +518,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), @@ -564,7 +564,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), @@ -593,7 +593,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", @@ -617,7 +617,7 @@ object PureContext { ) } - private val takeRightBytesBeforeV6: BaseFunction[NoContext] = + private val takeRightBytesBeforeV6: BaseFunction = UserFunction( "takeRight", "takeRightBytes", @@ -641,7 +641,7 @@ object PureContext { ) } - private val takeRightBytesFromV6: BaseFunction[NoContext] = + private val takeRightBytesFromV6: BaseFunction = NativeFunction( "takeRight", 6, @@ -662,7 +662,7 @@ object PureContext { notImplemented[Id, EVALUATED]("takeRight(xs: ByteVector, number: Int)", xs) } - private val dropRightBytesFromV6: BaseFunction[NoContext] = + private val dropRightBytesFromV6: BaseFunction = NativeFunction( "dropRight", 6, @@ -683,7 +683,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), @@ -696,7 +696,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), @@ -727,7 +727,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), @@ -740,7 +740,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, @@ -753,7 +753,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, @@ -766,7 +766,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), @@ -779,7 +779,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), @@ -810,7 +810,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), @@ -833,7 +833,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), @@ -856,7 +856,7 @@ object PureContext { ) } - private val takeRightStringFromV6: BaseFunction[NoContext] = + private val takeRightStringFromV6: BaseFunction = NativeFunction( "takeRight", 20L, @@ -879,7 +879,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), @@ -902,7 +902,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), @@ -925,7 +925,7 @@ object PureContext { ) } - private val dropRightStringFromV6: BaseFunction[NoContext] = + private val dropRightStringFromV6: BaseFunction = NativeFunction( "dropRight", 20L, @@ -950,7 +950,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), @@ -971,7 +971,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 { @@ -981,7 +981,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), @@ -1002,7 +1002,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), @@ -1023,7 +1023,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), @@ -1043,7 +1043,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), @@ -1067,7 +1067,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), @@ -1092,7 +1092,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), @@ -1113,7 +1113,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), @@ -1134,7 +1134,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), @@ -1159,7 +1159,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), @@ -1184,7 +1184,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) @@ -1192,7 +1192,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 => @@ -1248,7 +1248,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 => @@ -1262,7 +1262,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)) } @@ -1271,12 +1271,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, @@ -1295,13 +1295,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), @@ -1317,7 +1317,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) @@ -1325,7 +1325,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) @@ -1333,7 +1333,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) @@ -1341,7 +1341,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 { @@ -1354,13 +1354,13 @@ object PureContext { def bigIntConditionOp(op: BinaryOperation, func: Short)( body: (BigInt, BigInt) => Boolean - ): BaseFunction[NoContext] = + ): BaseFunction = NativeFunction(opsToFunctions(op), Map(V5 -> 8L, V6 -> 8L, V7 -> 8L, V8 -> 1L), 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, @@ -1377,13 +1377,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( @@ -1395,7 +1395,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( @@ -1407,7 +1407,7 @@ object PureContext { notImplemented[Id, EVALUATED]("min(list: List[Int])", xs) } - lazy val listBigIntMax: BaseFunction[NoContext] = + lazy val listBigIntMax: BaseFunction = NativeFunction("max", Map(V5 -> 192L, V6 -> 192L, V7 -> 192L, V8 -> 6L), MAX_LIST_BIGINT, BIGINT, ("list", PARAMETERIZEDLIST(BIGINT))) { case ARR(list) :: Nil => Either.cond( @@ -1419,7 +1419,7 @@ object PureContext { notImplemented[Id, EVALUATED]("max(list: List[BigInt])", xs) } - lazy val listBigIntMin: BaseFunction[NoContext] = + lazy val listBigIntMin: BaseFunction = NativeFunction("min", Map(V5 -> 192L, V6 -> 192L, V7 -> 192L, V8 -> 6L), MIN_LIST_BIGINT, BIGINT, ("list", PARAMETERIZEDLIST(BIGINT))) { case ARR(list) :: Nil => Either.cond( @@ -1431,7 +1431,7 @@ object PureContext { notImplemented[Id, EVALUATED]("min(list: List[BigInt])", xs) } - lazy val listIndexOf: BaseFunction[NoContext] = + lazy val listIndexOf: BaseFunction = NativeFunction( "indexOf", 5, @@ -1446,7 +1446,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, @@ -1461,7 +1461,7 @@ object PureContext { notImplemented[Id, EVALUATED]("lastIndexOf(list: List[T], element: T)", xs) } - lazy val listRemoveByIndex: BaseFunction[NoContext] = + lazy val listRemoveByIndex: BaseFunction = NativeFunction( "removeByIndex", Map(V4 -> 7L, V5 -> 7L, V6 -> 7L, V7 -> 7L, V8 -> 4L), @@ -1483,7 +1483,7 @@ object PureContext { notImplemented[Id, EVALUATED]("removeByIndex(list: List[T], index: Int)", xs) } - private val listReplaceByIndex: BaseFunction[NoContext] = + private val listReplaceByIndex: BaseFunction = NativeFunction( "replaceByIndex", 4, @@ -1533,7 +1533,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, @@ -1545,7 +1545,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 @@ -1564,17 +1564,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), @@ -1604,7 +1604,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), @@ -1619,7 +1619,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 ( @@ -1638,7 +1638,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), @@ -1670,7 +1670,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", @@ -1694,7 +1694,7 @@ object PureContext { ) } - def logBigInt(roundTypes: UNION): BaseFunction[NoContext] = + def logBigInt(roundTypes: UNION): BaseFunction = NativeFunction( "log", 200, @@ -1725,7 +1725,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])) { @@ -1739,7 +1739,7 @@ object PureContext { case xs => notImplemented[Id, EVALUATED](s"median(arr: List[Int])", xs) } - val getBigIntListMedian: BaseFunction[NoContext] = + val getBigIntListMedian: BaseFunction = NativeFunction( "median", Map(V5 -> 20 * 8L, V6 -> 20 * 8L, V7 -> 20 * 8L, V8 -> 35L), @@ -1759,7 +1759,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) @@ -1771,20 +1771,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( @@ -1801,7 +1801,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, @@ -2022,7 +2022,7 @@ object PureContext { makeString :+ splitStrFixed - private val v6Functions = + val v6Functions = fromV5Functions(true) ++ Array( sizeTuple, @@ -2047,48 +2047,48 @@ 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 ) private[this] val v8Ctx = - CTX[NoContext]( + CTX( v5Types, v5Vars, v6Functions :+ listReplaceByIndex ) - 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 d748f02c833..8cd26caa95c 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 @@ -19,86 +19,92 @@ import com.wavesplatform.lang.v1.evaluator.ctx.impl.converters.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Bindings.{scriptTransfer as _, *} import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Types.* 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, ThrownError} -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"${this.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"${this.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 => @@ -113,7 +119,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, @@ -137,13 +143,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), @@ -164,10 +170,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( @@ -180,15 +186,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"), @@ -231,15 +237,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) => @@ -293,9 +299,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( @@ -349,15 +355,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 => @@ -375,15 +381,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 => @@ -399,7 +405,7 @@ object Functions { } } - val stringFromAddressF: BaseFunction[Environment] = + val stringFromAddressF: BaseFunction = NativeFunction( "toString", Map(V3 -> 10L, V4 -> 10L, V5 -> 10L, V6 -> 1L), @@ -420,8 +426,8 @@ object Functions { case t => Left(ThrownError(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, @@ -429,7 +435,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))) @@ -454,8 +460,8 @@ object Functions { } } - val assetBalanceV4F: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val assetBalanceV4F: BaseFunction = + NativeFunction.withEnvironment( "assetBalance", 10, ACCOUNTASSETONLYBALANCE, @@ -463,7 +469,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 => @@ -478,15 +484,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 => @@ -516,16 +522,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 => @@ -542,20 +548,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 => @@ -569,16 +575,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 => @@ -593,9 +599,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, @@ -605,100 +611,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 monad: Monad[CoevalF[F, *]]): Coeval[F[(Either[ExecutionError, (EVALUATED, Log[F])], Int)]] = { - def thrown[R](message: String): F[Either[ExecutionError, R]] = - (ThrownError(message): ExecutionError).asLeft[R].pure[F] - - val processedArgs = for { - dAppBytes <- EitherT[F, ExecutionError, ByteStr]( - args match { - case (dApp: CaseObj) :: _ if dApp.caseType == addressType => - dApp.fields.get("bytes") match { - case Some(CONST_BYTESTR(d)) => d.asRight[ExecutionError].pure[F] - case a => thrown(s"Unexpected address bytes $a") - } - case (dApp: CaseObj) :: _ if dApp.caseType == aliasType => - dApp.fields.get("alias") match { - case Some(CONST_STRING(a)) => env.resolveAlias(a).map(_.bimap(ThrownError, _.bytes)) - case arg => thrown(s"Unexpected alias arg $arg") - } - case arg :: _ => - thrown(s"Unexpected recipient arg $arg") - case args => - thrown(s"Unexpected args $args") - } - ) - name <- EitherT[F, ExecutionError, String]( - args match { - case _ :: CONST_STRING(name) :: _ => name.asRight[ExecutionError].pure[F] - case _ :: CaseObj(UNIT, _) :: _ => "default".asRight[ExecutionError].pure[F] - case _ :: arg :: _ => thrown(s"Unexpected name arg $arg") - case args => thrown(s"Unexpected args $args") - } - ) - payments <- EitherT[F, ExecutionError, Seq[(Option[Array[Byte]], Long)]]( - args match { - case _ :: _ :: _ :: ARR(payments) :: Nil => - (payments: Seq[EVALUATED]) - .traverse { - case p: CaseObj if p.caseType == paymentType => - List("assetId", "amount").flatMap(p.fields.get) match { - case CONST_BYTESTR(a) :: CONST_LONG(v) :: Nil => Right((Some(a.arr), v)) - case CaseObj(UNIT, _) :: CONST_LONG(v) :: Nil => Right((None, v)) - case args => Left(ThrownError(s"Unexpected payment args $args"): ExecutionError) - } - case arg => - Left(ThrownError(s"Unexpected payment arg $arg"): ExecutionError) - } - .pure[F] - case args => - (s"Unexpected args $args": ExecutionError).asLeft[Seq[(Option[Array[Byte]], Long)]].pure[F] - } - ) - } yield (dAppBytes, name, payments) - monad.flatMap(Coeval(processedArgs.value)) { - case Right((dAppBytes, name, payments)) => - args match { - case _ :: _ :: ARR(passedArgs) :: _ :: Nil => - env - .callScript( - Recipient.Address(dAppBytes), - name, - passedArgs.toList, - payments, - availableComplexity, - reentrant - ) - .map(_.map { case (result, spentComplexity) => - val mappedError = result.leftMap { - case reject: FailOrRejectError => reject - case other => CommonError("Nested invoke error", Some(other)): ExecutionError - } - (mappedError, availableComplexity - spentComplexity) - }) - case xs => - val signature = "invoke(dApp: Address, func: String, args: List[Any], payments: List[Payment])" - val err = notImplemented[F, (EVALUATED, Log[F])](signature, xs) - Coeval.now(err.map((_, 0))) - } - case Left(error) => - (error.asLeft[(EVALUATED, Log[F])], availableComplexity).pure[F].pure[Coeval] - } - } + 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) } @@ -714,7 +638,7 @@ object Functions { } } - def extractedFuncs(v: StdLibVersion): Array[BaseFunction[Environment]] = + def extractedFuncs(v: StdLibVersion): Array[BaseFunction] = Array( getIntegerFromStateF, getBooleanFromStateF, @@ -731,7 +655,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, @@ -739,15 +663,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 => @@ -762,16 +686,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)) @@ -791,15 +715,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 => @@ -833,16 +757,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)) @@ -863,7 +787,7 @@ object Functions { } } - val simplifiedIssueActionConstructor: BaseFunction[Environment] = + val simplifiedIssueActionConstructor: BaseFunction = NativeFunction( "Issue", 1, @@ -879,7 +803,7 @@ object Functions { Right(CaseObj(issueActionType, typedArgs)) } - val detailedIssueActionConstructor: BaseFunction[Environment] = + val detailedIssueActionConstructor: BaseFunction = NativeFunction( "Issue", 1, @@ -897,7 +821,7 @@ object Functions { Right(CaseObj(issueActionType, typedArgs)) } - val simplifiedLeaseActionConstructor: BaseFunction[Environment] = + val simplifiedLeaseActionConstructor: BaseFunction = NativeFunction( "Lease", 1, @@ -910,7 +834,7 @@ object Functions { Right(CaseObj(leaseActionType, typedArgs)) } - val detailedLeaseActionConstructor: BaseFunction[Environment] = + val detailedLeaseActionConstructor: BaseFunction = NativeFunction( "Lease", 1, @@ -924,8 +848,8 @@ object Functions { Right(CaseObj(leaseActionType, typedArgs)) } - val calculateLeaseId: BaseFunction[Environment] = - NativeFunction.withEnvironment[Environment]( + val calculateLeaseId: BaseFunction = + NativeFunction.withEnvironment( "calculateLeaseId", 1, CALCULATE_LEASE_ID, @@ -933,7 +857,7 @@ object Functions { ("lease", leaseActionType) ) { 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 => @@ -962,18 +886,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) @@ -999,20 +923,20 @@ object Functions { } } - val calculateDelay: NativeFunction[Environment] = { + val calculateDelay: BaseFunction = { val args = Seq( ("generator", addressType), ("balance", LONG) ) - NativeFunction.withEnvironment[Environment]( + NativeFunction.withEnvironment( "calculateDelay", 1, CALCULATE_DELAY, LONG, args* ) { - new ContextfulNativeFunction.Simple[Environment]("calculateDelay", LONG, args) { + new ContextfulNativeFunction.Simple("calculateDelay", LONG, args) { override def evaluate[F[_]: Monad](env: Environment[F], args: List[EVALUATED]): F[Either[ExecutionError, EVALUATED]] = args match { case CaseObj(`addressType`, fields) :: CONST_LONG(balance) :: Nil => @@ -1028,7 +952,7 @@ object Functions { (CONST_LONG(delay): EVALUATED).asRight[ExecutionError].pure[F] } case xs => - notImplemented[Id, EVALUATED]("calculateDelay(generator: Address, balance: Long)", xs) + notImplemented[F, EVALUATED]("calculateDelay(generator: Address, balance: Long)", xs) } } } 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 79f5bd6b4ba..622b768f126 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, version) :: 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 ce8829ea19a..5c0068213ca 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 } @@ -137,7 +136,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/serialization/SerdeV1.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/serialization/SerdeV1.scala index 66299dd1e6f..2a67223d4bb 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/serialization/SerdeV1.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/serialization/SerdeV1.scala @@ -32,7 +32,7 @@ object SerdeV1 extends Serde[ByteBuffer, ByteArrayOutputStream] { out.writeInt(args.size) args.foreach(out.writeString) } *> aux(body) - case _: FAILED_DEC => + case FAILED_DEC => Coeval.raiseError(new Exception("Attempt to serialize failed declaration.")) } } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/serialization/SerdeV2.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/serialization/SerdeV2.scala index b03c14ec41f..8a80255840e 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/serialization/SerdeV2.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/serialization/SerdeV2.scala @@ -32,7 +32,7 @@ object SerdeV2 extends Serde[CodedInputStream, CodedOutputStream] { out.writeRawByte(args.size.toByte) args.foreach(out.writeStringNoTag) } *> aux(body) - case _: FAILED_DEC => + case FAILED_DEC => Coeval.raiseError(new Exception("Attempt to serialize failed declaration.")) } } 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 315a1184995..9a3b0f533c6 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 ed520803c23..0020187daf9 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,7 @@ 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.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.* @@ -27,16 +27,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,39 +64,33 @@ 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] { def height: Long = h def chainId: Byte = nByte def inputEntity = in() - def transactionById(id: Array[Byte]): Option[Tx] = ??? - def transferTransactionById(id: Array[Byte]): Option[Tx.Transfer] = ??? - def transactionHeightById(id: Array[Byte]): Option[Long] = ??? - def assetInfoById(id: Array[Byte]): Option[ScriptAssetInfo] = ??? - def lastBlockOpt(): Option[BlockInfo] = ??? - def blockInfoByHeight(height: Int): Option[BlockInfo] = ??? - def data(recipient: Recipient, key: String, dataType: DataType): Option[Any] = None - def hasData(recipient: Recipient): Boolean = false - def resolveAlias(name: String): Either[String, Recipient.Address] = ??? - def accountBalanceOf(a: Recipient, b: Option[Array[Byte]]): Either[String, Long] = ??? - def accountWavesBalanceOf(a: Recipient): Either[String, Environment.BalanceDetails] = ??? - def tthis: Environment.Tthis = Coproduct(Address(ByteStr.empty)) - def multiPaymentAllowed: Boolean = true - def txId: ByteStr = ??? - def transferTransactionFromProto(b: Array[Byte]): Option[Tx.Transfer] = ??? - def addressFromString(address: String): Either[String, Recipient.Address] = ??? - def addressFromPublicKey(publicKey: ByteStr): Either[String, Address] = ??? - def accountScript(addressOrAlias: Recipient): Option[Script] = ??? - def calculateDelay(gt: ByteStr, b: Long): Long = ??? - def callScript( + def transactionById(id: Array[Byte]): Option[Tx] = ??? + def transferTransactionById(id: Array[Byte]): Option[Tx.Transfer] = ??? + def transactionHeightById(id: Array[Byte]): Option[Long] = ??? + def assetInfoById(id: Array[Byte]): Option[ScriptAssetInfo] = ??? + def lastBlockOpt(): Option[BlockInfo] = ??? + def blockInfoByHeight(height: Int): Option[BlockInfo] = ??? + def data(recipient: Recipient, key: String, dataType: DataType): Option[Any] = None + def hasData(recipient: Recipient): Boolean = false + def resolveAlias(name: String): Either[String, Recipient.Address] = ??? + def accountBalanceOf(addressOrAlias: Recipient, assetId: Option[Array[Byte]]): Either[String, Long] = ??? + def accountWavesBalanceOf(addressOrAlias: Recipient): Either[String, Environment.BalanceDetails] = ??? + def tthis: Environment.Tthis = Coproduct(Address(ByteStr.empty)) + def multiPaymentAllowed: Boolean = true + def txId: ByteStr = ??? + def transferTransactionFromProto(b: Array[Byte]): Option[Tx.Transfer] = ??? + 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] = ??? + def calculateDelay( gt: ByteStr, b: Long): Long = ??? + override def callScript( dApp: Address, func: String, args: List[EVALUATED], 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 deebef973c6..2cc5784eb7b 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 @@ -14,22 +14,20 @@ 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.LibrariesOffset import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset.NoLibraries -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) - private 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 @@ -48,7 +46,7 @@ class TestCompiler(version: StdLibVersion) { ContractCompiler.compile( script, offset, - compilerContext, + dappContext.compilerContext, version, allowIllFormedStrings = allowIllFormedStrings, needCompaction = compact, @@ -83,13 +81,19 @@ class TestCompiler(version: StdLibVersion) { ): Either[String, ExprScript] = ExpressionCompiler .compile(script, offset, expressionCompilerContext, version, allowIllFormedStrings) - .map(s => ExprScript(version, s._1, checkSize = checkSize).explicitGet()) + .map(s => + ExprScript( + version, + s._1, + checkSize = checkSize + ).explicitGet() + ) def compileAsset(script: String, offset: LibrariesOffset = NoLibraries): Script = ExprScript(version, ExpressionCompiler.compile(script, offset, assetCompilerContext, version).explicitGet()._1).explicitGet() def compileFreeCall(script: String, offset: LibrariesOffset = NoLibraries): ExprScript = { - val expr = ContractCompiler.compileFreeCall(script, offset, compilerContext, version).explicitGet() + val expr = ContractCompiler.compileFreeCall(script, offset, 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 f53431ea3e2..7073f5e5a9c 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,10 +175,8 @@ class ContractIntegrationTest extends PropSpec with Inside { Int.MaxValue, correctFunctionCallScope = true, newMode = false, - enableExecutionLog = true, - fixedThrownError = true + State(ctx.evaluationContext(Common.emptyBlockchainEnvironment()), ComplexityLimit.Unlimited, false, V3) ) - .value() .leftMap { case (e, _, log) => (e, log) } } @@ -323,7 +321,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 ) } @@ -392,7 +390,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 1905a9fa6a6..e04f0c006de 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.parser.Parser.LibrariesOffset.NoLibraries import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.traits.domain.Recipient.{Address, Alias} @@ -30,7 +31,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 { @@ -38,20 +38,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, @@ -63,7 +63,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, @@ -75,22 +75,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, NoLibraries, ctx.compilerContext, StdLibVersion.VersionDic.all.last) - val evalCtx = ctx.evaluationContext(env).asInstanceOf[EvaluationContext[Environment, Id]] + val evalCtx = ctx.evaluationContext(env).asInstanceOf[EvaluationContext[Id]] compiled.flatMap(v => EvaluatorV2 .applyCompleted( @@ -429,18 +429,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) @@ -448,13 +444,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, @@ -624,10 +616,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() @@ -1100,21 +1092,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, @@ -1398,7 +1390,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) } @@ -1411,7 +1403,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)) } @@ -2030,7 +2022,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)) } @@ -2044,7 +2036,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)) } @@ -2052,9 +2044,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 65a8b222920..94223f5c6d6 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 @@ -12,13 +12,11 @@ import com.wavesplatform.lang.v1.FunctionHeader.User import com.wavesplatform.lang.v1.compiler.Terms.{CONST_LONG, CaseObj, FUNC} import com.wavesplatform.lang.v1.compiler.Types.{ANY, BIGINT, BOOLEAN, LIST, LONG, PARAMETERIZED, PARAMETERIZEDLIST, STRING, UNION} import com.wavesplatform.lang.v1.compiler.{ContractScriptCompactor, TestCompiler} -import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext import com.wavesplatform.lang.v1.evaluator.ContextfulVal import com.wavesplatform.lang.v1.evaluator.ctx.impl.Rounding.Down import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.{Types, WavesContext} import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, GlobalValNames, PureContext} import com.wavesplatform.lang.v1.parser.{Expressions, Parser} -import com.wavesplatform.lang.v1.traits.Environment import com.wavesplatform.lang.v1.{CTX, compiler} import com.wavesplatform.protobuf.dapp.DAppMeta import com.wavesplatform.protobuf.dapp.DAppMeta.CompactNameAndOriginalNamePair @@ -31,20 +29,20 @@ class ContractCompilerCompactorTest extends PropSpec { Script.decompile(ContractScriptImpl(stdLibVersion, dApp.copy(meta = DAppMeta())))._1 } - private def fullCtxForV(version: StdLibVersion): CTX[Environment] = { + private def fullCtxForV(version: StdLibVersion): CTX = { val transactionType = Types.buildTransferTransactionType(true) val tx = CaseObj(transactionType, Map("amount" -> CONST_LONG(100000000L))) ctxForV(version) |+| - CryptoContext.build(Global, version).withEnvironment[Environment] |+| - CTX[NoContext]( + CryptoContext.build(Global, version) |+| + CTX( Seq(transactionType), - Map(("tx", (transactionType, ContextfulVal.pure[NoContext](tx)))), + Map(("tx", (transactionType, ContextfulVal.pure(tx)))), Array.empty - ).withEnvironment[Environment] + ) } - private def ctxForV(version: StdLibVersion): CTX[Environment] = - PureContext.build(version, useNewPowPrecision = true).withEnvironment[Environment] |+| + private def ctxForV(version: StdLibVersion): CTX = + PureContext.build(version, useNewPowPrecision = true) |+| WavesContext.build(Global, DirectiveSet(version, Account, DAppType).explicitGet(), fixBigScriptField = true) property("contract script compaction - V4, V5, V6") { 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 44edd3216e3..91635cbf1f8 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 @@ -19,7 +19,6 @@ import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.{FieldNames, Types, Wa import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, GlobalValNames, PureContext} import com.wavesplatform.lang.v1.parser.Parser import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset.NoLibraries -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 @@ -31,8 +30,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(), @@ -367,8 +366,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(), @@ -495,8 +494,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(), @@ -814,7 +813,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") @@ -824,8 +823,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(), @@ -921,7 +920,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") @@ -959,7 +958,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") @@ -982,7 +981,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 f8f31be7e6b..14c002619c2 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 @@ -17,7 +17,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.protobuf.dapp.DAppMeta import com.wavesplatform.test.PropSpec @@ -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 1a7855d8fb4..ae1e153ef09 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 @@ -23,7 +23,6 @@ import com.wavesplatform.lang.v1.parser.Expressions.Pos.AnyPos import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset.NoLibraries 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.* @@ -312,8 +311,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 b953caabf5f..b0ee3516494 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,12 +3,12 @@ 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.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 @@ -17,7 +17,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 { @@ -28,10 +28,10 @@ class ScriptPreprocessorTest extends PropSpec with ScriptGenParser { } yield r private def eval(code: String): Either[String, EVALUATED] = { - val untyped = Parser.parseExpr(code).get.value - val ctx = Monoid.combineAll(Seq(PureContext.build(V3, useNewPowPrecision = true))) - val typed = ExpressionCompiler(ctx.compilerContext, V3, untyped) - typed.flatMap(v => evaluator[EVALUATED](ctx.evaluationContext, v._1).leftMap(_.toString)) + val untyped = Parser.parseExpr(code).get.value + val ctx: CTX = Monoid.combineAll(Seq(PureContext.build(V3, useNewPowPrecision = true))) + val typed = ExpressionCompiler(ctx.compilerContext, untyped) + 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 a28ea47dbab..9806d96155d 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 ed72f994c1f..1a97087c502 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 5f7bf558678..d40aa3fb462 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 80042803f63..5077ede6108 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 @@ -5,11 +5,11 @@ import cats.implicits.* import com.wavesplatform.common.utils.EitherExt2 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.LibrariesOffset.NoLibraries import com.wavesplatform.lang.v1.testing.ScriptGen import com.wavesplatform.lang.{Common, ExecutionError} @@ -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,16 +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, - fixedThrownError = true - ) + Ev.run(expr, evalCtx, ComplexityLimit.Unlimited, newMode = true, version) } private 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 93359c2b6e6..bab525ff525 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,23 +1,21 @@ 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} import com.wavesplatform.crypto.* import com.wavesplatform.lang.Common.* import com.wavesplatform.lang.Testing.* -import com.wavesplatform.lang.directives.values.{StdLibVersion, *} +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,15 +24,16 @@ 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.evaluator.{ContextfulVal, EvaluatorV1, EvaluatorV2, FunctionIds, Log} import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset.NoLibraries -import com.wavesplatform.lang.v1.traits.Environment 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 +import java.nio.ByteBuffer + class EvaluatorV1V2Test extends PropSpec with EitherValues { implicit val version: StdLibVersion = V4 @@ -45,11 +44,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(), @@ -58,15 +57,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, @@ -81,7 +80,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) @@ -89,22 +88,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])] = { - val evaluatorV1Result = defaultEvaluator.applyWithLogging[EVALUATED](context, expr) - val (evaluatorV2Log, _, evaluatorV2Result) = - EvaluatorV2.applyCompleted( - context, - expr, - LogExtraInfo(), - implicitly[StdLibVersion], - correctFunctionCallScope = true, - newMode = true, - enableExecutionLog = true, - fixedThrownError = true - ) + 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) = Ev.run(expr, context, ComplexityLimit.Unlimited, true, version) evaluatorV2Result shouldBe evaluatorV1Result.bimap(_._1, _._1) evaluatorV2Log should contain allElementsOf evaluatorV1Result.fold(_._2, _._2) @@ -122,7 +111,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( @@ -232,15 +221,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) } @@ -258,18 +248,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, @@ -282,22 +274,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")))) @@ -314,10 +301,11 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { val fooInstance = CaseObj(fooType, Map("bar" -> "bAr", "buz" -> 1L)) - val context = EvaluationContext.build( + val context = EvaluationContext( + Common.emptyBlockchainEnvironment(), typeDefs = Map.empty, letDefs = Map("fooInstance" -> LazyVal.fromEvaluated[Id](fooInstance)), - functions = Seq() + functions = Map.empty ) val expr = GETTER(REF("fooInstance"), "bar") @@ -327,14 +315,14 @@ 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( + val context = EvaluationContext( + Common.emptyBlockchainEnvironment(), typeDefs = Map.empty, letDefs = Map.empty, - functions = Seq(fooCtor) + functions = Map(fooCtor.header -> fooCtor) ) val expr = GETTER(FUNCTION_CALL(fooCtor.header, List.empty), "bar") @@ -344,7 +332,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, @@ -356,15 +344,16 @@ 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( + val context = EvaluationContext( + Common.emptyBlockchainEnvironment(), typeDefs = Map.empty, letDefs = Map.empty, - functions = Seq(fooCtor, fooTransform) + functions = Map(fooCtor.header -> fooCtor, fooTransform.header -> fooTransform) ) forAll(blockBuilder) { block => @@ -381,10 +370,11 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { property("successful on simple function evaluation") { evalPure[EVALUATED]( - context = EvaluationContext.build( + context = EvaluationContext( + Common.emptyBlockchainEnvironment(), typeDefs = Map.empty, letDefs = Map.empty, - functions = Seq(multiplierFunction) + functions = Map(multiplierFunction.header -> multiplierFunction) ), expr = FUNCTION_CALL(multiplierFunction.header, List(CONST_LONG(3), CONST_LONG(4))) ) shouldBe evaluated(12) @@ -485,7 +475,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) @@ -501,7 +491,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) @@ -567,7 +557,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)) } } @@ -581,7 +571,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") } } @@ -593,7 +583,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)) } } @@ -606,7 +596,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)) } } @@ -624,7 +614,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)) } } @@ -763,15 +753,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]) ) ) @@ -782,7 +772,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 @@ -840,17 +830,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, @@ -869,18 +857,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, version, xs) .explicitGet() @@ -891,17 +879,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];""" @@ -1020,10 +1007,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 @@ -1031,10 +1018,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""" @@ -1064,7 +1050,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, @@ -1099,7 +1092,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(Common.emptyBlockchainEnvironment(), typeDefs = Map(point -> pointType), letDefs = Map.empty, functions = Map()), FUNCTION_CALL(pointCtor, List(CONST_LONG(1), CONST_LONG(2))) ) shouldBe evaluated(CaseObj(pointType, Map("X" -> CONST_LONG(1), "Y" -> CONST_LONG(2)))) } @@ -1135,25 +1129,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, _) => @@ -1169,12 +1163,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"))) } @@ -1182,17 +1176,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))) @@ -1247,7 +1241,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 + ) } ) } @@ -1257,7 +1253,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 1b9dfbed60a..d68701edefb 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, @@ -35,8 +35,9 @@ class EvaluatorV2Test extends PropSpec with Inside { newMode, fixedThrownError = true ) - .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) @@ -46,21 +47,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) = @@ -1198,8 +1199,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 = """ @@ -1217,7 +1218,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)) } @@ -1249,7 +1250,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..a1ff1733261 --- /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} +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, 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 570ff7ad77b..09f710f29a1 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, V4, 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 67eb44c4716..fd15a56bbb5 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 @@ -145,8 +145,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, @@ -158,14 +158,12 @@ object UtilsEvaluator { enableExecutionLog = true, fixedThrownError = 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)) } snapshot <- ScriptResult - .fromObj(ctx, invoke.id(), evaluated, ds.stdLibVersion, unusedComplexity = 0) + .fromObj(ctx, invoke.id(), result._1, ds.stdLibVersion, result._2) .bimap( _ => Right(StateSnapshot.empty), r => @@ -176,7 +174,7 @@ object UtilsEvaluator { script.stdLibVersion, dAppAddress, dAppPk, - usedComplexity, + r.spentComplexity, invoke, SnapshotBlockchain(blockchain, environment.currentSnapshot), System.currentTimeMillis(), @@ -196,7 +194,7 @@ object UtilsEvaluator { _ <- TransactionDiffer.assetsVerifierDiff(blockchain, invoke, verify = true, totalSnapshot, Int.MaxValue, enableExecutionLog = true).resultE rootScriptResult = snapshot.scriptResults.headOption.map(_._2).getOrElse(InvokeScriptResult.empty) innerScriptResult = environment.currentSnapshot.scriptResults.values.fold(InvokeScriptResult.empty)(_ |+| _) - } yield ExecuteResult(evaluated, usedComplexity, log, innerScriptResult |+| rootScriptResult) + } yield ExecuteResult(result._1, result._2, log, innerScriptResult |+| rootScriptResult) private def addWavesToDefaultInvoker(snapshot: StateSnapshot, blockchain: Blockchain) = if (snapshot.balances.get((UtilsApiRoute.DefaultAddress, Waves)).exists(_ >= 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 73f3372376d..4a61e235267 100644 --- a/node/src/main/scala/com/wavesplatform/database/Caches.scala +++ b/node/src/main/scala/com/wavesplatform/database/Caches.scala @@ -405,7 +405,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/state/SnapshotBlockchain.scala b/node/src/main/scala/com/wavesplatform/state/SnapshotBlockchain.scala index 51146f08bb9..2a08d4a9755 100644 --- a/node/src/main/scala/com/wavesplatform/state/SnapshotBlockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/SnapshotBlockchain.scala @@ -142,18 +142,16 @@ case class SnapshotBlockchain( Some(bs) } - override def balanceSnapshots(address: Address, from: Int, to: Option[BlockId]): Seq[BalanceSnapshot] = { - val from1 = math.max(from, 1) - - if (maybeSnapshot.isEmpty || to.exists(id => inner.heightOf(id).isDefined)) { - inner.balanceSnapshots(address, from1, to) + override def balanceSnapshots(address: Address, from: Int, to: Option[BlockId]): Seq[BalanceSnapshot] = + if (maybeSnapshot.isEmpty || to != blockMeta.map(_._1.id())) { + inner.balanceSnapshots(address, from, to) } else { val balance = this.balance(address) val lease = this.leaseBalance(address) - val bs = BalanceSnapshot(height, Portfolio(balance, lease)) - val height2Fix = this.height == 2 && from1 < 2 && inner.isFeatureActivated(RideV6) - if (inner.height > 0 && (from1 < this.height - 1 || height2Fix)) - bs +: inner.balanceSnapshots(address, from1, to) + val bs = BalanceSnapshot(this.height, Portfolio(balance, lease), this.hasBannedEffectiveBalance(address, this.height)) + val height2Fix = this.height == 2 && inner.isFeatureActivated(RideV6) && from < this.height + if (inner.height > 0 && (from < this.height - 1 || height2Fix)) + bs +: inner.balanceSnapshots(address, from, None) else Seq(bs) } 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 5b6b69f867d..a6437ff8789 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/BlockDiffer.scala @@ -42,6 +42,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], @@ -124,15 +140,7 @@ object BlockDiffer { if (stateHeight >= sponsorshipHeight) { Right(Portfolio(balance = blockchain.carryFee(None))) } 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) @@ -449,8 +457,10 @@ object BlockDiffer { TxFeeInfo(feeAsset, feeAmount, carry, wavesFee) } + private val AllPatches = Seq(CancelAllLeases, CancelLeaseOverflow, CancelInvalidLeaseIn, CancelLeasesToDisabledAliases) private def leasePatchesSnapshot(blockchain: Blockchain): StateSnapshot = - Seq(CancelAllLeases, CancelLeaseOverflow, CancelInvalidLeaseIn, CancelLeasesToDisabledAliases) + AllPatches + .filter(_.isDefinedAt(blockchain)) .foldLeft(StateSnapshot.empty) { case (prevSnapshot, patch) => prevSnapshot |+| patch.lift(SnapshotBlockchain(blockchain, prevSnapshot)).orEmpty } 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 f8c2e1b4775..eae1907cbd1 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 @@ -254,7 +254,7 @@ object InvokeDiffsCommon { } yield () } - private def actionsToScriptResult( + def actionsToScriptResult( actions: StructuredCallableActions, storingComplexity: Int, tx: InvokeScriptLike, @@ -608,7 +608,10 @@ object InvokeDiffsCommon { def applyReissue(reissue: Reissue, pk: PublicKey): TracedResult[ValidationError, StateSnapshot] = { val reissueDiff = - DiffsCommon.processReissue(blockchain, dAppAddress, blockTime, fee = 0, reissue).leftMap(FailedTransactionError.asFailedScriptError) + DiffsCommon.processReissue(blockchain, dAppAddress, blockTime, fee = 0, reissue).leftMap[ValidationError] { + case fore: FailOrRejectError => fore + case other => FailOrRejectError(other.toString, false) + } val pseudoTx = ReissuePseudoTx(reissue, actionSender, pk, tx.txId, tx.timestamp) callAssetVerifierWithPseudoTx(reissueDiff, reissue.assetId, pseudoTx, AssetContext.Reissue) } @@ -653,7 +656,7 @@ object InvokeDiffsCommon { } yield diff def callAssetVerifierWithPseudoTx( - actionDiff: Either[FailedTransactionError, StateSnapshot], + actionDiff: Either[ValidationError, StateSnapshot], assetId: ByteStr, pseudoTx: PseudoTx, assetType: AssetContext @@ -714,7 +717,7 @@ object InvokeDiffsCommon { } } - private def validatePseudoTxWithSmartAssetScript(blockchain: Blockchain, tx: InvokeScriptLike)( + def validatePseudoTxWithSmartAssetScript(blockchain: Blockchain, tx: InvokeScriptLike)( pseudoTx: PseudoTx, assetId: ByteStr, nextSnapshot: StateSnapshot, @@ -752,6 +755,13 @@ object InvokeDiffsCommon { case Success(s) => s } + case class ActionCount( + asset: Int, + balance: Int, + data: Int, + dataSize: Int + ) + def checkCallResultLimits( currentVersion: StdLibVersion, rootVersion: StdLibVersion, 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 deleted file mode 100644 index 427ae759f82..00000000000 --- a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptDiff.scala +++ /dev/null @@ -1,474 +0,0 @@ -package com.wavesplatform.state.diffs.invoke - -import cats.Id -import cats.implicits.{catsSyntaxSemigroup, 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.BlockchainFeatures.LightNode -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.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.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.SnapshotBlockchain -import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} -import com.wavesplatform.transaction.TxValidationError.* -import com.wavesplatform.transaction.smart.DAppEnvironment.ActionLimits -import com.wavesplatform.transaction.smart.script.ScriptRunner -import com.wavesplatform.transaction.smart.script.ScriptRunner.TxOrd -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, - blockTime: Long, - rootVersion: StdLibVersion, - limitedExecution: Boolean, - enableExecutionLog: Boolean, - totalComplexityLimit: Int, - remainingComplexity: Int, - remainingCalls: Int, - remainingActions: ActionLimits, - remainingPayments: Int, - calledAddresses: Set[Address], - invocationRoot: DAppEnvironment.InvocationTreeTracker, - wrapDAppEnv: DAppEnvironment => DAppEnvironmentInterface = identity - )( - tx: InvokeScript - ): CoevalR[(StateSnapshot, EVALUATED, ActionLimits, Int)] = { - val dAppAddress = tx.dApp - val invoker = tx.sender.toAddress - - val result = blockchain.accountScript(dAppAddress) match { - case Some(AccountScriptInfo(pk, ContractScriptImpl(version, contract), _, _)) => - 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) - - 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(blockchain, tx, tx.dApp, Map())) - ( - resultSnapshot, - (scriptResult, log), - availableActions, - availablePayments - ) <- { - 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) (StateSnapshot.empty, paymentsPart) else (paymentsPart, StateSnapshot.empty) - val environment = wrapDAppEnv(new DAppEnvironment( - AddressScheme.current.chainId, - Coeval.evalOnce(input), - Coeval(height), - blockchain, - tthis, - directives, - rootVersion, - tx.root, - tx.dApp, - pk, - calledAddresses, - limitedExecution, - enableExecutionLog, - totalComplexityLimit, - remainingCalls - 1, - remainingActions, - remainingPayments - tx.payments.size, - paymentsPartInsideDApp, - invocationRoot, - wrapDAppEnv - )) - for { - evaluated <- CoevalR( - evaluateV2( - version, - blockchain, - contract, - dAppAddress, - invocation, - environment, - complexityAfterPayments, - remainingComplexity, - enableExecutionLog - ).map(TracedResult(_)) - ) - resultSnapshot = environment.currentSnapshot |+| paymentsPartToResolve - } yield ( - resultSnapshot, - evaluated, - environment.availableActions, - environment.availablePayments - ) - }) - } - _ = invocationRoot.setLog(log) - spentComplexity = remainingComplexity - scriptResult.unusedComplexity.max(0) - - _ <- - if (blockchain.isFeatureActivated(LightNode)) - traced(Right(())) - else - validateIntermediateBalances(blockchain, resultSnapshot, spentComplexity, log) - - doProcessActions = (actions: List[CallableAction], unusedComplexity: Int) => { - val storingComplexity = complexityAfterPayments - unusedComplexity - CoevalR( - Coeval.now( - InvokeDiffsCommon.processActions( - StructuredCallableActions(actions, blockchain), - version, - rootVersion, - dAppAddress, - pk, - storingComplexity, - tx, - SnapshotBlockchain(blockchain, resultSnapshot), - 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, - rootVersion, - blockchain, - remainingComplexity - unusedComplexity, - log, - actionsCount, - balanceActionsCount, - assetActionsCount, - dataCount, - dataSize, - availableActions - ) - ) - ) - snapshot <- doProcessActions(actions, unusedComplexity) - } yield ( - snapshot, - ret, - availableActions.decrease(actionsCount, balanceActionsCount, assetActionsCount, dataCount, dataSize), - availablePayments - ) - } - - ( - actionsSnapshot, - evaluated, - remainingActions1, - remainingPayments1 - ) <- - 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, - availablePayments - ) - ) - case r: IncompleteResult => - val usedComplexity = remainingComplexity - r.unusedComplexity - val error = - FailedTransactionError.dAppExecution(s"Invoke complexity limit = $totalComplexityLimit is exceeded", usedComplexity, log) - traced(error.asLeft[(StateSnapshot, EVALUATED, ActionLimits, Int)]) - } - resultSnapshot <- traced( - (resultSnapshot.setScriptsComplexity(0) |+| actionsSnapshot.addScriptsComplexity(paymentsComplexity)).asRight - ) - _ <- - if (blockchain.isFeatureActivated(LightNode)) - traced(Right(())) - else - validateIntermediateBalances(blockchain, resultSnapshot, resultSnapshot.scriptsComplexity, log) - _ = invocationRoot.setResult(scriptResult) - } yield ( - resultSnapshot, - evaluated, - remainingActions1, - remainingPayments1 - ) - } 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, - blockchain.isFeatureActivated(LightNode) - ) - .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) - }) - } - ) - } - - def validateIntermediateBalances(blockchain: Blockchain, snapshot: StateSnapshot, spentComplexity: Long, log: Log[Id]): CoevalR[Any] = - traced( - if (blockchain.isFeatureActivated(BlockchainFeatures.RideV6)) { - BalanceDiffValidation(blockchain)(snapshot) - .leftMap { be => FailedTransactionError.dAppExecution(be.toString, spentComplexity, log) } - } else if (blockchain.height >= blockchain.settings.functionalitySettings.enforceTransferValidationAfter) { - // reject transaction if any balance is negative - snapshot.balances.view - .flatMap { - case ((address, asset), balance) if balance < 0 => Some(address -> asset) - case _ => None - } - .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 = ${snapshot.balances((address, Waves))}" - case ia: IssuedAsset => - s"$address: Negative asset $ia balance: old = ${blockchain.balance(address, ia)}, new = ${snapshot.balances((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(()) - } - } -} 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 70ccce8b8a4..bb2de605d15 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 @@ -17,6 +17,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 @@ -40,7 +41,6 @@ import com.wavesplatform.transaction.smart.script.ScriptRunner.TxOrd import com.wavesplatform.transaction.smart.script.trace.{InvokeScriptTrace, TracedResult} import com.wavesplatform.transaction.smart.{DApp as DAppTarget, *} import com.wavesplatform.transaction.validation.impl.DataTxValidator -import monix.eval.Coeval import shapeless.Coproduct object InvokeScriptTransactionDiff { @@ -64,12 +64,12 @@ object InvokeScriptTransactionDiff { } yield (address, script) def executeInvoke( - pk: PublicKey, + dAppPublicKey: PublicKey, version: StdLibVersion, contract: DApp, dAppAddress: Address, - environment: DAppEnvironment, - invocation: ContractEvaluator.Invocation + invocation: ContractEvaluator.Invocation, + dappState: DAppState ) = { case class MainScriptResult( invocationSnapshot: StateSnapshot, @@ -103,18 +103,23 @@ object InvokeScriptTransactionDiff { contract, dAppAddress, invocation, - environment, fullLimit, failFreeLimit, paymentsComplexity, blockchain, - enableExecutionLog + dappState ) } yield MainScriptResult( - environment.currentSnapshot, + dappState.currentSnapshot, result, log, - environment.availableActions, + ActionLimits( + ContractLimits.MaxCallableActionsAmountBeforeV6(version), + ContractLimits.MaxBalanceScriptActionsAmountV6, + ContractLimits.MaxAssetScriptActionsAmountV6, + ContractLimits.MaxWriteSetSize, + ContractLimits.MaxTotalWriteSetSizeInBytes + ), fullLimit - paymentsComplexity ) } @@ -134,7 +139,7 @@ object InvokeScriptTransactionDiff { }, _.log ), - environment.invocationRoot.toTraceList(tx.id()) + Seq.empty ) ) ) @@ -155,7 +160,7 @@ object InvokeScriptTransactionDiff { version, version, dAppAddress, - pk, + dAppPublicKey, _, tx, SnapshotBlockchain(blockchain, invocationSnapshot), @@ -168,8 +173,8 @@ object InvokeScriptTransactionDiff { log ) - process = (actions: List[CallableAction], unusedComplexity: Long) => { - val storingComplexity = limit - unusedComplexity + process = (actions: List[CallableAction], usedComplexity: Long) => { + val storingComplexity = usedComplexity val dataEntries = actions.collect { case d: DataOp => InvokeDiffsCommon.dataItemToEntry(d) } val dataCount = dataEntries.length @@ -203,19 +208,19 @@ 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 = invocationSnapshot.setScriptsComplexity(0) |+| resultDiff } yield totalDiff } accScriptEi match { - case Right((dAppAddress, (pk, version, funcCall, contract, _))) => + case Right((dAppAddress, (dappPK, version, funcCall, contract, _))) => val invocationTracker = DAppEnvironment.InvocationTreeTracker(DAppEnvironment.DAppInvocation(dAppAddress, funcCall, tx.payments)) (for { _ <- TracedResult(checkCall(funcCall, blockchain).leftMap(GenericError(_))) @@ -230,33 +235,19 @@ object InvokeScriptTransactionDiff { else InvokeDiffsCommon.paymentsPart(blockchain, tx, dAppAddress, Map()) ) - environment = new DAppEnvironment( - AddressScheme.current.chainId, - Coeval.evalOnce(input), - Coeval.evalOnce(blockchain.height), - blockchain, - tthis, - directives, - version, + dappState = new DAppState( tx, - dAppAddress, - pk, - Set(tx.sender.toAddress), - limitedExecution, - enableExecutionLog, - ContractLimits.MaxTotalInvokeComplexity(version), - ContractLimits.MaxSyncDAppCalls(version), - ActionLimits( - ContractLimits.MaxCallableActionsAmountBeforeV6(version), - ContractLimits.MaxBalanceScriptActionsAmountV6, - ContractLimits.MaxAssetScriptActionsAmountV6, - ContractLimits.MaxWriteSetSize, - ContractLimits.MaxTotalWriteSetSizeInBytes - ), - ContractLimits.MaxTotalPaymentAmountRideV6 - tx.payments.size, + 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( @@ -270,7 +261,7 @@ object InvokeScriptTransactionDiff { tx.fee, tx.feeAssetId.compatId ) - result <- executeInvoke(pk, version, contract, dAppAddress, environment, invocation) + result <- executeInvoke(dappPK, version, contract, dAppAddress, invocation, dappState) } yield result).leftMap { case fte: FailedTransactionError => fte.copy(invocations = invocationTracker.toInvocationList) case other => other @@ -315,18 +306,15 @@ object InvokeScriptTransactionDiff { contract: DApp, dAppAddress: Address, invocation: ContractEvaluator.Invocation, - environment: Environment[Id], limit: Int, failFreeLimit: 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, @@ -334,17 +322,12 @@ object InvokeScriptTransactionDiff { startLimit, blockchain.correctFunctionCallScope, blockchain.newEvaluatorMode, - enableExecutionLog, - blockchain.isFeatureActivated(LightNode) + 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 @@ -352,16 +335,17 @@ object InvokeScriptTransactionDiff { if (usedComplexity > failFreeLimit) { FailedTransactionError.dAppExecution(msg, usedComplexity + paymentsComplexity, log) } else - InvokeRejectError(msg, log) + InvokeRejectError(s"$msg, used = $usedComplexity, failFreeLimit = $failFreeLimit", log) } .flatTap { case (r, log) => + dAppState.printInvocations() InvokeDiffsCommon .checkScriptResultFields(blockchain, r) .leftMap[ValidationError] { 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 @@ -369,7 +353,7 @@ object InvokeScriptTransactionDiff { if (usedComplexity > failFreeLimit) { FailedTransactionError.dAppExecution(msg, usedComplexity + paymentsComplexity, log) } else - InvokeRejectError(msg, log) + InvokeRejectError(s"$msg, used = $usedComplexity, failFreeLimit = $failFreeLimit", log) } } } 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 f77a8d72cd0..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 = 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..817d3f65bf0 --- /dev/null +++ b/node/src/main/scala/com/wavesplatform/transaction/smart/DAppState.scala @@ -0,0 +1,323 @@ +package com.wavesplatform.transaction.smart + +import cats.Id +import cats.syntax.either.* +import com.wavesplatform.account.{Address, 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.{IncompleteResult, 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, FailOrRejectError, ValidationError} +import com.wavesplatform.state.diffs.BalanceDiffValidation +import com.wavesplatform.state.diffs.invoke.* +import com.wavesplatform.state.diffs.invoke.InvokeDiffsCommon.ActionCount +import com.wavesplatform.state.reader.SnapshotBlockchain +import com.wavesplatform.state.{Blockchain, InvokeScriptResult, StateSnapshot} +import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.smart.DAppEnvironment.ActionLimits +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 snapshot: StateSnapshot, + inputEntity: Environment.InputEntity, + ds: DirectiveSet, + complexityLimit: ComplexityLimit, + newMode: Boolean +) extends State(complexityLimit, newMode) + with Logging { + import DAppState.* + + private var currentBlockchain = SnapshotBlockchain(blockchain, snapshot) + + 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 currentInvocation(): InvocationFrame = invocationStack.head + + def blockchain(): Blockchain = currentBlockchain + + override def stdlibVersion: StdLibVersion = currentInvocation().version + + override def evaluationContext: EvaluationContext[Id] = currentInvocation().ec + + 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(currentBlockchain, 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 + println(s"${" " * reentrancyStack.size}> ${invocation.dApp}") + push(new FromInvocation(rootVersion, 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 printInvocations(): Unit = { + def zzz(top: InvocationFrame, offset: Int, r: InvokeScriptResult): Unit = { + println(s"${" " * offset}${top.dappPK.toAddress}: $r") + top.invocations.foreach { case (f, r) => + zzz(f, offset + 1, r) + + } + } + currentInvocation().invocations.foreach { case (f, r) => zzz(f, 0, r) } + } + + + def countActions(actions: List[CallableAction]): ActionCount = { + 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 currentDApp: PublicKey = invocationStack.head.dappPK + def currentSnapshot: StateSnapshot = snapshot + + private[DAppState] def appendDiff(diff: StateSnapshot): StateSnapshot = { + import cats.syntax.semigroup.* + this.snapshot = this.snapshot.combine(diff) + this.currentBlockchain = SnapshotBlockchain(blockchain, this.snapshot) + this.currentSnapshot + } + + private[DAppState] def popInvocation(isr: InvokeScriptResult): InvocationFrame = { + val poppedFrame = invocationStack.head + println(s"${" " * (invocationStack.size - 1)}< ${poppedFrame.dappPK.toAddress}") + val newHead :: newTail = invocationStack.tail + invocationStack = newHead.copy(invocations = (newHead.invocations :+ (poppedFrame -> isr))) :: newTail + reentrancyStack = reentrancyStack.tail + poppedFrame + } +} + +object DAppState { + case class InvocationFrame( + invocation: InvokeScriptLike, + dappPK: PublicKey, + version: StdLibVersion, + ec: EvaluationContext[Id], + invocations: Seq[(InvocationFrame, InvokeScriptResult)] = Seq.empty + ) + + private def runPaymentAssetScripts( + initialDiff: StateSnapshot, + invocationFrame: InvocationFrame, + blockchain: Blockchain + ): Either[ValidationError, StateSnapshot] = { + 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(rootVersion: StdLibVersion, savedScope: Ev.Scope, ds: DAppState, invocation: InvocationFrame) extends Op { + override def ret(ev: EVALUATED): Op.Result = { + // if actions can not be extracted from `ev`, return Left(ExecutionError) + val scriptResultE = for { + comp <- ds.totalSpentComplexity() + result <- ScriptResult.fromObj(ds.evaluationContext, ds.root.id(), ev, invocation.version, comp.toInt) + } yield (result, comp) + + scriptResultE match { + case Right((result, comp)) => + val actions = StructuredCallableActions( + result match { + case v3: ScriptResultV3 => v3.ds ++ v3.ts + case v4: ScriptResultV4 => v4.actions + case _: IncompleteResult => Nil + }, + ds.blockchain() + ) + + val snapshotAndResultE = for { + _ <- InvokeDiffsCommon + .checkScriptResultFields(ds.blockchain(), result) + _ <- InvokeDiffsCommon + .checkCallResultLimits( + invocation.version, + rootVersion, + ds.blockchain(), + comp, + ds.logEntries.toList, + ds.allActionCount, + ds.balanceActionCount, + ds.assetActionCount, + ds.dataItemCount, + ds.dataSize, + ActionLimits( + ContractLimits.MaxCallableActionsAmountBeforeV6(invocation.version), + ContractLimits.MaxBalanceScriptActionsAmountV6, + ContractLimits.MaxAssetScriptActionsAmountV6, + ContractLimits.MaxWriteSetSize, + ContractLimits.MaxTotalWriteSetSizeInBytes + ) + ) + .resultE + snapshot <- InvokeDiffsCommon + .processActions( + actions, + invocation.version, + rootVersion, + ds.currentDApp.toAddress, + ds.currentDApp, + comp.toInt, + ds.currentInvocation().invocation, + ds.blockchain(), + ds.blockchain().lastBlockTimestamp.get, + true, + false, + Int.MaxValue, + Seq.empty, + false, + Nil + ) + .resultE + _ <- runPaymentAssetScripts(snapshot, invocation, ds.blockchain()) + isr <- InvokeDiffsCommon.actionsToScriptResult(actions, comp.toInt, invocation.invocation, Nil).resultE + } yield (snapshot, isr) + + snapshotAndResultE match { + case Right((snapshot, isr)) => + ds.appendDiff(snapshot) + ds.popInvocation(isr) + Right(result.returnedValue) -> Some(savedScope) + case Left(error) => + ds.popInvocation(InvokeScriptResult(error = Some(InvokeScriptResult.ErrorMessage(1, error.toString)))) + Left(FailOrRejectError(error.toString)) -> Some(savedScope) + } + + case Left(error) => + ds.popInvocation(InvokeScriptResult(error = Some(InvokeScriptResult.ErrorMessage(1, error.toString)))) + Left(error) -> Some(savedScope) + } + + } + } +} 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..41bd15d88cb --- /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")) + } + + for { + da <- dappAddress + functionName <- function match { + case fn: CONST_STRING => Right(fn.s) + case `unit` => Right("default") + case other => Left(CommonError(s"${other} is not a valid function name")) + } + (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 f4eb97e1a66..9ed43736590 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/smart/WavesEnvironment.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/smart/WavesEnvironment.scala @@ -1,7 +1,5 @@ package com.wavesplatform.transaction.smart -import cats.Id -import cats.implicits.catsSyntaxSemigroup import cats.syntax.either.* import com.wavesplatform.account import com.wavesplatform.account.{AddressOrAlias, PublicKey} @@ -15,27 +13,25 @@ import com.wavesplatform.features.MultiPaymentPolicyProvider.* import com.wavesplatform.lang.directives.DirectiveSet import com.wavesplatform.lang.directives.values.StdLibVersion 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.lang.{Global, ValidationError} import com.wavesplatform.state.* import com.wavesplatform.state.BlockRewardCalculator.CurrentBlockRewardPart -import com.wavesplatform.state.diffs.invoke.InvokeScriptDiff.validateIntermediateBalances -import com.wavesplatform.state.diffs.invoke.{InvokeScript, InvokeScriptDiff, InvokeScriptTransactionLike} +import com.wavesplatform.state.diffs.invoke.InvokeScriptTransactionLike +import com.wavesplatform.state.reader.SnapshotBlockchain 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.DAppEnvironment.ActionLimits -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.* @@ -45,31 +41,29 @@ object WavesEnvironment { type In = TransactionBase :+: Order :+: PseudoTx :+: CNil def apply( - nByte: Byte, - in: Coeval[Environment.InputEntity], - h: Coeval[Int], - blockchain: Blockchain, + inputEntity: Environment.InputEntity, tthis: Environment.Tthis, + txId: ByteStr, ds: DirectiveSet, - txId: ByteStr - ): WavesEnvironment = new WavesEnvironment(nByte, in, h, blockchain, tthis, ds, txId, blockchain) + 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, +abstract class WavesEnvironment( + override val inputEntity: Environment.InputEntity, + _tthis: Environment.Tthis, override val txId: ByteStr, - blockchainForRuntime: Blockchain + val ds: DirectiveSet ) extends Environment[Id] { - import com.wavesplatform.lang.v1.traits.Environment.* - def currentBlockchain(): Blockchain = blockchainForRuntime + def blockchain: Blockchain + + override def tthis: Tthis = _tthis - override def height: Long = h() + override def chainId: Byte = blockchain.settings.addressSchemeCharacter.toByte + override def height: Long = blockchain.height override def multiPaymentAllowed: Boolean = blockchainForRuntime.allowsMultiPayment @@ -81,8 +75,6 @@ class WavesEnvironment( .collect { case (_, tx) if tx.t.tpe != TransactionType.Ethereum => tx } .map(tx => RealTransactionWrapper(tx, blockchainForRuntime, 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 @@ -106,7 +98,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 { @@ -132,7 +124,7 @@ class WavesEnvironment( .flatMap(blockchain.resolveAlias) .toOption } - } yield currentBlockchain() + } yield blockchain .hasData(address)).getOrElse(false) } @@ -144,8 +136,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 { @@ -153,7 +143,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) } @@ -164,8 +154,8 @@ class WavesEnvironment( } for { address <- addressE.leftMap(_.toString) - portfolio = currentBlockchain().wavesPortfolio(address) - isBanned = currentBlockchain().hasBannedEffectiveBalance(address) + portfolio = blockchain.wavesPortfolio(address) + isBanned = blockchain.hasBannedEffectiveBalance(address) effectiveBalance <- portfolio.effectiveBalance(isBanned) } yield Environment.BalanceDetails( portfolio.balance - portfolio.lease.out, @@ -184,7 +174,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), @@ -265,7 +255,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") + ??? + } override def calculateDelay(generator: ByteStr, balance: Long): Long = { val baseTarget = blockchain.lastBlockHeader.map(_.header.baseTarget).getOrElse(0L) @@ -399,7 +392,7 @@ class DAppEnvironment( nByte: Byte, in: Coeval[Environment.InputEntity], h: Coeval[Int], - blockchain: Blockchain, + blockchain_ : Blockchain, tthis: Environment.Tthis, val ds: DirectiveSet, rootVersion: StdLibVersion, @@ -414,14 +407,12 @@ class DAppEnvironment( var availableActions: ActionLimits, var availablePayments: Int, var currentSnapshot: StateSnapshot, - val invocationRoot: DAppEnvironment.InvocationTreeTracker, - wrapDAppEnv: DAppEnvironment => DAppEnvironmentInterface = identity -) extends WavesEnvironment(nByte, in, h, blockchain, tthis, ds, tx.id(), blockchain) - with DAppEnvironmentInterface { + val invocationRoot: DAppEnvironment.InvocationTreeTracker +) extends WavesEnvironment(in(), tthis, tx.id(), ds) { - private[this] var mutableBlockchain = SnapshotBlockchain(blockchain, currentSnapshot) + private[this] val mutableBlockchain = SnapshotBlockchain(blockchain_, currentSnapshot) - override def currentBlockchain(): SnapshotBlockchain = this.mutableBlockchain + override def blockchain: SnapshotBlockchain = this.mutableBlockchain override def callScript( dApp: Address, @@ -431,6 +422,9 @@ class DAppEnvironment( availableComplexity: Int, reentrant: Boolean ): Coeval[(Either[ValidationError, (EVALUATED, Log[Id])], Int)] = { + println(s"\n\tDAppEnvironment: ???\n") + ??? + } /*{ val r = for { address <- traced( @@ -508,5 +502,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 3d19d95f3d6..b06e71cd4d1 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 @@ -2,7 +2,6 @@ package com.wavesplatform.transaction.smart.script import cats.Id import cats.syntax.either.* -import com.wavesplatform.account.AddressScheme import com.wavesplatform.common.state.ByteStr import com.wavesplatform.features.BlockchainFeatures.* import com.wavesplatform.features.EstimatorProvider.* @@ -79,7 +78,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 = @@ -89,9 +88,7 @@ object ScriptRunner { ctx <- BlockchainContext .build( script.stdLibVersion, - AddressScheme.current.chainId, Coeval.evalOnce(mi), - Coeval.evalOnce(blockchain.height), blockchain, isAssetScript, isContract, @@ -107,7 +104,7 @@ object ScriptRunner { } def evaluate( - ctx: EvaluationContext[Environment, Id], + ctx: EvaluationContext[Id], expr: EXPR, logExtraInfo: LogExtraInfo, version: StdLibVersion @@ -127,7 +124,7 @@ object ScriptRunner { else (defaultLimit, (_: EXPR) => Right(default)) - val (log, unusedComplexity, result) = + val (log, usedComplexity, result) = EvaluatorV2.applyOrDefault( ctx, expr, @@ -141,7 +138,7 @@ object ScriptRunner { fixedThrownError ) - (log, limit - unusedComplexity, result) + (log, usedComplexity, result) } script match { @@ -149,22 +146,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, bindingsVersion)), - _ => ??? - ) + 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, bindingsVersion)), + _ => ??? ) + ) } 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 58def9c43cc..d69661ef905 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -39,6 +39,7 @@ import monix.eval.Task import monix.execution.Scheduler 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} @@ -232,7 +233,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 => @@ -241,7 +242,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 7fb9411794a..7e00a102c3d 100644 --- a/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala +++ b/node/src/test/scala/com/wavesplatform/serialization/EvaluatedPBSerializationTest.scala @@ -106,9 +106,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/InvocationTrackerTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/InvocationTrackerTest.scala new file mode 100644 index 00000000000..e27e39ec8fd --- /dev/null +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/InvocationTrackerTest.scala @@ -0,0 +1,56 @@ +package com.wavesplatform.state.diffs.ci.sync + +import com.wavesplatform.db.WithDomain +import com.wavesplatform.db.WithState.AddrWithBalance +import com.wavesplatform.lang.directives.values.V6 +import com.wavesplatform.lang.v1.compiler.TestCompiler +import com.wavesplatform.test.* +import com.wavesplatform.transaction.TxHelpers + +class InvocationTrackerTest extends FreeSpec with WithDomain { + private val caller, dapp1, dapp2, dapp3 = TxHelpers.signer() + "nested invocation tree" in withDomain(DomainPresets.RideV6, balances = + Seq( + AddrWithBalance(caller.toAddress, 10.waves), + AddrWithBalance(dapp1.toAddress, 10.waves), + AddrWithBalance(dapp2.toAddress, 10.waves), + AddrWithBalance(dapp3.toAddress, 10.waves) + ) + ) { d => + d.appendBlock( + TxHelpers.setScript( + dapp1, + TestCompiler(V6).compileContract(s"""@Callable(i) + |func default() = { + | strict a1 = invoke(Address(base58'${dapp2.toAddress}'),"default",[],[]) + | strict a2 = invoke(Address(base58'${dapp2.toAddress}'),"default",[],[]) + | strict a3 = invoke(Address(base58'${dapp2.toAddress}'),"default",[],[]) + | [] + |} + |""".stripMargin) + ), + TxHelpers.setScript( + dapp2, + TestCompiler(V6).compileContract(s"""@Callable(i) + |func default() = { + | strict a1 = invoke(Address(base58'${dapp3.toAddress}'),"default",[],[]) + | strict a2 = invoke(Address(base58'${dapp3.toAddress}'),"default",[],[]) + | [] + |} + |""".stripMargin) + ), + TxHelpers.setScript( + dapp3, + TestCompiler(V6).compileContract("""@Callable(i) + |func default() = { + | [] + |} + |""".stripMargin) + ) + ) + val invocation = TxHelpers.invoke(dapp1.toAddress, invoker = caller) + d.appendBlock(invocation) + + println(d.transactionsApi.transactionById(invocation.id())) + } +} 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 a5e0a535087..923079be639 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) withTestState(features(invokeExpression)) { (bu, db) => 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 eea40a4817c..d08625c327c 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 @@ -49,7 +49,7 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain with Inside { | @Callable(i) | func default(end: Boolean, useSecondAddress: Boolean, forceInvoke: Boolean, forceReentrant: Boolean) = """.stripMargin - compile( + val scriptText = s""" | $prefix | if (end) @@ -57,7 +57,7 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain with Inside { | [] | 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 @@ -73,7 +73,7 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain with Inside { | [] | } """.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 e573d9caf04..e0aadd72a10 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 @@ -52,7 +52,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 @@ -63,7 +63,7 @@ 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 ) 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 57dbfd1b9e1..9f51fd3fe79 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 @@ -244,7 +244,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 87a67f98248..a19147ac893 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 @@ -20,7 +20,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.* @@ -835,8 +834,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 abc2a820aa8..7bf94ea58d6 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 @@ -31,10 +31,10 @@ 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.InvokeScriptTransaction.Payment +import com.wavesplatform.transaction.smart.{InvokeExpressionTransaction, InvokeScriptTransaction, WavesEnvironment} import com.wavesplatform.transaction.smart.{InvokeExpressionTransaction, InvokeScriptTransaction, WavesEnvironment, buildThisValue} import com.wavesplatform.transaction.{Asset, DataTransaction, Proofs, TxHelpers, TxVersion} import com.wavesplatform.utils.EmptyBlockchain -import monix.eval.Coeval import org.scalamock.scalatest.PathMockFactory import org.scalatest.EitherValues import play.api.libs.json.Json @@ -868,18 +868,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 = WavesEnvironment( - chainId, - Coeval(???), null, - EmptyBlockchain, Coproduct[Environment.Tthis](Environment.AssetId(Array())), + ByteStr.empty, directives, - ByteStr.empty + EmptyBlockchain ) for { compileResult <- compiler.ExpressionCompiler(ctx.compilerContext, V3, expr) @@ -899,18 +897,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 = WavesEnvironment( - chainId, - Coeval(buildThisValue(t, blockchain, directives, Coproduct[Environment.Tthis](Environment.AssetId(Array()))).explicitGet()), 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 a454344ab20..40474662bb6 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), version, 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 9c0808d152b..edd2a584768 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 @@ -16,7 +16,6 @@ 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.* @@ -116,8 +115,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) } @@ -178,8 +177,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 05e1d718a45..6bf4e874d20 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), V1, 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 = 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 1538621551e..ba9939b057d 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/TxHelpers.scala b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala index 580f611af20..5fe8976db73 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala @@ -29,11 +29,14 @@ import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTran import com.wavesplatform.transaction.transfer.{MassTransferTransaction, TransferTransaction} import com.wavesplatform.transaction.utils.EthConverters.* import com.wavesplatform.transaction.utils.Signed +import monix.execution.atomic.AtomicInt import monix.execution.atomic.AtomicLong import org.web3j.crypto.ECKeyPair object TxHelpers { + private[this] val accountCounter = AtomicInt(0) def signer(i: Int): SeedKeyPair = KeyPair(Ints.toByteArray(i)) + def signer(): SeedKeyPair = KeyPair(Ints.toByteArray(accountCounter.getAndIncrement())) def address(i: Int): Address = signer(i).toAddress val defaultSigner: SeedKeyPair = signer(0) 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 e8e1a4ca84e..835462aad63 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 = 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 86a85bb7a48..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 = 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 1ece24a85df..6852d3ffac9 100644 --- a/node/src/test/scala/com/wavesplatform/utils/UtilsSpecification.scala +++ b/node/src/test/scala/com/wavesplatform/utils/UtilsSpecification.scala @@ -4,28 +4,25 @@ 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 = 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[Id]( environment, typeDefs = Map.empty, letDefs = Map.empty, - functions = Seq(caller, callee) + functions = Map(caller.header -> caller, callee.header -> callee) ) estimate(V3, ctx).size shouldBe 2 } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index ac3e5a07c86..7d83a0d3588 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -101,6 +101,8 @@ object Dependencies { private val scalapbJson = "com.thesamet.scalapb" %% "scalapb-json4s" % "0.12.1" + val scalaLogging: ModuleID = "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5" + lazy val node = Def.setting( Seq( rocksdb, @@ -125,7 +127,7 @@ object Dependencies { kindProjector, monixModule("reactive").value, nettyHandler, - "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5", + scalaLogging, "eu.timepit" %% "refined" % "0.11.0" exclude ("org.scala-lang.modules", "scala-xml_2.13"), "com.esaulpaugh" % "headlong" % "10.0.1", "org.ehcache" % "sizeof" % "0.4.3", // Weighing caches diff --git a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/Repl.scala b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/Repl.scala index f5a3e003909..b706cdefc2f 100644 --- a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/Repl.scala +++ b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/Repl.scala @@ -1,32 +1,29 @@ package com.wavesplatform.lang.v1.repl -import cats.arrow.FunctionK import cats.implicits.* - -import scala.concurrent.Future -import cats.{Functor, Id, Monoid} -import com.wavesplatform.lang.v1.CTX +import cats.{Functor, Id} import com.wavesplatform.lang.v1.compiler.CompilerContext import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext import com.wavesplatform.lang.v1.repl.node.ErrorMessageEnvironment -import com.wavesplatform.lang.v1.repl.node.http.{NodeClient, NodeConnectionSettings} import com.wavesplatform.lang.v1.repl.node.http.WebEnvironment.executionContext -import com.wavesplatform.lang.v1.traits.Environment +import com.wavesplatform.lang.v1.repl.node.http.{NodeClient, NodeConnectionSettings} import monix.execution.atomic.Atomic +import scala.concurrent.Future + case class Repl( settings: Option[NodeConnectionSettings] = None, customHttpClient: Option[NodeClient] = None, libraries: List[String] = Nil, - lastContext: (CompilerContext, EvaluationContext[Environment, Future]) = - (CTX.empty.compilerContext, Monoid[EvaluationContext[Environment, Future]].empty) + lastContext: (CompilerContext, EvaluationContext[Future]) = ??? +// (CTX.empty.compilerContext, Monoid[EvaluationContext[Future]].empty) ) { - private val environment = buildEnvironment(settings, customHttpClient) - private val initialState = state( - ( +// private val environment = buildEnvironment(settings, customHttpClient) + private val initialState: ((CompilerContext, EvaluationContext[scala.concurrent.Future]), StateView) = state( + ??? /*( lastContext._1 |+| initialCtx.compilerContext, lastContext._2 |+| initialCtx.evaluationContext(environment) - ), + )*/, view ) private val currentState = Atomic(initialState) @@ -50,11 +47,11 @@ case class Repl( perform( currentState, view, - (oldCtx: (CompilerContext, EvaluationContext[Environment, Future])) => + (oldCtx: (CompilerContext, EvaluationContext[Future])) => engine.eval(expr, version, oldCtx._1, oldCtx._2).map { case Left(e) => (Left(e), oldCtx) case Right((r, newCtx)) => (Right(r), newCtx) - }: Future[(Either[String, String], (CompilerContext, EvaluationContext[Environment, Future]))] + }: Future[(Either[String, String], (CompilerContext, EvaluationContext[Future]))] ) private def initLibraries(): Unit = { @@ -67,7 +64,7 @@ case class Repl( ), view ) - perform[Id, (CompilerContext, EvaluationContext[Environment, Id]), Either[String, String], StateView]( + perform[Id, (CompilerContext, EvaluationContext[Id]), Either[String, String], StateView]( Atomic(libraryState), view, oldCtx => @@ -76,7 +73,7 @@ case class Repl( .fold( e => throw new RuntimeException(e), { case (r, ctx @ (compilerCtx, evaluationCtx)) => - val mappedCtx = evaluationCtx.mapK(λ[FunctionK[Id, Future]](Future.successful(_))) |+| initialCtx.evaluationContext(environment) + val mappedCtx = ???//evaluationCtx.mapK(λ[FunctionK[Id, Future]](Future.successful(_))) |+| initialCtx.evaluationContext(environment) currentState.set(state((compilerCtx, mappedCtx), view)) (Right(r), ctx) } diff --git a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/ReplEngine.scala b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/ReplEngine.scala index 927df4b029b..58759e151b1 100644 --- a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/ReplEngine.scala +++ b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/ReplEngine.scala @@ -14,17 +14,16 @@ import com.wavesplatform.lang.v1.parser.Expressions.EXPR import com.wavesplatform.lang.v1.parser.Expressions.Pos.AnyPos import com.wavesplatform.lang.v1.parser.Parser import com.wavesplatform.lang.v1.repl.Implicits.* -import com.wavesplatform.lang.v1.traits.Environment class ReplEngine[F[_]: Monad] { - val evaluator = new EvaluatorV1[F, Environment] + val evaluator = new EvaluatorV1[F] def eval( expr: String, version: StdLibVersion, compileCtx: CompilerContext, - evalCtx: EvaluationContext[Environment, F] - ): F[Either[String, (String, (CompilerContext, EvaluationContext[Environment, F]))]] = { + evalCtx: EvaluationContext[F] + ): F[Either[String, (String, (CompilerContext, EvaluationContext[F]))]] = { val r = for { parsed <- EitherT.fromEither[F](parse(expr)) @@ -46,7 +45,7 @@ class ReplEngine[F[_]: Monad] { ) private def resultWithCtx( - evaluated: (EvaluationContext[Environment, F], EVALUATED), + evaluated: (EvaluationContext[F], EVALUATED), compileCtx: CompilerContext, newCompileCtx: CompilerContext, exprType: FINAL @@ -127,8 +126,8 @@ class ReplEngine[F[_]: Monad] { private def addResultToCtx( result: (String, FINAL, EVALUATED), compileCtx: CompilerContext, - evalCtx: EvaluationContext[Environment, F] - ): (CompilerContext, EvaluationContext[Environment, F]) = { + evalCtx: EvaluationContext[F] + ): (CompilerContext, EvaluationContext[F]) = { val (name, t, value) = result ( compileCtx.copy(varDefs = compileCtx.varDefs + (name -> VariableInfo(AnyPos, t))), diff --git a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/package.scala b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/package.scala index 54ba8356222..784e1d1dd83 100644 --- a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/package.scala +++ b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/package.scala @@ -20,9 +20,9 @@ package object repl { val version = StdLibVersion.VersionDic.latest val directives: DirectiveSet = DirectiveSet(version, Account, DApp).explicitGet() - val initialCtx: CTX[Environment] = - CryptoContext.build(global, version).withEnvironment[Environment] |+| - PureContext.build(version, useNewPowPrecision = true).withEnvironment[Environment] |+| + val initialCtx: CTX = + CryptoContext.build(global, version) |+| + PureContext.build(version, useNewPowPrecision = true) |+| WavesContext.build(global, directives, fixBigScriptField = true) def buildEnvironment(settings: Option[NodeConnectionSettings], customHttpClient: Option[NodeClient]): Environment[Future] =