From f4c9fe593a40663e2c2f5b73c25f4aa0af62f1cd Mon Sep 17 00:00:00 2001 From: Le Karasique Date: Mon, 14 Dec 2020 19:59:07 +0300 Subject: [PATCH] NODE-2273: /utils/script/evaluate API route (#3305) --- .../com/wavesplatform/lang/JavaAdapter.scala | 2 +- .../wavesplatform/lang/v1/BaseGlobal.scala | 5 +- .../lang/v1/compiler/CompilerContext.scala | 33 +++- .../lang/v1/compiler/ExpressionCompiler.scala | 121 +++++++------- .../lang/v1/evaluator/ContractEvaluator.scala | 26 ++- .../lang/ContractIntegrationTest.scala | 1 - .../lang/EvaluatorV1V2Test.scala | 4 +- .../wavesplatform/lang/IntegrationTest.scala | 2 +- node/src/main/resources/application.conf | 3 + .../main/resources/swagger-ui/swagger.json | 149 +++++++++++++----- .../api/http/UtilsApiRoute.scala | 119 +++++++++++++- .../com/wavesplatform/api/http/package.scala | 14 +- .../wavesplatform/http/BroadcastRoute.scala | 8 +- .../settings/RestAPISettings.scala | 21 +-- .../invoke/InvokeScriptTransactionDiff.scala | 1 - .../smart/script/ScriptCompiler.scala | 2 +- .../wavesplatform/http/UtilsRouteSpec.scala | 121 +++++++++++++- .../RestAPISettingsSpecification.scala | 2 + .../diffs/SetScriptTransactionDiffTest.scala | 13 +- .../utx/UtxPoolSpecification.scala | 2 +- 20 files changed, 493 insertions(+), 156 deletions(-) diff --git a/lang/jvm/src/main/scala/com/wavesplatform/lang/JavaAdapter.scala b/lang/jvm/src/main/scala/com/wavesplatform/lang/JavaAdapter.scala index f197daf0a23..0a5335f9e06 100644 --- a/lang/jvm/src/main/scala/com/wavesplatform/lang/JavaAdapter.scala +++ b/lang/jvm/src/main/scala/com/wavesplatform/lang/JavaAdapter.scala @@ -20,7 +20,7 @@ object JavaAdapter { def compile(input: String): EXPR = { ExpressionCompiler - .compile(input, ctx) + .compileBoolean(input, ctx) .fold( error => throw new IllegalArgumentException(error), res => res diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/BaseGlobal.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/BaseGlobal.scala index 7433215be76..b1b75c7b077 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/BaseGlobal.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/BaseGlobal.scala @@ -11,8 +11,7 @@ import com.wavesplatform.lang.script.ContractScript.ContractScriptImpl import com.wavesplatform.lang.script.v1.ExprScript import com.wavesplatform.lang.script.{ContractScript, Script} import com.wavesplatform.lang.utils -import com.wavesplatform.lang.v1.BaseGlobal.DAppInfo -import com.wavesplatform.lang.v1.BaseGlobal.ArrayView +import com.wavesplatform.lang.v1.BaseGlobal.{ArrayView, DAppInfo} import com.wavesplatform.lang.v1.compiler.CompilationError.Generic import com.wavesplatform.lang.v1.compiler.Terms.EXPR import com.wavesplatform.lang.v1.compiler.Types.FINAL @@ -133,7 +132,7 @@ trait BaseGlobal { } val compileExpression = - compile(_, _, _, _, ExpressionCompiler.compile) + compile(_, _, _, _, ExpressionCompiler.compileBoolean) val compileDecls = compile(_, _, _, _, ExpressionCompiler.compileDecls) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilerContext.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilerContext.scala index d0305128cde..ac3afcf2437 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilerContext.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilerContext.scala @@ -3,22 +3,47 @@ package com.wavesplatform.lang.v1.compiler import cats.Monoid import com.wavesplatform.lang.v1.FunctionHeader import com.wavesplatform.lang.v1.compiler.CompilerContext._ -import com.wavesplatform.lang.v1.compiler.Types.{CASETYPEREF, FINAL} +import com.wavesplatform.lang.v1.compiler.Types._ import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext import com.wavesplatform.lang.v1.evaluator.ctx.{BaseFunction, FunctionTypeSignature} import com.wavesplatform.lang.v1.parser.Expressions.Pos import com.wavesplatform.lang.v1.parser.Expressions.Pos.AnyPos import shapeless._ -case class CompilerContext(predefTypes: Map[String, FINAL], varDefs: VariableTypes, functionDefs: FunctionTypes, tmpArgsIdx: Int = 0) { +case class CompilerContext( + predefTypes: Map[String, FINAL], + varDefs: VariableTypes, + functionDefs: FunctionTypes, + tmpArgsIdx: Int = 0, + arbitraryFunctions: Boolean = false +) { private lazy val allFuncDefs: FunctionTypes = predefTypes.collect { - case (_, t @ CASETYPEREF(typeName, fields, false)) => + case (_, CASETYPEREF(typeName, fields, false)) => typeName -> FunctionInfo(AnyPos, List(FunctionTypeSignature(CASETYPEREF(typeName, fields), fields, FunctionHeader.User(typeName)))) } ++ functionDefs - def functionTypeSignaturesByName(name: String): List[FunctionTypeSignature] = allFuncDefs.getOrElse(name, FunctionInfo(AnyPos, List.empty)).fSigList + private def resolveFunction(name: String): FunctionInfo = + if (arbitraryFunctions) { + val primitives = List(LONG, BYTESTR, BOOLEAN, STRING) + val maybeAllTypes = UNION( + UNION(primitives), + UNION.create(predefTypes.values.toSeq), + LIST(UNION(primitives)) + ) + def signature(name: String, i: Int) = { + FunctionTypeSignature(maybeAllTypes, Seq.fill(i)(("arg", maybeAllTypes)), FunctionHeader.User(name)) + } + allFuncDefs + .withDefault(name => FunctionInfo(AnyPos, (0 to 22).map(i => signature(name, i)).toList)) + .apply(name) + } else { + allFuncDefs.getOrElse(name, FunctionInfo(AnyPos, List.empty)) + } + + def functionTypeSignaturesByName(name: String): List[FunctionTypeSignature] = + resolveFunction(name).fSigList def getSimpleContext(): Map[String, Pos] = { (varDefs.map(el => el._1 -> el._2.pos) ++ functionDefs.map(el => el._1 -> el._2.pos)) 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 1ebe00ccb19..005bbd272af 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 @@ -38,28 +38,23 @@ object ExpressionCompiler { errors: Iterable[CompilationError] = Iterable.empty ) - def compile(input: String, ctx: CompilerContext): Either[String, EXPR] = { + def compile(input: String, ctx: CompilerContext): Either[String, (EXPR, FINAL)] = { Parser.parseExpr(input) match { - case fastparse.Parsed.Success(xs, _) => - ExpressionCompiler(ctx, xs) match { - case Left(err) => Left(err.toString) - case Right((expr, BOOLEAN)) => Right(expr) - case Right((_, _)) => Left("Script should return boolean") - } + case fastparse.Parsed.Success(xs, _) => ExpressionCompiler(ctx, xs) case f @ fastparse.Parsed.Failure(_, _, _) => Left(f.toString) } } + def compileBoolean(input: String, ctx: CompilerContext): Either[String, EXPR] = { + compile(input, ctx).flatMap { + case (expr, BOOLEAN) => Right(expr) + case (_, _) => Left("Script should return boolean") + } + } def compileUntyped(input: String, ctx: CompilerContext): Either[String, EXPR] = { - Parser.parseExpr(input) match { - case fastparse.Parsed.Success(xs, _) => - ExpressionCompiler(ctx, xs) match { - case Left(err) => Left(err.toString) - case Right((expr, _)) => Right(expr) - } - case f @ fastparse.Parsed.Failure(_, _, _) => Left(f.toString) - } + compile(input, ctx) + .map { case (expr, _) => expr } } def compileWithParseResult( @@ -98,10 +93,7 @@ object ExpressionCompiler { def compileDecls(input: String, ctx: CompilerContext): Either[String, EXPR] = { val adjustedDecls = s"$input\n${PureContext.unitVarName}" - Parser.parseExpr(adjustedDecls) match { - case fastparse.Parsed.Success(xs, _) => ExpressionCompiler(ctx, xs).map(_._1) - case f @ fastparse.Parsed.Failure(_, _, _) => Left(f.toString) - } + compileUntyped(adjustedDecls, ctx) } def compileExpr(expr: Expressions.EXPR): CompileM[(Terms.EXPR, FINAL, Expressions.EXPR)] = @@ -224,26 +216,30 @@ object ExpressionCompiler { ): CompileM[CompilationStepResultExpr] = for { ctx <- get[Id, CompilerContext, CompilationError] - _ <- { + _ <- { val types = ctx.predefTypes.keySet val typeNamedCases = cases.collect { case MATCH_CASE(_, Some(PART.VALID(_, name)), _, _, _, _) if types.contains(name) => name } - Either.cond( - typeNamedCases.isEmpty, - (), - TypeNamedCases(p.start, p.end, typeNamedCases) - ).toCompileM + Either + .cond( + typeNamedCases.isEmpty, + (), + TypeNamedCases(p.start, p.end, typeNamedCases) + ) + .toCompileM } - _ <- { + _ <- { val defaultCasesCount = cases.count(_.caseType.isEmpty) - Either.cond( - defaultCasesCount < 2, - (), - MultipleDefaultCases(p.start, p.end, defaultCasesCount) - ).toCompileM + Either + .cond( + defaultCasesCount < 2, + (), + MultipleDefaultCases(p.start, p.end, defaultCasesCount) + ) + .toCompileM } typedExpr <- compileExprWithCtx(expr, saveExprContext) exprTypesWithErr <- (typedExpr.t match { @@ -295,14 +291,14 @@ object ExpressionCompiler { (matchTypes, exprTypes) } matchedTypesUnion = UNION.create(checktypes._1) - checkWithErr <- - Either.cond( - (cases.last.caseType.isEmpty && (checktypes._2 >= matchedTypesUnion)) || (checktypes._2 equivalent matchedTypesUnion), - (), - MatchNotExhaustive(p.start, p.end, exprTypes.typeList, matchTypes) - ) - .toCompileM - .handleError() + checkWithErr <- Either + .cond( + (cases.last.caseType.isEmpty && (checktypes._2 >= matchedTypesUnion)) || (checktypes._2 equivalent matchedTypesUnion), + (), + MatchNotExhaustive(p.start, p.end, exprTypes.typeList, matchTypes) + ) + .toCompileM + .handleError() _ <- set[Id, CompilerContext, CompilationError](ctx.copy(tmpArgsIdx = tmpArgId)) errorList = exprTypesWithErr._2 ++ ifCasesWithErr._2 ++ compiledMatch.errors ++ checkWithErr._2 @@ -329,7 +325,7 @@ object ExpressionCompiler { val refIsOverlappedByDecl = decl.name match { case PART.VALID(_, name) if name == ref => true - case _ => false + case _ => false } if (refIsOverlappedByDecl) false else { @@ -341,7 +337,7 @@ object ExpressionCompiler { val refIsOverlappedByArg = args.exists { case (PART.VALID(_, name), _) if name == ref => true - case _ => false + case _ => false } if (!refIsOverlappedByArg) exprContainsRef(expr, ref) else false @@ -350,9 +346,9 @@ object ExpressionCompiler { } case Expressions.IF(_, cond, ifTrue, ifFalse, _, _) => - exprContainsRef(cond, ref) || - exprContainsRef(ifTrue, ref) || - exprContainsRef(ifFalse, ref) + exprContainsRef(cond, ref) || + exprContainsRef(ifTrue, ref) || + exprContainsRef(ifFalse, ref) case Expressions.FUNCTION_CALL(_, _, args, _, _) => args.exists(exprContainsRef(_, ref)) @@ -365,13 +361,13 @@ object ExpressionCompiler { case Expressions.MATCH(_, matchingExpr, cases, _, _) => exprContainsRef(matchingExpr, ref) || - cases.exists { - case MATCH_CASE(_, Some(PART.VALID(_, varName)), _, caseExpr, _, _) if varName != ref => - exprContainsRef(caseExpr, ref) - case MATCH_CASE(_, None, _, caseExpr, _, _) => - exprContainsRef(caseExpr, ref) - case _ => false - } + cases.exists { + case MATCH_CASE(_, Some(PART.VALID(_, varName)), _, caseExpr, _, _) if varName != ref => + exprContainsRef(caseExpr, ref) + case MATCH_CASE(_, None, _, caseExpr, _, _) => + exprContainsRef(caseExpr, ref) + case _ => false + } case _ => false } @@ -412,7 +408,7 @@ object ExpressionCompiler { compiledLet <- compileExprWithCtx(let.value, saveExprContext) ctx <- get[Id, CompilerContext, CompilationError] - letType = let.types.getOrElse(compiledLet.t) + letType = let.types.getOrElse(compiledLet.t) errorList = letNameWithErr._2 parseNodeDecl = let.copy(value = compiledLet.parseNodeExpr) @@ -444,7 +440,7 @@ object ExpressionCompiler { .traverse { case (argName, argType) => for { - name <- handlePart(argName) + name <- handlePart(argName) handledType <- handleCompositeType(p, argType, None, Some(name)) } yield (name, VariableInfo(argName.position, handledType)) } @@ -754,25 +750,26 @@ object ExpressionCompiler { } private def handleCompositeType( - pos: Pos, - t: Expressions.Type, - expectedType: Option[FINAL], - varName: Option[String] + pos: Pos, + t: Expressions.Type, + expectedType: Option[FINAL], + varName: Option[String] ): CompileM[FINAL] = t match { case Expressions.Single(name, parameter) => for { - ctx <- get[Id, CompilerContext, CompilationError] - handledName <- handlePart(name) + ctx <- get[Id, CompilerContext, CompilationError] + handledName <- handlePart(name) handledParameter <- parameter.traverse(handlePart) expectedTypes = expectedType.fold(ctx.predefTypes.keys.toList)(_.typeList.map(_.name)) parameter <- handledParameter.traverse(handleCompositeType(pos, _, expectedType, varName)) - t <- liftEither[Id, CompilerContext, CompilationError, FINAL](parameter.fold(flatSingle(pos, ctx.predefTypes, expectedTypes, varName, handledName).map(v => UNION.reduce(UNION.create(v, None)))) { - p => + t <- liftEither[Id, CompilerContext, CompilationError, FINAL]( + parameter.fold(flatSingle(pos, ctx.predefTypes, expectedTypes, varName, handledName).map(v => UNION.reduce(UNION.create(v, None)))) { p => for { typeConstr <- findGenericType(pos, handledName) } yield typeConstr(p) - }) + } + ) } yield t case Expressions.Union(types) => types.toList 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 70fd21f7c41..b11c2250517 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 @@ -24,12 +24,29 @@ object ContractEvaluator { caller: Recipient.Address, callerPk: ByteStr, payments: AttachedPayments, - dappAddress: ByteStr, transactionId: ByteStr, fee: Long, feeAssetId: Option[ByteStr] ) + def buildSyntheticCall(contract: DApp, call: EXPR): EXPR = { + val callables = contract.callableFuncs.flatMap { cf => + val argName = cf.annotation.invocationArgName + val invocation = Invocation( + null, + Recipient.Address(ByteStr(new Array[Byte](26))), + ByteStr(new Array[Byte](32)), + AttachedPayments.Single(None), + ByteStr(new Array[Byte](32)), + 0L, + None + ) + LET(argName, Bindings.buildInvocation(invocation, StdLibVersion.VersionDic.default)) :: cf.u :: Nil + } + + foldDeclarations(contract.decs ++ callables, BLOCK(LET("__synthetic_call", TRUE), call)) + } + private def buildExprFromInvocation(c: DApp, i: Invocation, version: StdLibVersion): Either[String, EXPR] = { val functionName = i.funcCall.function.funcName @@ -118,7 +135,8 @@ object ContractEvaluator { case (buildingExpr, (letName, letValue)) => BLOCK(LET(letName, letValue.value.value.explicitGet()), buildingExpr) } - EvaluatorV2.applyLimited(exprWithLets, limit, ctx, version) + EvaluatorV2 + .applyLimited(exprWithLets, limit, ctx, version) .flatMap { case (expr, unusedComplexity, log) => val result = @@ -126,7 +144,7 @@ object ContractEvaluator { case value: EVALUATED => ScriptResult.fromObj(ctx, transactionId, value, version) case expr: EXPR => Right(IncompleteResult(expr, unusedComplexity)) } - result.bimap((_, log), (_, log)) + result.bimap((_, log), (_, log)) } - } + } } 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 c437bd56c78..a57abb08f9f 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/ContractIntegrationTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/ContractIntegrationTest.scala @@ -162,7 +162,6 @@ class ContractIntegrationTest extends PropSpec with PropertyChecks with ScriptGe Recipient.Address(callerAddress), callerPublicKey, AttachedPayments.Single(None), - ByteStr.empty, transactionId, fee, feeAssetId diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/EvaluatorV1V2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/EvaluatorV1V2Test.scala index 7aed24be584..173aec173c6 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/EvaluatorV1V2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/EvaluatorV1V2Test.scala @@ -880,7 +880,7 @@ class EvaluatorV1V2Test extends PropSpec with PropertyChecks with Matchers with evalWithLogging( context.evaluationContext(Common.emptyBlockchainEnvironment()), ExpressionCompiler - .compile(script, context.compilerContext) + .compileBoolean(script, context.compilerContext) .explicitGet() ).map { case (CONST_BOOLEAN(b), log) => (b, log) @@ -1011,7 +1011,7 @@ class EvaluatorV1V2Test extends PropSpec with PropertyChecks with Matchers with evalWithLogging( context.evaluationContext[Id](Common.emptyBlockchainEnvironment()), ExpressionCompiler - .compile(script, context.compilerContext) + .compileBoolean(script, context.compilerContext) .explicitGet() ).map { case (CONST_BOOLEAN(b), log) => (b, log) 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 ccfda270ef9..161663c62a6 100755 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala @@ -285,7 +285,7 @@ class IntegrationTest extends PropSpec with PropertyChecks with ScriptGen with M } def compile(script: String): Either[String, Terms.EXPR] = - ExpressionCompiler.compile(script, CTX.empty.compilerContext) + ExpressionCompiler.compileBoolean(script, CTX.empty.compilerContext) property("wrong script return type") { compile("1") should produce("should return boolean") diff --git a/node/src/main/resources/application.conf b/node/src/main/resources/application.conf index b54a7b39f01..890476c62a2 100644 --- a/node/src/main/resources/application.conf +++ b/node/src/main/resources/application.conf @@ -208,6 +208,9 @@ waves { transactions-by-address-limit = 1000 distribution-address-limit = 1000 + # The limit of complexity of a function that is not Callable (/utils/script/evaluate) + evaluate-script-complexity-limit = 4000 + # Max number of time-limited pool threads (script compile/decompile/estimate) limited-pool-threads = 2 } diff --git a/node/src/main/resources/swagger-ui/swagger.json b/node/src/main/resources/swagger-ui/swagger.json index 809716a244b..aad0dee2fcf 100644 --- a/node/src/main/resources/swagger-ui/swagger.json +++ b/node/src/main/resources/swagger-ui/swagger.json @@ -2726,6 +2726,73 @@ "x-codegen-request-body-name": "code" } }, + "/utils/script/evaluate/{address}": { + "post": { + "tags": [ + "utils" + ], + "summary": "Evaluate", + "description": "Evaluates the provided expression, taking into account the deployed dApp contract", + "operationId": "script_evaluate", + "parameters": [ + { + "name": "address", + "in": "path", + "description": "Address of the deployed dApp contract", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Expression to evaluate", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "expr": { + "type": "string" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "expr": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "value": { + } + } + } + } + } + } + } + } + } + } + }, "/utils/seed": { "get": { "tags": [ @@ -3075,7 +3142,7 @@ "operationId": "asset_details", "parameters": [ { - "$ref":"#/components/parameters/assetId" + "$ref": "#/components/parameters/assetId" }, { "name": "full", @@ -3162,7 +3229,7 @@ "$ref": "#/components/parameters/address" }, { - "$ref":"#/components/parameters/assetId" + "$ref": "#/components/parameters/assetId" } ], "responses": { @@ -3206,7 +3273,7 @@ "operationId": "balanceDistribution_1", "parameters": [ { - "$ref":"#/components/parameters/assetId" + "$ref": "#/components/parameters/assetId" } ], "responses": { @@ -3234,7 +3301,7 @@ "operationId": "balanceDistributionAtHeight_1", "parameters": [ { - "$ref":"#/components/parameters/assetId" + "$ref": "#/components/parameters/assetId" }, { "name": "height", @@ -3906,7 +3973,7 @@ "type": "string" } } - } + } } } } @@ -4805,10 +4872,10 @@ "format": "int64" }, "VRF": { - "type": "string" + "type": "string" }, "transactionsRoot": { - "type": "string" + "type": "string" } } }, @@ -4897,10 +4964,10 @@ "format": "int64" }, "VRF": { - "type": "string" + "type": "string" }, "transactionsRoot": { - "type": "string" + "type": "string" } } }, @@ -4990,7 +5057,9 @@ }, "type": { "type": "string", - "enum": ["binary"] + "enum": [ + "binary" + ] }, "value": { "type": "string", @@ -5011,7 +5080,9 @@ }, "type": { "type": "string", - "enum": ["boolean"] + "enum": [ + "boolean" + ] }, "value": { "type": "boolean" @@ -5075,7 +5146,9 @@ }, "type": { "type": "string", - "enum": ["integer"] + "enum": [ + "integer" + ] }, "value": { "type": "integer", @@ -5096,9 +5169,11 @@ }, "type": { "type": "string", - "enum": ["string"] + "enum": [ + "string" + ] }, - "value":{ + "value": { "type": "string" } } @@ -5751,7 +5826,7 @@ "proofs": { "type": "array", "items": { - "type": "string" + "type": "string" } }, "version": { @@ -5885,7 +5960,7 @@ "proofs": { "type": "array", "items": { - "type": "string" + "type": "string" } }, "version": { @@ -5951,7 +6026,7 @@ } }, "applicationStatus": { - "type": "string" + "type": "string" }, "height": { "type": "integer", @@ -6010,7 +6085,7 @@ "proofs": { "type": "array", "items": { - "type": "string" + "type": "string" } }, "version": { @@ -6099,7 +6174,7 @@ "proofs": { "type": "array", "items": { - "type": "string" + "type": "string" } }, "version": { @@ -6121,7 +6196,7 @@ }, "height": { "type": "integer", - "format": "int32" + "format": "int32" } } }, @@ -6248,7 +6323,7 @@ "proofs": { "type": "array", "items": { - "type": "string" + "type": "string" } }, "version": { @@ -6276,19 +6351,19 @@ }, "transfers": { "type": "array", - "items": { - "type": "object", - "properties": { - "recipient": { - "type": "string" - }, - "amount": { - "type": "integer", - "format": "int64" - } + "items": { + "type": "object", + "properties": { + "recipient": { + "type": "string" + }, + "amount": { + "type": "integer", + "format": "int64" } } - }, + } + }, "applicationStatus": { "type": "string" }, @@ -6345,7 +6420,7 @@ "proofs": { "type": "array", "items": { - "type": "string" + "type": "string" } }, "version": { @@ -6421,7 +6496,7 @@ "proofs": { "type": "array", "items": { - "type": "string" + "type": "string" } }, "version": { @@ -6636,7 +6711,7 @@ "proofs": { "type": "array", "items": { - "type": "string" + "type": "string" } }, "version": { @@ -6671,7 +6746,7 @@ "type": "integer", "format": "int32" } - } + } }, "UpdateAssetInfoTransaction": { "type": "object", @@ -6720,7 +6795,7 @@ "proofs": { "type": "array", "items": { - "type": "string" + "type": "string" } }, "version": { diff --git a/node/src/main/scala/com/wavesplatform/api/http/UtilsApiRoute.scala b/node/src/main/scala/com/wavesplatform/api/http/UtilsApiRoute.scala index 5a21be8bdd5..ed43abcc283 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/UtilsApiRoute.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/UtilsApiRoute.scala @@ -2,23 +2,39 @@ package com.wavesplatform.api.http import java.security.SecureRandom -import akka.http.scaladsl.server.Route -import com.wavesplatform.api.http.ApiError.{ScriptCompilerError, TooBigArrayAllocation} -import com.wavesplatform.api.http.requests.ScriptWithImportsRequest +import akka.http.scaladsl.server.{PathMatcher1, Route} +import cats.implicits._ +import com.wavesplatform.account.{Address, AddressScheme} +import com.wavesplatform.api.http.ApiError.{CustomValidationError, ScriptCompilerError, TooBigArrayAllocation} +import com.wavesplatform.api.http.requests.{ScriptWithImportsRequest, byteStrFormat} +import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils._ import com.wavesplatform.crypto import com.wavesplatform.features.BlockchainFeatures +import com.wavesplatform.lang.ValidationError +import com.wavesplatform.lang.contract.DApp import com.wavesplatform.lang.directives.values._ import com.wavesplatform.lang.script.Script import com.wavesplatform.lang.script.Script.ComplexityInfo +import com.wavesplatform.lang.v1.Serde +import com.wavesplatform.lang.v1.compiler.Terms.{EVALUATED, EXPR} +import com.wavesplatform.lang.v1.compiler.{ExpressionCompiler, Terms} import com.wavesplatform.lang.v1.estimator.ScriptEstimator +import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext +import com.wavesplatform.lang.v1.evaluator.{ContractEvaluator, EvaluatorV2} +import com.wavesplatform.lang.v1.traits.Environment +import com.wavesplatform.lang.v1.traits.domain.Recipient import com.wavesplatform.settings.RestAPISettings import com.wavesplatform.state.Blockchain import com.wavesplatform.state.diffs.FeeValidation +import com.wavesplatform.transaction.TxValidationError.{GenericError, ScriptExecutionError} +import com.wavesplatform.transaction.smart.BlockchainContext import com.wavesplatform.transaction.smart.script.ScriptCompiler import com.wavesplatform.utils.Time +import monix.eval.Coeval import monix.execution.Scheduler import play.api.libs.json._ +import shapeless.Coproduct case class UtilsApiRoute( timeService: Time, @@ -39,7 +55,7 @@ case class UtilsApiRoute( } override val route: Route = pathPrefix("utils") { - decompile ~ compile ~ compileCode ~ compileWithImports ~ estimate ~ time ~ seedRoute ~ length ~ hashFast ~ hashSecure ~ transactionSerialize + decompile ~ compile ~ compileCode ~ compileWithImports ~ estimate ~ time ~ seedRoute ~ length ~ hashFast ~ hashSecure ~ transactionSerialize ~ evaluate } def decompile: Route = path("script" / "decompile") { @@ -200,9 +216,104 @@ case class UtilsApiRoute( path("transactionSerialize")(jsonPost[JsObject] { jsv => parseOrCreateTransaction(jsv)(tx => Json.obj("bytes" -> tx.bodyBytes().map(_.toInt & 0xff))) }) + + def evaluate: Route = + (path("script" / "evaluate" / ScriptedAddress) & jsonPostD[JsObject]) { (address: Address, obj: JsObject) => + val script = blockchain.accountScript(address).get.script + + def serializeResult(e: EVALUATED): JsValue = e match { // TODO: Tuple? + case Terms.CONST_LONG(num) => Json.obj("type" -> "Int", "value" -> num) + case Terms.CONST_BYTESTR(bs) => Json.obj("type" -> "ByteVector", "value" -> bs.toString) + case Terms.CONST_STRING(str) => Json.obj("type" -> "String", "value" -> str) + case Terms.CONST_BOOLEAN(b) => Json.obj("type" -> "Boolean", "value" -> b) + case Terms.CaseObj(caseType, fields) => + Json.obj("type" -> caseType.name, "value" -> JsObject(fields.view.mapValues(serializeResult).toSeq)) + case Terms.ARR(xs) => Json.obj("type" -> "Array", "value" -> xs.map(serializeResult)) + case Terms.FAIL(reason) => Json.obj("error" -> ApiError.ScriptExecutionError.Id, "error" -> reason) + } + + def parseCall(js: JsReadable) = { + val binaryCall = js + .asOpt[ByteStr] + .toRight(GenericError("Unable to parse expr bytes")) + .flatMap(ScriptCallEvaluator.parseBinaryCall) + + val textCall = js + .asOpt[String] + .toRight(GenericError("Unable to read expr string")) + .flatMap(ScriptCallEvaluator.compile(script.stdLibVersion)) + + binaryCall.orElse(textCall) + } + + val result = + for { + expr <- parseCall(obj \ "expr") + result <- ScriptCallEvaluator.executeExpression(blockchain, script, address, settings.evaluateScriptComplexityLimit)(expr) + } yield result + + val requestData = obj ++ Json.obj("address" -> address.stringRepr) + val responseJson = result + .map(r => Json.obj("result" -> serializeResult(r))) + .recover { + case e: ScriptExecutionError => Json.obj("error" -> ApiError.ScriptExecutionError.Id, "message" -> e.error) + case other => ApiError.fromValidationError(other).json + } + .explicitGet() ++ requestData + + complete(responseJson) + } + + private[this] val ScriptedAddress: PathMatcher1[Address] = AddrSegment.map { address => + if (blockchain.hasAccountScript(address)) address + else throw ApiException(CustomValidationError(s"Address $address is not dApp")) + } } object UtilsApiRoute { val MaxSeedSize = 1024 val DefaultSeedSize = 32 + + private object ScriptCallEvaluator { + def compile(stdLibVersion: StdLibVersion)(str: String): Either[GenericError, EXPR] = { + val ctx = PureContext.build(stdLibVersion).compilerContext.copy(arbitraryFunctions = true) + ExpressionCompiler.compileUntyped(str, ctx).leftMap(GenericError(_)) + } + + def parseBinaryCall(bs: ByteStr): Either[ValidationError, EXPR] = { + Serde + .deserialize(bs.arr) + .left + .map(GenericError(_)) + .map(_._1) + } + + def executeExpression(blockchain: Blockchain, script: Script, address: Address, limit: Int)(expr: EXPR): Either[ValidationError, EVALUATED] = { + for { + ctx <- BlockchainContext + .build( + script.stdLibVersion, + AddressScheme.current.chainId, + Coeval.raiseError(new IllegalStateException("No input entity available")), + Coeval.evalOnce(blockchain.height), + blockchain, + isTokenContext = false, + isContract = true, + Coproduct[Environment.Tthis](Recipient.Address(ByteStr(address.bytes))), + ByteStr.empty + ) + .left + .map(GenericError(_)) + + call = ContractEvaluator.buildSyntheticCall(script.expr.asInstanceOf[DApp], expr) + limitedResult <- EvaluatorV2 + .applyLimited(call, limit, ctx, script.stdLibVersion) + .leftMap { case (err, log) => ScriptExecutionError.dAppExecution(err, log) } + result <- limitedResult match { + case (eval: EVALUATED, _, _) => Right(eval) + case (_: EXPR, _, log) => Left(ScriptExecutionError.dAppExecution(s"Calculation complexity limit exceeded", log)) + } + } yield result + } + } } diff --git a/node/src/main/scala/com/wavesplatform/api/http/package.scala b/node/src/main/scala/com/wavesplatform/api/http/package.scala index 20a4988b317..10ff077376d 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/package.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/package.scala @@ -145,16 +145,10 @@ package object http extends ApiMarshallers with ScorexLogging { } def jsonPost[A: Reads](f: A => ToResponseMarshallable): Route = - jsonPostD[A](a => provide(f(a))) - - def jsonPostD[A: Reads](f: A => Directive1[ToResponseMarshallable]): Route = - post((handleExceptions(jsonExceptionHandler) & handleRejections(jsonRejectionHandler)) { - entity(as[A]) { a => - f(a) { result => - complete(result) - } - } - }) ~ get(complete(StatusCodes.MethodNotAllowed)) + jsonPostD[A].apply(obj => complete(f(obj))) ~ get(complete(StatusCodes.MethodNotAllowed)) + + def jsonPostD[A: Reads]: Directive1[A] = + (post & handleExceptions(jsonExceptionHandler) & handleRejections(jsonRejectionHandler) & entity(as[A])) def extractScheduler: Directive1[Scheduler] = extractExecutionContext.map(ec => Scheduler(ec)) diff --git a/node/src/main/scala/com/wavesplatform/http/BroadcastRoute.scala b/node/src/main/scala/com/wavesplatform/http/BroadcastRoute.scala index d22b9cd5fc9..1680f52fc02 100644 --- a/node/src/main/scala/com/wavesplatform/http/BroadcastRoute.scala +++ b/node/src/main/scala/com/wavesplatform/http/BroadcastRoute.scala @@ -26,11 +26,13 @@ trait BroadcastRoute { _: ApiRoute => provide(broadcastTransaction(tx, includeTrace)) } - def broadcast[A: Reads](f: A => Either[ValidationError, Transaction]): Route = - jsonPostD[A] { a => + def broadcast[A: Reads](f: A => Either[ValidationError, Transaction]): Route = { + val directive = jsonPostD[A].flatMap { a => f(a).fold( - e => provide(ApiError.fromValidationError(e)), + e => provide[ToResponseMarshallable](ApiError.fromValidationError(e)), extractTraceParameter ) } + directive(complete(_)) + } } diff --git a/node/src/main/scala/com/wavesplatform/settings/RestAPISettings.scala b/node/src/main/scala/com/wavesplatform/settings/RestAPISettings.scala index b0b5f20c7eb..003df5e08cc 100644 --- a/node/src/main/scala/com/wavesplatform/settings/RestAPISettings.scala +++ b/node/src/main/scala/com/wavesplatform/settings/RestAPISettings.scala @@ -1,11 +1,14 @@ package com.wavesplatform.settings -case class RestAPISettings(enable: Boolean, - bindAddress: String, - port: Int, - apiKeyHash: String, - cors: Boolean, - apiKeyDifferentHost: Boolean, - transactionsByAddressLimit: Int, - distributionAddressLimit: Int, - limitedPoolThreads: Int) +case class RestAPISettings( + enable: Boolean, + bindAddress: String, + port: Int, + apiKeyHash: String, + cors: Boolean, + apiKeyDifferentHost: Boolean, + transactionsByAddressLimit: Int, + distributionAddressLimit: Int, + evaluateScriptComplexityLimit: Int, + limitedPoolThreads: Int +) diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptTransactionDiff.scala b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeScriptTransactionDiff.scala index f7c0ae3fbee..90478ba7c8c 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 @@ -94,7 +94,6 @@ object InvokeScriptTransactionDiff { Recipient.Address(ByteStr(invoker.bytes)), tx.sender, payments, - ByteStr(tx.dAppAddressOrAlias.bytes), tx.id(), tx.fee, tx.feeAssetId.compatId diff --git a/node/src/main/scala/com/wavesplatform/transaction/smart/script/ScriptCompiler.scala b/node/src/main/scala/com/wavesplatform/transaction/smart/script/ScriptCompiler.scala index fe15d87c56a..0505e3d67e8 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/smart/script/ScriptCompiler.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/smart/script/ScriptCompiler.scala @@ -70,7 +70,7 @@ object ScriptCompiler extends ScorexLogging { val ctx = compilerContext(version, cType, isAssetScript) try { cType match { - case Expression => ExpressionCompiler.compile(src, ctx).flatMap(expr => ExprScript.apply(version, expr)) + case Expression => ExpressionCompiler.compileBoolean(src, ctx).flatMap(expr => ExprScript.apply(version, expr)) case DApp => ContractCompiler.compile(src, ctx, version).flatMap(expr => ContractScript.apply(version, expr)) case Library => ExpressionCompiler.compileDecls(src, ctx).flatMap(ExprScript(version, _)) } diff --git a/node/src/test/scala/com/wavesplatform/http/UtilsRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/UtilsRouteSpec.scala index b44e731e2b9..4c3c5c74885 100644 --- a/node/src/test/scala/com/wavesplatform/http/UtilsRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/UtilsRouteSpec.scala @@ -3,6 +3,7 @@ package com.wavesplatform.http import akka.http.scaladsl.testkit.RouteTestTimeout import cats.implicits._ import com.google.protobuf.ByteString +import com.wavesplatform.account.PublicKey import com.wavesplatform.api.http.ApiError.TooBigArrayAllocation import com.wavesplatform.api.http.UtilsApiRoute import com.wavesplatform.api.http.requests.ScriptWithImportsRequest @@ -23,17 +24,19 @@ 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.traits.Environment +import com.wavesplatform.lang.v1.{FunctionHeader, Serde} import com.wavesplatform.protobuf.dapp.DAppMeta import com.wavesplatform.protobuf.dapp.DAppMeta.CallableFuncSignature -import com.wavesplatform.state.Blockchain import com.wavesplatform.state.diffs.FeeValidation +import com.wavesplatform.state.{AccountScriptInfo, Blockchain} +import com.wavesplatform.transaction.TxHelpers import com.wavesplatform.transaction.smart.script.ScriptCompiler import com.wavesplatform.utils.{Schedulers, Time} import io.netty.util.HashedWheelTimer import org.scalacheck.Gen import org.scalamock.scalatest.PathMockFactory import org.scalatestplus.scalacheck.{ScalaCheckPropertyChecks => PropertyChecks} -import play.api.libs.json.{JsArray, JsObject, JsString, JsValue} +import play.api.libs.json._ import scala.concurrent.duration._ class UtilsRouteSpec extends RouteSpec("/utils") with RestAPISettingsHelper with PropertyChecks with PathMockFactory { @@ -41,10 +44,12 @@ class UtilsRouteSpec extends RouteSpec("/utils") with RestAPISettingsHelper with implicit val timeout = routeTestTimeout.duration private val estimator = ScriptEstimatorV2 - private val route = UtilsApiRoute( + + private val utilsApi: UtilsApiRoute = UtilsApiRoute( new Time { def correctedTime(): Long = System.currentTimeMillis() - def getTimestamp(): Long = System.currentTimeMillis() + + def getTimestamp(): Long = System.currentTimeMillis() }, restAPISettings, () => estimator, @@ -55,7 +60,8 @@ class UtilsRouteSpec extends RouteSpec("/utils") with RestAPISettingsHelper with "rest-time-limited" ), stub[Blockchain]("globalBlockchain") - ).route + ) + private val route = utilsApi.route val script = FUNCTION_CALL( function = PureContext.eq.header, @@ -674,6 +680,111 @@ class UtilsRouteSpec extends RouteSpec("/utils") with RestAPISettingsHelper with } } + routePath("/script/evaluate/{address}") in { + val testScript = { + val str = s""" + |{-# STDLIB_VERSION 4 #-} + |{-# CONTENT_TYPE DAPP #-} + |{-# SCRIPT_TYPE ACCOUNT #-} + | + |func test(i: Int) = i * 10 + |func testB() = true + |func testBS() = base58'MATCHER' + |func testS() = "Test" + |func testF() = throw("Test") + |func testCompl() = ${"sigVerify(base58'', base58'', base58'') ||" * 100} true + |func testThis() = this + |func testListArg(list: List[String|ByteVector|Int], str: String, bytes: ByteVector) = list.containsElement(str) + | + |@Callable(i) + |func testCallable() = [BinaryEntry("test", i.caller.bytes)] + |""".stripMargin + + val (script, _) = ScriptCompiler.compile(str, ScriptEstimatorV2).explicitGet() + AccountScriptInfo(PublicKey(new Array[Byte](32)), script, 0, Map.empty) + } + + val dAppAddress = TxHelpers.defaultSigner.toAddress + + def evalScript(text: String) = + Post(routePath(s"/script/evaluate/$dAppAddress"), Json.obj("expr" -> text)) + + def evalBin(expr: EXPR) = { + val serialized = ByteStr(Serde.serialize(expr)) + Post(routePath(s"/script/evaluate/$dAppAddress"), Json.obj("expr" -> serialized.toString)) + } + + def responseJson: JsObject = { + val fullJson = responseAs[JsObject] + (fullJson \ "address").as[String] shouldBe dAppAddress.stringRepr + (fullJson \ "expr").as[String] should not be empty + (fullJson \ "result").asOpt[JsObject].getOrElse(fullJson - "address" - "expr") + } + + (utilsApi.blockchain.hasAccountScript _).when(dAppAddress).returning(false).once() + + evalScript("testNone()") ~> route ~> check { + responseAs[JsObject] shouldBe Json.obj("error" -> 199, "message" -> s"Address $dAppAddress is not dApp") + } + + (utilsApi.blockchain.hasAccountScript _).when(dAppAddress).returning(true).anyNumberOfTimes() + (utilsApi.blockchain.accountScript _).when(dAppAddress).returning(Some(testScript)).anyNumberOfTimes() + + evalScript("testListArg([\"test\", 111, base64'dGVzdA==', false], \"test\", base58'aaa')") ~> route ~> check { + responseJson shouldBe Json.obj("type" -> "Boolean", "value" -> true) + } + + evalScript("testCallable()") ~> route ~> check { + responseAs[String] shouldBe "{\"result\":{\"type\":\"Array\",\"value\":[{\"type\":\"BinaryEntry\",\"value\":{\"key\":{\"type\":\"String\",\"value\":\"test\"},\"value\":{\"type\":\"ByteVector\",\"value\":\"11111111111111111111111111\"}}}]},\"expr\":\"testCallable()\",\"address\":\"3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9\"}" + } + + evalScript("testThis()") ~> route ~> check { + responseJson shouldBe Json.obj( + "type" -> "Address", + "value" -> Json.obj("bytes" -> Json.obj("type" -> "ByteVector", "value" -> "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9")) + ) + } + + evalScript("testNone()") ~> route ~> check { + responseJson shouldBe Json.obj("error" -> 306, "message" -> "Function or type 'testNone' not found") + } + + evalScript("testCompl()") ~> route ~> check { + responseJson shouldBe Json.obj("error" -> 306, "message" -> "Calculation complexity limit exceeded") + } + + evalScript("testF()") ~> route ~> check { + responseJson shouldBe Json.obj("error" -> 306, "message" -> "Test") + } + + evalScript("test(123)") ~> route ~> check { + responseJson shouldBe Json.obj("type" -> "Int", "value" -> 1230) + } + + evalBin(FUNCTION_CALL(FunctionHeader.User("test"), List(CONST_LONG(123)))) ~> route ~> check { + responseJson shouldBe Json.obj("type" -> "Int", "value" -> 1230) + } + + evalScript("testS()") ~> route ~> check { + responseJson shouldBe Json.obj("type" -> "String", "value" -> "Test") + } + + evalScript("testB()") ~> route ~> check { + responseJson shouldBe Json.obj("type" -> "Boolean", "value" -> true) + } + + evalScript("testBS()") ~> route ~> check { + responseJson shouldBe Json.obj("type" -> "ByteVector", "value" -> "MATCHER") + } + + evalScript("""match test(123) { + | case i: Int => i * 123 + | case _ => throw("") + |}""".stripMargin) ~> route ~> check { + responseJson shouldBe Json.obj("type" -> "Int", "value" -> 151290) + } + } + for ((hash, f) <- Seq[(String, String => Array[Byte])]( "secure" -> crypto.secureHash, "fast" -> crypto.fastHash diff --git a/node/src/test/scala/com/wavesplatform/settings/RestAPISettingsSpecification.scala b/node/src/test/scala/com/wavesplatform/settings/RestAPISettingsSpecification.scala index e5fa1fbac7d..8c3e23c3b28 100644 --- a/node/src/test/scala/com/wavesplatform/settings/RestAPISettingsSpecification.scala +++ b/node/src/test/scala/com/wavesplatform/settings/RestAPISettingsSpecification.scala @@ -17,6 +17,7 @@ class RestAPISettingsSpecification extends FlatSpec with Matchers { | api-key-different-host: yes | transactions-by-address-limit = 10000 | distribution-address-limit = 10000 + | evaluate-script-complexity-limit = 4000 | limited-pool-threads = 2 | } |} @@ -31,6 +32,7 @@ class RestAPISettingsSpecification extends FlatSpec with Matchers { settings.apiKeyDifferentHost should be(true) settings.transactionsByAddressLimit shouldBe 10000 settings.distributionAddressLimit shouldBe 10000 + settings.evaluateScriptComplexityLimit shouldBe 4000 settings.limitedPoolThreads shouldBe 2 } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/SetScriptTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/SetScriptTransactionDiffTest.scala index 5cb7fecc7ec..c04710f648e 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/SetScriptTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/SetScriptTransactionDiffTest.scala @@ -9,15 +9,14 @@ import com.wavesplatform.lang.Global 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._ -import com.wavesplatform.lang.directives.values.{DApp => DAppType} -import com.wavesplatform.lang.script.{ContractScript, Script} +import com.wavesplatform.lang.directives.values.{DApp => DAppType, _} import com.wavesplatform.lang.script.v1.ExprScript +import com.wavesplatform.lang.script.{ContractScript, Script} import com.wavesplatform.lang.v1.FunctionHeader.Native -import com.wavesplatform.lang.v1.compiler.{ContractCompiler, ExpressionCompiler, Terms} import com.wavesplatform.lang.v1.compiler.Terms._ -import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} +import com.wavesplatform.lang.v1.compiler.{ContractCompiler, ExpressionCompiler, Terms} 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.protobuf.dapp.DAppMeta import com.wavesplatform.settings.{FunctionalitySettings, TestFunctionalitySettings} @@ -144,7 +143,7 @@ class SetScriptTransactionDiffTest extends PropSpec with PropertyChecks with Tra | rsaVerify(SHA256, base64'ZGdnZHMK',base64'ZGdnZHMK',base64'ZGdnZHMK') """.stripMargin - val expr = ExpressionCompiler.compile(script, ctx.compilerContext).explicitGet() + val expr = ExpressionCompiler.compileBoolean(script, ctx.compilerContext).explicitGet() ExprScript(V3, expr).explicitGet() } @@ -194,7 +193,7 @@ class SetScriptTransactionDiffTest extends PropSpec with PropertyChecks with Tra | groth16Verify_5inputs(base64'ZGdnZHMK',base64'ZGdnZHMK',base64'ZGdnZHMK') || groth16Verify_1inputs(base64'ZGdnZHMK',base64'ZGdnZHMK',base64'ZGdnZHMK') """.stripMargin - val expr = ExpressionCompiler.compile(script, ctx.compilerContext).explicitGet() + val expr = ExpressionCompiler.compileBoolean(script, ctx.compilerContext).explicitGet() ExprScript(V4, expr).explicitGet() } diff --git a/node/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala b/node/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala index 0f5511f0673..fa11ed135bf 100644 --- a/node/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala +++ b/node/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala @@ -401,7 +401,7 @@ class UtxPoolSpecification """let x = 1 |let y = 2 |true""".stripMargin - ExpressionCompiler.compile(code, CompilerContext.empty).explicitGet() + ExpressionCompiler.compileBoolean(code, CompilerContext.empty).explicitGet() } private val script: Script = ExprScript(expr).explicitGet()