From 4004cc9efcd724b115f2b43f280c1f3d9dc5c8fe Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 14 May 2024 10:10:51 +0100 Subject: [PATCH 01/40] i486-toBytes: tests added to SigmaTyperTest --- .../sigmastate/lang/SigmaTyperTest.scala | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index a474727943..f2ac5e8cf2 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -20,6 +20,7 @@ import sigma.serialization.ErgoTreeSerializer import sigma.serialization.generators.ObjectGenerators import sigma.ast.Select import sigma.exceptions.TyperException +import sigmastate.helpers.SigmaPPrint class SigmaTyperTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with LangTests with ObjectGenerators { @@ -37,7 +38,12 @@ class SigmaTyperTest extends AnyPropSpec val typer = new SigmaTyper(builder, predefinedFuncRegistry, lowerMethodCalls = true) val typed = typer.typecheck(bound) assertSrcCtxForAllNodes(typed) - if (expected != null) typed shouldBe expected + if (expected != null) { + if (expected != typed) { + SigmaPPrint.pprintln(typed, width = 100) + } + typed shouldBe expected + } typed.tpe } catch { case e: Exception => throw e @@ -508,6 +514,48 @@ class SigmaTyperTest extends AnyPropSpec typefail(env, "1.toSuperBigInteger", 1, 1) } + property("toBytes method for numeric types") { + typecheck(env, "1.toByte.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + Select(IntConstant(1), "toByte", Some(SByte)), + SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SByte)), + Vector(), + Map() + )) shouldBe SByteArray + + typecheck(env, "1.toShort.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + Select(IntConstant(1), "toShort", Some(SShort)), + SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SShort)), + Vector(), + Map() + )) shouldBe SByteArray + + typecheck(env, "1.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + IntConstant(1), + SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SInt)), + Vector(), + Map() + )) shouldBe SByteArray + + typecheck(env, "1.toLong.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + Select(IntConstant(1), "toLong", Some(SLong)), + SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SLong)), + Vector(), + Map() + )) shouldBe SByteArray + + typecheck(env, "1.toBigInt.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + Select(IntConstant(1), "toBigInt", Some(SBigInt)), + SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SBigInt)), + Vector(), + Map() + )) shouldBe SByteArray + } + property("string concat") { typecheck(env, """ "a" + "b" """) shouldBe SString } From b55d3362de79afad58f9755e89dc75984bf78c3c Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 14 May 2024 12:34:02 +0100 Subject: [PATCH 02/40] i486-toBytes: ExactNumeric.toBytes implemented --- .../main/scala/sigma/data/BigIntegerOps.scala | 3 +++ .../main/scala/sigma/data/ExactIntegral.scala | 7 +++++++ .../src/main/scala/sigma/data/ExactNumeric.scala | 7 +++++++ .../test/scala/sigma/SigmaDslSpecification.scala | 16 ++++++++++++++-- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 168b2f8266..b93334daf6 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -2,6 +2,7 @@ package sigma.data import sigma._ import sigma.eval.Extensions.IntExt +import sigma.util.Extensions.BigIntOps import scala.math.{Integral, Ordering} @@ -89,6 +90,8 @@ object NumericOps { * NOTE: This method should not be used in v4.x */ override def divisionRemainder(x: BigInt, y: BigInt): BigInt = x.mod(y) + + override def toBytes(x: BigInt): Coll[Byte] = Colls.fromArray(x.toBigInteger.toByteArray) } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 34e2f47f63..b207a1794d 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -1,5 +1,6 @@ package sigma.data +import sigma.{Coll, Colls} import sigma.util.Extensions.{ByteOps, ShortOps} /** Type-class which defines the operations on Integral types (Byte, Short, Int, Long, BigInt) @@ -37,6 +38,7 @@ object ExactIntegral { override def plus(x: Byte, y: Byte): Byte = x.addExact(y) override def minus(x: Byte, y: Byte): Byte = x.subtractExact(y) override def times(x: Byte, y: Byte): Byte = x.multiplyExact(y) + override def toBytes(x: Byte): Coll[Byte] = Colls.fromItems(x) } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -44,6 +46,7 @@ object ExactIntegral { override def plus(x: Short, y: Short): Short = x.addExact(y) override def minus(x: Short, y: Short): Short = x.subtractExact(y) override def times(x: Short, y: Short): Short = x.multiplyExact(y) + override def toBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -51,6 +54,8 @@ object ExactIntegral { override def plus(x: Int, y: Int): Int = java7.compat.Math.addExact(x, y) override def minus(x: Int, y: Int): Int = java7.compat.Math.subtractExact(x, y) override def times(x: Int, y: Int): Int = java7.compat.Math.multiplyExact(x, y) + override def toBytes(x: Int): Coll[Byte] = + Colls.fromItems((x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -58,5 +63,7 @@ object ExactIntegral { override def plus(x: Long, y: Long): Long = java7.compat.Math.addExact(x, y) override def minus(x: Long, y: Long): Long = java7.compat.Math.subtractExact(x, y) override def times(x: Long, y: Long): Long = java7.compat.Math.multiplyExact(x, y) + override def toBytes(x: Long): Coll[Byte] = + Colls.fromItems((x >> 56).toByte, (x >> 48).toByte, (x >> 40).toByte, (x >> 32).toByte, (x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 2e9b799a61..fef951b4d7 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -1,5 +1,6 @@ package sigma.data +import sigma.Coll import sigma.data.ExactIntegral._ /** Numeric operations with overflow checks. @@ -30,6 +31,12 @@ trait ExactNumeric[T] { def toInt(x: T): Int = n.toInt(x) def toLong(x: T): Long = n.toLong(x) + /** Returns a big-endian representation of this value in a collection of bytes. + * For example, the `Int` value `0x12131415` would yield the + * collection of bytes [0x12, 0x13, 0x14, 0x15] + */ + def toBytes(x: T): Coll[Byte] + /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) diff --git a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala b/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala index 4dd576f03a..e9ea2b8776 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala @@ -1058,8 +1058,20 @@ class SigmaDslSpecification extends SigmaDslTesting ">=", GE.apply)(_ >= _) } +// property("Numeric.toBytes methods equivalence") { +// lazy val toBytes = changedFeature( +// (x: Byte) => x.toBytes, +// (x: Byte) => x.toBytes, +// "{ (x: Byte) => x.toBytes }") +// val cases = Seq( +// (0.toByte, Success(Coll(0.toByte))), +// (1.toByte, Success(Coll(1.toByte))) +// ) +// +// testCases(cases, toBytes) +// } + property("Byte methods equivalence (new features)") { - lazy val toBytes = newFeature((x: Byte) => x.toBytes, "{ (x: Byte) => x.toBytes }") lazy val toAbs = newFeature((x: Byte) => x.toAbs, "{ (x: Byte) => x.toAbs }") lazy val compareTo = newFeature( (x: (Byte, Byte)) => x._1.compareTo(x._2), @@ -1074,7 +1086,7 @@ class SigmaDslSpecification extends SigmaDslTesting "{ (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }") forAll { x: Byte => - Seq(toBytes, toAbs).foreach(f => f.checkEquality(x)) + Seq(toAbs).foreach(f => f.checkEquality(x)) } forAll { x: (Byte, Byte) => From 3ccf11ea33c584eae38d3af7baa8e99bd80d829f Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Thu, 23 May 2024 18:11:20 +0200 Subject: [PATCH 03/40] i486-toBytes: fixes after merge --- .../scala/sigmastate/utils/Extensions.scala | 8 ++--- .../scala/sigma/LanguageSpecificationV5.scala | 36 ------------------- .../scala/sigma/LanguageSpecificationV6.scala | 16 +++++++++ 3 files changed, 20 insertions(+), 40 deletions(-) diff --git a/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala b/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala index 54abc40f4e..2e66eb6b19 100644 --- a/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala +++ b/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala @@ -13,7 +13,7 @@ object Extensions { * For example, the Byte value {@code 0x12} would yield the * byte array {@code {0x12}}. */ - def toBytes: Coll[Byte] = SigmaDsl.Colls.fromItems(b) + def toBigEndianBytes: Coll[Byte] = SigmaDsl.Colls.fromItems(b) } @@ -22,7 +22,7 @@ object Extensions { * For example, the Short value {@code 0x1213} would yield the * byte array {@code {0x12, 0x13}}. */ - def toBytes: Coll[Byte] = Colls.fromArray(Shorts.toByteArray(x)) + def toBigEndianBytes: Coll[Byte] = Colls.fromArray(Shorts.toByteArray(x)) } implicit class IntOpsForSigma(val x: Int) extends AnyVal { @@ -30,7 +30,7 @@ object Extensions { * For example, the Int value {@code 0x12131415} would yield the * byte array {@code {0x12, 0x13, 0x14, 0x15}}. */ - def toBytes: Coll[Byte] = Colls.fromArray(Ints.toByteArray(x)) + def toBigEndianBytes: Coll[Byte] = Colls.fromArray(Ints.toByteArray(x)) } implicit class LongOpsForSigma(val x: Long) extends AnyVal { @@ -38,7 +38,7 @@ object Extensions { * For example, the Long value {@code 0x1213141516171819} would yield the * byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}}. */ - def toBytes: Coll[Byte] = Colls.fromArray(Longs.toByteArray(x)) + def toBigEndianBytes: Coll[Byte] = Colls.fromArray(Longs.toByteArray(x)) } /** Provides extension methods for `ModifierId` instances. diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index dc6d1f6173..f568515c89 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -929,42 +929,6 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ">=", GE.apply)(_ >= _) } -// property("Numeric.toBytes methods equivalence") { -// lazy val toBytes = changedFeature( -// (x: Byte) => x.toBytes, -// (x: Byte) => x.toBytes, -// "{ (x: Byte) => x.toBytes }") -// val cases = Seq( -// (0.toByte, Success(Coll(0.toByte))), -// (1.toByte, Success(Coll(1.toByte))) -// ) -// -// testCases(cases, toBytes) -// } - - property("Byte methods equivalence (new features)") { - lazy val toAbs = newFeature((x: Byte) => x.toAbs, "{ (x: Byte) => x.toAbs }") - lazy val compareTo = newFeature( - (x: (Byte, Byte)) => x._1.compareTo(x._2), - "{ (x: (Byte, Byte)) => x._1.compareTo(x._2) }") - - lazy val bitOr = newFeature( - { (x: (Byte, Byte)) => (x._1 | x._2).toByteExact }, - "{ (x: (Byte, Byte)) => (x._1 | x._2).toByteExact }") - - lazy val bitAnd = newFeature( - { (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }, - "{ (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }") - - forAll { x: Byte => - Seq(toAbs).foreach(f => f.checkEquality(x)) - } - - forAll { x: (Byte, Byte) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } - property("Short methods equivalence") { SShort.upcast(0.toShort) shouldBe 0.toShort // boundary test case SShort.downcast(0.toShort) shouldBe 0.toShort // boundary test case diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index fd2e18ebfb..e105e4bee9 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -10,6 +10,7 @@ import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ErgoTreeSerializer import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound +import sigmastate.utils.Extensions.ByteOpsForSigma import sigmastate.utils.Helpers import java.math.BigInteger @@ -473,4 +474,19 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => tree.constants.length shouldBe t2.constants.length tree.root shouldBe t2.root } + + property("Numeric.toBytes methods equivalence") { + lazy val toBytes = newFeature( + { (x: Byte) => x.toBigEndianBytes }, + "{ (x: Byte) => x.toBytes }", + sinceVersion = VersionContext.V6SoftForkVersion) + val cases = Seq( + (0.toByte, Success(Coll(0.toByte))), + (1.toByte, Success(Coll(1.toByte))) + ) + + testCases(cases, toBytes) + } + + } From e5a32c8fe2efd772ef3d803e06d76f45d37454b9 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 25 May 2024 10:21:58 +0200 Subject: [PATCH 04/40] i486-toBytes: Versioned.scala added --- .../src/main/scala/sigma/util/Versioned.scala | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 core/shared/src/main/scala/sigma/util/Versioned.scala diff --git a/core/shared/src/main/scala/sigma/util/Versioned.scala b/core/shared/src/main/scala/sigma/util/Versioned.scala new file mode 100644 index 0000000000..a3eae2325a --- /dev/null +++ b/core/shared/src/main/scala/sigma/util/Versioned.scala @@ -0,0 +1,27 @@ +package sigma.util + +import sigma.VersionContext + +import scala.reflect.ClassTag + +/** Represents a versioned object that can be created for each supported version. + * The object is created lazily and cached for each version. + * + * @param builder a total function that creates an object for a given version in [0, + * maxVersion] range. + * @param maxVersion the maximum supported version. + */ +case class Versioned[T <: AnyRef: ClassTag](builder: Byte => T, maxVersion: Byte = VersionContext.MaxSupportedScriptVersion) { + private val cache = new Array[T](maxVersion + 1) + + def get(version: Byte): T = { + require(version <= VersionContext.MaxSupportedScriptVersion, s"Not supported version $version") + if (cache(version) == null) { + val v = builder(version) + cache(version) = v + v + } else { + cache(version) + } + } +} From bba2230da50f1fc4cb1cba8d097138aa9adadf72 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 25 May 2024 10:24:23 +0200 Subject: [PATCH 05/40] i486-toBytes: ensure VersionContext.current in tests --- .../shared/src/test/scala/sigmastate/CrossVersionProps.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala b/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala index e55b874dc3..87101a1f71 100644 --- a/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala +++ b/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala @@ -31,7 +31,9 @@ trait CrossVersionProps extends AnyPropSpecLike with TestsBase { System.gc() } forEachScriptAndErgoTreeVersion(activatedVersions, ergoTreeVersions) { + VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { testFun_Run(testName, testFun) + } } } } From dc361a1469b85365e321285308eb010a155f8234 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 25 May 2024 10:49:51 +0200 Subject: [PATCH 06/40] i486-toBytes: ErgoTreeSpecification versioned and updated --- .../src/main/scala/sigma/ast/SType.scala | 36 +++-- .../src/main/scala/sigma/ast/methods.scala | 27 +++- .../main/scala/sigma/data/BigIntegerOps.scala | 2 +- .../main/scala/sigma/data/ExactIntegral.scala | 8 +- .../main/scala/sigma/data/ExactNumeric.scala | 2 +- .../sigma/compiler/ir/GraphBuilding.scala | 6 + .../sigma/compiler/ir/TreeBuilding.scala | 7 + .../compiler/ir/primitives/NumericOps.scala | 9 ++ .../scala/sigma/LanguageSpecificationV6.scala | 22 +-- .../sigmastate/ErgoTreeSpecification.scala | 145 +++++++++++++----- 10 files changed, 192 insertions(+), 72 deletions(-) diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index f75cbc9e8b..8b386fa729 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -7,7 +7,8 @@ import sigma.data.OverloadHack.Overloaded1 import sigma.data.{CBigInt, Nullable, SigmaConstants} import sigma.reflection.{RClass, RMethod, ReflectionData} import sigma.util.Extensions.{IntOps, LongOps, ShortOps} -import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp} +import sigma.util.Versioned +import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp, VersionContext} import java.math.BigInteger @@ -113,27 +114,40 @@ object SType { * typeId this map contains a companion object which can be used to access the list of * corresponding methods. * - * NOTE: in the current implementation only monomorphic methods are supported (without - * type parameters) + * @note starting from v6.0 methods with type parameters are also supported. * - * NOTE2: in v3.x SNumericType.typeId is silently shadowed by SGlobal.typeId as part of + * @note on versioning: + * In v3.x-5.x SNumericType.typeId is silently shadowed by SGlobal.typeId as part of * `toMap` operation. As a result, the methods collected into SByte.methods cannot be * resolved (using SMethod.fromIds()) for all numeric types (SByte, SShort, SInt, * SLong, SBigInt). See the corresponding regression `property("MethodCall on numerics")`. * However, this "shadowing" is not a problem since all casting methods are implemented * via Downcast, Upcast opcodes and the remaining `toBytes`, `toBits` methods are not * implemented at all. - * In order to allow MethodCalls on numeric types in future versions the SNumericType.typeId - * should be changed and SGlobal.typeId should be preserved. The regression tests in - * `property("MethodCall Codes")` should pass. + * + * Starting from v6.0 the SNumericType.typeId is not used as receiver of object of + * method call, instead, all methods from SNumericTypeMethods are copied to all the + * concrete numeric types (SByte, SShort, SInt, SLong, SBigInt) and the generic tNum + * type parameter is specialized accordingly. This difference in behaviour is tested by + * `property("MethodCall on numerics")`. + * + * The regression tests in `property("MethodCall Codes")` should pass. */ // TODO v6.0: should contain all numeric types (including also SNumericType) // to support method calls like 10.toByte which encoded as MethodCall with typeId = 4, methodId = 1 // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 - lazy val types: Map[Byte, STypeCompanion] = Seq( - SBoolean, SNumericType, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, - SAvlTree, SBox, SOption, SCollection, SBigInt - ).map { t => (t.typeId, t) }.toMap + private val _types: Versioned[Map[Byte, STypeCompanion]] = Versioned({ version => + val v5x = Seq( + SBoolean, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, + SAvlTree, SBox, SOption, SCollection, SBigInt + ) + val v6 = if (version >= VersionContext.V6SoftForkVersion) + Seq(SByte, SShort, SInt, SLong) + else + Seq.empty + (v5x ++ v6).map { t => (t.typeId, t) }.toMap + }) + def types: Map[Byte, STypeCompanion] = _types.get(VersionContext.current.activatedVersion) /** Checks that the type of the value corresponds to the descriptor `tpe`. * If the value has complex structure only root type constructor is checked. diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 3f27f85e2d..c53a9b84c7 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -13,6 +13,7 @@ import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConsta import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} import sigma.reflection.RClass import sigma.serialization.CoreByteWriter.ArgInfo +import sigma.util.Versioned import sigma.utils.SparseArrayContainer import scala.annotation.unused @@ -157,11 +158,23 @@ trait MonoTypeMethods extends MethodsContainer { trait SNumericTypeMethods extends MonoTypeMethods { import SNumericTypeMethods.tNum - protected override def getMethods(): Seq[SMethod] = { - super.getMethods() ++ SNumericTypeMethods.methods.map { - m => m.copy(stype = applySubst(m.stype, Map(tNum -> this.ownerType)).asFunc) - } - } + private val _getMethods = Versioned({ version => + val subst = Map(tNum -> this.ownerType) + val numericMethods = if (version < VersionContext.V6SoftForkVersion) + SNumericTypeMethods.methods.map { m => + m.copy(stype = applySubst(m.stype, subst).asFunc ) + } + else + SNumericTypeMethods.methods.map { m => + m.copy( + objType = this, // associate the method with the concrete numeric type + stype = applySubst(m.stype, subst).asFunc + )} + super.getMethods() ++ numericMethods + }) + + protected override def getMethods(): Seq[SMethod] = + _getMethods.get(VersionContext.current.activatedVersion) } object SNumericTypeMethods extends MethodsContainer { @@ -313,8 +326,8 @@ case object SBigIntMethods extends SNumericTypeMethods { final val ToNBitsCostInfo = OperationCostInfo( FixedCost(JitCost(5)), NamedDesc("NBitsMethodCall")) - //id = 8 to make it after toBits - val ToNBits = SMethod(this, "nbits", SFunc(this.ownerType, SLong), 8, ToNBitsCostInfo.costKind) + //id = 20 to make it after toBits and reserve space for future methods at SNumericTypeMethods + val ToNBits = SMethod(this, "nbits", SFunc(this.ownerType, SLong), 20, ToNBitsCostInfo.costKind) .withInfo(ModQ, "Encode this big integer value as NBits") /** The following `modQ` methods are not fully implemented in v4.x and this descriptors. diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index b93334daf6..0bb9e9101b 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -91,7 +91,7 @@ object NumericOps { */ override def divisionRemainder(x: BigInt, y: BigInt): BigInt = x.mod(y) - override def toBytes(x: BigInt): Coll[Byte] = Colls.fromArray(x.toBigInteger.toByteArray) + override def toBigEndianBytes(x: BigInt): Coll[Byte] = Colls.fromArray(x.toBigInteger.toByteArray) } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index b207a1794d..eaf89947c1 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -38,7 +38,7 @@ object ExactIntegral { override def plus(x: Byte, y: Byte): Byte = x.addExact(y) override def minus(x: Byte, y: Byte): Byte = x.subtractExact(y) override def times(x: Byte, y: Byte): Byte = x.multiplyExact(y) - override def toBytes(x: Byte): Coll[Byte] = Colls.fromItems(x) + override def toBigEndianBytes(x: Byte): Coll[Byte] = Colls.fromItems(x) } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -46,7 +46,7 @@ object ExactIntegral { override def plus(x: Short, y: Short): Short = x.addExact(y) override def minus(x: Short, y: Short): Short = x.subtractExact(y) override def times(x: Short, y: Short): Short = x.multiplyExact(y) - override def toBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) + override def toBigEndianBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -54,7 +54,7 @@ object ExactIntegral { override def plus(x: Int, y: Int): Int = java7.compat.Math.addExact(x, y) override def minus(x: Int, y: Int): Int = java7.compat.Math.subtractExact(x, y) override def times(x: Int, y: Int): Int = java7.compat.Math.multiplyExact(x, y) - override def toBytes(x: Int): Coll[Byte] = + override def toBigEndianBytes(x: Int): Coll[Byte] = Colls.fromItems((x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) } @@ -63,7 +63,7 @@ object ExactIntegral { override def plus(x: Long, y: Long): Long = java7.compat.Math.addExact(x, y) override def minus(x: Long, y: Long): Long = java7.compat.Math.subtractExact(x, y) override def times(x: Long, y: Long): Long = java7.compat.Math.multiplyExact(x, y) - override def toBytes(x: Long): Coll[Byte] = + override def toBigEndianBytes(x: Long): Coll[Byte] = Colls.fromItems((x >> 56).toByte, (x >> 48).toByte, (x >> 40).toByte, (x >> 32).toByte, (x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index fef951b4d7..d8b3b5df0d 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -35,7 +35,7 @@ trait ExactNumeric[T] { * For example, the `Int` value `0x12131415` would yield the * collection of bytes [0x12, 0x13, 0x14, 0x15] */ - def toBytes(x: T): Coll[Byte] + def toBigEndianBytes(x: T): Coll[Byte] /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) 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 a6a244778e..c34d91b41a 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1151,6 +1151,12 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => g.serialize(value) case _ => throwError } + case (x: Ref[tNum], SNumericTypeMethods) => method.name match { + case SNumericTypeMethods.ToBytesMethod.name => + val op = NumericToBigEndianBytes(elemToExactNumeric(x.elem)) + ApplyUnOp(op, x) + 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..655010dbc5 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -106,6 +106,13 @@ trait TreeBuilding extends Base { IR: IRContext => object IsNumericUnOp { def unapply(op: UnOp[_,_]): Option[SValue => SValue] = op match { case NumericNegate(_) => Some({ v: SValue => builder.mkNegation(v.asNumValue) }) + case NumericToBigEndianBytes(_) => + val mkNode = { v: SValue => + val specMethod = SNumericTypeMethods.ToBytesMethod + .withConcreteTypes(Map(SNumericTypeMethods.tNum -> v.tpe)) + builder.mkMethodCall(v.asNumValue, specMethod, IndexedSeq.empty) + } + Some(mkNode) case _ => None } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index 4e732bbb5f..ef4124d0b5 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -14,6 +14,7 @@ trait NumericOps extends Base { self: IRContext => def unary_- : Ref[T] = NumericNegate(n)(x.elem).apply(x) def toInt: Ref[Int] = NumericToInt(n).apply(x) def toLong: Ref[Long] = NumericToLong(n).apply(x) + def toBigEndianBytes: Ref[Coll[Byte]] = NumericToBigEndianBytes(n).apply(x) } /** Extension methods over `Ref[T]` where T is instance of ExactIntegral type-class. */ @@ -66,6 +67,14 @@ trait NumericOps extends Base { self: IRContext => override def applySeq(x: T): Long = n.toLong(x) } + import Coll._ + /** Descriptor of unary `ToBigEndianBytes` conversion operation. */ + case class NumericToBigEndianBytes[T](n: ExactNumeric[T]) + extends UnOp[T, Coll[Byte]]("ToBigEndianBytes")(element[Coll[Byte]]) { + override def applySeq(x: T): Coll[Byte] = + n.toBigEndianBytes(x).asInstanceOf[Coll[Byte]] + } + /** Descriptor of binary `/` operation (integral division). */ case class IntegralDivide[T](i: ExactIntegral[T])(implicit elem: Elem[T]) extends DivOp[T]("/", i) { override def applySeq(x: T, y: T): T = i.quot(x, y) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index e105e4bee9..ed6b6d5165 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -475,18 +475,18 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => tree.root shouldBe t2.root } - property("Numeric.toBytes methods equivalence") { - lazy val toBytes = newFeature( - { (x: Byte) => x.toBigEndianBytes }, - "{ (x: Byte) => x.toBytes }", - sinceVersion = VersionContext.V6SoftForkVersion) - val cases = Seq( - (0.toByte, Success(Coll(0.toByte))), - (1.toByte, Success(Coll(1.toByte))) - ) + property("Numeric.toBytes methods equivalence") { + lazy val toBytes = newFeature( + { (x: Byte) => x.toBigEndianBytes }, + "{ (x: Byte) => x.toBytes }", + sinceVersion = VersionContext.V6SoftForkVersion) + val cases = Seq( + (0.toByte, Success(Coll(0.toByte))), + (1.toByte, Success(Coll(1.toByte))) + ) - testCases(cases, toBytes) - } + testCases(cases, toBytes) + } } diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 7539bd5e48..7a48ec8b45 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -21,14 +21,14 @@ import sigma.compiler.CompilerSettings import sigma.eval.EvalSettings import sigma.exceptions.{CostLimitException, InterpreterException} import sigma.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.Plus +import sigmastate.{CrossVersionProps, Plus} import sigmastate.utils.Helpers.TryOps /** Regression tests with ErgoTree related test vectors. * This test vectors verify various constants which are consensus critical and should not change. */ -class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { +class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with CrossVersionProps { property("Value.sourceContext") { val srcCtx = SourceContext.fromParserIndex(0, "") @@ -313,37 +313,97 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { */ case class MInfo(methodId: Byte, method: SMethod, isResolvableFromIds: Boolean = true) + def isV6Activated = VersionContext.current.isV6SoftForkActivated + // NOTE, the type code constants are checked above // The methodId codes as checked here, they MUST be PRESERVED. - // The following table should be made dependent on HF activation - val methods = Table( + // Note, the following table is dependent on SF activation. + def methods = { + import SNumericTypeMethods._ + Table( ("typeId", "methods", "CanHaveMethods"), (SBoolean.typeId, Seq.empty[MInfo], true), - (SByte.typeId, Seq.empty[MInfo], false), - (SShort.typeId, Seq.empty[MInfo], false), - (SInt.typeId, Seq.empty[MInfo], false), - (SLong.typeId, Seq.empty[MInfo], false), - - { // SNumericType.typeId is erroneously shadowed by SGlobal.typeId - // this should be preserved in v3.x and fixed in v4.0 - (SNumericType.typeId, Seq( - MInfo(methodId = 1, SGlobalMethods.groupGeneratorMethod), - MInfo(2, SGlobalMethods.xorMethod) - ), true) + { + if (isV6Activated) + (SByte.typeId, Seq( + MInfo(methodId = 1, ToByteMethod), + MInfo(2, ToShortMethod), + MInfo(3, ToIntMethod), + MInfo(4, ToLongMethod), + MInfo(5, ToBigIntMethod), + MInfo(6, ToBytesMethod), + MInfo(7, ToBitsMethod) + ), true) + else + (SByte.typeId, Seq.empty[MInfo], false) + }, + { + if (isV6Activated) + (SShort.typeId, Seq( + MInfo(methodId = 1, ToByteMethod), + MInfo(2, ToShortMethod), + MInfo(3, ToIntMethod), + MInfo(4, ToLongMethod), + MInfo(5, ToBigIntMethod), + MInfo(6, ToBytesMethod), + MInfo(7, ToBitsMethod) + ), true) + else + (SShort.typeId, Seq.empty[MInfo], false) + }, + { + if (isV6Activated) + (SInt.typeId, Seq( + MInfo(methodId = 1, ToByteMethod), + MInfo(2, ToShortMethod), + MInfo(3, ToIntMethod), + MInfo(4, ToLongMethod), + MInfo(5, ToBigIntMethod), + MInfo(6, ToBytesMethod), + MInfo(7, ToBitsMethod) + ), true) + else + (SInt.typeId, Seq.empty[MInfo], false) + }, + { + if (isV6Activated) + (SLong.typeId, Seq( + MInfo(methodId = 1, ToByteMethod), + MInfo(2, ToShortMethod), + MInfo(3, ToIntMethod), + MInfo(4, ToLongMethod), + MInfo(5, ToBigIntMethod), + MInfo(6, ToBytesMethod), + MInfo(7, ToBitsMethod) + ), true) + else + (SLong.typeId, Seq.empty[MInfo], false) }, +// { // SNumericType.typeId is erroneously shadowed by SGlobal.typeId +// // this should be preserved in v3.x and fixed in v4.0 +// (SNumericType.typeId, Seq( +// MInfo(methodId = 1, SGlobalMethods.groupGeneratorMethod), +// MInfo(2, SGlobalMethods.xorMethod) +// ), true) +// }, + { // SBigInt inherit methods from SNumericType.methods - // however they are not resolvable via SBigInt.typeId + // however they are not resolvable via SBigInt.typeId before v6.0 import SNumericTypeMethods._ (SBigInt.typeId, Seq( - MInfo(methodId = 1, ToByteMethod, isResolvableFromIds = false), - MInfo(2, ToShortMethod, isResolvableFromIds = false), - MInfo(3, ToIntMethod, isResolvableFromIds = false), - MInfo(4, ToLongMethod, isResolvableFromIds = false), - MInfo(5, ToBigIntMethod, isResolvableFromIds = false), - MInfo(6, ToBytesMethod, isResolvableFromIds = false), - MInfo(7, ToBitsMethod, isResolvableFromIds = false) - ), true) + MInfo(methodId = 1, ToByteMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(2, ToShortMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(3, ToIntMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(4, ToLongMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(5, ToBigIntMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(6, ToBytesMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(7, ToBitsMethod, isResolvableFromIds = if (isV6Activated) true else false)) ++ + (if (isV6Activated) Seq( + // methods added in v6.0 + MInfo(20, SBigIntMethods.ToNBits) + ) else Seq.empty) + , true) }, { import SGroupElementMethods._ (SGroupElement.typeId, Seq( @@ -419,7 +479,10 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { { import SGlobalMethods._ (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) - ), true) + ) ++ (if (isV6Activated) Seq( + // methods added in v6.0 + MInfo(3, serializeMethod) + ) else Seq.empty), true) }, { import SCollectionMethods._ (SCollection.typeId, Seq( @@ -475,7 +538,8 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(8, FilterMethod) ), true) } - ) + ) + } property("MethodCall Codes") { forAll(methods) { (typeId, methods, canHaveMethods) => @@ -508,21 +572,28 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { assert(!canHaveMethods, s"Type with code $typeId can have methods") } } - } property("MethodCall on numerics") { forAll(Table[STypeCompanion]("type", SByte, SShort, SInt, SLong, SBigInt)) { t => - // this methods are expected to fail resolution in v3.x (but may change in future) - (1 to 7).foreach { methodId => - assertExceptionThrown( - SMethod.fromIds(t.typeId, methodId.toByte), - { - case _: ValidationException => true - case _ => false - }, - s"SMethod mustn't resolve for typeId = ${t.typeId} and methodId = $methodId" - ) + // this methods are expected to fail resolution in before v6.0 + if (!isV6Activated) { + (1 to 7).foreach { methodId => + assertExceptionThrown( + SMethod.fromIds(t.typeId, methodId.toByte), + { + case _: ValidationException => true + case _ => false + }, + s"SMethod mustn't resolve for typeId = ${t.typeId} and methodId = $methodId" + ) + } + } else { + // in v6.0 these codes should resolve to the methods of the concrete numeric type + (1 to 7).foreach { methodId => + val m = SMethod.fromIds(t.typeId, methodId.toByte) + m.objType.ownerType shouldBe t + } } } } From 70786a0c7dcfabc6cf65a92229d44926698785d8 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 25 May 2024 11:22:19 +0200 Subject: [PATCH 07/40] i486-toBytes: clarified description of demotion of SNumericType --- .../src/main/scala/sigma/ast/SType.scala | 29 +++++++++++-------- .../sigmastate/ErgoTreeSpecification.scala | 5 ++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 8b386fa729..7143504ca8 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -118,24 +118,29 @@ object SType { * * @note on versioning: * In v3.x-5.x SNumericType.typeId is silently shadowed by SGlobal.typeId as part of - * `toMap` operation. As a result, the methods collected into SByte.methods cannot be + * `toMap` operation. As a result, SNumericTypeMethods container cannot be resolved by + * typeId = 106, because SNumericType was being silently removed when `_types` map is + * constructed. See `property("SNumericType.typeId resolves to SGlobal")`. + * In addition, the methods associated with the concrete numeric types cannot be * resolved (using SMethod.fromIds()) for all numeric types (SByte, SShort, SInt, - * SLong, SBigInt). See the corresponding regression `property("MethodCall on numerics")`. + * SLong) because these types are not registered in the `_types` map. + * See the corresponding property("MethodCall on numerics")`. * However, this "shadowing" is not a problem since all casting methods are implemented - * via Downcast, Upcast opcodes and the remaining `toBytes`, `toBits` methods are not - * implemented at all. + * via lowering to Downcast, Upcast opcodes and the remaining `toBytes`, `toBits` + * methods are not implemented at all. * - * Starting from v6.0 the SNumericType.typeId is not used as receiver of object of - * method call, instead, all methods from SNumericTypeMethods are copied to all the - * concrete numeric types (SByte, SShort, SInt, SLong, SBigInt) and the generic tNum - * type parameter is specialized accordingly. This difference in behaviour is tested by - * `property("MethodCall on numerics")`. + * Starting from v6.0 the SNumericType.typeId is demoted as a receiver object of + * method calls and: + * 1) numeric type SByte, SShort, SInt, SLong are promoted as receivers and added to + * the _types map. + * 2) all methods from SNumericTypeMethods are copied to all the concrete numeric types + * (SByte, SShort, SInt, SLong, SBigInt) and the generic tNum type parameter is + * specialized accordingly. + * + * This difference in behaviour is tested by `property("MethodCall on numerics")`. * * The regression tests in `property("MethodCall Codes")` should pass. */ - // TODO v6.0: should contain all numeric types (including also SNumericType) - // to support method calls like 10.toByte which encoded as MethodCall with typeId = 4, methodId = 1 - // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 private val _types: Versioned[Map[Byte, STypeCompanion]] = Versioned({ version => val v5x = Seq( SBoolean, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 7a48ec8b45..6e6fb824de 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -541,6 +541,11 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C ) } + property("SNumericType.typeId resolves to SGlobal") { + SNumericType.typeId shouldBe SGlobal.typeId + SMethod.fromIds(SNumericType.typeId, 1) shouldBe SGlobalMethods.groupGeneratorMethod + } + property("MethodCall Codes") { forAll(methods) { (typeId, methods, canHaveMethods) => SType.types.get(typeId) match { From c76ac1a484e8d15a1e34469f35f1693e507a0715 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 25 May 2024 13:09:23 +0200 Subject: [PATCH 08/40] i486-toBytes: GraphBuilding to handle numeric methods --- .../sigma/compiler/ir/GraphBuilding.scala | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) 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 c34d91b41a..ddbec96518 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1,6 +1,7 @@ package sigma.compiler.ir import org.ergoplatform._ +import sigma.Evaluation.stypeToRType import sigma.ast.TypeCodes.LastConstantCode import sigma.ast.Value.Typed import sigma.ast.syntax.{SValue, ValueOps} @@ -437,8 +438,8 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => } Nullable(res) }} - def throwError = - error(s"Don't know how to buildNode($node)", node.sourceContext.toOption) + def throwError(clue: String = "") = + error((if (clue.nonEmpty) clue + ": " else "") + s"Don't know how to buildNode($node)", node.sourceContext.toOption) val res: Ref[Any] = node match { case Constant(v, tpe) => v match { @@ -985,7 +986,7 @@ 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 _ => throwError + case _ => throwError() } case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match { case SOptionMethods.GetMethod.name => @@ -999,7 +1000,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => opt.map(asRep[t => Any](argsV(0))) case SOptionMethods.FilterMethod.name => opt.filter(asRep[t => Boolean](argsV(0))) - case _ => throwError + case _ => throwError() } case (ge: Ref[GroupElement]@unchecked, SGroupElementMethods) => method.name match { case SGroupElementMethods.GetEncodedMethod.name => @@ -1012,12 +1013,12 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SGroupElementMethods.ExponentiateMethod.name => val k = asRep[BigInt](argsV(0)) ge.exp(k) - case _ => throwError + case _ => throwError() } case (box: Ref[Box]@unchecked, SBoxMethods) => method.name match { case SBoxMethods.tokensMethod.name => box.tokens - case _ => throwError + case _ => throwError() } case (ctx: Ref[Context]@unchecked, SContextMethods) => method.name match { case SContextMethods.dataInputsMethod.name => @@ -1040,7 +1041,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => ctx.LastBlockUtxoRootHash case SContextMethods.minerPubKeyMethod.name => ctx.minerPubKey - case _ => throwError + case _ => throwError() } case (tree: Ref[AvlTree]@unchecked, SAvlTreeMethods) => method.name match { case SAvlTreeMethods.digestMethod.name => @@ -1087,7 +1088,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val operations = asRep[Coll[(Coll[Byte], Coll[Byte])]](argsV(0)) val proof = asRep[Coll[Byte]](argsV(1)) tree.update(operations, proof) - case _ => throwError + case _ => throwError() } case (ph: Ref[PreHeader]@unchecked, SPreHeaderMethods) => method.name match { case SPreHeaderMethods.versionMethod.name => @@ -1104,7 +1105,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => ph.minerPk case SPreHeaderMethods.votesMethod.name => ph.votes - case _ => throwError + case _ => throwError() } case (h: Ref[Header]@unchecked, SHeaderMethods) => method.name match { case SHeaderMethods.idMethod.name => @@ -1137,7 +1138,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => h.powDistance case SHeaderMethods.votesMethod.name => h.votes - case _ => throwError + case _ => throwError() } case (g: Ref[SigmaDslBuilder]@unchecked, SGlobalMethods) => method.name match { case SGlobalMethods.groupGeneratorMethod.name => @@ -1149,19 +1150,19 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SGlobalMethods.serializeMethod.name => val value = asRep[Any](argsV(0)) g.serialize(value) - case _ => throwError + case _ => throwError() } - case (x: Ref[tNum], SNumericTypeMethods) => method.name match { + case (x: Ref[tNum], _: SNumericTypeMethods) => method.name match { case SNumericTypeMethods.ToBytesMethod.name => val op = NumericToBigEndianBytes(elemToExactNumeric(x.elem)) ApplyUnOp(op, x) - case _ => throwError + case _ => throwError() } - case _ => throwError + case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") } case _ => - throwError + throwError() } val resC = asRep[T#WrappedType](res) resC From 2427db30fdaedd4f75c85677507d656ce4c86412 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 25 May 2024 13:10:24 +0200 Subject: [PATCH 09/40] i486-toBytes: added userDefinedInvoke handler to SMethod --- .../src/main/scala/sigma/ast/package.scala | 6 ++++++ .../src/main/scala/sigma/ast/SMethod.scala | 19 ++++++++++++++++--- .../sigma/compiler/ir/TreeBuilding.scala | 8 ++++---- .../sigma/compiler/phases/SigmaTyper.scala | 3 ++- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/core/shared/src/main/scala/sigma/ast/package.scala b/core/shared/src/main/scala/sigma/ast/package.scala index 63a2cfbcba..ec51ca6e3a 100644 --- a/core/shared/src/main/scala/sigma/ast/package.scala +++ b/core/shared/src/main/scala/sigma/ast/package.scala @@ -137,6 +137,12 @@ package object ast { def asNumType: SNumericType = tpe.asInstanceOf[SNumericType] + /** Cast this type to numeric type or else throws the given error. */ + def asNumTypeOrElse(error: => Exception): SNumericType = tpe match { + case nt: SNumericType => nt + case _ => throw error + } + def asFunc: SFunc = tpe.asInstanceOf[SFunc] def asProduct: SProduct = tpe.asInstanceOf[SProduct] diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala index 332c74be6b..19fa427b9e 100644 --- a/data/shared/src/main/scala/sigma/ast/SMethod.scala +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -73,7 +73,9 @@ case class SMethod( explicitTypeArgs: Seq[STypeVar], irInfo: MethodIRInfo, docInfo: Option[OperationInfo], - costFunc: Option[MethodCostFunc]) { + costFunc: Option[MethodCostFunc], + userDefinedInvoke: Option[SMethod.InvokeHandler] +) { /** Operation descriptor of this method. */ lazy val opDesc = MethodDesc(this) @@ -112,7 +114,12 @@ case class SMethod( /** Invoke this method on the given object with the arguments. * This is used for methods with FixedCost costKind. */ def invokeFixed(obj: Any, args: Array[Any]): Any = { - javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) + userDefinedInvoke match { + case Some(h) => + h(obj, args) + case None => + javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) + } } // TODO optimize: avoid lookup when this SMethod is created via `specializeFor` @@ -261,6 +268,12 @@ object SMethod { */ type InvokeDescBuilder = SFunc => Seq[SType] + /** Type of user-defined function which is called to handle method invocation. + * Instances of this type can be attached to [[SMethod]] instances. + * @see SNumericTypeMethods.ToBytesMethod + */ + type InvokeHandler = (Any, Array[Any]) => Any + /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. * @param methodName the name of the method to lookup * @param cT the class where to search the methodName @@ -295,7 +308,7 @@ object SMethod { ): SMethod = { SMethod( objType, name, stype, methodId, costKind, explicitTypeArgs, - MethodIRInfo(None, None, None), None, None) + MethodIRInfo(None, None, None), None, None, None) } 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 655010dbc5..f7daff90ae 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -106,11 +106,11 @@ trait TreeBuilding extends Base { IR: IRContext => object IsNumericUnOp { def unapply(op: UnOp[_,_]): Option[SValue => SValue] = op match { case NumericNegate(_) => Some({ v: SValue => builder.mkNegation(v.asNumValue) }) - case NumericToBigEndianBytes(_) => + case _: NumericToBigEndianBytes[_] => val mkNode = { v: SValue => - val specMethod = SNumericTypeMethods.ToBytesMethod - .withConcreteTypes(Map(SNumericTypeMethods.tNum -> v.tpe)) - builder.mkMethodCall(v.asNumValue, specMethod, IndexedSeq.empty) + val receiverType = v.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${v.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.ToBytesMethod.methodId) + builder.mkMethodCall(v.asNumValue, m, IndexedSeq.empty) } Some(mkNode) case _ => None diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index f9b52fe061..ac30a6cd0a 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -139,7 +139,8 @@ class SigmaTyper(val builder: SigmaBuilder, obj.tpe match { case p: SProduct => MethodsContainer.getMethod(p, n) match { - case Some(method @ SMethod(_, _, genFunTpe @ SFunc(_, _, _), _, _, _, _, _, _)) => + case Some(method: SMethod) => + val genFunTpe = method.stype val subst = Map(genFunTpe.tpeParams.head.ident -> rangeTpe) val concrFunTpe = applySubst(genFunTpe, subst) val expectedArgs = concrFunTpe.asFunc.tDom.tail From 02e2d06d7b66082bed697767d6446cce138d6ef8 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 25 May 2024 15:15:40 +0200 Subject: [PATCH 10/40] i486-toBytes: toBigEndianBytes implemented --- .../src/main/scala/sigma/VersionContext.scala | 13 ++++ .../src/main/scala/sigma/ast/SMethod.scala | 9 ++- .../src/main/scala/sigma/ast/methods.scala | 11 ++- .../scala/sigma/LanguageSpecificationV5.scala | 14 ++-- .../scala/sigma/LanguageSpecificationV6.scala | 70 +++++++++---------- .../test/scala/sigma/SigmaDslTesting.scala | 23 +++--- 6 files changed, 84 insertions(+), 56 deletions(-) diff --git a/core/shared/src/main/scala/sigma/VersionContext.scala b/core/shared/src/main/scala/sigma/VersionContext.scala index 19a4857086..ecdcfb5f62 100644 --- a/core/shared/src/main/scala/sigma/VersionContext.scala +++ b/core/shared/src/main/scala/sigma/VersionContext.scala @@ -25,6 +25,12 @@ case class VersionContext(activatedVersion: Byte, ergoTreeVersion: Byte) { /** @return true, if the activated script version of Ergo protocol on the network is * including Evolution update. */ def isV6SoftForkActivated: Boolean = activatedVersion >= V6SoftForkVersion + + /** @return true if another [[VersionContext]] is greater than this. */ + def <= (that: VersionContext): Boolean = { + this.activatedVersion < that.activatedVersion || + (this.activatedVersion == that.activatedVersion && this.ergoTreeVersion <= that.ergoTreeVersion) + } } object VersionContext { @@ -104,4 +110,11 @@ object VersionContext { } } + /** Returns the VersionContext with V5 activation and the given ErgoTree version. */ + def sinceV5AndTreeVersion(treeVersion: Byte): VersionContext = + VersionContext(JitActivationVersion, ergoTreeVersion = treeVersion) + + /** Returns the VersionContext with V6 activation and the given ErgoTree version. */ + def sinceV6AndTreeVersion(treeVersion: Byte): VersionContext = + VersionContext(V6SoftForkVersion, ergoTreeVersion = treeVersion) } diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala index 19fa427b9e..6bf8d4f9a0 100644 --- a/data/shared/src/main/scala/sigma/ast/SMethod.scala +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -116,7 +116,7 @@ case class SMethod( def invokeFixed(obj: Any, args: Array[Any]): Any = { userDefinedInvoke match { case Some(h) => - h(obj, args) + h(this, obj, args) case None => javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) } @@ -159,6 +159,11 @@ case class SMethod( m } + /** Create a new instance with the given user-defined invoke handler. */ + def withUserDefinedInvoke(handler: SMethod.InvokeHandler): SMethod = { + copy(userDefinedInvoke = Some(handler)) + } + /** Create a new instance with the given stype. */ def withSType(newSType: SFunc): SMethod = copy(stype = newSType) @@ -272,7 +277,7 @@ object SMethod { * Instances of this type can be attached to [[SMethod]] instances. * @see SNumericTypeMethods.ToBytesMethod */ - type InvokeHandler = (Any, Array[Any]) => Any + type InvokeHandler = (SMethod, Any, Array[Any]) => Any /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. * @param methodName the name of the method to lookup diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index c53a9b84c7..1a0841e97c 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -8,6 +8,7 @@ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} import sigma.ast.SType.{TypeCode, paramT, tT} import sigma.ast.syntax.{SValue, ValueOps} +import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} import sigma.data.OverloadHack.Overloaded1 import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} @@ -230,6 +231,15 @@ object SNumericTypeMethods extends MethodsContainer { val ToBytesMethod: SMethod = SMethod( this, "toBytes", SFunc(tNum, SByteArray), 6, ToBytes_CostKind) .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Long]) + case SBigIntMethods => obj.asInstanceOf[BigInt].toBytes + } + }) .withInfo(PropertyCall, """ Returns a big-endian representation of this numeric value in a collection of bytes. | For example, the \lst{Int} value \lst{0x12131415} would yield the @@ -1547,7 +1557,6 @@ case object SGlobalMethods extends MonoTypeMethods { def serialize_eval(mc: MethodCall, G: SigmaDslBuilder, value: SType#WrappedType) (implicit E: ErgoTreeEvaluator): Coll[Byte] = { // TODO v6.0: accumulate cost - CheckMinimalErgoTreeVersion(E.context.currentErgoTreeVersion, VersionContext.V6SoftForkVersion) val t = Evaluation.stypeToRType(mc.args(0).tpe) G.serialize(value)(t) } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index f568515c89..48f1a3f7ca 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -4802,7 +4802,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => })) ), changedFeature( - changedInVersion = VersionContext.JitActivationVersion, + changedInVersion = VersionContext.sinceV5AndTreeVersion(0), { (x: Context) => x.selfBoxIndex }, { (x: Context) => x.selfBoxIndex }, // see versioning in selfBoxIndex implementation "{ (x: Context) => x.selfBoxIndex }", @@ -5004,7 +5004,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ), changedFeature( - changedInVersion = VersionContext.JitActivationVersion, + changedInVersion = VersionContext.sinceV5AndTreeVersion(0), scalaFunc = { (x: Context) => // this error is expected in v3.x, v4.x throw expectedError @@ -5978,7 +5978,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) }, changedFeature( - changedInVersion = VersionContext.JitActivationVersion, + changedInVersion = VersionContext.sinceV5AndTreeVersion(0), (x: Coll[Boolean]) => SigmaDsl.xorOf(x), (x: Coll[Boolean]) => SigmaDsl.xorOf(x), "{ (x: Coll[Boolean]) => xorOf(x) }", @@ -6241,7 +6241,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) }, changedFeature( - changedInVersion = VersionContext.JitActivationVersion, + changedInVersion = VersionContext.sinceV5AndTreeVersion(0), (x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), (x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), "{ (x: (Coll[Byte], Coll[Byte])) => xor(x._1, x._2) }", @@ -8811,7 +8811,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => (Some(Long.MaxValue) -> Expected(new ArithmeticException("long overflow"))) ), changedFeature( - changedInVersion = VersionContext.JitActivationVersion, + changedInVersion = VersionContext.sinceV5AndTreeVersion(0), scalaFunc = { (x: Option[Long]) => def f(opt: Long): Long = n.plus(opt, 1) if (x.isDefined) f(x.get) @@ -9367,7 +9367,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) }, changedFeature( - changedInVersion = VersionContext.JitActivationVersion, + changedInVersion = VersionContext.sinceV5AndTreeVersion(0), { (x: (Coll[Byte], Int)) => SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) }, @@ -9430,7 +9430,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ), changedFeature( - changedInVersion = VersionContext.JitActivationVersion, + changedInVersion = VersionContext.sinceV5AndTreeVersion(0), { (x: Context) => throw error true diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index ed6b6d5165..d89fc1f623 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -44,7 +44,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Map() ) ), - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) } property("Global.serialize[Byte]") { @@ -76,7 +76,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Boolean.toByte") { val toByte = newFeature((x: Boolean) => x.toByte, "{ (x: Boolean) => x.toByte }", - sinceVersion = VersionContext.V6SoftForkVersion + sinceVersion = VersionContext.sinceV6AndTreeVersion(0) ) val cases = Seq( @@ -102,22 +102,22 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // which is checked below lazy val toAbs = newFeature((x: Byte) => x.toAbs, "{ (x: Byte) => x.toAbs }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature( (x: (Byte, Byte)) => x._1.compareTo(x._2), "{ (x: (Byte, Byte)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature( { (x: (Byte, Byte)) => (x._1 | x._2).toByteExact }, "{ (x: (Byte, Byte)) => (x._1 | x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature( { (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }, "{ (x: (Byte, Byte)) => (x._1 & x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) forAll { x: Byte => Seq(toAbs).foreach(f => f.checkEquality(x)) @@ -136,21 +136,21 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // which is checked below lazy val toAbs = newFeature((x: Short) => x.toAbs, "{ (x: Short) => x.toAbs }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature((x: (Short, Short)) => x._1.compareTo(x._2), "{ (x: (Short, Short)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature( { (x: (Short, Short)) => (x._1 | x._2).toShortExact }, "{ (x: (Short, Short)) => x._1 | x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature( { (x: (Short, Short)) => (x._1 & x._2).toShortExact }, "{ (x: (Short, Short)) => x._1 & x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) forAll { x: Short => Seq(toAbs).foreach(_.checkEquality(x)) @@ -166,18 +166,18 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // NOTE, for such versions the new features are not supported // which is checked below lazy val toAbs = newFeature((x: Int) => x.toAbs, "{ (x: Int) => x.toAbs }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature((x: (Int, Int)) => x._1.compareTo(x._2), "{ (x: (Int, Int)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature( { (x: (Int, Int)) => x._1 | x._2 }, "{ (x: (Int, Int)) => x._1 | x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature( { (x: (Int, Int)) => x._1 & x._2 }, "{ (x: (Int, Int)) => x._1 & x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) forAll { x: Int => Seq(toAbs).foreach(_.checkEquality(x)) } @@ -192,20 +192,20 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // NOTE, for such versions the new features are not supported // which is checked below lazy val toAbs = newFeature((x: Long) => x.toAbs, "{ (x: Long) => x.toAbs }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature((x: (Long, Long)) => x._1.compareTo(x._2), "{ (x: (Long, Long)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature( { (x: (Long, Long)) => x._1 | x._2 }, "{ (x: (Long, Long)) => x._1 | x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature( { (x: (Long, Long)) => x._1 & x._2 }, "{ (x: (Long, Long)) => x._1 & x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) forAll { x: Long => Seq(toAbs).foreach(_.checkEquality(x)) @@ -240,30 +240,30 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => val toByte = newFeature((x: BigInt) => x.toByte, "{ (x: BigInt) => x.toByte }", FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SByte)), - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) val toShort = newFeature((x: BigInt) => x.toShort, "{ (x: BigInt) => x.toShort }", FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SShort)), - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) val toInt = newFeature((x: BigInt) => x.toInt, "{ (x: BigInt) => x.toInt }", FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SInt)), - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) val toLong = newFeature((x: BigInt) => x.toLong, "{ (x: BigInt) => x.toLong }", FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SLong)), - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val toAbs = newFeature((x: BigInt) => x.toAbs, "{ (x: BigInt) => x.toAbs }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature((x: (BigInt, BigInt)) => x._1.compareTo(x._2), "{ (x: (BigInt, BigInt)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature({ (x: (BigInt, BigInt)) => x._1 | x._2 }, "{ (x: (BigInt, BigInt)) => x._1 | x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature({ (x: (BigInt, BigInt)) => x._1 & x._2 }, "{ (x: (BigInt, BigInt)) => x._1 & x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) forAll { x: BigInt => Seq(toByte, toShort, toInt, toLong, toAbs).foreach(_.checkEquality(x)) @@ -278,7 +278,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // TODO v6.0: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416 val getReg = newFeature((x: Box) => x.getReg[Int](1).get, "{ (x: Box) => x.getReg[Int](1).get }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -294,7 +294,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Coll find method equivalence") { val find = newFeature((x: Coll[Int]) => x.find({ (v: Int) => v > 0 }), "{ (x: Coll[Int]) => x.find({ (v: Int) => v > 0} ) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -313,7 +313,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => if (x.size > 2) x.slice(0, x.size - 2) else Colls.emptyColl[Boolean] }, "{ (x: Coll[Boolean]) => x >> 2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -329,7 +329,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Coll diff methods equivalence") { val diff = newFeature((x: (Coll[Int], Coll[Int])) => x._1.diff(x._2), "{ (x: (Coll[Int], Coll[Int])) => x._1.diff(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -346,7 +346,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => 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 }) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -362,7 +362,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("allZK equivalence") { lazy val allZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.allZK(x), "{ (x: Coll[SigmaProp]) => allZK(x) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -378,7 +378,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("anyZK equivalence") { lazy val anyZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.anyZK(x), "{ (x: Coll[SigmaProp]) => anyZK(x) }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -442,7 +442,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => } ), changedFeature( - changedInVersion = VersionContext.V6SoftForkVersion, + changedInVersion = VersionContext.sinceV6AndTreeVersion(0), { (x: (Coll[Byte], Int)) => SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) }, @@ -479,7 +479,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => lazy val toBytes = newFeature( { (x: Byte) => x.toBigEndianBytes }, "{ (x: Byte) => x.toBytes }", - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) val cases = Seq( (0.toByte, Success(Coll(0.toByte))), (1.toByte, Success(Coll(1.toByte))) diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 3246f968a0..0b512d1380 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -126,8 +126,8 @@ class SigmaDslTesting extends AnyPropSpec /** Checks if this feature is supported in the given version context. */ def isSupportedIn(vc: VersionContext): Boolean - /** Version in which the feature is first implemented of changed. */ - def sinceVersion: Byte + /** Version in which the feature is first implemented or changed. */ + def sinceVersion: VersionContext /** Script containing this feature. */ def script: String @@ -400,7 +400,7 @@ class SigmaDslTesting extends AnyPropSpec ctx } - val (expectedResult, expectedCost) = if (activatedVersionInTests < sinceVersion) + val (expectedResult, expectedCost) = if (activatedVersionInTests < sinceVersion.activatedVersion) (expected.oldResult, expected.verificationCostOpt) else { val res = expected.newResults(ergoTreeVersionInTests) @@ -507,7 +507,7 @@ class SigmaDslTesting extends AnyPropSpec implicit val cs = compilerSettingsInTests - override def sinceVersion: Byte = 0 + override def sinceVersion: VersionContext = VersionContext(0, 0) // has always been supported override def isSupportedIn(vc: VersionContext): Boolean = true @@ -669,7 +669,7 @@ class SigmaDslTesting extends AnyPropSpec * @param allowDifferentErrors if true, allow v4.x and v5.0 to fail with different error */ case class ChangedFeature[A, B]( - changedInVersion: Byte, + changedInVersion: VersionContext, script: String, scalaFunc: A => B, override val scalaFuncNew: A => B, @@ -683,7 +683,7 @@ class SigmaDslTesting extends AnyPropSpec implicit val cs = compilerSettingsInTests - override def sinceVersion: Byte = changedInVersion + override def sinceVersion: VersionContext = changedInVersion override def isSupportedIn(vc: VersionContext): Boolean = true @@ -764,7 +764,7 @@ class SigmaDslTesting extends AnyPropSpec checkEq(scalaFuncNew)(newF)(input) } - if (VersionContext.current.activatedVersion < changedInVersion) { + if (VersionContext.current.activatedVersion < changedInVersion.activatedVersion) { // check the old implementation with Scala semantic val expectedOldRes = expected.value @@ -859,7 +859,7 @@ class SigmaDslTesting extends AnyPropSpec * @param logScript if true, log scripts to console */ case class NewFeature[A, B]( - sinceVersion: Byte, + sinceVersion: VersionContext, script: String, override val scalaFuncNew: A => B, expectedExpr: Option[SValue], @@ -869,7 +869,7 @@ class SigmaDslTesting extends AnyPropSpec extends Feature[A, B] { override def isSupportedIn(vc: VersionContext): Boolean = - vc.activatedVersion >= sinceVersion && vc.ergoTreeVersion >= sinceVersion + sinceVersion <= vc override def scalaFunc: A => B = { x => sys.error(s"Semantic Scala function is not defined for old implementation: $this") @@ -1063,7 +1063,7 @@ class SigmaDslTesting extends AnyPropSpec * various ways */ def changedFeature[A: RType, B: RType] - (changedInVersion: Byte, + (changedInVersion: VersionContext, scalaFunc: A => B, scalaFuncNew: A => B, script: String, @@ -1088,7 +1088,8 @@ class SigmaDslTesting extends AnyPropSpec * various ways */ def newFeature[A: RType, B: RType] - (scalaFunc: A => B, script: String, expectedExpr: SValue = null, sinceVersion: Byte = VersionContext.JitActivationVersion) + (scalaFunc: A => B, script: String, expectedExpr: SValue = null, + sinceVersion: VersionContext = VersionContext(VersionContext.JitActivationVersion, 0)) (implicit IR: IRContext, es: EvalSettings): Feature[A, B] = { NewFeature(sinceVersion, script, scalaFunc, Option(expectedExpr)) } From dd7bdc63024bfa4e16eb4723c7739c9e0187fb2a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 7 Jun 2024 13:21:45 +0300 Subject: [PATCH 11/40] removing serialize --- .../src/main/scala/sigma/SigmaDsl.scala | 3 - .../sigma/reflection/ReflectionData.scala | 4 - .../validation/ValidationRules.scala | 17 +-- .../scala/sigma/SigmaDataReflection.scala | 5 - .../src/main/scala/sigma/ast/ErgoTree.scala | 5 - .../main/scala/sigma/ast/SigmaPredef.scala | 18 --- .../src/main/scala/sigma/ast/methods.scala | 37 +---- .../scala/sigma/data/CSigmaDslBuilder.scala | 8 -- .../serialization/ErgoTreeSerializer.scala | 48 ++----- docs/LangSpec.md | 7 +- .../sigmastate/lang/SigmaParserTest.scala | 14 +- .../sigmastate/ReflectionGenerator.scala | 11 ++ .../sigma/compiler/ir/GraphIRReflection.scala | 3 - .../ir/wrappers/sigma/SigmaDslUnit.scala | 1 - .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 15 -- .../sigma/compiler/phases/SigmaBinder.scala | 3 - .../scala/sigma/LanguageSpecificationV6.scala | 130 ------------------ .../sigmastate/lang/SigmaBinderTest.scala | 11 -- .../sigmastate/lang/SigmaTyperTest.scala | 31 ----- 19 files changed, 30 insertions(+), 341 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 6e306f7a0b..df2b419273 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -727,9 +727,6 @@ trait SigmaDslBuilder { /** Construct a new authenticated dictionary with given parameters and tree root digest. */ def avlTree(operationFlags: Byte, digest: Coll[Byte], keyLength: Int, valueLengthOpt: Option[Int]): AvlTree - /** Serializes the given `value` into bytes using the default serialization format. */ - def serialize[T](value: T)(implicit cT: RType[T]): Coll[Byte] - /** Returns a byte-wise XOR of the two collections of bytes. */ def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte] } diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index 5cd678b712..028e68bf72 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -441,10 +441,6 @@ object ReflectionData { mkMethod(clazz, "sha256", Array[Class[_]](cColl)) { (obj, args) => obj.asInstanceOf[SigmaDslBuilder].sha256(args(0).asInstanceOf[Coll[Byte]]) }, - mkMethod(clazz, "serialize", Array[Class[_]](classOf[Object], classOf[RType[_]])) { (obj, args) => - obj.asInstanceOf[SigmaDslBuilder].serialize[Any]( - args(0).asInstanceOf[Any])(args(1).asInstanceOf[RType[Any]]) - }, mkMethod(clazz, "decodePoint", Array[Class[_]](cColl)) { (obj, args) => obj.asInstanceOf[SigmaDslBuilder].decodePoint(args(0).asInstanceOf[Coll[Byte]]) } diff --git a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala index 07fe8db0ee..9d4de47a99 100644 --- a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala +++ b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala @@ -155,20 +155,6 @@ object ValidationRules { override protected lazy val settings: SigmaValidationSettings = currentSettings } - object CheckMinimalErgoTreeVersion extends ValidationRule(1016, - "ErgoTree should have at least required version") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = currentSettings - - final def apply(currentVersion: Byte, minVersion: Byte): Unit = { - checkRule() - if (currentVersion < minVersion) { - throwValidationException( - new SigmaException(s"ErgoTree should have at least $minVersion version, but was $currentVersion"), - Array(currentVersion, minVersion)) - } - } - } - val ruleSpecs: Seq[ValidationRule] = Seq( CheckDeserializedScriptType, CheckDeserializedScriptIsSigmaProp, @@ -185,8 +171,7 @@ object ValidationRules { CheckHeaderSizeBit, CheckCostFuncOperation, CheckPositionLimit, - CheckLoopLevelInCostFunction, - CheckMinimalErgoTreeVersion + CheckLoopLevelInCostFunction ) /** Validation settings that correspond to the current version of the ErgoScript implementation. diff --git a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala index a6e5de2a26..48939b1460 100644 --- a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala +++ b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala @@ -322,11 +322,6 @@ object SigmaDataReflection { args(1).asInstanceOf[SigmaDslBuilder], args(2).asInstanceOf[Coll[Byte]], args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "serialize_eval", Array[Class[_]](classOf[MethodCall], classOf[SigmaDslBuilder], classOf[Object], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SGlobalMethods.type].serialize_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[SigmaDslBuilder], - args(2).asInstanceOf[SType#WrappedType])(args(3).asInstanceOf[ErgoTreeEvaluator]) } ) ) diff --git a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala index eae420612e..68d69abd91 100644 --- a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala +++ b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala @@ -228,11 +228,6 @@ object ErgoTree { type HeaderType = HeaderType.Type - implicit class HeaderTypeOps(val header: HeaderType) extends AnyVal { - def withVersion(version: Byte): HeaderType = ErgoTree.headerWithVersion(header, version) - def withConstantSegregation: HeaderType = ErgoTree.setConstantSegregation(header) - } - /** Current version of ErgoTree serialization format (aka bite-code language version) */ val VersionFlag: Byte = VersionContext.MaxSupportedScriptVersion diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index f2da24d2df..897ac6d757 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -403,24 +403,6 @@ object SigmaPredef { ArgInfo("default", "optional default value, if register is not available"))) ) - val SerializeFunc = PredefinedFunc("serialize", - Lambda(Seq(paramT), Array("value" -> tT), SByteArray, None), - PredefFuncInfo( - { case (_, args @ Seq(value)) => - MethodCall.typed[Value[SCollection[SByte.type]]]( - Global, - SGlobalMethods.serializeMethod.withConcreteTypes(Map(tT -> value.tpe)), - args.toIndexedSeq, - Map() - ) - }), - OperationInfo(MethodCall, - """ - """.stripMargin, - Seq(ArgInfo("value", "")) - ) - ) - val globalFuncs: Map[String, PredefinedFunc] = Seq( AllOfFunc, AnyOfFunc, diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 1a0841e97c..136225f256 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1,7 +1,6 @@ package sigma.ast import org.ergoplatform._ -import org.ergoplatform.validation.ValidationRules.CheckMinimalErgoTreeVersion import org.ergoplatform.validation._ import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} @@ -1543,37 +1542,9 @@ case object SGlobalMethods extends MonoTypeMethods { Xor.xorWithCosting(ls, rs) } - lazy val serializeMethod = SMethod(this, "serialize", - SFunc(Array(SGlobal, tT), SByteArray, Array(paramT)), 3, DynamicCost) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "", - ArgInfo("value", "value to be serialized")) - - - /** Implements evaluation of Global.serialize method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def serialize_eval(mc: MethodCall, G: SigmaDslBuilder, value: SType#WrappedType) - (implicit E: ErgoTreeEvaluator): Coll[Byte] = { - // TODO v6.0: accumulate cost - val t = Evaluation.stypeToRType(mc.args(0).tpe) - G.serialize(value)(t) - } - - protected override def getMethods() = super.getMethods() ++ { - if (VersionContext.current.isV6SoftForkActivated) { - Seq( - groupGeneratorMethod, - xorMethod, - serializeMethod - ) - } else { - Seq( - groupGeneratorMethod, - xorMethod - ) - } - } + protected override def getMethods() = super.getMethods() ++ Seq( + groupGeneratorMethod, + xorMethod + ) } diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 21b9900028..d7b092fc0e 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -200,14 +200,6 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => val p = GroupElementSerializer.parse(r) this.GroupElement(p) } - - /** Serializes the given `value` into bytes using the default serialization format. */ - override def serialize[T](value: T)(implicit cT: RType[T]): Coll[Byte] = { - val tpe = Evaluation.rtypeToSType(cT) - val w = SigmaSerializer.startWriter() - DataSerializer.serialize(value.asInstanceOf[SType#WrappedType], tpe, w) - Colls.fromArray(w.toBytes) - } } /** Default singleton instance of Global object, which implements global ErgoTree functions. */ diff --git a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala index e7bb46429a..43e41f91ff 100644 --- a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala @@ -287,9 +287,6 @@ class ErgoTreeSerializer { * allow to use serialized scripts as pre-defined templates. * See [[SubstConstants]] for details. * - * Note, this operation doesn't require (de)serialization of ErgoTree expression, - * thus it is more efficient than serialization roundtrip. - * * @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1. * @param positions zero based indexes in ErgoTree.constants array which * should be replaced with new values @@ -307,23 +304,21 @@ class ErgoTreeSerializer { s"expected positions and newVals to have the same length, got: positions: ${positions.toSeq},\n newVals: ${newVals.toSeq}") val r = SigmaSerializer.startReader(scriptBytes) val (header, _, constants, treeBytes) = deserializeHeaderWithTreeBytes(r) - val nConstants = constants.length - - val resBytes = if (VersionContext.current.isJitActivated) { - // need to measure the serialized size of the new constants - // by serializing them into a separate writer - val constW = SigmaSerializer.startWriter() + val w = SigmaSerializer.startWriter() + w.put(header) + if (VersionContext.current.isJitActivated) { // The following `constants.length` should not be serialized when segregation is off // in the `header`, because in this case there is no `constants` section in the // ErgoTree serialization format. Thus, applying this `substituteConstants` for // non-segregated trees will return non-parsable ErgoTree bytes (when // `constants.length` is put in `w`). if (ErgoTree.isConstantSegregation(header)) { - constW.putUInt(constants.length) + w.putUInt(constants.length) } // The following is optimized O(nConstants + position.length) implementation + val nConstants = constants.length if (nConstants > 0) { val backrefs = getPositionsBackref(positions, nConstants) cfor(0)(_ < nConstants, _ + 1) { i => @@ -331,38 +326,17 @@ class ErgoTreeSerializer { val iPos = backrefs(i) // index to `positions` if (iPos == -1) { // no position => no substitution, serialize original constant - constantSerializer.serialize(c, constW) + constantSerializer.serialize(c, w) } else { - require(positions(iPos) == i) // INV: backrefs and positions are mutually inverse + assert(positions(iPos) == i) // INV: backrefs and positions are mutually inverse val newConst = newVals(iPos) require(c.tpe == newConst.tpe, s"expected new constant to have the same ${c.tpe} tpe, got ${newConst.tpe}") - constantSerializer.serialize(newConst, constW) + constantSerializer.serialize(newConst, w) } } } - - val constBytes = constW.toBytes // nConstants + serialized new constants - - // start composing the resulting tree bytes - val w = SigmaSerializer.startWriter() - w.put(header) // header byte - - if (VersionContext.current.isV6SoftForkActivated) { - // fix in v6.0 to save tree size to respect size bit of the original tree - if (ErgoTree.hasSize(header)) { - val size = constBytes.length + treeBytes.length - w.putUInt(size) // tree size - } - } - - w.putBytes(constBytes) // constants section - w.putBytes(treeBytes) // tree section - w.toBytes } else { - val w = SigmaSerializer.startWriter() - w.put(header) - // for v4.x compatibility we save constants.length here (see the above comment to // understand the consequences) w.putUInt(constants.length) @@ -383,12 +357,10 @@ class ErgoTreeSerializer { case (c, _) => constantSerializer.serialize(c, w) } - - w.putBytes(treeBytes) - w.toBytes } - (resBytes, nConstants) + w.putBytes(treeBytes) + (w.toBytes, constants.length) } } diff --git a/docs/LangSpec.md b/docs/LangSpec.md index 04b345d6d9..ba66748f08 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -68,7 +68,7 @@ The following sections describe ErgoScript and its operations. #### Operations and constructs overview - Binary operations: `>, <, >=, <=, +, -, &&, ||, ==, !=, |, &, *, /, %, ^, ++` -- predefined primitives: `serialize`, `blake2b256`, `byteArrayToBigInt`, `proveDlog` etc. +- predefined primitives: `blake2b256`, `byteArrayToBigInt`, `proveDlog` etc. - val declarations: `val h = blake2b256(pubkey)` - if-then-else clause: `if (x > 0) 1 else 0` - collection literals: `Coll(1, 2, 3, 4)` @@ -1041,11 +1041,6 @@ def deserialize[T](string: String): T * replaced and all other bytes remain exactly the same */ def substConstants[T](scriptBytes: Coll[Byte], positions: Coll[Int], newValues: Coll[T]): Coll[Byte] - -/** Serializes an instance of type T using default serialization format. - * See https://github.com/ScorexFoundation/sigmastate-interpreter/issues/988 for more details - */ -def serialize[T](value: T): Coll[Byte] ``` ## Examples diff --git a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala index 70aa540a4f..dc63330f95 100644 --- a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala @@ -35,6 +35,9 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat } } + /** Checks parsing result, printing the actual value as a test vector if expected value + * is not equal to actual. + */ def checkParsed(x: String, expected: SValue) = { val parsed = parse(x) if (expected != parsed) { @@ -903,17 +906,6 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat ) } - property("serialize") { - checkParsed("serialize(1)", Apply(Ident("serialize", NoType), Array(IntConstant(1)))) - checkParsed("serialize((1, 2L))", - Apply(Ident("serialize", NoType), Array(Tuple(Vector(IntConstant(1), LongConstant(2L)))))) - checkParsed("serialize(Coll(1, 2, 3))", - Apply( - Ident("serialize", NoType), - Array(Apply(Ident("Coll", NoType), Array(IntConstant(1), IntConstant(2), IntConstant(3)))) - )) - } - property("single name pattern fail") { fail("{val (a,b) = (1,2)}", 1, 6) } diff --git a/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala b/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala index 324542505e..dd5262e700 100644 --- a/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala +++ b/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala @@ -5,6 +5,17 @@ import sigma.reflection._ import scala.annotation.unused import scala.collection.mutable +/** Generates code for registering classes in the ReflectionData. + * It is not used in the runtime. + * + * The generated invocations of `registerClassEntry`, `mkMethod`, `mkConstructor` may + * require manual adjustments. + * + * It uses [[sigma.reflection.Platform.unknownClasses]] to collect classes which were + * accessed during runtime + * + * @see [[ReflectionData]] + */ object ReflectionGenerator { def normalizeName(name: String): String = { 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 6c0403f643..69736a0224 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -504,9 +504,6 @@ object GraphIRReflection { }, mkMethod(clazz, "decodePoint", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.SigmaDslBuilder].decodePoint(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]]) - }, - mkMethod(clazz, "serialize", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => - obj.asInstanceOf[ctx.SigmaDslBuilder].serialize(args(0).asInstanceOf[ctx.Ref[Any]]) } ) ) 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 d7e574c68c..2a6a341686 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,7 +114,6 @@ 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 serialize[T](value: Ref[T]): Ref[Coll[Byte]] }; 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 9cd524149d..c113cb7de3 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,14 +1945,6 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { Array[AnyRef](l, r), true, false, element[Coll[Byte]])) } - - def serialize[T](value: Ref[T]): Ref[Coll[Byte]] = { - asRep[Coll[Byte]](mkMethodCall(self, - SigmaDslBuilderClass.getMethod("serialize", classOf[Sym]), - Array[AnyRef](value), - true, true, element[Coll[Byte]])) - } - } implicit object LiftableSigmaDslBuilder @@ -2112,13 +2104,6 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { Array[AnyRef](l, r), true, true, element[Coll[Byte]])) } - - def serialize[T](value: Ref[T]): Ref[Coll[Byte]] = { - asRep[Coll[Byte]](mkMethodCall(source, - SigmaDslBuilderClass.getMethod("serialize", classOf[Sym]), - Array[AnyRef](value), - true, true, element[Coll[Byte]])) - } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala index d4943ef892..af5be938be 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala @@ -105,9 +105,6 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder, case a @ Apply(PKFunc.symNoType, args) => Some(PKFunc.irInfo.irBuilder(PKFunc.sym, args).withPropagatedSrcCtx(a.sourceContext)) - case a @ Apply(predefFuncRegistry.SerializeFunc.symNoType, args) => - Some(predefFuncRegistry.SerializeFunc.irInfo.irBuilder(PKFunc.sym, args).withPropagatedSrcCtx(a.sourceContext)) - case sel @ Select(obj, "isEmpty", _) => Some(mkLogicalNot(mkSelect(obj, "isDefined").asBoolValue).withPropagatedSrcCtx(sel.sourceContext)) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index d89fc1f623..6eb3dc4c11 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -29,51 +29,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq(0, 1, 2, 3).map(version => version -> res) } - def mkSerializeFeature[A: RType]: Feature[A, Coll[Byte]] = { - val tA = RType[A] - val tpe = Evaluation.rtypeToSType(tA) - newFeature( - (x: A) => SigmaDsl.serialize(x), - s"{ (x: ${tA.name}) => serialize(x) }", - expectedExpr = FuncValue( - Array((1, tpe)), - MethodCall( - Global, - SGlobalMethods.serializeMethod.withConcreteTypes(Map(STypeVar("T") -> tpe)), - Array(ValUse(1, tpe)), - Map() - ) - ), - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) - } - - property("Global.serialize[Byte]") { - lazy val serializeByte = mkSerializeFeature[Byte] - val cases = Seq( - (-128.toByte, Success(Coll(-128.toByte))), - (-1.toByte, Success(Coll(-1.toByte))), - (0.toByte, Success(Coll(0.toByte))), - (1.toByte, Success(Coll(1.toByte))), - (127.toByte, Success(Coll(127.toByte))) - ) - testCases(cases, serializeByte) - } - - property("Global.serialize[Short]") { - lazy val serializeShort = mkSerializeFeature[Short] - val cases = Seq( - (Short.MinValue, Success(Coll[Byte](0xFF.toByte, 0xFF.toByte, 0x03.toByte))), - (-1.toShort, Success(Coll(1.toByte))), - (0.toShort, Success(Coll(0.toByte))), - (1.toShort, Success(Coll(2.toByte))), - (Short.MaxValue, Success(Coll(-2.toByte, -1.toByte, 3.toByte))) - ) - testCases(cases, serializeShort) - } - - // TODO v6.0: implement serialization roundtrip tests after merge with deserializeTo - - property("Boolean.toByte") { val toByte = newFeature((x: Boolean) => x.toByte, "{ (x: Boolean) => x.toByte }", sinceVersion = VersionContext.sinceV6AndTreeVersion(0) @@ -390,91 +345,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => } } - property("Fix substConstants in v6.0 for ErgoTree version > 0") { - // tree with one segregated constant and v0 - val t1 = ErgoTree( - header = ZeroHeader.withConstantSegregation, - constants = Vector(TrueSigmaProp), - ConstantPlaceholder(0, SSigmaProp)) - - // tree with one segregated constant and max supported version - val t2 = ErgoTree( - header = ZeroHeader - .withVersion(VersionContext.MaxSupportedScriptVersion) - .withConstantSegregation, - Vector(TrueSigmaProp), - ConstantPlaceholder(0, SSigmaProp)) - - def costDetails(nItems: Int) = TracedCost( - traceBase ++ Array( - FixedCostItem(SelectField), - FixedCostItem(ConcreteCollection), - FixedCostItem(ValUse), - FixedCostItem(SelectField), - FixedCostItem(ConcreteCollection), - FixedCostItem(Constant), - FixedCostItem(BoolToSigmaProp), - ast.SeqCostItem(CompanionDesc(SubstConstants), PerItemCost(JitCost(100), JitCost(100), 1), nItems) - ) - ) - - val expectedTreeBytes_beforeV6 = Helpers.decodeBytes("1b0108d27300") - val expectedTreeBytes_V6 = Helpers.decodeBytes("1b050108d27300") - - verifyCases( - Seq( - // for tree v0, the result is the same for all versions - (Coll(t1.bytes: _*), 0) -> Expected( - Success(Helpers.decodeBytes("100108d27300")), - cost = 1793, - expectedDetails = CostDetails.ZeroCost, - newCost = 1793, - newVersionedResults = expectedSuccessForAllTreeVersions(Helpers.decodeBytes("100108d27300"), 1793, costDetails(1)) - ), - // for tree version > 0, the result depend on activated version - { - (Coll(t2.bytes: _*), 0) -> Expected( - Success(expectedTreeBytes_beforeV6), - cost = 1793, - expectedDetails = CostDetails.ZeroCost, - newCost = 1793, - newVersionedResults = expectedSuccessForAllTreeVersions(expectedTreeBytes_V6, 1793, costDetails(1))) - } - ), - changedFeature( - changedInVersion = VersionContext.sinceV6AndTreeVersion(0), - { (x: (Coll[Byte], Int)) => - SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) - }, - { (x: (Coll[Byte], Int)) => - SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) - }, - "{ (x: (Coll[Byte], Int)) => substConstants[Any](x._1, Coll[Int](x._2), Coll[Any](sigmaProp(false))) }", - FuncValue( - Vector((1, SPair(SByteArray, SInt))), - SubstConstants( - SelectField.typed[Value[SCollection[SByte.type]]](ValUse(1, SPair(SByteArray, SInt)), 1.toByte), - ConcreteCollection( - Array(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByteArray, SInt)), 2.toByte)), - SInt - ), - ConcreteCollection(Array(BoolToSigmaProp(FalseLeaf)), SSigmaProp) - ) - ) - ) - ) - - // before v6.0 the expected tree is not parsable - ErgoTree.fromBytes(expectedTreeBytes_beforeV6.toArray).isRightParsed shouldBe false - - // in v6.0 the expected tree should be parsable and similar to the original tree - val tree = ErgoTree.fromBytes(expectedTreeBytes_V6.toArray) - tree.isRightParsed shouldBe true - tree.header shouldBe t2.header - tree.constants.length shouldBe t2.constants.length - tree.root shouldBe t2.root - } - property("Numeric.toBytes methods equivalence") { lazy val toBytes = newFeature( { (x: Byte) => x.toBigEndianBytes }, diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala index 54bd89c9c2..1f15f5d747 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala @@ -213,15 +213,4 @@ class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat e.source shouldBe Some(SourceContext(2, 5, "val x = 10")) } - property("predefined `serialize` should be transformed to MethodCall") { - runWithVersion(VersionContext.V6SoftForkVersion) { - checkBound(env, "serialize(1)", - MethodCall.typed[Value[SCollection[SByte.type]]]( - Global, - SGlobalMethods.getMethodByName("serialize").withConcreteTypes(Map(STypeVar("T") -> SInt)), - Array(IntConstant(1)), - Map() - )) - } - } } diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 102033ead9..ba6eb55ee6 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -713,35 +713,4 @@ class SigmaTyperTest extends AnyPropSpec typecheck(customEnv, "substConstants(scriptBytes, positions, newVals)") shouldBe SByteArray } - property("Global.serialize") { - runWithVersion(VersionContext.V6SoftForkVersion) { - typecheck(env, "Global.serialize(1)", - MethodCall.typed[Value[SCollection[SByte.type]]]( - Global, - SGlobalMethods.getMethodByName("serialize").withConcreteTypes(Map(STypeVar("T") -> SInt)), - Array(IntConstant(1)), - Map() - )) shouldBe SByteArray - } - - runWithVersion((VersionContext.V6SoftForkVersion - 1).toByte) { - assertExceptionThrown( - typecheck(env, "Global.serialize(1)"), - exceptionLike[MethodNotFound]("Cannot find method 'serialize' in in the object Global") - ) - } - } - - property("predefined serialize") { - runWithVersion(VersionContext.V6SoftForkVersion) { - typecheck(env, "serialize((1, 2L))", - expected = MethodCall.typed[Value[SCollection[SByte.type]]]( - Global, - SGlobalMethods.getMethodByName("serialize").withConcreteTypes(Map(STypeVar("T") -> SPair(SInt, SLong))), - Array(Tuple(Vector(IntConstant(1), LongConstant(2L)))), - Map() - )) shouldBe SByteArray - } - } - } From e1bfb1c07999f81a9b9308695f0cd24feec43f12 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 7 Jun 2024 15:21:08 +0300 Subject: [PATCH 12/40] remove serialize from GraphBuilding and ErgoTreeSpecification --- data/shared/src/main/scala/sigma/ast/SigmaPredef.scala | 3 +-- .../src/main/scala/sigma/compiler/ir/GraphBuilding.scala | 3 --- .../src/test/scala/sigmastate/ErgoTreeSpecification.scala | 5 +---- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 897ac6d757..ebe8aa0213 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -430,8 +430,7 @@ object SigmaPredef { AvlTreeFunc, SubstConstantsFunc, ExecuteFromVarFunc, - ExecuteFromSelfRegFunc, - SerializeFunc + ExecuteFromSelfRegFunc ).map(f => f.name -> f).toMap def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { 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 ddbec96518..5595ded3db 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1147,9 +1147,6 @@ 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.serializeMethod.name => - val value = asRep[Any](argsV(0)) - g.serialize(value) case _ => throwError() } case (x: Ref[tNum], _: SNumericTypeMethods) => method.name match { diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 6e6fb824de..05a6017e98 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -479,10 +479,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C { import SGlobalMethods._ (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) - ) ++ (if (isV6Activated) Seq( - // methods added in v6.0 - MInfo(3, serializeMethod) - ) else Seq.empty), true) + ), true) }, { import SCollectionMethods._ (SCollection.typeId, Seq( From a731962932c40d2e240e4d282ca78c5092e96743 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 5 Jul 2024 22:44:32 +0300 Subject: [PATCH 13/40] simplifying the code --- .../src/main/scala/sigma/VersionContext.scala | 13 ---- .../src/main/scala/sigma/ast/SType.scala | 28 ++++---- .../src/main/scala/sigma/util/Versioned.scala | 27 ------- .../src/main/scala/sigma/ast/methods.scala | 44 +++++++----- .../scala/sigma/data/CSigmaDslBuilder.scala | 6 +- .../scala/sigma/LanguageSpecificationV5.scala | 20 ++++-- .../scala/sigma/LanguageSpecificationV6.scala | 71 ++++++++++--------- .../test/scala/sigma/SigmaDslTesting.scala | 6 +- 8 files changed, 98 insertions(+), 117 deletions(-) delete mode 100644 core/shared/src/main/scala/sigma/util/Versioned.scala diff --git a/core/shared/src/main/scala/sigma/VersionContext.scala b/core/shared/src/main/scala/sigma/VersionContext.scala index ecdcfb5f62..19a4857086 100644 --- a/core/shared/src/main/scala/sigma/VersionContext.scala +++ b/core/shared/src/main/scala/sigma/VersionContext.scala @@ -25,12 +25,6 @@ case class VersionContext(activatedVersion: Byte, ergoTreeVersion: Byte) { /** @return true, if the activated script version of Ergo protocol on the network is * including Evolution update. */ def isV6SoftForkActivated: Boolean = activatedVersion >= V6SoftForkVersion - - /** @return true if another [[VersionContext]] is greater than this. */ - def <= (that: VersionContext): Boolean = { - this.activatedVersion < that.activatedVersion || - (this.activatedVersion == that.activatedVersion && this.ergoTreeVersion <= that.ergoTreeVersion) - } } object VersionContext { @@ -110,11 +104,4 @@ object VersionContext { } } - /** Returns the VersionContext with V5 activation and the given ErgoTree version. */ - def sinceV5AndTreeVersion(treeVersion: Byte): VersionContext = - VersionContext(JitActivationVersion, ergoTreeVersion = treeVersion) - - /** Returns the VersionContext with V6 activation and the given ErgoTree version. */ - def sinceV6AndTreeVersion(treeVersion: Byte): VersionContext = - VersionContext(V6SoftForkVersion, ergoTreeVersion = treeVersion) } diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 4c0e797066..aa4ad0bccf 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -7,7 +7,6 @@ import sigma.data.OverloadHack.Overloaded1 import sigma.data.{CBigInt, Nullable, SigmaConstants} import sigma.reflection.{RClass, RMethod, ReflectionData} import sigma.util.Extensions.{IntOps, LongOps, ShortOps} -import sigma.util.Versioned import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp, VersionContext} import java.math.BigInteger @@ -141,18 +140,21 @@ object SType { * * The regression tests in `property("MethodCall Codes")` should pass. */ - private val _types: Versioned[Map[Byte, STypeCompanion]] = Versioned({ version => - val v5x = Seq( - SBoolean, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, - SAvlTree, SBox, SOption, SCollection, SBigInt - ) - val v6 = if (version >= VersionContext.V6SoftForkVersion) - Seq(SByte, SShort, SInt, SLong) - else - Seq.empty - (v5x ++ v6).map { t => (t.typeId, t) }.toMap - }) - def types: Map[Byte, STypeCompanion] = _types.get(VersionContext.current.activatedVersion) + private val v5Types = Seq( + SBoolean, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, + SAvlTree, SBox, SOption, SCollection, SBigInt + ) + private val v6Types = v5Types ++ Seq(SByte, SShort, SInt, SLong) + + private val v5TypesMap = v5Types.map { t => (t.typeId, t) }.toMap + + private val v6TypesMap = v6Types.map { t => (t.typeId, t) }.toMap + + def types: Map[Byte, STypeCompanion] = if (VersionContext.current.isV6SoftForkActivated) { + v6TypesMap + } else { + v5TypesMap + } /** Checks that the type of the value corresponds to the descriptor `tpe`. * If the value has complex structure only root type constructor is checked. diff --git a/core/shared/src/main/scala/sigma/util/Versioned.scala b/core/shared/src/main/scala/sigma/util/Versioned.scala deleted file mode 100644 index a3eae2325a..0000000000 --- a/core/shared/src/main/scala/sigma/util/Versioned.scala +++ /dev/null @@ -1,27 +0,0 @@ -package sigma.util - -import sigma.VersionContext - -import scala.reflect.ClassTag - -/** Represents a versioned object that can be created for each supported version. - * The object is created lazily and cached for each version. - * - * @param builder a total function that creates an object for a given version in [0, - * maxVersion] range. - * @param maxVersion the maximum supported version. - */ -case class Versioned[T <: AnyRef: ClassTag](builder: Byte => T, maxVersion: Byte = VersionContext.MaxSupportedScriptVersion) { - private val cache = new Array[T](maxVersion + 1) - - def get(version: Byte): T = { - require(version <= VersionContext.MaxSupportedScriptVersion, s"Not supported version $version") - if (cache(version) == null) { - val v = builder(version) - cache(version) = v - v - } else { - cache(version) - } - } -} diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 136225f256..27112f7ac2 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -5,7 +5,7 @@ import org.ergoplatform.validation._ import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} -import sigma.ast.SType.{TypeCode, paramT, tT} +import sigma.ast.SType.TypeCode import sigma.ast.syntax.{SValue, ValueOps} import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} import sigma.data.OverloadHack.Overloaded1 @@ -13,7 +13,6 @@ import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConsta import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} import sigma.reflection.RClass import sigma.serialization.CoreByteWriter.ArgInfo -import sigma.util.Versioned import sigma.utils.SparseArrayContainer import scala.annotation.unused @@ -158,23 +157,30 @@ trait MonoTypeMethods extends MethodsContainer { trait SNumericTypeMethods extends MonoTypeMethods { import SNumericTypeMethods.tNum - private val _getMethods = Versioned({ version => - val subst = Map(tNum -> this.ownerType) - val numericMethods = if (version < VersionContext.V6SoftForkVersion) - SNumericTypeMethods.methods.map { m => - m.copy(stype = applySubst(m.stype, subst).asFunc ) - } - else - SNumericTypeMethods.methods.map { m => - m.copy( - objType = this, // associate the method with the concrete numeric type - stype = applySubst(m.stype, subst).asFunc - )} - super.getMethods() ++ numericMethods - }) - - protected override def getMethods(): Seq[SMethod] = - _getMethods.get(VersionContext.current.activatedVersion) + + private val subst = Map(tNum -> this.ownerType) + + private val v5Methods = { + SNumericTypeMethods.methods.map { m => + m.copy(stype = applySubst(m.stype, subst).asFunc) + } + } + + private val v6Methods = { + SNumericTypeMethods.methods.map { m => + m.copy( + objType = this, // associate the method with the concrete numeric type + stype = applySubst(m.stype, subst).asFunc + )} + } + + protected override def getMethods(): Seq[SMethod] = { + if (VersionContext.current.isV6SoftForkActivated) { + super.getMethods() ++ v6Methods + } else { + super.getMethods() ++ v5Methods + } + } } object SNumericTypeMethods extends MethodsContainer { diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index d7b092fc0e..3938feacd3 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -5,13 +5,13 @@ import org.ergoplatform.ErgoBox import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} import scorex.utils.Longs -import sigma.ast.{AtLeast, SType, SubstConstants} +import sigma.ast.{AtLeast, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps -import sigma.serialization.{DataSerializer, GroupElementSerializer, SigmaSerializer} +import sigma.serialization.{GroupElementSerializer, SigmaSerializer} import sigma.util.Extensions.BigIntegerOps import sigma.validation.SigmaValidationSettings -import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, Evaluation, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext} +import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext} import java.math.BigInteger diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index 48f1a3f7ca..592971ac82 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -10,6 +10,7 @@ import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId import sigma.Extensions.{ArrayOps, CollOps} +import sigma.VersionContext.JitActivationVersion import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} import sigma.ast.SCollection._ import sigma.ast.syntax._ @@ -47,6 +48,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => import TestData._ + /** Returns the VersionContext with V5 activation and the given ErgoTree version. */ + def sinceV5AndTreeVersion(treeVersion: Byte): VersionContext = + VersionContext(JitActivationVersion, ergoTreeVersion = treeVersion) + + def upcastCostDetails(tpe: SType) = TracedCost(traceBase :+ TypeBasedCostItem(Upcast, tpe)) def downcastCostDetails(tpe: SType) = TracedCost(traceBase :+ TypeBasedCostItem(Downcast, tpe)) def arithOpsCostDetails(tpe: SType) = CostDetails( @@ -4802,7 +4808,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => })) ), changedFeature( - changedInVersion = VersionContext.sinceV5AndTreeVersion(0), + changedInVersion = sinceV5AndTreeVersion(0), { (x: Context) => x.selfBoxIndex }, { (x: Context) => x.selfBoxIndex }, // see versioning in selfBoxIndex implementation "{ (x: Context) => x.selfBoxIndex }", @@ -5004,7 +5010,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ), changedFeature( - changedInVersion = VersionContext.sinceV5AndTreeVersion(0), + changedInVersion = sinceV5AndTreeVersion(0), scalaFunc = { (x: Context) => // this error is expected in v3.x, v4.x throw expectedError @@ -5978,7 +5984,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) }, changedFeature( - changedInVersion = VersionContext.sinceV5AndTreeVersion(0), + changedInVersion = sinceV5AndTreeVersion(0), (x: Coll[Boolean]) => SigmaDsl.xorOf(x), (x: Coll[Boolean]) => SigmaDsl.xorOf(x), "{ (x: Coll[Boolean]) => xorOf(x) }", @@ -6241,7 +6247,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) }, changedFeature( - changedInVersion = VersionContext.sinceV5AndTreeVersion(0), + changedInVersion = sinceV5AndTreeVersion(0), (x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), (x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), "{ (x: (Coll[Byte], Coll[Byte])) => xor(x._1, x._2) }", @@ -8811,7 +8817,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => (Some(Long.MaxValue) -> Expected(new ArithmeticException("long overflow"))) ), changedFeature( - changedInVersion = VersionContext.sinceV5AndTreeVersion(0), + changedInVersion = sinceV5AndTreeVersion(0), scalaFunc = { (x: Option[Long]) => def f(opt: Long): Long = n.plus(opt, 1) if (x.isDefined) f(x.get) @@ -9367,7 +9373,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) }, changedFeature( - changedInVersion = VersionContext.sinceV5AndTreeVersion(0), + changedInVersion = sinceV5AndTreeVersion(0), { (x: (Coll[Byte], Int)) => SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) }, @@ -9430,7 +9436,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ), changedFeature( - changedInVersion = VersionContext.sinceV5AndTreeVersion(0), + changedInVersion = sinceV5AndTreeVersion(0), { (x: Context) => throw error true diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 6eb3dc4c11..3848e3268f 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,6 +1,7 @@ package sigma import org.ergoplatform.sdk.utils.ErgoTreeUtils +import sigma.VersionContext.V6SoftForkVersion import sigma.ast.ErgoTree.ZeroHeader import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp @@ -24,6 +25,10 @@ import scala.util.Success class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => override def languageVersion: Byte = VersionContext.V6SoftForkVersion + /** Returns the VersionContext with V6 activation and the given ErgoTree version. */ + def sinceV6AndTreeVersion(treeVersion: Byte): VersionContext = + VersionContext(V6SoftForkVersion, ergoTreeVersion = treeVersion) + def expectedSuccessForAllTreeVersions[A](value: A, cost: Int, costDetails: CostDetails) = { val res = ExpectedResult(Success(value), Some(cost)) -> Some(costDetails) Seq(0, 1, 2, 3).map(version => version -> res) @@ -31,7 +36,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Boolean.toByte") { val toByte = newFeature((x: Boolean) => x.toByte, "{ (x: Boolean) => x.toByte }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0) + sinceVersion = sinceV6AndTreeVersion(0) ) val cases = Seq( @@ -57,22 +62,22 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // which is checked below lazy val toAbs = newFeature((x: Byte) => x.toAbs, "{ (x: Byte) => x.toAbs }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature( (x: (Byte, Byte)) => x._1.compareTo(x._2), "{ (x: (Byte, Byte)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature( { (x: (Byte, Byte)) => (x._1 | x._2).toByteExact }, "{ (x: (Byte, Byte)) => (x._1 | x._2) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature( { (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }, "{ (x: (Byte, Byte)) => (x._1 & x._2) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) forAll { x: Byte => Seq(toAbs).foreach(f => f.checkEquality(x)) @@ -91,21 +96,21 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // which is checked below lazy val toAbs = newFeature((x: Short) => x.toAbs, "{ (x: Short) => x.toAbs }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature((x: (Short, Short)) => x._1.compareTo(x._2), "{ (x: (Short, Short)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature( { (x: (Short, Short)) => (x._1 | x._2).toShortExact }, "{ (x: (Short, Short)) => x._1 | x._2 }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature( { (x: (Short, Short)) => (x._1 & x._2).toShortExact }, "{ (x: (Short, Short)) => x._1 & x._2 }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) forAll { x: Short => Seq(toAbs).foreach(_.checkEquality(x)) @@ -121,18 +126,18 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // NOTE, for such versions the new features are not supported // which is checked below lazy val toAbs = newFeature((x: Int) => x.toAbs, "{ (x: Int) => x.toAbs }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature((x: (Int, Int)) => x._1.compareTo(x._2), "{ (x: (Int, Int)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature( { (x: (Int, Int)) => x._1 | x._2 }, "{ (x: (Int, Int)) => x._1 | x._2 }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature( { (x: (Int, Int)) => x._1 & x._2 }, "{ (x: (Int, Int)) => x._1 & x._2 }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) forAll { x: Int => Seq(toAbs).foreach(_.checkEquality(x)) } @@ -147,20 +152,20 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // NOTE, for such versions the new features are not supported // which is checked below lazy val toAbs = newFeature((x: Long) => x.toAbs, "{ (x: Long) => x.toAbs }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature((x: (Long, Long)) => x._1.compareTo(x._2), "{ (x: (Long, Long)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature( { (x: (Long, Long)) => x._1 | x._2 }, "{ (x: (Long, Long)) => x._1 | x._2 }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature( { (x: (Long, Long)) => x._1 & x._2 }, "{ (x: (Long, Long)) => x._1 & x._2 }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) forAll { x: Long => Seq(toAbs).foreach(_.checkEquality(x)) @@ -195,30 +200,30 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => val toByte = newFeature((x: BigInt) => x.toByte, "{ (x: BigInt) => x.toByte }", FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SByte)), - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) val toShort = newFeature((x: BigInt) => x.toShort, "{ (x: BigInt) => x.toShort }", FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SShort)), - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) val toInt = newFeature((x: BigInt) => x.toInt, "{ (x: BigInt) => x.toInt }", FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SInt)), - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) val toLong = newFeature((x: BigInt) => x.toLong, "{ (x: BigInt) => x.toLong }", FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SLong)), - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val toAbs = newFeature((x: BigInt) => x.toAbs, "{ (x: BigInt) => x.toAbs }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val compareTo = newFeature((x: (BigInt, BigInt)) => x._1.compareTo(x._2), "{ (x: (BigInt, BigInt)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitOr = newFeature({ (x: (BigInt, BigInt)) => x._1 | x._2 }, "{ (x: (BigInt, BigInt)) => x._1 | x._2 }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) lazy val bitAnd = newFeature({ (x: (BigInt, BigInt)) => x._1 & x._2 }, "{ (x: (BigInt, BigInt)) => x._1 & x._2 }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) forAll { x: BigInt => Seq(toByte, toShort, toInt, toLong, toAbs).foreach(_.checkEquality(x)) @@ -233,7 +238,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // TODO v6.0: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416 val getReg = newFeature((x: Box) => x.getReg[Int](1).get, "{ (x: Box) => x.getReg[Int](1).get }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -249,7 +254,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Coll find method equivalence") { val find = newFeature((x: Coll[Int]) => x.find({ (v: Int) => v > 0 }), "{ (x: Coll[Int]) => x.find({ (v: Int) => v > 0} ) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -268,7 +273,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => if (x.size > 2) x.slice(0, x.size - 2) else Colls.emptyColl[Boolean] }, "{ (x: Coll[Boolean]) => x >> 2 }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -284,7 +289,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Coll diff methods equivalence") { val diff = newFeature((x: (Coll[Int], Coll[Int])) => x._1.diff(x._2), "{ (x: (Coll[Int], Coll[Int])) => x._1.diff(x._2) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -301,7 +306,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => 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 }) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -317,7 +322,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("allZK equivalence") { lazy val allZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.allZK(x), "{ (x: Coll[SigmaProp]) => allZK(x) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -333,7 +338,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("anyZK equivalence") { lazy val anyZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.anyZK(x), "{ (x: Coll[SigmaProp]) => anyZK(x) }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -349,7 +354,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => lazy val toBytes = newFeature( { (x: Byte) => x.toBigEndianBytes }, "{ (x: Byte) => x.toBytes }", - sinceVersion = VersionContext.sinceV6AndTreeVersion(0)) + sinceVersion = sinceV6AndTreeVersion(0)) val cases = Seq( (0.toByte, Success(Coll(0.toByte))), (1.toByte, Success(Coll(1.toByte))) diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 0b512d1380..33c4708942 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -868,8 +868,10 @@ class SigmaDslTesting extends AnyPropSpec )(implicit IR: IRContext, override val evalSettings: EvalSettings, val tA: RType[A], val tB: RType[B]) extends Feature[A, B] { - override def isSupportedIn(vc: VersionContext): Boolean = - sinceVersion <= vc + override def isSupportedIn(vc: VersionContext): Boolean = { + sinceVersion.activatedVersion < vc.activatedVersion || + (sinceVersion.activatedVersion == vc.activatedVersion && sinceVersion.ergoTreeVersion <= vc.ergoTreeVersion) + } override def scalaFunc: A => B = { x => sys.error(s"Semantic Scala function is not defined for old implementation: $this") From cb7e26209ee2608a7c36657e8e37279a8a48a2a6 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 6 Jul 2024 22:15:09 +0300 Subject: [PATCH 14/40] initial toBits failing tests --- .../sigmastate/utxo/BasicOpsSpecification.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..f6f8399aeb 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} @@ -135,6 +136,20 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } + property("BigInt.toBits") { + def toBitsTest() = test("R1", env, ext, + """{ + | val b = 1.toBigInt + | b.toBits == Coll(true) + |}""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 From f577fd359fe310d61c9a19b1c5bdbc996e1e96cb Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 8 Jul 2024 10:59:16 +0300 Subject: [PATCH 15/40] passing test (compilation fixed) --- .../scala/sigma/compiler/ir/TreeBuilding.scala | 6 +++++- .../compiler/ir/primitives/NumericOps.scala | 5 +++-- .../sigmastate/utxo/BasicOpsSpecification.scala | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) 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 f7daff90ae..4894940ef6 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -186,7 +186,11 @@ trait TreeBuilding extends Base { IR: IRContext => .asInstanceOf[ConstantNode[SType]] s.put(constant)(builder) case None => - mkConstant[tpe.type](x.asInstanceOf[tpe.WrappedType], tpe) + if(x.isInstanceOf[CollConst[_, _]]) { // hack used to process NumericToBigEndianBytes only + mkConstant[tpe.type](x.asInstanceOf[CollConst[_, _]].constValue.asInstanceOf[tpe.WrappedType], tpe) + } else { + mkConstant[tpe.type](x.asInstanceOf[tpe.WrappedType], tpe) + } } case Def(IR.ConstantPlaceholder(id, elem)) => val tpe = elemToSType(elem) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index ef4124d0b5..80a2560d0f 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -71,8 +71,9 @@ trait NumericOps extends Base { self: IRContext => /** Descriptor of unary `ToBigEndianBytes` conversion operation. */ case class NumericToBigEndianBytes[T](n: ExactNumeric[T]) extends UnOp[T, Coll[Byte]]("ToBigEndianBytes")(element[Coll[Byte]]) { - override def applySeq(x: T): Coll[Byte] = - n.toBigEndianBytes(x).asInstanceOf[Coll[Byte]] + override def applySeq(x: T): Coll[Byte] = { + liftableColl(Liftables.ByteIsLiftable).lift(n.toBigEndianBytes(x)) + } } /** Descriptor of binary `/` operation (integral division). */ diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..f88e821e9e 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,22 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Long.toBytes") { + def toBytesTest() = test("Long.toBytes", env, ext, + """{ + | val l = 1L + | l.toBytes.size > 0 + | }""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[Exception] shouldBe thrownBy(toBytesTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From cadf53d8e4439cfc2427183bd56069407304a37c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 8 Jul 2024 11:10:23 +0300 Subject: [PATCH 16/40] tests for Byte and BigInt --- .../utxo/BasicOpsSpecification.scala | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index f88e821e9e..de30a6f509 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -158,11 +158,44 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } - property("Long.toBytes") { - def toBytesTest() = test("Long.toBytes", env, ext, + property("Int.toBytes") { + def toBytesTest() = test("Int.toBytes", env, ext, """{ - | val l = 1L - | l.toBytes.size > 0 + | val l = 1 + | l.toBytes == Coll(0.toByte, 0.toByte, 0.toByte, 1.toByte) + | }""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[Exception] shouldBe thrownBy(toBytesTest()) + } + } + + property("Byte.toBytes") { + def toBytesTest() = test("Byte.toBytes", env, ext, + """{ + | val l = 10.toByte + | l.toBytes == Coll(10.toByte) + | }""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[Exception] shouldBe thrownBy(toBytesTest()) + } + } + + + property("BigInt.toBytes") { + def toBytesTest() = test("BigInt.toBytes", env, ext, + s"""{ + | val l = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | l.toBytes.size == 32 | }""".stripMargin, null ) From ee59083d9ada7e8ea51b7ad728cc6ada74089c7c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 8 Jul 2024 13:35:48 +0300 Subject: [PATCH 17/40] Byte.toBits test passing --- .../src/main/scala/sigma/data/BigIntegerOps.scala | 2 ++ .../src/main/scala/sigma/data/ExactIntegral.scala | 11 +++++++++++ .../src/main/scala/sigma/data/ExactNumeric.scala | 2 ++ .../main/scala/sigma/compiler/ir/GraphBuilding.scala | 3 +++ .../main/scala/sigma/compiler/ir/TreeBuilding.scala | 7 +++++++ .../sigma/compiler/ir/primitives/NumericOps.scala | 8 ++++++++ .../sigmastate/utxo/BasicOpsSpecification.scala | 12 +++++++----- 7 files changed, 40 insertions(+), 5 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 0bb9e9101b..788915a1e8 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -92,6 +92,8 @@ object NumericOps { override def divisionRemainder(x: BigInt, y: BigInt): BigInt = x.mod(y) override def toBigEndianBytes(x: BigInt): Coll[Byte] = Colls.fromArray(x.toBigInteger.toByteArray) + + override def toBits(x: BigInt): Coll[Boolean] = ??? } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index eaf89947c1..ad01e34dc3 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -39,6 +39,13 @@ object ExactIntegral { override def minus(x: Byte, y: Byte): Byte = x.subtractExact(y) override def times(x: Byte, y: Byte): Byte = x.multiplyExact(y) override def toBigEndianBytes(x: Byte): Coll[Byte] = Colls.fromItems(x) + override def toBits(x: Byte): Coll[Boolean] = { + def byte2Bools(b: Byte): Array[Boolean] = (0 to 7).toArray.reverse.map(isBitSet(b)) + + def isBitSet(byte: Byte)(bit: Int): Boolean = ((byte >> bit) & 1) == 1 + + Colls.fromArray(byte2Bools(x)) + } } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -47,6 +54,7 @@ object ExactIntegral { override def minus(x: Short, y: Short): Short = x.subtractExact(y) override def times(x: Short, y: Short): Short = x.multiplyExact(y) override def toBigEndianBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) + override def toBits(x: Short): Coll[Boolean] = ??? } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -56,6 +64,7 @@ object ExactIntegral { override def times(x: Int, y: Int): Int = java7.compat.Math.multiplyExact(x, y) override def toBigEndianBytes(x: Int): Coll[Byte] = Colls.fromItems((x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) + override def toBits(x: Int): Coll[Boolean] = ??? } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -65,5 +74,7 @@ object ExactIntegral { override def times(x: Long, y: Long): Long = java7.compat.Math.multiplyExact(x, y) override def toBigEndianBytes(x: Long): Coll[Byte] = Colls.fromItems((x >> 56).toByte, (x >> 48).toByte, (x >> 40).toByte, (x >> 32).toByte, (x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) + + override def toBits(x: Long): Coll[Boolean] = ??? } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index d8b3b5df0d..166e8d4fd1 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -37,6 +37,8 @@ trait ExactNumeric[T] { */ def toBigEndianBytes(x: T): Coll[Byte] + def toBits(x: T): Coll[Boolean] + /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) 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 5595ded3db..67bd0e37f3 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1153,6 +1153,9 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SNumericTypeMethods.ToBytesMethod.name => val op = NumericToBigEndianBytes(elemToExactNumeric(x.elem)) ApplyUnOp(op, x) + case SNumericTypeMethods.ToBitsMethod.name => + val op = NumericToBits(elemToExactNumeric(x.elem)) + ApplyUnOp(op, x) case _ => throwError() } case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") 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 4894940ef6..0da5f332cc 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -113,6 +113,13 @@ trait TreeBuilding extends Base { IR: IRContext => builder.mkMethodCall(v.asNumValue, m, IndexedSeq.empty) } Some(mkNode) + case _: NumericToBits[_] => + val mkNode = { v: SValue => + val receiverType = v.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${v.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.ToBitsMethod.methodId) + builder.mkMethodCall(v.asNumValue, m, IndexedSeq.empty) + } + Some(mkNode) case _ => None } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index 80a2560d0f..543bd708c9 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -76,6 +76,14 @@ trait NumericOps extends Base { self: IRContext => } } + /** Descriptor of unary `ToBigEndianBytes` conversion operation. */ + case class NumericToBits[T](n: ExactNumeric[T]) + extends UnOp[T, Coll[Boolean]]("ToBits")(element[Coll[Boolean]]) { + override def applySeq(x: T): Coll[Boolean] = { + liftableColl(Liftables.BooleanIsLiftable).lift(n.toBits(x)) + } + } + /** Descriptor of binary `/` operation (integral division). */ case class IntegralDivide[T](i: ExactIntegral[T])(implicit elem: Elem[T]) extends DivOp[T]("/", i) { override def applySeq(x: T, y: T): T = i.quot(x, y) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 28886ba6c6..1380de5f72 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -136,17 +136,19 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } - property("BigInt.toBits") { - def toBitsTest() = test("R1", env, ext, + property("Byte.toBits") { + def toBitsTest() = test("Byte.toBits", env, ext, """{ - | val b = 1.toBigInt - | b.toBits == Coll(true) + | val b = 1.toByte + | b.toBits == Coll(false, false, false, false, false, false, false, true) |}""".stripMargin, null ) - if(VersionContext.current.isV6SoftForkActivated) { + if (VersionContext.current.isV6SoftForkActivated) { toBitsTest() + } else { + an[Exception] shouldBe thrownBy(toBitsTest()) } } From 3fde5faf34368843d9bcc1e2199217b378ccb1e3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 8 Jul 2024 19:43:08 +0300 Subject: [PATCH 18/40] toBits tests for Long and BigInt(failing) --- .../main/scala/sigma/data/BigIntegerOps.scala | 2 -- .../main/scala/sigma/data/ExactIntegral.scala | 11 ------ .../main/scala/sigma/data/ExactNumeric.scala | 19 ++++++++-- .../utxo/BasicOpsSpecification.scala | 36 +++++++++++++++++++ 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 788915a1e8..0bb9e9101b 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -92,8 +92,6 @@ object NumericOps { override def divisionRemainder(x: BigInt, y: BigInt): BigInt = x.mod(y) override def toBigEndianBytes(x: BigInt): Coll[Byte] = Colls.fromArray(x.toBigInteger.toByteArray) - - override def toBits(x: BigInt): Coll[Boolean] = ??? } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index ad01e34dc3..eaf89947c1 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -39,13 +39,6 @@ object ExactIntegral { override def minus(x: Byte, y: Byte): Byte = x.subtractExact(y) override def times(x: Byte, y: Byte): Byte = x.multiplyExact(y) override def toBigEndianBytes(x: Byte): Coll[Byte] = Colls.fromItems(x) - override def toBits(x: Byte): Coll[Boolean] = { - def byte2Bools(b: Byte): Array[Boolean] = (0 to 7).toArray.reverse.map(isBitSet(b)) - - def isBitSet(byte: Byte)(bit: Int): Boolean = ((byte >> bit) & 1) == 1 - - Colls.fromArray(byte2Bools(x)) - } } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -54,7 +47,6 @@ object ExactIntegral { override def minus(x: Short, y: Short): Short = x.subtractExact(y) override def times(x: Short, y: Short): Short = x.multiplyExact(y) override def toBigEndianBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) - override def toBits(x: Short): Coll[Boolean] = ??? } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -64,7 +56,6 @@ object ExactIntegral { override def times(x: Int, y: Int): Int = java7.compat.Math.multiplyExact(x, y) override def toBigEndianBytes(x: Int): Coll[Byte] = Colls.fromItems((x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) - override def toBits(x: Int): Coll[Boolean] = ??? } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -74,7 +65,5 @@ object ExactIntegral { override def times(x: Long, y: Long): Long = java7.compat.Math.multiplyExact(x, y) override def toBigEndianBytes(x: Long): Coll[Byte] = Colls.fromItems((x >> 56).toByte, (x >> 48).toByte, (x >> 40).toByte, (x >> 32).toByte, (x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) - - override def toBits(x: Long): Coll[Boolean] = ??? } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 166e8d4fd1..93bc577e14 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -1,8 +1,10 @@ package sigma.data -import sigma.Coll +import sigma.{Coll, Colls} import sigma.data.ExactIntegral._ +import scala.collection.mutable + /** Numeric operations with overflow checks. * Raise exception when overflow is detected. * Each instance of this typeclass overrides three methods `plus`, `minus`, `times`. @@ -37,7 +39,20 @@ trait ExactNumeric[T] { */ def toBigEndianBytes(x: T): Coll[Byte] - def toBits(x: T): Coll[Boolean] + def toBits(x: T): Coll[Boolean] = { + def byte2Bools(b: Byte): Array[Boolean] = (0 to 7).toArray.reverse.map(isBitSet(b)) + + def isBitSet(byte: Byte)(bit: Int): Boolean = ((byte >> bit) & 1) == 1 + + val bytes = toBigEndianBytes(x) + val builder = mutable.ArrayBuilder.make[Boolean] + val l = bytes.length + (0 until l).foreach{i=> + val b = bytes(i) + builder.addAll(byte2Bools(b)) + } + Colls.fromArray(builder.result()) + } /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 1380de5f72..85e61b31d1 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -152,6 +152,42 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Long.toBits") { + def toBitsTest() = test("Long.toBits", env, ext, + """{ + | val b = 1L + | val ba = b.toBits + | + | // only rightmost bit is set + | ba.size == 64 && ba(63) == true && ba.slice(0, 63).forall({ (b: Boolean ) => b == false }) + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[Exception] shouldBe thrownBy(toBitsTest()) + } + } + + property("BigInt.toBits") { + def toBitsTest() = test("BigInt.toBits", env, ext, + s"""{ + | val b = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val ba = b.toBits + | ba.size == 256 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[Exception] shouldBe thrownBy(toBitsTest()) + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 From 9ea75971dac5118e0cb90ecdddf88f2efd92e562 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 8 Jul 2024 19:50:07 +0300 Subject: [PATCH 19/40] BigInt.toBits test passing --- data/shared/src/main/scala/sigma/ast/methods.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 27112f7ac2..7ac65a4613 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -8,6 +8,7 @@ import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} import sigma.ast.SType.TypeCode import sigma.ast.syntax.{SValue, ValueOps} import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} +import sigma.data.NumericOps.BigIntIsExactIntegral import sigma.data.OverloadHack.Overloaded1 import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} @@ -258,6 +259,15 @@ object SNumericTypeMethods extends MethodsContainer { val ToBitsMethod: SMethod = SMethod( this, "toBits", SFunc(tNum, SBooleanArray), 7, ToBits_CostKind) .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.toBits(obj.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.toBits(obj.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.toBits(obj.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.toBits(obj.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.toBits(obj.asInstanceOf[BigInt]) + } + }) .withInfo(PropertyCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. From be77315def76163c44006430bfba2d6f7e4e933a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 9 Jul 2024 19:12:59 +0300 Subject: [PATCH 20/40] bitwise inversion --- .../src/main/scala/sigma/ast/methods.scala | 23 ++++++++++++++++++- .../main/scala/sigma/data/BigIntegerOps.scala | 2 ++ .../main/scala/sigma/data/ExactIntegral.scala | 4 ++++ .../main/scala/sigma/data/ExactNumeric.scala | 2 ++ .../sigma/compiler/ir/GraphBuilding.scala | 3 +++ .../sigma/compiler/ir/TreeBuilding.scala | 7 ++++++ .../compiler/ir/primitives/NumericOps.scala | 7 +++++- .../utxo/BasicOpsSpecification.scala | 18 +++++++++++++++ 8 files changed, 64 insertions(+), 2 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 7ac65a4613..22ba217d5f 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -273,6 +273,26 @@ object SNumericTypeMethods extends MethodsContainer { | Each boolean corresponds to one bit. """.stripMargin) + /** Cost of inverting bits of a number. */ + val BitwiseInverse_CostKind = FixedCost(JitCost(5)) + + val BitwiseInverseMethod: SMethod = SMethod( + this, "bitwiseInverse", SFunc(tNum, tNum), 8, BitwiseInverse_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[BigInt]) + } + }) + .withInfo(PropertyCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.stripMargin) + protected override def getMethods(): Seq[SMethod] = Array( ToByteMethod, // see Downcast ToShortMethod, // see Downcast @@ -280,7 +300,8 @@ object SNumericTypeMethods extends MethodsContainer { ToLongMethod, // see Downcast ToBigIntMethod, // see Downcast ToBytesMethod, - ToBitsMethod + ToBitsMethod, + BitwiseInverseMethod ) /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 0bb9e9101b..723e6a6691 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -92,6 +92,8 @@ object NumericOps { override def divisionRemainder(x: BigInt, y: BigInt): BigInt = x.mod(y) override def toBigEndianBytes(x: BigInt): Coll[Byte] = Colls.fromArray(x.toBigInteger.toByteArray) + + override def bitwiseInverse(x: BigInt): BigInt = CBigInt(x.toBigInteger.not()) } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index eaf89947c1..7242259480 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -39,6 +39,7 @@ object ExactIntegral { override def minus(x: Byte, y: Byte): Byte = x.subtractExact(y) override def times(x: Byte, y: Byte): Byte = x.multiplyExact(y) override def toBigEndianBytes(x: Byte): Coll[Byte] = Colls.fromItems(x) + override def bitwiseInverse(x: Byte): Byte = (~x).toByte } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -47,6 +48,7 @@ object ExactIntegral { override def minus(x: Short, y: Short): Short = x.subtractExact(y) override def times(x: Short, y: Short): Short = x.multiplyExact(y) override def toBigEndianBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) + override def bitwiseInverse(x: Short): Short = (~x).toShort } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -56,6 +58,7 @@ object ExactIntegral { override def times(x: Int, y: Int): Int = java7.compat.Math.multiplyExact(x, y) override def toBigEndianBytes(x: Int): Coll[Byte] = Colls.fromItems((x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) + override def bitwiseInverse(x: Int): Int = ~x } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -65,5 +68,6 @@ object ExactIntegral { override def times(x: Long, y: Long): Long = java7.compat.Math.multiplyExact(x, y) override def toBigEndianBytes(x: Long): Coll[Byte] = Colls.fromItems((x >> 56).toByte, (x >> 48).toByte, (x >> 40).toByte, (x >> 32).toByte, (x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) + override def bitwiseInverse(x: Long): Long = ~x } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 93bc577e14..7809ec4ca6 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -54,6 +54,8 @@ trait ExactNumeric[T] { Colls.fromArray(builder.result()) } + def bitwiseInverse(x: T): T + /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) 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 67bd0e37f3..fcde26e0d5 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1156,6 +1156,9 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SNumericTypeMethods.ToBitsMethod.name => val op = NumericToBits(elemToExactNumeric(x.elem)) ApplyUnOp(op, x) + case SNumericTypeMethods.BitwiseInverseMethod.name => + val op = NumericBitwiseInverse(elemToExactNumeric(x.elem))(x.elem) + ApplyUnOp(op, x) case _ => throwError() } case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") 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 0da5f332cc..c671726507 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -120,6 +120,13 @@ trait TreeBuilding extends Base { IR: IRContext => builder.mkMethodCall(v.asNumValue, m, IndexedSeq.empty) } Some(mkNode) + case _: NumericBitwiseInverse[_] => + val mkNode = { v: SValue => + val receiverType = v.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${v.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseInverseMethod.methodId) + builder.mkMethodCall(v.asNumValue, m, IndexedSeq.empty) + } + Some(mkNode) case _ => None } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index 543bd708c9..a9f0c0a3fa 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -76,7 +76,7 @@ trait NumericOps extends Base { self: IRContext => } } - /** Descriptor of unary `ToBigEndianBytes` conversion operation. */ + /** Descriptor of unary `ToBits` conversion operation. */ case class NumericToBits[T](n: ExactNumeric[T]) extends UnOp[T, Coll[Boolean]]("ToBits")(element[Coll[Boolean]]) { override def applySeq(x: T): Coll[Boolean] = { @@ -84,6 +84,11 @@ trait NumericOps extends Base { self: IRContext => } } + /** Descriptor of unary `ToBits` conversion operation. */ + case class NumericBitwiseInverse[T: Elem](n: ExactNumeric[T]) extends UnOp[T, T]("~") { + override def applySeq(x: T): T = n.bitwiseInverse(x) + } + /** Descriptor of binary `/` operation (integral division). */ case class IntegralDivide[T](i: ExactIntegral[T])(implicit elem: Elem[T]) extends DivOp[T]("/", i) { override def applySeq(x: T, y: T): T = i.quot(x, y) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 85e61b31d1..570a1b76d7 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -2,6 +2,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ +import org.scalatest.Assertion import sigma.Extensions.ArrayOps import sigma.VersionContext import sigma.ast.SCollection.SByteArray @@ -188,6 +189,23 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("BigInt.bitwiseInverse") { + def bitwiseInverseTest(): Assertion = test("BigInt.bitwiseInverse", env, ext, + s"""{ + | val b = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val bi = b.bitwiseInverse + | bi.bitwiseInverse == b + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseInverseTest() + } else { + an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 From a575d38a1b874cb9b87d3bdfb5610f918ed39868 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 9 Jul 2024 19:41:04 +0300 Subject: [PATCH 21/40] bitwiseInverse tests for Long and Byte --- .../utxo/BasicOpsSpecification.scala | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 570a1b76d7..c9e05f5226 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -206,6 +206,39 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Byte.bitwiseInverse") { + def bitwiseInverseTest(): Assertion = test("Byte.bitwiseInverse", env, ext, + s"""{ + | val b = 127.toByte + | b.bitwiseInverse == (-128).toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseInverseTest() + } else { + an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + } + } + + property("Long.bitwiseInverse") { + def bitwiseInverseTest(): Assertion = test("Long.bitwiseInverse", env, ext, + s"""{ + | val l = 9223372036854775807L + | val lb = l.bitwiseInverse + | lb.bitwiseInverse == l + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseInverseTest() + } else { + an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 From 7ff35428b87d6bdee060a390f84a212728738b38 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 10 Jul 2024 12:34:10 +0300 Subject: [PATCH 22/40] bitwise or impl --- .../src/main/scala/sigma/ast/methods.scala | 54 ++++++++++++++++++- .../main/scala/sigma/data/BigIntegerOps.scala | 2 + .../main/scala/sigma/data/ExactIntegral.scala | 4 ++ .../main/scala/sigma/data/ExactNumeric.scala | 2 + .../sigma/compiler/ir/GraphBuilding.scala | 4 ++ .../sigma/compiler/ir/TreeBuilding.scala | 7 +++ .../compiler/ir/primitives/NumericOps.scala | 5 ++ .../utxo/BasicOpsSpecification.scala | 35 +++++++++++- 8 files changed, 111 insertions(+), 2 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 22ba217d5f..ade482c168 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -293,6 +293,57 @@ object SNumericTypeMethods extends MethodsContainer { | Each boolean corresponds to one bit. """.stripMargin) + val BitwiseOrMethod: SMethod = SMethod( + this, "bitwiseOr", SFunc(Array(tNum, tNum), tNum), 9, BitwiseInverse_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.bitwiseOr(obj.asInstanceOf[Byte], other.head.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseOr(obj.asInstanceOf[Short], other.head.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseOr(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseOr(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseOr(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) + } + }) + .withInfo(PropertyCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.stripMargin) + + val BitwiseAndMethod: SMethod = SMethod( + this, "bitwiseInverse", SFunc(tNum, tNum), 10, BitwiseInverse_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[BigInt]) + } + }) + .withInfo(PropertyCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.stripMargin) + + val BitwiseXorMethod: SMethod = SMethod( + this, "bitwiseInverse", SFunc(tNum, tNum), 11, BitwiseInverse_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[BigInt]) + } + }) + .withInfo(PropertyCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.stripMargin) + protected override def getMethods(): Seq[SMethod] = Array( ToByteMethod, // see Downcast ToShortMethod, // see Downcast @@ -301,7 +352,8 @@ object SNumericTypeMethods extends MethodsContainer { ToBigIntMethod, // see Downcast ToBytesMethod, ToBitsMethod, - BitwiseInverseMethod + BitwiseInverseMethod, + BitwiseOrMethod ) /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 723e6a6691..8a7d597ed0 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -94,6 +94,8 @@ object NumericOps { override def toBigEndianBytes(x: BigInt): Coll[Byte] = Colls.fromArray(x.toBigInteger.toByteArray) override def bitwiseInverse(x: BigInt): BigInt = CBigInt(x.toBigInteger.not()) + + def bitwiseOr(x: BigInt, y: BigInt): BigInt = CBigInt(x.toBigInteger.or(y.toBigInteger)) } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 7242259480..787800a14a 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -40,6 +40,7 @@ object ExactIntegral { override def times(x: Byte, y: Byte): Byte = x.multiplyExact(y) override def toBigEndianBytes(x: Byte): Coll[Byte] = Colls.fromItems(x) override def bitwiseInverse(x: Byte): Byte = (~x).toByte + override def bitwiseOr(x: Byte, y: Byte): Byte = (x | y).toByte } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -49,6 +50,7 @@ object ExactIntegral { override def times(x: Short, y: Short): Short = x.multiplyExact(y) override def toBigEndianBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) override def bitwiseInverse(x: Short): Short = (~x).toShort + override def bitwiseOr(x: Short, y: Short): Short = (x | y).toShort } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -59,6 +61,7 @@ object ExactIntegral { override def toBigEndianBytes(x: Int): Coll[Byte] = Colls.fromItems((x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) override def bitwiseInverse(x: Int): Int = ~x + override def bitwiseOr(x: Int, y: Int): Int = x | y } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -69,5 +72,6 @@ object ExactIntegral { override def toBigEndianBytes(x: Long): Coll[Byte] = Colls.fromItems((x >> 56).toByte, (x >> 48).toByte, (x >> 40).toByte, (x >> 32).toByte, (x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) override def bitwiseInverse(x: Long): Long = ~x + override def bitwiseOr(x: Long, y: Long): Long = x | y } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 7809ec4ca6..8328ac4143 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -56,6 +56,8 @@ trait ExactNumeric[T] { def bitwiseInverse(x: T): T + def bitwiseOr(x: T, y: T): T + /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) 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 fcde26e0d5..cfa67f40a4 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1159,6 +1159,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SNumericTypeMethods.BitwiseInverseMethod.name => val op = NumericBitwiseInverse(elemToExactNumeric(x.elem))(x.elem) ApplyUnOp(op, x) + case SNumericTypeMethods.BitwiseOrMethod.name => + val y = asRep[tNum](argsV(0)) + val op = NumericBitwiseOr(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOp(op, x, y) case _ => throwError() } case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") 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 c671726507..9fa4f80208 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -217,6 +217,13 @@ trait TreeBuilding extends Base { IR: IRContext => case Def(IsContextProperty(v)) => v case s if s == sigmaDslBuilder => Global + case Def(ApplyBinOp(op, xSym, ySym)) if op.isInstanceOf[NumericBitwiseOr[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseOrMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + + case Def(ApplyBinOp(IsArithOp(opCode), xSym, ySym)) => val Seq(x, y) = Seq(xSym, ySym).map(recurse) mkArith(x.asNumValue, y.asNumValue, opCode) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index a9f0c0a3fa..6c77b02fdf 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -47,6 +47,11 @@ trait NumericOps extends Base { self: IRContext => override def applySeq(x: T, y: T): T = n.times(x, y) } + /** Descriptor of unary `ToBits` conversion operation. */ + case class NumericBitwiseOr[T: Elem](n: ExactNumeric[T]) extends EndoBinOp[T]("|") { + override def applySeq(x: T, y: T): T = n.bitwiseOr(x, y) + } + /** Base class for descriptors of binary division operations. */ abstract class DivOp[T: Elem](opName: String, n: ExactIntegral[T]) extends EndoBinOp[T](opName) { override def shouldPropagate(lhs: T, rhs: T) = rhs != n.zero diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index c9e05f5226..b87121e4e4 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -209,7 +209,7 @@ class BasicOpsSpecification extends CompilerTestingCommons property("Byte.bitwiseInverse") { def bitwiseInverseTest(): Assertion = test("Byte.bitwiseInverse", env, ext, s"""{ - | val b = 127.toByte + | val b = (126 + 1).toByte // max byte value | b.bitwiseInverse == (-128).toByte |}""".stripMargin, null @@ -239,6 +239,39 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Byte.bitwiseOr") { + def bitwiseOrTest(): Assertion = test("Byte.bitwiseOrTest", env, ext, + s"""{ + | val x = 127.toByte + | val y = (-128).toByte + | x.bitwiseOr(y) == -1 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseOrTest() + } else { + an[Exception] shouldBe thrownBy(bitwiseOrTest()) + } + } + + property("BigInt.bitwiseOr") { + def bitwiseInverseTest(): Assertion = test("BigInt.bitwiseInverse", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | x.bitwiseOr(x) == x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseInverseTest() + } else { + an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 From 868fbb10c43626faec175698bf94e8f1b9dc3908 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 10 Jul 2024 15:24:19 +0300 Subject: [PATCH 23/40] bitwiseAnd --- .../src/main/scala/sigma/ast/methods.scala | 17 ++++---- .../main/scala/sigma/data/BigIntegerOps.scala | 2 + .../main/scala/sigma/data/ExactIntegral.scala | 4 ++ .../main/scala/sigma/data/ExactNumeric.scala | 2 + .../sigma/compiler/ir/GraphBuilding.scala | 4 ++ .../sigma/compiler/ir/TreeBuilding.scala | 6 +++ .../compiler/ir/primitives/NumericOps.scala | 4 ++ .../utxo/BasicOpsSpecification.scala | 41 +++++++++++++++++-- 8 files changed, 69 insertions(+), 11 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index ade482c168..003b9923e0 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -311,15 +311,15 @@ object SNumericTypeMethods extends MethodsContainer { """.stripMargin) val BitwiseAndMethod: SMethod = SMethod( - this, "bitwiseInverse", SFunc(tNum, tNum), 10, BitwiseInverse_CostKind) + this, "bitwiseAnd", SFunc(Array(tNum, tNum), tNum), 10, BitwiseInverse_CostKind) .withIRInfo(MethodCallIrBuilder) - .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => m.objType match { - case SByteMethods => ByteIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Byte]) - case SShortMethods => ShortIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Short]) - case SIntMethods => IntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Int]) - case SLongMethods => LongIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Long]) - case SBigIntMethods => BigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[BigInt]) + case SByteMethods => ByteIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Byte], other.head.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Short], other.head.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseAnd(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) } }) .withInfo(PropertyCall, @@ -353,7 +353,8 @@ object SNumericTypeMethods extends MethodsContainer { ToBytesMethod, ToBitsMethod, BitwiseInverseMethod, - BitwiseOrMethod + BitwiseOrMethod, + BitwiseAndMethod ) /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 8a7d597ed0..2aefe1cbc8 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -96,6 +96,8 @@ object NumericOps { override def bitwiseInverse(x: BigInt): BigInt = CBigInt(x.toBigInteger.not()) def bitwiseOr(x: BigInt, y: BigInt): BigInt = CBigInt(x.toBigInteger.or(y.toBigInteger)) + + def bitwiseAnd(x: BigInt, y: BigInt): BigInt = CBigInt(x.toBigInteger.and(y.toBigInteger)) } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 787800a14a..490890e21a 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -41,6 +41,7 @@ object ExactIntegral { override def toBigEndianBytes(x: Byte): Coll[Byte] = Colls.fromItems(x) override def bitwiseInverse(x: Byte): Byte = (~x).toByte override def bitwiseOr(x: Byte, y: Byte): Byte = (x | y).toByte + override def bitwiseAnd(x: Byte, y: Byte): Byte = (x & y).toByte } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -51,6 +52,7 @@ object ExactIntegral { override def toBigEndianBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) override def bitwiseInverse(x: Short): Short = (~x).toShort override def bitwiseOr(x: Short, y: Short): Short = (x | y).toShort + override def bitwiseAnd(x: Short, y: Short): Short = (x & y).toShort } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -62,6 +64,7 @@ object ExactIntegral { Colls.fromItems((x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) override def bitwiseInverse(x: Int): Int = ~x override def bitwiseOr(x: Int, y: Int): Int = x | y + override def bitwiseAnd(x: Int, y: Int): Int = x & y } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -73,5 +76,6 @@ object ExactIntegral { Colls.fromItems((x >> 56).toByte, (x >> 48).toByte, (x >> 40).toByte, (x >> 32).toByte, (x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) override def bitwiseInverse(x: Long): Long = ~x override def bitwiseOr(x: Long, y: Long): Long = x | y + override def bitwiseAnd(x: Long, y: Long): Long = x & y } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 8328ac4143..116599ba9b 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -58,6 +58,8 @@ trait ExactNumeric[T] { def bitwiseOr(x: T, y: T): T + def bitwiseAnd(x: T, y: T): T + /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) 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 cfa67f40a4..1c467fada8 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1163,6 +1163,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val y = asRep[tNum](argsV(0)) val op = NumericBitwiseOr(elemToExactNumeric(x.elem))(x.elem) ApplyBinOp(op, x, y) + case SNumericTypeMethods.BitwiseAndMethod.name => + val y = asRep[tNum](argsV(0)) + val op = NumericBitwiseAnd(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOp(op, x, y) case _ => throwError() } case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") 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 9fa4f80208..e79fb56a2e 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -223,6 +223,12 @@ trait TreeBuilding extends Base { IR: IRContext => val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseOrMethod.methodId) builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + case Def(ApplyBinOp(op, xSym, ySym)) if op.isInstanceOf[NumericBitwiseAnd[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseAndMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + case Def(ApplyBinOp(IsArithOp(opCode), xSym, ySym)) => val Seq(x, y) = Seq(xSym, ySym).map(recurse) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index 6c77b02fdf..13766b17b7 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -52,6 +52,10 @@ trait NumericOps extends Base { self: IRContext => override def applySeq(x: T, y: T): T = n.bitwiseOr(x, y) } + case class NumericBitwiseAnd[T: Elem](n: ExactNumeric[T]) extends EndoBinOp[T]("|") { + override def applySeq(x: T, y: T): T = n.bitwiseAnd(x, y) + } + /** Base class for descriptors of binary division operations. */ abstract class DivOp[T: Elem](opName: String, n: ExactIntegral[T]) extends EndoBinOp[T](opName) { override def shouldPropagate(lhs: T, rhs: T) = rhs != n.zero diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index b87121e4e4..0987b84835 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -257,7 +257,7 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("BigInt.bitwiseOr") { - def bitwiseInverseTest(): Assertion = test("BigInt.bitwiseInverse", env, ext, + def bitwiseOrTest(): Assertion = test("BigInt.bitwiseOr", env, ext, s"""{ | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") | x.bitwiseOr(x) == x @@ -266,9 +266,44 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (VersionContext.current.isV6SoftForkActivated) { - bitwiseInverseTest() + bitwiseOrTest() } else { - an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + an[Exception] shouldBe thrownBy(bitwiseOrTest()) + } + } + + property("BigInt.bitwiseAnd") { + def bitwiseAndTest(): Assertion = test("BigInt.bitwiseAnd", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val y = 0.toBigInt + | x.bitwiseAnd(y) == y + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[Exception] shouldBe thrownBy(bitwiseAndTest()) + } + } + + + property("Short.bitwiseAnd") { + def bitwiseAndTest(): Assertion = test("BigInt.bitwiseAnd", env, ext, + s"""{ + | val x = (32767).toShort + | val y = (-32768).toShort + | x.bitwiseAnd(y) == 0 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[Exception] shouldBe thrownBy(bitwiseAndTest()) } } From f21436d390d197cd12666cf943c2f8ef7e38a296 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 10 Jul 2024 15:37:29 +0300 Subject: [PATCH 24/40] bitwiseXor --- .../src/main/scala/sigma/ast/methods.scala | 18 +++++++++-------- .../main/scala/sigma/data/BigIntegerOps.scala | 2 ++ .../main/scala/sigma/data/ExactIntegral.scala | 4 ++++ .../main/scala/sigma/data/ExactNumeric.scala | 2 ++ .../sigma/compiler/ir/GraphBuilding.scala | 4 ++++ .../sigma/compiler/ir/TreeBuilding.scala | 6 ++++++ .../compiler/ir/primitives/NumericOps.scala | 6 +++++- .../utxo/BasicOpsSpecification.scala | 20 +++++++++++++++++-- 8 files changed, 51 insertions(+), 11 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 003b9923e0..0992788124 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -5,6 +5,7 @@ import org.ergoplatform.validation._ import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} +import sigma.ast.SNumericTypeMethods.BitwiseAndMethod import sigma.ast.SType.TypeCode import sigma.ast.syntax.{SValue, ValueOps} import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} @@ -328,15 +329,15 @@ object SNumericTypeMethods extends MethodsContainer { """.stripMargin) val BitwiseXorMethod: SMethod = SMethod( - this, "bitwiseInverse", SFunc(tNum, tNum), 11, BitwiseInverse_CostKind) + this, "bitwiseXor", SFunc(Array(tNum, tNum), tNum), 11, BitwiseInverse_CostKind) .withIRInfo(MethodCallIrBuilder) - .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => m.objType match { - case SByteMethods => ByteIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Byte]) - case SShortMethods => ShortIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Short]) - case SIntMethods => IntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Int]) - case SLongMethods => LongIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Long]) - case SBigIntMethods => BigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[BigInt]) + case SByteMethods => ByteIsExactIntegral.bitwiseXor(obj.asInstanceOf[Byte], other.head.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseXor(obj.asInstanceOf[Short], other.head.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseXor(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseXor(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseXor(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) } }) .withInfo(PropertyCall, @@ -354,7 +355,8 @@ object SNumericTypeMethods extends MethodsContainer { ToBitsMethod, BitwiseInverseMethod, BitwiseOrMethod, - BitwiseAndMethod + BitwiseAndMethod, + BitwiseXorMethod ) /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 2aefe1cbc8..9b50469883 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -98,6 +98,8 @@ object NumericOps { def bitwiseOr(x: BigInt, y: BigInt): BigInt = CBigInt(x.toBigInteger.or(y.toBigInteger)) def bitwiseAnd(x: BigInt, y: BigInt): BigInt = CBigInt(x.toBigInteger.and(y.toBigInteger)) + + def bitwiseXor(x: BigInt, y: BigInt): BigInt = CBigInt(x.toBigInteger.xor(y.toBigInteger)) } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 490890e21a..58d600d987 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -42,6 +42,7 @@ object ExactIntegral { override def bitwiseInverse(x: Byte): Byte = (~x).toByte override def bitwiseOr(x: Byte, y: Byte): Byte = (x | y).toByte override def bitwiseAnd(x: Byte, y: Byte): Byte = (x & y).toByte + override def bitwiseXor(x: Byte, y: Byte): Byte = (x ^ y).toByte } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -53,6 +54,7 @@ object ExactIntegral { override def bitwiseInverse(x: Short): Short = (~x).toShort override def bitwiseOr(x: Short, y: Short): Short = (x | y).toShort override def bitwiseAnd(x: Short, y: Short): Short = (x & y).toShort + override def bitwiseXor(x: Short, y: Short): Short = (x ^ y).toShort } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -65,6 +67,7 @@ object ExactIntegral { override def bitwiseInverse(x: Int): Int = ~x override def bitwiseOr(x: Int, y: Int): Int = x | y override def bitwiseAnd(x: Int, y: Int): Int = x & y + override def bitwiseXor(x: Int, y: Int): Int = x ^ y } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -77,5 +80,6 @@ object ExactIntegral { override def bitwiseInverse(x: Long): Long = ~x override def bitwiseOr(x: Long, y: Long): Long = x | y override def bitwiseAnd(x: Long, y: Long): Long = x & y + override def bitwiseXor(x: Long, y: Long): Long = x ^ y } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 116599ba9b..c9903028db 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -60,6 +60,8 @@ trait ExactNumeric[T] { def bitwiseAnd(x: T, y: T): T + def bitwiseXor(x: T, y: T): T + /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) 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 1c467fada8..fa1e6fb8e4 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1167,6 +1167,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val y = asRep[tNum](argsV(0)) val op = NumericBitwiseAnd(elemToExactNumeric(x.elem))(x.elem) ApplyBinOp(op, x, y) + case SNumericTypeMethods.BitwiseXorMethod.name => + val y = asRep[tNum](argsV(0)) + val op = NumericBitwiseXor(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOp(op, x, y) case _ => throwError() } case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") 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 e79fb56a2e..c0f38b6801 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -229,6 +229,12 @@ trait TreeBuilding extends Base { IR: IRContext => val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseAndMethod.methodId) builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + case Def(ApplyBinOp(op, xSym, ySym)) if op.isInstanceOf[NumericBitwiseXor[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseXorMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + case Def(ApplyBinOp(IsArithOp(opCode), xSym, ySym)) => val Seq(x, y) = Seq(xSym, ySym).map(recurse) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index 13766b17b7..11f7c6a195 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -52,10 +52,14 @@ trait NumericOps extends Base { self: IRContext => override def applySeq(x: T, y: T): T = n.bitwiseOr(x, y) } - case class NumericBitwiseAnd[T: Elem](n: ExactNumeric[T]) extends EndoBinOp[T]("|") { + case class NumericBitwiseAnd[T: Elem](n: ExactNumeric[T]) extends EndoBinOp[T]("&") { override def applySeq(x: T, y: T): T = n.bitwiseAnd(x, y) } + case class NumericBitwiseXor[T: Elem](n: ExactNumeric[T]) extends EndoBinOp[T]("^") { + override def applySeq(x: T, y: T): T = n.bitwiseXor(x, y) + } + /** Base class for descriptors of binary division operations. */ abstract class DivOp[T: Elem](opName: String, n: ExactIntegral[T]) extends EndoBinOp[T](opName) { override def shouldPropagate(lhs: T, rhs: T) = rhs != n.zero diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 0987b84835..e4279f05bc 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -289,9 +289,8 @@ class BasicOpsSpecification extends CompilerTestingCommons } } - property("Short.bitwiseAnd") { - def bitwiseAndTest(): Assertion = test("BigInt.bitwiseAnd", env, ext, + def bitwiseAndTest(): Assertion = test("Short.bitwiseAnd", env, ext, s"""{ | val x = (32767).toShort | val y = (-32768).toShort @@ -307,6 +306,23 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Short.bitwiseXor") { + def bitwiseXorTest(): Assertion = test("Short.bitwiseXor", env, ext, + s"""{ + | val x = (32767).toShort + | val y = (-32768).toShort + | x.bitwiseXor(y) == -1 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseXorTest() + } else { + an[Exception] shouldBe thrownBy(bitwiseXorTest()) + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 From ced229f60e5d1adf9a731d275fc554efd282951e Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 11 Jul 2024 15:38:49 +0300 Subject: [PATCH 25/40] shiftLeft --- .../src/main/scala/sigma/SigmaDsl.scala | 4 ++ .../src/main/scala/sigma/data/CBigInt.scala | 4 ++ .../src/main/scala/sigma/ast/methods.scala | 37 +++++++++- .../main/scala/sigma/data/BigIntegerOps.scala | 8 ++- .../main/scala/sigma/data/ExactIntegral.scala | 4 ++ .../main/scala/sigma/data/ExactNumeric.scala | 2 + .../sigma/compiler/ir/GraphBuilding.scala | 4 ++ .../sigma/compiler/ir/TreeBuilding.scala | 6 ++ .../compiler/ir/primitives/NumericOps.scala | 4 ++ .../compiler/ir/primitives/UnBinOps.scala | 35 ++++++++++ .../utxo/BasicOpsSpecification.scala | 67 +++++++++++++++++++ 11 files changed, 171 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..806d72c908 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -154,6 +154,10 @@ trait BigInt { */ def or(that: BigInt): BigInt def |(that: BigInt): BigInt = or(that) + + def xor(that: BigInt): BigInt + + def shiftLeft(bits: Int): BigInt } /** Base class for points on elliptic curves. */ diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index bbf1a85e46..a22bebbf5b 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -49,4 +49,8 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr override def and(that: BigInt): BigInt = CBigInt(wrappedValue.and(that.asInstanceOf[CBigInt].wrappedValue)) override def or(that: BigInt): BigInt = CBigInt(wrappedValue.or(that.asInstanceOf[CBigInt].wrappedValue)) + + override def xor(that: BigInt): BigInt = CBigInt(wrappedValue.xor(that.asInstanceOf[CBigInt].wrappedValue)) + + def shiftLeft(bits: Int): BigInt = CBigInt(wrappedValue.shiftLeft(bits).to256BitValueExact) } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 0992788124..5d21ff1d23 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -345,6 +345,40 @@ object SNumericTypeMethods extends MethodsContainer { | Each boolean corresponds to one bit. """.stripMargin) + val ShiftLeftMethod: SMethod = SMethod( + this, "shiftLeft", SFunc(Array(tNum, SInt), tNum), 12, BitwiseInverse_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.shiftLeft(obj.asInstanceOf[Byte], other.head.asInstanceOf[Int]) + case SShortMethods => ShortIsExactIntegral.shiftLeft(obj.asInstanceOf[Short], other.head.asInstanceOf[Int]) + case SIntMethods => IntIsExactIntegral.shiftLeft(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.shiftLeft(obj.asInstanceOf[Long], other.head.asInstanceOf[Int]) + case SBigIntMethods => BigIntIsExactIntegral.shiftLeft(obj.asInstanceOf[BigInt], other.head.asInstanceOf[Int]) + } + }) + .withInfo(PropertyCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.stripMargin) + + val ShiftRightMethod: SMethod = SMethod( + this, "shiftRight", SFunc(Array(tNum, tNum), tNum), 13, BitwiseInverse_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.bitwiseXor(obj.asInstanceOf[Byte], other.head.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseXor(obj.asInstanceOf[Short], other.head.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseXor(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseXor(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseXor(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) + } + }) + .withInfo(PropertyCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.stripMargin) + protected override def getMethods(): Seq[SMethod] = Array( ToByteMethod, // see Downcast ToShortMethod, // see Downcast @@ -356,7 +390,8 @@ object SNumericTypeMethods extends MethodsContainer { BitwiseInverseMethod, BitwiseOrMethod, BitwiseAndMethod, - BitwiseXorMethod + BitwiseXorMethod, + ShiftLeftMethod ) /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 9b50469883..0db7c8fc0b 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -95,11 +95,13 @@ object NumericOps { override def bitwiseInverse(x: BigInt): BigInt = CBigInt(x.toBigInteger.not()) - def bitwiseOr(x: BigInt, y: BigInt): BigInt = CBigInt(x.toBigInteger.or(y.toBigInteger)) + override def bitwiseOr(x: BigInt, y: BigInt): BigInt = x.or(y) - def bitwiseAnd(x: BigInt, y: BigInt): BigInt = CBigInt(x.toBigInteger.and(y.toBigInteger)) + override def bitwiseAnd(x: BigInt, y: BigInt): BigInt = x.and(y) - def bitwiseXor(x: BigInt, y: BigInt): BigInt = CBigInt(x.toBigInteger.xor(y.toBigInteger)) + override def bitwiseXor(x: BigInt, y: BigInt): BigInt = x.xor(y) + + override def shiftLeft(x: BigInt, y: Int): BigInt = x.shiftLeft(y) } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 58d600d987..a2aa6575f9 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -43,6 +43,7 @@ object ExactIntegral { override def bitwiseOr(x: Byte, y: Byte): Byte = (x | y).toByte override def bitwiseAnd(x: Byte, y: Byte): Byte = (x & y).toByte override def bitwiseXor(x: Byte, y: Byte): Byte = (x ^ y).toByte + override def shiftLeft(x: Byte, y: Int): Byte = (x << y).toByte } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -55,6 +56,7 @@ object ExactIntegral { override def bitwiseOr(x: Short, y: Short): Short = (x | y).toShort override def bitwiseAnd(x: Short, y: Short): Short = (x & y).toShort override def bitwiseXor(x: Short, y: Short): Short = (x ^ y).toShort + override def shiftLeft(x: Short, y: Int): Short = (x << y).toShort } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -68,6 +70,7 @@ object ExactIntegral { override def bitwiseOr(x: Int, y: Int): Int = x | y override def bitwiseAnd(x: Int, y: Int): Int = x & y override def bitwiseXor(x: Int, y: Int): Int = x ^ y + override def shiftLeft(x: Int, y: Int): Int = x << y } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -81,5 +84,6 @@ object ExactIntegral { override def bitwiseOr(x: Long, y: Long): Long = x | y override def bitwiseAnd(x: Long, y: Long): Long = x & y override def bitwiseXor(x: Long, y: Long): Long = x ^ y + override def shiftLeft(x: Long, y: Int): Long = x << y } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index c9903028db..0c105c5825 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -62,6 +62,8 @@ trait ExactNumeric[T] { def bitwiseXor(x: T, y: T): T + def shiftLeft(x: T, y: Int): T + /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) 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 fa1e6fb8e4..6496198075 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1171,6 +1171,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val y = asRep[tNum](argsV(0)) val op = NumericBitwiseXor(elemToExactNumeric(x.elem))(x.elem) ApplyBinOp(op, x, y) + case SNumericTypeMethods.ShiftLeftMethod.name => + val y = asRep[Int](argsV(0)) + val op = NumericShiftLeft(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOpDiffArgs(op, x, y) case _ => throwError() } case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") 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 c0f38b6801..1a7621a739 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -235,6 +235,12 @@ trait TreeBuilding extends Base { IR: IRContext => val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseXorMethod.methodId) builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + case Def(ApplyBinOpDiffArgs(op, xSym, ySym)) if op.isInstanceOf[NumericShiftLeft[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.ShiftLeftMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + case Def(ApplyBinOp(IsArithOp(opCode), xSym, ySym)) => val Seq(x, y) = Seq(xSym, ySym).map(recurse) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index 11f7c6a195..d8c495220a 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -60,6 +60,10 @@ trait NumericOps extends Base { self: IRContext => override def applySeq(x: T, y: T): T = n.bitwiseXor(x, y) } + case class NumericShiftLeft[T: Elem](n: ExactNumeric[T]) extends BinDiffArgsOp[T, Int]("<<") { + override def applySeq(x: T, y: Int): T = n.shiftLeft(x, y) + } + /** Base class for descriptors of binary division operations. */ abstract class DivOp[T: Elem](opName: String, n: ExactIntegral[T]) extends EndoBinOp[T](opName) { override def shouldPropagate(lhs: T, rhs: T) = rhs != n.zero diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala index 23f8bc3800..62bfc29f68 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala @@ -45,6 +45,29 @@ trait UnBinOps extends Base { self: IRContext => def shouldPropagate(lhs: A, rhs: A) = true } + abstract class BinDiffArgsOp[A, B](val opName: String)(implicit val eResult: Elem[A]) { + override def toString = opName + + /** Called as part of graph interpretation to execute the given binary operation. + * @param x operation argument + * @param y operation argument + * @return result of applying this operation to (x, y) + */ + def applySeq(x: A, y: B): A + + /** Builds a new graph node by applying this operation to the given arguments. */ + def apply(lhs: Ref[A], rhs: Ref[B]) = ApplyBinOpDiffArgs(this, lhs, rhs) + + /** Builds a new graph node by applying this operation to the given arguments. + * This is a short-cuting (aka lazy) version of the operation, where the lazyness is + * represented by Thunk. + */ + def applyLazy(lhs: Ref[A], rhs: Ref[Thunk[B]]) = ApplyBinOpDiffArgsLazy(this, lhs, rhs) + + /** Whether the constants should be propagated through this operations by rewriting. */ + def shouldPropagate(lhs: A, rhs: B) = true + } + type EndoUnOp[A] = UnOp[A, A] type EndoBinOp[A] = BinOp[A, A] @@ -68,6 +91,18 @@ trait UnBinOps extends Base { self: IRContext => override def transform(t: Transformer): Def[R] = ApplyBinOpLazy[A,R](op, t(lhs), t(rhs)) } + /** Graph node which represents application of the given binary operation to the given arguments. */ + case class ApplyBinOpDiffArgsLazy[A, B](op: BinDiffArgsOp[A, B], lhs: Ref[A], rhs: Ref[Thunk[B]]) extends BaseDef[A]()(op.eResult) { + override def toString = s"$lhs $op { $rhs }" + override def transform(t: Transformer): Def[A] = ApplyBinOpDiffArgsLazy[A, B](op, t(lhs), t(rhs)) + } + + /** Graph node which represents application of the given binary operation to the given arguments. */ + case class ApplyBinOpDiffArgs[A, B](op: BinDiffArgsOp[A, B], lhs: Ref[A], rhs: Ref[B]) extends BaseDef[A]()(op.eResult) { + override def toString = s"$op($lhs, $rhs)" + override def transform(t: Transformer): Def[A] = ApplyBinOpDiffArgs[A, B](op, t(lhs), t(rhs)) + } + /** Overridable constructor of an unary operation node. */ def applyUnOp[A, R](op: UnOp[A, R], arg: Ref[A]): Ref[R] = ApplyUnOp(op, arg) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index e4279f05bc..7bd3812443 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -323,6 +323,73 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Byte.shiftLeft") { + def shiftLeftTest(): Assertion = test("Byte.shiftLeft", env, ext, + s"""{ + | val x = 4.toByte + | val y = 2 + | x.shiftLeft(y) == 16.toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftLeftTest() + } else { + an[Exception] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("Byte.shiftLeft - over limit") { + def shiftLeftTest(): Assertion = test("Byte.shiftLeft2", env, ext, + s"""{ + | val x = 4.toByte + | val y = 2222 + | x.shiftLeft(y) == 0 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftLeftTest() + } else { + an[Exception] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("BigInt.shiftLeft") { + def shiftLeftTest(): Assertion = test("BigInt.shiftLeft", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | val y = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | x.shiftLeft(2) == y + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftLeftTest() + } else { + an[Exception] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("BigInt.shiftLeft over limits") { + def shiftLeftTest(): Assertion = test("BigInt.shiftLeft", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | x.shiftLeft(1) > x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[ArithmeticException] shouldBe thrownBy(shiftLeftTest()) + } else { + an[Exception] shouldBe thrownBy(shiftLeftTest()) + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 From 9519ef62c2d7b41163096374a2057c26602905f7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 16 Jul 2024 21:17:28 +0300 Subject: [PATCH 26/40] shiftRight --- .../src/main/scala/sigma/SigmaDsl.scala | 2 + .../src/main/scala/sigma/data/CBigInt.scala | 2 + .../src/main/scala/sigma/ast/methods.scala | 15 ++- .../main/scala/sigma/data/BigIntegerOps.scala | 2 + .../main/scala/sigma/data/ExactIntegral.scala | 9 +- .../main/scala/sigma/data/ExactNumeric.scala | 6 +- .../sigma/compiler/ir/GraphBuilding.scala | 4 + .../sigma/compiler/ir/TreeBuilding.scala | 6 + .../compiler/ir/primitives/NumericOps.scala | 4 + .../utxo/BasicOpsSpecification.scala | 121 ++++++++++++++++++ 10 files changed, 160 insertions(+), 11 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 806d72c908..6ba84374df 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -158,6 +158,8 @@ trait BigInt { def xor(that: BigInt): BigInt def shiftLeft(bits: Int): BigInt + + def shiftRight(bits: Int): BigInt } /** Base class for points on elliptic curves. */ diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index a22bebbf5b..dbfec02844 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -53,4 +53,6 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr override def xor(that: BigInt): BigInt = CBigInt(wrappedValue.xor(that.asInstanceOf[CBigInt].wrappedValue)) def shiftLeft(bits: Int): BigInt = CBigInt(wrappedValue.shiftLeft(bits).to256BitValueExact) + + def shiftRight(bits: Int): BigInt = CBigInt(wrappedValue.shiftRight(bits).to256BitValueExact) } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 5d21ff1d23..e7bf6a9bda 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -363,15 +363,15 @@ object SNumericTypeMethods extends MethodsContainer { """.stripMargin) val ShiftRightMethod: SMethod = SMethod( - this, "shiftRight", SFunc(Array(tNum, tNum), tNum), 13, BitwiseInverse_CostKind) + this, "shiftRight", SFunc(Array(tNum, SInt), tNum), 13, BitwiseInverse_CostKind) .withIRInfo(MethodCallIrBuilder) .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => m.objType match { - case SByteMethods => ByteIsExactIntegral.bitwiseXor(obj.asInstanceOf[Byte], other.head.asInstanceOf[Byte]) - case SShortMethods => ShortIsExactIntegral.bitwiseXor(obj.asInstanceOf[Short], other.head.asInstanceOf[Short]) - case SIntMethods => IntIsExactIntegral.bitwiseXor(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) - case SLongMethods => LongIsExactIntegral.bitwiseXor(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) - case SBigIntMethods => BigIntIsExactIntegral.bitwiseXor(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) + case SByteMethods => ByteIsExactIntegral.shiftRight(obj.asInstanceOf[Byte], other.head.asInstanceOf[Int]) + case SShortMethods => ShortIsExactIntegral.shiftRight(obj.asInstanceOf[Short], other.head.asInstanceOf[Int]) + case SIntMethods => IntIsExactIntegral.shiftRight(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.shiftRight(obj.asInstanceOf[Long], other.head.asInstanceOf[Int]) + case SBigIntMethods => BigIntIsExactIntegral.shiftRight(obj.asInstanceOf[BigInt], other.head.asInstanceOf[Int]) } }) .withInfo(PropertyCall, @@ -391,7 +391,8 @@ object SNumericTypeMethods extends MethodsContainer { BitwiseOrMethod, BitwiseAndMethod, BitwiseXorMethod, - ShiftLeftMethod + ShiftLeftMethod, + ShiftRightMethod ) /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 0db7c8fc0b..2e1d2f62ce 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -102,6 +102,8 @@ object NumericOps { override def bitwiseXor(x: BigInt, y: BigInt): BigInt = x.xor(y) override def shiftLeft(x: BigInt, y: Int): BigInt = x.shiftLeft(y) + + override def shiftRight(x: BigInt, y: Int): BigInt = x.shiftRight(y) } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index a2aa6575f9..418bc1837b 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -43,12 +43,14 @@ object ExactIntegral { override def bitwiseOr(x: Byte, y: Byte): Byte = (x | y).toByte override def bitwiseAnd(x: Byte, y: Byte): Byte = (x & y).toByte override def bitwiseXor(x: Byte, y: Byte): Byte = (x ^ y).toByte - override def shiftLeft(x: Byte, y: Int): Byte = (x << y).toByte + override def shiftLeft(x: Byte, bits: Int): Byte = (x << bits).toByte + override def shiftRight(x: Byte, bits: Int): Byte = (x >> bits).toByte } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { val n = scala.math.Numeric.ShortIsIntegral - override def plus(x: Short, y: Short): Short = x.addExact(y) + override def plus(x: Short, y: Short): + Short = x.addExact(y) override def minus(x: Short, y: Short): Short = x.subtractExact(y) override def times(x: Short, y: Short): Short = x.multiplyExact(y) override def toBigEndianBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) @@ -57,6 +59,7 @@ object ExactIntegral { override def bitwiseAnd(x: Short, y: Short): Short = (x & y).toShort override def bitwiseXor(x: Short, y: Short): Short = (x ^ y).toShort override def shiftLeft(x: Short, y: Int): Short = (x << y).toShort + override def shiftRight(x: Short, bits: Int): Short = (x >> bits).toShort } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -71,6 +74,7 @@ object ExactIntegral { override def bitwiseAnd(x: Int, y: Int): Int = x & y override def bitwiseXor(x: Int, y: Int): Int = x ^ y override def shiftLeft(x: Int, y: Int): Int = x << y + override def shiftRight(x: Int, bits: Int): Int = x >> bits } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -85,5 +89,6 @@ object ExactIntegral { override def bitwiseAnd(x: Long, y: Long): Long = x & y override def bitwiseXor(x: Long, y: Long): Long = x ^ y override def shiftLeft(x: Long, y: Int): Long = x << y + override def shiftRight(x: Long, bits: Int): Long = x >> bits } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 0c105c5825..214569e6e5 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -1,6 +1,6 @@ package sigma.data -import sigma.{Coll, Colls} +import sigma.{BigInt, Coll, Colls} import sigma.data.ExactIntegral._ import scala.collection.mutable @@ -62,7 +62,9 @@ trait ExactNumeric[T] { def bitwiseXor(x: T, y: T): T - def shiftLeft(x: T, y: Int): T + def shiftLeft(x: T, bits: Int): T + + def shiftRight(x: T, bits: Int): T /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) 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 6496198075..af681fdd2e 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1175,6 +1175,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val y = asRep[Int](argsV(0)) val op = NumericShiftLeft(elemToExactNumeric(x.elem))(x.elem) ApplyBinOpDiffArgs(op, x, y) + case SNumericTypeMethods.ShiftRightMethod.name => + val y = asRep[Int](argsV(0)) + val op = NumericShiftRight(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOpDiffArgs(op, x, y) case _ => throwError() } case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") 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 1a7621a739..c218760e24 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -241,6 +241,12 @@ trait TreeBuilding extends Base { IR: IRContext => val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.ShiftLeftMethod.methodId) builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + case Def(ApplyBinOpDiffArgs(op, xSym, ySym)) if op.isInstanceOf[NumericShiftRight[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.ShiftRightMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + case Def(ApplyBinOp(IsArithOp(opCode), xSym, ySym)) => val Seq(x, y) = Seq(xSym, ySym).map(recurse) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index d8c495220a..9a6b372033 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -64,6 +64,10 @@ trait NumericOps extends Base { self: IRContext => override def applySeq(x: T, y: Int): T = n.shiftLeft(x, y) } + case class NumericShiftRight[T: Elem](n: ExactNumeric[T]) extends BinDiffArgsOp[T, Int](">>") { + override def applySeq(x: T, y: Int): T = n.shiftRight(x, y) + } + /** Base class for descriptors of binary division operations. */ abstract class DivOp[T: Elem](opName: String, n: ExactIntegral[T]) extends EndoBinOp[T](opName) { override def shouldPropagate(lhs: T, rhs: T) = rhs != n.zero diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 7bd3812443..fcd597f7cb 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -390,6 +390,127 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Byte.shiftRight") { + def shiftRightTest(): Assertion = test("Byte.shiftRight", env, ext, + s"""{ + | val x = 8.toByte + | val y = 2 + | x.shiftRight(y) == 2.toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[Exception] shouldBe thrownBy(shiftRightTest()) + } + } + + property("Byte.shiftRight - neg") { + def shiftRightTest(): Assertion = test("Byte.shiftRight", env, ext, + s"""{ + | val x = (-8).toByte + | val y = 2 + | x.shiftRight(y) == (-2).toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[Exception] shouldBe thrownBy(shiftRightTest()) + } + } + + property("Byte.shiftRight - neg - neg shift") { + def shiftRightTest(): Assertion = test("Byte.shiftRight", env, ext, + s"""{ + | val x = (-8).toByte + | val y = -2 + | x.shiftRight(y) == (-1).toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[Exception] shouldBe thrownBy(shiftRightTest()) + } + } + + property("Long.shiftRight - neg") { + def shiftRightTest(): Assertion = test("Long.shiftRight", env, ext, + s"""{ + | val x = -32L + | val y = 2 + | x.shiftRight(y) == -8L + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[Exception] shouldBe thrownBy(shiftRightTest()) + } + } + + property("Long.shiftRight - neg - neg shift") { + def shiftRightTest(): Assertion = test("Long.shiftRight", env, ext, + s"""{ + | val x = -32L + | val y = -2 + | x.shiftRight(y) == -1L + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[Exception] shouldBe thrownBy(shiftRightTest()) + } + } + + property("BigInt.shiftRight") { + def shiftRightTest(): Assertion = test("BigInt.shiftRight", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val y = 2 + | val z = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | x.shiftRight(y) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[Exception] shouldBe thrownBy(shiftRightTest()) + } + } + + property("BigInt.shiftRight - neg shift") { + def shiftRightTest(): Assertion = test("BigInt.shiftRight", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val y = -2 + | val z = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | z.shiftRight(y) == x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[Exception] shouldBe thrownBy(shiftRightTest()) + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 From 016262b6b2759529ae3f5f9567548b1191f49f7c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 27 Aug 2024 21:26:16 +0300 Subject: [PATCH 27/40] removing unused SNumericTypeMethods.getMethods --- data/shared/src/main/scala/sigma/ast/methods.scala | 8 +++----- .../src/test/scala/sigmastate/ErgoTreeSpecification.scala | 3 --- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 99459840de..f2481fd879 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -186,6 +186,7 @@ trait SNumericTypeMethods extends MonoTypeMethods { } object SNumericTypeMethods extends MethodsContainer { + /** Type for which this container defines methods. */ override def ownerType: STypeCompanion = SNumericType @@ -399,11 +400,8 @@ object SNumericTypeMethods extends MethodsContainer { ) protected override def getMethods(): Seq[SMethod] = { - if (VersionContext.current.isV6SoftForkActivated) { - v6Methods - } else { - v5Methods - } + // this .getMethods shouldn't ever be called + ??? } /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 7ca46c8d16..406b84e26a 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -580,9 +580,6 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C val mc = MethodsContainer(tyDesc.typeId) - println("mc: " + mc.methods.map(_.name)) - println("methods: " + methods.map(_.method.name)) - mc.methods.length shouldBe methods.length for (expectedMethod <- methods) { From baef87f403cf65ffdeaf264420a35c41e626eb48 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 2 Sep 2024 20:20:50 +0300 Subject: [PATCH 28/40] more efficient toBits impl --- .../main/scala/sigma/data/ExactNumeric.scala | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 214569e6e5..90d28e5370 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -1,7 +1,10 @@ package sigma.data +import debox.cfor +import sigma.Evaluation.stypeToRType import sigma.{BigInt, Coll, Colls} import sigma.data.ExactIntegral._ +import sigma.data.RType.SomeType import scala.collection.mutable @@ -40,18 +43,22 @@ trait ExactNumeric[T] { def toBigEndianBytes(x: T): Coll[Byte] def toBits(x: T): Coll[Boolean] = { - def byte2Bools(b: Byte): Array[Boolean] = (0 to 7).toArray.reverse.map(isBitSet(b)) def isBitSet(byte: Byte)(bit: Int): Boolean = ((byte >> bit) & 1) == 1 + def byte2Bools(b: Byte): Array[Boolean] = (0 to 7).toArray.reverse.map(isBitSet(b)) + val bytes = toBigEndianBytes(x) - val builder = mutable.ArrayBuilder.make[Boolean] val l = bytes.length - (0 until l).foreach{i=> + val res = new Array[Boolean](l * 8) + var offset = 0 + cfor(0)(_ < l, _ + 1) { i => val b = bytes(i) - builder.addAll(byte2Bools(b)) + val bits = byte2Bools(b) + Array.copy(bits, 0, res, offset, 8) + offset += 8 } - Colls.fromArray(builder.result()) + Colls.fromArray(res) } def bitwiseInverse(x: T): T From 2e8d8f41a86acbca8b4e8d25ad2d7c86511cc7c3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 3 Sep 2024 14:06:50 +0300 Subject: [PATCH 29/40] optimizing imports, polishing --- core/shared/src/main/scala/sigma/SigmaDsl.scala | 3 +++ .../src/main/scala/sigma/serialization/TypeSerializer.scala | 2 -- data/shared/src/main/scala/sigma/ast/methods.scala | 1 - data/shared/src/main/scala/sigma/data/ExactNumeric.scala | 6 +----- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 5e647cad0e..9cf632cc59 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -155,6 +155,9 @@ trait BigInt { def or(that: BigInt): BigInt def |(that: BigInt): BigInt = or(that) + /** + * @return a big integer whose value is `this xor that` + */ def xor(that: BigInt): BigInt def shiftLeft(bits: Int): BigInt diff --git a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala index c21a3458c0..1936bbcd9a 100644 --- a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala @@ -4,7 +4,6 @@ import debox.cfor import sigma.VersionContext import sigma.ast.SCollectionType.{CollectionTypeCode, NestedCollectionTypeCode} import sigma.ast._ -import sigma.serialization.{CoreByteReader, CoreByteWriter, InvalidTypePrefix} import sigma.util.safeNewArray import sigma.validation.ValidationRules.{CheckPrimitiveTypeCode, CheckTypeCode} @@ -215,7 +214,6 @@ class TypeSerializer { STypeParam(ident.asInstanceOf[STypeVar]) } SFunc(tDom, tRange, tpeParams) - // todo: serialize tParams case _ => // todo: 6.0: replace 1008 check with identical behavior but other opcode, to activate // ReplacedRule(1008 -> new opcode) during 6.0 activation diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index b40cdca79c..67bb1c74fb 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -5,7 +5,6 @@ import org.ergoplatform.validation._ import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} -import sigma.ast.SNumericTypeMethods.BitwiseAndMethod import sigma.ast.SType.TypeCode import sigma.ast.syntax.{SValue, ValueOps} import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 90d28e5370..9612cccc34 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -1,12 +1,8 @@ package sigma.data import debox.cfor -import sigma.Evaluation.stypeToRType -import sigma.{BigInt, Coll, Colls} +import sigma.{Coll, Colls} import sigma.data.ExactIntegral._ -import sigma.data.RType.SomeType - -import scala.collection.mutable /** Numeric operations with overflow checks. * Raise exception when overflow is detected. From ed5b8f5383db1fba53a5d90887fb6efa21a13810 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 4 Sep 2024 14:48:39 +0300 Subject: [PATCH 30/40] fixing SigmaTyperTest --- .../test/scala/sigmastate/lang/SigmaTyperTest.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index e8bfeb777f..3696a7e14e 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -523,7 +523,7 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "1.toByte.toBytes", expected = MethodCall.typed[Value[SCollection[SByte.type]]]( Select(IntConstant(1), "toByte", Some(SByte)), - SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SByte)), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SByte)), Vector(), Map() )) shouldBe SByteArray @@ -531,7 +531,7 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "1.toShort.toBytes", expected = MethodCall.typed[Value[SCollection[SByte.type]]]( Select(IntConstant(1), "toShort", Some(SShort)), - SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SShort)), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SShort)), Vector(), Map() )) shouldBe SByteArray @@ -539,7 +539,7 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "1.toBytes", expected = MethodCall.typed[Value[SCollection[SByte.type]]]( IntConstant(1), - SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SInt)), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SInt)), Vector(), Map() )) shouldBe SByteArray @@ -547,7 +547,7 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "1.toLong.toBytes", expected = MethodCall.typed[Value[SCollection[SByte.type]]]( Select(IntConstant(1), "toLong", Some(SLong)), - SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SLong)), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SLong)), Vector(), Map() )) shouldBe SByteArray @@ -555,7 +555,7 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "1.toBigInt.toBytes", expected = MethodCall.typed[Value[SCollection[SByte.type]]]( Select(IntConstant(1), "toBigInt", Some(SBigInt)), - SNumericTypeMethods.getMethodByName("toBytes").withConcreteTypes(Map(STypeVar("TNum") -> SBigInt)), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SBigInt)), Vector(), Map() )) shouldBe SByteArray From b2de9cf057648691d395b036e14f2ade1049dff1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 6 Sep 2024 19:57:59 +0300 Subject: [PATCH 31/40] scaladoc --- .../src/main/scala/sigma/SigmaDsl.scala | 16 +++++++++---- .../src/main/scala/sigma/data/CBigInt.scala | 4 ++-- .../main/scala/sigma/data/ExactNumeric.scala | 23 +++++++++++++++++++ .../compiler/ir/primitives/UnBinOps.scala | 6 +++-- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 9cf632cc59..6f9a3e2ffe 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -5,9 +5,7 @@ import java.math.BigInteger import sigma.data._ /** - * All `modQ` operations assume that Q is a global constant (an order of the only one cryptographically strong group - * which is used for all cryptographic operations). - * So it is globally and implicitly used in all methods. + * Functions defined for 256-bit signed integers * */ trait BigInt { /** Convert this BigInt value to Byte. @@ -160,9 +158,17 @@ trait BigInt { */ def xor(that: BigInt): BigInt - def shiftLeft(bits: Int): BigInt + /** + * @return a 256-bit signed integer whose value is (this << n). The shift distance, n, may be negative, + * in which case this method performs a right shift. (Computes floor(this * 2n).) + */ + def shiftLeft(n: Int): BigInt - def shiftRight(bits: Int): BigInt + /** + * @return a 256-bit signed integer whose value is (this >> n). Sign extension is performed. The shift distance, n, + * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).) + */ + def shiftRight(n: Int): BigInt } /** Base class for points on elliptic curves. */ diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index dbfec02844..ea69174877 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -52,7 +52,7 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr override def xor(that: BigInt): BigInt = CBigInt(wrappedValue.xor(that.asInstanceOf[CBigInt].wrappedValue)) - def shiftLeft(bits: Int): BigInt = CBigInt(wrappedValue.shiftLeft(bits).to256BitValueExact) + override def shiftLeft(n: Int): BigInt = CBigInt(wrappedValue.shiftLeft(n).to256BitValueExact) - def shiftRight(bits: Int): BigInt = CBigInt(wrappedValue.shiftRight(bits).to256BitValueExact) + override def shiftRight(n: Int): BigInt = CBigInt(wrappedValue.shiftRight(n).to256BitValueExact) } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 9612cccc34..56b4a9369e 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -38,6 +38,9 @@ trait ExactNumeric[T] { */ def toBigEndianBytes(x: T): Coll[Byte] + /** + * Returns a big-endian binary representation of this value as boolean array. + */ def toBits(x: T): Coll[Boolean] = { def isBitSet(byte: Byte)(bit: Int): Boolean = ((byte >> bit) & 1) == 1 @@ -57,16 +60,36 @@ trait ExactNumeric[T] { Colls.fromArray(res) } + /** + * @return a numeric value which is inverse of `x` (every bit, including sign, is flipped) + */ def bitwiseInverse(x: T): T + /** + * @return a numeric value which is `this | that` + */ def bitwiseOr(x: T, y: T): T + /** + * @return a numeric value which is `this && that` + */ def bitwiseAnd(x: T, y: T): T + /** + * @return a numeric value which is `this xor that` + */ def bitwiseXor(x: T, y: T): T + /** + * @return a value which is (this << n). The shift distance, n, may be negative, + * in which case this method performs a right shift. (Computes floor(this * 2n).) + */ def shiftLeft(x: T, bits: Int): T + /** + * @return a value which is (this >> n). Sign extension is performed. The shift distance, n, + * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).) + */ def shiftRight(x: T, bits: Int): T /** A value of type T which corresponds to integer 0. */ diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala index 62bfc29f68..b55596052e 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala @@ -45,6 +45,7 @@ trait UnBinOps extends Base { self: IRContext => def shouldPropagate(lhs: A, rhs: A) = true } + /** Base class for descriptors of binary operations where arguments are of different types. */ abstract class BinDiffArgsOp[A, B](val opName: String)(implicit val eResult: Elem[A]) { override def toString = opName @@ -91,13 +92,14 @@ trait UnBinOps extends Base { self: IRContext => override def transform(t: Transformer): Def[R] = ApplyBinOpLazy[A,R](op, t(lhs), t(rhs)) } - /** Graph node which represents application of the given binary operation to the given arguments. */ + /** Graph node which represents application of the given binary operation to the given arguments of different types + * where the second argument is lazy. */ case class ApplyBinOpDiffArgsLazy[A, B](op: BinDiffArgsOp[A, B], lhs: Ref[A], rhs: Ref[Thunk[B]]) extends BaseDef[A]()(op.eResult) { override def toString = s"$lhs $op { $rhs }" override def transform(t: Transformer): Def[A] = ApplyBinOpDiffArgsLazy[A, B](op, t(lhs), t(rhs)) } - /** Graph node which represents application of the given binary operation to the given arguments. */ + /** Graph node which represents application of the given binary operation to the given arguments of different types. */ case class ApplyBinOpDiffArgs[A, B](op: BinDiffArgsOp[A, B], lhs: Ref[A], rhs: Ref[B]) extends BaseDef[A]()(op.eResult) { override def toString = s"$op($lhs, $rhs)" override def transform(t: Transformer): Def[A] = ApplyBinOpDiffArgs[A, B](op, t(lhs), t(rhs)) From eccc9cae1b13def59988e60c201410a812982bd6 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 10 Sep 2024 12:55:38 +0300 Subject: [PATCH 32/40] 6.0 methods for Byte in LSV6 --- .../src/main/scala/sigma/ast/methods.scala | 14 +- .../compiler/ir/primitives/NumericOps.scala | 2 +- .../scala/sigma/LanguageSpecificationV6.scala | 197 +++++++++++++++--- .../utxo/BasicOpsSpecification.scala | 17 ++ 4 files changed, 196 insertions(+), 34 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 67bb1c74fb..1e8fb911bb 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -161,13 +161,13 @@ trait SNumericTypeMethods extends MonoTypeMethods { private val subst = Map(tNum -> this.ownerType) - private val v5Methods = { + val v5Methods = { SNumericTypeMethods.v5Methods.map { m => m.copy(stype = applySubst(m.stype, subst).asFunc) } } - private val v6Methods = { + val v6Methods = { SNumericTypeMethods.v6Methods.map { m => m.copy( objType = this, // associate the method with the concrete numeric type @@ -306,7 +306,7 @@ object SNumericTypeMethods extends MethodsContainer { case SBigIntMethods => BigIntIsExactIntegral.bitwiseOr(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) } }) - .withInfo(PropertyCall, + .withInfo(MethodCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. """.stripMargin) @@ -323,7 +323,7 @@ object SNumericTypeMethods extends MethodsContainer { case SBigIntMethods => BigIntIsExactIntegral.bitwiseAnd(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) } }) - .withInfo(PropertyCall, + .withInfo(MethodCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. """.stripMargin) @@ -340,7 +340,7 @@ object SNumericTypeMethods extends MethodsContainer { case SBigIntMethods => BigIntIsExactIntegral.bitwiseXor(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) } }) - .withInfo(PropertyCall, + .withInfo(MethodCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. """.stripMargin) @@ -357,7 +357,7 @@ object SNumericTypeMethods extends MethodsContainer { case SBigIntMethods => BigIntIsExactIntegral.shiftLeft(obj.asInstanceOf[BigInt], other.head.asInstanceOf[Int]) } }) - .withInfo(PropertyCall, + .withInfo(MethodCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. """.stripMargin) @@ -374,7 +374,7 @@ object SNumericTypeMethods extends MethodsContainer { case SBigIntMethods => BigIntIsExactIntegral.shiftRight(obj.asInstanceOf[BigInt], other.head.asInstanceOf[Int]) } }) - .withInfo(PropertyCall, + .withInfo(MethodCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. """.stripMargin) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index 9a6b372033..5b858d8586 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -105,7 +105,7 @@ trait NumericOps extends Base { self: IRContext => } } - /** Descriptor of unary `ToBits` conversion operation. */ + /** Descriptor of unary `BitwiseInverse` conversion operation. */ case class NumericBitwiseInverse[T: Elem](n: ExactNumeric[T]) extends UnOp[T, T]("~") { override def applySeq(x: T): T = n.bitwiseInverse(x) } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index dd577b5b38..57951586fb 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -50,38 +50,183 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => testCases(cases, toByte) } - property("Byte methods equivalence (new features)") { - // TODO v6.0: implement as part of https://github.com/ScorexFoundation/sigmastate-interpreter/issues/474 - if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { - // NOTE, for such versions the new features are not supported - // which is checked below + property("Byte methods - 6.0 features") { - lazy val toAbs = newFeature((x: Byte) => x.toAbs, "{ (x: Byte) => x.toAbs }", - sinceVersion = V6SoftForkVersion) + lazy val bitOr = newFeature( + { (x: (Byte, Byte)) => (x._1 | x._2).toByteExact }, + "{ (x: (Byte, Byte)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SByte, SByte))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) - lazy val compareTo = newFeature( - (x: (Byte, Byte)) => x._1.compareTo(x._2), - "{ (x: (Byte, Byte)) => x._1.compareTo(x._2) }", - sinceVersion = V6SoftForkVersion) + verifyCases( + Seq( + (1.toByte, 2.toByte) -> new Expected(ExpectedResult(Success(3.toByte), None)) + ), + bitOr + ) - lazy val bitOr = newFeature( - { (x: (Byte, Byte)) => (x._1 | x._2).toByteExact }, - "{ (x: (Byte, Byte)) => (x._1 | x._2) }", - sinceVersion = V6SoftForkVersion) + lazy val bitNot = newFeature( + { (x: Byte) => (~x).toByteExact }, + "{ (x: Byte) => x.bitwiseInverse }", + FuncValue( + Array((1, SByte)), + MethodCall.typed[Value[SByte.type]]( + ValUse(1, SByte), + SByteMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) - lazy val bitAnd = newFeature( - { (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }, - "{ (x: (Byte, Byte)) => (x._1 & x._2) }", - sinceVersion = V6SoftForkVersion) + verifyCases( + Seq( + 1.toByte -> new Expected(ExpectedResult(Success((-2).toByte), None)) + ), + bitNot + ) - forAll { x: Byte => - Seq(toAbs).foreach(f => f.checkEquality(x)) - } + lazy val bitAnd = newFeature( + { (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }, + "{ (x: (Byte, Byte)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SByte, SByte))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) - forAll { x: (Byte, Byte) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } + verifyCases( + Seq( + (3.toByte, 5.toByte) -> new Expected(ExpectedResult(Success(1.toByte), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (Byte, Byte)) => (x._1 ^ x._2).toByteExact }, + "{ (x: (Byte, Byte)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SByte, SByte))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toByte, 5.toByte) -> new Expected(ExpectedResult(Success(6.toByte), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature( + { x: Byte => Coll(x) }, + "{ (x: Byte) => x.toBytes }", + FuncValue( + Array((1, SByte)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SByte), + SByteMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 127.toByte -> new Expected(ExpectedResult(Success(Coll(127.toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[Byte, Coll[Boolean]]( + { x: Byte => Colls.fromArray(byte2Bools(x).toArray) }, + "{ (x: Byte) => x.toBits }", + FuncValue( + Array((1, SByte)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SByte), + SByteMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 83.toByte -> new Expected(ExpectedResult(Success(Coll(false, true, false, true, false, false, true, true)), None)), + -55.toByte -> new Expected(ExpectedResult(Success(Coll(true, true, false, false, true, false, false, true)), None)), + -1.toByte -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true)), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (Byte, Int)) => (x._1 << x._2).toByte }, + "{ (x: (Byte, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SByte, SInt))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SInt)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByte, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toByte, 3) -> new Expected(ExpectedResult(Success(24.toByte), None)) + ), + shiftLeft + ) + + lazy val shiftRight = newFeature( + { (x: (Byte, Int)) => (x._1 >> x._2).toByte }, + "{ (x: (Byte, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SByte, SInt))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SInt)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByte, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (24.toByte, 3) -> new Expected(ExpectedResult(Success(3.toByte), None)) + ), + shiftRight + ) } // TODO v6.0: enable as part of https://github.com/ScorexFoundation/sigmastate-interpreter/issues/474 diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index f531c2b07d..a37a3d1079 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -360,6 +360,23 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Byte.shiftLeft - over limit 2") { + def shiftLeftTest(): Assertion = test("Byte.shiftLeft2", env, ext, + s"""{ + | val x = (-128).toByte + | val y = 1 + | x.shiftLeft(y) == 0 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftLeftTest() + } else { + an[Exception] shouldBe thrownBy(shiftLeftTest()) + } + } + property("BigInt.shiftLeft") { def shiftLeftTest(): Assertion = test("BigInt.shiftLeft", env, ext, s"""{ From f566b7bd4bb93e41ad52357db6d1251979b865d2 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 10 Sep 2024 14:09:02 +0300 Subject: [PATCH 33/40] 6.0 methods for Short in LSV6 --- .../scala/sigma/LanguageSpecificationV6.scala | 205 ++++++++++++++++-- 1 file changed, 182 insertions(+), 23 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 57951586fb..725b999f6e 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -6,11 +6,12 @@ import sigma.VersionContext.V6SoftForkVersion import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.Token import scorex.util.ModifierId +import scorex.utils.Shorts import sigma.ast.ErgoTree.ZeroHeader import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast._ -import sigma.data.{CBigInt, CHeader, CBox, ExactNumeric} +import sigma.data.{CBigInt, CBox, CHeader, ExactNumeric} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} @@ -229,36 +230,194 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } - // TODO v6.0: enable as part of https://github.com/ScorexFoundation/sigmastate-interpreter/issues/474 - property("Short methods equivalence (new features)") { - if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { - // NOTE, for such versions the new features are not supported - // which is checked below + property("Short - 6.0 methods") { - lazy val toAbs = newFeature((x: Short) => x.toAbs, "{ (x: Short) => x.toAbs }", - sinceVersion = V6SoftForkVersion) + lazy val bitOr = newFeature( + { (x: (Short, Short)) => (x._1 | x._2).toShortExact }, + "{ (x: (Short, Short)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SShort, SShort))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1,SPair(SShort, SShort)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) - lazy val compareTo = newFeature((x: (Short, Short)) => x._1.compareTo(x._2), - "{ (x: (Short, Short)) => x._1.compareTo(x._2) }", - sinceVersion = V6SoftForkVersion) + verifyCases( + Seq( + (1.toShort, 2.toShort) -> new Expected(ExpectedResult(Success(3.toShort), None)), + (1001.toShort, 2002.toShort) -> new Expected(ExpectedResult(Success(2043.toShort), None)) + ), + bitOr + ) - lazy val bitOr = newFeature( - { (x: (Short, Short)) => (x._1 | x._2).toShortExact }, - "{ (x: (Short, Short)) => x._1 | x._2 }", + lazy val bitNot = newFeature( + { (x: Short) => (~x).toShortExact }, + "{ (x: Short) => x.bitwiseInverse }", + FuncValue( + Array((1, SShort)), + MethodCall.typed[Value[SShort.type]]( + ValUse(1, SShort), + SShortMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), sinceVersion = V6SoftForkVersion) - lazy val bitAnd = newFeature( + verifyCases( + Seq( + 1.toShort -> new Expected(ExpectedResult(Success((-2).toShort), None)), + 10001.toShort -> new Expected(ExpectedResult(Success((-10002).toShort), None)) + ), + bitNot + ) + + lazy val bitAnd = newFeature( { (x: (Short, Short)) => (x._1 & x._2).toShortExact }, - "{ (x: (Short, Short)) => x._1 & x._2 }", + "{ (x: (Short, Short)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SShort, SShort))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)),2.toByte)), + Map() + ) + ), sinceVersion = V6SoftForkVersion) - forAll { x: Short => - Seq(toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (Short, Short) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } + verifyCases( + Seq( + (3.toShort, 5.toShort) -> new Expected(ExpectedResult(Success(1.toShort), None)), + (10001.toShort, 2202.toShort) -> new Expected(ExpectedResult(Success(16.toShort), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (Short, Short)) => (x._1 ^ x._2).toShortExact }, + "{ (x: (Short, Short)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SShort, SShort))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toShort, 5.toShort) -> new Expected(ExpectedResult(Success(6.toShort), None)), + (10001.toShort, 2202.toShort) -> new Expected(ExpectedResult(Success(12171.toShort), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature[Short, Coll[Byte]]( + { x: Short => Colls.fromArray(Shorts.toByteArray(x)) }, + "{ (x: Short) => x.toBytes }", + FuncValue( + Array((1, SShort)), + MethodCall.typed[Value[SCollection[SShort.type]]]( + ValUse(1, SShort), + SShortMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 127.toShort -> new Expected(ExpectedResult(Success(Coll(0.toByte, 127.toByte)), None)), + Short.MaxValue -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte)), None)), + Short.MinValue -> new Expected(ExpectedResult(Success(Coll((-128).toByte, 0.toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[Short, Coll[Boolean]]( + { x: Short => Colls.fromArray(Shorts.toByteArray(x).flatMap(b => byte2Bools(b).toArray)) }, + "{ (x: Short) => x.toBits }", + FuncValue( + Array((1, SShort)), + MethodCall.typed[Value[SCollection[SShort.type]]]( + ValUse(1, SShort), + SShortMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 83.toShort -> new Expected(ExpectedResult(Success(Coll(false, false, false, false, false, false, false, false, false, true, false, true, false, false, true, true)), None)), + -55.toShort -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, false, false, true, false, false, true)), None)), + -1.toShort-> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true)), None)), + -10001.toShort-> new Expected(ExpectedResult(Success(Coll(true, true, false, true, true, false, false, false, true, true, true, false, true, true, true, true)), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (Short, Int)) => (x._1 << x._2).toShort }, + "{ (x: (Short, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SShort, SInt))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SInt)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SShort, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toShort, 3) -> new Expected(ExpectedResult(Success(24.toShort), None)), + (3.toShort, 8) -> new Expected(ExpectedResult(Success(768.toShort), None)), + ((-2).toShort, 10) -> new Expected(ExpectedResult(Success((-2048).toShort), None)), + ), + shiftLeft + ) + + lazy val shiftRight = newFeature( + { (x: (Short, Int)) => (x._1 >> x._2).toShort }, + "{ (x: (Short, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SShort, SInt))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SInt)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SShort, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (24.toShort, 3) -> new Expected(ExpectedResult(Success(3.toShort), None)), + (1600.toShort, 8) -> new Expected(ExpectedResult(Success(6.toShort), None)), + ((-3200).toShort, 8) -> new Expected(ExpectedResult(Success((-13).toShort), None)) + ), + shiftRight + ) } property("Int methods equivalence (new features)") { From 8cf4264e6c080d61193e8564c4f62ee1fec52ca1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 10 Sep 2024 15:19:07 +0300 Subject: [PATCH 34/40] 6.0 methods for Int in LSV6 --- .../scala/sigma/LanguageSpecificationV6.scala | 217 ++++++++++++++++-- 1 file changed, 194 insertions(+), 23 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 725b999f6e..fbc117235a 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -6,11 +6,11 @@ import sigma.VersionContext.V6SoftForkVersion import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.Token import scorex.util.ModifierId -import scorex.utils.Shorts +import scorex.utils.{Ints, Shorts} import sigma.ast.ErgoTree.ZeroHeader import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp -import sigma.ast._ +import sigma.ast.{SInt, _} import sigma.data.{CBigInt, CBox, CHeader, ExactNumeric} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode @@ -420,30 +420,201 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } - property("Int methods equivalence (new features)") { - if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { - // NOTE, for such versions the new features are not supported - // which is checked below - lazy val toAbs = newFeature((x: Int) => x.toAbs, "{ (x: Int) => x.toAbs }", - sinceVersion = V6SoftForkVersion) - lazy val compareTo = newFeature((x: (Int, Int)) => x._1.compareTo(x._2), - "{ (x: (Int, Int)) => x._1.compareTo(x._2) }", - sinceVersion = V6SoftForkVersion) - lazy val bitOr = newFeature( - { (x: (Int, Int)) => x._1 | x._2 }, - "{ (x: (Int, Int)) => x._1 | x._2 }", + property("Int - 6.0 methods") { + + lazy val bitOr = newFeature( + { (x: (Int, Int)) => (x._1 | x._2)}, + "{ (x: (Int, Int)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1,SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)),2.toByte)), + Map() + ) + ), sinceVersion = V6SoftForkVersion) - lazy val bitAnd = newFeature( + + verifyCases( + Seq( + (1, 2) -> new Expected(ExpectedResult(Success(3), None)), + (1001, 2002) -> new Expected(ExpectedResult(Success(2043), None)), + (100001, 20002) -> new Expected(ExpectedResult(Success(118435), None)) + ), + bitOr + ) + + lazy val bitNot = newFeature( + { (x: Int) => ~x }, + "{ (x: Int) => x.bitwiseInverse }", + FuncValue( + Array((1, SInt)), + MethodCall.typed[Value[SInt.type]]( + ValUse(1, SInt), + SIntMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 1 -> new Expected(ExpectedResult(Success(-2), None)), + 10001 -> new Expected(ExpectedResult(Success(-10002), None)), + Int.MinValue -> new Expected(ExpectedResult(Success(Int.MaxValue), None)) + ), + bitNot + ) + + lazy val bitAnd = newFeature( { (x: (Int, Int)) => x._1 & x._2 }, - "{ (x: (Int, Int)) => x._1 & x._2 }", + "{ (x: (Int, Int)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)),2.toByte)), + Map() + ) + ), sinceVersion = V6SoftForkVersion) - forAll { x: Int => - Seq(toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (Int, Int) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } + + verifyCases( + Seq( + (3, 5) -> new Expected(ExpectedResult(Success(1), None)), + (10001, 2202) -> new Expected(ExpectedResult(Success(16), None)), + (-10001, 200202) -> new Expected(ExpectedResult(Success(198666), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (Int, Int)) => (x._1 ^ x._2) }, + "{ (x: (Int, Int)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3, 5) -> new Expected(ExpectedResult(Success(6), None)), + (10001, 2202) -> new Expected(ExpectedResult(Success(12171), None)), + (-10001, 200202) -> new Expected(ExpectedResult(Success(-207131), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature[Int, Coll[Byte]]( + { x: Int => Colls.fromArray(Ints.toByteArray(x)) }, + "{ (x: Int) => x.toBytes }", + FuncValue( + Array((1, SInt)), + MethodCall.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SInt), + SIntMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 127 -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 0.toByte, 127.toByte)), None)), + Short.MaxValue.toInt -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 127.toByte, (-1).toByte)), None)), + Short.MinValue.toInt -> new Expected(ExpectedResult(Success(Coll((-1).toByte, (-1).toByte, (-128).toByte, 0.toByte)), None)), + Int.MaxValue.toInt -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte, (-1).toByte, (-1).toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[Int, Coll[Boolean]]( + { x: Int => Colls.fromArray(Ints.toByteArray(x).flatMap(b => byte2Bools(b).toArray)) }, + "{ (x: Int) => x.toBits }", + FuncValue( + Array((1, SInt)), + MethodCall.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SInt), + SIntMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 83 -> new Expected(ExpectedResult(Success(Coll(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, true, true)), None)), + -55 -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, true, false, false, true)), None)), + -1 -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true)), None)), + -10001 -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, true, false, false, false, true, true, true, false, true, true, true, true)), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (Int, Int)) => (x._1 << x._2) }, + "{ (x: (Int, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3, 3) -> new Expected(ExpectedResult(Success(24), None)), + (3, 8) -> new Expected(ExpectedResult(Success(768), None)), + (-2, 10) -> new Expected(ExpectedResult(Success(-2048), None)), + (-222, 10) -> new Expected(ExpectedResult(Success(-227328), None)) + ), + shiftLeft + ) + + lazy val shiftRight = newFeature( + { (x: (Int, Int)) => (x._1 >> x._2) }, + "{ (x: (Int, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (24, 3) -> new Expected(ExpectedResult(Success(3), None)), + (1600, 8) -> new Expected(ExpectedResult(Success(6), None)), + (-3200, 8) -> new Expected(ExpectedResult(Success(-13), None)), + (-320019, 18) -> new Expected(ExpectedResult(Success(-2), None)) + ), + shiftRight + ) } property("Long methods equivalence (new features)") { From fa2c2dfc7fe0f1dc0102901bf7ce75d1005af87c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 11 Sep 2024 14:11:39 +0300 Subject: [PATCH 35/40] Long tests in LSV6, shift check in shiftRight --- .../main/scala/sigma/data/ExactIntegral.scala | 32 ++- .../scala/sigma/LanguageSpecificationV6.scala | 237 +++++++++++++++--- .../test/scala/sigma/SigmaDslTesting.scala | 7 +- 3 files changed, 242 insertions(+), 34 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 418bc1837b..4b0d9cb720 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -44,7 +44,13 @@ object ExactIntegral { override def bitwiseAnd(x: Byte, y: Byte): Byte = (x & y).toByte override def bitwiseXor(x: Byte, y: Byte): Byte = (x ^ y).toByte override def shiftLeft(x: Byte, bits: Int): Byte = (x << bits).toByte - override def shiftRight(x: Byte, bits: Int): Byte = (x >> bits).toByte + override def shiftRight(x: Byte, bits: Int): Byte = { + if (bits < 0 || bits >= 8){ + throw new IllegalArgumentException(s"Wrong argument in Byte.shiftRight: bits < 0 || bits >= 8 ($bits)") + } else { + (x >> bits).toByte + } + } } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -59,7 +65,13 @@ object ExactIntegral { override def bitwiseAnd(x: Short, y: Short): Short = (x & y).toShort override def bitwiseXor(x: Short, y: Short): Short = (x ^ y).toShort override def shiftLeft(x: Short, y: Int): Short = (x << y).toShort - override def shiftRight(x: Short, bits: Int): Short = (x >> bits).toShort + override def shiftRight(x: Short, bits: Int): Short = { + if (bits < 0 || bits >= 16){ + throw new IllegalArgumentException(s"Wrong argument in Short.shiftRight: bits < 0 || bits >= 16 ($bits)") + } else { + (x >> bits).toShort + } + } } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -74,7 +86,13 @@ object ExactIntegral { override def bitwiseAnd(x: Int, y: Int): Int = x & y override def bitwiseXor(x: Int, y: Int): Int = x ^ y override def shiftLeft(x: Int, y: Int): Int = x << y - override def shiftRight(x: Int, bits: Int): Int = x >> bits + override def shiftRight(x: Int, bits: Int): Int = { + if (bits < 0 || bits >= 32){ + throw new IllegalArgumentException(s"Wrong argument in Int.shiftRight: bits < 0 || bits >= 32 ($bits)") + } else { + x >> bits + } + } } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -89,6 +107,12 @@ object ExactIntegral { override def bitwiseAnd(x: Long, y: Long): Long = x & y override def bitwiseXor(x: Long, y: Long): Long = x ^ y override def shiftLeft(x: Long, y: Int): Long = x << y - override def shiftRight(x: Long, bits: Int): Long = x >> bits + override def shiftRight(x: Long, bits: Int): Long = { + if (bits < 0 || bits >= 64){ + throw new IllegalArgumentException(s"Wrong argument in Long.shiftRight: bits < 0 || bits >= 64 ($bits)") + } else { + x >> bits + } + } } } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index fbc117235a..b7dd5b028d 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -6,7 +6,7 @@ import sigma.VersionContext.V6SoftForkVersion import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.Token import scorex.util.ModifierId -import scorex.utils.{Ints, Shorts} +import scorex.utils.{Ints, Longs, Shorts} import sigma.ast.ErgoTree.ZeroHeader import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp @@ -20,7 +20,7 @@ import sigmastate.utils.Extensions.ByteOpsForSigma import sigmastate.utils.Helpers import java.math.BigInteger -import scala.util.Success +import scala.util.{Failure, Success} /** This suite tests all operations for v6.0 version of the language. * The base classes establish the infrastructure for the tests. @@ -226,7 +226,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq( (24.toByte, 3) -> new Expected(ExpectedResult(Success(3.toByte), None)) ), - shiftRight + shiftRight, + preGeneratedSamples = Some(Seq()) ) } @@ -416,7 +417,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (1600.toShort, 8) -> new Expected(ExpectedResult(Success(6.toShort), None)), ((-3200).toShort, 8) -> new Expected(ExpectedResult(Success((-13).toShort), None)) ), - shiftRight + shiftRight, + preGeneratedSamples = Some(Seq()) ) } @@ -613,38 +615,215 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (-3200, 8) -> new Expected(ExpectedResult(Success(-13), None)), (-320019, 18) -> new Expected(ExpectedResult(Success(-2), None)) ), - shiftRight + shiftRight, + preGeneratedSamples = Some(Seq()) ) } - property("Long methods equivalence (new features)") { - if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { - // NOTE, for such versions the new features are not supported - // which is checked below - lazy val toAbs = newFeature((x: Long) => x.toAbs, "{ (x: Long) => x.toAbs }", - sinceVersion = V6SoftForkVersion) - lazy val compareTo = newFeature((x: (Long, Long)) => x._1.compareTo(x._2), - "{ (x: (Long, Long)) => x._1.compareTo(x._2) }", - sinceVersion = V6SoftForkVersion) + property("Long - 6.0 methods") { - lazy val bitOr = newFeature( - { (x: (Long, Long)) => x._1 | x._2 }, - "{ (x: (Long, Long)) => x._1 | x._2 }", - sinceVersion = V6SoftForkVersion) + lazy val bitOr = newFeature( + { (x: (Long, Long)) => (x._1 | x._2)}, + "{ (x: (Long, Long)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SLong, SLong))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1,SPair(SLong, SLong)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) - lazy val bitAnd = newFeature( - { (x: (Long, Long)) => x._1 & x._2 }, - "{ (x: (Long, Long)) => x._1 & x._2 }", - sinceVersion = V6SoftForkVersion) + verifyCases( + Seq( + (1L, 2L) -> new Expected(ExpectedResult(Success(3L), None)), + (1001L, 2002L) -> new Expected(ExpectedResult(Success(2043L), None)), + (100001L, 20002L) -> new Expected(ExpectedResult(Success(118435L), None)), + (1000010111L, -22L) -> new Expected(ExpectedResult(Success(-1L), None)) + ), + bitOr, + failOnTestVectors = false + ) - forAll { x: Long => - Seq(toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (Long, Long) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } + lazy val bitNot = newFeature( + { (x: Long) => ~x }, + "{ (x: Long) => x.bitwiseInverse }", + FuncValue( + Array((1, SLong)), + MethodCall.typed[Value[SLong.type]]( + ValUse(1, SLong), + SLongMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + verifyCases( + Seq( + 1L -> new Expected(ExpectedResult(Success(-2L), None)), + 10001L -> new Expected(ExpectedResult(Success(-10002L), None)), + Int.MinValue.toLong -> new Expected(ExpectedResult(Success(Int.MaxValue.toLong), None)), + Long.MinValue -> new Expected(ExpectedResult(Success(Long.MaxValue), None)), + Long.MaxValue -> new Expected(ExpectedResult(Success(Long.MinValue), None)) + ), + bitNot + ) + + lazy val bitAnd = newFeature( + { (x: (Long, Long)) => x._1 & x._2 }, + "{ (x: (Long, Long)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SLong, SLong))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3L, 5L) -> new Expected(ExpectedResult(Success(1L), None)), + (10001L, 2202L) -> new Expected(ExpectedResult(Success(16L), None)), + (-10001L, 200202L) -> new Expected(ExpectedResult(Success(198666L), None)), + (1000010111L, -22L) -> new Expected(ExpectedResult(Success(1000010090L), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (Long, Long)) => (x._1 ^ x._2) }, + "{ (x: (Long, Long)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SLong, SLong))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3L, 5L) -> new Expected(ExpectedResult(Success(6L), None)), + (10001L, 2202L) -> new Expected(ExpectedResult(Success(12171L), None)), + (-10001L, 200202L) -> new Expected(ExpectedResult(Success(-207131L), None)), + (1000010111L, -22L) -> new Expected(ExpectedResult(Success(-1000010091L), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature[Long, Coll[Byte]]( + { x: Long => Colls.fromArray(Longs.toByteArray(x)) }, + "{ (x: Long) => x.toBytes }", + FuncValue( + Array((1, SLong)), + MethodCall.typed[Value[SCollection[SLong.type]]]( + ValUse(1, SLong), + SLongMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 127L -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 127.toByte)), None)), + Short.MaxValue.toLong -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 127.toByte, (-1).toByte)), None)), + Short.MinValue.toLong -> new Expected(ExpectedResult(Success(Coll((-1).toByte, (-1).toByte, (-1).toByte, (-1).toByte, (-1).toByte, (-1).toByte, (-128).toByte, 0.toByte)), None)), + Int.MaxValue.toLong -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 0.toByte, 0.toByte, 127.toByte, (-1).toByte, (-1).toByte, (-1).toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[Long, Coll[Boolean]]( + { x: Long => Colls.fromArray(Longs.toByteArray(x).flatMap(b => byte2Bools(b).toArray)) }, + "{ (x: Long) => x.toBits }", + FuncValue( + Array((1, SLong)), + MethodCall.typed[Value[SCollection[SLong.type]]]( + ValUse(1, SLong), + SLongMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 83L -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(57)(false)).append(Coll(true, false, true, false, false, true, true))), None)), + -55L -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(58)(true)).append(Coll(false, false, true, false, false, true))), None)), + -1L -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(64)(true))), None)), + -10001L -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(50)(true)).append(Coll( false, true, true, false, false, false, true, true, true, false, true, true, true, true))), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (Long, Int)) => (x._1 << x._2) }, + "{ (x: (Long, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SLong, SInt))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SInt)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SLong, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3L, 3) -> new Expected(ExpectedResult(Success(24L), None)), + (3L, 8) -> new Expected(ExpectedResult(Success(768L), None)), + (-2L, 10) -> new Expected(ExpectedResult(Success(-2048L), None)), + (-222L, 10) -> new Expected(ExpectedResult(Success(-227328L), None)) + ), + shiftLeft + ) + + lazy val shiftRight = newFeature( + { (x: (Long, Int)) => if(x._2 < 0 || x._2 >= 64) throw new IllegalArgumentException() else (x._1 >> x._2) }, + "{ (x: (Long, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SLong, SInt))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SInt)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SLong, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (24L, 3) -> new Expected(ExpectedResult(Success(3L), None)), + (1600L, 8) -> new Expected(ExpectedResult(Success(6L), None)), + (-3200L, 8) -> new Expected(ExpectedResult(Success(-13L), None)), + (-320019L, 18) -> new Expected(ExpectedResult(Success(-2L), None)), + (-320019L, 63) -> new Expected(ExpectedResult(Success(-1L), None)), + (24L, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) } property("BigInt methods equivalence (new features)") { diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index d334ac4653..6ab62e39a9 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -940,7 +940,12 @@ class SigmaDslTesting extends AnyPropSpec funcRes.isFailure shouldBe true } if(isSupportedIn(VersionContext.current)) { - Try(scalaFunc(input)) shouldBe expected.value + val res = Try(scalaFunc(input)) + if(expected.value.isSuccess) { + res shouldBe expected.value + } else { + res.isFailure shouldBe true + } } else { Try(scalaFunc(input)).isFailure shouldBe true } From 48db43217fb1d3bf8846fb310e6520c4d30a2cb3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 11 Sep 2024 15:02:27 +0300 Subject: [PATCH 36/40] shiftLeft/shiftRight shift limit --- .../main/scala/sigma/data/BigIntegerOps.scala | 18 ++++++-- .../main/scala/sigma/data/ExactIntegral.scala | 42 +++++++++++++++---- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 2e1d2f62ce..8d272439f4 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -101,9 +101,21 @@ object NumericOps { override def bitwiseXor(x: BigInt, y: BigInt): BigInt = x.xor(y) - override def shiftLeft(x: BigInt, y: Int): BigInt = x.shiftLeft(y) - - override def shiftRight(x: BigInt, y: Int): BigInt = x.shiftRight(y) + override def shiftLeft(x: BigInt, bits: Int): BigInt = { + if (bits < 0 || bits >= 256) { + throw new IllegalArgumentException(s"Wrong argument in BigInt.shiftRight: bits < 0 || bits >= 256 ($bits)") + } else { + x.shiftLeft(bits) + } + } + + override def shiftRight(x: BigInt, bits: Int): BigInt = { + if (bits < 0 || bits >= 256) { + throw new IllegalArgumentException(s"Wrong argument in BigInt.shiftRight: bits < 0 || bits >= 256 ($bits)") + } else { + x.shiftRight(bits) + } + } } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 4b0d9cb720..2bd7fbe341 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -43,9 +43,15 @@ object ExactIntegral { override def bitwiseOr(x: Byte, y: Byte): Byte = (x | y).toByte override def bitwiseAnd(x: Byte, y: Byte): Byte = (x & y).toByte override def bitwiseXor(x: Byte, y: Byte): Byte = (x ^ y).toByte - override def shiftLeft(x: Byte, bits: Int): Byte = (x << bits).toByte + override def shiftLeft(x: Byte, bits: Int): Byte = { + if (bits < 0 || bits >= 8) { + throw new IllegalArgumentException(s"Wrong argument in Byte.shiftRight: bits < 0 || bits >= 8 ($bits)") + } else { + (x << bits).toByte + } + } override def shiftRight(x: Byte, bits: Int): Byte = { - if (bits < 0 || bits >= 8){ + if (bits < 0 || bits >= 8) { throw new IllegalArgumentException(s"Wrong argument in Byte.shiftRight: bits < 0 || bits >= 8 ($bits)") } else { (x >> bits).toByte @@ -64,7 +70,13 @@ object ExactIntegral { override def bitwiseOr(x: Short, y: Short): Short = (x | y).toShort override def bitwiseAnd(x: Short, y: Short): Short = (x & y).toShort override def bitwiseXor(x: Short, y: Short): Short = (x ^ y).toShort - override def shiftLeft(x: Short, y: Int): Short = (x << y).toShort + override def shiftLeft(x: Short, bits: Int): Short = { + if (bits < 0 || bits >= 16) { + throw new IllegalArgumentException(s"Wrong argument in Short.shiftRight: bits < 0 || bits >= 16 ($bits)") + } else { + (x << bits).toShort + } + } override def shiftRight(x: Short, bits: Int): Short = { if (bits < 0 || bits >= 16){ throw new IllegalArgumentException(s"Wrong argument in Short.shiftRight: bits < 0 || bits >= 16 ($bits)") @@ -85,9 +97,17 @@ object ExactIntegral { override def bitwiseOr(x: Int, y: Int): Int = x | y override def bitwiseAnd(x: Int, y: Int): Int = x & y override def bitwiseXor(x: Int, y: Int): Int = x ^ y - override def shiftLeft(x: Int, y: Int): Int = x << y + + override def shiftLeft(x: Int, bits: Int): Int = { + if (bits < 0 || bits >= 32) { + throw new IllegalArgumentException(s"Wrong argument in Byte.shiftRight: bits < 0 || bits >= 32 ($bits)") + } else { + x << bits + } + } + override def shiftRight(x: Int, bits: Int): Int = { - if (bits < 0 || bits >= 32){ + if (bits < 0 || bits >= 32) { throw new IllegalArgumentException(s"Wrong argument in Int.shiftRight: bits < 0 || bits >= 32 ($bits)") } else { x >> bits @@ -106,9 +126,17 @@ object ExactIntegral { override def bitwiseOr(x: Long, y: Long): Long = x | y override def bitwiseAnd(x: Long, y: Long): Long = x & y override def bitwiseXor(x: Long, y: Long): Long = x ^ y - override def shiftLeft(x: Long, y: Int): Long = x << y + + override def shiftLeft(x: Long, bits: Int): Long = { + if (bits < 0 || bits >= 64) { + throw new IllegalArgumentException(s"Wrong argument in Long.shiftRight: bits < 0 || bits >= 64 ($bits)") + } else { + x << bits + } + } + override def shiftRight(x: Long, bits: Int): Long = { - if (bits < 0 || bits >= 64){ + if (bits < 0 || bits >= 64) { throw new IllegalArgumentException(s"Wrong argument in Long.shiftRight: bits < 0 || bits >= 64 ($bits)") } else { x >> bits From a2f3030d877f692c80c24ab74318c08c7df06bbd Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 12 Sep 2024 00:01:40 +0300 Subject: [PATCH 37/40] bigint - LSV6 tests --- .../src/main/scala/sigma/ast/methods.scala | 4 +- .../scala/sigma/LanguageSpecificationV6.scala | 270 ++++++++++++++---- 2 files changed, 221 insertions(+), 53 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 1e8fb911bb..34c87096cd 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -360,7 +360,7 @@ object SNumericTypeMethods extends MethodsContainer { .withInfo(MethodCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. - """.stripMargin) + """.stripMargin) // todo: describe shift arg and its limits val ShiftRightMethod: SMethod = SMethod( this, "shiftRight", SFunc(Array(tNum, SInt), tNum), 13, BitwiseInverse_CostKind) @@ -377,7 +377,7 @@ object SNumericTypeMethods extends MethodsContainer { .withInfo(MethodCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. - """.stripMargin) + """.stripMargin) // todo: describe shift arg and its limits lazy val v5Methods = Array( ToByteMethod, // see Downcast diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index b7dd5b028d..6c1796cef0 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -205,7 +205,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq( (3.toByte, 3) -> new Expected(ExpectedResult(Success(24.toByte), None)) ), - shiftLeft + shiftLeft, + preGeneratedSamples = Some(Seq()) ) lazy val shiftRight = newFeature( @@ -394,7 +395,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (3.toShort, 8) -> new Expected(ExpectedResult(Success(768.toShort), None)), ((-2).toShort, 10) -> new Expected(ExpectedResult(Success((-2048).toShort), None)), ), - shiftLeft + shiftLeft, + preGeneratedSamples = Some(Seq()) ) lazy val shiftRight = newFeature( @@ -591,7 +593,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (-2, 10) -> new Expected(ExpectedResult(Success(-2048), None)), (-222, 10) -> new Expected(ExpectedResult(Success(-227328), None)) ), - shiftLeft + shiftLeft, + preGeneratedSamples = Some(Seq()) ) lazy val shiftRight = newFeature( @@ -643,8 +646,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (100001L, 20002L) -> new Expected(ExpectedResult(Success(118435L), None)), (1000010111L, -22L) -> new Expected(ExpectedResult(Success(-1L), None)) ), - bitOr, - failOnTestVectors = false + bitOr ) lazy val bitNot = newFeature( @@ -795,7 +797,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (-2L, 10) -> new Expected(ExpectedResult(Success(-2048L), None)), (-222L, 10) -> new Expected(ExpectedResult(Success(-227328L), None)) ), - shiftLeft + shiftLeft, + preGeneratedSamples = Some(Seq()) ) lazy val shiftRight = newFeature( @@ -826,7 +829,9 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } - property("BigInt methods equivalence (new features)") { + property("BigInt - 6.0 features") { + import sigma.data.OrderingOps.BigIntOrdering + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. // Fixed in 6.0 @@ -841,50 +846,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => SBigInt.downcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), _.getMessage.contains("Cannot downcast value") ) - } else { - forAll { x: BigInteger => - SBigInt.upcast(CBigInt(x).asInstanceOf[AnyVal]) shouldBe CBigInt(x) - SBigInt.downcast(CBigInt(x).asInstanceOf[AnyVal]) shouldBe CBigInt(x) - } - } - - if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { - // NOTE, for such versions the new features are not supported - // which is checked below - val toByte = newFeature((x: BigInt) => x.toByte, - "{ (x: BigInt) => x.toByte }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SByte)), - sinceVersion = V6SoftForkVersion) - val toShort = newFeature((x: BigInt) => x.toShort, - "{ (x: BigInt) => x.toShort }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SShort)), - sinceVersion = V6SoftForkVersion) - val toInt = newFeature((x: BigInt) => x.toInt, - "{ (x: BigInt) => x.toInt }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SInt)), - sinceVersion = V6SoftForkVersion) - val toLong = newFeature((x: BigInt) => x.toLong, - "{ (x: BigInt) => x.toLong }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SLong)), - sinceVersion = V6SoftForkVersion) - lazy val toAbs = newFeature((x: BigInt) => x.toAbs, "{ (x: BigInt) => x.toAbs }", - sinceVersion = V6SoftForkVersion) - lazy val compareTo = newFeature((x: (BigInt, BigInt)) => x._1.compareTo(x._2), - "{ (x: (BigInt, BigInt)) => x._1.compareTo(x._2) }", - sinceVersion = V6SoftForkVersion) - lazy val bitOr = newFeature({ (x: (BigInt, BigInt)) => x._1 | x._2 }, - "{ (x: (BigInt, BigInt)) => x._1 | x._2 }", - sinceVersion = V6SoftForkVersion) - lazy val bitAnd = newFeature({ (x: (BigInt, BigInt)) => x._1 & x._2 }, - "{ (x: (BigInt, BigInt)) => x._1 & x._2 }", - sinceVersion = V6SoftForkVersion) - - forAll { x: BigInt => - Seq(toByte, toShort, toInt, toLong, toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (BigInt, BigInt) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } forAll { x: Long => assertExceptionThrown( @@ -911,6 +872,10 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } } else { + forAll { x: BigInteger => + SBigInt.upcast(CBigInt(x).asInstanceOf[AnyVal]) shouldBe CBigInt(x) + SBigInt.downcast(CBigInt(x).asInstanceOf[AnyVal]) shouldBe CBigInt(x) + } forAll { x: Long => SLong.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x } @@ -924,6 +889,209 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => SShort.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x } } + + lazy val bitOr = newFeature[(BigInt, BigInt), BigInt]( + { (x: (BigInt, BigInt)) => (x._1 | x._2)}, + "{ (x: (BigInt, BigInt)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SBigInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1,SPair(SBigInt, SBigInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(1)), CBigInt(BigInteger.valueOf(2))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(3))), None)), + (CBigInt(BigInteger.valueOf(1001)), CBigInt(BigInteger.valueOf(2002))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(2043))), None)), + (CBigInt(BigInteger.valueOf(100001)), CBigInt(BigInteger.valueOf(20002))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(118435))), None)), + (CBigInt(BigInteger.valueOf(1000010111)), CBigInt(BigInteger.valueOf(-22))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-1))), None)) + ), + bitOr + ) + + lazy val bitNot = newFeature[BigInt, BigInt]( + { (x: BigInt) => CBigInt(x.asInstanceOf[CBigInt].wrappedValue.not()) }, + "{ (x: BigInt) => x.bitwiseInverse }", + FuncValue( + Array((1, SBigInt)), + MethodCall.typed[Value[SBigInt.type]]( + ValUse(1, SBigInt), + SBigIntMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + CBigInt(BigInteger.valueOf(1)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-2))), None)), + CBigInt(BigInteger.valueOf(10001)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-10002))), None)), + CBigInt(BigInteger.valueOf(Int.MinValue)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(Int.MaxValue))), None)), + CBigInt(BigInteger.valueOf(Long.MinValue)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(Long.MaxValue))), None)), + CBigInt(BigInteger.valueOf(Long.MaxValue)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(Long.MinValue))), None)) + ), + bitNot + ) + + lazy val bitAnd = newFeature( + { (x: (BigInt, BigInt)) => x._1.asInstanceOf[CBigInt].and(x._2.asInstanceOf[CBigInt]) }, + "{ (x: (BigInt, BigInt)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SBigInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(3)), CBigInt(BigInteger.valueOf(5))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(1))), None)), + (CBigInt(BigInteger.valueOf(10001)), CBigInt(BigInteger.valueOf(2202))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(16))), None)), + (CBigInt(BigInteger.valueOf(-10001)), CBigInt(BigInteger.valueOf(200202))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(198666))), None)), + (CBigInt(BigInteger.valueOf(1000010111)), CBigInt(BigInteger.valueOf(-22))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(1000010090))), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (BigInt, BigInt)) => x._1.asInstanceOf[CBigInt].xor(x._2.asInstanceOf[CBigInt]) }, + "{ (x: (BigInt, BigInt)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SBigInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(3)), CBigInt(BigInteger.valueOf(5))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(6))), None)), + (CBigInt(BigInteger.valueOf(10001)), CBigInt(BigInteger.valueOf(2202))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(12171))), None)), + (CBigInt(BigInteger.valueOf(-10001)), CBigInt(BigInteger.valueOf(200202))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-207131))), None)), + (CBigInt(BigInteger.valueOf(1000010111)), CBigInt(BigInteger.valueOf(-22))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-1000010091))), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature[BigInt, Coll[Byte]]( + { x: BigInt => x.toBytes }, + "{ (x: BigInt) => x.toBytes }", + FuncValue( + Array((1, SBigInt)), + MethodCall.typed[Value[SCollection[SBigInt.type]]]( + ValUse(1, SBigInt), + SBigIntMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + CBigInt(BigInteger.valueOf(127)) -> new Expected(ExpectedResult(Success(Coll(127.toByte)), None)), + CBigInt(BigInteger.valueOf(Short.MaxValue)) -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte)), None)), + CBigInt(BigInteger.valueOf(Short.MinValue)) -> new Expected(ExpectedResult(Success(Coll((-128).toByte, 0.toByte)), None)), + CBigInt(BigInteger.valueOf(Int.MaxValue)) -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte, (-1).toByte, (-1).toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[BigInt, Coll[Boolean]]( + { x: BigInt => Colls.fromArray(x.toBytes.toArray.flatMap(b => byte2Bools(b).toArray)) }, + "{ (x: BigInt) => x.toBits }", + FuncValue( + Array((1, SBigInt)), + MethodCall.typed[Value[SCollection[SBigInt.type]]]( + ValUse(1, SBigInt), + SBigIntMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + CBigInt(BigInteger.valueOf(83)) -> new Expected(ExpectedResult(Success(Coll(false, true, false, true, false, false, true, true)), None)), + CBigInt(BigInteger.valueOf(-55)) -> new Expected(ExpectedResult(Success(Coll(true, true, false, false, true, false, false, true)), None)), + CBigInt(BigInteger.valueOf(-1L)) -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(8)(true))), None)), + CBigInt(BigInteger.valueOf(-10001L)) -> new Expected(ExpectedResult(Success(Coll(true,true,false,true,true,false,false,false,true,true,true,false,true,true,true,true)), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (BigInt, Int)) => (x._1.asInstanceOf[BigInt].shiftLeft(x._2)) }, + "{ (x: (BigInt, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SBigInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(3)), 3) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(24))), None)), + (CBigInt(BigInteger.valueOf(3)), 8) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(768))), None)), + (CBigInt(BigInteger.valueOf(-2)), 10) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-2048))), None)), + (CBigInt(BigInteger.valueOf(-222)), 10) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-227328L))), None)) + ), + shiftLeft, + preGeneratedSamples = Some(Seq()) + ) + + lazy val shiftRight = newFeature( + { (x: (BigInt, Int)) => if(x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[BigInt].shiftRight(x._2)) }, + "{ (x: (BigInt, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SBigInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(24)), 3) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(3))), None)), + (CBigInt(BigInteger.valueOf(1600)), 8) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(6))), None)), + (CBigInt(BigInteger.valueOf(-3200)), 8) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-13))), None)), + (CBigInt(BigInteger.valueOf(-320019)), 18) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-2))), None)), + (CBigInt(BigInteger.valueOf(-320019)), 63) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-1))), None)), + (CBigInt(BigInteger.valueOf(24)), -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) } property("Box properties equivalence (new features)") { From 297265f0989d3b5e6f2ab7eaff6518479e29ed94 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 12 Sep 2024 18:26:22 +0300 Subject: [PATCH 38/40] arg descs, 2.11 comp fix, BOS tests fixed --- .../src/main/scala/sigma/ast/methods.scala | 28 +++++++++---------- .../scala/sigma/LanguageSpecificationV6.scala | 10 +++---- .../utxo/BasicOpsSpecification.scala | 8 +++--- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 34c87096cd..634145c6e1 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -289,10 +289,7 @@ object SNumericTypeMethods extends MethodsContainer { case SBigIntMethods => BigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[BigInt]) } }) - .withInfo(PropertyCall, - """ Returns a big-endian representation of this numeric in a collection of Booleans. - | Each boolean corresponds to one bit. - """.stripMargin) + .withInfo(PropertyCall, desc = "Returns bitwise inverse of this numeric. ") val BitwiseOrMethod: SMethod = SMethod( this, "bitwiseOr", SFunc(Array(tNum, tNum), tNum), 9, BitwiseInverse_CostKind) @@ -307,9 +304,8 @@ object SNumericTypeMethods extends MethodsContainer { } }) .withInfo(MethodCall, - """ Returns a big-endian representation of this numeric in a collection of Booleans. - | Each boolean corresponds to one bit. - """.stripMargin) + """ Returns bitwise or of this numeric and provided one. """.stripMargin, + ArgInfo("that", "A numeric value to calculate or with.")) val BitwiseAndMethod: SMethod = SMethod( this, "bitwiseAnd", SFunc(Array(tNum, tNum), tNum), 10, BitwiseInverse_CostKind) @@ -324,9 +320,8 @@ object SNumericTypeMethods extends MethodsContainer { } }) .withInfo(MethodCall, - """ Returns a big-endian representation of this numeric in a collection of Booleans. - | Each boolean corresponds to one bit. - """.stripMargin) + """ Returns bitwise and of this numeric and provided one. """.stripMargin, + ArgInfo("that", "A numeric value to calculate and with.")) val BitwiseXorMethod: SMethod = SMethod( this, "bitwiseXor", SFunc(Array(tNum, tNum), tNum), 11, BitwiseInverse_CostKind) @@ -341,9 +336,8 @@ object SNumericTypeMethods extends MethodsContainer { } }) .withInfo(MethodCall, - """ Returns a big-endian representation of this numeric in a collection of Booleans. - | Each boolean corresponds to one bit. - """.stripMargin) + """ Returns bitwise xor of this numeric and provided one. """.stripMargin, + ArgInfo("that", "A numeric value to calculate xor with.")) val ShiftLeftMethod: SMethod = SMethod( this, "shiftLeft", SFunc(Array(tNum, SInt), tNum), 12, BitwiseInverse_CostKind) @@ -360,7 +354,9 @@ object SNumericTypeMethods extends MethodsContainer { .withInfo(MethodCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. - """.stripMargin) // todo: describe shift arg and its limits + """.stripMargin, + ArgInfo("bits", "Number of bit to shift to the left. Note, that bits value must be non-negative and less than " + + "the size of the number in bits (e.g. 64 for Long, 256 for BigInt)")) val ShiftRightMethod: SMethod = SMethod( this, "shiftRight", SFunc(Array(tNum, SInt), tNum), 13, BitwiseInverse_CostKind) @@ -377,7 +373,9 @@ object SNumericTypeMethods extends MethodsContainer { .withInfo(MethodCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. - """.stripMargin) // todo: describe shift arg and its limits + """.stripMargin, + ArgInfo("bits", "Number of bit to shift to the right. Note, that bits value must be non-negative and less than " + + "the size of the number in bits (e.g. 64 for Long, 256 for BigInt)")) lazy val v5Methods = Array( ToByteMethod, // see Downcast diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 6c1796cef0..ddbc06b6b3 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -352,7 +352,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ((byte >> bit) & 1) == 1 lazy val toBits = newFeature[Short, Coll[Boolean]]( - { x: Short => Colls.fromArray(Shorts.toByteArray(x).flatMap(b => byte2Bools(b).toArray)) }, + { x: Short => Colls.fromArray(Shorts.toByteArray(x)).flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, "{ (x: Short) => x.toBits }", FuncValue( Array((1, SShort)), @@ -393,7 +393,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq( (3.toShort, 3) -> new Expected(ExpectedResult(Success(24.toShort), None)), (3.toShort, 8) -> new Expected(ExpectedResult(Success(768.toShort), None)), - ((-2).toShort, 10) -> new Expected(ExpectedResult(Success((-2048).toShort), None)), + ((-2).toShort, 10) -> new Expected(ExpectedResult(Success((-2048).toShort), None)) ), shiftLeft, preGeneratedSamples = Some(Seq()) @@ -549,7 +549,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ((byte >> bit) & 1) == 1 lazy val toBits = newFeature[Int, Coll[Boolean]]( - { x: Int => Colls.fromArray(Ints.toByteArray(x).flatMap(b => byte2Bools(b).toArray)) }, + { x: Int => Colls.fromArray(Ints.toByteArray(x)).flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, "{ (x: Int) => x.toBits }", FuncValue( Array((1, SInt)), @@ -753,7 +753,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ((byte >> bit) & 1) == 1 lazy val toBits = newFeature[Long, Coll[Boolean]]( - { x: Long => Colls.fromArray(Longs.toByteArray(x).flatMap(b => byte2Bools(b).toArray)) }, + { x: Long => Colls.fromArray(Longs.toByteArray(x)).flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, "{ (x: Long) => x.toBits }", FuncValue( Array((1, SLong)), @@ -1018,7 +1018,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ((byte >> bit) & 1) == 1 lazy val toBits = newFeature[BigInt, Coll[Boolean]]( - { x: BigInt => Colls.fromArray(x.toBytes.toArray.flatMap(b => byte2Bools(b).toArray)) }, + { x: BigInt => x.toBytes.flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, "{ (x: BigInt) => x.toBits }", FuncValue( Array((1, SBigInt)), diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index a37a3d1079..a5d73d2442 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -354,7 +354,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (VersionContext.current.isV6SoftForkActivated) { - shiftLeftTest() + an[IllegalArgumentException] shouldBe thrownBy(shiftLeftTest()) } else { an[Exception] shouldBe thrownBy(shiftLeftTest()) } @@ -455,7 +455,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (VersionContext.current.isV6SoftForkActivated) { - shiftRightTest() + an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) } else { an[Exception] shouldBe thrownBy(shiftRightTest()) } @@ -489,7 +489,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (VersionContext.current.isV6SoftForkActivated) { - shiftRightTest() + an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) } else { an[Exception] shouldBe thrownBy(shiftRightTest()) } @@ -525,7 +525,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (VersionContext.current.isV6SoftForkActivated) { - shiftRightTest() + an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) } else { an[Exception] shouldBe thrownBy(shiftRightTest()) } From 55d03cd38c3d4553520be75a837d561d68ca1cb8 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 17 Sep 2024 01:50:22 +0300 Subject: [PATCH 39/40] addressing review comments --- .../src/main/scala/sigma/ast/methods.scala | 3 +- .../main/scala/sigma/data/ExactNumeric.scala | 9 ++-- .../scala/sigma/LanguageSpecificationV6.scala | 53 +++++++++++++------ 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 634145c6e1..6c401f8fc3 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -397,8 +397,7 @@ object SNumericTypeMethods extends MethodsContainer { ) protected override def getMethods(): Seq[SMethod] = { - // this .getMethods shouldn't ever be called - ??? + throw new Exception("SNumericTypeMethods.getMethods shouldn't ever be called") } /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 56b4a9369e..244cc6b7b7 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -45,17 +45,14 @@ trait ExactNumeric[T] { def isBitSet(byte: Byte)(bit: Int): Boolean = ((byte >> bit) & 1) == 1 - def byte2Bools(b: Byte): Array[Boolean] = (0 to 7).toArray.reverse.map(isBitSet(b)) - val bytes = toBigEndianBytes(x) val l = bytes.length val res = new Array[Boolean](l * 8) - var offset = 0 cfor(0)(_ < l, _ + 1) { i => val b = bytes(i) - val bits = byte2Bools(b) - Array.copy(bits, 0, res, offset, 8) - offset += 8 + cfor(0)(_ < 8, _ + 1) { bitIdx => + res(i * 8 + (7 - bitIdx)) = isBitSet(b)(bitIdx) + } } Colls.fromArray(res) } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index ddbc06b6b3..4ca378c35e 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -188,7 +188,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) lazy val shiftLeft = newFeature( - { (x: (Byte, Int)) => (x._1 << x._2).toByte }, + { (x: (Byte, Int)) => if(x._2 < 0 || x._2 >= 8) throw new IllegalArgumentException() else (x._1 << x._2).toByte }, "{ (x: (Byte, Int)) => x._1.shiftLeft(x._2) }", FuncValue( Array((1, SPair(SByte, SInt))), @@ -203,14 +203,17 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (3.toByte, 3) -> new Expected(ExpectedResult(Success(24.toByte), None)) + (3.toByte, 3) -> new Expected(ExpectedResult(Success(24.toByte), None)), + (3.toByte, 0) -> new Expected(ExpectedResult(Success(3.toByte), None)), + (3.toByte, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (3.toByte, 8) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), shiftLeft, preGeneratedSamples = Some(Seq()) ) lazy val shiftRight = newFeature( - { (x: (Byte, Int)) => (x._1 >> x._2).toByte }, + { (x: (Byte, Int)) => if(x._2 < 0 || x._2 >= 8) throw new IllegalArgumentException() else (x._1 >> x._2).toByte }, "{ (x: (Byte, Int)) => x._1.shiftRight(x._2) }", FuncValue( Array((1, SPair(SByte, SInt))), @@ -225,7 +228,10 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (24.toByte, 3) -> new Expected(ExpectedResult(Success(3.toByte), None)) + (24.toByte, 3) -> new Expected(ExpectedResult(Success(3.toByte), None)), + (24.toByte, 0) -> new Expected(ExpectedResult(Success(24.toByte), None)), + (24.toByte, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (24.toByte, 8) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), shiftRight, preGeneratedSamples = Some(Seq()) @@ -376,7 +382,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) lazy val shiftLeft = newFeature( - { (x: (Short, Int)) => (x._1 << x._2).toShort }, + { (x: (Short, Int)) => if(x._2 < 0 || x._2 >= 16) throw new IllegalArgumentException() else (x._1 << x._2).toShort }, "{ (x: (Short, Int)) => x._1.shiftLeft(x._2) }", FuncValue( Array((1, SPair(SShort, SInt))), @@ -393,14 +399,16 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq( (3.toShort, 3) -> new Expected(ExpectedResult(Success(24.toShort), None)), (3.toShort, 8) -> new Expected(ExpectedResult(Success(768.toShort), None)), - ((-2).toShort, 10) -> new Expected(ExpectedResult(Success((-2048).toShort), None)) + ((-2).toShort, 10) -> new Expected(ExpectedResult(Success((-2048).toShort), None)), + ((-2).toShort, 20) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (3.toShort, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), shiftLeft, preGeneratedSamples = Some(Seq()) ) lazy val shiftRight = newFeature( - { (x: (Short, Int)) => (x._1 >> x._2).toShort }, + { (x: (Short, Int)) => if(x._2 < 0 || x._2 >= 16) throw new IllegalArgumentException() else (x._1 >> x._2).toShort }, "{ (x: (Short, Int)) => x._1.shiftRight(x._2) }", FuncValue( Array((1, SPair(SShort, SInt))), @@ -417,7 +425,9 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq( (24.toShort, 3) -> new Expected(ExpectedResult(Success(3.toShort), None)), (1600.toShort, 8) -> new Expected(ExpectedResult(Success(6.toShort), None)), - ((-3200).toShort, 8) -> new Expected(ExpectedResult(Success((-13).toShort), None)) + ((-3200).toShort, 8) -> new Expected(ExpectedResult(Success((-13).toShort), None)), + (3.toShort, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (3.toShort, 16) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), shiftRight, preGeneratedSamples = Some(Seq()) @@ -573,7 +583,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) lazy val shiftLeft = newFeature( - { (x: (Int, Int)) => (x._1 << x._2) }, + { (x: (Int, Int)) => if(x._2 < 0 || x._2 >= 32) throw new IllegalArgumentException() else (x._1 << x._2) }, "{ (x: (Int, Int)) => x._1.shiftLeft(x._2) }", FuncValue( Array((1, SPair(SInt, SInt))), @@ -591,14 +601,16 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (3, 3) -> new Expected(ExpectedResult(Success(24), None)), (3, 8) -> new Expected(ExpectedResult(Success(768), None)), (-2, 10) -> new Expected(ExpectedResult(Success(-2048), None)), - (-222, 10) -> new Expected(ExpectedResult(Success(-227328), None)) + (-222, 10) -> new Expected(ExpectedResult(Success(-227328), None)), + (-222, 32) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (-222, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), shiftLeft, preGeneratedSamples = Some(Seq()) ) lazy val shiftRight = newFeature( - { (x: (Int, Int)) => (x._1 >> x._2) }, + { (x: (Int, Int)) => if(x._2 < 0 || x._2 >= 32) throw new IllegalArgumentException() else (x._1 >> x._2) }, "{ (x: (Int, Int)) => x._1.shiftRight(x._2) }", FuncValue( Array((1, SPair(SInt, SInt))), @@ -616,7 +628,9 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (24, 3) -> new Expected(ExpectedResult(Success(3), None)), (1600, 8) -> new Expected(ExpectedResult(Success(6), None)), (-3200, 8) -> new Expected(ExpectedResult(Success(-13), None)), - (-320019, 18) -> new Expected(ExpectedResult(Success(-2), None)) + (-320019, 18) -> new Expected(ExpectedResult(Success(-2), None)), + (-320019, 32) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (-320019, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), shiftRight, preGeneratedSamples = Some(Seq()) @@ -777,7 +791,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) lazy val shiftLeft = newFeature( - { (x: (Long, Int)) => (x._1 << x._2) }, + { (x: (Long, Int)) => if(x._2 < 0 || x._2 >= 32) throw new IllegalArgumentException() else (x._1 << x._2) }, "{ (x: (Long, Int)) => x._1.shiftLeft(x._2) }", FuncValue( Array((1, SPair(SLong, SInt))), @@ -795,7 +809,9 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (3L, 3) -> new Expected(ExpectedResult(Success(24L), None)), (3L, 8) -> new Expected(ExpectedResult(Success(768L), None)), (-2L, 10) -> new Expected(ExpectedResult(Success(-2048L), None)), - (-222L, 10) -> new Expected(ExpectedResult(Success(-227328L), None)) + (-222L, 10) -> new Expected(ExpectedResult(Success(-227328L), None)), + (-222L, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (-222L, 64) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), shiftLeft, preGeneratedSamples = Some(Seq()) @@ -1042,7 +1058,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) lazy val shiftLeft = newFeature( - { (x: (BigInt, Int)) => (x._1.asInstanceOf[BigInt].shiftLeft(x._2)) }, + { (x: (BigInt, Int)) => if(x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[BigInt].shiftLeft(x._2)) }, "{ (x: (BigInt, Int)) => x._1.shiftLeft(x._2) }", FuncValue( Array((1, SPair(SBigInt, SInt))), @@ -1060,7 +1076,9 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (CBigInt(BigInteger.valueOf(3)), 3) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(24))), None)), (CBigInt(BigInteger.valueOf(3)), 8) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(768))), None)), (CBigInt(BigInteger.valueOf(-2)), 10) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-2048))), None)), - (CBigInt(BigInteger.valueOf(-222)), 10) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-227328L))), None)) + (CBigInt(BigInteger.valueOf(-222)), 10) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-227328L))), None)), + (CBigInt(BigInteger.valueOf(-222)), -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (CBigInt(BigInteger.valueOf(-222)), 256) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), shiftLeft, preGeneratedSamples = Some(Seq()) @@ -1087,7 +1105,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (CBigInt(BigInteger.valueOf(-3200)), 8) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-13))), None)), (CBigInt(BigInteger.valueOf(-320019)), 18) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-2))), None)), (CBigInt(BigInteger.valueOf(-320019)), 63) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-1))), None)), - (CBigInt(BigInteger.valueOf(24)), -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + (CBigInt(BigInteger.valueOf(24)), -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (CBigInt(BigInteger.valueOf(24)), 256) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), shiftRight, preGeneratedSamples = Some(Seq()) From 5f0c5c5cb299cf32ae5bf58e0e9bf686c2f378f7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 20 Sep 2024 16:11:07 +0300 Subject: [PATCH 40/40] scaladoc improvements, Ints.toBits --- core/shared/src/main/scala/sigma/SigmaDsl.scala | 9 ++++----- .../src/main/scala/sigma/ast/methods.scala | 14 +++++++------- .../main/scala/sigma/data/ExactIntegral.scala | 3 +-- .../sigmastate/utxo/BasicOpsSpecification.scala | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 6f9a3e2ffe..c2fd44550a 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -154,19 +154,18 @@ trait BigInt { def |(that: BigInt): BigInt = or(that) /** - * @return a big integer whose value is `this xor that` + * @return a big integer whose value is `this xor that`. + * This method returns a negative BigInteger if and only if exactly one of this and val are negative. */ def xor(that: BigInt): BigInt /** - * @return a 256-bit signed integer whose value is (this << n). The shift distance, n, may be negative, - * in which case this method performs a right shift. (Computes floor(this * 2n).) + * @return a 256-bit signed integer whose value is (this << n). `n` should be in 0..255 range (inclusive). */ def shiftLeft(n: Int): BigInt /** - * @return a 256-bit signed integer whose value is (this >> n). Sign extension is performed. The shift distance, n, - * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).) + * @return a 256-bit signed integer whose value is (this >> n). `n` should be in 0..255 range (inclusive). */ def shiftRight(n: Int): BigInt } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 6c401f8fc3..c980a22300 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -275,10 +275,10 @@ object SNumericTypeMethods extends MethodsContainer { """.stripMargin) /** Cost of inverting bits of a number. */ - val BitwiseInverse_CostKind = FixedCost(JitCost(5)) + val BitwiseOp_CostKind = FixedCost(JitCost(5)) val BitwiseInverseMethod: SMethod = SMethod( - this, "bitwiseInverse", SFunc(tNum, tNum), 8, BitwiseInverse_CostKind) + this, "bitwiseInverse", SFunc(tNum, tNum), 8, BitwiseOp_CostKind) .withIRInfo(MethodCallIrBuilder) .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => m.objType match { @@ -292,7 +292,7 @@ object SNumericTypeMethods extends MethodsContainer { .withInfo(PropertyCall, desc = "Returns bitwise inverse of this numeric. ") val BitwiseOrMethod: SMethod = SMethod( - this, "bitwiseOr", SFunc(Array(tNum, tNum), tNum), 9, BitwiseInverse_CostKind) + this, "bitwiseOr", SFunc(Array(tNum, tNum), tNum), 9, BitwiseOp_CostKind) .withIRInfo(MethodCallIrBuilder) .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => m.objType match { @@ -308,7 +308,7 @@ object SNumericTypeMethods extends MethodsContainer { ArgInfo("that", "A numeric value to calculate or with.")) val BitwiseAndMethod: SMethod = SMethod( - this, "bitwiseAnd", SFunc(Array(tNum, tNum), tNum), 10, BitwiseInverse_CostKind) + this, "bitwiseAnd", SFunc(Array(tNum, tNum), tNum), 10, BitwiseOp_CostKind) .withIRInfo(MethodCallIrBuilder) .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => m.objType match { @@ -324,7 +324,7 @@ object SNumericTypeMethods extends MethodsContainer { ArgInfo("that", "A numeric value to calculate and with.")) val BitwiseXorMethod: SMethod = SMethod( - this, "bitwiseXor", SFunc(Array(tNum, tNum), tNum), 11, BitwiseInverse_CostKind) + this, "bitwiseXor", SFunc(Array(tNum, tNum), tNum), 11, BitwiseOp_CostKind) .withIRInfo(MethodCallIrBuilder) .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => m.objType match { @@ -340,7 +340,7 @@ object SNumericTypeMethods extends MethodsContainer { ArgInfo("that", "A numeric value to calculate xor with.")) val ShiftLeftMethod: SMethod = SMethod( - this, "shiftLeft", SFunc(Array(tNum, SInt), tNum), 12, BitwiseInverse_CostKind) + this, "shiftLeft", SFunc(Array(tNum, SInt), tNum), 12, BitwiseOp_CostKind) .withIRInfo(MethodCallIrBuilder) .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => m.objType match { @@ -359,7 +359,7 @@ object SNumericTypeMethods extends MethodsContainer { "the size of the number in bits (e.g. 64 for Long, 256 for BigInt)")) val ShiftRightMethod: SMethod = SMethod( - this, "shiftRight", SFunc(Array(tNum, SInt), tNum), 13, BitwiseInverse_CostKind) + this, "shiftRight", SFunc(Array(tNum, SInt), tNum), 13, BitwiseOp_CostKind) .withIRInfo(MethodCallIrBuilder) .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => m.objType match { diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 2bd7fbe341..86a9bfffce 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -61,8 +61,7 @@ object ExactIntegral { implicit object ShortIsExactIntegral extends ExactIntegral[Short] { val n = scala.math.Numeric.ShortIsIntegral - override def plus(x: Short, y: Short): - Short = x.addExact(y) + override def plus(x: Short, y: Short): Short = x.addExact(y) override def minus(x: Short, y: Short): Short = x.subtractExact(y) override def times(x: Short, y: Short): Short = x.multiplyExact(y) override def toBigEndianBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index a5d73d2442..6d21dd9edb 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -569,6 +569,22 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Int.toBits") { + def toBytesTest() = test("Int.toBytes", env, ext, + """{ + | val l = 1477959696 + | l.toBits == Coll(false, true, false, true, true, false, false, false, false, false, false, true, false, true, true ,true, true, true, true, false, false, false, false, false, false, false, false, true, false, false, false, false) + | }""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[Exception] shouldBe thrownBy(toBytesTest()) + } + } + property("Byte.toBytes") { def toBytesTest() = test("Byte.toBytes", env, ext, """{