diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 29d917aed5..497273d2db 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. @@ -154,6 +152,22 @@ trait BigInt { */ def or(that: BigInt): BigInt def |(that: BigInt): BigInt = or(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). `n` should be in 0..255 range (inclusive). + */ + def shiftLeft(n: Int): BigInt + + /** + * @return a 256-bit signed integer whose value is (this >> n). `n` should be in 0..255 range (inclusive). + */ + def shiftRight(n: Int): BigInt } /** Base class for points on elliptic curves. */ diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 46a639e37c..d289f4067a 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -113,27 +113,48 @@ 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 - * `toMap` operation. As a result, the methods collected into SByte.methods cannot be + * @note on versioning: + * In v3.x-5.x SNumericType.typeId is silently shadowed by SGlobal.typeId as part of + * `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. - * 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. + * 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 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 - lazy val types: Map[Byte, STypeCompanion] = Seq( - SBoolean, SNumericType, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, + private val v5Types = Seq( + SBoolean, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, SAvlTree, SBox, SOption, SCollection, SBigInt - ).map { t => (t.typeId, t) }.toMap + ) + 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. @@ -452,7 +473,7 @@ case object SLong extends SPrimType with SEmbeddable with SNumericType with SMon } } -/** Type of 256 bit integet values. Implemented using [[java.math.BigInteger]]. */ +/** Type of 256 bit integer values. Implemented using [[java.math.BigInteger]]. */ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SMonoType { override type WrappedType = BigInt override val typeCode: TypeCode = 6: Byte 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/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index bbf1a85e46..ea69174877 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -49,4 +49,10 @@ 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)) + + override def shiftLeft(n: Int): BigInt = CBigInt(wrappedValue.shiftLeft(n).to256BitValueExact) + + override def shiftRight(n: Int): BigInt = CBigInt(wrappedValue.shiftRight(n).to256BitValueExact) } 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 5ae0da9cf8..77758320f1 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -7,6 +7,8 @@ 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.NumericOps.BigIntIsExactIntegral import sigma.data.OverloadHack.Overloaded1 import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} @@ -157,14 +159,34 @@ trait MonoTypeMethods extends MethodsContainer { trait SNumericTypeMethods extends MonoTypeMethods { import SNumericTypeMethods.tNum + + private val subst = Map(tNum -> this.ownerType) + + val v5Methods = { + SNumericTypeMethods.v5Methods.map { m => + m.copy(stype = applySubst(m.stype, subst).asFunc) + } + } + + val v6Methods = { + SNumericTypeMethods.v6Methods.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] = { - super.getMethods() ++ SNumericTypeMethods.methods.map { - m => m.copy(stype = applySubst(m.stype, Map(tNum -> this.ownerType)).asFunc) + if (VersionContext.current.isV6SoftForkActivated) { + super.getMethods() ++ v6Methods + } else { + super.getMethods() ++ v5Methods } } } object SNumericTypeMethods extends MethodsContainer { + /** Type for which this container defines methods. */ override def ownerType: STypeCompanion = SNumericType @@ -217,6 +239,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 @@ -230,12 +261,124 @@ 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. """.stripMargin) - protected override def getMethods(): Seq[SMethod] = Array( + /** Cost of inverting bits of a number. */ + val BitwiseOp_CostKind = FixedCost(JitCost(5)) + + val BitwiseInverseMethod: SMethod = SMethod( + this, "bitwiseInverse", SFunc(tNum, tNum), 8, BitwiseOp_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, desc = "Returns bitwise inverse of this numeric. ") + + val BitwiseOrMethod: SMethod = SMethod( + this, "bitwiseOr", SFunc(Array(tNum, tNum), tNum), 9, BitwiseOp_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(MethodCall, + """ 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, BitwiseOp_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + 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(MethodCall, + """ 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, BitwiseOp_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(MethodCall, + """ 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, BitwiseOp_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(MethodCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.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, BitwiseOp_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + 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(MethodCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.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 ToShortMethod, // see Downcast ToIntMethod, // see Downcast @@ -245,7 +388,21 @@ object SNumericTypeMethods extends MethodsContainer { ToBitsMethod ) + lazy val v6Methods = v5Methods ++ Array( + BitwiseInverseMethod, + BitwiseOrMethod, + BitwiseAndMethod, + BitwiseXorMethod, + ShiftLeftMethod, + ShiftRightMethod + ) + + protected override def getMethods(): Seq[SMethod] = { + throw new Exception("SNumericTypeMethods.getMethods shouldn't ever be called") + } + /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ + // todo: add unsigned big int val castMethods: Array[String] = Array(ToByteMethod, ToShortMethod, ToIntMethod, ToLongMethod, ToBigIntMethod) .map(_.name) @@ -310,21 +467,6 @@ case object SBigIntMethods extends SNumericTypeMethods { /** Type for which this container defines methods. */ override def ownerType: SMonoType = SBigInt - /** The following `modQ` methods are not fully implemented in v4.x and this descriptors. - * This descritors are remain here in the code and are waiting for full implementation - * is upcoming soft-forks at which point the cost parameters should be calculated and - * changed. - */ - val ModQMethod = SMethod(this, "modQ", SFunc(this.ownerType, SBigInt), 1, FixedCost(JitCost(1))) - .withInfo(ModQ, "Returns this \\lst{mod} Q, i.e. remainder of division by Q, where Q is an order of the cryprographic group.") - val PlusModQMethod = SMethod(this, "plusModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 2, FixedCost(JitCost(1))) - .withInfo(ModQArithOp.PlusModQ, "Adds this number with \\lst{other} by module Q.", ArgInfo("other", "Number to add to this.")) - val MinusModQMethod = SMethod(this, "minusModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 3, FixedCost(JitCost(1))) - .withInfo(ModQArithOp.MinusModQ, "Subtracts \\lst{other} number from this by module Q.", ArgInfo("other", "Number to subtract from this.")) - val MultModQMethod = SMethod(this, "multModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 4, FixedCost(JitCost(1))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) - protected override def getMethods(): Seq[SMethod] = { if (VersionContext.current.isV6SoftForkActivated) { super.getMethods() @@ -338,12 +480,6 @@ case object SBigIntMethods extends SNumericTypeMethods { } } - /** - * - */ - def nbits_eval(mc: MethodCall, bi: sigma.BigInt)(implicit E: ErgoTreeEvaluator): Long = { - ??? - } } diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 168b2f8266..8d272439f4 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,32 @@ 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 toBigEndianBytes(x: BigInt): Coll[Byte] = Colls.fromArray(x.toBigInteger.toByteArray) + + override def bitwiseInverse(x: BigInt): BigInt = CBigInt(x.toBigInteger.not()) + + override def bitwiseOr(x: BigInt, y: BigInt): BigInt = x.or(y) + + override def bitwiseAnd(x: BigInt, y: BigInt): BigInt = x.and(y) + + override def bitwiseXor(x: BigInt, y: BigInt): BigInt = x.xor(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 34e2f47f63..86a9bfffce 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,25 @@ 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 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 + override def bitwiseXor(x: Byte, y: Byte): Byte = (x ^ y).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) { + throw new IllegalArgumentException(s"Wrong argument in Byte.shiftRight: bits < 0 || bits >= 8 ($bits)") + } else { + (x >> bits).toByte + } + } } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -44,6 +64,25 @@ 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 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 + override def bitwiseXor(x: Short, y: Short): 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)") + } else { + (x >> bits).toShort + } + } } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -51,6 +90,28 @@ 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 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 + 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, 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) { + throw new IllegalArgumentException(s"Wrong argument in Int.shiftRight: bits < 0 || bits >= 32 ($bits)") + } else { + x >> bits + } + } } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -58,5 +119,27 @@ 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 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 + 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, 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) { + throw new IllegalArgumentException(s"Wrong argument in Long.shiftRight: bits < 0 || bits >= 64 ($bits)") + } else { + 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 2e9b799a61..244cc6b7b7 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -1,5 +1,7 @@ package sigma.data +import debox.cfor +import sigma.{Coll, Colls} import sigma.data.ExactIntegral._ /** Numeric operations with overflow checks. @@ -30,6 +32,63 @@ 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 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 + + val bytes = toBigEndianBytes(x) + val l = bytes.length + val res = new Array[Boolean](l * 8) + cfor(0)(_ < l, _ + 1) { i => + val b = bytes(i) + cfor(0)(_ < 8, _ + 1) { bitIdx => + res(i * 8 + (7 - bitIdx)) = isBitSet(b)(bitIdx) + } + } + 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. */ lazy val zero: T = fromInt(0) 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/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 8fbec75dfb..d4b2abed89 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.SType.tT import sigma.ast.TypeCodes.LastConstantCode import sigma.ast.Value.Typed @@ -438,8 +439,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 { @@ -986,7 +987,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 => @@ -1000,7 +1001,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 => @@ -1013,7 +1014,7 @@ 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 => @@ -1022,7 +1023,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val c1 = asRep[Int](argsV(0)) val c2 = stypeToElem(typeSubst.apply(tT)) box.getReg(c1)(c2) - case _ => throwError + case _ => throwError() } case (ctx: Ref[Context]@unchecked, SContextMethods) => method.name match { case SContextMethods.dataInputsMethod.name => @@ -1045,7 +1046,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 => @@ -1092,7 +1093,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 => @@ -1109,7 +1110,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 => @@ -1144,7 +1145,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => h.votes case SHeaderMethods.checkPowMethod.name if VersionContext.current.isV6SoftForkActivated => h.checkPow - case _ => throwError + case _ => throwError() } case (g: Ref[SigmaDslBuilder]@unchecked, SGlobalMethods) => method.name match { case SGlobalMethods.groupGeneratorMethod.name => @@ -1156,13 +1157,45 @@ 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 _ => throwError + case (x: Ref[tNum], _: SNumericTypeMethods) => method.name match { + 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 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 SNumericTypeMethods.BitwiseAndMethod.name => + 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 SNumericTypeMethods.ShiftLeftMethod.name => + 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") } case _ => - throwError + throwError() } val resC = asRep[T#WrappedType](res) resC 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 7b73999253..aed197843c 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,27 @@ 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 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 _: 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 _: 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 } } @@ -179,7 +200,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) @@ -192,6 +217,37 @@ 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(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(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(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(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) 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 4e732bbb5f..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 @@ -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. */ @@ -46,6 +47,27 @@ 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) + } + + 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) + } + + case class NumericShiftLeft[T: Elem](n: ExactNumeric[T]) extends BinDiffArgsOp[T, Int]("<<") { + 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 @@ -66,6 +88,28 @@ 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] = { + liftableColl(Liftables.ByteIsLiftable).lift(n.toBigEndianBytes(x)) + } + } + + /** 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] = { + liftableColl(Liftables.BooleanIsLiftable).lift(n.toBits(x)) + } + } + + /** 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) + } + /** 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/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala index 23f8bc3800..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,30 @@ 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 + + /** 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 +92,19 @@ 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 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 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)) + } + /** 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/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index 2c532c88d9..bd86feb82b 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -11,6 +11,7 @@ import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId import sigma.Extensions.{ArrayOps, CollOps} import sigma.VersionContext.V6SoftForkVersion +import sigma.VersionContext.JitActivationVersion import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} import sigma.ast.SCollection._ import sigma.ast.syntax._ @@ -956,6 +957,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SByte), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(_ >= _) } + 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 28b3ed873e..7605043cea 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -2,23 +2,26 @@ package sigma import org.ergoplatform.ErgoHeader import scorex.util.encode.Base16 +import sigma.VersionContext.V6SoftForkVersion import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.Token import scorex.util.ModifierId +import scorex.utils.{Ints, Longs, 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.ast.{SInt, _} +import sigma.data.{CBigInt, CBox, CHeader, ExactNumeric} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode import sigma.data.{RType} 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 -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. @@ -103,7 +106,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Boolean.toByte") { val toByte = newFeature((x: Boolean) => x.toByte, "{ (x: Boolean) => x.toByte }", - sinceVersion = VersionContext.V6SoftForkVersion + sinceVersion = V6SoftForkVersion ) val cases = Seq( @@ -122,129 +125,803 @@ 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 = VersionContext.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 = VersionContext.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 = VersionContext.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 = VersionContext.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 + ) - // 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 + 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) - lazy val toAbs = newFeature((x: Short) => x.toAbs, "{ (x: Short) => x.toAbs }", - sinceVersion = VersionContext.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 compareTo = newFeature((x: (Short, Short)) => x._1.compareTo(x._2), - "{ (x: (Short, Short)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) + lazy val shiftLeft = newFeature( + { (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))), + 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) - lazy val bitOr = newFeature( + verifyCases( + Seq( + (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)) => 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))), + 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)), + (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()) + ) + } + + property("Short - 6.0 methods") { + + lazy val bitOr = newFeature( { (x: (Short, Short)) => (x._1 | x._2).toShortExact }, - "{ (x: (Short, Short)) => x._1 | x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + "{ (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) + + 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 bitAnd = newFeature( + 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) + + 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 }", - sinceVersion = VersionContext.V6SoftForkVersion) + "{ (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 => Colls.fromArray(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)) => 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))), + 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)), + ((-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)) => 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))), + 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)), + (3.toShort, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (3.toShort, 16) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) } - 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 = VersionContext.V6SoftForkVersion) - lazy val compareTo = newFeature((x: (Int, Int)) => x._1.compareTo(x._2), - "{ (x: (Int, Int)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) - lazy val bitOr = newFeature( - { (x: (Int, Int)) => x._1 | x._2 }, - "{ (x: (Int, Int)) => x._1 | x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) - lazy val bitAnd = newFeature( + 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) + + 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 }", - sinceVersion = VersionContext.V6SoftForkVersion) - forAll { x: Int => - Seq(toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (Int, Int) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } + "{ (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) + + 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 => Colls.fromArray(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)) => 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))), + 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)), + (-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)) => 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))), + 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)), + (-320019, 32) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (-320019, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + 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 = VersionContext.V6SoftForkVersion) - lazy val compareTo = newFeature((x: (Long, Long)) => x._1.compareTo(x._2), - "{ (x: (Long, Long)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) - - lazy val bitOr = newFeature( - { (x: (Long, Long)) => x._1 | x._2 }, - "{ (x: (Long, Long)) => x._1 | x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) - - lazy val bitAnd = newFeature( - { (x: (Long, Long)) => x._1 & x._2 }, - "{ (x: (Long, Long)) => x._1 & x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) + property("Long - 6.0 methods") { - forAll { x: Long => - Seq(toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (Long, Long) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } + 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) + + 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 + ) + + 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 => Colls.fromArray(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)) => 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))), + 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)), + (-222L, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (-222L, 64) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftLeft, + preGeneratedSamples = Some(Seq()) + ) + + 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)") { + 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 @@ -259,50 +936,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 = VersionContext.V6SoftForkVersion) - val toShort = newFeature((x: BigInt) => x.toShort, - "{ (x: BigInt) => x.toShort }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SShort)), - sinceVersion = VersionContext.V6SoftForkVersion) - val toInt = newFeature((x: BigInt) => x.toInt, - "{ (x: BigInt) => x.toInt }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SInt)), - sinceVersion = VersionContext.V6SoftForkVersion) - val toLong = newFeature((x: BigInt) => x.toLong, - "{ (x: BigInt) => x.toLong }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SLong)), - sinceVersion = VersionContext.V6SoftForkVersion) - lazy val toAbs = newFeature((x: BigInt) => x.toAbs, "{ (x: BigInt) => x.toAbs }", - sinceVersion = VersionContext.V6SoftForkVersion) - lazy val compareTo = newFeature((x: (BigInt, BigInt)) => x._1.compareTo(x._2), - "{ (x: (BigInt, BigInt)) => x._1.compareTo(x._2) }", - sinceVersion = VersionContext.V6SoftForkVersion) - lazy val bitOr = newFeature({ (x: (BigInt, BigInt)) => x._1 | x._2 }, - "{ (x: (BigInt, BigInt)) => x._1 | x._2 }", - sinceVersion = VersionContext.V6SoftForkVersion) - lazy val bitAnd = newFeature({ (x: (BigInt, BigInt)) => x._1 & x._2 }, - "{ (x: (BigInt, BigInt)) => x._1 & x._2 }", - sinceVersion = VersionContext.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( @@ -329,6 +962,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 } @@ -342,6 +979,212 @@ 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 => x.toBytes.flatMap(b => Colls.fromArray(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)) => 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))), + 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)), + (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()) + ) + + 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)), + (CBigInt(BigInteger.valueOf(24)), 256) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) } property("Box properties equivalence (new features)") { @@ -352,9 +1195,9 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Array((1, SBox)), OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R0, SOption(SLong))) ), - sinceVersion = VersionContext.V6SoftForkVersion) + sinceVersion = V6SoftForkVersion) - if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + if (activatedVersionInTests < V6SoftForkVersion) { // NOTE, for such versions getReg is not supported // which is checked below @@ -378,7 +1221,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 = V6SoftForkVersion) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -397,7 +1240,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 = V6SoftForkVersion) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -413,7 +1256,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 = V6SoftForkVersion) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -430,7 +1273,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 = V6SoftForkVersion) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -446,7 +1289,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 = V6SoftForkVersion) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -462,7 +1305,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 = V6SoftForkVersion) if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions getReg is not supported @@ -474,6 +1317,28 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => } } + property("Numeric.toBytes methods equivalence") { + lazy val toBytes = newFeature( + { (x: Byte) => x.toBigEndianBytes }, + "{ (x: Byte) => x.toBytes }", + FuncValue( + Array((1, SByte)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SByte), + SByteMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + val cases = Seq( + (0.toByte, Success(Coll(0.toByte))), + (1.toByte, Success(Coll(1.toByte))) + ) + + testCases(cases, toBytes) + } + property("Fix substConstants in v6.0 for ErgoTree version > 0") { // tree with one segregated constant and v0 val t1 = ErgoTree( @@ -501,7 +1366,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ast.SeqCostItem(CompanionDesc(SubstConstants), PerItemCost(JitCost(100), JitCost(100), 1), nItems) ) ) - val expectedTreeBytes_beforeV6 = Helpers.decodeBytes("1b0108d27300") val expectedTreeBytes_V6 = Helpers.decodeBytes("1b050108d27300") diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 551cd16273..2eb73428a2 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 } diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index df15125488..4403716e5a 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -317,41 +317,122 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C // 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 - def 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), - - (if(!isV6Activated) { - { // 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) + { + 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), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true) ), true) - } - } else { - // rechecking boolean again, after merging w. https://github.com/ScorexFoundation/sigmastate-interpreter/pull/1017 - // there will be check against numerics in 6.0 here - (SBoolean.typeId, Seq.empty[MInfo], 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), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true) + ), 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), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true) + ), 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), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true) + ), 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(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true) + ) else Seq.empty) + , true) }, { import SGroupElementMethods._ (SGroupElement.typeId, Seq( @@ -494,7 +575,13 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C MInfo(8, FilterMethod) ), true) } - ) + ) + } + + 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) => @@ -505,6 +592,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C val mc = MethodsContainer(tyDesc.typeId) mc.methods.length shouldBe methods.length + for (expectedMethod <- methods) { if (expectedMethod.isResolvableFromIds) { @@ -528,21 +616,28 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C 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 + } } } } diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 8aeefba8a7..5992259628 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -519,6 +519,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.ToBytesMethod.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.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SShort)), + Vector(), + Map() + )) shouldBe SByteArray + + typecheck(env, "1.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + IntConstant(1), + SNumericTypeMethods.ToBytesMethod.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.ToBytesMethod.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.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SBigInt)), + Vector(), + Map() + )) shouldBe SByteArray + } + property("string concat") { typecheck(env, """ "a" + "b" """) shouldBe SString } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 4a5a9be59e..68fe1022e7 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 scorex.util.encode.Base16 import org.scalatest.Assertion import sigma.Extensions.ArrayOps @@ -141,6 +142,397 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } + property("Byte.toBits") { + def toBitsTest() = test("Byte.toBits", env, ext, + """{ + | val b = 1.toByte + | b.toBits == Coll(false, false, false, false, false, false, false, true) + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[Exception] shouldBe thrownBy(toBitsTest()) + } + } + + 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("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("Byte.bitwiseInverse") { + def bitwiseInverseTest(): Assertion = test("Byte.bitwiseInverse", env, ext, + s"""{ + | val b = (126 + 1).toByte // max byte value + | 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("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 bitwiseOrTest(): Assertion = test("BigInt.bitwiseOr", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | x.bitwiseOr(x) == x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseOrTest() + } else { + 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("Short.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()) + } + } + + 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("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) { + an[IllegalArgumentException] shouldBe thrownBy(shiftLeftTest()) + } else { + an[Exception] shouldBe thrownBy(shiftLeftTest()) + } + } + + 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"""{ + | 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("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) { + an[IllegalArgumentException] shouldBe thrownBy(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) { + an[IllegalArgumentException] shouldBe thrownBy(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) { + an[IllegalArgumentException] shouldBe thrownBy(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 @@ -163,6 +555,71 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Int.toBytes") { + def toBytesTest() = test("Int.toBytes", env, ext, + """{ + | 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("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, + """{ + | 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 + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[Exception] shouldBe thrownBy(toBytesTest()) + } + } + property("serialize - byte array") { def deserTest() = test("serialize", env, ext, s"""{ @@ -213,7 +670,7 @@ class BasicOpsSpecification extends CompilerTestingCommons deserTest() } } - + property("serialize(long) is producing different result from longToByteArray()") { def deserTest() = test("serialize", env, ext, s"""{