From 6fd73e8aa6f22efacab7edfdef4520064bd158fc Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Tue, 7 Feb 2023 10:10:16 +0800 Subject: [PATCH 01/42] readme typing error revised change "quarding propostion" to "guarding propostion" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index afda393b92..8153c4030c 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ be verified before the spending transaction is added to the blockchain. To allow valid coin spending a [verifier](sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala) is running the ErgoTree interpreter with the following three inputs: -- a quarding proposition given by an ErgoTree +- a guarding proposition given by an ErgoTree - a blockchain _context_ of the transaction being verified - a _proof_ (aka transaction signature) generated by a _prover_ From 3e9d43b019e9f940ebfe824b33f80ae203db0016 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Mon, 13 Feb 2023 21:50:06 +0530 Subject: [PATCH 02/42] Add isEmpty method to the frontend compiler --- .gitignore | 3 ++- .../scala/sigmastate/lang/SigmaBinder.scala | 3 +++ .../sigmastate/eval/EvaluationTest.scala | 3 +++ .../sigmastate/lang/SigmaBinderTest.scala | 2 +- .../sigmastate/lang/SigmaParserTest.scala | 2 ++ .../sigmastate/lang/SigmaTyperTest.scala | 3 +-- .../utxo/BasicOpsSpecification.scala | 26 +++++++++++++++++++ 7 files changed, 38 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a3d1c520e6..3192ecc461 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,5 @@ spam-tests/ .metals/ .vscode/ project/metals.sbt -/project/project/ \ No newline at end of file +/project/project/ +.bsp/ \ No newline at end of file diff --git a/sc/src/main/scala/sigmastate/lang/SigmaBinder.scala b/sc/src/main/scala/sigmastate/lang/SigmaBinder.scala index 7ca4f4db73..07616af0c4 100644 --- a/sc/src/main/scala/sigmastate/lang/SigmaBinder.scala +++ b/sc/src/main/scala/sigmastate/lang/SigmaBinder.scala @@ -112,6 +112,9 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder, case a @ Apply(PKFunc.symNoType, args) => Some(PKFunc.irInfo.irBuilder(PKFunc.sym, args).withPropagatedSrcCtx(a.sourceContext)) + case sel @ Select(obj, "isEmpty", _) => + Some(mkLogicalNot(mkSelect(obj, "isDefined").asBoolValue).withPropagatedSrcCtx(sel.sourceContext)) + })))(e) def bind(e: SValue): SValue = diff --git a/sc/src/test/scala/sigmastate/eval/EvaluationTest.scala b/sc/src/test/scala/sigmastate/eval/EvaluationTest.scala index 9edc4a33d6..6d80400c2d 100644 --- a/sc/src/test/scala/sigmastate/eval/EvaluationTest.scala +++ b/sc/src/test/scala/sigmastate/eval/EvaluationTest.scala @@ -42,11 +42,14 @@ class EvaluationTest extends BaseCtxTests val ctx = newErgoContext(height = 1, self) // guarded register access: existing reg reduce(emptyEnv, "lazy1", "SELF.R4[Int].isDefined && SELF.R4[Int].get == 10", ctx, true) + reduce(emptyEnv, "lazy4", "SELF.R4[Int].isEmpty == false && SELF.R4[Int].get == 10", ctx, true) // guarded register access: non-existing reg reduce(emptyEnv, "lazy2", "SELF.R5[Int].isDefined && SELF.R5[Int].get == 10", ctx, false) + reduce(emptyEnv, "lazy5", "SELF.R5[Int].isEmpty == false && SELF.R5[Int].get == 10", ctx, false) // guarded register access: reading register if it is defined and another one is undefined reduce(emptyEnv, "lazy3", "SELF.R4[Int].isDefined && (SELF.R5[Int].isDefined || SELF.R4[Int].get == 10)", ctx, true) + reduce(emptyEnv, "lazy6", "SELF.R4[Int].isEmpty == false && (SELF.R5[Int].isEmpty == false || SELF.R4[Int].get == 10)", ctx, true) } test("context data") { diff --git a/sc/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sc/src/test/scala/sigmastate/lang/SigmaBinderTest.scala index de52c93fc3..b8aefc1041 100644 --- a/sc/src/test/scala/sigmastate/lang/SigmaBinderTest.scala +++ b/sc/src/test/scala/sigmastate/lang/SigmaBinderTest.scala @@ -191,7 +191,7 @@ class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat bind(env, "X[(Int, Boolean)]") shouldBe ApplyTypes(Ident("X"), Seq(STuple(SInt, SBoolean))) bind(env, "X[Int, Boolean]") shouldBe ApplyTypes(Ident("X"), Seq(SInt, SBoolean)) bind(env, "SELF.R1[Int]") shouldBe ApplyTypes(Select(Self, "R1"), Seq(SInt)) - bind(env, "SELF.R1[Int].isEmpty") shouldBe Select(ApplyTypes(Select(Self, "R1"), Seq(SInt)), "isEmpty") + bind(env, "SELF.R1[Int].isEmpty") shouldBe LogicalNot(Select(ApplyTypes(Select(Self, "R1"), Seq(SInt)), "isDefined").asBoolValue) bind(env, "f[Int](10)") shouldBe Apply(ApplyTypes(Ident("f"), Seq(SInt)), IndexedSeq(IntConstant(10))) bind(env, "INPUTS.map[Int]") shouldBe ApplyTypes(Select(Inputs, "map"), Seq(SInt)) bind(env, "INPUTS.map[Int](10)") shouldBe Apply(ApplyTypes(Select(Inputs, "map"), Seq(SInt)), IndexedSeq(IntConstant(10))) diff --git a/sc/src/test/scala/sigmastate/lang/SigmaParserTest.scala b/sc/src/test/scala/sigmastate/lang/SigmaParserTest.scala index d02c30ccd8..24dc35c2bb 100644 --- a/sc/src/test/scala/sigmastate/lang/SigmaParserTest.scala +++ b/sc/src/test/scala/sigmastate/lang/SigmaParserTest.scala @@ -539,11 +539,13 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat parse("X[Byte]") shouldBe ApplyTypes(Ident("X"), Seq(SByte)) parse("X[Int]") shouldBe ApplyTypes(Ident("X"), Seq(SInt)) parse("X[Int].isDefined") shouldBe Select(ApplyTypes(Ident("X"), Seq(SInt)), "isDefined") + parse("X[Int].isEmpty") shouldBe Select(ApplyTypes(Ident("X"), Seq(SInt)), "isEmpty") parse("X[(Int, Boolean)]") shouldBe ApplyTypes(Ident("X"), Seq(STuple(SInt, SBoolean))) parse("X[Int, Boolean]") shouldBe ApplyTypes(Ident("X"), Seq(SInt, SBoolean)) parse("SELF.R1[Int]") shouldBe ApplyTypes(Select(Ident("SELF"), "R1"), Seq(SInt)) parse("SELF.getReg[Int](1)") shouldBe Apply(ApplyTypes(Select(Ident("SELF"), "getReg"), Seq(SInt)), IndexedSeq(IntConstant(1))) parse("SELF.R1[Int].isDefined") shouldBe Select(ApplyTypes(Select(Ident("SELF"), "R1"), Seq(SInt)),"isDefined") + parse("SELF.R1[Int].isEmpty") shouldBe Select(ApplyTypes(Select(Ident("SELF"), "R1"), Seq(SInt)),"isEmpty") parse("f[Int](10)") shouldBe Apply(ApplyTypes(Ident("f"), Seq(SInt)), IndexedSeq(IntConstant(10))) parse("INPUTS.map[Int]") shouldBe ApplyTypes(Select(Ident("INPUTS"), "map"), Seq(SInt)) parse("INPUTS.map[Int](10)") shouldBe Apply(ApplyTypes(Select(Ident("INPUTS"), "map"), Seq(SInt)), IndexedSeq(IntConstant(10))) diff --git a/sc/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 609751fefb..ff559cc073 100644 --- a/sc/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -298,8 +298,7 @@ class SigmaTyperTest extends AnyPropSpec property("type parameters") { typecheck(env, "SELF.R1[Int]") shouldBe SOption(SInt) typecheck(env, "SELF.R1[Int].isDefined") shouldBe SBoolean - // TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - // typecheck(env, "SELF.R1[Int].isEmpty") shouldBe SBoolean + typecheck(env, "SELF.R1[Int].isEmpty") shouldBe SBoolean typecheck(env, "SELF.R1[Int].get") shouldBe SInt // TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416 // typecheck(env, "SELF.getReg[Int](1)") shouldBe SOption.SIntOption diff --git a/sc/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 7a74899ef1..8ae9bbf376 100644 --- a/sc/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -469,6 +469,32 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("OptionIsEmpty") { + test("Def1", env, ext, + "{ SELF.R4[SigmaProp].isEmpty == false }", + ExtractRegisterAs[SSigmaProp.type](Self, reg1).isDefined.toSigmaProp, + true + ) + // no value + test("Def2", env, ext, + "{ SELF.R8[Int].isEmpty }", + LogicalNot(ExtractRegisterAs[SInt.type](Self, R8).isDefined).toSigmaProp, + true + ) + + test("Def3", env, ext, + "{ getVar[Int](intVar2).isEmpty == false }", + GetVarInt(intVar2).isDefined.toSigmaProp, + true + ) + // there should be no variable with this id + test("Def4", env, ext, + "{ getVar[Int](99).isEmpty }", + LogicalNot(GetVarInt(99).isDefined).toSigmaProp, + true + ) + } + // TODO this is valid for BigIntModQ type (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554) ignore("ByteArrayToBigInt: big int should always be positive") { test("BATBI1", env, ext, From f33c24729f148d8e7eae08ee005a2f7d441d1286 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Wed, 15 Feb 2023 00:58:23 +0530 Subject: [PATCH 03/42] Add isEmpty to LangSpec --- docs/LangSpec.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/LangSpec.md b/docs/LangSpec.md index e0152c4e7a..2aa10484b0 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -594,6 +594,10 @@ class Option[A] { /** Returns true if the option is an instance of Some(value), false otherwise. */ def isDefined: Boolean; + + /** Returns true if the option is None, false otherwise. + */ + def isEmpty: Boolean; /** Returns the option's value if the option is nonempty, otherwise * return the result of evaluating `default`. From 2be744581795e0dc844e94f79456af13664665d8 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Thu, 16 Feb 2023 02:21:34 +0530 Subject: [PATCH 04/42] remove isEmpty from Option new methods test --- sc/src/test/scala/special/sigma/SigmaDslSpecification.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sc/src/test/scala/special/sigma/SigmaDslSpecification.scala b/sc/src/test/scala/special/sigma/SigmaDslSpecification.scala index c5a8d963a4..8f8a4b9a66 100644 --- a/sc/src/test/scala/special/sigma/SigmaDslSpecification.scala +++ b/sc/src/test/scala/special/sigma/SigmaDslSpecification.scala @@ -9097,15 +9097,12 @@ class SigmaDslSpecification extends SigmaDslTesting // TODO v6.0 (3h): implement Option.fold property("Option new methods") { - val isEmpty = newFeature({ (x: Option[Long]) => x.isEmpty }, - "{ (x: Option[Long]) => x.isEmpty }") - val n = ExactNumeric.LongIsExactNumeric val fold = newFeature({ (x: Option[Long]) => x.fold(5.toLong)( (v: Long) => n.plus(v, 1) ) }, "{ (x: Option[Long]) => x.fold(5, { (v: Long) => v + 1 }) }") forAll { x: Option[Long] => - Seq(isEmpty, fold).map(_.checkEquality(x)) + Seq(fold).map(_.checkEquality(x)) } } From 1d96276ac3444081d794b2fc4ad160052f6fc495 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Fri, 17 Feb 2023 01:30:01 +0530 Subject: [PATCH 05/42] Extract SigmaParser and deps into a new module --- build.sbt | 25 +++++- .../scala/sigmastate/lang/SigmaParser.scala | 0 .../main/scala/sigmastate/lang/Types.scala | 0 .../scala/sigmastate/lang/syntax/Basic.scala | 0 .../scala/sigmastate/lang/syntax/Core.scala | 0 .../scala/sigmastate/lang/syntax/Exprs.scala | 0 .../sigmastate/lang/syntax/Identifiers.scala | 0 .../sigmastate/lang/syntax/Literals.scala | 0 .../scala/sigmastate/lang/LangTests.scala | 0 .../sigmastate/lang/SigmaParserTest.scala | 64 --------------- .../sigmastate/lang/SigmaCompilerTest.scala | 78 +++++++++++++++++++ 11 files changed, 100 insertions(+), 67 deletions(-) rename {sc => parsers/shared}/src/main/scala/sigmastate/lang/SigmaParser.scala (100%) rename {sc => parsers/shared}/src/main/scala/sigmastate/lang/Types.scala (100%) rename {sc => parsers/shared}/src/main/scala/sigmastate/lang/syntax/Basic.scala (100%) rename {sc => parsers/shared}/src/main/scala/sigmastate/lang/syntax/Core.scala (100%) rename {sc => parsers/shared}/src/main/scala/sigmastate/lang/syntax/Exprs.scala (100%) rename {sc => parsers/shared}/src/main/scala/sigmastate/lang/syntax/Identifiers.scala (100%) rename {sc => parsers/shared}/src/main/scala/sigmastate/lang/syntax/Literals.scala (100%) rename {sc => parsers/shared}/src/test/scala/sigmastate/lang/LangTests.scala (100%) rename {sc => parsers/shared}/src/test/scala/sigmastate/lang/SigmaParserTest.scala (95%) diff --git a/build.sbt b/build.sbt index ee6259721c..3b7b09dc78 100644 --- a/build.sbt +++ b/build.sbt @@ -245,9 +245,28 @@ lazy val interpreterJS = interpreter.js ) ) +lazy val parsers = crossProject(JVMPlatform, JSPlatform) + .in(file("parsers")) + .dependsOn(interpreter % allConfigDependency) + .settings(libraryDefSettings) + .settings(libraryDependencies ++= + Seq(scorexUtil, fastparse) ++ circeDeps(scalaVersion.value) + ) + .jvmSettings( + crossScalaSettings + ) + .jsSettings( + crossScalaSettingsJS, + libraryDependencies ++= Seq( + "org.scala-js" %%% "scala-js-macrotask-executor" % "1.0.0" + ), + useYarn := true + ) +lazy val parsersJS = parsers.js + .enablePlugins(ScalaJSBundlerPlugin) lazy val sc = (project in file("sc")) - .dependsOn(graphir % allConfigDependency, interpreter.jvm % allConfigDependency) + .dependsOn(graphir % allConfigDependency, interpreter.jvm % allConfigDependency, parsers.jvm % allConfigDependency) .settings(libraryDefSettings) .settings(libraryDependencies ++= Seq(scorexUtil, fastparse) ++ circeDeps(scalaVersion.value) @@ -286,13 +305,13 @@ lazy val sdkJS = sdk.js ) lazy val sigma = (project in file(".")) - .aggregate(common.jvm, corelib.jvm, graphir, interpreter.jvm, sc, sdk.jvm) + .aggregate(common.jvm, corelib.jvm, graphir, interpreter.jvm, parsers.jvm, sc, sdk.jvm) .settings(libraryDefSettings, rootSettings) .settings(publish / aggregate := false) .settings(publishLocal / aggregate := false) lazy val aggregateCompile = ScopeFilter( - inProjects(common.jvm, corelib.jvm, graphir, interpreter.jvm, sc, sdk.jvm), + inProjects(common.jvm, corelib.jvm, graphir, interpreter.jvm, parsers.jvm, sc, sdk.jvm), inConfigurations(Compile)) lazy val rootSettings = Seq( diff --git a/sc/src/main/scala/sigmastate/lang/SigmaParser.scala b/parsers/shared/src/main/scala/sigmastate/lang/SigmaParser.scala similarity index 100% rename from sc/src/main/scala/sigmastate/lang/SigmaParser.scala rename to parsers/shared/src/main/scala/sigmastate/lang/SigmaParser.scala diff --git a/sc/src/main/scala/sigmastate/lang/Types.scala b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala similarity index 100% rename from sc/src/main/scala/sigmastate/lang/Types.scala rename to parsers/shared/src/main/scala/sigmastate/lang/Types.scala diff --git a/sc/src/main/scala/sigmastate/lang/syntax/Basic.scala b/parsers/shared/src/main/scala/sigmastate/lang/syntax/Basic.scala similarity index 100% rename from sc/src/main/scala/sigmastate/lang/syntax/Basic.scala rename to parsers/shared/src/main/scala/sigmastate/lang/syntax/Basic.scala diff --git a/sc/src/main/scala/sigmastate/lang/syntax/Core.scala b/parsers/shared/src/main/scala/sigmastate/lang/syntax/Core.scala similarity index 100% rename from sc/src/main/scala/sigmastate/lang/syntax/Core.scala rename to parsers/shared/src/main/scala/sigmastate/lang/syntax/Core.scala diff --git a/sc/src/main/scala/sigmastate/lang/syntax/Exprs.scala b/parsers/shared/src/main/scala/sigmastate/lang/syntax/Exprs.scala similarity index 100% rename from sc/src/main/scala/sigmastate/lang/syntax/Exprs.scala rename to parsers/shared/src/main/scala/sigmastate/lang/syntax/Exprs.scala diff --git a/sc/src/main/scala/sigmastate/lang/syntax/Identifiers.scala b/parsers/shared/src/main/scala/sigmastate/lang/syntax/Identifiers.scala similarity index 100% rename from sc/src/main/scala/sigmastate/lang/syntax/Identifiers.scala rename to parsers/shared/src/main/scala/sigmastate/lang/syntax/Identifiers.scala diff --git a/sc/src/main/scala/sigmastate/lang/syntax/Literals.scala b/parsers/shared/src/main/scala/sigmastate/lang/syntax/Literals.scala similarity index 100% rename from sc/src/main/scala/sigmastate/lang/syntax/Literals.scala rename to parsers/shared/src/main/scala/sigmastate/lang/syntax/Literals.scala diff --git a/sc/src/test/scala/sigmastate/lang/LangTests.scala b/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala similarity index 100% rename from sc/src/test/scala/sigmastate/lang/LangTests.scala rename to parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala diff --git a/sc/src/test/scala/sigmastate/lang/SigmaParserTest.scala b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala similarity index 95% rename from sc/src/test/scala/sigmastate/lang/SigmaParserTest.scala rename to parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala index 24dc35c2bb..f57fae2da8 100644 --- a/sc/src/test/scala/sigmastate/lang/SigmaParserTest.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala @@ -10,7 +10,6 @@ import sigmastate.Values._ import sigmastate._ import sigmastate.lang.SigmaPredef.PredefinedFuncRegistry import sigmastate.lang.Terms._ -import sigmastate.lang.syntax.ParserException import sigmastate.serialization.OpCodes class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with LangTests { @@ -36,15 +35,6 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat SigmaParser.parseType(x) } - def fail(x: String, expectedLine: Int, expectedCol: Int): Unit = { - val compiler = new SigmaCompiler(ErgoAddressEncoder.TestnetNetworkPrefix) - val exception = the[ParserException] thrownBy compiler.parse(x) - withClue(s"Exception: $exception, is missing source context:") { exception.source shouldBe defined } - val sourceContext = exception.source.get - sourceContext.line shouldBe expectedLine - sourceContext.column shouldBe expectedCol - } - def and(l: SValue, r: SValue) = MethodCallLike(l, "&&", IndexedSeq(r)) def or(l: SValue, r: SValue) = MethodCallLike(l, "||", IndexedSeq(r)) def xor(l: SValue, r: SValue) = MethodCallLike(l, "^", IndexedSeq(r)) @@ -561,24 +551,6 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat parseType("Coll[(Coll[Byte], (Coll[Long], Long))]") shouldBe SCollection(STuple(SByteArray, STuple(SLongArray, SLong))) } - property("negative tests") { - fail("(10", 1, 4) - fail("10)", 1, 3) - fail("X)", 1, 2) - fail("(X", 1, 3) - fail("{ X", 1, 4) - fail("{ val X", 1, 8) - fail("\"str", 1, 5) - } - - property("not(yet) supported lambda syntax") { - // passing a lambda without curly braces is not supported yet :) - fail("arr.exists ( (a: Int) => a >= 1 )", 1, 16) - // no argument type - an[ParserException] should be thrownBy parse("arr.exists ( a => a >= 1 )") - an[ParserException] should be thrownBy parse("arr.exists { a => a >= 1 }") - } - property("numeric casts") { parse("1.toByte") shouldBe Select(IntConstant(1), "toByte") parse("1.toShort") shouldBe Select(IntConstant(1), "toShort") @@ -629,10 +601,6 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat IndexedSeq(Apply(SigmaPropFunc.symNoType, IndexedSeq(GT(Ident("HEIGHT"), IntConstant(1000)))))) } - property("invalid ZKProof (non block parameter)") { - fail("ZKProof 1 > 1", 1, 9) - } - property("sigmaProp") { parse("sigmaProp(HEIGHT > 1000)") shouldBe Apply(SigmaPropFunc.symNoType, IndexedSeq(GT(Ident("HEIGHT"), IntConstant(1000)))) @@ -878,36 +846,4 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat ApplyTypes(ExecuteFromVarFunc.symNoType, Vector(SBoolean)), Vector(IntConstant(1)) ) } - - property("single name pattern fail") { - fail("{val (a,b) = (1,2)}", 1, 6) - } - - property("unknown prefix in unary op") { - fail("+1", 1, 2) - } - - property("empty lines before invalid op") { - fail( - """ - | - | - |+1""".stripMargin, 4, 2) - } - - property("unknown binary op") { - fail("1**1", 1, 1) - } - - property("compound types not supported") { - fail("Coll[Int with Sortable](1)", 1, 6) - } - - property("path types not supported") { - fail("Coll[Int.A](1)", 1, 10) - } - - property("block contains non-Val binding before expression") { - fail("{1 ; 1 == 1}", 1, 2) - } } \ No newline at end of file diff --git a/sc/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/sc/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 8afd6015fa..6937cb618d 100644 --- a/sc/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/sc/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -1,5 +1,6 @@ package sigmastate.lang +import fastparse.Parsed import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix import org.ergoplatform._ import scorex.util.encode.Base58 @@ -8,6 +9,7 @@ import sigmastate._ import sigmastate.helpers.CompilerTestingCommons import sigmastate.interpreter.Interpreter.ScriptEnv import sigmastate.lang.Terms.{Apply, Ident, Lambda, MethodCall, ZKProofBlock} +import sigmastate.lang.syntax.ParserException import sigmastate.exceptions.{CosterException, InvalidArguments, TyperException} import sigmastate.serialization.ValueSerializer import sigmastate.serialization.generators.ObjectGenerators @@ -41,6 +43,28 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec private def costerFail(x: String, expectedLine: Int, expectedCol: Int): Unit = costerFail(env, x, expectedLine, expectedCol) + def parse(x: String): SValue = { + SigmaParser(x) match { + case Parsed.Success(v, _) => + v.sourceContext.isDefined shouldBe true + assertSrcCtxForAllNodes(v) + v + case f@Parsed.Failure(_, _, extra) => + val traced = extra.traced + println(s"\nTRACE: ${traced.trace}") + f.get // force show error diagnostics + } + } + + def fail(x: String, expectedLine: Int, expectedCol: Int): Unit = { + val compiler = new SigmaCompiler(ErgoAddressEncoder.TestnetNetworkPrefix) + val exception = the[ParserException] thrownBy compiler.parse(x) + withClue(s"Exception: $exception, is missing source context:") { exception.source shouldBe defined } + val sourceContext = exception.source.get + sourceContext.line shouldBe expectedLine + sourceContext.column shouldBe expectedCol + } + property("array indexed access") { comp(env, "Coll(1)(0)") shouldBe ByIndex(ConcreteCollection.fromSeq(Array(IntConstant(1)))(SInt), 0) @@ -340,4 +364,58 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec GetVar(3.toByte, SCollection(SSigmaProp)).get ) } + + property("negative tests") { + fail("(10", 1, 4) + fail("10)", 1, 3) + fail("X)", 1, 2) + fail("(X", 1, 3) + fail("{ X", 1, 4) + fail("{ val X", 1, 8) + fail("\"str", 1, 5) + } + + property("not(yet) supported lambda syntax") { + // passing a lambda without curly braces is not supported yet :) + fail("arr.exists ( (a: Int) => a >= 1 )", 1, 16) + // no argument type + an[ParserException] should be thrownBy parse("arr.exists ( a => a >= 1 )") + an[ParserException] should be thrownBy parse("arr.exists { a => a >= 1 }") + } + + property("invalid ZKProof (non block parameter)") { + fail("ZKProof 1 > 1", 1, 9) + } + + property("single name pattern fail") { + fail("{val (a,b) = (1,2)}", 1, 6) + } + + property("unknown prefix in unary op") { + fail("+1", 1, 2) + } + + property("empty lines before invalid op") { + fail( + """ + | + | + |+1""".stripMargin, 4, 2) + } + + property("unknown binary op") { + fail("1**1", 1, 1) + } + + property("compound types not supported") { + fail("Coll[Int with Sortable](1)", 1, 6) + } + + property("path types not supported") { + fail("Coll[Int.A](1)", 1, 10) + } + + property("block contains non-Val binding before expression") { + fail("{1 ; 1 == 1}", 1, 2) + } } From cc744e0061e7af1fde29596b8d94b903b406adb6 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Wed, 22 Feb 2023 00:39:46 +0530 Subject: [PATCH 06/42] revert movement of SigmaParser tests --- .../sigmastate/lang/SigmaParserTest.scala | 71 +++++++++++++++++ .../sigmastate/lang/SigmaCompilerTest.scala | 78 ------------------- 2 files changed, 71 insertions(+), 78 deletions(-) diff --git a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala index f57fae2da8..179b6f89a8 100644 --- a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala @@ -10,6 +10,7 @@ import sigmastate.Values._ import sigmastate._ import sigmastate.lang.SigmaPredef.PredefinedFuncRegistry import sigmastate.lang.Terms._ +import sigmastate.lang.syntax.ParserException import sigmastate.serialization.OpCodes class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with LangTests { @@ -31,10 +32,26 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat } } + def parseWithException(x: String): SValue = { + SigmaParser(x) match { + case Parsed.Success(v, _) => v + case f: Parsed.Failure => + throw new ParserException(s"Syntax error: $f", Some(SourceContext.fromParserFailure(f))) + } + } + def parseType(x: String): SType = { SigmaParser.parseType(x) } + def fail(x: String, expectedLine: Int, expectedCol: Int): Unit = { + val exception = the[ParserException] thrownBy parseWithException(x) + withClue(s"Exception: $exception, is missing source context:") { exception.source shouldBe defined } + val sourceContext = exception.source.get + sourceContext.line shouldBe expectedLine + sourceContext.column shouldBe expectedCol + } + def and(l: SValue, r: SValue) = MethodCallLike(l, "&&", IndexedSeq(r)) def or(l: SValue, r: SValue) = MethodCallLike(l, "||", IndexedSeq(r)) def xor(l: SValue, r: SValue) = MethodCallLike(l, "^", IndexedSeq(r)) @@ -551,6 +568,24 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat parseType("Coll[(Coll[Byte], (Coll[Long], Long))]") shouldBe SCollection(STuple(SByteArray, STuple(SLongArray, SLong))) } + property("negative tests") { + fail("(10", 1, 4) + fail("10)", 1, 3) + fail("X)", 1, 2) + fail("(X", 1, 3) + fail("{ X", 1, 4) + fail("{ val X", 1, 8) + fail("\"str", 1, 5) + } + + property("not(yet) supported lambda syntax") { + // passing a lambda without curly braces is not supported yet :) + fail("arr.exists ( (a: Int) => a >= 1 )", 1, 16) + // no argument type + an[ParserException] should be thrownBy parse("arr.exists ( a => a >= 1 )") + an[ParserException] should be thrownBy parse("arr.exists { a => a >= 1 }") + } + property("numeric casts") { parse("1.toByte") shouldBe Select(IntConstant(1), "toByte") parse("1.toShort") shouldBe Select(IntConstant(1), "toShort") @@ -601,6 +636,10 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat IndexedSeq(Apply(SigmaPropFunc.symNoType, IndexedSeq(GT(Ident("HEIGHT"), IntConstant(1000)))))) } + property("invalid ZKProof (non block parameter)") { + fail("ZKProof 1 > 1", 1, 9) + } + property("sigmaProp") { parse("sigmaProp(HEIGHT > 1000)") shouldBe Apply(SigmaPropFunc.symNoType, IndexedSeq(GT(Ident("HEIGHT"), IntConstant(1000)))) @@ -846,4 +885,36 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat ApplyTypes(ExecuteFromVarFunc.symNoType, Vector(SBoolean)), Vector(IntConstant(1)) ) } + + property("single name pattern fail") { + fail("{val (a,b) = (1,2)}", 1, 6) + } + + property("unknown prefix in unary op") { + fail("+1", 1, 2) + } + + property("empty lines before invalid op") { + fail( + """ + | + | + |+1""".stripMargin, 4, 2) + } + + property("unknown binary op") { + fail("1**1", 1, 1) + } + + property("compound types not supported") { + fail("Coll[Int with Sortable](1)", 1, 6) + } + + property("path types not supported") { + fail("Coll[Int.A](1)", 1, 10) + } + + property("block contains non-Val binding before expression") { + fail("{1 ; 1 == 1}", 1, 2) + } } \ No newline at end of file diff --git a/sc/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/sc/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 6937cb618d..8afd6015fa 100644 --- a/sc/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/sc/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -1,6 +1,5 @@ package sigmastate.lang -import fastparse.Parsed import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix import org.ergoplatform._ import scorex.util.encode.Base58 @@ -9,7 +8,6 @@ import sigmastate._ import sigmastate.helpers.CompilerTestingCommons import sigmastate.interpreter.Interpreter.ScriptEnv import sigmastate.lang.Terms.{Apply, Ident, Lambda, MethodCall, ZKProofBlock} -import sigmastate.lang.syntax.ParserException import sigmastate.exceptions.{CosterException, InvalidArguments, TyperException} import sigmastate.serialization.ValueSerializer import sigmastate.serialization.generators.ObjectGenerators @@ -43,28 +41,6 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec private def costerFail(x: String, expectedLine: Int, expectedCol: Int): Unit = costerFail(env, x, expectedLine, expectedCol) - def parse(x: String): SValue = { - SigmaParser(x) match { - case Parsed.Success(v, _) => - v.sourceContext.isDefined shouldBe true - assertSrcCtxForAllNodes(v) - v - case f@Parsed.Failure(_, _, extra) => - val traced = extra.traced - println(s"\nTRACE: ${traced.trace}") - f.get // force show error diagnostics - } - } - - def fail(x: String, expectedLine: Int, expectedCol: Int): Unit = { - val compiler = new SigmaCompiler(ErgoAddressEncoder.TestnetNetworkPrefix) - val exception = the[ParserException] thrownBy compiler.parse(x) - withClue(s"Exception: $exception, is missing source context:") { exception.source shouldBe defined } - val sourceContext = exception.source.get - sourceContext.line shouldBe expectedLine - sourceContext.column shouldBe expectedCol - } - property("array indexed access") { comp(env, "Coll(1)(0)") shouldBe ByIndex(ConcreteCollection.fromSeq(Array(IntConstant(1)))(SInt), 0) @@ -364,58 +340,4 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec GetVar(3.toByte, SCollection(SSigmaProp)).get ) } - - property("negative tests") { - fail("(10", 1, 4) - fail("10)", 1, 3) - fail("X)", 1, 2) - fail("(X", 1, 3) - fail("{ X", 1, 4) - fail("{ val X", 1, 8) - fail("\"str", 1, 5) - } - - property("not(yet) supported lambda syntax") { - // passing a lambda without curly braces is not supported yet :) - fail("arr.exists ( (a: Int) => a >= 1 )", 1, 16) - // no argument type - an[ParserException] should be thrownBy parse("arr.exists ( a => a >= 1 )") - an[ParserException] should be thrownBy parse("arr.exists { a => a >= 1 }") - } - - property("invalid ZKProof (non block parameter)") { - fail("ZKProof 1 > 1", 1, 9) - } - - property("single name pattern fail") { - fail("{val (a,b) = (1,2)}", 1, 6) - } - - property("unknown prefix in unary op") { - fail("+1", 1, 2) - } - - property("empty lines before invalid op") { - fail( - """ - | - | - |+1""".stripMargin, 4, 2) - } - - property("unknown binary op") { - fail("1**1", 1, 1) - } - - property("compound types not supported") { - fail("Coll[Int with Sortable](1)", 1, 6) - } - - property("path types not supported") { - fail("Coll[Int.A](1)", 1, 10) - } - - property("block contains non-Val binding before expression") { - fail("{1 ; 1 == 1}", 1, 2) - } } From 71b9b8c657991143404250cac2dd8c3ad4c3ad36 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Thu, 23 Feb 2023 17:01:39 +0530 Subject: [PATCH 07/42] Move DataJsonEncoder and JsonCodecs to the sdk module --- build.sbt | 2 +- .../org/ergoplatform/sdk}/DataJsonEncoder.scala | 8 ++++++-- .../scala/org/ergoplatform/sdk}/JsonCodecs.scala | 14 ++++++++++++-- .../sdk}/DataJsonEncoderSpecification.scala | 4 ++-- .../sdk}/JsonSerializationSpec.scala | 16 ++++++++++++---- 5 files changed, 33 insertions(+), 11 deletions(-) rename {sc/src/main/scala/sigmastate/serialization => sdk/shared/src/main/scala/org/ergoplatform/sdk}/DataJsonEncoder.scala (98%) rename {sc/src/main/scala/org/ergoplatform => sdk/shared/src/main/scala/org/ergoplatform/sdk}/JsonCodecs.scala (97%) rename {sc/src/test/scala/sigmastate/serialization => sdk/shared/src/test/scala/org/ergoplatform/sdk}/DataJsonEncoderSpecification.scala (99%) rename {sc/src/test/scala/org/ergoplatform => sdk/shared/src/test/scala/org/ergoplatform/sdk}/JsonSerializationSpec.scala (93%) diff --git a/build.sbt b/build.sbt index 3b7b09dc78..f5252a2f7a 100644 --- a/build.sbt +++ b/build.sbt @@ -275,7 +275,7 @@ lazy val sc = (project in file("sc")) lazy val sdk = crossProject(JVMPlatform, JSPlatform) .in(file("sdk")) - .dependsOn(corelib % allConfigDependency, interpreter % allConfigDependency) + .dependsOn(corelib % allConfigDependency, interpreter % allConfigDependency, parsers % allConfigDependency) .settings(commonSettings ++ testSettings2, commonDependenies2, testingDependencies2, diff --git a/sc/src/main/scala/sigmastate/serialization/DataJsonEncoder.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala similarity index 98% rename from sc/src/main/scala/sigmastate/serialization/DataJsonEncoder.scala rename to sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala index fba9cf28fb..e81faeb889 100644 --- a/sc/src/main/scala/sigmastate/serialization/DataJsonEncoder.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala @@ -1,4 +1,4 @@ -package sigmastate.serialization +package org.ergoplatform.sdk import java.math.BigInteger import io.circe._ @@ -10,14 +10,18 @@ import scalan.RType import scorex.util._ import sigmastate.Values.{Constant, EvaluatedValue} import sigmastate._ -import sigmastate.eval._ import sigmastate.lang.SigmaParser +import sigmastate.eval._ import special.collection.Coll import special.sigma._ import debox.cfor import sigmastate.exceptions.SerializerException import scala.collection.compat.immutable.ArraySeq import scala.collection.mutable +import fastparse.{Parsed, parse} +import sigmastate.serialization.SigmaSerializer +import sigmastate.serialization.DataSerializer +import sigmastate.serialization.ErgoTreeSerializer object DataJsonEncoder { def encode[T <: SType](v: T#WrappedType, tpe: T): Json = { diff --git a/sc/src/main/scala/org/ergoplatform/JsonCodecs.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala similarity index 97% rename from sc/src/main/scala/org/ergoplatform/JsonCodecs.scala rename to sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala index ea239af46f..2c357a558f 100644 --- a/sc/src/main/scala/org/ergoplatform/JsonCodecs.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala @@ -1,4 +1,4 @@ -package org.ergoplatform +package org.ergoplatform.sdk import java.math.BigInteger @@ -16,12 +16,22 @@ import sigmastate.eval.Extensions._ import sigmastate.eval.{CPreHeader, WrapperOf, _} import sigmastate.exceptions.SigmaException import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.serialization.{DataJsonEncoder, ErgoTreeSerializer, ValueSerializer} import sigmastate.{AvlTreeData, AvlTreeFlags, SType} import special.collection.Coll import special.sigma.{AnyValue, Header, PreHeader} import scala.util.Try import sigmastate.utils.Helpers._ // required for Scala 2.11 +import org.ergoplatform.ErgoBox +import sigmastate.serialization.ValueSerializer +import org.ergoplatform.DataInput +import org.ergoplatform.Input +import org.ergoplatform.UnsignedInput +import sigmastate.serialization.ErgoTreeSerializer +import org.ergoplatform.ErgoLikeTransaction +import org.ergoplatform.UnsignedErgoLikeTransaction +import org.ergoplatform.ErgoLikeTransactionTemplate +import org.ergoplatform.ErgoBoxCandidate +import org.ergoplatform.ErgoLikeContext trait JsonCodecs { diff --git a/sc/src/test/scala/sigmastate/serialization/DataJsonEncoderSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala similarity index 99% rename from sc/src/test/scala/sigmastate/serialization/DataJsonEncoderSpecification.scala rename to sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala index a5465ad366..3627f99582 100644 --- a/sc/src/test/scala/sigmastate/serialization/DataJsonEncoderSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala @@ -1,9 +1,8 @@ -package sigmastate.serialization +package org.ergoplatform.sdk import java.math.BigInteger -import org.ergoplatform.JsonCodecs import org.scalacheck.Arbitrary._ import scalan.RType import sigmastate.SCollection.SByteArray @@ -15,6 +14,7 @@ import sigmastate.eval.{Evaluation, _} import sigmastate.basics.CryptoConstants.EcPointType import sigmastate.exceptions.SerializerException import special.sigma.{Box, AvlTree} +import sigmastate.serialization.SerializationSpecification class DataJsonEncoderSpecification extends SerializationSpecification { object JsonCodecs extends JsonCodecs diff --git a/sc/src/test/scala/org/ergoplatform/JsonSerializationSpec.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala similarity index 93% rename from sc/src/test/scala/org/ergoplatform/JsonSerializationSpec.scala rename to sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala index fec85d4095..e651a542b8 100644 --- a/sc/src/test/scala/org/ergoplatform/JsonSerializationSpec.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala @@ -1,4 +1,5 @@ -package org.ergoplatform +package org.ergoplatform.sdk + import io.circe._ import io.circe.syntax._ @@ -13,14 +14,21 @@ import sigmastate.Values.{ByteArrayConstant, ByteConstant, ErgoTree, EvaluatedVa import sigmastate.basics.CryptoConstants import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.eval.Digest32Coll -import sigmastate.helpers.CompilerTestingCommons import sigmastate.interpreter.{ContextExtension, ProverResult} import sigmastate.serialization.SerializationSpecification import sigmastate.utils.Helpers._ import special.collection.Coll import special.sigma.{Header, PreHeader} - -class JsonSerializationSpec extends CompilerTestingCommons with SerializationSpecification with JsonCodecs { +import org.ergoplatform.ErgoLikeContext +import org.ergoplatform.DataInput +import org.ergoplatform.Input +import org.ergoplatform.UnsignedInput +import org.ergoplatform.ErgoBox +import org.ergoplatform.ErgoLikeTransaction +import org.ergoplatform.UnsignedErgoLikeTransaction +import org.ergoplatform.ErgoLikeTransactionTemplate + +class JsonSerializationSpec extends SerializationSpecification with JsonCodecs { def jsonRoundTrip[T](v: T)(implicit encoder: Encoder[T], decoder: Decoder[T]): Unit = { val json = v.asJson From ed8d3c399da9c6a6a63bbab0f7a3510f2c3621e8 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Thu, 23 Feb 2023 19:50:17 +0530 Subject: [PATCH 08/42] Implement ContractTemplate data structures according to EIP5 --- .../ergoplatform/sdk/ContractTemplate.scala | 514 ++++++++++++++++++ .../ergoplatform/sdk/DataJsonEncoder.scala | 4 +- .../sdk/ContractTemplateSpecification.scala | 344 ++++++++++++ 3 files changed, 860 insertions(+), 2 deletions(-) create mode 100644 sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala create mode 100644 sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala new file mode 100644 index 0000000000..6041071605 --- /dev/null +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -0,0 +1,514 @@ +package org.ergoplatform.sdk + +import sigmastate.Values + +import io.circe.syntax.EncoderOps +import io.circe.{Decoder, DecodingFailure, Encoder, Json} + +import cats.syntax.either._ +import java.math.BigInteger +import java.util.{Arrays, Objects} +import org.ergoplatform.settings.ErgoAlgos +import org.ergoplatform.validation.ValidationException +import org.ergoplatform.validation.ValidationRules.CheckDeserializedScriptIsSigmaProp +import scalan.{Nullable, RType} +import scalan.util.CollectionUtil._ +import sigmastate.SCollection.{SByteArray, SIntArray} +import sigmastate.serialization.{ConstantStore, OpCodes, _} +import sigmastate.serialization.OpCodes._ +import sigmastate.TrivialProp.{FalseProp, TrueProp} +import sigmastate.Values.ErgoTree.substConstants +import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.basics.ProveDHTuple +import sigmastate.lang.Terms._ +import sigmastate.utxo._ +import sigmastate.eval._ +import sigmastate.eval.Extensions._ +import sigmastate._ +import scalan.util.Extensions.ByteOps +import sigmastate.interpreter.ErgoTreeEvaluator._ +import sigmastate.Values._ +import debox.cfor + +import scala.language.implicitConversions +import scala.reflect.ClassTag +import sigmastate.lang.CheckingSigmaBuilder._ +import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer +import sigmastate.serialization.transformers.ProveDHTupleSerializer +import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import special.sigma.{AvlTree, Header, PreHeader, _} +import sigmastate.lang.{SigmaParser, SourceContext} +import sigmastate.util.safeNewArray +import special.collection.Coll + +import java.nio.charset.StandardCharsets +import scala.collection.immutable.IndexedSeq +import scala.util.Try +import sigmastate.SType +import sigmastate.lang.StdSigmaBuilder +import sigmastate.lang.DeserializationSigmaBuilder +import sigmastate.basics.CryptoConstants +import org.ergoplatform.ErgoBox +import scorex.util.ModifierId +import sigmastate.utils.Helpers +import scorex.crypto.authds.ADDigest +import scorex.crypto.hash.Blake2b256 + + +case class Parameter( + name: String, + description: String, + placeholder: Int +) { + +} + +object Parameter { + + private def serializeString(s: String, w: SigmaByteWriter): Unit = { + val bytes = s.getBytes(StandardCharsets.UTF_8) + w.putUInt(bytes.length) + w.putBytes(bytes) + } + + private def parseString(r: SigmaByteReader): String = { + val length = r.getUInt().toInt + new String(r.getBytes(length), StandardCharsets.UTF_8) + } + + /** Immutable empty IndexedSeq, can be used to save allocations in many places. */ + val EmptySeq: IndexedSeq[Parameter] = Array.empty[Parameter] + + /** HOTSPOT: don't beautify this code */ + object serializer extends SigmaSerializer[Parameter, Parameter] { + override def serialize(data: Parameter, w: SigmaByteWriter): Unit = { + import sigmastate.Operations.ConstantPlaceholderInfo._ + + serializeString(data.name, w) + serializeString(data.description, w) + w.putUInt(data.placeholder, indexArg) + } + + override def parse(r: SigmaByteReader): Parameter = { + val name = parseString(r) + val description = parseString(r) + val placeholder = r.getUInt().toInt + Parameter(name, description, placeholder) + } + } + + implicit val encoder: Encoder[Parameter] = Encoder.instance({ p => + Json.obj( + "name" -> Json.fromString(p.name), + "description" -> Json.fromString(p.description), + "placeholder" -> Json.fromInt(p.placeholder) + ) + }) + + implicit val decoder: Decoder[Parameter] = Decoder.instance({ cursor => + for { + name <- cursor.downField("name").as[String] + description <- cursor.downField("description").as[String] + placeholder <- cursor.downField("placeholder").as[Int] + } yield new Parameter(name, description, placeholder) + }) +} + +case class ContractTemplate( + treeVersion: Option[Byte], + name: String, + description: String, + constTypes: IndexedSeq[SType], + constValues: Option[IndexedSeq[Option[SType#WrappedType]]], + parameters: IndexedSeq[Parameter], + expressionTree: SigmaPropValue +) { + + validate() + + private def validate(): Unit = { + require(constValues.isEmpty || constValues.get.size == constTypes.size, + s"constValues must be empty or of same length as constTypes. Got ${constValues.get.size}, expected ${constTypes.size}") + require(parameters.size <= constTypes.size, "number of parameters must be <= number of constants") + + // Validate that no parameter is duplicated i.e. points to the same position & also to a valid constant. + // Also validate that no two parameters exist with the same name. + val paramNames = mutable.Set[String]() + val paramIndices = this.parameters.map(p => { + require(p.placeholder >= 0 && p.placeholder < constTypes.size, + s"parameter placeholder must be in range [0, ${constTypes.size})") + require(!paramNames.contains(p.name), + s"parameter names must be unique. Found duplicate parameters with name ${p.name}") + paramNames += p.name + p.placeholder + }).toSet + require(paramIndices.size == parameters.size, "multiple parameters point to the same placeholder") + + // Validate that any constValues[i] = None has a parameter. + if (constValues.isEmpty) { + require(parameters.size == constTypes.size, + "all the constants must be provided via parameter since constValues == None") + } else { + cfor(0)(_ < constTypes.size, _ + 1) { i => + require(constValues.get(i).isDefined || paramIndices.contains(i), + s"placeholder ${i} does not have a default value and absent from parameter as well") + } + } + } + + def applyTemplate(paramValues: Map[String, Values.Constant[SType]]): Values.ErgoTree = { + val nConsts = constTypes.size + val requiredParameterNames = + this.parameters + .filter(p => constValues.isEmpty || constValues.get(p.placeholder).isEmpty) + .map(p => p.name) + requiredParameterNames.foreach(name => require( + paramValues.contains(name), + s"value for parameter ${name} was not provided while it does not have a default value.")) + + val paramIndices = this.parameters.map(p => p.placeholder).toSet + val constants = safeNewArray[Constant[SType]](nConsts) + cfor(0)(_ < nConsts, _ + 1) { i => + if (paramIndices.contains(i) && paramValues.contains(parameters(i).name)) { + val paramValue = paramValues(parameters(i).name) + require(paramValue.tpe == constTypes(i), + s"parameter type mismatch, expected ${constTypes(i)}, got ${paramValue.tpe}") + constants(i) = StdSigmaBuilder.mkConstant(paramValue.value, constTypes(i)) + } else { + constants(i) = StdSigmaBuilder.mkConstant(constValues.get(i).get, constTypes(i)) + } + } + + ErgoTree( + ErgoTree.ConstantSegregationHeader, + constants, + this.expressionTree + ) + } + + /** The default equality of case class is overridden to exclude `complexity`. */ + override def canEqual(that: Any): Boolean = that.isInstanceOf[ContractTemplate] + + override def hashCode(): Int = Objects.hash(treeVersion, name, description, constTypes, constValues, parameters, expressionTree) + + override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || + ((obj.asInstanceOf[AnyRef] != null) && (obj match { + case other: ContractTemplate => + other.treeVersion == treeVersion && other.name == name && other.description == description && other.constTypes == constTypes && other.constValues == constValues && other.parameters == parameters && other.expressionTree == expressionTree + case _ => false + })) +} + +object ContractTemplate { + def apply(name: String, + description: String, + constTypes: IndexedSeq[SType], + constValues: Option[IndexedSeq[Option[SType#WrappedType]]], + parameters: IndexedSeq[Parameter], + expressionTree: SigmaPropValue): ContractTemplate = { + new ContractTemplate(None, name, description, constTypes, constValues, parameters, expressionTree) + } + + /** HOTSPOT: don't beautify this code */ + object serializer extends SigmaSerializer[ContractTemplate, ContractTemplate] { + + private def serializeString(s: String, w: SigmaByteWriter): Unit = { + val bytes = s.getBytes(StandardCharsets.UTF_8) + w.putUInt(bytes.length) + w.putBytes(bytes) + } + + private def parseString(r: SigmaByteReader): String = { + val length = r.getUInt().toInt + new String(r.getBytes(length), StandardCharsets.UTF_8) + } + + override def serialize(data: ContractTemplate, w: SigmaByteWriter): Unit = { + w.putOption(data.treeVersion)(_.putUByte(_)) + serializeString(data.name, w) + serializeString(data.description, w) + + val nConstants = data.constTypes.length + w.putUInt(nConstants) + cfor(0)(_ < nConstants, _ + 1) { i => + TypeSerializer.serialize(data.constTypes(i), w) + } + w.putOption(data.constValues)((_, values) => { + cfor(0)(_ < nConstants, _ + 1) { i => + w.putOption(values(i))((_, const) => + DataSerializer.serialize(const, data.constTypes(i), w)) + } + }) + + val nParameters = data.parameters.length + w.putUInt(nParameters) + cfor(0)(_ < nParameters, _ + 1) { i => + Parameter.serializer.serialize(data.parameters(i), w) + } + + val expressionTreeWriter = SigmaSerializer.startWriter() + ValueSerializer.serialize(data.expressionTree, expressionTreeWriter) + val expressionBytes = expressionTreeWriter.toBytes + w.putUInt(expressionBytes.length) + w.putBytes(expressionBytes) + } + + override def parse(r: SigmaByteReader): ContractTemplate = { + val treeVersion = r.getOption(r.getUByte().toByte) + val name = parseString(r) + val description = parseString(r) + + val nConstants = r.getUInt().toInt + val constTypes: IndexedSeq[SType] = { + if (nConstants > 0) { + // HOTSPOT:: allocate new array only if it is not empty + val res = safeNewArray[SType](nConstants) + cfor(0)(_ < nConstants, _ + 1) { i => + res(i) = TypeSerializer.deserialize(r) + } + res + } else { + SType.EmptySeq + } + } + val constValues: Option[IndexedSeq[Option[SType#WrappedType]]] = r.getOption((() => { + if (nConstants > 0) { + // HOTSPOT:: allocate new array only if it is not empty + val res = safeNewArray[Option[SType#WrappedType]](nConstants) + cfor(0)(_ < nConstants, _ + 1) { i => + res(i) = r.getOption((() => DataSerializer.deserialize(constTypes(i), r))()) + } + res + } else { + Array.empty[Option[SType#WrappedType]] + } + })()) + + val nParameters = r.getUInt().toInt + val parameters: IndexedSeq[Parameter] = { + if (nParameters > 0) { + val res = safeNewArray[Parameter](nParameters) + cfor(0)(_ < nParameters, _ + 1) { i => + res(i) = Parameter.serializer.parse(r) + } + res + } else { + Parameter.EmptySeq + } + } + + // Populate constants in constantStore so that the expressionTree can be deserialized. + val constants = constTypes.indices.map(i => { + val t = constTypes(i) + DeserializationSigmaBuilder.mkConstant(defaultOf(t), t) + }) + constants.foreach(c => r.constantStore.put(c)(DeserializationSigmaBuilder)) + + val _ = r.getUInt().toInt + val expressionTree = ValueSerializer.deserialize(r) + CheckDeserializedScriptIsSigmaProp(expressionTree) + + ContractTemplate( + treeVersion, name, description, + constTypes, constValues, parameters, + expressionTree.toSigmaProp) + } + } + + object jsonEncoder extends JsonCodecs { + + implicit val sTypeEncoder: Encoder[SType] = Encoder.instance({ tpe => + Json.fromString(tpe.toTermString) + }) + + implicit val sTypeDecoder: Decoder[SType] = Decoder.instance({ implicit cursor => + fromTry(Try.apply(SigmaParser.parseType(cursor.value.asString.get))) + }) + + implicit val encoder: Encoder[ContractTemplate] = Encoder.instance({ ct => + val expressionTreeWriter = SigmaSerializer.startWriter() + ValueSerializer.serialize(ct.expressionTree, expressionTreeWriter) + + Json.obj( + "treeVersion" -> ct.treeVersion.asJson, + "name" -> Json.fromString(ct.name), + "description" -> Json.fromString(ct.description), + "constTypes" -> ct.constTypes.asJson, + "constValues" -> ( + if (ct.constValues.isEmpty) Json.Null + else ct.constValues.get.indices.map(i => ct.constValues.get(i) match { + case Some(const) => DataJsonEncoder.encodeData(const, ct.constTypes(i)) + case None => Json.Null + }).asJson), + "parameters" -> ct.parameters.asJson, + "expressionTree" -> expressionTreeWriter.toBytes.asJson + ) + }) + + implicit val decoder: Decoder[ContractTemplate] = Decoder.instance({ implicit cursor => + val constTypesResult = cursor.downField("constTypes").as[IndexedSeq[SType]] + val expressionTreeBytesResult = cursor.downField("expressionTree").as[Array[Byte]] + (constTypesResult, expressionTreeBytesResult) match { + case (Right(constTypes), Right(expressionTreeBytes)) => + val constValuesOpt = { + val constValuesJson = cursor.downField("constValues").focus.get + if (constValuesJson != Json.Null) { + val jsonValues = constValuesJson.asArray.get + Some(jsonValues.indices.map( + i => if (jsonValues(i) == Json.Null) None + else Some(DataJsonEncoder.decodeData(jsonValues(i), constTypes(i))))) + } else { + None + } + } + + // Populate synthetic constants in the constant store for deserialization of expression tree. + val r = SigmaSerializer.startReader(expressionTreeBytes) + val constants = constTypes.indices.map(i => { + val t = constTypes(i) + DeserializationSigmaBuilder.mkConstant(defaultOf(t), t) + }) + constants.foreach(c => r.constantStore.put(c)(DeserializationSigmaBuilder)) + + for { + treeVersion <- cursor.downField("treeVersion").as[Option[Byte]] + name <- cursor.downField("name").as[String] + description <- cursor.downField("description").as[String] + parameters <- cursor.downField("parameters").as[IndexedSeq[Parameter]] + } yield new ContractTemplate( + treeVersion, + name, + description, + constTypes, + constValuesOpt, + parameters, + ValueSerializer.deserialize(r).toSigmaProp) + case _ => Left(DecodingFailure("Failed to decode contract template", cursor.history)) + } + }) + } + + /** Synthetic default value for the type. + * Used for deserializing contract templates. + */ + private def defaultOf[T <: SType](tpe: T): T#WrappedType = { + val res = (tpe match { + case SBoolean => false + case SByte => 0.toByte + case SShort => 0.toShort + case SInt => 0 + case SLong => 0.toLong + case SBigInt => BigInt(0) + case SGroupElement => CGroupElement(CryptoConstants.dlogGroup.identity) + case SSigmaProp => CSigmaProp(TrivialProp(false)) + case SBox => CostingBox( + new ErgoBox( + 0L, + new ErgoTree( + 0.toByte, + Vector(), + Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) + ), + Colls.emptyColl, + Map(), + ModifierId @@ ("synthetic_transaction_id"), + 0.toShort, + 0 + ) + ) + case c: SCollectionType[_] => SCollectionType(c.elemType) + case _: SOption[_] => None + case _: STuple => STuple(SInt, SLong) + case _: SFunc => SFunc(SBoolean, NoType) + case SContext => CostingDataContext( + _dataInputs = Colls.emptyColl, + headers = Colls.emptyColl, + preHeader = CPreHeader( + 0.toByte, + Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), + -755484979487531112L, + 9223372036854775807L, + 11, + Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + Helpers.decodeBytes("007f00") + ), + inputs = Colls.emptyColl, + outputs = Colls.emptyColl, + height = 11, + selfBox = CostingBox( + new ErgoBox( + 0L, + new ErgoTree( + 0.toByte, + Vector(), + Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) + ), + Colls.emptyColl, + Map(), + ModifierId @@ ("synthetic_transaction_id"), + 0.toShort, + 0 + ) + ), + selfIndex = 0, + lastBlockUtxoRootHash = CAvlTree( + AvlTreeData( + ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), + AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = true), + 1211925457, + None + ) + ), + _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + vars = Colls.emptyColl, + activatedScriptVersion = 0.toByte, + currentErgoTreeVersion = 0.toByte + ) + case SAvlTree => CAvlTree( + AvlTreeData( + ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), + AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = false), + 2147483647, + None + ) + ) + case SGlobal => CostingSigmaDslBuilder + case SHeader => CHeader( + Colls.fromArray(Blake2b256("Header.id")), + 0, + Colls.fromArray(Blake2b256("Header.parentId")), + Colls.fromArray(Blake2b256("ADProofsRoot")), + CAvlTree( + AvlTreeData( + ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), + AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = false), + 2147483647, + None + ) + ), + Colls.fromArray(Blake2b256("transactionsRoot")), + timestamp = 0, + nBits = 0, + height = 0, + extensionRoot = Colls.fromArray(Blake2b256("transactionsRoot")), + minerPk = SigmaDsl.groupGenerator, + powOnetimePk = SigmaDsl.groupGenerator, + powNonce = Colls.fromArray(Array[Byte](0, 1, 2, 3, 4, 5, 6, 7)), + powDistance = SigmaDsl.BigInt(BigInt("1405498250268750867257727119510201256371618473728619086008183115260323").bigInteger), + votes = Colls.fromArray(Array[Byte](0, 1, 2)) + ) + case SPreHeader => CPreHeader( + 0.toByte, + Helpers.decodeBytes("7fff7fdd6f62018bae0001006d9ca888ff7f56ff8006573700a167f17f2c9f40"), + 6306290372572472443L, + -3683306095029417063L, + 1, + Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b"), + Helpers.decodeBytes("ff8087") + ) + case SUnit => () + case _ => sys.error(s"Unknown type $tpe") + }).asInstanceOf[T#WrappedType] + res + } +} \ No newline at end of file diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala index e81faeb889..35c80b1433 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala @@ -46,7 +46,7 @@ object DataJsonEncoder { ) } - private def encodeData[T <: SType](v: T#WrappedType, tpe: T): Json = tpe match { + def encodeData[T <: SType](v: T#WrappedType, tpe: T): Json = tpe match { case SUnit => Json.fromFields(ArraySeq.empty) case SBoolean => v.asInstanceOf[Boolean].asJson case SByte => v.asInstanceOf[Byte].asJson @@ -159,7 +159,7 @@ object DataJsonEncoder { } } - private def decodeData[T <: SType](json: Json, tpe: T): (T#WrappedType) = { + def decodeData[T <: SType](json: Json, tpe: T): (T#WrappedType) = { val res = (tpe match { case SUnit => () case SBoolean => json.asBoolean.get diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala new file mode 100644 index 0000000000..3669b8d427 --- /dev/null +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -0,0 +1,344 @@ +package org.ergoplatform.sdk + +import sigmastate.Values._ +import sigmastate.eval.CBigInt +import sigmastate.lang.TransformingSigmaBuilder.mkConstant +import special.sigma.ContractsTestkit +import sigmastate.serialization.SerializationSpecification +import sigmastate.SType +import sigmastate._ +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigmastate.helpers.NegativeTesting + +import java.math.BigInteger +import sigmastate.EQ +import org.scalatest.compatible.Assertion +import sigmastate.serialization.SigmaSerializer + +class ContractTemplateSpecification extends SerializationSpecification + with ScalaCheckPropertyChecks + with ContractsTestkit + with NegativeTesting + with CrossVersionProps { + object JsonCodecs extends JsonCodecs + + private def contractTemplateNameInTests: String = "TestContractTemplate" + private def contractTemplateDescriptionInTests: String = "TestContractTemplateDescription" + + def jsonRoundtrip[T <: SType](obj: ContractTemplate) = { + val json = ContractTemplate.jsonEncoder.encoder(obj) + val res = ContractTemplate.jsonEncoder.decoder(json.hcursor).right.get + res shouldBe obj + } + + def serializationRoundTrip(template: ContractTemplate): Assertion = { + val w = SigmaSerializer.startWriter() + ContractTemplate.serializer.serialize(template, w) + val bytes = w.toBytes + val r = SigmaSerializer.startReader(bytes) + val res2 = ContractTemplate.serializer.parse(r) + res2 shouldEqual template + } + + private def createParameter(name: String, placeholder: Int): Parameter = { + Parameter( + name, + s"${name}_description", + placeholder + ) + } + + private def createContractTemplate(constTypes: IndexedSeq[SType], + constValues: Option[IndexedSeq[Option[SType#WrappedType]]], + parameters: IndexedSeq[Parameter], + expressionTree: SigmaPropValue): ContractTemplate = { + ContractTemplate( + contractTemplateNameInTests, + contractTemplateDescriptionInTests, + constTypes, + constValues, + parameters, + expressionTree + ) + } + + property("unequal length of constTypes and constValues") { + assertExceptionThrown( + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), Some(20.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + IndexedSeq( + createParameter("p1", 0), + createParameter("p2", 1), + createParameter("p3", 2)), + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + t => + t.isInstanceOf[IllegalArgumentException] && + t.getMessage.contains("constValues must be empty or of same length as constTypes. Got 2, expected 3")) + } + + property("more parameters than constants") { + assertExceptionThrown( + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + IndexedSeq( + createParameter("p1", 0), + createParameter("p2", 1), + createParameter("p3", 2), + createParameter("p4", 3)), + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + t => + t.isInstanceOf[IllegalArgumentException] && + t.getMessage.contains("number of parameters must be <= number of constants")) + } + + property("invalid parameter placeholder") { + assertExceptionThrown( + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + IndexedSeq( + createParameter("p1", 0), + createParameter("p2", 1), + createParameter("p3", 100)), + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + t => + t.isInstanceOf[IllegalArgumentException] && + t.getMessage.contains("parameter placeholder must be in range [0, 3)")) + } + + property("duplicate parameter placeholder") { + assertExceptionThrown( + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + IndexedSeq( + createParameter("p1", 0), + createParameter("p2", 1), + createParameter("p3", 1)), + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + t => + t.isInstanceOf[IllegalArgumentException] && + t.getMessage.contains("multiple parameters point to the same placeholder")) + } + + property("duplicate parameter names") { + assertExceptionThrown( + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + IndexedSeq( + createParameter("duplicate_name", 0), + createParameter("p2", 1), + createParameter("duplicate_name", 2)), + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + t => + t.isInstanceOf[IllegalArgumentException] && + t.getMessage.contains("parameter names must be unique. Found duplicate parameters with name duplicate_name")) + } + + property("placeholder without default value and parameter") { + assertExceptionThrown( + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(None, Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + IndexedSeq( + createParameter("p2", 1), + createParameter("p3", 2)), + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + t => + t.isInstanceOf[IllegalArgumentException] && + t.getMessage.contains("placeholder 0 does not have a default value and absent from parameter as well")) + } + + property("applyTemplate") { + val parameters = IndexedSeq( + createParameter("p1", 0), + createParameter("p2", 1), + createParameter("p3", 2)) + val expressionTrees = IndexedSeq( + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp, + EQ(Plus(ConstantPlaceholder(0, SType.typeInt), + ConstantPlaceholder(1, SType.typeInt)), + ConstantPlaceholder(2, SType.typeInt)).toSigmaProp, + EQ(Plus(CBigInt(BigInteger.valueOf(10L)), BigIntConstant(20L)), BigIntConstant(30L)).toSigmaProp + ) + val templates = Seq( + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + expressionTrees(0) + ), + createContractTemplate( + IndexedSeq(SType.typeInt, SType.typeInt, SType.typeInt).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10), None, Some(30)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + expressionTrees(1) + ), + createContractTemplate( + SType.EmptySeq, + None, + Parameter.EmptySeq, + expressionTrees(2) + ) + ) + val templateValues = Seq( + Map("p1" -> ByteConstant(10.toByte), "p2" -> ByteConstant(40.toByte), "p3" -> ByteConstant(50.toByte)), + Map("p1" -> IntConstant(10), "p2" -> IntConstant(20)), + Map[String, Constant[SType]](), + ) + val expectedErgoTree = Seq( + ErgoTree( + ErgoTree.ConstantSegregationHeader, + IndexedSeq( + ByteConstant(10.toByte), + ByteConstant(40.toByte), + ByteConstant(50.toByte) + ), + expressionTrees(0) + ), + ErgoTree( + ErgoTree.ConstantSegregationHeader, + IndexedSeq( + IntConstant(10), + IntConstant(20), + IntConstant(30) + ), + expressionTrees(1) + ), + ErgoTree( + ErgoTree.ConstantSegregationHeader, + Constant.EmptySeq, + expressionTrees(2) + ) + ) + + templates.indices.foreach(i => + templates(i).applyTemplate(templateValues(i)) shouldEqual expectedErgoTree(i) + ) + } + + property("(de)serialization round trip") { + val parameters = IndexedSeq( + createParameter("p1", 0), + createParameter("p2", 1), + createParameter("p3", 2)) + val templates = Seq( + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), None, Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + createContractTemplate( + IndexedSeq(SType.typeShort, SType.typeShort, SType.typeShort).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toShort), Some(20.toShort), Some(30.toShort)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + EQ(Plus(ConstantPlaceholder(0, SType.typeShort), + ConstantPlaceholder(1, SType.typeShort)), + ConstantPlaceholder(2, SType.typeShort)).toSigmaProp + ), + createContractTemplate( + IndexedSeq(SType.typeInt, SType.typeInt, SType.typeInt).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10), Some(20), Some(30)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + EQ(Plus(ConstantPlaceholder(0, SType.typeInt), + ConstantPlaceholder(1, SType.typeInt)), + ConstantPlaceholder(2, SType.typeInt)).toSigmaProp + ), + createContractTemplate( + SType.EmptySeq, + None, + Parameter.EmptySeq, + EQ(Plus(CBigInt(BigInteger.valueOf(10L)), BigIntConstant(20L)), BigIntConstant(30L)).toSigmaProp + ) + ) + + templates.foreach { template => + serializationRoundTrip(template) + } + } + + property("Data Json serialization round trip") { + val parameters = IndexedSeq( + createParameter("p1", 0), + createParameter("p2", 1), + createParameter("p3", 2)) + val templates = Seq( + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + createContractTemplate( + IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toByte), None, Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + EQ(Plus(ConstantPlaceholder(0, SType.typeByte), + ConstantPlaceholder(1, SType.typeByte)), + ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + ), + createContractTemplate( + IndexedSeq(SType.typeShort, SType.typeShort, SType.typeShort).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10.toShort), Some(20.toShort), Some(30.toShort)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + EQ(Plus(ConstantPlaceholder(0, SType.typeShort), + ConstantPlaceholder(1, SType.typeShort)), + ConstantPlaceholder(2, SType.typeShort)).toSigmaProp + ), + createContractTemplate( + IndexedSeq(SType.typeInt, SType.typeInt, SType.typeInt).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(Some(10), Some(20), Some(30)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + EQ(Plus(ConstantPlaceholder(0, SType.typeInt), + ConstantPlaceholder(1, SType.typeInt)), + ConstantPlaceholder(2, SType.typeInt)).toSigmaProp + ), + createContractTemplate( + SType.EmptySeq, + None, + Parameter.EmptySeq, + EQ(Plus(CBigInt(BigInteger.valueOf(10L)), BigIntConstant(20L)), BigIntConstant(30L)).toSigmaProp + ) + ) + + templates.foreach { template => + jsonRoundtrip(template) + } + } +} \ No newline at end of file From 858d0c1b99ffa9d8c18d4891144d95762cf8f598 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Thu, 23 Feb 2023 20:11:07 +0530 Subject: [PATCH 09/42] fix scala 2.11 compilation --- .../src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 6041071605..b688c4a83e 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -42,7 +42,8 @@ import sigmastate.util.safeNewArray import special.collection.Coll import java.nio.charset.StandardCharsets -import scala.collection.immutable.IndexedSeq +import scala.collection.mutable +import scala.collection.compat.immutable.ArraySeq import scala.util.Try import sigmastate.SType import sigmastate.lang.StdSigmaBuilder From db646804958536abcf256dce924d7b32c9ce7fd4 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Fri, 24 Feb 2023 22:34:55 +0530 Subject: [PATCH 10/42] cleanup defaultOf --- .../ergoplatform/sdk/ContractTemplate.scala | 117 ++++++------------ .../sdk/ContractTemplateSpecification.scala | 2 +- 2 files changed, 40 insertions(+), 79 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index b688c4a83e..e958d898df 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -210,7 +210,6 @@ object ContractTemplate { new ContractTemplate(None, name, description, constTypes, constValues, parameters, expressionTree) } - /** HOTSPOT: don't beautify this code */ object serializer extends SigmaSerializer[ContractTemplate, ContractTemplate] { private def serializeString(s: String, w: SigmaByteWriter): Unit = { @@ -262,7 +261,6 @@ object ContractTemplate { val nConstants = r.getUInt().toInt val constTypes: IndexedSeq[SType] = { if (nConstants > 0) { - // HOTSPOT:: allocate new array only if it is not empty val res = safeNewArray[SType](nConstants) cfor(0)(_ < nConstants, _ + 1) { i => res(i) = TypeSerializer.deserialize(r) @@ -274,7 +272,6 @@ object ContractTemplate { } val constValues: Option[IndexedSeq[Option[SType#WrappedType]]] = r.getOption((() => { if (nConstants > 0) { - // HOTSPOT:: allocate new array only if it is not empty val res = safeNewArray[Option[SType#WrappedType]](nConstants) cfor(0)(_ < nConstants, _ + 1) { i => res(i) = r.getOption((() => DataSerializer.deserialize(constTypes(i), r))()) @@ -393,6 +390,35 @@ object ContractTemplate { * Used for deserializing contract templates. */ private def defaultOf[T <: SType](tpe: T): T#WrappedType = { + val syntheticBox = new ErgoBox( + 0L, + new ErgoTree( + 0.toByte, + Vector(), + Right(CSigmaProp(TrivialProp(false))) + ), + Colls.emptyColl, + Map(), + ModifierId @@ ("synthetic_transaction_id"), + 0.toShort, + 0 + ) + val syntheticPreHeader = CPreHeader( + 0.toByte, + Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), + -755484979487531112L, + 9223372036854775807L, + 11, + Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + Helpers.decodeBytes("007f00") + ) + val syntheticAvlTree = AvlTreeData( + ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), + AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = false), + 2147483647, + None + ) + val res = (tpe match { case SBoolean => false case SByte => 0.toByte @@ -402,21 +428,7 @@ object ContractTemplate { case SBigInt => BigInt(0) case SGroupElement => CGroupElement(CryptoConstants.dlogGroup.identity) case SSigmaProp => CSigmaProp(TrivialProp(false)) - case SBox => CostingBox( - new ErgoBox( - 0L, - new ErgoTree( - 0.toByte, - Vector(), - Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) - ), - Colls.emptyColl, - Map(), - ModifierId @@ ("synthetic_transaction_id"), - 0.toShort, - 0 - ) - ) + case SBox => CostingBox(syntheticBox) case c: SCollectionType[_] => SCollectionType(c.elemType) case _: SOption[_] => None case _: STuple => STuple(SInt, SLong) @@ -424,89 +436,38 @@ object ContractTemplate { case SContext => CostingDataContext( _dataInputs = Colls.emptyColl, headers = Colls.emptyColl, - preHeader = CPreHeader( - 0.toByte, - Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), - -755484979487531112L, - 9223372036854775807L, - 11, - Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), - Helpers.decodeBytes("007f00") - ), + preHeader = syntheticPreHeader, inputs = Colls.emptyColl, outputs = Colls.emptyColl, - height = 11, - selfBox = CostingBox( - new ErgoBox( - 0L, - new ErgoTree( - 0.toByte, - Vector(), - Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) - ), - Colls.emptyColl, - Map(), - ModifierId @@ ("synthetic_transaction_id"), - 0.toShort, - 0 - ) - ), + height = 1, + selfBox = CostingBox(syntheticBox), selfIndex = 0, - lastBlockUtxoRootHash = CAvlTree( - AvlTreeData( - ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), - AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = true), - 1211925457, - None - ) - ), + lastBlockUtxoRootHash = CAvlTree(syntheticAvlTree), _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), vars = Colls.emptyColl, activatedScriptVersion = 0.toByte, currentErgoTreeVersion = 0.toByte ) - case SAvlTree => CAvlTree( - AvlTreeData( - ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), - AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = false), - 2147483647, - None - ) - ) + case SAvlTree => CAvlTree(syntheticAvlTree) case SGlobal => CostingSigmaDslBuilder case SHeader => CHeader( Colls.fromArray(Blake2b256("Header.id")), 0, Colls.fromArray(Blake2b256("Header.parentId")), Colls.fromArray(Blake2b256("ADProofsRoot")), - CAvlTree( - AvlTreeData( - ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), - AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = false), - 2147483647, - None - ) - ), + CAvlTree(syntheticAvlTree), Colls.fromArray(Blake2b256("transactionsRoot")), timestamp = 0, - nBits = 0, + nBits = 0, height = 0, extensionRoot = Colls.fromArray(Blake2b256("transactionsRoot")), minerPk = SigmaDsl.groupGenerator, powOnetimePk = SigmaDsl.groupGenerator, powNonce = Colls.fromArray(Array[Byte](0, 1, 2, 3, 4, 5, 6, 7)), - powDistance = SigmaDsl.BigInt(BigInt("1405498250268750867257727119510201256371618473728619086008183115260323").bigInteger), + powDistance = SigmaDsl.BigInt(BigInt("0").bigInteger), votes = Colls.fromArray(Array[Byte](0, 1, 2)) ) - case SPreHeader => CPreHeader( - 0.toByte, - Helpers.decodeBytes("7fff7fdd6f62018bae0001006d9ca888ff7f56ff8006573700a167f17f2c9f40"), - 6306290372572472443L, - -3683306095029417063L, - 1, - Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b"), - Helpers.decodeBytes("ff8087") - ) + case SPreHeader => syntheticPreHeader case SUnit => () case _ => sys.error(s"Unknown type $tpe") }).asInstanceOf[T#WrappedType] diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 3669b8d427..3a3e5ee3f1 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -207,7 +207,7 @@ class ContractTemplateSpecification extends SerializationSpecification val templateValues = Seq( Map("p1" -> ByteConstant(10.toByte), "p2" -> ByteConstant(40.toByte), "p3" -> ByteConstant(50.toByte)), Map("p1" -> IntConstant(10), "p2" -> IntConstant(20)), - Map[String, Constant[SType]](), + Map.empty[String, Constant[SType]], ) val expectedErgoTree = Seq( ErgoTree( From 2a8303d0f8f1512fe9d298c42cf4888c2d845edc Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Sun, 12 Mar 2023 20:34:57 +0530 Subject: [PATCH 11/42] Optimize imports & incorporate trivial code review suggestions --- .../ergoplatform/sdk/ContractTemplate.scala | 65 +++++-------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index e958d898df..12ba9ae320 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -1,68 +1,35 @@ package org.ergoplatform.sdk -import sigmastate.Values - +import debox.cfor import io.circe.syntax.EncoderOps import io.circe.{Decoder, DecodingFailure, Encoder, Json} - -import cats.syntax.either._ -import java.math.BigInteger -import java.util.{Arrays, Objects} +import org.ergoplatform.ErgoBox import org.ergoplatform.settings.ErgoAlgos -import org.ergoplatform.validation.ValidationException import org.ergoplatform.validation.ValidationRules.CheckDeserializedScriptIsSigmaProp -import scalan.{Nullable, RType} -import scalan.util.CollectionUtil._ -import sigmastate.SCollection.{SByteArray, SIntArray} -import sigmastate.serialization.{ConstantStore, OpCodes, _} -import sigmastate.serialization.OpCodes._ -import sigmastate.TrivialProp.{FalseProp, TrueProp} -import sigmastate.Values.ErgoTree.substConstants -import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.basics.ProveDHTuple -import sigmastate.lang.Terms._ -import sigmastate.utxo._ -import sigmastate.eval._ -import sigmastate.eval.Extensions._ -import sigmastate._ -import scalan.util.Extensions.ByteOps -import sigmastate.interpreter.ErgoTreeEvaluator._ +import scorex.crypto.authds.ADDigest +import scorex.crypto.hash.Blake2b256 +import scorex.util.ModifierId import sigmastate.Values._ -import debox.cfor - -import scala.language.implicitConversions -import scala.reflect.ClassTag -import sigmastate.lang.CheckingSigmaBuilder._ -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.serialization.transformers.ProveDHTupleSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import special.sigma.{AvlTree, Header, PreHeader, _} -import sigmastate.lang.{SigmaParser, SourceContext} +import sigmastate._ +import sigmastate.basics.CryptoConstants +import sigmastate.eval._ +import sigmastate.lang.{DeserializationSigmaBuilder, SigmaParser, StdSigmaBuilder} +import sigmastate.serialization._ import sigmastate.util.safeNewArray -import special.collection.Coll +import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} +import special.sigma._ import java.nio.charset.StandardCharsets +import java.util.Objects import scala.collection.mutable -import scala.collection.compat.immutable.ArraySeq +import scala.language.implicitConversions import scala.util.Try -import sigmastate.SType -import sigmastate.lang.StdSigmaBuilder -import sigmastate.lang.DeserializationSigmaBuilder -import sigmastate.basics.CryptoConstants -import org.ergoplatform.ErgoBox -import scorex.util.ModifierId -import sigmastate.utils.Helpers -import scorex.crypto.authds.ADDigest -import scorex.crypto.hash.Blake2b256 - case class Parameter( name: String, description: String, placeholder: Int -) { - -} +) object Parameter { @@ -136,7 +103,7 @@ case class ContractTemplate( // Also validate that no two parameters exist with the same name. val paramNames = mutable.Set[String]() val paramIndices = this.parameters.map(p => { - require(p.placeholder >= 0 && p.placeholder < constTypes.size, + require(constTypes.isDefinedAt(p.placeholder), s"parameter placeholder must be in range [0, ${constTypes.size})") require(!paramNames.contains(p.name), s"parameter names must be unique. Found duplicate parameters with name ${p.name}") From 41a3766af202b156f2021b8e3fe5fab7ab67c1df Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Sun, 12 Mar 2023 20:46:13 +0530 Subject: [PATCH 12/42] Rename placeholder to constantIndex in Parameter --- .../ergoplatform/sdk/ContractTemplate.scala | 28 +++++++-------- .../sdk/ContractTemplateSpecification.scala | 34 ++++++++----------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 12ba9ae320..5c7c483141 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -28,7 +28,7 @@ import scala.util.Try case class Parameter( name: String, description: String, - placeholder: Int + constantIndex: Int ) object Parameter { @@ -54,14 +54,14 @@ object Parameter { serializeString(data.name, w) serializeString(data.description, w) - w.putUInt(data.placeholder, indexArg) + w.putUInt(data.constantIndex, indexArg) } override def parse(r: SigmaByteReader): Parameter = { val name = parseString(r) val description = parseString(r) - val placeholder = r.getUInt().toInt - Parameter(name, description, placeholder) + val constantIndex = r.getUInt().toInt + Parameter(name, description, constantIndex) } } @@ -69,7 +69,7 @@ object Parameter { Json.obj( "name" -> Json.fromString(p.name), "description" -> Json.fromString(p.description), - "placeholder" -> Json.fromInt(p.placeholder) + "constantIndex" -> Json.fromInt(p.constantIndex) ) }) @@ -77,8 +77,8 @@ object Parameter { for { name <- cursor.downField("name").as[String] description <- cursor.downField("description").as[String] - placeholder <- cursor.downField("placeholder").as[Int] - } yield new Parameter(name, description, placeholder) + constantIndex <- cursor.downField("constantIndex").as[Int] + } yield new Parameter(name, description, constantIndex) }) } @@ -103,14 +103,14 @@ case class ContractTemplate( // Also validate that no two parameters exist with the same name. val paramNames = mutable.Set[String]() val paramIndices = this.parameters.map(p => { - require(constTypes.isDefinedAt(p.placeholder), - s"parameter placeholder must be in range [0, ${constTypes.size})") + require(constTypes.isDefinedAt(p.constantIndex), + s"parameter constantIndex must be in range [0, ${constTypes.size})") require(!paramNames.contains(p.name), s"parameter names must be unique. Found duplicate parameters with name ${p.name}") paramNames += p.name - p.placeholder + p.constantIndex }).toSet - require(paramIndices.size == parameters.size, "multiple parameters point to the same placeholder") + require(paramIndices.size == parameters.size, "multiple parameters point to the same constantIndex") // Validate that any constValues[i] = None has a parameter. if (constValues.isEmpty) { @@ -119,7 +119,7 @@ case class ContractTemplate( } else { cfor(0)(_ < constTypes.size, _ + 1) { i => require(constValues.get(i).isDefined || paramIndices.contains(i), - s"placeholder ${i} does not have a default value and absent from parameter as well") + s"constantIndex ${i} does not have a default value and absent from parameter as well") } } } @@ -128,13 +128,13 @@ case class ContractTemplate( val nConsts = constTypes.size val requiredParameterNames = this.parameters - .filter(p => constValues.isEmpty || constValues.get(p.placeholder).isEmpty) + .filter(p => constValues.isEmpty || constValues.get(p.constantIndex).isEmpty) .map(p => p.name) requiredParameterNames.foreach(name => require( paramValues.contains(name), s"value for parameter ${name} was not provided while it does not have a default value.")) - val paramIndices = this.parameters.map(p => p.placeholder).toSet + val paramIndices = this.parameters.map(p => p.constantIndex).toSet val constants = safeNewArray[Constant[SType]](nConsts) cfor(0)(_ < nConsts, _ + 1) { i => if (paramIndices.contains(i) && paramValues.contains(parameters(i).name)) { diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 3a3e5ee3f1..53111d5d27 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -1,19 +1,15 @@ package org.ergoplatform.sdk +import org.scalatest.compatible.Assertion +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import sigmastate.Values._ -import sigmastate.eval.CBigInt -import sigmastate.lang.TransformingSigmaBuilder.mkConstant -import special.sigma.ContractsTestkit -import sigmastate.serialization.SerializationSpecification -import sigmastate.SType import sigmastate._ -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigmastate.eval.CBigInt import sigmastate.helpers.NegativeTesting +import sigmastate.serialization.{SerializationSpecification, SigmaSerializer} +import special.sigma.ContractsTestkit import java.math.BigInteger -import sigmastate.EQ -import org.scalatest.compatible.Assertion -import sigmastate.serialization.SigmaSerializer class ContractTemplateSpecification extends SerializationSpecification with ScalaCheckPropertyChecks @@ -25,13 +21,13 @@ class ContractTemplateSpecification extends SerializationSpecification private def contractTemplateNameInTests: String = "TestContractTemplate" private def contractTemplateDescriptionInTests: String = "TestContractTemplateDescription" - def jsonRoundtrip[T <: SType](obj: ContractTemplate) = { + private def jsonRoundtrip[T <: SType](obj: ContractTemplate) = { val json = ContractTemplate.jsonEncoder.encoder(obj) val res = ContractTemplate.jsonEncoder.decoder(json.hcursor).right.get res shouldBe obj } - def serializationRoundTrip(template: ContractTemplate): Assertion = { + private def serializationRoundTrip(template: ContractTemplate): Assertion = { val w = SigmaSerializer.startWriter() ContractTemplate.serializer.serialize(template, w) val bytes = w.toBytes @@ -40,11 +36,11 @@ class ContractTemplateSpecification extends SerializationSpecification res2 shouldEqual template } - private def createParameter(name: String, placeholder: Int): Parameter = { + private def createParameter(name: String, constantIndex: Int): Parameter = { Parameter( name, s"${name}_description", - placeholder + constantIndex ) } @@ -99,7 +95,7 @@ class ContractTemplateSpecification extends SerializationSpecification t.getMessage.contains("number of parameters must be <= number of constants")) } - property("invalid parameter placeholder") { + property("invalid parameter constantIndex") { assertExceptionThrown( createContractTemplate( IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], @@ -114,10 +110,10 @@ class ContractTemplateSpecification extends SerializationSpecification ), t => t.isInstanceOf[IllegalArgumentException] && - t.getMessage.contains("parameter placeholder must be in range [0, 3)")) + t.getMessage.contains("parameter constantIndex must be in range [0, 3)")) } - property("duplicate parameter placeholder") { + property("duplicate parameter constantIndex") { assertExceptionThrown( createContractTemplate( IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], @@ -132,7 +128,7 @@ class ContractTemplateSpecification extends SerializationSpecification ), t => t.isInstanceOf[IllegalArgumentException] && - t.getMessage.contains("multiple parameters point to the same placeholder")) + t.getMessage.contains("multiple parameters point to the same constantIndex")) } property("duplicate parameter names") { @@ -153,7 +149,7 @@ class ContractTemplateSpecification extends SerializationSpecification t.getMessage.contains("parameter names must be unique. Found duplicate parameters with name duplicate_name")) } - property("placeholder without default value and parameter") { + property("constantIndex without default value and parameter") { assertExceptionThrown( createContractTemplate( IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], @@ -167,7 +163,7 @@ class ContractTemplateSpecification extends SerializationSpecification ), t => t.isInstanceOf[IllegalArgumentException] && - t.getMessage.contains("placeholder 0 does not have a default value and absent from parameter as well")) + t.getMessage.contains("constantIndex 0 does not have a default value and absent from parameter as well")) } property("applyTemplate") { From caa0bf517510b9e27e20ae43f608017285117d3a Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Sun, 12 Mar 2023 21:03:30 +0530 Subject: [PATCH 13/42] Move serializeString and parseString to SerializationUtils in sdk.utils --- .../ergoplatform/sdk/ContractTemplate.scala | 30 ++----------------- .../sdk/utils/SerializationUtils.scala | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 28 deletions(-) create mode 100644 sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/SerializationUtils.scala diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 5c7c483141..59a9f5b646 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -4,6 +4,7 @@ import debox.cfor import io.circe.syntax.EncoderOps import io.circe.{Decoder, DecodingFailure, Encoder, Json} import org.ergoplatform.ErgoBox +import org.ergoplatform.sdk.utils.SerializationUtils.{parseString, serializeString} import org.ergoplatform.settings.ErgoAlgos import org.ergoplatform.validation.ValidationRules.CheckDeserializedScriptIsSigmaProp import scorex.crypto.authds.ADDigest @@ -33,28 +34,15 @@ case class Parameter( object Parameter { - private def serializeString(s: String, w: SigmaByteWriter): Unit = { - val bytes = s.getBytes(StandardCharsets.UTF_8) - w.putUInt(bytes.length) - w.putBytes(bytes) - } - - private def parseString(r: SigmaByteReader): String = { - val length = r.getUInt().toInt - new String(r.getBytes(length), StandardCharsets.UTF_8) - } - /** Immutable empty IndexedSeq, can be used to save allocations in many places. */ val EmptySeq: IndexedSeq[Parameter] = Array.empty[Parameter] /** HOTSPOT: don't beautify this code */ object serializer extends SigmaSerializer[Parameter, Parameter] { override def serialize(data: Parameter, w: SigmaByteWriter): Unit = { - import sigmastate.Operations.ConstantPlaceholderInfo._ - serializeString(data.name, w) serializeString(data.description, w) - w.putUInt(data.constantIndex, indexArg) + w.putUInt(data.constantIndex) } override def parse(r: SigmaByteReader): Parameter = { @@ -154,9 +142,6 @@ case class ContractTemplate( ) } - /** The default equality of case class is overridden to exclude `complexity`. */ - override def canEqual(that: Any): Boolean = that.isInstanceOf[ContractTemplate] - override def hashCode(): Int = Objects.hash(treeVersion, name, description, constTypes, constValues, parameters, expressionTree) override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || @@ -179,17 +164,6 @@ object ContractTemplate { object serializer extends SigmaSerializer[ContractTemplate, ContractTemplate] { - private def serializeString(s: String, w: SigmaByteWriter): Unit = { - val bytes = s.getBytes(StandardCharsets.UTF_8) - w.putUInt(bytes.length) - w.putBytes(bytes) - } - - private def parseString(r: SigmaByteReader): String = { - val length = r.getUInt().toInt - new String(r.getBytes(length), StandardCharsets.UTF_8) - } - override def serialize(data: ContractTemplate, w: SigmaByteWriter): Unit = { w.putOption(data.treeVersion)(_.putUByte(_)) serializeString(data.name, w) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/SerializationUtils.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/SerializationUtils.scala new file mode 100644 index 0000000000..1530e1876a --- /dev/null +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/SerializationUtils.scala @@ -0,0 +1,30 @@ +package org.ergoplatform.sdk.utils + +import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} + +import java.nio.charset.StandardCharsets + +object SerializationUtils { + + /** + * Serialize the string as UTF_8. + * + * @param s the string to serialize + * @param w the writer to which the serialized string will be appended + */ + def serializeString(s: String, w: SigmaByteWriter): Unit = { + val bytes = s.getBytes(StandardCharsets.UTF_8) + w.putUInt(bytes.length) + w.putBytes(bytes) + } + + /** + * Parse a string from the reader + * @param r the reader from which the string will be parsed + * @return the parsed string + */ + def parseString(r: SigmaByteReader): String = { + val length = r.getUInt().toInt + new String(r.getBytes(length), StandardCharsets.UTF_8) + } +} From 9021ea67ceaa66e628d5cb2e9cb9c466e8b0af9a Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Mon, 13 Mar 2023 00:34:51 +0530 Subject: [PATCH 14/42] Merge ErgoTree version in the header in applyTemplate --- .../org/ergoplatform/sdk/ContractTemplate.scala | 13 ++++++++----- .../sdk/ContractTemplateSpecification.scala | 9 +++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 59a9f5b646..4b19b63fd6 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -20,7 +20,6 @@ import sigmastate.util.safeNewArray import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} import special.sigma._ -import java.nio.charset.StandardCharsets import java.util.Objects import scala.collection.mutable import scala.language.implicitConversions @@ -112,11 +111,14 @@ case class ContractTemplate( } } - def applyTemplate(paramValues: Map[String, Values.Constant[SType]]): Values.ErgoTree = { + def applyTemplate(version: Option[Byte], paramValues: Map[String, Values.Constant[SType]]): Values.ErgoTree = { + require(treeVersion.isDefined || version.isDefined, "ErgoTreeVersion must be provided to generate the ErgoTree.") val nConsts = constTypes.size val requiredParameterNames = - this.parameters - .filter(p => constValues.isEmpty || constValues.get(p.constantIndex).isEmpty) + if (constValues.isEmpty) + this.parameters.map(p => p.name) + else this.parameters + .filter(p => constValues.get(p.constantIndex).isEmpty) .map(p => p.name) requiredParameterNames.foreach(name => require( paramValues.contains(name), @@ -135,8 +137,9 @@ case class ContractTemplate( } } + val usedErgoTreeVersion = if (version.isDefined) version.get else treeVersion.get ErgoTree( - ErgoTree.ConstantSegregationHeader, + (ErgoTree.ConstantSegregationHeader | usedErgoTreeVersion).toByte, constants, this.expressionTree ) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 53111d5d27..aff2a41662 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -205,9 +205,10 @@ class ContractTemplateSpecification extends SerializationSpecification Map("p1" -> IntConstant(10), "p2" -> IntConstant(20)), Map.empty[String, Constant[SType]], ) + val ergoTreeVersion = (ErgoTree.ConstantSegregationHeader | ergoTreeVersionInTests | ErgoTree.SizeFlag).toByte val expectedErgoTree = Seq( ErgoTree( - ErgoTree.ConstantSegregationHeader, + ergoTreeVersion, IndexedSeq( ByteConstant(10.toByte), ByteConstant(40.toByte), @@ -216,7 +217,7 @@ class ContractTemplateSpecification extends SerializationSpecification expressionTrees(0) ), ErgoTree( - ErgoTree.ConstantSegregationHeader, + ergoTreeVersion, IndexedSeq( IntConstant(10), IntConstant(20), @@ -225,14 +226,14 @@ class ContractTemplateSpecification extends SerializationSpecification expressionTrees(1) ), ErgoTree( - ErgoTree.ConstantSegregationHeader, + ergoTreeVersion, Constant.EmptySeq, expressionTrees(2) ) ) templates.indices.foreach(i => - templates(i).applyTemplate(templateValues(i)) shouldEqual expectedErgoTree(i) + templates(i).applyTemplate(Some((ergoTreeVersionInTests | ErgoTree.SizeFlag).toByte), templateValues(i)) shouldEqual expectedErgoTree(i) ) } From 4bcc990a1b043e3af6e3387437afc25f66a465da Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Mon, 13 Mar 2023 00:46:02 +0530 Subject: [PATCH 15/42] Replace usage of ValidationRules with direct typecheck and throw SerializationException --- .../main/scala/org/ergoplatform/sdk/ContractTemplate.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 4b19b63fd6..6eb2176f16 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -6,7 +6,6 @@ import io.circe.{Decoder, DecodingFailure, Encoder, Json} import org.ergoplatform.ErgoBox import org.ergoplatform.sdk.utils.SerializationUtils.{parseString, serializeString} import org.ergoplatform.settings.ErgoAlgos -import org.ergoplatform.validation.ValidationRules.CheckDeserializedScriptIsSigmaProp import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Blake2b256 import scorex.util.ModifierId @@ -14,6 +13,7 @@ import sigmastate.Values._ import sigmastate._ import sigmastate.basics.CryptoConstants import sigmastate.eval._ +import sigmastate.exceptions.SerializerException import sigmastate.lang.{DeserializationSigmaBuilder, SigmaParser, StdSigmaBuilder} import sigmastate.serialization._ import sigmastate.util.safeNewArray @@ -248,7 +248,10 @@ object ContractTemplate { val _ = r.getUInt().toInt val expressionTree = ValueSerializer.deserialize(r) - CheckDeserializedScriptIsSigmaProp(expressionTree) + if (!expressionTree.tpe.isSigmaProp) { + throw SerializerException( + s"Failed deserialization, expected deserialized script to have type SigmaProp; got ${expressionTree.tpe}") + } ContractTemplate( treeVersion, name, description, From 8d1d004478f20e59d7a591bfca9de2635770e841 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Mon, 13 Mar 2023 22:26:11 +0530 Subject: [PATCH 16/42] Use TypeSerializer to serialize/deserialize SType in Json --- .../org/ergoplatform/sdk/ContractTemplate.scala | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 6eb2176f16..b2a2ec6c7e 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -14,7 +14,7 @@ import sigmastate._ import sigmastate.basics.CryptoConstants import sigmastate.eval._ import sigmastate.exceptions.SerializerException -import sigmastate.lang.{DeserializationSigmaBuilder, SigmaParser, StdSigmaBuilder} +import sigmastate.lang.{DeserializationSigmaBuilder, StdSigmaBuilder} import sigmastate.serialization._ import sigmastate.util.safeNewArray import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} @@ -23,7 +23,6 @@ import special.sigma._ import java.util.Objects import scala.collection.mutable import scala.language.implicitConversions -import scala.util.Try case class Parameter( name: String, @@ -263,11 +262,16 @@ object ContractTemplate { object jsonEncoder extends JsonCodecs { implicit val sTypeEncoder: Encoder[SType] = Encoder.instance({ tpe => - Json.fromString(tpe.toTermString) + val w = SigmaSerializer.startWriter() + TypeSerializer.serialize(tpe, w) + w.toBytes.asJson }) implicit val sTypeDecoder: Decoder[SType] = Decoder.instance({ implicit cursor => - fromTry(Try.apply(SigmaParser.parseType(cursor.value.asString.get))) + cursor.as[Array[Byte]] flatMap { bytes => + val r = SigmaSerializer.startReader(bytes) + fromThrows(TypeSerializer.deserialize(r)) + } }) implicit val encoder: Encoder[ContractTemplate] = Encoder.instance({ ct => From 8cfd687e6838719b32df0b16e937de3c75cfef59 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Thu, 16 Mar 2023 02:22:55 +0530 Subject: [PATCH 17/42] Add ScalaDoc to all public functions and classes --- .../ergoplatform/sdk/ContractTemplate.scala | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index b2a2ec6c7e..6db0d2535c 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -24,6 +24,12 @@ import java.util.Objects import scala.collection.mutable import scala.language.implicitConversions +/** + * Represents a ContractTemplate parameter. + * @param name user readable parameter name (string bytes in UTF-8 encoding) + * @param description user readable parameter description (string bytes in UTF-8 encoding) + * @param constantIndex index in the ErgoTree.constants array + */ case class Parameter( name: String, description: String, @@ -35,7 +41,7 @@ object Parameter { /** Immutable empty IndexedSeq, can be used to save allocations in many places. */ val EmptySeq: IndexedSeq[Parameter] = Array.empty[Parameter] - /** HOTSPOT: don't beautify this code */ + /** HOTSPOT: don't beautify this code. */ object serializer extends SigmaSerializer[Parameter, Parameter] { override def serialize(data: Parameter, w: SigmaByteWriter): Unit = { serializeString(data.name, w) @@ -51,6 +57,7 @@ object Parameter { } } + /** Json encoder for Parameter. */ implicit val encoder: Encoder[Parameter] = Encoder.instance({ p => Json.obj( "name" -> Json.fromString(p.name), @@ -59,6 +66,7 @@ object Parameter { ) }) + /** Json decoder for Parameter. */ implicit val decoder: Decoder[Parameter] = Decoder.instance({ cursor => for { name <- cursor.downField("name").as[String] @@ -68,6 +76,25 @@ object Parameter { }) } +/** + * Represents a reusable ContractTemplate with support to generate ErgoTree based on provided parameters. + * + * @param treeVersion the optional version of ErgoTree which should be used. If this value is not provided here then + * it must be provided while generating the `ErgoTree` by calling `applyTemplate`. + * @param name user readable name (non-empty string bytes in UTF-8 encoding) + * @param description user readable contract description (string bytes in UTF-8 encoding) + * @param constTypes list denoting the type of ConstantPlaceholders in the expressionTree + * @param constValues optional list of optional default values for the ConstantPlaceholders in the expressionTree. + * If an entry in the sequence is None, it must have a corresponding entry in parameters and its + * value must be provided while generating the `ErgoTree` by calling `applyTemplate`. If all the + * entries are None, the whole `constValues` field can be set to None. + * @param parameters typed template parameters of the contract template. It must have an entry for each + * `ConstantPlaceholder` which has a `None` in the `constValues` field. Other fields which do have + * a value defined in `constValues` can also be allowed to be optionally overridden by accepting + * it in `parameters`. + * @param expressionTree root of the contract which is a valid expression of `SigmaProp` type. Must have constants + * segregated into `constTypes` and optionally `constValues` + */ case class ContractTemplate( treeVersion: Option[Byte], name: String, @@ -110,6 +137,18 @@ case class ContractTemplate( } } + /** + * Generate the ErgoTree from the template by providing the values for parameters. + * + * @param version the version of the `ErgoTree` to use. Must be provided if the `treeVersion` was not provided in the + * template. + * @param paramValues the name-value map for the parameters accepted by the `ContractTemplate`. Must contain an entry + * for each parameter for which no default value was provided in the template. Optionally, can also + * provide values to override for parameters which do have a default value defined in the template. + * The type of the provided value must match with the corresponding entry in the `constTypes` + * provided in the template. + * @return `ErgoTree` generated by replacing the template parameters with the value provided in `paramValues`. + */ def applyTemplate(version: Option[Byte], paramValues: Map[String, Values.Constant[SType]]): Values.ErgoTree = { require(treeVersion.isDefined || version.isDefined, "ErgoTreeVersion must be provided to generate the ErgoTree.") val nConsts = constTypes.size @@ -261,12 +300,14 @@ object ContractTemplate { object jsonEncoder extends JsonCodecs { + /** Json encoder for SType */ implicit val sTypeEncoder: Encoder[SType] = Encoder.instance({ tpe => val w = SigmaSerializer.startWriter() TypeSerializer.serialize(tpe, w) w.toBytes.asJson }) + /** Json decoder for SType */ implicit val sTypeDecoder: Decoder[SType] = Decoder.instance({ implicit cursor => cursor.as[Array[Byte]] flatMap { bytes => val r = SigmaSerializer.startReader(bytes) @@ -274,6 +315,7 @@ object ContractTemplate { } }) + /** Json encoder for ContractTemplate */ implicit val encoder: Encoder[ContractTemplate] = Encoder.instance({ ct => val expressionTreeWriter = SigmaSerializer.startWriter() ValueSerializer.serialize(ct.expressionTree, expressionTreeWriter) @@ -294,6 +336,7 @@ object ContractTemplate { ) }) + /** Json decoder for ContractTemplate */ implicit val decoder: Decoder[ContractTemplate] = Decoder.instance({ implicit cursor => val constTypesResult = cursor.downField("constTypes").as[IndexedSeq[SType]] val expressionTreeBytesResult = cursor.downField("expressionTree").as[Array[Byte]] From fcb56ae46faf9e78ca75b7fe88cc7007a4a2ebc7 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Thu, 16 Mar 2023 02:24:31 +0530 Subject: [PATCH 18/42] Update build.sbt dependencies for scrypto, debox and scorex-util --- build.sbt | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/build.sbt b/build.sbt index f5252a2f7a..7b9d1cb2c2 100644 --- a/build.sbt +++ b/build.sbt @@ -61,21 +61,15 @@ dynverSeparator in ThisBuild := "-" val bouncycastleBcprov = "org.bouncycastle" % "bcprov-jdk15on" % "1.66" -val scrypto = "org.scorexfoundation" %% "scrypto" % "2.2.1-37-59c4fbd9-SNAPSHOT" -def scryptoDependency(platform: String) = { +val scrypto = "org.scorexfoundation" %% "scrypto" % "2.3.0" +def scryptoDependency = { libraryDependencies += - "org.scorexfoundation" %%% "scrypto" % ( - if (platform == "js") "0.0.0-1-59c4fbd9-SNAPSHOT" - else "2.2.1-37-59c4fbd9-SNAPSHOT" // for JVM - ) + "org.scorexfoundation" %%% "scrypto" % "2.3.0" } -//val scorexUtil = "org.scorexfoundation" %% "scorex-util" % "0.1.8" -//val debox = "org.scorexfoundation" %% "debox" % "0.9.0" -val scorexUtil = "org.scorexfoundation" %% "scorex-util" % "0.1.8-20-565873cd-SNAPSHOT" +val scorexUtil = "org.scorexfoundation" %% "scorex-util" % "0.2.0" val scorexUtilDependency = - libraryDependencies += "org.scorexfoundation" %%% "scorex-util" % "0.1.8-20-565873cd-SNAPSHOT" - -val debox = "org.scorexfoundation" %% "debox" % "0.9.0-9-f853cdce-SNAPSHOT" + libraryDependencies += "org.scorexfoundation" %%% "scorex-util" % "0.2.0" +val debox = "org.scorexfoundation" %% "debox" % "0.10.0" val spireMacros = "org.typelevel" %% "spire-macros" % "0.17.0-M1" val fastparse = "com.lihaoyi" %% "fastparse" % "2.3.3" @@ -160,7 +154,7 @@ def libraryDefSettings = commonSettings ++ crossScalaSettings ++ testSettings lazy val commonDependenies2 = libraryDependencies ++= Seq( "org.scala-lang" % "scala-reflect" % scalaVersion.value, - "org.scorexfoundation" %%% "debox" % "0.9.0-9-f853cdce-SNAPSHOT", + "org.scorexfoundation" %%% "debox" % "0.10.0", "org.scala-lang.modules" %%% "scala-collection-compat" % "2.7.0" ) @@ -195,11 +189,11 @@ lazy val corelib = crossProject(JVMPlatform, JSPlatform) ) .jvmSettings( crossScalaSettings, - scryptoDependency("jvm") + scryptoDependency ) .jsSettings( crossScalaSettingsJS, - scryptoDependency("js"), + scryptoDependency, libraryDependencies ++= Seq( "org.scala-js" %%% "scala-js-macrotask-executor" % "1.0.0" ), From 00f72469d6e133a24a4d13a795d3d57a28685a49 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Fri, 17 Mar 2023 02:34:47 +0530 Subject: [PATCH 19/42] WIP: Bring back Zero[T] typeclass. Need to fix forward references and cleanup code --- .../ergoplatform/sdk/ContractTemplate.scala | 223 ++++++++++-------- 1 file changed, 129 insertions(+), 94 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 6db0d2535c..79df723c61 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -5,9 +5,10 @@ import io.circe.syntax.EncoderOps import io.circe.{Decoder, DecodingFailure, Encoder, Json} import org.ergoplatform.ErgoBox import org.ergoplatform.sdk.utils.SerializationUtils.{parseString, serializeString} -import org.ergoplatform.settings.ErgoAlgos -import scorex.crypto.authds.ADDigest -import scorex.crypto.hash.Blake2b256 +import scalan.RType +import scalan.RType._ +import scorex.crypto.authds.avltree.batch.BatchAVLProver +import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId import sigmastate.Values._ import sigmastate._ @@ -18,8 +19,11 @@ import sigmastate.lang.{DeserializationSigmaBuilder, StdSigmaBuilder} import sigmastate.serialization._ import sigmastate.util.safeNewArray import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} -import special.sigma._ +import special.Types.TupleType +import special.collection.{Coll, CollType} +import special.sigma.{Header, SigmaDslBuilder, _} +import java.math.BigInteger import java.util.Objects import scala.collection.mutable import scala.language.implicitConversions @@ -132,7 +136,7 @@ case class ContractTemplate( } else { cfor(0)(_ < constTypes.size, _ + 1) { i => require(constValues.get(i).isDefined || paramIndices.contains(i), - s"constantIndex ${i} does not have a default value and absent from parameter as well") + s"constantIndex $i does not have a default value and absent from parameter as well") } } } @@ -160,7 +164,7 @@ case class ContractTemplate( .map(p => p.name) requiredParameterNames.foreach(name => require( paramValues.contains(name), - s"value for parameter ${name} was not provided while it does not have a default value.")) + s"value for parameter $name was not provided while it does not have a default value.")) val paramIndices = this.parameters.map(p => p.constantIndex).toSet val constants = safeNewArray[Constant[SType]](nConsts) @@ -280,7 +284,7 @@ object ContractTemplate { // Populate constants in constantStore so that the expressionTree can be deserialized. val constants = constTypes.indices.map(i => { val t = constTypes(i) - DeserializationSigmaBuilder.mkConstant(defaultOf(t), t) + DeserializationSigmaBuilder.mkConstant(Zero.zeroOf(Zero.typeToZero(Evaluation.stypeToRType(t))), t) }) constants.foreach(c => r.constantStore.put(c)(DeserializationSigmaBuilder)) @@ -358,7 +362,7 @@ object ContractTemplate { val r = SigmaSerializer.startReader(expressionTreeBytes) val constants = constTypes.indices.map(i => { val t = constTypes(i) - DeserializationSigmaBuilder.mkConstant(defaultOf(t), t) + DeserializationSigmaBuilder.mkConstant(Zero.zeroOf(Zero.typeToZero(Evaluation.stypeToRType(t))), t) }) constants.foreach(c => r.constantStore.put(c)(DeserializationSigmaBuilder)) @@ -379,92 +383,123 @@ object ContractTemplate { } }) } +} - /** Synthetic default value for the type. - * Used for deserializing contract templates. - */ - private def defaultOf[T <: SType](tpe: T): T#WrappedType = { - val syntheticBox = new ErgoBox( - 0L, - new ErgoTree( - 0.toByte, - Vector(), - Right(CSigmaProp(TrivialProp(false))) - ), - Colls.emptyColl, - Map(), - ModifierId @@ ("synthetic_transaction_id"), - 0.toShort, - 0 - ) - val syntheticPreHeader = CPreHeader( - 0.toByte, - Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), - -755484979487531112L, - 9223372036854775807L, - 11, - Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), - Helpers.decodeBytes("007f00") - ) - val syntheticAvlTree = AvlTreeData( - ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), - AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = false), - 2147483647, - None - ) +private trait Zero[T] { + def zero: T +} - val res = (tpe match { - case SBoolean => false - case SByte => 0.toByte - case SShort => 0.toShort - case SInt => 0 - case SLong => 0.toLong - case SBigInt => BigInt(0) - case SGroupElement => CGroupElement(CryptoConstants.dlogGroup.identity) - case SSigmaProp => CSigmaProp(TrivialProp(false)) - case SBox => CostingBox(syntheticBox) - case c: SCollectionType[_] => SCollectionType(c.elemType) - case _: SOption[_] => None - case _: STuple => STuple(SInt, SLong) - case _: SFunc => SFunc(SBoolean, NoType) - case SContext => CostingDataContext( - _dataInputs = Colls.emptyColl, - headers = Colls.emptyColl, - preHeader = syntheticPreHeader, - inputs = Colls.emptyColl, - outputs = Colls.emptyColl, - height = 1, - selfBox = CostingBox(syntheticBox), - selfIndex = 0, - lastBlockUtxoRootHash = CAvlTree(syntheticAvlTree), - _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), - vars = Colls.emptyColl, - activatedScriptVersion = 0.toByte, - currentErgoTreeVersion = 0.toByte - ) - case SAvlTree => CAvlTree(syntheticAvlTree) - case SGlobal => CostingSigmaDslBuilder - case SHeader => CHeader( - Colls.fromArray(Blake2b256("Header.id")), - 0, - Colls.fromArray(Blake2b256("Header.parentId")), - Colls.fromArray(Blake2b256("ADProofsRoot")), - CAvlTree(syntheticAvlTree), - Colls.fromArray(Blake2b256("transactionsRoot")), - timestamp = 0, - nBits = 0, - height = 0, - extensionRoot = Colls.fromArray(Blake2b256("transactionsRoot")), - minerPk = SigmaDsl.groupGenerator, - powOnetimePk = SigmaDsl.groupGenerator, - powNonce = Colls.fromArray(Array[Byte](0, 1, 2, 3, 4, 5, 6, 7)), - powDistance = SigmaDsl.BigInt(BigInt("0").bigInteger), - votes = Colls.fromArray(Array[Byte](0, 1, 2)) - ) - case SPreHeader => syntheticPreHeader - case SUnit => () - case _ => sys.error(s"Unknown type $tpe") - }).asInstanceOf[T#WrappedType] - res - } +private case class CZero[T](zero: T) extends Zero[T] + +private trait ZeroLowPriority { + implicit def collIsZero[T: Zero: RType]: Zero[Coll[T]] = CZero(Colls.emptyColl[T]) + implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(Some(Zero.zeroOf[T])) + implicit def pairIsZero[A: Zero, B: Zero]: Zero[(A,B)] = CZero(Zero[A].zero, Zero[B].zero) + implicit def funcIsZero[A: Zero, B: Zero]: Zero[A =>B] = CZero((_ : A) => { Zero[B].zero }) +} +private object Zero extends ZeroLowPriority { + def apply[T](implicit z: Zero[T]): Zero[T] = z + def zeroOf[T: Zero]: T = Zero[T].zero + + implicit val BooleanIsZero: Zero[Boolean] = CZero(false) + implicit val ByteIsZero: Zero[Byte] = CZero(0.toByte) + implicit val ShortIsZero: Zero[Short] = CZero(0.toShort) + implicit val IntIsZero: Zero[Int] = CZero(0) + implicit val LongIsZero: Zero[Long] = CZero(0L) + implicit val BigIntIsZero: Zero[BigInt] = CZero(CBigInt(BigInteger.ZERO)) + implicit val GroupElementIsZero: Zero[GroupElement] = CZero(CGroupElement(CryptoConstants.dlogGroup.identity)) + implicit val AvlTreeIsZero: Zero[AvlTree] = CZero({ + val avlProver = new BatchAVLProver[Digest32, Blake2b256.type](keyLength = 32, None) + val digest = avlProver.digest + val treeData = new AvlTreeData(digest, AvlTreeFlags.AllOperationsAllowed, 32, None) + CAvlTree(treeData) + }) + implicit val sigmaPropIsZero: Zero[SigmaProp] = CZero(CSigmaProp(TrivialProp.FalseProp)) + implicit val AnyIsZero: Zero[Any] = CZero(0) + implicit val UnitIsZero: Zero[Unit] = CZero() + implicit val BoxIsZero: Zero[Box] = CZero(syntheticBox) + implicit val ContextIsZero: Zero[Context] = CZero(syntheticContext) + implicit val SigmaDslBuilderIsZero: Zero[SigmaDslBuilder] = CZero(CostingSigmaDslBuilder) + implicit val HeaderIsZero: Zero[Header] = CZero(syntheticHeader) + implicit val PreHeaderIsZero: Zero[PreHeader] = CZero(syntheticPreHeader) + + def typeToZero[T](t: RType[T]): Zero[T] = (t match { + case BooleanType => Zero[Boolean] + case ByteType => Zero[Byte] + case ShortType => Zero[Short] + case IntType => Zero[Int] + case LongType => Zero[Long] + case AnyType => Zero[Any] + case UnitType => Zero[Unit] + case BigIntRType => Zero[BigInt] + case BoxRType => Zero[Box] + case ContextRType => Zero[Context] + case SigmaDslBuilderRType => Zero[SigmaDslBuilder] + case HeaderRType => Zero[Header] + case PreHeaderRType => Zero[PreHeader] + case GroupElementRType => Zero[GroupElement] + case AvlTreeRType => Zero[AvlTree] + case SigmaPropRType => sigmaPropIsZero + case ct: CollType[a] => collIsZero(typeToZero(ct.tItem), ct.tItem) + case ct: OptionType[a] => optionIsZero(typeToZero(ct.tA)) + case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd))`` + case tt: TupleType => CZero(tt.emptyArray) + case ft: FuncType[a, b] => funcIsZero(typeToZero(ft.tDom), typeToZero(ft.tRange)) + case _ => sys.error(s"Don't know how to compute Zero for type $t") + }).asInstanceOf[Zero[T]] + + private val syntheticBox = new ErgoBox( + 0L, + new ErgoTree( + 0.toByte, + Vector(), + Right(CSigmaProp(TrivialProp(false))) + ), + Colls.emptyColl, + Map(), + ModifierId @@ ("synthetic_transaction_id"), + 0.toShort, + 0 + ) + private val syntheticPreHeader = CPreHeader( + 0.toByte, + Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), + -755484979487531112L, + 9223372036854775807L, + 11, + Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + Helpers.decodeBytes("007f00") + ) + private val syntheticHeader = CHeader( + Colls.fromArray(Blake2b256("Header.id")), + 0, + Colls.fromArray(Blake2b256("Header.parentId")), + Colls.fromArray(Blake2b256("ADProofsRoot")), + CAvlTree(AvlTreeIsZero.zero), + Colls.fromArray(Blake2b256("transactionsRoot")), + timestamp = 0, + nBits = 0, + height = 0, + extensionRoot = Colls.fromArray(Blake2b256("transactionsRoot")), + minerPk = SigmaDsl.groupGenerator, + powOnetimePk = SigmaDsl.groupGenerator, + powNonce = Colls.fromArray(Array[Byte](0, 1, 2, 3, 4, 5, 6, 7)), + powDistance = SigmaDsl.BigInt(BigInt("0").bigInteger), + votes = Colls.fromArray(Array[Byte](0, 1, 2)) + ) + private val syntheticContext = CostingDataContext( + _dataInputs = Colls.emptyColl, + headers = Colls.emptyColl, + preHeader = syntheticPreHeader, + inputs = Colls.emptyColl, + outputs = Colls.emptyColl, + height = 1, + selfBox = CostingBox(syntheticBox), + selfIndex = 0, + lastBlockUtxoRootHash = AvlTreeIsZero.zero, + _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + vars = Colls.emptyColl, + activatedScriptVersion = 0.toByte, + currentErgoTreeVersion = 0.toByte + ) } \ No newline at end of file From 6d3126be73465d45a56534ac82455095b3b5eb9d Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Fri, 17 Mar 2023 23:01:29 +0530 Subject: [PATCH 20/42] Fix scala 2.11 build by adding correct imports. --- .../org/ergoplatform/sdk/ContractTemplate.scala | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 79df723c61..ddf58d132d 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -1,8 +1,9 @@ package org.ergoplatform.sdk +import cats.syntax.either._ // required for Scala 2.11 import debox.cfor -import io.circe.syntax.EncoderOps -import io.circe.{Decoder, DecodingFailure, Encoder, Json} +import io.circe._ +import io.circe.syntax.{EncoderOps, _} import org.ergoplatform.ErgoBox import org.ergoplatform.sdk.utils.SerializationUtils.{parseString, serializeString} import scalan.RType @@ -10,10 +11,10 @@ import scalan.RType._ import scorex.crypto.authds.avltree.batch.BatchAVLProver import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId -import sigmastate.Values._ +import sigmastate.Values.{ErgoTree, _} import sigmastate._ import sigmastate.basics.CryptoConstants -import sigmastate.eval._ +import sigmastate.eval.{Colls, _} import sigmastate.exceptions.SerializerException import sigmastate.lang.{DeserializationSigmaBuilder, StdSigmaBuilder} import sigmastate.serialization._ @@ -21,7 +22,7 @@ import sigmastate.util.safeNewArray import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} import special.Types.TupleType import special.collection.{Coll, CollType} -import special.sigma.{Header, SigmaDslBuilder, _} +import special.sigma.{Header, PreHeader, SigmaDslBuilder, _} import java.math.BigInteger import java.util.Objects @@ -416,7 +417,7 @@ private object Zero extends ZeroLowPriority { }) implicit val sigmaPropIsZero: Zero[SigmaProp] = CZero(CSigmaProp(TrivialProp.FalseProp)) implicit val AnyIsZero: Zero[Any] = CZero(0) - implicit val UnitIsZero: Zero[Unit] = CZero() + implicit val UnitIsZero: Zero[Unit] = CZero(()) implicit val BoxIsZero: Zero[Box] = CZero(syntheticBox) implicit val ContextIsZero: Zero[Context] = CZero(syntheticContext) implicit val SigmaDslBuilderIsZero: Zero[SigmaDslBuilder] = CZero(CostingSigmaDslBuilder) @@ -442,7 +443,7 @@ private object Zero extends ZeroLowPriority { case SigmaPropRType => sigmaPropIsZero case ct: CollType[a] => collIsZero(typeToZero(ct.tItem), ct.tItem) case ct: OptionType[a] => optionIsZero(typeToZero(ct.tA)) - case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd))`` + case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd)) case tt: TupleType => CZero(tt.emptyArray) case ft: FuncType[a, b] => funcIsZero(typeToZero(ft.tDom), typeToZero(ft.tRange)) case _ => sys.error(s"Don't know how to compute Zero for type $t") From 6ec0c23045833de25c9f8c9d8aaf659174bfefdd Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Fri, 17 Mar 2023 23:58:55 +0530 Subject: [PATCH 21/42] Fix forward references in Zero typeclass Also cleanup the class --- .../ergoplatform/sdk/ContractTemplate.scala | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index ddf58d132d..5fc47c527d 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -418,11 +418,7 @@ private object Zero extends ZeroLowPriority { implicit val sigmaPropIsZero: Zero[SigmaProp] = CZero(CSigmaProp(TrivialProp.FalseProp)) implicit val AnyIsZero: Zero[Any] = CZero(0) implicit val UnitIsZero: Zero[Unit] = CZero(()) - implicit val BoxIsZero: Zero[Box] = CZero(syntheticBox) - implicit val ContextIsZero: Zero[Context] = CZero(syntheticContext) implicit val SigmaDslBuilderIsZero: Zero[SigmaDslBuilder] = CZero(CostingSigmaDslBuilder) - implicit val HeaderIsZero: Zero[Header] = CZero(syntheticHeader) - implicit val PreHeaderIsZero: Zero[PreHeader] = CZero(syntheticPreHeader) def typeToZero[T](t: RType[T]): Zero[T] = (t match { case BooleanType => Zero[Boolean] @@ -450,43 +446,43 @@ private object Zero extends ZeroLowPriority { }).asInstanceOf[Zero[T]] private val syntheticBox = new ErgoBox( - 0L, + LongIsZero.zero, new ErgoTree( - 0.toByte, - Vector(), - Right(CSigmaProp(TrivialProp(false))) + ByteIsZero.zero, + IndexedSeq.empty, + Right(sigmaPropIsZero.zero) ), Colls.emptyColl, - Map(), + Map.empty, ModifierId @@ ("synthetic_transaction_id"), - 0.toShort, - 0 + ShortIsZero.zero, + IntIsZero.zero ) private val syntheticPreHeader = CPreHeader( - 0.toByte, - Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), - -755484979487531112L, - 9223372036854775807L, - 11, - Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), - Helpers.decodeBytes("007f00") + ByteIsZero.zero, + Colls.emptyColl[Byte], + LongIsZero.zero, + LongIsZero.zero, + IntIsZero.zero, + GroupElementIsZero.zero, + Colls.emptyColl[Byte] ) private val syntheticHeader = CHeader( - Colls.fromArray(Blake2b256("Header.id")), - 0, - Colls.fromArray(Blake2b256("Header.parentId")), - Colls.fromArray(Blake2b256("ADProofsRoot")), + Colls.emptyColl[Byte], + ByteIsZero.zero, + Colls.emptyColl[Byte], + Colls.emptyColl[Byte], CAvlTree(AvlTreeIsZero.zero), - Colls.fromArray(Blake2b256("transactionsRoot")), - timestamp = 0, - nBits = 0, - height = 0, - extensionRoot = Colls.fromArray(Blake2b256("transactionsRoot")), - minerPk = SigmaDsl.groupGenerator, - powOnetimePk = SigmaDsl.groupGenerator, - powNonce = Colls.fromArray(Array[Byte](0, 1, 2, 3, 4, 5, 6, 7)), - powDistance = SigmaDsl.BigInt(BigInt("0").bigInteger), - votes = Colls.fromArray(Array[Byte](0, 1, 2)) + Colls.emptyColl[Byte], + timestamp = LongIsZero.zero, + nBits = LongIsZero.zero, + height = IntIsZero.zero, + extensionRoot = Colls.emptyColl[Byte], + minerPk = GroupElementIsZero.zero, + powOnetimePk = GroupElementIsZero.zero, + powNonce = Colls.emptyColl[Byte], + powDistance = BigIntIsZero.zero, + votes = Colls.emptyColl[Byte] ) private val syntheticContext = CostingDataContext( _dataInputs = Colls.emptyColl, @@ -494,13 +490,18 @@ private object Zero extends ZeroLowPriority { preHeader = syntheticPreHeader, inputs = Colls.emptyColl, outputs = Colls.emptyColl, - height = 1, + height = IntIsZero.zero, selfBox = CostingBox(syntheticBox), - selfIndex = 0, + selfIndex = IntIsZero.zero, lastBlockUtxoRootHash = AvlTreeIsZero.zero, - _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + _minerPubKey = Colls.emptyColl[Byte], vars = Colls.emptyColl, - activatedScriptVersion = 0.toByte, - currentErgoTreeVersion = 0.toByte + activatedScriptVersion = ByteIsZero.zero, + currentErgoTreeVersion = ByteIsZero.zero ) + + implicit val BoxIsZero: Zero[Box] = CZero(syntheticBox) + implicit val ContextIsZero: Zero[Context] = CZero(syntheticContext) + implicit val HeaderIsZero: Zero[Header] = CZero(syntheticHeader) + implicit val PreHeaderIsZero: Zero[PreHeader] = CZero(syntheticPreHeader) } \ No newline at end of file From f05fe24449aeb9ef53d1796d90f6a9a74958b9ba Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 Date: Sat, 18 Mar 2023 01:18:44 +0530 Subject: [PATCH 22/42] Use correct treeVersion by adding sizeBit Also updated the test to assert accordingly --- .../org/ergoplatform/sdk/ContractTemplate.scala | 5 +++-- .../sdk/ContractTemplateSpecification.scala | 13 ++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 5fc47c527d..d6aeac29f2 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -1,6 +1,6 @@ package org.ergoplatform.sdk -import cats.syntax.either._ // required for Scala 2.11 +import cats.syntax.either._ import debox.cfor import io.circe._ import io.circe.syntax.{EncoderOps, _} @@ -11,6 +11,7 @@ import scalan.RType._ import scorex.crypto.authds.avltree.batch.BatchAVLProver import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId +import sigmastate.Values.ErgoTree.headerWithVersion import sigmastate.Values.{ErgoTree, _} import sigmastate._ import sigmastate.basics.CryptoConstants @@ -180,7 +181,7 @@ case class ContractTemplate( } } - val usedErgoTreeVersion = if (version.isDefined) version.get else treeVersion.get + val usedErgoTreeVersion = headerWithVersion(if (version.isDefined) version.get else treeVersion.get) ErgoTree( (ErgoTree.ConstantSegregationHeader | usedErgoTreeVersion).toByte, constants, diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index aff2a41662..54762f8bab 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -205,10 +205,13 @@ class ContractTemplateSpecification extends SerializationSpecification Map("p1" -> IntConstant(10), "p2" -> IntConstant(20)), Map.empty[String, Constant[SType]], ) - val ergoTreeVersion = (ErgoTree.ConstantSegregationHeader | ergoTreeVersionInTests | ErgoTree.SizeFlag).toByte + var expectedErgoTreeVersion = (ErgoTree.ConstantSegregationHeader | ergoTreeVersionInTests).toByte + if (ergoTreeVersionInTests > 0) { + expectedErgoTreeVersion = (expectedErgoTreeVersion | ErgoTree.SizeFlag).toByte + } val expectedErgoTree = Seq( ErgoTree( - ergoTreeVersion, + expectedErgoTreeVersion, IndexedSeq( ByteConstant(10.toByte), ByteConstant(40.toByte), @@ -217,7 +220,7 @@ class ContractTemplateSpecification extends SerializationSpecification expressionTrees(0) ), ErgoTree( - ergoTreeVersion, + expectedErgoTreeVersion, IndexedSeq( IntConstant(10), IntConstant(20), @@ -226,14 +229,14 @@ class ContractTemplateSpecification extends SerializationSpecification expressionTrees(1) ), ErgoTree( - ergoTreeVersion, + expectedErgoTreeVersion, Constant.EmptySeq, expressionTrees(2) ) ) templates.indices.foreach(i => - templates(i).applyTemplate(Some((ergoTreeVersionInTests | ErgoTree.SizeFlag).toByte), templateValues(i)) shouldEqual expectedErgoTree(i) + templates(i).applyTemplate(Some(ergoTreeVersionInTests), templateValues(i)) shouldEqual expectedErgoTree(i) ) } From 0b78da7d9155cd5e54b2ba745a42b1fd119461c2 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 22 Mar 2023 19:49:37 +0100 Subject: [PATCH 23/42] simplify code a bit --- .../main/scala/org/ergoplatform/sdk/ContractTemplate.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index d6aeac29f2..4f4723cb00 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -258,7 +258,7 @@ object ContractTemplate { SType.EmptySeq } } - val constValues: Option[IndexedSeq[Option[SType#WrappedType]]] = r.getOption((() => { + val constValues: Option[IndexedSeq[Option[SType#WrappedType]]] = r.getOption { if (nConstants > 0) { val res = safeNewArray[Option[SType#WrappedType]](nConstants) cfor(0)(_ < nConstants, _ + 1) { i => @@ -268,7 +268,7 @@ object ContractTemplate { } else { Array.empty[Option[SType#WrappedType]] } - })()) + } val nParameters = r.getUInt().toInt val parameters: IndexedSeq[Parameter] = { From 7f35fa5d9095be87992d19fa703bff406030b9c4 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 31 Mar 2023 00:41:34 +0530 Subject: [PATCH 24/42] Fix incorrect index into parameters array during apply --- .../ergoplatform/sdk/ContractTemplate.scala | 8 ++-- .../sdk/ContractTemplateSpecification.scala | 38 ++++++++++++++++++- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 4f4723cb00..5747c88429 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -168,11 +168,13 @@ case class ContractTemplate( paramValues.contains(name), s"value for parameter $name was not provided while it does not have a default value.")) - val paramIndices = this.parameters.map(p => p.constantIndex).toSet + val parameterizedConstantIndices = this.parameters.map(p => p.constantIndex).toSet + val constIndexToParamIndex = this.parameters.zipWithIndex.map(pi => pi._1.constantIndex -> pi._2).toMap val constants = safeNewArray[Constant[SType]](nConsts) cfor(0)(_ < nConsts, _ + 1) { i => - if (paramIndices.contains(i) && paramValues.contains(parameters(i).name)) { - val paramValue = paramValues(parameters(i).name) + // constants which are parameterized have to taken from the parameters when available. + if (parameterizedConstantIndices.contains(i) && paramValues.contains(parameters(constIndexToParamIndex(i)).name)) { + val paramValue = paramValues(parameters(constIndexToParamIndex(i)).name) require(paramValue.tpe == constTypes(i), s"parameter type mismatch, expected ${constTypes(i)}, got ${paramValue.tpe}") constants(i) = StdSigmaBuilder.mkConstant(paramValue.value, constTypes(i)) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 54762f8bab..71b020c630 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -166,7 +166,8 @@ class ContractTemplateSpecification extends SerializationSpecification t.getMessage.contains("constantIndex 0 does not have a default value and absent from parameter as well")) } - property("applyTemplate") { + property("applyTemplate") + { val parameters = IndexedSeq( createParameter("p1", 0), createParameter("p2", 1), @@ -240,6 +241,39 @@ class ContractTemplateSpecification extends SerializationSpecification ) } + property("applyTemplate num(parameters) < num(constants)") { + val parameters = IndexedSeq( + createParameter("p1", 0), + createParameter("p2", 2)) + val expressionTree = + EQ(Plus(ConstantPlaceholder(0, SType.typeInt), + ConstantPlaceholder(1, SType.typeInt)), + ConstantPlaceholder(2, SType.typeInt)).toSigmaProp + val template = createContractTemplate( + IndexedSeq(SType.typeInt, SType.typeInt, SType.typeInt).asInstanceOf[IndexedSeq[SType]], + Some(IndexedSeq(None, Some(20), None).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), + parameters, + expressionTree + ) + val templateValues = Map("p1" -> IntConstant(10), "p2" -> IntConstant(30)) + + var expectedErgoTreeVersion = (ErgoTree.ConstantSegregationHeader | ergoTreeVersionInTests).toByte + if (ergoTreeVersionInTests > 0) { + expectedErgoTreeVersion = (expectedErgoTreeVersion | ErgoTree.SizeFlag).toByte + } + val expectedErgoTree = ErgoTree( + expectedErgoTreeVersion, + IndexedSeq( + IntConstant(10), + IntConstant(20), + IntConstant(30) + ), + expressionTree + ) + + template.applyTemplate(Some(ergoTreeVersionInTests), templateValues) shouldEqual expectedErgoTree + } + property("(de)serialization round trip") { val parameters = IndexedSeq( createParameter("p1", 0), @@ -341,4 +375,4 @@ class ContractTemplateSpecification extends SerializationSpecification jsonRoundtrip(template) } } -} \ No newline at end of file +} From 5e3f24df1df4e91be6265f6b51382bfa45fdbf13 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 31 Mar 2023 02:54:15 +0530 Subject: [PATCH 25/42] Change Zero[Option] to None --- .../src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 5747c88429..40d1f2bba2 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -397,7 +397,7 @@ private case class CZero[T](zero: T) extends Zero[T] private trait ZeroLowPriority { implicit def collIsZero[T: Zero: RType]: Zero[Coll[T]] = CZero(Colls.emptyColl[T]) - implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(Some(Zero.zeroOf[T])) + implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(None) implicit def pairIsZero[A: Zero, B: Zero]: Zero[(A,B)] = CZero(Zero[A].zero, Zero[B].zero) implicit def funcIsZero[A: Zero, B: Zero]: Zero[A =>B] = CZero((_ : A) => { Zero[B].zero }) } From beeb70da83b445eb272607c2abb47def04b27468 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 31 Mar 2023 03:04:12 +0530 Subject: [PATCH 26/42] Add check for bytes => template => bytes roundtrip --- .../org/ergoplatform/sdk/ContractTemplateSpecification.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 71b020c630..1935c584d1 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -34,6 +34,10 @@ class ContractTemplateSpecification extends SerializationSpecification val r = SigmaSerializer.startReader(bytes) val res2 = ContractTemplate.serializer.parse(r) res2 shouldEqual template + + val w2 = SigmaSerializer.startWriter() + ContractTemplate.serializer.serialize(res2, w2) + bytes shouldEqual w2.toBytes } private def createParameter(name: String, constantIndex: Int): Parameter = { From a54e0d801ce059103526f3c6d0a2a8ae1311e989 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 31 Mar 2023 03:12:11 +0530 Subject: [PATCH 27/42] Use exceptionLike method --- .../sdk/ContractTemplateSpecification.scala | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 1935c584d1..74fd52d340 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -5,6 +5,7 @@ import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import sigmastate.Values._ import sigmastate._ import sigmastate.eval.CBigInt +import sigmastate.exceptions.InterpreterException import sigmastate.helpers.NegativeTesting import sigmastate.serialization.{SerializationSpecification, SigmaSerializer} import special.sigma.ContractsTestkit @@ -13,7 +14,7 @@ import java.math.BigInteger class ContractTemplateSpecification extends SerializationSpecification with ScalaCheckPropertyChecks - with ContractsTestkit + with ContractsTestkit with NegativeTesting with CrossVersionProps { object JsonCodecs extends JsonCodecs @@ -75,9 +76,8 @@ class ContractTemplateSpecification extends SerializationSpecification ConstantPlaceholder(1, SType.typeByte)), ConstantPlaceholder(2, SType.typeByte)).toSigmaProp ), - t => - t.isInstanceOf[IllegalArgumentException] && - t.getMessage.contains("constValues must be empty or of same length as constTypes. Got 2, expected 3")) + exceptionLike[IllegalArgumentException]("constValues must be empty or of same length as constTypes. Got 2, expected 3") + ) } property("more parameters than constants") { @@ -94,9 +94,8 @@ class ContractTemplateSpecification extends SerializationSpecification ConstantPlaceholder(1, SType.typeByte)), ConstantPlaceholder(2, SType.typeByte)).toSigmaProp ), - t => - t.isInstanceOf[IllegalArgumentException] && - t.getMessage.contains("number of parameters must be <= number of constants")) + exceptionLike[IllegalArgumentException]("number of parameters must be <= number of constants") + ) } property("invalid parameter constantIndex") { @@ -112,9 +111,8 @@ class ContractTemplateSpecification extends SerializationSpecification ConstantPlaceholder(1, SType.typeByte)), ConstantPlaceholder(2, SType.typeByte)).toSigmaProp ), - t => - t.isInstanceOf[IllegalArgumentException] && - t.getMessage.contains("parameter constantIndex must be in range [0, 3)")) + exceptionLike[IllegalArgumentException]("parameter constantIndex must be in range [0, 3)") + ) } property("duplicate parameter constantIndex") { @@ -130,9 +128,8 @@ class ContractTemplateSpecification extends SerializationSpecification ConstantPlaceholder(1, SType.typeByte)), ConstantPlaceholder(2, SType.typeByte)).toSigmaProp ), - t => - t.isInstanceOf[IllegalArgumentException] && - t.getMessage.contains("multiple parameters point to the same constantIndex")) + exceptionLike[IllegalArgumentException]("multiple parameters point to the same constantIndex") + ) } property("duplicate parameter names") { @@ -148,9 +145,8 @@ class ContractTemplateSpecification extends SerializationSpecification ConstantPlaceholder(1, SType.typeByte)), ConstantPlaceholder(2, SType.typeByte)).toSigmaProp ), - t => - t.isInstanceOf[IllegalArgumentException] && - t.getMessage.contains("parameter names must be unique. Found duplicate parameters with name duplicate_name")) + exceptionLike[IllegalArgumentException]("parameter names must be unique. Found duplicate parameters with name duplicate_name") + ) } property("constantIndex without default value and parameter") { @@ -165,9 +161,8 @@ class ContractTemplateSpecification extends SerializationSpecification ConstantPlaceholder(1, SType.typeByte)), ConstantPlaceholder(2, SType.typeByte)).toSigmaProp ), - t => - t.isInstanceOf[IllegalArgumentException] && - t.getMessage.contains("constantIndex 0 does not have a default value and absent from parameter as well")) + exceptionLike[IllegalArgumentException]("constantIndex 0 does not have a default value and absent from parameter as well") + ) } property("applyTemplate") From 6e340c337e560055fdd7da830e24c69b76ca72e1 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 1 Apr 2023 21:07:48 +0530 Subject: [PATCH 28/42] Move Zero related functionality to a new file --- .../ergoplatform/sdk/ContractTemplate.scala | 121 +-------------- .../org/ergoplatform/sdk/utils/Zero.scala | 142 ++++++++++++++++++ .../sdk/ContractTemplateSpecification.scala | 1 - 3 files changed, 143 insertions(+), 121 deletions(-) create mode 100644 sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 40d1f2bba2..1f3dda59ba 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -6,6 +6,7 @@ import io.circe._ import io.circe.syntax.{EncoderOps, _} import org.ergoplatform.ErgoBox import org.ergoplatform.sdk.utils.SerializationUtils.{parseString, serializeString} +import org.ergoplatform.sdk.utils.Zero import scalan.RType import scalan.RType._ import scorex.crypto.authds.avltree.batch.BatchAVLProver @@ -388,123 +389,3 @@ object ContractTemplate { }) } } - -private trait Zero[T] { - def zero: T -} - -private case class CZero[T](zero: T) extends Zero[T] - -private trait ZeroLowPriority { - implicit def collIsZero[T: Zero: RType]: Zero[Coll[T]] = CZero(Colls.emptyColl[T]) - implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(None) - implicit def pairIsZero[A: Zero, B: Zero]: Zero[(A,B)] = CZero(Zero[A].zero, Zero[B].zero) - implicit def funcIsZero[A: Zero, B: Zero]: Zero[A =>B] = CZero((_ : A) => { Zero[B].zero }) -} -private object Zero extends ZeroLowPriority { - def apply[T](implicit z: Zero[T]): Zero[T] = z - def zeroOf[T: Zero]: T = Zero[T].zero - - implicit val BooleanIsZero: Zero[Boolean] = CZero(false) - implicit val ByteIsZero: Zero[Byte] = CZero(0.toByte) - implicit val ShortIsZero: Zero[Short] = CZero(0.toShort) - implicit val IntIsZero: Zero[Int] = CZero(0) - implicit val LongIsZero: Zero[Long] = CZero(0L) - implicit val BigIntIsZero: Zero[BigInt] = CZero(CBigInt(BigInteger.ZERO)) - implicit val GroupElementIsZero: Zero[GroupElement] = CZero(CGroupElement(CryptoConstants.dlogGroup.identity)) - implicit val AvlTreeIsZero: Zero[AvlTree] = CZero({ - val avlProver = new BatchAVLProver[Digest32, Blake2b256.type](keyLength = 32, None) - val digest = avlProver.digest - val treeData = new AvlTreeData(digest, AvlTreeFlags.AllOperationsAllowed, 32, None) - CAvlTree(treeData) - }) - implicit val sigmaPropIsZero: Zero[SigmaProp] = CZero(CSigmaProp(TrivialProp.FalseProp)) - implicit val AnyIsZero: Zero[Any] = CZero(0) - implicit val UnitIsZero: Zero[Unit] = CZero(()) - implicit val SigmaDslBuilderIsZero: Zero[SigmaDslBuilder] = CZero(CostingSigmaDslBuilder) - - def typeToZero[T](t: RType[T]): Zero[T] = (t match { - case BooleanType => Zero[Boolean] - case ByteType => Zero[Byte] - case ShortType => Zero[Short] - case IntType => Zero[Int] - case LongType => Zero[Long] - case AnyType => Zero[Any] - case UnitType => Zero[Unit] - case BigIntRType => Zero[BigInt] - case BoxRType => Zero[Box] - case ContextRType => Zero[Context] - case SigmaDslBuilderRType => Zero[SigmaDslBuilder] - case HeaderRType => Zero[Header] - case PreHeaderRType => Zero[PreHeader] - case GroupElementRType => Zero[GroupElement] - case AvlTreeRType => Zero[AvlTree] - case SigmaPropRType => sigmaPropIsZero - case ct: CollType[a] => collIsZero(typeToZero(ct.tItem), ct.tItem) - case ct: OptionType[a] => optionIsZero(typeToZero(ct.tA)) - case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd)) - case tt: TupleType => CZero(tt.emptyArray) - case ft: FuncType[a, b] => funcIsZero(typeToZero(ft.tDom), typeToZero(ft.tRange)) - case _ => sys.error(s"Don't know how to compute Zero for type $t") - }).asInstanceOf[Zero[T]] - - private val syntheticBox = new ErgoBox( - LongIsZero.zero, - new ErgoTree( - ByteIsZero.zero, - IndexedSeq.empty, - Right(sigmaPropIsZero.zero) - ), - Colls.emptyColl, - Map.empty, - ModifierId @@ ("synthetic_transaction_id"), - ShortIsZero.zero, - IntIsZero.zero - ) - private val syntheticPreHeader = CPreHeader( - ByteIsZero.zero, - Colls.emptyColl[Byte], - LongIsZero.zero, - LongIsZero.zero, - IntIsZero.zero, - GroupElementIsZero.zero, - Colls.emptyColl[Byte] - ) - private val syntheticHeader = CHeader( - Colls.emptyColl[Byte], - ByteIsZero.zero, - Colls.emptyColl[Byte], - Colls.emptyColl[Byte], - CAvlTree(AvlTreeIsZero.zero), - Colls.emptyColl[Byte], - timestamp = LongIsZero.zero, - nBits = LongIsZero.zero, - height = IntIsZero.zero, - extensionRoot = Colls.emptyColl[Byte], - minerPk = GroupElementIsZero.zero, - powOnetimePk = GroupElementIsZero.zero, - powNonce = Colls.emptyColl[Byte], - powDistance = BigIntIsZero.zero, - votes = Colls.emptyColl[Byte] - ) - private val syntheticContext = CostingDataContext( - _dataInputs = Colls.emptyColl, - headers = Colls.emptyColl, - preHeader = syntheticPreHeader, - inputs = Colls.emptyColl, - outputs = Colls.emptyColl, - height = IntIsZero.zero, - selfBox = CostingBox(syntheticBox), - selfIndex = IntIsZero.zero, - lastBlockUtxoRootHash = AvlTreeIsZero.zero, - _minerPubKey = Colls.emptyColl[Byte], - vars = Colls.emptyColl, - activatedScriptVersion = ByteIsZero.zero, - currentErgoTreeVersion = ByteIsZero.zero - ) - - implicit val BoxIsZero: Zero[Box] = CZero(syntheticBox) - implicit val ContextIsZero: Zero[Context] = CZero(syntheticContext) - implicit val HeaderIsZero: Zero[Header] = CZero(syntheticHeader) - implicit val PreHeaderIsZero: Zero[PreHeader] = CZero(syntheticPreHeader) -} \ No newline at end of file diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala new file mode 100644 index 0000000000..b5116ed326 --- /dev/null +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala @@ -0,0 +1,142 @@ +package org.ergoplatform.sdk.utils + +import org.ergoplatform.ErgoBox +import scalan.RType +import scalan.RType.{AnyType, BooleanType, ByteType, FuncType, IntType, LongType, OptionType, PairType, ShortType, UnitType} +import scorex.crypto.authds.avltree.batch.BatchAVLProver +import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.util.ModifierId +import sigmastate.{AvlTreeData, AvlTreeFlags, TrivialProp} +import sigmastate.Values.ErgoTree +import sigmastate.basics.CryptoConstants +import sigmastate.eval.{CAvlTree, CBigInt, CGroupElement, CHeader, CPreHeader, CSigmaProp, Colls, CostingBox, CostingDataContext, CostingSigmaDslBuilder} +import special.Types.TupleType +import special.collection.{Coll, CollType} +import special.sigma.{AvlTree, AvlTreeRType, BigInt, BigIntRType, Box, BoxRType, Context, ContextRType, GroupElement, GroupElementRType, Header, HeaderRType, PreHeader, PreHeaderRType, SigmaDslBuilder, SigmaDslBuilderRType, SigmaProp, SigmaPropRType} + +import java.math.BigInteger +import scalan.RType._ +import sigmastate.eval._ +import special.sigma._ +import scala.language.implicitConversions + +trait Zero[T] { + def zero: T +} + +case class CZero[T](zero: T) extends Zero[T] + +trait ZeroLowPriority { + implicit def collIsZero[T: Zero: RType]: Zero[Coll[T]] = CZero(Colls.emptyColl[T]) + implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(None) + implicit def pairIsZero[A: Zero, B: Zero]: Zero[(A,B)] = CZero(Zero[A].zero, Zero[B].zero) + implicit def funcIsZero[A: Zero, B: Zero]: Zero[A =>B] = CZero((_ : A) => { Zero[B].zero }) +} + +object Zero extends ZeroLowPriority { + def apply[T](implicit z: Zero[T]): Zero[T] = z + def zeroOf[T: Zero]: T = Zero[T].zero + + implicit val BooleanIsZero: Zero[Boolean] = CZero(false) + implicit val ByteIsZero: Zero[Byte] = CZero(0.toByte) + implicit val ShortIsZero: Zero[Short] = CZero(0.toShort) + implicit val IntIsZero: Zero[Int] = CZero(0) + implicit val LongIsZero: Zero[Long] = CZero(0L) + implicit val BigIntIsZero: Zero[BigInt] = CZero(CBigInt(BigInteger.ZERO)) + implicit val GroupElementIsZero: Zero[GroupElement] = CZero(CGroupElement(CryptoConstants.dlogGroup.identity)) + implicit val AvlTreeIsZero: Zero[AvlTree] = CZero({ + val avlProver = new BatchAVLProver[Digest32, Blake2b256.type](keyLength = 32, None) + val digest = avlProver.digest + val treeData = new AvlTreeData(digest, AvlTreeFlags.AllOperationsAllowed, 32, None) + CAvlTree(treeData) + }) + implicit val sigmaPropIsZero: Zero[SigmaProp] = CZero(CSigmaProp(TrivialProp.FalseProp)) + implicit val AnyIsZero: Zero[Any] = CZero(0) + implicit val UnitIsZero: Zero[Unit] = CZero(()) + implicit val SigmaDslBuilderIsZero: Zero[SigmaDslBuilder] = CZero(CostingSigmaDslBuilder) + + def typeToZero[T](t: RType[T]): Zero[T] = (t match { + case BooleanType => Zero[Boolean] + case ByteType => Zero[Byte] + case ShortType => Zero[Short] + case IntType => Zero[Int] + case LongType => Zero[Long] + case AnyType => Zero[Any] + case UnitType => Zero[Unit] + case BigIntRType => Zero[BigInt] + case BoxRType => Zero[Box] + case ContextRType => Zero[Context] + case SigmaDslBuilderRType => Zero[SigmaDslBuilder] + case HeaderRType => Zero[Header] + case PreHeaderRType => Zero[PreHeader] + case GroupElementRType => Zero[GroupElement] + case AvlTreeRType => Zero[AvlTree] + case SigmaPropRType => sigmaPropIsZero + case ct: CollType[a] => collIsZero(typeToZero(ct.tItem), ct.tItem) + case ct: OptionType[a] => optionIsZero(typeToZero(ct.tA)) + case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd)) + case tt: TupleType => CZero(tt.emptyArray) + case ft: FuncType[a, b] => funcIsZero(typeToZero(ft.tDom), typeToZero(ft.tRange)) + case _ => sys.error(s"Don't know how to compute Zero for type $t") + }).asInstanceOf[Zero[T]] + + private val syntheticBox = new ErgoBox( + LongIsZero.zero, + new ErgoTree( + ByteIsZero.zero, + IndexedSeq.empty, + Right(sigmaPropIsZero.zero) + ), + Colls.emptyColl, + Map.empty, + ModifierId @@ ("synthetic_transaction_id"), + ShortIsZero.zero, + IntIsZero.zero + ) + private val syntheticPreHeader = CPreHeader( + ByteIsZero.zero, + Colls.emptyColl[Byte], + LongIsZero.zero, + LongIsZero.zero, + IntIsZero.zero, + GroupElementIsZero.zero, + Colls.emptyColl[Byte] + ) + private val syntheticHeader = CHeader( + Colls.emptyColl[Byte], + ByteIsZero.zero, + Colls.emptyColl[Byte], + Colls.emptyColl[Byte], + CAvlTree(AvlTreeIsZero.zero), + Colls.emptyColl[Byte], + timestamp = LongIsZero.zero, + nBits = LongIsZero.zero, + height = IntIsZero.zero, + extensionRoot = Colls.emptyColl[Byte], + minerPk = GroupElementIsZero.zero, + powOnetimePk = GroupElementIsZero.zero, + powNonce = Colls.emptyColl[Byte], + powDistance = BigIntIsZero.zero, + votes = Colls.emptyColl[Byte] + ) + private val syntheticContext = CostingDataContext( + _dataInputs = Colls.emptyColl, + headers = Colls.emptyColl, + preHeader = syntheticPreHeader, + inputs = Colls.emptyColl, + outputs = Colls.emptyColl, + height = IntIsZero.zero, + selfBox = CostingBox(syntheticBox), + selfIndex = IntIsZero.zero, + lastBlockUtxoRootHash = AvlTreeIsZero.zero, + _minerPubKey = Colls.emptyColl[Byte], + vars = Colls.emptyColl, + activatedScriptVersion = ByteIsZero.zero, + currentErgoTreeVersion = ByteIsZero.zero + ) + + implicit val BoxIsZero: Zero[Box] = CZero(syntheticBox) + implicit val ContextIsZero: Zero[Context] = CZero(syntheticContext) + implicit val HeaderIsZero: Zero[Header] = CZero(syntheticHeader) + implicit val PreHeaderIsZero: Zero[PreHeader] = CZero(syntheticPreHeader) +} diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 74fd52d340..bec9b3ba1a 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -5,7 +5,6 @@ import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import sigmastate.Values._ import sigmastate._ import sigmastate.eval.CBigInt -import sigmastate.exceptions.InterpreterException import sigmastate.helpers.NegativeTesting import sigmastate.serialization.{SerializationSpecification, SigmaSerializer} import special.sigma.ContractsTestkit From 8bbb10a941941f5493c2b23cbd5342ae32c6f29c Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 1 Apr 2023 21:09:21 +0530 Subject: [PATCH 29/42] Optimize imports in ContractTemplate.scala after moving Zero to new file --- .../org/ergoplatform/sdk/ContractTemplate.scala | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 1f3dda59ba..c24a9fb4b3 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -4,29 +4,18 @@ import cats.syntax.either._ import debox.cfor import io.circe._ import io.circe.syntax.{EncoderOps, _} -import org.ergoplatform.ErgoBox import org.ergoplatform.sdk.utils.SerializationUtils.{parseString, serializeString} import org.ergoplatform.sdk.utils.Zero -import scalan.RType -import scalan.RType._ -import scorex.crypto.authds.avltree.batch.BatchAVLProver -import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.util.ModifierId import sigmastate.Values.ErgoTree.headerWithVersion import sigmastate.Values.{ErgoTree, _} import sigmastate._ -import sigmastate.basics.CryptoConstants import sigmastate.eval.{Colls, _} import sigmastate.exceptions.SerializerException import sigmastate.lang.{DeserializationSigmaBuilder, StdSigmaBuilder} import sigmastate.serialization._ import sigmastate.util.safeNewArray -import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} -import special.Types.TupleType -import special.collection.{Coll, CollType} -import special.sigma.{Header, PreHeader, SigmaDslBuilder, _} +import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import java.math.BigInteger import java.util.Objects import scala.collection.mutable import scala.language.implicitConversions From 05c092f7c9ff5b91df24883e3bf013d277b8836a Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 1 Apr 2023 22:14:35 +0530 Subject: [PATCH 30/42] Check for json -> template -> json in json round trip as well --- .../org/ergoplatform/sdk/ContractTemplateSpecification.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index bec9b3ba1a..e67821f993 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -25,6 +25,8 @@ class ContractTemplateSpecification extends SerializationSpecification val json = ContractTemplate.jsonEncoder.encoder(obj) val res = ContractTemplate.jsonEncoder.decoder(json.hcursor).right.get res shouldBe obj + val json2 = ContractTemplate.jsonEncoder.encoder(obj) + json shouldBe json2 } private def serializationRoundTrip(template: ContractTemplate): Assertion = { From 0ee0830c88a3b995014b9fcae711bed191b27794 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 2 Apr 2023 18:04:21 +0530 Subject: [PATCH 31/42] WIP: Add a generator for ContractTemplate --- .../generators/ObjectGenerators.scala | 6 + .../sdk/ContractTemplateSpecification.scala | 109 ++---------------- .../sdk/generators/ObjectGenerators.scala | 58 ++++++++++ 3 files changed, 73 insertions(+), 100 deletions(-) create mode 100644 sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala b/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala index a4feda1ed7..9bce0f0246 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala +++ b/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala @@ -680,6 +680,12 @@ trait ObjectGenerators extends TypeGenerators ErgoTree.withoutSegregation)) } yield treeBuilder(prop) + lazy val ergoTreeWithSegregationGen: Gen[ErgoTree] = for { + sigmaBoolean <- Gen.delay(sigmaBooleanGen) + propWithConstants <- Gen.delay(logicalExprTreeNodeGen(Seq(AND.apply, OR.apply, XorOf.apply)).map(_.toSigmaProp)) + prop <- Gen.oneOf(propWithConstants, sigmaBoolean.toSigmaProp) + } yield ErgoTree.withSegregation(prop) + def headerGen(stateRoot: AvlTree, parentId: Coll[Byte]): Gen[Header] = for { id <- modifierIdBytesGen version <- arbByte.arbitrary diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index e67821f993..4fb07eaf9b 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -1,5 +1,6 @@ package org.ergoplatform.sdk +import org.ergoplatform.sdk.generators.ObjectGenerators import org.scalatest.compatible.Assertion import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import sigmastate.Values._ @@ -15,13 +16,11 @@ class ContractTemplateSpecification extends SerializationSpecification with ScalaCheckPropertyChecks with ContractsTestkit with NegativeTesting - with CrossVersionProps { + with CrossVersionProps + with ObjectGenerators { object JsonCodecs extends JsonCodecs - private def contractTemplateNameInTests: String = "TestContractTemplate" - private def contractTemplateDescriptionInTests: String = "TestContractTemplateDescription" - - private def jsonRoundtrip[T <: SType](obj: ContractTemplate) = { + private def jsonRoundTrip[T <: SType](obj: ContractTemplate) = { val json = ContractTemplate.jsonEncoder.encoder(obj) val res = ContractTemplate.jsonEncoder.decoder(json.hcursor).right.get res shouldBe obj @@ -166,8 +165,7 @@ class ContractTemplateSpecification extends SerializationSpecification ) } - property("applyTemplate") - { + property("applyTemplate") { val parameters = IndexedSeq( createParameter("p1", 0), createParameter("p2", 1), @@ -275,104 +273,15 @@ class ContractTemplateSpecification extends SerializationSpecification } property("(de)serialization round trip") { - val parameters = IndexedSeq( - createParameter("p1", 0), - createParameter("p2", 1), - createParameter("p3", 2)) - val templates = Seq( - createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], - Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), - parameters, - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp - ), - createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], - Some(IndexedSeq(Some(10.toByte), None, Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), - parameters, - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp - ), - createContractTemplate( - IndexedSeq(SType.typeShort, SType.typeShort, SType.typeShort).asInstanceOf[IndexedSeq[SType]], - Some(IndexedSeq(Some(10.toShort), Some(20.toShort), Some(30.toShort)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), - parameters, - EQ(Plus(ConstantPlaceholder(0, SType.typeShort), - ConstantPlaceholder(1, SType.typeShort)), - ConstantPlaceholder(2, SType.typeShort)).toSigmaProp - ), - createContractTemplate( - IndexedSeq(SType.typeInt, SType.typeInt, SType.typeInt).asInstanceOf[IndexedSeq[SType]], - Some(IndexedSeq(Some(10), Some(20), Some(30)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), - parameters, - EQ(Plus(ConstantPlaceholder(0, SType.typeInt), - ConstantPlaceholder(1, SType.typeInt)), - ConstantPlaceholder(2, SType.typeInt)).toSigmaProp - ), - createContractTemplate( - SType.EmptySeq, - None, - Parameter.EmptySeq, - EQ(Plus(CBigInt(BigInteger.valueOf(10L)), BigIntConstant(20L)), BigIntConstant(30L)).toSigmaProp - ) - ) - - templates.foreach { template => + forAll(contractTemplateGen, minSuccessful(500)) { template => + val x = 1 serializationRoundTrip(template) } } property("Data Json serialization round trip") { - val parameters = IndexedSeq( - createParameter("p1", 0), - createParameter("p2", 1), - createParameter("p3", 2)) - val templates = Seq( - createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], - Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), - parameters, - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp - ), - createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], - Some(IndexedSeq(Some(10.toByte), None, Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), - parameters, - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp - ), - createContractTemplate( - IndexedSeq(SType.typeShort, SType.typeShort, SType.typeShort).asInstanceOf[IndexedSeq[SType]], - Some(IndexedSeq(Some(10.toShort), Some(20.toShort), Some(30.toShort)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), - parameters, - EQ(Plus(ConstantPlaceholder(0, SType.typeShort), - ConstantPlaceholder(1, SType.typeShort)), - ConstantPlaceholder(2, SType.typeShort)).toSigmaProp - ), - createContractTemplate( - IndexedSeq(SType.typeInt, SType.typeInt, SType.typeInt).asInstanceOf[IndexedSeq[SType]], - Some(IndexedSeq(Some(10), Some(20), Some(30)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), - parameters, - EQ(Plus(ConstantPlaceholder(0, SType.typeInt), - ConstantPlaceholder(1, SType.typeInt)), - ConstantPlaceholder(2, SType.typeInt)).toSigmaProp - ), - createContractTemplate( - SType.EmptySeq, - None, - Parameter.EmptySeq, - EQ(Plus(CBigInt(BigInteger.valueOf(10L)), BigIntConstant(20L)), BigIntConstant(30L)).toSigmaProp - ) - ) - - templates.foreach { template => - jsonRoundtrip(template) + forAll(contractTemplateGen, minSuccessful(500)) { template => + jsonRoundTrip(template) } } } diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala new file mode 100644 index 0000000000..f4622babd1 --- /dev/null +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala @@ -0,0 +1,58 @@ +package org.ergoplatform.sdk.generators + +import org.ergoplatform.sdk.{ContractTemplate, Parameter} +import org.ergoplatform.validation.ValidationSpecification +import org.scalacheck.Gen +import sigmastate.Values.{ErgoTree, SigmaPropValue} +import sigmastate.serialization.generators.{ConcreteCollectionGenerators, TypeGenerators, ObjectGenerators => InterpreterObjectGenerators} +import sigmastate.{SType, TestsBase} + +trait ObjectGenerators extends TypeGenerators + with ValidationSpecification + with ConcreteCollectionGenerators + with TestsBase + with InterpreterObjectGenerators { + + def contractTemplateNameInTests: String = "TestContractTemplate" + + def contractTemplateDescriptionInTests: String = "TestContractTemplateDescription" + + private def getConstValues(ergoTree: ErgoTree, + noDefaultValueIndices: Set[Int]): + Option[IndexedSeq[Option[SType#WrappedType]]] = { + val values = ergoTree + .constants + .zipWithIndex + .map(c_i => if (noDefaultValueIndices.contains(c_i._2)) None else Some(c_i._1.asWrappedType)) + val allNone = values.forall(v => v.isEmpty) + if (allNone) { + None + } else { + Some(values) + } + } + + private def getConstAndParams(ergoTree: ErgoTree): (IndexedSeq[SType], Option[IndexedSeq[Option[SType#WrappedType]]], IndexedSeq[Parameter]) = { + val paramIndices = ergoTree.constants.indices.filter(_ => math.random() < 0.5).toSet + val noDefaultValueIndices = paramIndices.filter(_ => math.random() < 0.5) + val constTypes = ergoTree.constants.map(c => c.tpe) + val constValues = getConstValues(ergoTree, noDefaultValueIndices) + val parameters = paramIndices + .map(i => Parameter(s"param_for_idx$i", s"description_for_idx$i", i)).toIndexedSeq + (constTypes, constValues, parameters) + } + + lazy val contractTemplateGen: Gen[ContractTemplate] = for { + ergoTree <- Gen.delay(ergoTreeWithSegregationGen) + } yield { + val (constTypes, constValues, parameters) = getConstAndParams(ergoTree) + ContractTemplate( + contractTemplateNameInTests, + contractTemplateDescriptionInTests, + constTypes, + constValues, + parameters, + ergoTree.toProposition(false) + ) + } +} From 186b6860e55f26b217304548db809aac5c76b13f Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Apr 2023 01:49:49 +0530 Subject: [PATCH 32/42] Finish contract template generator --- .../org/ergoplatform/sdk/ContractTemplateSpecification.scala | 1 - .../org/ergoplatform/sdk/generators/ObjectGenerators.scala | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 4fb07eaf9b..31dad367ef 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -274,7 +274,6 @@ class ContractTemplateSpecification extends SerializationSpecification property("(de)serialization round trip") { forAll(contractTemplateGen, minSuccessful(500)) { template => - val x = 1 serializationRoundTrip(template) } } diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala index f4622babd1..cf28f3ef96 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala @@ -23,7 +23,7 @@ trait ObjectGenerators extends TypeGenerators val values = ergoTree .constants .zipWithIndex - .map(c_i => if (noDefaultValueIndices.contains(c_i._2)) None else Some(c_i._1.asWrappedType)) + .map(c_i => if (noDefaultValueIndices.contains(c_i._2)) None else Some(c_i._1.value)) val allNone = values.forall(v => v.isEmpty) if (allNone) { None From f54ec21f76d430eae74a2a624d335a15e0ecfa25 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Apr 2023 18:14:54 +0530 Subject: [PATCH 33/42] Remove extra synthetic variables and move declarations inline in Zeros --- .../org/ergoplatform/sdk/utils/Zero.scala | 119 +++++++++--------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala index b5116ed326..2f5154a339 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala @@ -80,63 +80,66 @@ object Zero extends ZeroLowPriority { case _ => sys.error(s"Don't know how to compute Zero for type $t") }).asInstanceOf[Zero[T]] - private val syntheticBox = new ErgoBox( - LongIsZero.zero, - new ErgoTree( + implicit val PreHeaderIsZero: Zero[PreHeader] = CZero({ + CPreHeader( ByteIsZero.zero, - IndexedSeq.empty, - Right(sigmaPropIsZero.zero) - ), - Colls.emptyColl, - Map.empty, - ModifierId @@ ("synthetic_transaction_id"), - ShortIsZero.zero, - IntIsZero.zero - ) - private val syntheticPreHeader = CPreHeader( - ByteIsZero.zero, - Colls.emptyColl[Byte], - LongIsZero.zero, - LongIsZero.zero, - IntIsZero.zero, - GroupElementIsZero.zero, - Colls.emptyColl[Byte] - ) - private val syntheticHeader = CHeader( - Colls.emptyColl[Byte], - ByteIsZero.zero, - Colls.emptyColl[Byte], - Colls.emptyColl[Byte], - CAvlTree(AvlTreeIsZero.zero), - Colls.emptyColl[Byte], - timestamp = LongIsZero.zero, - nBits = LongIsZero.zero, - height = IntIsZero.zero, - extensionRoot = Colls.emptyColl[Byte], - minerPk = GroupElementIsZero.zero, - powOnetimePk = GroupElementIsZero.zero, - powNonce = Colls.emptyColl[Byte], - powDistance = BigIntIsZero.zero, - votes = Colls.emptyColl[Byte] - ) - private val syntheticContext = CostingDataContext( - _dataInputs = Colls.emptyColl, - headers = Colls.emptyColl, - preHeader = syntheticPreHeader, - inputs = Colls.emptyColl, - outputs = Colls.emptyColl, - height = IntIsZero.zero, - selfBox = CostingBox(syntheticBox), - selfIndex = IntIsZero.zero, - lastBlockUtxoRootHash = AvlTreeIsZero.zero, - _minerPubKey = Colls.emptyColl[Byte], - vars = Colls.emptyColl, - activatedScriptVersion = ByteIsZero.zero, - currentErgoTreeVersion = ByteIsZero.zero - ) - - implicit val BoxIsZero: Zero[Box] = CZero(syntheticBox) - implicit val ContextIsZero: Zero[Context] = CZero(syntheticContext) - implicit val HeaderIsZero: Zero[Header] = CZero(syntheticHeader) - implicit val PreHeaderIsZero: Zero[PreHeader] = CZero(syntheticPreHeader) + Colls.emptyColl[Byte], + LongIsZero.zero, + LongIsZero.zero, + IntIsZero.zero, + GroupElementIsZero.zero, + Colls.emptyColl[Byte] + ) + }) + implicit val BoxIsZero: Zero[Box] = CZero({ + new ErgoBox( + LongIsZero.zero, + new ErgoTree( + ByteIsZero.zero, + IndexedSeq.empty, + Right(sigmaPropIsZero.zero) + ), + Colls.emptyColl, + Map.empty, + ModifierId @@ ("synthetic_transaction_id"), + ShortIsZero.zero, + IntIsZero.zero + ) + }) + implicit val ContextIsZero: Zero[Context] = CZero({ + CostingDataContext( + _dataInputs = Colls.emptyColl, + headers = Colls.emptyColl, + preHeader = PreHeaderIsZero.zero, + inputs = Colls.emptyColl, + outputs = Colls.emptyColl, + height = IntIsZero.zero, + selfBox = CostingBox(BoxIsZero.zero), + selfIndex = IntIsZero.zero, + lastBlockUtxoRootHash = AvlTreeIsZero.zero, + _minerPubKey = Colls.emptyColl[Byte], + vars = Colls.emptyColl, + activatedScriptVersion = ByteIsZero.zero, + currentErgoTreeVersion = ByteIsZero.zero + ) + }) + implicit val HeaderIsZero: Zero[Header] = CZero({ + CHeader( + Colls.emptyColl[Byte], + ByteIsZero.zero, + Colls.emptyColl[Byte], + Colls.emptyColl[Byte], + CAvlTree(AvlTreeIsZero.zero), + Colls.emptyColl[Byte], + timestamp = LongIsZero.zero, + nBits = LongIsZero.zero, + height = IntIsZero.zero, + extensionRoot = Colls.emptyColl[Byte], + minerPk = GroupElementIsZero.zero, + powOnetimePk = GroupElementIsZero.zero, + powNonce = Colls.emptyColl[Byte], + powDistance = BigIntIsZero.zero, + votes = Colls.emptyColl[Byte] + ) + }) } From e22a053dbc35a4976cc2d35381e1d13f60e46ff4 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Apr 2023 18:18:18 +0530 Subject: [PATCH 34/42] Fix json round trip test to use res instead of original template --- .../org/ergoplatform/sdk/ContractTemplateSpecification.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 31dad367ef..e88f1a283e 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -24,7 +24,7 @@ class ContractTemplateSpecification extends SerializationSpecification val json = ContractTemplate.jsonEncoder.encoder(obj) val res = ContractTemplate.jsonEncoder.decoder(json.hcursor).right.get res shouldBe obj - val json2 = ContractTemplate.jsonEncoder.encoder(obj) + val json2 = ContractTemplate.jsonEncoder.encoder(res) json shouldBe json2 } From 0022b5ba5fe17cb0ebe6d9592620282da4e59617 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Apr 2023 18:31:34 +0530 Subject: [PATCH 35/42] Group all Zero declarations together --- .../org/ergoplatform/sdk/utils/Zero.scala | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala index 2f5154a339..80b1e03b97 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala @@ -54,32 +54,6 @@ object Zero extends ZeroLowPriority { implicit val AnyIsZero: Zero[Any] = CZero(0) implicit val UnitIsZero: Zero[Unit] = CZero(()) implicit val SigmaDslBuilderIsZero: Zero[SigmaDslBuilder] = CZero(CostingSigmaDslBuilder) - - def typeToZero[T](t: RType[T]): Zero[T] = (t match { - case BooleanType => Zero[Boolean] - case ByteType => Zero[Byte] - case ShortType => Zero[Short] - case IntType => Zero[Int] - case LongType => Zero[Long] - case AnyType => Zero[Any] - case UnitType => Zero[Unit] - case BigIntRType => Zero[BigInt] - case BoxRType => Zero[Box] - case ContextRType => Zero[Context] - case SigmaDslBuilderRType => Zero[SigmaDslBuilder] - case HeaderRType => Zero[Header] - case PreHeaderRType => Zero[PreHeader] - case GroupElementRType => Zero[GroupElement] - case AvlTreeRType => Zero[AvlTree] - case SigmaPropRType => sigmaPropIsZero - case ct: CollType[a] => collIsZero(typeToZero(ct.tItem), ct.tItem) - case ct: OptionType[a] => optionIsZero(typeToZero(ct.tA)) - case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd)) - case tt: TupleType => CZero(tt.emptyArray) - case ft: FuncType[a, b] => funcIsZero(typeToZero(ft.tDom), typeToZero(ft.tRange)) - case _ => sys.error(s"Don't know how to compute Zero for type $t") - }).asInstanceOf[Zero[T]] - implicit val PreHeaderIsZero: Zero[PreHeader] = CZero({ CPreHeader( ByteIsZero.zero, @@ -142,4 +116,28 @@ object Zero extends ZeroLowPriority { votes = Colls.emptyColl[Byte] ) }) + def typeToZero[T](t: RType[T]): Zero[T] = (t match { + case BooleanType => Zero[Boolean] + case ByteType => Zero[Byte] + case ShortType => Zero[Short] + case IntType => Zero[Int] + case LongType => Zero[Long] + case AnyType => Zero[Any] + case UnitType => Zero[Unit] + case BigIntRType => Zero[BigInt] + case BoxRType => Zero[Box] + case ContextRType => Zero[Context] + case SigmaDslBuilderRType => Zero[SigmaDslBuilder] + case HeaderRType => Zero[Header] + case PreHeaderRType => Zero[PreHeader] + case GroupElementRType => Zero[GroupElement] + case AvlTreeRType => Zero[AvlTree] + case SigmaPropRType => sigmaPropIsZero + case ct: CollType[a] => collIsZero(typeToZero(ct.tItem), ct.tItem) + case ct: OptionType[a] => optionIsZero(typeToZero(ct.tA)) + case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd)) + case tt: TupleType => CZero(tt.emptyArray) + case ft: FuncType[a, b] => funcIsZero(typeToZero(ft.tDom), typeToZero(ft.tRange)) + case _ => sys.error(s"Don't know how to compute Zero for type $t") + }).asInstanceOf[Zero[T]] } From 3e42c31356ef3011bdf29401cff6c400edaef2db Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Apr 2023 18:33:14 +0530 Subject: [PATCH 36/42] Minor formatting fix --- sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala index 80b1e03b97..36ee244684 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala @@ -116,6 +116,7 @@ object Zero extends ZeroLowPriority { votes = Colls.emptyColl[Byte] ) }) + def typeToZero[T](t: RType[T]): Zero[T] = (t match { case BooleanType => Zero[Boolean] case ByteType => Zero[Byte] From 61c519255d6fb703c443cf5ca83cf9c32221de1b Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Apr 2023 19:18:21 +0530 Subject: [PATCH 37/42] Remove unsupported types - AnyZero, SigmaDslBuilderIsZero, Zero[Header], Zero[PreHeader], Zero[Context] --- .../org/ergoplatform/sdk/utils/Zero.scala | 62 ++----------------- 1 file changed, 4 insertions(+), 58 deletions(-) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala index 36ee244684..84f31d6334 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala @@ -2,14 +2,14 @@ package org.ergoplatform.sdk.utils import org.ergoplatform.ErgoBox import scalan.RType -import scalan.RType.{AnyType, BooleanType, ByteType, FuncType, IntType, LongType, OptionType, PairType, ShortType, UnitType} +import scalan.RType.{BooleanType, ByteType, FuncType, IntType, LongType, OptionType, PairType, ShortType, UnitType} import scorex.crypto.authds.avltree.batch.BatchAVLProver import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId import sigmastate.{AvlTreeData, AvlTreeFlags, TrivialProp} import sigmastate.Values.ErgoTree import sigmastate.basics.CryptoConstants -import sigmastate.eval.{CAvlTree, CBigInt, CGroupElement, CHeader, CPreHeader, CSigmaProp, Colls, CostingBox, CostingDataContext, CostingSigmaDslBuilder} +import sigmastate.eval.{CAvlTree, CBigInt, CGroupElement, CHeader, CPreHeader, CSigmaProp, Colls, CostingBox, CostingDataContext} import special.Types.TupleType import special.collection.{Coll, CollType} import special.sigma.{AvlTree, AvlTreeRType, BigInt, BigIntRType, Box, BoxRType, Context, ContextRType, GroupElement, GroupElementRType, Header, HeaderRType, PreHeader, PreHeaderRType, SigmaDslBuilder, SigmaDslBuilderRType, SigmaProp, SigmaPropRType} @@ -30,7 +30,7 @@ trait ZeroLowPriority { implicit def collIsZero[T: Zero: RType]: Zero[Coll[T]] = CZero(Colls.emptyColl[T]) implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(None) implicit def pairIsZero[A: Zero, B: Zero]: Zero[(A,B)] = CZero(Zero[A].zero, Zero[B].zero) - implicit def funcIsZero[A: Zero, B: Zero]: Zero[A =>B] = CZero((_ : A) => { Zero[B].zero }) + implicit def funcIsZero[A, B: Zero]: Zero[A =>B] = CZero((_ : A) => { Zero[B].zero }) } object Zero extends ZeroLowPriority { @@ -51,20 +51,7 @@ object Zero extends ZeroLowPriority { CAvlTree(treeData) }) implicit val sigmaPropIsZero: Zero[SigmaProp] = CZero(CSigmaProp(TrivialProp.FalseProp)) - implicit val AnyIsZero: Zero[Any] = CZero(0) implicit val UnitIsZero: Zero[Unit] = CZero(()) - implicit val SigmaDslBuilderIsZero: Zero[SigmaDslBuilder] = CZero(CostingSigmaDslBuilder) - implicit val PreHeaderIsZero: Zero[PreHeader] = CZero({ - CPreHeader( - ByteIsZero.zero, - Colls.emptyColl[Byte], - LongIsZero.zero, - LongIsZero.zero, - IntIsZero.zero, - GroupElementIsZero.zero, - Colls.emptyColl[Byte] - ) - }) implicit val BoxIsZero: Zero[Box] = CZero({ new ErgoBox( LongIsZero.zero, @@ -80,42 +67,6 @@ object Zero extends ZeroLowPriority { IntIsZero.zero ) }) - implicit val ContextIsZero: Zero[Context] = CZero({ - CostingDataContext( - _dataInputs = Colls.emptyColl, - headers = Colls.emptyColl, - preHeader = PreHeaderIsZero.zero, - inputs = Colls.emptyColl, - outputs = Colls.emptyColl, - height = IntIsZero.zero, - selfBox = CostingBox(BoxIsZero.zero), - selfIndex = IntIsZero.zero, - lastBlockUtxoRootHash = AvlTreeIsZero.zero, - _minerPubKey = Colls.emptyColl[Byte], - vars = Colls.emptyColl, - activatedScriptVersion = ByteIsZero.zero, - currentErgoTreeVersion = ByteIsZero.zero - ) - }) - implicit val HeaderIsZero: Zero[Header] = CZero({ - CHeader( - Colls.emptyColl[Byte], - ByteIsZero.zero, - Colls.emptyColl[Byte], - Colls.emptyColl[Byte], - CAvlTree(AvlTreeIsZero.zero), - Colls.emptyColl[Byte], - timestamp = LongIsZero.zero, - nBits = LongIsZero.zero, - height = IntIsZero.zero, - extensionRoot = Colls.emptyColl[Byte], - minerPk = GroupElementIsZero.zero, - powOnetimePk = GroupElementIsZero.zero, - powNonce = Colls.emptyColl[Byte], - powDistance = BigIntIsZero.zero, - votes = Colls.emptyColl[Byte] - ) - }) def typeToZero[T](t: RType[T]): Zero[T] = (t match { case BooleanType => Zero[Boolean] @@ -123,14 +74,9 @@ object Zero extends ZeroLowPriority { case ShortType => Zero[Short] case IntType => Zero[Int] case LongType => Zero[Long] - case AnyType => Zero[Any] case UnitType => Zero[Unit] case BigIntRType => Zero[BigInt] case BoxRType => Zero[Box] - case ContextRType => Zero[Context] - case SigmaDslBuilderRType => Zero[SigmaDslBuilder] - case HeaderRType => Zero[Header] - case PreHeaderRType => Zero[PreHeader] case GroupElementRType => Zero[GroupElement] case AvlTreeRType => Zero[AvlTree] case SigmaPropRType => sigmaPropIsZero @@ -138,7 +84,7 @@ object Zero extends ZeroLowPriority { case ct: OptionType[a] => optionIsZero(typeToZero(ct.tA)) case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd)) case tt: TupleType => CZero(tt.emptyArray) - case ft: FuncType[a, b] => funcIsZero(typeToZero(ft.tDom), typeToZero(ft.tRange)) + case ft: FuncType[a, b] => funcIsZero(typeToZero(ft.tRange)) case _ => sys.error(s"Don't know how to compute Zero for type $t") }).asInstanceOf[Zero[T]] } From 432050ffb9d451c22b59ad5b3cc66eda3db71afe Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Apr 2023 19:40:32 +0530 Subject: [PATCH 38/42] Fix scala 2.11 compilation --- .../org/ergoplatform/sdk/ContractTemplateSpecification.scala | 2 +- .../org/ergoplatform/sdk/generators/ObjectGenerators.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index e88f1a283e..c660f44e02 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -202,7 +202,7 @@ class ContractTemplateSpecification extends SerializationSpecification val templateValues = Seq( Map("p1" -> ByteConstant(10.toByte), "p2" -> ByteConstant(40.toByte), "p3" -> ByteConstant(50.toByte)), Map("p1" -> IntConstant(10), "p2" -> IntConstant(20)), - Map.empty[String, Constant[SType]], + Map.empty[String, Constant[SType]] ) var expectedErgoTreeVersion = (ErgoTree.ConstantSegregationHeader | ergoTreeVersionInTests).toByte if (ergoTreeVersionInTests > 0) { diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala index cf28f3ef96..3104456414 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala @@ -33,8 +33,8 @@ trait ObjectGenerators extends TypeGenerators } private def getConstAndParams(ergoTree: ErgoTree): (IndexedSeq[SType], Option[IndexedSeq[Option[SType#WrappedType]]], IndexedSeq[Parameter]) = { - val paramIndices = ergoTree.constants.indices.filter(_ => math.random() < 0.5).toSet - val noDefaultValueIndices = paramIndices.filter(_ => math.random() < 0.5) + val paramIndices = ergoTree.constants.indices.filter(_ => (math.random() < 0.5)).toSet + val noDefaultValueIndices = paramIndices.filter(_ => (math.random() < 0.5)) val constTypes = ergoTree.constants.map(c => c.tpe) val constValues = getConstValues(ergoTree, noDefaultValueIndices) val parameters = paramIndices From 5d849f0b84d596c73882bb15324fe3fe04815957 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 3 Mar 2023 14:49:52 +0100 Subject: [PATCH 39/42] restructure-part4: rollback safeSimpleName in ReflectionUtil --- .../scala/scalan/util/ReflectionUtil.scala | 21 +++++++++++++++++++ graph-ir/src/main/scala/scalan/Entities.scala | 3 ++- .../src/main/scala/scalan/TypeDescs.scala | 6 +++--- 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 common/shared/src/main/scala/scalan/util/ReflectionUtil.scala diff --git a/common/shared/src/main/scala/scalan/util/ReflectionUtil.scala b/common/shared/src/main/scala/scalan/util/ReflectionUtil.scala new file mode 100644 index 0000000000..dd98f3069e --- /dev/null +++ b/common/shared/src/main/scala/scalan/util/ReflectionUtil.scala @@ -0,0 +1,21 @@ +package scalan.util + +import scala.language.existentials + +object ReflectionUtil { + + implicit class ClassOps(val cl: Class[_]) extends AnyVal { + private def isSpecialChar(c: Char): Boolean = { + ('0' <= c && c <= '9') || c == '$' + } + def safeSimpleName: String = { + if (cl.getEnclosingClass == null) return cl.getSimpleName + val simpleName = cl.getName.substring(cl.getEnclosingClass.getName.length) + val length = simpleName.length + var index = 0 + while (index < length && isSpecialChar(simpleName.charAt(index))) { index += 1 } + // Eventually, this is the empty string iff this is an anonymous class + simpleName.substring(index) + } + } +} diff --git a/graph-ir/src/main/scala/scalan/Entities.scala b/graph-ir/src/main/scala/scalan/Entities.scala index 6c1c062f4c..376bd5a529 100644 --- a/graph-ir/src/main/scala/scalan/Entities.scala +++ b/graph-ir/src/main/scala/scalan/Entities.scala @@ -1,6 +1,7 @@ package scalan import scala.language.higherKinds +import scalan.util.ReflectionUtil.ClassOps /** A slice in the Scalan cake with base classes for various descriptors. */ trait Entities extends TypeDescs { self: Scalan => @@ -13,7 +14,7 @@ trait Entities extends TypeDescs { self: Scalan => def parent: Option[Elem[_]] = None /** Name of the entity type without `Elem` suffix. */ def entityName: String = { - val n = this.getClass.getSimpleName.stripSuffix("Elem") + val n = this.getClass.safeSimpleName.stripSuffix("Elem") n } def convert(x: Ref[Def[_]]): Ref[A] = !!!("should not be called") diff --git a/graph-ir/src/main/scala/scalan/TypeDescs.scala b/graph-ir/src/main/scala/scalan/TypeDescs.scala index 029304bb9e..8963d4faf5 100644 --- a/graph-ir/src/main/scala/scalan/TypeDescs.scala +++ b/graph-ir/src/main/scala/scalan/TypeDescs.scala @@ -5,7 +5,7 @@ import scala.annotation.implicitNotFound import scala.collection.immutable.ListMap import scalan.util._ import scalan.RType._ - +import scalan.util.ReflectionUtil.ClassOps import scala.collection.mutable import debox.cfor import scalan.reflection.{RClass, RConstructor, RMethod} @@ -108,7 +108,7 @@ abstract class TypeDescs extends Base { self: Scalan => lazy val name: String = getName(_.name) // <> to delimit because: [] is used inside name; {} looks bad with structs. - override def toString = s"${getClass.getSimpleName}<$name>" + override def toString = s"${getClass.safeSimpleName}<$name>" } /** Type descriptor of staged types, which correspond to source (unstaged) RTypes @@ -134,7 +134,7 @@ abstract class TypeDescs extends Base { self: Scalan => be.sourceType.name case e => val cl = e.getClass - val name = cl.getSimpleName.stripSuffix("Elem") + val name = cl.safeSimpleName.stripSuffix("Elem") name } if (typeArgs.isEmpty) From af8c5851cbbe25684a1ee1acacf70712668675b8 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Wed, 5 Apr 2023 22:43:59 +0530 Subject: [PATCH 40/42] Fix 2.11 build to pass the CI --- .../org/ergoplatform/sdk/generators/ObjectGenerators.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala index 3104456414..3908760e59 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala @@ -7,6 +7,8 @@ import sigmastate.Values.{ErgoTree, SigmaPropValue} import sigmastate.serialization.generators.{ConcreteCollectionGenerators, TypeGenerators, ObjectGenerators => InterpreterObjectGenerators} import sigmastate.{SType, TestsBase} +import scala.util.Random + trait ObjectGenerators extends TypeGenerators with ValidationSpecification with ConcreteCollectionGenerators @@ -33,8 +35,8 @@ trait ObjectGenerators extends TypeGenerators } private def getConstAndParams(ergoTree: ErgoTree): (IndexedSeq[SType], Option[IndexedSeq[Option[SType#WrappedType]]], IndexedSeq[Parameter]) = { - val paramIndices = ergoTree.constants.indices.filter(_ => (math.random() < 0.5)).toSet - val noDefaultValueIndices = paramIndices.filter(_ => (math.random() < 0.5)) + val paramIndices = ergoTree.constants.indices.filter(_ => (Random.nextDouble() < 0.5)).toSet + val noDefaultValueIndices = paramIndices.filter(_ => (Random.nextDouble() < 0.5)) val constTypes = ergoTree.constants.map(c => c.tpe) val constValues = getConstValues(ergoTree, noDefaultValueIndices) val parameters = paramIndices From 7450df6f247ed5786cda6fdb86885eb7db3e3607 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 11 Apr 2023 16:15:56 +0530 Subject: [PATCH 41/42] Document Zero.scala with Scaladocs --- .../org/ergoplatform/sdk/utils/Zero.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala index 84f31d6334..e899319231 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala @@ -20,12 +20,23 @@ import sigmastate.eval._ import special.sigma._ import scala.language.implicitConversions +/** + * A trait representing the zero value of each type in the ErgoTree. + * @tparam T The type of the zero value. + */ trait Zero[T] { + /** Get the underlying zero value. */ def zero: T } +/** + * A wrapper over the zero value of a type. + * @param zero the zero value of the type T. + * @tparam T The type of the zero value. + */ case class CZero[T](zero: T) extends Zero[T] +/** A trait providing implicit conversions to create instances of Zero for various types. */ trait ZeroLowPriority { implicit def collIsZero[T: Zero: RType]: Zero[Coll[T]] = CZero(Colls.emptyColl[T]) implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(None) @@ -68,6 +79,13 @@ object Zero extends ZeroLowPriority { ) }) + /** + * Returns the zero value of the specified type `T` using the provided runtime type t. + * @param t the runtime type of the value whose zero value is to be returned. + * @tparam T the type of value whose zero value is to be returned. + * @return the zero value of type `T` + * @throws `RuntimeException` if the method is unable to compute the zero value for the specified type + */ def typeToZero[T](t: RType[T]): Zero[T] = (t match { case BooleanType => Zero[Boolean] case ByteType => Zero[Byte] From c728da6201a9ea9fbf05209761edb32a5d24a9f9 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Thu, 8 Jun 2023 17:35:37 +0200 Subject: [PATCH 42/42] v5.0.9-RC: bump Scala 2.13.9 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index ee2a1ebdc5..9d95ffa3a2 100644 --- a/build.sbt +++ b/build.sbt @@ -5,7 +5,7 @@ organization := "org.scorexfoundation" name := "sigma-state" -lazy val scala213 = "2.13.8" +lazy val scala213 = "2.13.9" lazy val scala212 = "2.12.15" lazy val scala211 = "2.11.12"