From 7ccce9a0f9eb702e58d5c70da18fbe53b80c7f47 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 12 Jun 2024 15:20:17 +0300 Subject: [PATCH 01/16] distinct and reverse (w. no costing) --- .../src/main/scala/sigma/ast/methods.scala | 107 ++++++++++++++---- .../sigma/compiler/ir/GraphBuilding.scala | 4 + .../ir/wrappers/sigma/CollsUnit.scala | 2 + .../ir/wrappers/sigma/impl/CollsImpl.scala | 28 +++++ .../utxo/BasicOpsSpecification.scala | 35 ++++++ 5 files changed, 155 insertions(+), 21 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index e4cf0007e0..34f69eb965 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -890,7 +890,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { SFunc(Array(ThisType, tIV, SInt), SInt, paramIVSeq), 26, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(10), chunkSize = 2)) .withIRInfo(MethodCallIrBuilder, javaMethodOf[Coll[_], Any, Int]("indexOf")) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Returns index of a collection element, or -1 if not found") /** Implements evaluation of Coll.indexOf method call ErgoTree node. * Called via reflection based on naming convention. @@ -939,29 +939,94 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + + // 6.0 methods below + val ReverseMethod = SMethod(this, "reverse", + SFunc(Array(ThisType), ThisType, paramIVSeq), + 30, Zip_CostKind) // todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + /** Implements evaluation of Coll.reverse method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def reverse_eval[A](mc: MethodCall, xs: Coll[A]) + (implicit E: ErgoTreeEvaluator): Coll[A] = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.reverse + } + } + + val DistinctMethod = SMethod(this, "distinct", + SFunc(Array(ThisType), ThisType, paramIVSeq), + 31, Zip_CostKind) // todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + /** Implements evaluation of Coll.reverse method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def distinct_eval[A](mc: MethodCall, xs: Coll[A]) + (implicit E: ErgoTreeEvaluator): Coll[A] = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.distinct + } + } + + val StartsWithMethod = SMethod(this, "startsWith", + SFunc(Array(ThisType), ThisType, paramIVSeq), + 32, Zip_CostKind) // todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + val EndsWithMethod = SMethod(this, "endsWith", + SFunc(Array(ThisType), ThisType, paramIVSeq), + 33, Zip_CostKind) // todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + private val v5Methods = Seq( + SizeMethod, + GetOrElseMethod, + MapMethod, + ExistsMethod, + FoldMethod, + ForallMethod, + SliceMethod, + FilterMethod, + AppendMethod, + ApplyMethod, + IndicesMethod, + FlatMapMethod, + PatchMethod, + UpdatedMethod, + UpdateManyMethod, + IndexOfMethod, + ZipMethod + ) + + private val v6Methods = Seq( + ReverseMethod, + DistinctMethod, + StartsWithMethod, + EndsWithMethod + ) + /** This method should be overriden in derived classes to add new methods in addition to inherited. * Typical override: `super.getMethods() ++ Seq(m1, m2, m3)` */ - override protected def getMethods(): Seq[SMethod] = super.getMethods() ++ - Seq( - SizeMethod, - GetOrElseMethod, - MapMethod, - ExistsMethod, - FoldMethod, - ForallMethod, - SliceMethod, - FilterMethod, - AppendMethod, - ApplyMethod, - IndicesMethod, - FlatMapMethod, - PatchMethod, - UpdatedMethod, - UpdateManyMethod, - IndexOfMethod, - ZipMethod - ) + override protected def getMethods(): Seq[SMethod] = { + if(VersionContext.current.isV6SoftForkActivated) { + super.getMethods() ++ v5Methods ++ v6Methods + } else { + super.getMethods() ++ v5Methods + } + } + } object STupleMethods extends MethodsContainer { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 7c7b80d39a..29200bfaeb 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -985,6 +985,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val i = asRep[Int](argsV(0)) val d = asRep[t](argsV(1)) xs.getOrElse(i, d) + case SCollectionMethods.ReverseMethod.name => + xs.reverse + case SCollectionMethods.DistinctMethod.name => + xs.distinct case _ => throwError } case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala index 9b4a002a14..8fe6ff847c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala @@ -29,6 +29,8 @@ import sigma.compiler.ir.{Base, IRContext} def updateMany(indexes: Ref[Coll[Int]], values: Ref[Coll[A]]): Ref[Coll[A]]; def slice(from: Ref[Int], until: Ref[Int]): Ref[Coll[A]]; def append(other: Ref[Coll[A]]): Ref[Coll[A]]; + def reverse: Ref[Coll[A]] + def distinct: Ref[Coll[A]] }; trait CollBuilder extends Def[CollBuilder] { def fromItems[T](items: Ref[T]*)(implicit cT: Elem[T]): Ref[Coll[T]]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala index 0a18ea586a..0ce4c0a178 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala @@ -164,6 +164,20 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](other), true, false, element[Coll[A]])) } + + override def reverse: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(self, + CollClass.getMethod("reverse"), + Array[AnyRef](), + true, false, element[Coll[A]])) + } + + override def distinct: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(self, + CollClass.getMethod("distinct"), + Array[AnyRef](), + true, false, element[Coll[A]])) + } } case class LiftableColl[SA, A](lA: Liftable[SA, A]) @@ -311,6 +325,20 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](other), true, true, element[Coll[A]])) } + + def reverse: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(source, + CollClass.getMethod("reverse"), + Array[AnyRef](), + true, true, element[Coll[A]])) + } + + def distinct: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(source, + CollClass.getMethod("distinct"), + Array[AnyRef](), + true, true, element[Coll[A]])) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..f1545a8867 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import sigma.Extensions.ArrayOps +import sigma.VersionContext import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -157,6 +158,40 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Coll.reverse"){ + def reverseTest() = test("reverse", env, ext, + """{ + | val c1 = Coll(1, 2, 3) + | val c2 = Coll(3, 2, 1) + | c1.reverse == c2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.distinct"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll(1, 2, 3, 3, 2) + | val c2 = Coll(3, 2, 1) + | c1.distinct.reverse == c2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 2fa224bf13db51e77d50481d772224395f3615db Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 12 Jun 2024 19:06:54 +0300 Subject: [PATCH 02/16] startsWith and endsWith --- core/shared/src/main/scala/sigma/Colls.scala | 8 ++ .../scala/sigma/data/CollsOverArrays.scala | 8 ++ .../src/main/scala/sigma/ast/methods.scala | 28 ++++- .../sigma/compiler/ir/GraphBuilding.scala | 6 + .../ir/wrappers/sigma/CollsUnit.scala | 2 + .../ir/wrappers/sigma/impl/CollsImpl.scala | 28 +++++ .../utxo/BasicOpsSpecification.scala | 108 +++++++++++++++++- 7 files changed, 184 insertions(+), 4 deletions(-) diff --git a/core/shared/src/main/scala/sigma/Colls.scala b/core/shared/src/main/scala/sigma/Colls.scala index 625120deac..a74da8bf58 100644 --- a/core/shared/src/main/scala/sigma/Colls.scala +++ b/core/shared/src/main/scala/sigma/Colls.scala @@ -76,6 +76,14 @@ trait Coll[@specialized A] { * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ def zip[@specialized B](ys: Coll[B]): Coll[(A, B)] + /** For this collection (x0, ..., xN) and other collection (y0, ..., yM) + * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ + def startsWith(ys: Coll[A]): Boolean + + /** For this collection (x0, ..., xN) and other collection (y0, ..., yM) + * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ + def endsWith(ys: Coll[A]): Boolean + /** Tests whether a predicate holds for at least one element of this collection. * @param p the predicate used to test elements. * @return `true` if the given predicate `p` is satisfied by at least one element of this collection, otherwise `false` diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index 2413f7f427..506dc001ea 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -39,6 +39,10 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil @inline def zip[@specialized B](ys: Coll[B]): PairColl[A, B] = builder.pairColl(this, ys) + @inline def startsWith(ys: Coll[A]): Boolean = toArray.startsWith(ys.toArray) + + @inline def endsWith(ys: Coll[A]): Boolean = toArray.endsWith(ys.toArray) + def append(other: Coll[A]): Coll[A] = { if (toArray.length <= 0) return other val result = if (VersionContext.current.isJitActivated) { @@ -350,6 +354,10 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R def zip[@specialized B](ys: Coll[B]): PairColl[(L,R), B] = builder.pairColl(this, ys) + def startsWith(ys: Coll[(L, R)]): Boolean = toArray.startsWith(ys.toArray) + + def endsWith(ys: Coll[(L, R)]): Boolean = toArray.endsWith(ys.toArray) + override def indices: Coll[Int] = if (ls.length <= rs.length) ls.indices else rs.indices override def flatMap[B: RType](f: ((L, R)) => Coll[B]): Coll[B] = diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 34f69eb965..d73df90999 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -978,17 +978,41 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val StartsWithMethod = SMethod(this, "startsWith", - SFunc(Array(ThisType), ThisType, paramIVSeq), + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") + /** Implements evaluation of Coll.zip method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def startsWith_eval[A](mc: MethodCall, xs: Coll[A], ys: Coll[A]) + (implicit E: ErgoTreeEvaluator): Boolean = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.startsWith(ys) + } + } + val EndsWithMethod = SMethod(this, "endsWith", - SFunc(Array(ThisType), ThisType, paramIVSeq), + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") + /** Implements evaluation of Coll.zip method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def endsWith_eval[A](mc: MethodCall, xs: Coll[A], ys: Coll[A]) + (implicit E: ErgoTreeEvaluator): Boolean = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.endsWith(ys) + } + } + private val v5Methods = Seq( SizeMethod, GetOrElseMethod, diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 29200bfaeb..4a2c8428c1 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -989,6 +989,12 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => xs.reverse case SCollectionMethods.DistinctMethod.name => xs.distinct + case SCollectionMethods.StartsWithMethod.name => + val ys = asRep[Coll[t]](argsV(0)) + xs.startsWith(ys) + case SCollectionMethods.EndsWithMethod.name => + val ys = asRep[Coll[t]](argsV(0)) + xs.endsWith(ys) case _ => throwError } case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala index 8fe6ff847c..a2d89ddb55 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala @@ -31,6 +31,8 @@ import sigma.compiler.ir.{Base, IRContext} def append(other: Ref[Coll[A]]): Ref[Coll[A]]; def reverse: Ref[Coll[A]] def distinct: Ref[Coll[A]] + def startsWith(ys: Ref[Coll[A]]): Ref[Boolean]; + def endsWith(ys: Ref[Coll[A]]): Ref[Boolean]; }; trait CollBuilder extends Def[CollBuilder] { def fromItems[T](items: Ref[T]*)(implicit cT: Elem[T]): Ref[Coll[T]]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala index 0ce4c0a178..4862c90a41 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala @@ -178,6 +178,20 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](), true, false, element[Coll[A]])) } + + def startsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(self, + CollClass.getMethod("startsWith", classOf[Sym]), + Array[AnyRef](ys), + true, false, element[Boolean])) + } + + def endsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(self, + CollClass.getMethod("endsWith", classOf[Sym]), + Array[AnyRef](ys), + true, false, element[Boolean])) + } } case class LiftableColl[SA, A](lA: Liftable[SA, A]) @@ -339,6 +353,20 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](), true, true, element[Coll[A]])) } + + def startsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(source, + CollClass.getMethod("startsWith", classOf[Sym]), + Array[AnyRef](ys), + true, true, element[Boolean])) + } + + def endsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(source, + CollClass.getMethod("endsWith", classOf[Sym]), + Array[AnyRef](ys), + true, true, element[Boolean])) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index f1545a8867..aa502db7d0 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -163,7 +163,11 @@ class BasicOpsSpecification extends CompilerTestingCommons """{ | val c1 = Coll(1, 2, 3) | val c2 = Coll(3, 2, 1) - | c1.reverse == c2 + | + | val b1 = Coll(INPUTS(0), OUTPUTS(0)) + | val b2 = Coll(OUTPUTS(0), INPUTS(0)) + | + | c1.reverse == c2 && b1.reverse == b2 | }""".stripMargin, null ) @@ -180,7 +184,107 @@ class BasicOpsSpecification extends CompilerTestingCommons """{ | val c1 = Coll(1, 2, 3, 3, 2) | val c2 = Coll(3, 2, 1) - | c1.distinct.reverse == c2 + | + | val h1 = Coll(INPUTS(0), INPUTS(0)) + | val h2 = Coll(INPUTS(0)) + | + | c1.distinct.reverse == c2 && h1.distinct == h2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.startsWith"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll(1, 2, 3) + | val c2 = Coll(1, 2) + | val c3 = Coll(1, 3) + | val c4 = Coll[Int]() + | val c5 = Coll(1, 2, 3, 4) + | + | val b1 = c1.startsWith(c3) + | val b2 = c1.startsWith(c5) + | + | c1.startsWith(c2) && c1.startsWith(c4) && c1.startsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.startsWith - tuples"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll((1, 2), (3, 4), (5, 6)) + | val c2 = Coll((1, 2), (3, 4)) + | val c3 = Coll((1, 3)) + | val c4 = Coll[(Int, Int)]() + | val c5 = Coll((1, 2), (3, 4), (5, 6), (7, 8)) + | + | val b1 = c1.startsWith(c3) + | val b2 = c1.startsWith(c5) + | + | c1.startsWith(c2) && c1.startsWith(c4) && c1.startsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.endsWith"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll(1, 2, 3) + | val c2 = Coll(2, 3) + | val c3 = Coll(2, 2) + | val c4 = Coll[Int]() + | val c5 = Coll(1, 2, 3, 4) + | + | val b1 = c1.endsWith(c3) + | val b2 = c1.endsWith(c5) + | + | c1.endsWith(c2) && c1.endsWith(c4) && c1.endsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.endsWith - tuples"){ + def reverseTest() = test("endsWith tuples", env, ext, + """{ + | val c1 = Coll((1, 2), (2, 3)) + | val c2 = Coll((2, 3)) + | val c3 = Coll((2, 2)) + | val c4 = Coll[(Int, Int)]() + | val c5 = Coll((0, 2), (2, 3)) + | + | val b1 = c1.endsWith(c3) + | val b2 = c1.endsWith(c5) + | + | c1.endsWith(c2) && c1.endsWith(c4) && c1.endsWith(c1) && !b1 && !b2 | }""".stripMargin, null ) From b17c8c81f1dc256276efda7e90c7a762de6999f5 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 12 Jun 2024 22:04:03 +0300 Subject: [PATCH 03/16] get --- core/shared/src/main/scala/sigma/Colls.scala | 16 +++++++++++++++ .../src/main/scala/sigma/ast/methods.scala | 20 ++++++++++++++++++- .../sigma/compiler/ir/GraphBuilding.scala | 3 +++ .../ir/wrappers/sigma/CollsUnit.scala | 1 + .../ir/wrappers/sigma/impl/CollsImpl.scala | 15 ++++++++++++++ .../utxo/BasicOpsSpecification.scala | 18 +++++++++++++++++ 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/core/shared/src/main/scala/sigma/Colls.scala b/core/shared/src/main/scala/sigma/Colls.scala index a74da8bf58..f1633a68a4 100644 --- a/core/shared/src/main/scala/sigma/Colls.scala +++ b/core/shared/src/main/scala/sigma/Colls.scala @@ -45,6 +45,22 @@ trait Coll[@specialized A] { */ def apply(i: Int): A + /** The element at given index. + * Indices start at `0`; `xs.apply(0)` is the first element of collection `xs`. + * Note the indexing syntax `xs(i)` is a shorthand for `xs.apply(i)`. + * + * @param i the index + * @return the element at the given index + * @throws ArrayIndexOutOfBoundsException if `i < 0` or `length <= i` + */ + def get(i: Int): Option[A] = { + if (isDefinedAt(i)) { + Some(apply(i)) + } else { + None + } + } + /** Tests whether this $coll contains given index. * * The implementations of methods `apply` and `isDefinedAt` turn a `Coll[A]` into diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index d73df90999..5cab1118ef 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1013,6 +1013,23 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + val GetMethod = SMethod(this, "get", + SFunc(Array(ThisType, SInt), SOption(tIV), Array[STypeParam](tIV)), 34, ByIndex.costKind) //todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + /** Implements evaluation of Coll.zip method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def get_eval[A](mc: MethodCall, xs: Coll[A], index: Int) + (implicit E: ErgoTreeEvaluator): Option[A] = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => // todo: costing + xs.get(index) + } + } + private val v5Methods = Seq( SizeMethod, GetOrElseMethod, @@ -1037,7 +1054,8 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { ReverseMethod, DistinctMethod, StartsWithMethod, - EndsWithMethod + EndsWithMethod, + GetMethod ) /** This method should be overriden in derived classes to add new methods in addition to inherited. diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 4a2c8428c1..81092ee0c7 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -995,6 +995,9 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SCollectionMethods.EndsWithMethod.name => val ys = asRep[Coll[t]](argsV(0)) xs.endsWith(ys) + case SCollectionMethods.GetMethod.name => + val idx = asRep[Int](argsV(0)) + xs.get(idx) case _ => throwError } case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala index a2d89ddb55..fdc9fadcba 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala @@ -14,6 +14,7 @@ import sigma.compiler.ir.{Base, IRContext} implicit def eA: Elem[A]; def length: Ref[Int]; def apply(i: Ref[Int]): Ref[A]; + def get(index: Ref[Int]): Ref[WOption[A]]; def getOrElse(index: Ref[Int], default: Ref[A]): Ref[A]; def map[B](f: Ref[scala.Function1[A, B]]): Ref[Coll[B]]; def zip[B](ys: Ref[Coll[B]]): Ref[Coll[scala.Tuple2[A, B]]]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala index 4862c90a41..04a7070acf 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala @@ -63,6 +63,14 @@ class CollCls extends EntityObject("Coll") { true, false, element[A])) } + override def get(index: Ref[Int]): Ref[WOption[A]] = { + asRep[WOption[A]](mkMethodCall(self, + CollClass.getMethod("get", classOf[Sym]), + Array[AnyRef](index), + true, false, element[WOption[A]])) + } + + override def map[B](f: Ref[A => B]): Ref[Coll[B]] = { implicit val eB = f.elem.eRange asRep[Coll[B]](mkMethodCall(self, @@ -238,6 +246,13 @@ class CollCls extends EntityObject("Coll") { true, true, element[A])) } + def get(index: Ref[Int]): Ref[WOption[A]] = { + asRep[WOption[A]](mkMethodCall(source, + CollClass.getMethod("get", classOf[Sym]), + Array[AnyRef](index), + true, true, element[WOption[A]])) + } + def map[B](f: Ref[A => B]): Ref[Coll[B]] = { implicit val eB = f.elem.eRange asRep[Coll[B]](mkMethodCall(source, diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index aa502db7d0..8784f4b3c5 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -296,6 +296,24 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Coll.get"){ + def getTest() = test("get", env, ext, + """{ + | val c1 = Coll(1) + | val c2 = Coll[Int]() + | + | c2.get(0).getOrElse(c1.get(0).get) == c1.get(0).get + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + getTest() + } else { + an[Exception] shouldBe thrownBy(getTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From fc3be57fa2223d12312ba8e838d8d4341a8e1c70 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 12 Jun 2024 22:35:35 +0300 Subject: [PATCH 04/16] non-execution of get_eval todo --- data/shared/src/main/scala/sigma/ast/methods.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 5cab1118ef..84fc4955a5 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1024,10 +1024,9 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { */ def get_eval[A](mc: MethodCall, xs: Coll[A], index: Int) (implicit E: ErgoTreeEvaluator): Option[A] = { - val m = mc.method - E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => // todo: costing - xs.get(index) - } + E.addCost(ByIndex.costKind, mc.method.opDesc) //todo: costing + ??? // todo: this get is not actually executed, why? + xs.get(index) } private val v5Methods = Seq( From 62fd2a363976797051bba876704b0ea86f54bd75 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 17 Jun 2024 22:07:59 +0300 Subject: [PATCH 05/16] initial stub and roundtrip test with longToByteArray --- .../src/main/scala/sigma/SigmaDsl.scala | 5 ++- .../src/main/scala/sigma/ast/methods.scala | 31 +++++++++++++++++-- .../scala/sigma/data/CSigmaDslBuilder.scala | 15 ++++++++- .../utxo/BasicOpsSpecification.scala | 18 +++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index df2b419273..18211c7187 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -1,7 +1,8 @@ package sigma -import java.math.BigInteger +import sigma.ast.SType +import java.math.BigInteger import sigma.data._ /** @@ -729,5 +730,7 @@ trait SigmaDslBuilder { /** Returns a byte-wise XOR of the two collections of bytes. */ def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte] + + def fromBigEndianBytes[T](tpe: SType, bytes: Coll[Byte])(implicit cT: RType[T]): T } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index e4cf0007e0..4d5ccb4e02 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,10 +2,11 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ +import sigma.Evaluation.stypeToRType import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} -import sigma.ast.SType.TypeCode +import sigma.ast.SType.{TypeCode, paramT, tT} import sigma.ast.syntax.{SValue, ValueOps} import sigma.data.OverloadHack.Overloaded1 import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} @@ -1519,9 +1520,35 @@ case object SGlobalMethods extends MonoTypeMethods { Xor.xorWithCosting(ls, rs) } - protected override def getMethods() = super.getMethods() ++ Seq( + lazy val fromBigEndianBytesMethod = SMethod( + this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 3, Xor.costKind) // todo: id, cossting + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) // todo: desc + + def fromBigEndianBytes_eval(mc: MethodCall, G: SigmaDslBuilder, bytes: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Any = { + val tpe = mc.tpe + val cT = stypeToRType(tpe) + E.addSeqCost(Xor.costKind, bytes.length, Xor.opDesc) { () => // todo: cost + G.fromBigEndianBytes(tpe, bytes)(cT) + } + } + + private val v5Methods = super.getMethods() ++ Seq( groupGeneratorMethod, xorMethod ) + + private val v6Methods = v5Methods ++ Seq( + fromBigEndianBytesMethod + ) + + protected override def getMethods() = { + if (VersionContext.current.isV6SoftForkActivated) { + v6Methods + } else { + v5Methods + } + } } diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 3938feacd3..b91184ccdb 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -5,7 +5,7 @@ import org.ergoplatform.ErgoBox import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} import scorex.utils.Longs -import sigma.ast.{AtLeast, SubstConstants} +import sigma.ast.{AtLeast, SByte, SLong, SType, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps import sigma.serialization.{GroupElementSerializer, SigmaSerializer} @@ -200,6 +200,19 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => val p = GroupElementSerializer.parse(r) this.GroupElement(p) } + + override def fromBigEndianBytes[T](tpe: SType, bytes: Coll[Byte])(implicit cT: RType[T]): T = { + tpe match { + case SByte => if (bytes.length != 1) { + throw new IllegalArgumentException("To deserialize SByte with fromBigEndianBytes, exactly one byte should be provided") + } else { + bytes.apply(0).asInstanceOf[T] + } + case SLong => Longs.fromByteArray(bytes.toArray).asInstanceOf[T] + case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") + // todo: more types + } + } } /** Default singleton instance of Global object, which implements global ErgoTree functions. */ diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..46fa24de96 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import sigma.Extensions.ArrayOps +import sigma.VersionContext import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -157,6 +158,23 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Global.fromBigEndianBytes") { + def fromTest() = test("R1", env, ext, + s"""{ + | val l = -1000L + | val ba = longToByteArray(l) + | Global.fromBigEndianBytes[Long](ba) == l + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 8bcc1efbbfe34cc034720395dc3699235804e9e7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 18 Jun 2024 22:57:07 +0300 Subject: [PATCH 06/16] impl and tests done without costing etc --- .../src/main/scala/sigma/ast/methods.scala | 2 +- .../scala/sigma/data/CSigmaDslBuilder.scala | 32 ++++++-- .../sigma/compiler/ir/GraphBuilding.scala | 6 +- .../sigma/compiler/ir/TreeBuilding.scala | 19 ++++- .../ir/wrappers/sigma/SigmaDslUnit.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 14 ++++ .../utxo/BasicOpsSpecification.scala | 75 ++++++++++++++++++- 7 files changed, 136 insertions(+), 13 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 4d5ccb4e02..c1a59fea2c 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1521,7 +1521,7 @@ case object SGlobalMethods extends MonoTypeMethods { } lazy val fromBigEndianBytesMethod = SMethod( - this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 3, Xor.costKind) // todo: id, cossting + this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 30, Xor.costKind, Seq(tT)) // todo: id, cossting .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) // todo: desc diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index b91184ccdb..2e0b3912fc 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -4,11 +4,11 @@ import debox.cfor import org.ergoplatform.ErgoBox import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} -import scorex.utils.Longs -import sigma.ast.{AtLeast, SByte, SLong, SType, SubstConstants} +import scorex.utils.{Ints, Longs, Shorts} +import sigma.ast.{AtLeast, SBigInt, SByte, SInt, SLong, SShort, SType, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps -import sigma.serialization.{GroupElementSerializer, SigmaSerializer} +import sigma.serialization.{CoreDataSerializer, DataSerializer, GroupElementSerializer, SerializerException, SigmaSerializer} import sigma.util.Extensions.BigIntegerOps import sigma.validation.SigmaValidationSettings import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext} @@ -208,9 +208,31 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => } else { bytes.apply(0).asInstanceOf[T] } - case SLong => Longs.fromByteArray(bytes.toArray).asInstanceOf[T] + case SShort => if (bytes.length != 2) { + throw new IllegalArgumentException("To deserialize SShort with fromBigEndianBytes, exactly two bytes should be provided") + } else { + val b0 = bytes(0) + val b1 = bytes(1) + ((b0 & 0xFF) << 8 | (b1 & 0xFF)).toShort.asInstanceOf[T] + } + case SInt => if (bytes.length != 4) { + throw new IllegalArgumentException("To deserialize SInt with fromBigEndianBytes, exactly four bytes should be provided") + } else { + Ints.fromByteArray(bytes.toArray).asInstanceOf[T] + } + case SLong => if (bytes.length != 8) { + throw new IllegalArgumentException("To deserialize SLong with fromBigEndianBytes, exactly eight bytes should be provided") + } else { + Longs.fromByteArray(bytes.toArray).asInstanceOf[T] + } + case SBigInt => + if (bytes.length > SBigInt.MaxSizeInBytes) { + throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") + } + CBigInt(new BigInteger(bytes.toArray)).asInstanceOf[T] + // todo: UnsignedBitInt case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") - // todo: more types + } } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 7c7b80d39a..23abf72d06 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -928,7 +928,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => sigmaDslBuilder.decodePoint(bytes) // fallback rule for MethodCall, should be the last case in the list - case sigma.ast.MethodCall(obj, method, args, _) => + case sigma.ast.MethodCall(obj, method, args, typeSubst) => val objV = eval(obj) val argsV = args.map(eval) (objV, method.objType) match { @@ -1146,6 +1146,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val c1 = asRep[Coll[Byte]](argsV(0)) val c2 = asRep[Coll[Byte]](argsV(1)) g.xor(c1, c2) + case SGlobalMethods.fromBigEndianBytesMethod.name => + val bytes = asRep[Coll[Byte]](argsV(0)) + val cT = stypeToElem(method.stype.tRange.withSubstTypes(typeSubst)) + g.fromBigEndianBytes(bytes)(cT) case _ => throwError } case _ => throwError diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala index 725e3b1d19..ffe8ab1460 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -1,6 +1,8 @@ package sigma.compiler.ir import org.ergoplatform._ +import sigma.Evaluation.{rtypeToSType, stypeToRType} +import sigma.ast.SType.tT import sigma.ast._ import sigma.ast.syntax.{ValueOps, _} import sigma.data.{ProveDHTuple, ProveDlog} @@ -399,13 +401,24 @@ trait TreeBuilding extends Base { IR: IRContext => mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement) // Fallback MethodCall rule: should be the last in this list of cases - case Def(MethodCall(objSym, m, argSyms, _)) => + case Def(mc @ MethodCall(objSym, m, argSyms, _)) => val obj = recurse[SType](objSym) val args = argSyms.collect { case argSym: Sym => recurse[SType](argSym) } MethodsContainer.getMethod(obj.tpe, m.getName) match { case Some(method) => - val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)) - builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, Map()) + + val typeSubst: STypeSubst = { + if (method.hasExplicitTypeArgs) { + val cT = rtypeToSType(mc.resultType.sourceType) + Map(tT -> cT) + } else { + Map.empty + } + } + + val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)).withConcreteTypes(typeSubst) + + builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, typeSubst) case None => error(s"Cannot find method ${m.getName} in object $obj") } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index 2a6a341686..b5b4287950 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -114,6 +114,7 @@ import scalan._ /** This method will be used in v6.0 to handle CreateAvlTree operation in GraphBuilding */ def avlTree(operationFlags: Ref[Byte], digest: Ref[Coll[Byte]], keyLength: Ref[Int], valueLengthOpt: Ref[WOption[Int]]): Ref[AvlTree]; def xor(l: Ref[Coll[Byte]], r: Ref[Coll[Byte]]): Ref[Coll[Byte]] + def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] }; trait CostModelCompanion; trait BigIntCompanion; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index c113cb7de3..ad0450a1a7 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -1945,6 +1945,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { Array[AnyRef](l, r), true, false, element[Coll[Byte]])) } + + override def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = { + asRep[T](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](bytes, cT), + true, false, cT)) + } } implicit object LiftableSigmaDslBuilder @@ -2104,6 +2111,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { Array[AnyRef](l, r), true, true, element[Coll[Byte]])) } + + def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = { + asRep[T](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](bytes, cT), + true, true, cT)) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 46fa24de96..fc8221f983 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -2,6 +2,8 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ +import scorex.util.encode.Base16 +import scorex.utils.Ints import sigma.Extensions.ArrayOps import sigma.VersionContext import sigma.ast.SCollection.SByteArray @@ -158,10 +160,58 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } - property("Global.fromBigEndianBytes") { - def fromTest() = test("R1", env, ext, + property("Global.fromBigEndianBytes - byte") { + def fromTest() = test("fromBigEndianBytes - byte", env, ext, s"""{ - | val l = -1000L + | val ba = Coll(5.toByte) + | Global.fromBigEndianBytes[Byte](ba) == 5 + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - short") { + def fromTest() = test("fromBigEndianBytes - short", env, ext, + s"""{ + | val ba = Coll(5.toByte, 5.toByte) + | Global.fromBigEndianBytes[Short](ba) != 0 + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - int") { + def fromTest() = test("fromBigEndianBytes - int", env, ext, + s"""{ + | val ba = fromBase16("${Base16.encode(Ints.toByteArray(Int.MaxValue))}") + | Global.fromBigEndianBytes[Int](ba) == ${Int.MaxValue} + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - long") { + def fromTest() = test("fromBigEndianBytes - long", env, ext, + s"""{ + | val l = 1088800L | val ba = longToByteArray(l) | Global.fromBigEndianBytes[Long](ba) == l |} @@ -175,6 +225,25 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Global.fromBigEndianBytes - bigInt") { + val bi = new BigInteger("9785856985394593489356430476450674590674598659865986594859056865984690568904") + def fromTest() = test("fromBigEndianBytes - bigInt", env, ext, + s"""{ + | val ba = fromBase16("${Base16.encode(bi.toByteArray)}") + | Global.fromBigEndianBytes[BigInt](ba) == bigInt("$bi") + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + + // todo: roundtrip with .toBytes + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 649069b6b10241258c6535bb27dfe181db8158aa Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 19 Aug 2024 14:24:02 +0300 Subject: [PATCH 07/16] fixed cost, improved args list for SigmaDslBuilder.fromBigEndianBytes --- .../src/main/scala/sigma/SigmaDsl.scala | 3 ++- .../src/main/scala/sigma/ast/methods.scala | 21 ++++++++----------- .../scala/sigma/data/CSigmaDslBuilder.scala | 21 +++++++++---------- .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 4 +++- .../utxo/BasicOpsSpecification.scala | 1 - 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 18211c7187..eaf21b5007 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -731,6 +731,7 @@ trait SigmaDslBuilder { /** Returns a byte-wise XOR of the two collections of bytes. */ def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte] - def fromBigEndianBytes[T](tpe: SType, bytes: Coll[Byte])(implicit cT: RType[T]): T + /** Returns a number decoded from provided big-endian bytes array. */ + def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index c1a59fea2c..5e4a1752c9 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1520,19 +1520,16 @@ case object SGlobalMethods extends MonoTypeMethods { Xor.xorWithCosting(ls, rs) } + private val BigEndianBytesCostKind = FixedCost(JitCost(10)) + lazy val fromBigEndianBytesMethod = SMethod( - this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 30, Xor.costKind, Seq(tT)) // todo: id, cossting - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) // todo: desc - - def fromBigEndianBytes_eval(mc: MethodCall, G: SigmaDslBuilder, bytes: Coll[Byte]) - (implicit E: ErgoTreeEvaluator): Any = { - val tpe = mc.tpe - val cT = stypeToRType(tpe) - E.addSeqCost(Xor.costKind, bytes.length, Xor.opDesc) { () => // todo: cost - G.fromBigEndianBytes(tpe, bytes)(cT) - } - } + this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 10, BigEndianBytesCostKind, Seq(tT)) // todo: id + .withIRInfo(MethodCallIrBuilder, + javaMethodOf[SigmaDslBuilder, Coll[Byte], RType[_]]("fromBigEndianBytes"), + { mtype => Array(mtype.tRange) }) + .withInfo(MethodCall, + "Decode a number from big endian bytes.", + ArgInfo("first", "Bytes which are big-endian encoded number.")) private val v5Methods = super.getMethods() ++ Seq( groupGeneratorMethod, diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 2e0b3912fc..b74b39c654 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -4,11 +4,11 @@ import debox.cfor import org.ergoplatform.ErgoBox import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} -import scorex.utils.{Ints, Longs, Shorts} -import sigma.ast.{AtLeast, SBigInt, SByte, SInt, SLong, SShort, SType, SubstConstants} +import scorex.utils.{Ints, Longs} +import sigma.ast.{AtLeast, SBigInt, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps -import sigma.serialization.{CoreDataSerializer, DataSerializer, GroupElementSerializer, SerializerException, SigmaSerializer} +import sigma.serialization.{GroupElementSerializer, SerializerException, SigmaSerializer} import sigma.util.Extensions.BigIntegerOps import sigma.validation.SigmaValidationSettings import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext} @@ -201,38 +201,37 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => this.GroupElement(p) } - override def fromBigEndianBytes[T](tpe: SType, bytes: Coll[Byte])(implicit cT: RType[T]): T = { - tpe match { - case SByte => if (bytes.length != 1) { + override def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T = { + cT match { + case sigma.ByteType => if (bytes.length != 1) { throw new IllegalArgumentException("To deserialize SByte with fromBigEndianBytes, exactly one byte should be provided") } else { bytes.apply(0).asInstanceOf[T] } - case SShort => if (bytes.length != 2) { + case sigma.ShortType => if (bytes.length != 2) { throw new IllegalArgumentException("To deserialize SShort with fromBigEndianBytes, exactly two bytes should be provided") } else { val b0 = bytes(0) val b1 = bytes(1) ((b0 & 0xFF) << 8 | (b1 & 0xFF)).toShort.asInstanceOf[T] } - case SInt => if (bytes.length != 4) { + case sigma.IntType => if (bytes.length != 4) { throw new IllegalArgumentException("To deserialize SInt with fromBigEndianBytes, exactly four bytes should be provided") } else { Ints.fromByteArray(bytes.toArray).asInstanceOf[T] } - case SLong => if (bytes.length != 8) { + case sigma.LongType => if (bytes.length != 8) { throw new IllegalArgumentException("To deserialize SLong with fromBigEndianBytes, exactly eight bytes should be provided") } else { Longs.fromByteArray(bytes.toArray).asInstanceOf[T] } - case SBigInt => + case sigma.BigIntRType => if (bytes.length > SBigInt.MaxSizeInBytes) { throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") } CBigInt(new BigInteger(bytes.toArray)).asInstanceOf[T] // todo: UnsignedBitInt case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") - } } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index ad0450a1a7..c42533993c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -2135,7 +2135,9 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { override protected def collectMethods: Map[RMethod, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[SigmaDslBuilder]), RClass(classOf[SSigmaDslBuilder]), Set( - "Colls", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants", "decodePoint", "avlTree", "xor" + "Colls", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256", + "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants", + "decodePoint", "avlTree", "xor", "fromBigEndianBytes" )) } } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index ed97bf3f27..37141585c4 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,7 +3,6 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import scorex.util.encode.Base16 -import scorex.util.encode.Base16 import scorex.utils.Ints import sigma.Extensions.ArrayOps import sigma.VersionContext From 877ed789ead40ae29811969e3b0f25feb0a68209 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 30 Aug 2024 19:30:44 +0300 Subject: [PATCH 08/16] adding new methods to GraphIRReflection --- .../src/main/scala/sigma/ast/methods.scala | 38 ++++++------------- .../sigma/compiler/ir/GraphIRReflection.scala | 16 ++++++++ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 84fc4955a5..0b69b4a2c0 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -922,8 +922,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { baseCost = JitCost(10), perChunkCost = JitCost(1), chunkSize = 10) val ZipMethod = SMethod(this, "zip", - SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)), - 29, Zip_CostKind) + SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)), 29, Zip_CostKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -939,11 +938,10 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + // ======== 6.0 methods below =========== - // 6.0 methods below val ReverseMethod = SMethod(this, "reverse", - SFunc(Array(ThisType), ThisType, paramIVSeq), - 30, Zip_CostKind) // todo: costing + SFunc(Array(ThisType), ThisType, paramIVSeq), 30, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -960,8 +958,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val DistinctMethod = SMethod(this, "distinct", - SFunc(Array(ThisType), ThisType, paramIVSeq), - 31, Zip_CostKind) // todo: costing + SFunc(Array(ThisType), ThisType, paramIVSeq), 31, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -978,8 +975,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val StartsWithMethod = SMethod(this, "startsWith", - SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), - 32, Zip_CostKind) // todo: costing + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -996,8 +992,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val EndsWithMethod = SMethod(this, "endsWith", - SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), - 33, Zip_CostKind) // todo: costing + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -1018,18 +1013,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") - /** Implements evaluation of Coll.zip method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def get_eval[A](mc: MethodCall, xs: Coll[A], index: Int) - (implicit E: ErgoTreeEvaluator): Option[A] = { - E.addCost(ByIndex.costKind, mc.method.opDesc) //todo: costing - ??? // todo: this get is not actually executed, why? - xs.get(index) - } - - private val v5Methods = Seq( + private val v5Methods = super.getMethods() ++ Seq( SizeMethod, GetOrElseMethod, MapMethod, @@ -1049,7 +1033,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { ZipMethod ) - private val v6Methods = Seq( + private val v6Methods = v5Methods ++ Seq( ReverseMethod, DistinctMethod, StartsWithMethod, @@ -1061,10 +1045,10 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { * Typical override: `super.getMethods() ++ Seq(m1, m2, m3)` */ override protected def getMethods(): Seq[SMethod] = { - if(VersionContext.current.isV6SoftForkActivated) { - super.getMethods() ++ v5Methods ++ v6Methods + if (VersionContext.current.isV6SoftForkActivated) { + v6Methods } else { - super.getMethods() ++ v5Methods + v5Methods } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala index 69736a0224..829a37f871 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -202,6 +202,22 @@ object GraphIRReflection { }, mkMethod(clazz, "exists", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.Coll[Any]].exists(args(0).asInstanceOf[ctx.Ref[Any => Boolean]]) + }, + // V6 methods + mkMethod(clazz, "reverse", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[ctx.Coll[Any]].reverse + }, + mkMethod(clazz, "distinct", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[ctx.Coll[Any]].distinct + }, + mkMethod(clazz, "startsWith", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Coll[Any]].startsWith(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Any]]]) + }, + mkMethod(clazz, "endsWith", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Coll[Any]].endsWith(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Any]]]) + }, + mkMethod(clazz, "get", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Coll[_]].apply(args(0).asInstanceOf[ctx.Ref[Int]]) } ) ) From 8136c8f620b9097cd6313d4971aafbc113507a50 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 24 Sep 2024 19:37:07 +0300 Subject: [PATCH 09/16] new methods added to ReflectionData --- .../scala/sigma/reflection/ReflectionData.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index a2de9dd8c5..bfe7a639e3 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -162,6 +162,9 @@ object ReflectionData { mkMethod(clazz, "apply", Array[Class[_]](classOf[Int])) { (obj, args) => obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int]) }, + mkMethod(clazz, "get", Array[Class[_]](classOf[Int])) { (obj, args) => + obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int]) + }, mkMethod(clazz, "append", Array[Class[_]](classOf[Coll[_]])) { (obj, args) => obj.asInstanceOf[Coll[Any]].append(args(0).asInstanceOf[Coll[Any]]) }, @@ -170,6 +173,18 @@ object ReflectionData { }, mkMethod(clazz, "map", Array[Class[_]](classOf[Function1[_, _]], classOf[RType[_]])) { (obj, args) => obj.asInstanceOf[Coll[Any]].map(args(0).asInstanceOf[Any => Any])(args(1).asInstanceOf[RType[Any]]) + }, + mkMethod(clazz, "reverse", Array[Class[_]]()) { (obj, args) => + obj.asInstanceOf[Coll[Any]].reverse + }, + mkMethod(clazz, "distinct", Array[Class[_]]()) { (obj, args) => + obj.asInstanceOf[Coll[Any]].distinct + }, + mkMethod(clazz, "startsWith", Array[Class[_]](classOf[Coll[_]])) { (obj, args) => + obj.asInstanceOf[Coll[Any]].startsWith(args(0).asInstanceOf[Coll[Any]]) + }, + mkMethod(clazz, "endsWith", Array[Class[_]](classOf[Coll[_]])) { (obj, args) => + obj.asInstanceOf[Coll[Any]].endsWith(args(0).asInstanceOf[Coll[Any]]) } ) ) From 586aaddc6c7b2aec1dd195f8d15020490060f6fa Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 26 Sep 2024 19:35:02 +0300 Subject: [PATCH 10/16] LSV6 tests, predefined method --- .../sigma/reflection/ReflectionData.scala | 3 + .../main/scala/sigma/ast/SigmaPredef.scala | 22 ++++- .../scala/sigma/LanguageSpecificationV6.scala | 94 ++++++++++++++++++- .../sigmastate/ErgoTreeSpecification.scala | 2 +- .../utxo/BasicOpsSpecification.scala | 19 +++- 5 files changed, 133 insertions(+), 7 deletions(-) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index a2de9dd8c5..ef0b37de10 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -450,6 +450,9 @@ object ReflectionData { }, mkMethod(clazz, "decodePoint", Array[Class[_]](cColl)) { (obj, args) => obj.asInstanceOf[SigmaDslBuilder].decodePoint(args(0).asInstanceOf[Coll[Byte]]) + }, + mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](cColl, classOf[RType[_]])) { (obj, args) => + obj.asInstanceOf[SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[Coll[Byte]])(args(1).asInstanceOf[RType[_]]) } ) ) diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 8b89851938..96573d095d 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -420,6 +420,25 @@ object SigmaPredef { ) ) + val FromBigEndianBytesFunc = PredefinedFunc("fromBigEndianBytes", + Lambda(Seq(paramT), Array("bytes" -> SByteArray), tT, None), + irInfo = PredefFuncInfo( + irBuilder = { case (u, args) => + val resType = u.opType.tRange.asInstanceOf[SFunc].tRange + MethodCall( + Global, + SGlobalMethods.fromBigEndianBytesMethod.withConcreteTypes(Map(tT -> resType)), + args.toIndexedSeq, + Map(tT -> resType) + ) + }), + docInfo = OperationInfo(MethodCall, + """Deserializes provided big endian bytes into a numeric value of given type. + """.stripMargin, + Seq(ArgInfo("bytes", "bytes to deserialize")) + ) + ) + val globalFuncs: Map[String, PredefinedFunc] = Seq( AllOfFunc, AnyOfFunc, @@ -448,7 +467,8 @@ object SigmaPredef { SubstConstantsFunc, ExecuteFromVarFunc, ExecuteFromSelfRegFunc, - SerializeFunc + SerializeFunc, + FromBigEndianBytesFunc ).map(f => f.name -> f).toMap def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 7605043cea..bbfd260f21 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -11,11 +11,10 @@ import sigma.ast.ErgoTree.ZeroHeader import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast.{SInt, _} -import sigma.data.{CBigInt, CBox, CHeader, ExactNumeric} +import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, RType} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode -import sigma.data.{RType} -import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} +import sigma.util.Extensions.{BooleanOps, IntOps} import sigmastate.exceptions.MethodNotFound import sigmastate.utils.Extensions.ByteOpsForSigma import sigmastate.utils.Helpers @@ -1523,4 +1522,93 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + + property("Global - fromBigEndianBytes") { + import sigma.data.OrderingOps.BigIntOrdering + + def byteFromBigEndianBytes: Feature[Byte, Boolean] = { + newFeature( + { (x: Byte) => CSigmaDslBuilder.fromBigEndianBytes[Byte](Colls.fromArray(Array(x))) == x}, + "{ (x: Byte) => fromBigEndianBytes[Byte](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + 5.toByte -> new Expected(ExpectedResult(Success(true), None)), + Byte.MaxValue -> new Expected(ExpectedResult(Success(true), None)), + Byte.MinValue -> new Expected(ExpectedResult(Success(true), None)) + ), + byteFromBigEndianBytes + ) + + def shortFromBigEndianBytes: Feature[Short, Boolean] = { + newFeature( + { (x: Short) => CSigmaDslBuilder.fromBigEndianBytes[Short](Colls.fromArray(Shorts.toByteArray(x))) == x}, + "{ (x: Short) => fromBigEndianBytes[Short](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + 5.toShort -> new Expected(ExpectedResult(Success(true), None)), + Short.MaxValue -> new Expected(ExpectedResult(Success(true), None)), + Short.MinValue -> new Expected(ExpectedResult(Success(true), None)) + ), + shortFromBigEndianBytes + ) + + def intFromBigEndianBytes: Feature[Int, Boolean] = { + newFeature( + { (x: Int) => CSigmaDslBuilder.fromBigEndianBytes[Int](Colls.fromArray(Ints.toByteArray(x))) == x}, + "{ (x: Int) => fromBigEndianBytes[Int](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + 5 -> new Expected(ExpectedResult(Success(true), None)), + Int.MaxValue -> new Expected(ExpectedResult(Success(true), None)) + ), + intFromBigEndianBytes + ) + + def longFromBigEndianBytes: Feature[Long, Boolean] = { + newFeature( + { (x: Long) => CSigmaDslBuilder.fromBigEndianBytes[Long](Colls.fromArray(Longs.toByteArray(x))) == x}, + "{ (x: Long) => fromBigEndianBytes[Long](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + 5L -> new Expected(ExpectedResult(Success(true), None)), + Long.MinValue -> new Expected(ExpectedResult(Success(true), None)) + ), + longFromBigEndianBytes + ) + + def bigIntFromBigEndianBytes: Feature[BigInt, Boolean] = { + newFeature( + { (x: BigInt) => CSigmaDslBuilder.fromBigEndianBytes[BigInt](x.toBytes) == x}, + "{ (x: BigInt) => Global.fromBigEndianBytes[BigInt](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + CBigInt(BigInteger.valueOf(50)) -> new Expected(ExpectedResult(Success(true), None)), + CBigInt(BigInteger.valueOf(-500000000000L)) -> new Expected(ExpectedResult(Success(true), None)), + CBigInt(sigma.crypto.CryptoConstants.groupOrder.divide(BigInteger.valueOf(2))) -> new Expected(ExpectedResult(Success(true), None)) + ), + bigIntFromBigEndianBytes + ) + + } + } diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 4403716e5a..fee93248f7 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -516,7 +516,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) ) ++ (if (isV6Activated) { - Seq(MInfo(3, serializeMethod)) // methods added in v6.0 + Seq(MInfo(3, serializeMethod), MInfo(5, fromBigEndianBytesMethod)) // methods added in v6.0 } else { Seq.empty[MInfo] }), true) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 47815ea41f..16ffaa3dbb 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -622,6 +622,23 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Global.fromBigEndianBytes - Long.toBytes") { + def fromTest() = test("fromBigEndianBytes - long", env, ext, + s"""{ + | val l = 1088800L + | val ba = l.toBytes + | Global.fromBigEndianBytes[Long](ba) == l + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + property("Global.fromBigEndianBytes - bigInt") { val bi = new BigInteger("9785856985394593489356430476450674590674598659865986594859056865984690568904") def fromTest() = test("fromBigEndianBytes - bigInt", env, ext, @@ -639,8 +656,6 @@ class BasicOpsSpecification extends CompilerTestingCommons } } - // todo: roundtrip with .toBytes - property("Int.toBytes") { def toBytesTest() = test("Int.toBytes", env, ext, """{ From 2cdc34eaeb57fc77824f65f4379887e4feb5ab76 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 27 Sep 2024 00:20:34 +0300 Subject: [PATCH 11/16] fixing JS tests --- .../src/main/scala/sigma/compiler/ir/GraphIRReflection.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala index dcdce623ac..79fba9bb96 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -1,5 +1,6 @@ package sigma.compiler.ir +import sigma.ast.SType import sigma.compiler.ir.primitives.Thunks import sigma.data.RType import sigma.reflection.ReflectionData.registerClassEntry @@ -510,6 +511,9 @@ object GraphIRReflection { }, mkMethod(clazz, "serialize", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.SigmaDslBuilder].serialize(args(0).asInstanceOf[ctx.Ref[Any]]) + }, + mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])(args(1).asInstanceOf[ctx.Elem[SType]]) } ) ) From d738ac54f5d4b96a5f5927e96bc1210fb9b02fc4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 27 Sep 2024 14:02:15 +0300 Subject: [PATCH 12/16] fixing JS tests --- .../scala/sigma/reflection/ReflectionData.scala | 2 +- .../main/scala/sigma/SigmaDataReflection.scala | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index bfe7a639e3..d2165711f6 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -163,7 +163,7 @@ object ReflectionData { obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int]) }, mkMethod(clazz, "get", Array[Class[_]](classOf[Int])) { (obj, args) => - obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int]) + obj.asInstanceOf[Coll[_]].get(args(0).asInstanceOf[Int]) }, mkMethod(clazz, "append", Array[Class[_]](classOf[Coll[_]])) { (obj, args) => obj.asInstanceOf[Coll[Any]].append(args(0).asInstanceOf[Coll[Any]]) diff --git a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala index c64bdee877..341ee647b3 100644 --- a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala +++ b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala @@ -317,6 +317,22 @@ object SigmaDataReflection { mkMethod(clazz, "flatMap_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Function1[_,_]], classOf[ErgoTreeEvaluator])) { (obj, args) => obj.asInstanceOf[SCollectionMethods.type].flatMap_eval(args(0).asInstanceOf[MethodCall], args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Any => Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "reverse_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].reverse_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "distinct_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].distinct_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "startsWith_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].startsWith_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "endsWith_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].endsWith_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) } ) ) From 1989d197dc9461683bd351e31be540271ac94bec Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 27 Sep 2024 15:15:07 +0300 Subject: [PATCH 13/16] LSV6 tests --- .../scala/sigma/LanguageSpecificationV6.scala | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 7605043cea..d9797e36bf 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1523,4 +1523,94 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + property("Coll.reverse") { + val f = newFeature[Coll[Int], Coll[Int]]( + { (xs: Coll[Int]) => xs.reverse }, + """{(xs: Coll[Int]) => xs.reverse }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + Coll(1, 2) -> Expected(ExpectedResult(Success(Coll(2, 1)), None)), + Coll[Int]() -> Expected(ExpectedResult(Success(Coll[Int]()), None)) + ), + f + ) + } + + property("Coll.distinct") { + val f = newFeature[Coll[Int], Coll[Int]]( + { (xs: Coll[Int]) => xs.distinct }, + """{(xs: Coll[Int]) => xs.distinct }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + Coll(1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll(1, 1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll(1, 2, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll[Int]() -> Expected(ExpectedResult(Success(Coll[Int]()), None)) + ), + f + ) + } + + property("Coll.startsWith") { + val f = newFeature[(Coll[Int], Coll[Int]), Boolean]( + { (xs: (Coll[Int], Coll[Int])) => xs._1.startsWith(xs._2) }, + """{(xs: (Coll[Int], Coll[Int])) => xs._1.startsWith(xs._2) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + (Coll(1, 2, 3), Coll(1, 2)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(1, 2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(1, 2, 3, 4)) -> Expected(ExpectedResult(Success(false), None)), + (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) + ), + f + ) + } + + property("Coll.endsWith") { + val f = newFeature[(Coll[Int], Coll[Int]), Boolean]( + { (xs: (Coll[Int], Coll[Int])) => xs._1.endsWith(xs._2) }, + """{(xs: (Coll[Int], Coll[Int])) => xs._1.endsWith(xs._2) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + (Coll(1, 2, 3), Coll(1, 2)) -> Expected(ExpectedResult(Success(false), None)), + (Coll(1, 2, 3), Coll(2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(2, 3, 4)) -> Expected(ExpectedResult(Success(false), None)), + (Coll(1, 2, 3), Coll(1, 2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) + ), + f + ) + } + + property("Coll.get") { + val f = newFeature[(Coll[Int], Int), Option[Int]]( + { (xs: (Coll[Int], Int)) => xs._1.get(xs._2) }, + """{(xs: (Coll[Int], Int)) => xs._1.get(xs._2) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + (Coll(1, 2), 0) -> Expected(ExpectedResult(Success(Some(1)), None)), + (Coll(1, 2), 1) -> Expected(ExpectedResult(Success(Some(2)), None)), + (Coll(1, 2), -1) -> Expected(ExpectedResult(Success(None), None)), + (Coll(1, 2), 2) -> Expected(ExpectedResult(Success(None), None)), + (Coll[Int](), 0) -> Expected(ExpectedResult(Success(None), None)) + ), + f + ) + } + } From 6459b07aa6b92ee6131af7a35fa9f63aa428b927 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 27 Sep 2024 16:06:10 +0300 Subject: [PATCH 14/16] fix ErgoTreeSpec --- .../src/test/scala/sigmastate/ErgoTreeSpecification.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 4403716e5a..f5db0217dc 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -561,7 +561,9 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C EndsWithMethod, MapReduceMethod, */ - ), true) + ) ++ (if (isV6Activated) { + Seq(MInfo(30, ReverseMethod), MInfo(31, DistinctMethod), MInfo(32, StartsWithMethod), MInfo(33, EndsWithMethod), MInfo(34, GetMethod)) + } else Seq.empty), true) }, { import SOptionMethods._ (SOption.typeId, Seq( From af792b53bac04e488aa55cbb6c4a318f796a7d7a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 1 Oct 2024 19:21:31 +0300 Subject: [PATCH 15/16] addressing review comments --- data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala | 2 +- sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 75fbe460b9..2ae4f73703 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -232,7 +232,7 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => if (bytes.length > SBigInt.MaxSizeInBytes) { throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") } - CBigInt(new BigInteger(bytes.toArray)).asInstanceOf[T] + CBigInt(new BigInteger(bytes.toArray).to256BitValueExact).asInstanceOf[T] // todo: UnsignedBitInt case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") } diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index fee93248f7..7a4b8de827 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -516,6 +516,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) ) ++ (if (isV6Activated) { + // id = 4 reserved for deserializeTo method Seq(MInfo(3, serializeMethod), MInfo(5, fromBigEndianBytesMethod)) // methods added in v6.0 } else { Seq.empty[MInfo] From 1d47d5ffc69be4f9fe12a9713bb277b2775cd988 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 7 Oct 2024 18:47:46 +0300 Subject: [PATCH 16/16] addressing review comments --- core/shared/src/main/scala/sigma/Colls.scala | 19 ++++++------ .../scala/sigma/data/CollsOverArrays.scala | 10 ++++-- .../src/test/scala/sigma/CollsTests.scala | 26 ++++++++++++++++ .../src/main/scala/sigma/ast/methods.scala | 31 +++++++++++++------ .../scala/sigma/LanguageSpecificationV6.scala | 8 +++-- 5 files changed, 72 insertions(+), 22 deletions(-) diff --git a/core/shared/src/main/scala/sigma/Colls.scala b/core/shared/src/main/scala/sigma/Colls.scala index f1633a68a4..d10026066b 100644 --- a/core/shared/src/main/scala/sigma/Colls.scala +++ b/core/shared/src/main/scala/sigma/Colls.scala @@ -45,13 +45,10 @@ trait Coll[@specialized A] { */ def apply(i: Int): A - /** The element at given index. - * Indices start at `0`; `xs.apply(0)` is the first element of collection `xs`. - * Note the indexing syntax `xs(i)` is a shorthand for `xs.apply(i)`. + /** The element at given index or None if there is no such element. Indices start at `0`. * * @param i the index - * @return the element at the given index - * @throws ArrayIndexOutOfBoundsException if `i < 0` or `length <= i` + * @return the element at the given index, or None if there is no such element */ def get(i: Int): Option[A] = { if (isDefinedAt(i)) { @@ -92,12 +89,16 @@ trait Coll[@specialized A] { * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ def zip[@specialized B](ys: Coll[B]): Coll[(A, B)] - /** For this collection (x0, ..., xN) and other collection (y0, ..., yM) - * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ + /** + * @return true if first elements of this collection form given `ys` collection, false otherwise. + * E.g. [1,2,3] starts with [1,2] + */ def startsWith(ys: Coll[A]): Boolean - /** For this collection (x0, ..., xN) and other collection (y0, ..., yM) - * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ + /** + * @return true if last elements of this collection form given `ys` collection, false otherwise. + * E.g. [1,2,3] ends with [2,3] + */ def endsWith(ys: Coll[A]): Boolean /** Tests whether a predicate holds for at least one element of this collection. diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index 506dc001ea..eb4c1e4931 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -354,9 +354,15 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R def zip[@specialized B](ys: Coll[B]): PairColl[(L,R), B] = builder.pairColl(this, ys) - def startsWith(ys: Coll[(L, R)]): Boolean = toArray.startsWith(ys.toArray) + def startsWith(ys: Coll[(L, R)]): Boolean = ys match { + case yp: PairOfCols[L, R] => ls.startsWith(yp.ls) && rs.startsWith(yp.rs) + case _ => toArray.startsWith(ys.toArray) + } - def endsWith(ys: Coll[(L, R)]): Boolean = toArray.endsWith(ys.toArray) + def endsWith(ys: Coll[(L, R)]): Boolean = ys match { + case yp: PairOfCols[L, R] => ls.endsWith(yp.ls) && rs.endsWith(yp.rs) + case _ => toArray.endsWith(ys.toArray) + } override def indices: Coll[Int] = if (ls.length <= rs.length) ls.indices else rs.indices diff --git a/core/shared/src/test/scala/sigma/CollsTests.scala b/core/shared/src/test/scala/sigma/CollsTests.scala index 4886112742..da427ba576 100644 --- a/core/shared/src/test/scala/sigma/CollsTests.scala +++ b/core/shared/src/test/scala/sigma/CollsTests.scala @@ -386,6 +386,32 @@ class CollsTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers } } + property("Coll.startsWith") { + val minSuccess = minSuccessful(50) + forAll(collGen, minSuccess) { col => + val n = col.length / 2 + val prefix = col.take(n) + val pairs = col.zip(col) + pairs.startsWith(prefix.zip(prefix)) shouldBe true + col.startsWith(prefix) shouldBe true + val pairOfCols = new PairOfCols[Int, Int](col, col) + pairOfCols.startsWith(pairOfCols.take(n)) shouldBe true + } + } + + property("Coll.endsWith") { + val minSuccess = minSuccessful(50) + forAll(collGen, minSuccess) { col => + val n = col.length / 2 + val suffix = col.slice(n, col.length) + col.endsWith(suffix) shouldBe true + val pairs = col.zip(col) + pairs.endsWith(suffix.zip(suffix)) shouldBe true + val pairOfCols = new PairOfCols[Int, Int](col, col) + pairOfCols.endsWith(pairOfCols.slice(n, col.length)) shouldBe true + } + } + property("Coll.equals") { def checkColls(repl: Coll[_], coll: Coll[_]) = { assert(coll == repl) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 855da4eb73..dc85205b37 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1070,8 +1070,10 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { // ======== 6.0 methods below =========== + private val reverseCostKind = Append.costKind + val ReverseMethod = SMethod(this, "reverse", - SFunc(Array(ThisType), ThisType, paramIVSeq), 30, Zip_CostKind) // todo: costing + SFunc(Array(ThisType), ThisType, paramIVSeq), 30, reverseCostKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -1087,10 +1089,12 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + private val distinctCostKind = PerItemCost(baseCost = JitCost(60), perChunkCost = JitCost(5), chunkSize = 100) + val DistinctMethod = SMethod(this, "distinct", - SFunc(Array(ThisType), ThisType, paramIVSeq), 31, Zip_CostKind) // todo: costing + SFunc(Array(ThisType), ThisType, paramIVSeq), 31, distinctCostKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Returns inversed collection.") /** Implements evaluation of Coll.reverse method call ErgoTree node. * Called via reflection based on naming convention. @@ -1104,10 +1108,13 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + private val startsWithCostKind = Zip_CostKind + val StartsWithMethod = SMethod(this, "startsWith", - SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, Zip_CostKind) // todo: costing + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, startsWithCostKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Returns true if this collection starts with given one, false otherwise.", + ArgInfo("prefix", "Collection to be checked for being a prefix of this collection.")) /** Implements evaluation of Coll.zip method call ErgoTree node. * Called via reflection based on naming convention. @@ -1121,10 +1128,13 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + private val endsWithCostKind = Zip_CostKind + val EndsWithMethod = SMethod(this, "endsWith", - SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, Zip_CostKind) // todo: costing + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, endsWithCostKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Returns true if this collection ends with given one, false otherwise.", + ArgInfo("suffix", "Collection to be checked for being a suffix of this collection.")) /** Implements evaluation of Coll.zip method call ErgoTree node. * Called via reflection based on naming convention. @@ -1139,9 +1149,12 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val GetMethod = SMethod(this, "get", - SFunc(Array(ThisType, SInt), SOption(tIV), Array[STypeParam](tIV)), 34, ByIndex.costKind) //todo: costing + SFunc(Array(ThisType, SInt), SOption(tIV), Array[STypeParam](tIV)), 34, ByIndex.costKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, + "Returns Some(element) if there is an element at given index, None otherwise.", + ArgInfo("index", "Index of an element (starting from 0).") + ) private val v5Methods = super.getMethods() ++ Seq( SizeMethod, diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index ecd2c411ed..481c7101da 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -11,7 +11,7 @@ import sigma.ast.ErgoTree.ZeroHeader import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast.{SInt, _} -import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, RType} +import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, PairOfCols, RType} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, IntOps} @@ -1639,6 +1639,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Coll(1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), Coll(1, 1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), Coll(1, 2, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll(2, 2, 2) -> Expected(ExpectedResult(Success(Coll(2)), None)), + Coll(3, 1, 2, 2, 2, 4, 4, 1) -> Expected(ExpectedResult(Success(Coll(3, 1, 2, 4)), None)), Coll[Int]() -> Expected(ExpectedResult(Success(Coll[Int]()), None)) ), f @@ -1656,8 +1658,10 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq( (Coll(1, 2, 3), Coll(1, 2)) -> Expected(ExpectedResult(Success(true), None)), (Coll(1, 2, 3), Coll(1, 2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(1, 2, 4)) -> Expected(ExpectedResult(Success(false), None)), (Coll(1, 2, 3), Coll(1, 2, 3, 4)) -> Expected(ExpectedResult(Success(false), None)), - (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) + (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)), + (Coll[Int](1, 2), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) ), f )