From 3d47f2ac29e975de1754b46ddf210708d8eee088 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 24 May 2024 20:50:25 +0300 Subject: [PATCH 01/78] initial raw, buggy & unversioned unsigned bit int impl --- .../main/scala/sigma/crypto/Platform.scala | 1 + .../src/main/scala/sigma/Evaluation.scala | 2 + .../src/main/scala/sigma/SigmaDsl.scala | 140 +++++++++++++++- .../src/main/scala/sigma/ast/SType.scala | 45 +++++- .../src/main/scala/sigma/data/CBigInt.scala | 43 ++++- .../src/main/scala/sigma/data/package.scala | 1 + .../shared/src/main/scala/sigma/package.scala | 1 + .../serialization/CoreDataSerializer.scala | 11 ++ .../sigma/serialization/TypeSerializer.scala | 3 +- .../main/scala/sigma/ast/SigmaPredef.scala | 12 ++ .../src/main/scala/sigma/ast/trees.scala | 4 +- .../src/main/scala/sigma/ast/values.scala | 16 +- .../scala/sigma/data/CSigmaDslBuilder.scala | 4 +- .../main/scala/sigma/data/ExactIntegral.scala | 2 +- .../sigma/data/UnsignedBigIntegerOps.scala | 98 +++++++++++ .../main/scala/sigma/eval/Extensions.scala | 3 +- .../crypto/BigIntSpecification.scala | 9 ++ .../scala/sigmastate/eval/GraphBuilding.scala | 15 +- .../scala/special/sigma/SigmaDslUnit.scala | 9 ++ .../special/sigma/impl/SigmaDslImpl.scala | 153 ++++++++++++++++++ .../utxo/BasicOpsSpecification.scala | 114 ++++++++++++- .../OracleExamplesSpecification.scala | 2 +- 22 files changed, 668 insertions(+), 20 deletions(-) create mode 100644 data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala create mode 100644 interpreter/shared/src/test/scala/sigmastate/crypto/BigIntSpecification.scala diff --git a/core/jvm/src/main/scala/sigma/crypto/Platform.scala b/core/jvm/src/main/scala/sigma/crypto/Platform.scala index b71694e81b..13c8d6515e 100644 --- a/core/jvm/src/main/scala/sigma/crypto/Platform.scala +++ b/core/jvm/src/main/scala/sigma/crypto/Platform.scala @@ -185,6 +185,7 @@ object Platform { case _: Int => tpe == SInt case _: Long => tpe == SLong case _: BigInt => tpe == SBigInt + case _: UnsignedBigInt => tpe == SUnsignedBigInt case _: String => tpe == SString // TODO v6.0: remove this case (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) case _: GroupElement => tpe.isGroupElement case _: SigmaProp => tpe.isSigmaProp diff --git a/core/shared/src/main/scala/sigma/Evaluation.scala b/core/shared/src/main/scala/sigma/Evaluation.scala index d86b7c1650..c3ffcc8896 100644 --- a/core/shared/src/main/scala/sigma/Evaluation.scala +++ b/core/shared/src/main/scala/sigma/Evaluation.scala @@ -25,6 +25,7 @@ object Evaluation { case SAny => AnyType case SUnit => UnitType case SBigInt => BigIntRType + case SUnsignedBigInt => UnsignedBigIntRType case SBox => BoxRType case SContext => ContextRType case SGlobal => SigmaDslBuilderRType @@ -67,6 +68,7 @@ object Evaluation { case AnyType => SAny case UnitType => SUnit case BigIntRType => SBigInt + case UnsignedBigIntRType => SUnsignedBigInt case GroupElementRType => SGroupElement case AvlTreeRType => SAvlTree case ot: OptionType[_] => SOption(rtypeToSType(ot.tA)) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index df2b419273..cc102b6b36 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -5,10 +5,8 @@ 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. - * */ + * Base class for signed 256-bits integers + */ trait BigInt { /** Convert this BigInt value to Byte. * @throws ArithmeticException if overflow happens. @@ -156,6 +154,138 @@ trait BigInt { def |(that: BigInt): BigInt = or(that) } + +trait UnsignedBigInt { + /** Convert this BigInt value to Byte. + * @throws ArithmeticException if overflow happens. + */ + def toByte: Byte + + /** Convert this BigInt value to Short. + * @throws ArithmeticException if overflow happens. + */ + def toShort: Short + + /** Convert this BigInt value to Int. + * @throws ArithmeticException if overflow happens. + */ + def toInt: Int + + /** Convert this BigInt value to Int. + * @throws ArithmeticException if overflow happens. + */ + def toLong: Long + + /** Returns a big-endian representation of this BigInt in a collection of bytes. + * For example, the value {@code 0x1213141516171819} would yield the + * byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}}. + * @since 2.0 + */ + def toBytes: Coll[Byte] + + + /** Compares this numeric with that numeric for order. Returns a negative integer, zero, or a positive integer as the + * `this` is less than, equal to, or greater than `that`. + */ + def compareTo(that: UnsignedBigInt): Int + + /** Returns a BigInt whose value is {@code (this + that)}. + * + * @param that value to be added to this BigInt. + * @return { @code this + that} + */ + def add(that: UnsignedBigInt): UnsignedBigInt + def +(that: UnsignedBigInt): UnsignedBigInt = add(that) + + /** Returns a BigInt whose value is {@code (this - that)}. + * + * @param that value to be subtracted from this BigInt. + * @return { @code this - that} + */ + def subtract(that: UnsignedBigInt): UnsignedBigInt + + def -(that: UnsignedBigInt): UnsignedBigInt = subtract(that) + + /** Returns a BigInt whose value is {@code (this * that)}. + * + * @implNote An implementation may offer better algorithmic + * performance when { @code that == this}. + * @param that value to be multiplied by this BigInt. + * @return { @code this * that} + */ + def multiply(that: UnsignedBigInt): UnsignedBigInt + def *(that: UnsignedBigInt): UnsignedBigInt = multiply(that) + + /** Returns a BigInt whose value is {@code (this / that)}. + * + * @param that value by which this BigInt is to be divided. + * @return { @code this / that} + * @throws ArithmeticException if { @code that} is zero. + */ + def divide(that: UnsignedBigInt): UnsignedBigInt + def /(that: UnsignedBigInt): UnsignedBigInt = divide(that) + + /** + * Returns a BigInt whose value is {@code (this mod m}). This method + * differs from {@code remainder} in that it always returns a + * non-negative BigInteger. + * + * @param m the modulus. + * @return { @code this mod m} + * @throws ArithmeticException { @code m} ≤ 0 + * @see #remainder + */ + def mod(m: UnsignedBigInt): UnsignedBigInt + def %(m: UnsignedBigInt): UnsignedBigInt = mod(m) + + /** + * Returns a BigInt whose value is {@code (this % that)}. + * + * @param that value by which this BigInt is to be divided, and the + * remainder computed. + * @return { @code this % that} + * @throws ArithmeticException if { @code that} is zero. + */ + def remainder(that: UnsignedBigInt): UnsignedBigInt + + /** + * Returns the minimum of this BigInteger and {@code val}. + * + * @param that value with which the minimum is to be computed. + * @return the BigInteger whose value is the lesser of this BigInteger and + * { @code val}. If they are equal, either may be returned. + */ + def min(that: UnsignedBigInt): UnsignedBigInt + + /** + * Returns the maximum of this BigInteger and {@code val}. + * + * @param that value with which the maximum is to be computed. + * @return the BigInteger whose value is the greater of this and + * { @code val}. If they are equal, either may be returned. + */ + def max(that: UnsignedBigInt): UnsignedBigInt + + /** Returns a BigInteger whose value is `(this & that)`. + * @param that value to be AND'ed with this BigInteger. + * @return `this & that` + */ + def and(that: UnsignedBigInt): UnsignedBigInt + def &(that: UnsignedBigInt): UnsignedBigInt = and(that) + + /** Returns a BigInteger whose value is `(this | that)`. (This + * method returns a negative BigInteger if and only if either `this` or `that`` is + * negative.) + * + * @param that value to be OR'ed with this BigInteger. + * @return `this | that` + */ + def or(that: UnsignedBigInt): UnsignedBigInt + def |(that: UnsignedBigInt): UnsignedBigInt = or(that) +} + + + /** Base class for points on elliptic curves. */ trait GroupElement { /** Checks if the provided element is an identity element. */ @@ -721,6 +851,8 @@ trait SigmaDslBuilder { /** Create DSL big integer from existing `java.math.BigInteger`*/ def BigInt(n: BigInteger): BigInt + def UnsignedBigInt(n: BigInteger): UnsignedBigInt + /** Extract `java.math.BigInteger` from DSL's `BigInt` type*/ def toBigInteger(n: BigInt): BigInteger diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index f75cbc9e8b..50763ba56e 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -4,10 +4,10 @@ import sigma.Evaluation.stypeToRType import sigma.ast.SCollection.SByteArray import sigma.ast.SType.TypeCode import sigma.data.OverloadHack.Overloaded1 -import sigma.data.{CBigInt, Nullable, SigmaConstants} +import sigma.data.{CBigInt, CUnsignedBigInt, Nullable, SigmaConstants} import sigma.reflection.{RClass, RMethod, ReflectionData} import sigma.util.Extensions.{IntOps, LongOps, ShortOps} -import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp} +import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp, UnsignedBigInt} import java.math.BigInteger @@ -156,6 +156,7 @@ object SType { case SInt => x.isInstanceOf[Int] case SLong => x.isInstanceOf[Long] case SBigInt => x.isInstanceOf[BigInt] + case SUnsignedBigInt => x.isInstanceOf[UnsignedBigInt] case SGroupElement => x.isInstanceOf[GroupElement] case SSigmaProp => x.isInstanceOf[SigmaProp] case SBox => x.isInstanceOf[Box] @@ -448,13 +449,12 @@ 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 signed 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 override val reprClass: RClass[_] = RClass(classOf[BigInt]) override def typeId = typeCode - implicit def typeBigInt: SBigInt.type = this /** Type of Relation binary op like GE, LE, etc. */ val RelationOpType = SFunc(Array(SBigInt, SBigInt), SBoolean) @@ -486,6 +486,43 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM } } +/** Type of 256-bit unsigned integer values. Implemented using [[java.math.BigInteger]]. */ +case object SUnsignedBigInt extends SPrimType with SEmbeddable with SNumericType with SMonoType { + override type WrappedType = UnsignedBigInt + override val typeCode: TypeCode = 9: Byte + override val reprClass: RClass[_] = RClass(classOf[BigInt]) + override def typeId = typeCode + + /** Type of Relation binary op like GE, LE, etc. */ + val RelationOpType = SFunc(Array(SUnsignedBigInt, SUnsignedBigInt), SBoolean) + + /** The maximum size of BigInteger value in byte array representation. */ + val MaxSizeInBytes: Long = SigmaConstants.MaxBigIntSizeInBytes.value // todo: 256 bits or more? + + override def numericTypeIndex: Int = 5 + + override def upcast(v: AnyVal): UnsignedBigInt = { + val bi = v match { + case x: Byte => BigInteger.valueOf(x.toLong) + case x: Short => BigInteger.valueOf(x.toLong) + case x: Int => BigInteger.valueOf(x.toLong) + case x: Long => BigInteger.valueOf(x) + case _ => sys.error(s"Cannot upcast value $v to the type $this") + } + CUnsignedBigInt(bi) + } + override def downcast(v: AnyVal): UnsignedBigInt = { + val bi = v match { + case x: Byte => BigInteger.valueOf(x.toLong) + case x: Short => BigInteger.valueOf(x.toLong) + case x: Int => BigInteger.valueOf(x.toLong) + case x: Long => BigInteger.valueOf(x) + case _ => sys.error(s"Cannot downcast value $v to the type $this") + } + CUnsignedBigInt(bi) + } +} + /** Descriptor of type `String` which is not used in ErgoTree, but used in ErgoScript. * NOTE: this descriptor both type and type companion */ case object SString extends SProduct with SMonoType { diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index bbf1a85e46..5ae617feba 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -1,7 +1,7 @@ package sigma.data import sigma.util.Extensions.BigIntegerOps -import sigma.{BigInt, Coll, Colls} +import sigma.{BigInt, Coll, Colls, UnsignedBigInt} import java.math.BigInteger @@ -50,3 +50,44 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr override def or(that: BigInt): BigInt = CBigInt(wrappedValue.or(that.asInstanceOf[CBigInt].wrappedValue)) } + +/** A default implementation of [[BigInt]] interface. + * + * @see [[BigInt]] for detailed descriptions + */ +case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends UnsignedBigInt with WrapperOf[BigInteger] { + + override def toByte: Byte = wrappedValue.toByteExact + + override def toShort: Short = wrappedValue.toShortExact + + override def toInt: Int = wrappedValue.toIntExact + + override def toLong: Long = wrappedValue.toLongExact + + override def toBytes: Coll[Byte] = Colls.fromArray(wrappedValue.toByteArray) + + override def compareTo(that: UnsignedBigInt): Int = + wrappedValue.compareTo(that.asInstanceOf[CUnsignedBigInt].wrappedValue) + + //todo: consider result's bits limit + override def add(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.add(that.asInstanceOf[CUnsignedBigInt].wrappedValue).to256BitValueExact) + + override def subtract(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.subtract(that.asInstanceOf[CUnsignedBigInt].wrappedValue).to256BitValueExact) + + override def multiply(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.multiply(that.asInstanceOf[CUnsignedBigInt].wrappedValue).to256BitValueExact) + + override def divide(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.divide(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) + + override def mod(m: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.mod(m.asInstanceOf[CUnsignedBigInt].wrappedValue)) + + override def remainder(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.remainder(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) + + override def min(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.min(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) + + override def max(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.max(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) + + override def and(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.and(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) + + override def or(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.or(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) +} diff --git a/core/shared/src/main/scala/sigma/data/package.scala b/core/shared/src/main/scala/sigma/data/package.scala index c5a35f7b5f..58870c0888 100644 --- a/core/shared/src/main/scala/sigma/data/package.scala +++ b/core/shared/src/main/scala/sigma/data/package.scala @@ -14,6 +14,7 @@ package object data { val StringClassTag = classTag[String] val BigIntClassTag = classTag[BigInt] + val UnsignedBigIntClassTag = classTag[UnsignedBigInt] val GroupElementClassTag = classTag[GroupElement] val SigmaPropClassTag = classTag[SigmaProp] val SigmaBooleanClassTag = classTag[SigmaBoolean] diff --git a/core/shared/src/main/scala/sigma/package.scala b/core/shared/src/main/scala/sigma/package.scala index 89b883f52d..4473bd338f 100644 --- a/core/shared/src/main/scala/sigma/package.scala +++ b/core/shared/src/main/scala/sigma/package.scala @@ -26,6 +26,7 @@ package object sigma { implicit val StringType : RType[String] = GeneralType(StringClassTag) implicit val BigIntRType : RType[BigInt] = GeneralType(BigIntClassTag) + implicit val UnsignedBigIntRType : RType[UnsignedBigInt] = GeneralType(UnsignedBigIntClassTag) implicit val GroupElementRType: RType[GroupElement] = GeneralType(GroupElementClassTag) implicit val SigmaPropRType : RType[SigmaProp] = GeneralType(SigmaPropClassTag) implicit val SigmaBooleanRType: RType[SigmaBoolean] = GeneralType(SigmaBooleanClassTag) diff --git a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala index 479b199da5..834edfb9a4 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala @@ -33,6 +33,10 @@ class CoreDataSerializer { val data = v.asInstanceOf[BigInt].toBigInteger.toByteArray w.putUShort(data.length) w.putBytes(data) + case SUnsignedBigInt => // todo: versioning + val data = v.asInstanceOf[CUnsignedBigInt].wrappedValue.toByteArray + w.putUShort(data.length) + w.putBytes(data) case SGroupElement => GroupElementSerializer.serialize(v.asInstanceOf[GroupElement].toECPoint, w) case SSigmaProp => @@ -103,6 +107,13 @@ class CoreDataSerializer { } val valueBytes = r.getBytes(size) CBigInt(new BigInteger(valueBytes)) + case SUnsignedBigInt => // todo: versioning + val size: Short = r.getUShort().toShort + if (size > SBigInt.MaxSizeInBytes + 1) { //todo: use encoding with no sign bit + throw new SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size") + } + val valueBytes = r.getBytes(size) + CUnsignedBigInt(new BigInteger(valueBytes)) case SGroupElement => CGroupElement(GroupElementSerializer.parse(r)) case SSigmaProp => diff --git a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala index 9c84de0944..f00421a31c 100644 --- a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala @@ -215,6 +215,7 @@ class TypeSerializer { object TypeSerializer extends TypeSerializer { /** The list of embeddable types, i.e. types that can be combined with type constructor for optimized encoding. * For each embeddable type `T`, and type constructor `C`, the type `C[T]` can be represented by single byte. */ - val embeddableIdToType = Array[SType](null, SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp) + //todo: versioning + val embeddableIdToType = Array[SType](null, SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp, SUnsignedBigInt) } \ No newline at end of file diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 631f7f2d75..d8c65a7afe 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -192,6 +192,17 @@ object SigmaPredef { Seq(ArgInfo("", ""))) ) + val UBigIntFromStringFunc = PredefinedFunc("unsignedBigInt", + Lambda(Array("input" -> SString), SUnsignedBigInt, None), + PredefFuncInfo( + { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => + UnsignedBigIntConstant(new BigInteger(arg.value)) + }), + OperationInfo(Constant, + """Parsing string literal argument as a 256-bit unsigned big integer.""".stripMargin, + Seq(ArgInfo("", ""))) + ) + val FromBase16Func = PredefinedFunc("fromBase16", Lambda(Array("input" -> SString), SByteArray, None), PredefFuncInfo( @@ -416,6 +427,7 @@ object SigmaPredef { GetVarFunc, DeserializeFunc, BigIntFromStringFunc, + UBigIntFromStringFunc, FromBase16Func, FromBase64Func, FromBase58Func, diff --git a/data/shared/src/main/scala/sigma/ast/trees.scala b/data/shared/src/main/scala/sigma/ast/trees.scala index 39e666a389..7be73ad55a 100644 --- a/data/shared/src/main/scala/sigma/ast/trees.scala +++ b/data/shared/src/main/scala/sigma/ast/trees.scala @@ -15,6 +15,7 @@ import sigma.serialization.CoreByteWriter.ArgInfo import sigma.validation.SigmaValidationSettings import sigma.{Coll, Colls, GroupElement, SigmaProp, VersionContext} import NumericOps.{BigIntIsExactIntegral, BigIntIsExactOrdering} +import sigma.data.UnsignedBigIntNumericOps.{UnsignedBigIntIsExactIntegral, UnsignedBigIntIsExactOrdering} import sigma.eval.ErgoTreeEvaluator.DataEnv import sigma.eval.Extensions.EvalCollOps import sigma.eval.{ErgoTreeEvaluator, SigmaDsl} @@ -875,7 +876,8 @@ object ArithOp { SShort -> new OperationImpl(ShortIsExactIntegral, ShortIsExactOrdering, SShort), SInt -> new OperationImpl(IntIsExactIntegral, IntIsExactOrdering, SInt), SLong -> new OperationImpl(LongIsExactIntegral, LongIsExactOrdering, SLong), - SBigInt -> new OperationImpl(BigIntIsExactIntegral, BigIntIsExactOrdering, SBigInt) + SBigInt -> new OperationImpl(BigIntIsExactIntegral, BigIntIsExactOrdering, SBigInt), + SUnsignedBigInt -> new OperationImpl(UnsignedBigIntIsExactIntegral, UnsignedBigIntIsExactOrdering, SUnsignedBigInt) ).map { case (t, n) => (t.typeCode, n) }) /** Returns operation name for the given opCode. */ diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala index 87c661a00a..8c55d27a94 100644 --- a/data/shared/src/main/scala/sigma/ast/values.scala +++ b/data/shared/src/main/scala/sigma/ast/values.scala @@ -8,7 +8,7 @@ import sigma.ast.TypeCodes.ConstantCode import sigma.ast.syntax._ import sigma.crypto.{CryptoConstants, EcPointType} import sigma.data.OverloadHack.Overloaded1 -import sigma.data.{CSigmaDslBuilder, CSigmaProp, Nullable, RType, SigmaBoolean} +import sigma.data.{CSigmaDslBuilder, CSigmaProp, CUnsignedBigInt, Nullable, RType, SigmaBoolean} import sigma.eval.ErgoTreeEvaluator.DataEnv import sigma.eval.{ErgoTreeEvaluator, SigmaDsl} import sigma.exceptions.InterpreterException @@ -499,6 +499,20 @@ object BigIntConstant { def apply(value: Long): Constant[SBigInt.type] = Constant[SBigInt.type](SigmaDsl.BigInt(BigInteger.valueOf(value)), SBigInt) } +object UnsignedBigIntConstant { + def apply(value: UnsignedBigInt): Constant[SUnsignedBigInt.type] = { + Constant[SUnsignedBigInt.type](value, SUnsignedBigInt) + } + + def apply(value: BigInteger): Constant[SUnsignedBigInt.type] = { + Constant[SUnsignedBigInt.type](CUnsignedBigInt(value), SUnsignedBigInt) + } + + def apply(value: Long): Constant[SUnsignedBigInt.type] = { + Constant[SUnsignedBigInt.type](CUnsignedBigInt(BigInteger.valueOf(value)), SUnsignedBigInt) + } +} + object StringConstant { def apply(value: String): Constant[SString.type] = Constant[SString.type](value, SString) diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 3938feacd3..4a3842e250 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -11,7 +11,7 @@ import sigma.eval.Extensions.EvalCollOps import sigma.serialization.{GroupElementSerializer, SigmaSerializer} import sigma.util.Extensions.BigIntegerOps import sigma.validation.SigmaValidationSettings -import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext} +import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, UnsignedBigInt, VersionContext} import java.math.BigInteger @@ -26,6 +26,8 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => override def BigInt(n: BigInteger): BigInt = CBigInt(n) + override def UnsignedBigInt(n: BigInteger): UnsignedBigInt = CUnsignedBigInt(n) + override def toBigInteger(n: BigInt): BigInteger = n.asInstanceOf[CBigInt].wrappedValue /** Wraps the given elliptic curve point into GroupElement type. */ diff --git a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 34e2f47f63..70158b828f 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -2,7 +2,7 @@ package sigma.data import sigma.util.Extensions.{ByteOps, ShortOps} -/** Type-class which defines the operations on Integral types (Byte, Short, Int, Long, BigInt) +/** Type-class which defines the operations on Integral types (Byte, Short, Int, Long, BigInt, UnsignedBigInt) * with overflow checks. * * An exception is raised when an overflow is detected. diff --git a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala new file mode 100644 index 0000000000..77b4a339e4 --- /dev/null +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -0,0 +1,98 @@ +package sigma.data + +import sigma._ +import sigma.data.UnsignedBigIntOrderingOps.UnsignedBigIntOrdering +import sigma.eval.Extensions.IntExt + +import scala.math.{Integral, Ordering} + +object UnsignedBigIntOrderingOps { + def apply[T](implicit ord: Ordering[T]) = ord + + trait UnsignedBigIntOrdering extends Ordering[UnsignedBigInt] { + def compare(x: UnsignedBigInt, y: UnsignedBigInt) = x.compareTo(y) + } + implicit object UnsignedBigIntOrdering extends UnsignedBigIntOrdering +} + +object UnsignedBigIntNumericOps { + + /** Base implementation of Integral methods for UnsignedBigInt. */ + trait UnsignedBigIntIsIntegral extends Integral[UnsignedBigInt] { + /** This method should not be used in v4.x */ + def quot(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.divide(y) + + /** This method is used in ErgoTreeEvaluator based interpreter, to implement + * '%' operation of ErgoTree (i.e. `%: (T, T) => T` operation) for all + * numeric types T including BigInt. + * + * In the v4.x interpreter, however, the `%` operation is implemented using + * [[CBigInt]].mod method , which delegates to [[java.math.BigInteger]].mod method. + * + * Even though this method is called `rem`, the semantics of ErgoTree + * language requires it to correspond to [[java.math.BigInteger]].mod + * method. + * + * For this reason we define implementation of this `rem` method using + * [[BigInt]].mod. + * + * NOTE: This method should not be used in v4.x + */ + def rem(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.mod(y) + + def plus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.add(y) + def minus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.subtract(y) + def times(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.multiply(y) + def negate(x: UnsignedBigInt): UnsignedBigInt = ??? + def fromInt(x: Int): UnsignedBigInt = x.toUnsignedBigInt + def toInt(x: UnsignedBigInt): Int = x.toInt + def toLong(x: UnsignedBigInt): Long = x.toLong + def toFloat(x: UnsignedBigInt): Float = ??? + def toDouble(x: UnsignedBigInt): Double = ??? + } + + /** The instance of Integral for BigInt. + * + * Note: ExactIntegral was not defined for [[sigma.BigInt]] in v4.x. + * This is because arithmetic BigInt operations were handled in a special way + * (see `case op: ArithOp[t] if op.tpe == SBigInt =>` in RuntimeCosting.scala). + * As result [[scalan.primitives.UnBinOps.ApplyBinOp]] nodes were not created for + * BigInt operations in v4.x., and hence operation descriptors such as + * [[scalan.primitives.NumericOps.IntegralDivide]] and + * [[scalan.primitives.NumericOps.IntegralMod]] were not used for BigInt. + * NOTE: this instance is used in the new v5.0 interpreter. + */ + object UnsignedBigIntIsIntegral extends UnsignedBigIntIsIntegral with UnsignedBigIntOrdering { + def parseString(str: String): Option[UnsignedBigInt] = ??? + } + + /** The instance of [[ExactIntegral]] typeclass for [[BigInt]]. */ + implicit object UnsignedBigIntIsExactIntegral extends ExactIntegral[UnsignedBigInt] { + val n = UnsignedBigIntIsIntegral + override def plus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = n.plus(x, y) + override def minus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = n.minus(x, y) + override def times(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = n.times(x, y) + + override def quot(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.divide(y) + + /** This method is used in ErgoTreeEvaluator based interpreter, to implement + * '%' operation of ErgoTree (i.e. `%: (T, T) => T` operation) for all + * numeric types T including BigInt. + * + * In the v4.x interpreter, however, the `%` operation is implemented using + * [[CBigInt]].mod method, which delegates to [[java.math.BigInteger]].mod method. + * + * Even though this method is called `divisionRemainder`, the semantics of ErgoTree + * language requires it to correspond to [[java.math.BigInteger]].mod method. + * + * For this reason we define implementation of this method using [[BigInt]].mod. + * + * NOTE: This method should not be used in v4.x + */ + override def divisionRemainder(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.mod(y) + } + + /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ + implicit object UnsignedBigIntIsExactOrdering extends ExactOrderingImpl[UnsignedBigInt](UnsignedBigIntIsIntegral) +} + diff --git a/data/shared/src/main/scala/sigma/eval/Extensions.scala b/data/shared/src/main/scala/sigma/eval/Extensions.scala index def9086e02..520d97377d 100644 --- a/data/shared/src/main/scala/sigma/eval/Extensions.scala +++ b/data/shared/src/main/scala/sigma/eval/Extensions.scala @@ -2,7 +2,7 @@ package sigma.eval import sigma.ast.syntax.SigmaPropValue import sigma.data.{CAnyValue, CSigmaDslBuilder, Nullable, RType, SigmaBoolean} -import sigma.{BigInt, Coll, Colls, Evaluation, Platform} +import sigma.{BigInt, Coll, Colls, Evaluation, Platform, UnsignedBigInt} import sigma.ast.{Constant, ConstantNode, SBoolean, SCollection, SCollectionType, SType, SigmaPropConstant, SigmaPropIsProven, TransformingSigmaBuilder, Value} import java.math.BigInteger @@ -19,6 +19,7 @@ object Extensions { implicit class IntExt(val x: Int) extends AnyVal { /** Convert this value to BigInt. */ @inline def toBigInt: BigInt = CSigmaDslBuilder.BigInt(BigInteger.valueOf(x.toLong)) + @inline def toUnsignedBigInt: UnsignedBigInt = CSigmaDslBuilder.UnsignedBigInt(BigInteger.valueOf(x.toLong)) } implicit class LongExt(val x: Long) extends AnyVal { diff --git a/interpreter/shared/src/test/scala/sigmastate/crypto/BigIntSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/crypto/BigIntSpecification.scala new file mode 100644 index 0000000000..2662ff0a7a --- /dev/null +++ b/interpreter/shared/src/test/scala/sigmastate/crypto/BigIntSpecification.scala @@ -0,0 +1,9 @@ +package sigmastate.crypto + +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigmastate.TestsBase + +class BigIntSpecification extends AnyPropSpec with ScalaCheckPropertyChecks with TestsBase { + +} diff --git a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala index 5ddcdfa946..e06e37272e 100644 --- a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala @@ -14,6 +14,7 @@ import sigma.data.{CSigmaDslBuilder, ExactIntegral, ExactNumeric, ExactOrdering, import sigma.util.Extensions.ByteOps import sigmastate.interpreter.Interpreter.ScriptEnv import sigma.ast.{Ident, Select, Val} +import sigma.data.UnsignedBigIntNumericOps.{UnsignedBigIntIsExactIntegral, UnsignedBigIntIsExactOrdering} import sigma.exceptions.GraphBuildingException import sigma.serialization.OpCodes @@ -30,6 +31,7 @@ import scala.collection.mutable.ArrayBuffer trait GraphBuilding extends SigmaLibrary { IR: IRContext => import AvlTree._ import BigInt._ + import UnsignedBigInt._ import Box._ import Coll._ import CollBuilder._ @@ -255,6 +257,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case SString => StringElement case SAny => AnyElement case SBigInt => bigIntElement + case SUnsignedBigInt => unsignedBigIntElement case SBox => boxElement case SContext => contextElement case SGlobal => sigmaDslBuilderElement @@ -281,6 +284,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case StringElement => SString case AnyElement => SAny case _: BigIntElem[_] => SBigInt + case _: UnsignedBigIntElem[_] => SUnsignedBigInt case _: GroupElementElem[_] => SGroupElement case _: AvlTreeElem[_] => SAvlTree case oe: WOptionElem[_, _] => SOption(elemToSType(oe.eItem)) @@ -308,6 +312,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case StringElement => StringIsLiftable case UnitElement => UnitIsLiftable case _: BigIntElem[_] => LiftableBigInt + case _: UnsignedBigIntElem[_] => LiftableUnsignedBigInt case _: GroupElementElem[_] => LiftableGroupElement case ce: CollElem[t,_] => implicit val lt = liftableFromElem[t](ce.eItem) @@ -328,7 +333,8 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => (ShortElement, ShortIsExactIntegral), (IntElement, IntIsExactIntegral), (LongElement, LongIsExactIntegral), - (bigIntElement, BigIntIsExactIntegral) + (bigIntElement, BigIntIsExactIntegral), + (unsignedBigIntElement, UnsignedBigIntIsExactIntegral) ) private lazy val elemToExactIntegralMap = Map[Elem[_], ExactIntegral[_]]( (ByteElement, ByteIsExactIntegral), @@ -341,7 +347,8 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => (ShortElement, ShortIsExactOrdering), (IntElement, IntIsExactOrdering), (LongElement, LongIsExactOrdering), - (bigIntElement, BigIntIsExactOrdering) + (bigIntElement, BigIntIsExactOrdering), + (unsignedBigIntElement, UnsignedBigIntIsExactOrdering) ) /** @return [[ExactNumeric]] instance for the given type */ @@ -439,6 +446,10 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => assert(tpe == SBigInt) val resV = liftConst(bi) resV + case ubi: SUnsignedBigInt => + assert(tpe == SUnsignedBigInt) + val resV = liftConst(ubi) + resV case p: SGroupElement => assert(tpe == SGroupElement) val resV = liftConst(p) diff --git a/sc/shared/src/main/scala/special/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/special/sigma/SigmaDslUnit.scala index 48548226a5..4984f2f35d 100644 --- a/sc/shared/src/main/scala/special/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/special/sigma/SigmaDslUnit.scala @@ -12,6 +12,15 @@ package sigma { def min(that: Ref[BigInt]): Ref[BigInt]; def max(that: Ref[BigInt]): Ref[BigInt]; }; + trait UnsignedBigInt extends Def[UnsignedBigInt] { + def add(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; + def subtract(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; + def multiply(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; + def divide(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; + def mod(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; + def min(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; + def max(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; + }; trait GroupElement extends Def[GroupElement] { def exp(k: Ref[BigInt]): Ref[GroupElement]; def multiply(that: Ref[GroupElement]): Ref[GroupElement]; diff --git a/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala index 70fb35c329..fb4360e05c 100644 --- a/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala @@ -263,6 +263,159 @@ object BigInt extends EntityObject("BigInt") { } // of object BigInt registerEntityObject("BigInt", BigInt) +object UnsignedBigInt extends EntityObject("UnsignedBigInt") { + import Liftables._ + + type SUnsignedBigInt = sigma.UnsignedBigInt + unsignedBigIntElement + + case class UnsignedBigIntConst(constValue: SUnsignedBigInt) + extends LiftedConst[SUnsignedBigInt, UnsignedBigInt] with UnsignedBigInt + with Def[UnsignedBigInt] with UnsignedBigIntConstMethods { + val liftable: Liftable[SUnsignedBigInt, UnsignedBigInt] = LiftableUnsignedBigInt + val resultType: Elem[UnsignedBigInt] = liftable.eW + } + + trait UnsignedBigIntConstMethods extends UnsignedBigInt { thisConst: Def[_] => + + private val UnsignedBigIntClass = RClass(classOf[UnsignedBigInt]) + + override def add(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("add", classOf[Sym]), + Array[AnyRef](that), + true, false, element[UnsignedBigInt])) + } + + override def subtract(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("subtract", classOf[Sym]), + Array[AnyRef](that), + true, false, element[UnsignedBigInt])) + } + + override def multiply(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("multiply", classOf[Sym]), + Array[AnyRef](that), + true, false, element[UnsignedBigInt])) + } + + override def divide(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("divide", classOf[Sym]), + Array[AnyRef](that), + true, false, element[UnsignedBigInt])) + } + + override def mod(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("mod", classOf[Sym]), + Array[AnyRef](m), + true, false, element[UnsignedBigInt])) + } + + override def min(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("min", classOf[Sym]), + Array[AnyRef](that), + true, false, element[UnsignedBigInt])) + } + + override def max(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("max", classOf[Sym]), + Array[AnyRef](that), + true, false, element[UnsignedBigInt])) + } + } + + + implicit object LiftableUnsignedBigInt extends Liftable[SUnsignedBigInt, UnsignedBigInt] { + lazy val eW: Elem[UnsignedBigInt] = unsignedBigIntElement + lazy val sourceType: RType[SUnsignedBigInt] = { + RType[SUnsignedBigInt] + } + + def lift(x: SUnsignedBigInt): Ref[UnsignedBigInt] = UnsignedBigIntConst(x) + } + + private val UnsignedBigIntClass = RClass(classOf[UnsignedBigInt]) + + // entityAdapter for BigInt trait + case class UnsignedBigIntAdapter(source: Ref[UnsignedBigInt]) + extends Node with UnsignedBigInt + with Def[UnsignedBigInt] { + val resultType: Elem[UnsignedBigInt] = element[UnsignedBigInt] + + override def transform(t: Transformer) = UnsignedBigIntAdapter(t(source)) + + def add(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("add", classOf[Sym]), + Array[AnyRef](that), + true, true, element[UnsignedBigInt])) + } + + def subtract(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("subtract", classOf[Sym]), + Array[AnyRef](that), + true, true, element[UnsignedBigInt])) + } + + def multiply(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("multiply", classOf[Sym]), + Array[AnyRef](that), + true, true, element[UnsignedBigInt])) + } + + def divide(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("divide", classOf[Sym]), + Array[AnyRef](that), + true, true, element[UnsignedBigInt])) + } + + def mod(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("mod", classOf[Sym]), + Array[AnyRef](m), + true, true, element[UnsignedBigInt])) + } + + def min(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("min", classOf[Sym]), + Array[AnyRef](that), + true, true, element[UnsignedBigInt])) + } + + def max(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("max", classOf[Sym]), + Array[AnyRef](that), + true, true, element[UnsignedBigInt])) + } + } + + class UnsignedBigIntElem[To <: UnsignedBigInt] + extends EntityElem[To] { + override val liftable: Liftables.Liftable[_, To] = asLiftable[SUnsignedBigInt, To](LiftableUnsignedBigInt) + + override protected def collectMethods: Map[RMethod, MethodDesc] = { + super.collectMethods ++ + Elem.declaredMethods(RClass(classOf[UnsignedBigInt]), RClass(classOf[UnsignedBigInt]), Set( + "add", "subtract", "multiply", "divide", "mod", "min", "max" + )) + } + } + + implicit lazy val unsignedBigIntElement: Elem[UnsignedBigInt] = new UnsignedBigIntElem[UnsignedBigInt] +} // of object BigInt + registerEntityObject("UnsignedBigInt", UnsignedBigInt) + object GroupElement extends EntityObject("GroupElement") { // entityConst: single const for each entity import Liftables._ diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..621e32ad3f 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -2,14 +2,17 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ +import scorex.util.encode.Base16 import sigma.Extensions.ArrayOps +import sigma.GroupElement +import sigma.VersionContext.V6SoftForkVersion import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps -import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} +import sigma.data.{AvlTreeData, CAnyValue, CBigInt, CGroupElement, CSigmaDslBuilder} import sigma.util.StringUtil._ import sigma.ast._ import sigma.ast.syntax._ -import sigma.crypto.CryptoConstants +import sigma.crypto.{CryptoConstants, SecP256K1Group} import sigmastate._ import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} @@ -22,6 +25,8 @@ import sigma.exceptions.InvalidType import sigmastate.utils.Helpers._ import java.math.BigInteger +import java.security.SecureRandom +import scala.annotation.tailrec class BasicOpsSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -135,6 +140,111 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } + property("group order serialization") { + val value = SecP256K1Group.q.divide(new BigInteger("2")) + + def deserTest() = {test("big int - q", env, ext, + s"{ val b = bigInt(\"${value.toString}\"); b > 1 }", + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + deserTest() + } else { + deserTest() + } + } + + property("restoring unsigned 256 bits") { + val b = new BigInteger("92805629300808893548929804498612226467505866636839045998233220279839291898608") + val ub = new BigInteger(1, b.toByteArray) + + def deserTest() = {test("restoring", env, ext, + s"{ val b = unsignedBigInt(\"${ub.toString}\"); b > 1 }", + null, + true + )} + + deserTest() + } + + property("signed <-> unsigned bigint conversion") { + + } + + property("schnorr sig check") { + + val g = CGroupElement(SecP256K1Group.generator) + + def randBigInt: BigInt = { + val random = new SecureRandom() + val values = new Array[Byte](32) + random.nextBytes(values) + BigInt(values).mod(SecP256K1Group.q) + } + + @tailrec + def sign(msg: Array[Byte], secretKey: BigInt): (GroupElement, BigInt) = { + val r = randBigInt + + val a: GroupElement = g.exp(CBigInt(r.bigInteger)) + val z = (r + secretKey * BigInt(scorex.crypto.hash.Blake2b256(msg))) % CryptoConstants.groupOrder + + if(z.bitLength > 255) { + println("z: " + z) + (a, z) + } else { + sign(msg,secretKey) + } + } + + val holderSecret = randBigInt + val holderPk = g.exp(CBigInt(holderSecret.bigInteger)) + + val message = Array.fill(5)(1.toByte) + + val (a,z) = sign(message, holderSecret) + + val customExt: Seq[(Byte, EvaluatedValue[_ <: SType])] = Map( + 0.toByte -> GroupElementConstant(holderPk), + 1.toByte -> GroupElementConstant(a), + 2.toByte -> ByteArrayConstant(z.bigInteger.toByteArray) + ).toSeq + + def deserTest() = {test("schnorr", env, customExt, + s"""{ + | + | val g: GroupElement = groupGenerator + | val holder = getVar[GroupElement](0).get + | + | val message = fromBase16("${Base16.encode(message)}") + | val e: Coll[Byte] = blake2b256(message) // weak Fiat-Shamir + | val eInt = byteArrayToBigInt(e) // challenge as big integer + | + | // a of signature in (a, z) + | val a = getVar[GroupElement](1).get + | val aBytes = a.getEncoded + | + | // z of signature in (a, z) + | val zBytes = getVar[Coll[Byte]](2).get + | val z = byteArrayToBigInt(zBytes) + | + | // Signature is valid if g^z = a * x^e + | val properSignature = g.exp(z) == a.multiply(holder.exp(eInt)) + | sigmaProp(properSignature) + |}""".stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + deserTest() + } else { + deserTest() + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala index 9d0ffc880b..8d3d09de74 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala @@ -138,7 +138,7 @@ class OracleExamplesSpecification extends CompilerTestingCommons LastBlockUtxoRootHash, SAvlTreeMethods.getMethod, IndexedSeq(ExtractId(GetVarBox(22: Byte).get), GetVarByteArray(23: Byte).get)).asOption[SByteArray]), EQ(extract[SByteArray](ErgoBox.ScriptRegId), ByteArrayConstant(ErgoTree.fromSigmaBoolean(oraclePubKey).bytes)), - EQ(Exponentiate(GroupGenerator, extract[SBigInt.type](reg3)), + EQ(Exponentiate(GroupGenerator, extract[SBigInt.type](reg3)(SBigInt)), MultiplyGroup(extract[SGroupElement.type](reg2), Exponentiate(GroupElementConstant(oraclePubImage.value), ByteArrayToBigInt( From b13cef6d0713821cb7fb31fa2f2bf82bf689fe13 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 25 May 2024 00:23:28 +0300 Subject: [PATCH 02/78] failing schnorr sig check w. unsigned --- .../test/scala/sigmastate/utxo/BasicOpsSpecification.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 621e32ad3f..28084be987 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -192,7 +192,6 @@ class BasicOpsSpecification extends CompilerTestingCommons val z = (r + secretKey * BigInt(scorex.crypto.hash.Blake2b256(msg))) % CryptoConstants.groupOrder if(z.bitLength > 255) { - println("z: " + z) (a, z) } else { sign(msg,secretKey) @@ -209,7 +208,7 @@ class BasicOpsSpecification extends CompilerTestingCommons val customExt: Seq[(Byte, EvaluatedValue[_ <: SType])] = Map( 0.toByte -> GroupElementConstant(holderPk), 1.toByte -> GroupElementConstant(a), - 2.toByte -> ByteArrayConstant(z.bigInteger.toByteArray) + 2.toByte -> UnsignedBigIntConstant(z.bigInteger) ).toSeq def deserTest() = {test("schnorr", env, customExt, @@ -227,8 +226,7 @@ class BasicOpsSpecification extends CompilerTestingCommons | val aBytes = a.getEncoded | | // z of signature in (a, z) - | val zBytes = getVar[Coll[Byte]](2).get - | val z = byteArrayToBigInt(zBytes) + | val z = getVar[UnsignedBigInt](2).get | | // Signature is valid if g^z = a * x^e | val properSignature = g.exp(z) == a.multiply(holder.exp(eInt)) From 2e23a2c5e01101c8655ca41160faaf61a5e63427 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 28 May 2024 15:07:01 +0200 Subject: [PATCH 03/78] v6.0-serialize: CheckMinimalErgoTreeVersion rule removed --- .../validation/ValidationRules.scala | 17 +---------------- .../src/main/scala/sigma/ast/methods.scala | 2 -- .../src/test/scala/sigma/SigmaDslTesting.scala | 2 +- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala index 07fe8db0ee..9d4de47a99 100644 --- a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala +++ b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala @@ -155,20 +155,6 @@ object ValidationRules { override protected lazy val settings: SigmaValidationSettings = currentSettings } - object CheckMinimalErgoTreeVersion extends ValidationRule(1016, - "ErgoTree should have at least required version") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = currentSettings - - final def apply(currentVersion: Byte, minVersion: Byte): Unit = { - checkRule() - if (currentVersion < minVersion) { - throwValidationException( - new SigmaException(s"ErgoTree should have at least $minVersion version, but was $currentVersion"), - Array(currentVersion, minVersion)) - } - } - } - val ruleSpecs: Seq[ValidationRule] = Seq( CheckDeserializedScriptType, CheckDeserializedScriptIsSigmaProp, @@ -185,8 +171,7 @@ object ValidationRules { CheckHeaderSizeBit, CheckCostFuncOperation, CheckPositionLimit, - CheckLoopLevelInCostFunction, - CheckMinimalErgoTreeVersion + CheckLoopLevelInCostFunction ) /** Validation settings that correspond to the current version of the ErgoScript implementation. diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 3f27f85e2d..706c1a7894 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1,7 +1,6 @@ package sigma.ast import org.ergoplatform._ -import org.ergoplatform.validation.ValidationRules.CheckMinimalErgoTreeVersion import org.ergoplatform.validation._ import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} @@ -1534,7 +1533,6 @@ case object SGlobalMethods extends MonoTypeMethods { def serialize_eval(mc: MethodCall, G: SigmaDslBuilder, value: SType#WrappedType) (implicit E: ErgoTreeEvaluator): Coll[Byte] = { // TODO v6.0: accumulate cost - CheckMinimalErgoTreeVersion(E.context.currentErgoTreeVersion, VersionContext.V6SoftForkVersion) val t = Evaluation.stypeToRType(mc.args(0).tpe) G.serialize(value)(t) } diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 28c9c05fec..f14ba275ff 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -860,7 +860,7 @@ class SigmaDslTesting extends AnyPropSpec extends Feature[A, B] { override def isSupportedIn(vc: VersionContext): Boolean = - vc.activatedVersion >= sinceVersion && vc.ergoTreeVersion >= sinceVersion + vc.activatedVersion >= sinceVersion override def scalaFunc: A => B = { x => sys.error(s"Semantic Scala function is not defined for old implementation: $this") From fe68c66859104cd1580a5b16c6f324b318d7d4bc Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 28 May 2024 15:53:43 +0200 Subject: [PATCH 04/78] v6.0-serialize: cleanup + ScalaDocs --- data/shared/src/main/scala/sigma/ast/SMethod.scala | 4 +++- data/shared/src/main/scala/sigma/ast/SigmaPredef.scala | 10 +++++----- data/shared/src/main/scala/sigma/ast/methods.scala | 2 +- data/shared/src/main/scala/sigma/ast/values.scala | 2 +- .../src/test/scala/sigmastate/CrossVersionProps.scala | 2 ++ .../test/scala/sigmastate/lang/SigmaParserTest.scala | 3 +++ .../compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala | 2 +- .../test/scala/sigmastate/lang/SigmaBinderTest.scala | 3 +++ .../test/scala/sigmastate/lang/SigmaTyperTest.scala | 1 + 9 files changed, 20 insertions(+), 9 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala index 332c74be6b..47f6e744b0 100644 --- a/data/shared/src/main/scala/sigma/ast/SMethod.scala +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -78,7 +78,9 @@ case class SMethod( /** Operation descriptor of this method. */ lazy val opDesc = MethodDesc(this) - /** Return true if this method has runtime type parameters */ + /** Return true if this method has explicit type parameters, which need to be serialized + * as part of [[MethodCall]]. + */ def hasExplicitTypeArgs: Boolean = explicitTypeArgs.nonEmpty /** Finds and keeps the [[RMethod]] instance which corresponds to this method descriptor. diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index f2da24d2df..7d6da0a5f9 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -405,8 +405,8 @@ object SigmaPredef { val SerializeFunc = PredefinedFunc("serialize", Lambda(Seq(paramT), Array("value" -> tT), SByteArray, None), - PredefFuncInfo( - { case (_, args @ Seq(value)) => + irInfo = PredefFuncInfo( + irBuilder = { case (_, args @ Seq(value)) => MethodCall.typed[Value[SCollection[SByte.type]]]( Global, SGlobalMethods.serializeMethod.withConcreteTypes(Map(tT -> value.tpe)), @@ -414,10 +414,10 @@ object SigmaPredef { Map() ) }), - OperationInfo(MethodCall, - """ + docInfo = OperationInfo(MethodCall, + """Serializes the given `value` into bytes using the default serialization format. """.stripMargin, - Seq(ArgInfo("value", "")) + Seq(ArgInfo("value", "value to serialize")) ) ) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 706c1a7894..262a3266dd 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1522,7 +1522,7 @@ case object SGlobalMethods extends MonoTypeMethods { lazy val serializeMethod = SMethod(this, "serialize", SFunc(Array(SGlobal, tT), SByteArray, Array(paramT)), 3, DynamicCost) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "", + .withInfo(MethodCall, "Serializes the given `value` into bytes using the default serialization format.", ArgInfo("value", "value to be serialized")) diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala index 7980fd96ce..dd1f10d9be 100644 --- a/data/shared/src/main/scala/sigma/ast/values.scala +++ b/data/shared/src/main/scala/sigma/ast/values.scala @@ -1299,7 +1299,7 @@ case class MethodCall( args: IndexedSeq[Value[SType]], typeSubst: Map[STypeVar, SType]) extends Value[SType] { require(method.explicitTypeArgs.forall(tyArg => typeSubst.contains(tyArg)), - s"Runtime Generic method call should have concrete type for each runtime type parameter, but was: $this") + s"Generic method call should have concrete type for each explicit type parameter, but was: $this") override def companion = if (args.isEmpty) PropertyCall else MethodCall override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe) diff --git a/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala b/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala index e55b874dc3..87101a1f71 100644 --- a/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala +++ b/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala @@ -31,7 +31,9 @@ trait CrossVersionProps extends AnyPropSpecLike with TestsBase { System.gc() } forEachScriptAndErgoTreeVersion(activatedVersions, ergoTreeVersions) { + VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { testFun_Run(testName, testFun) + } } } } diff --git a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala index 70aa540a4f..5a0f2b3465 100644 --- a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala @@ -35,6 +35,9 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat } } + /** Checks parsing result, printing the actual value as a test vector if expected value + * is not equal to actual. + */ def checkParsed(x: String, expected: SValue) = { val parsed = parse(x) if (expected != parsed) { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index 9cd524149d..c089c0ccb5 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -1950,7 +1950,7 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { asRep[Coll[Byte]](mkMethodCall(self, SigmaDslBuilderClass.getMethod("serialize", classOf[Sym]), Array[AnyRef](value), - true, true, element[Coll[Byte]])) + true, false, element[Coll[Byte]])) } } diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala index 54bd89c9c2..3c3687cb37 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala @@ -31,6 +31,9 @@ class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat res } + /** Checks that parsing and binding results in the expected value. + * @return the inferred type of the expression + */ def checkBound(env: ScriptEnv, x: String, expected: SValue) = { val bound = bind(env, x) if (expected != bound) { diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 2c3bd44d39..8aeefba8a7 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -30,6 +30,7 @@ class SigmaTyperTest extends AnyPropSpec private val predefFuncRegistry = new PredefinedFuncRegistry(StdSigmaBuilder) import predefFuncRegistry._ + /** Checks that parsing, binding and typing of `x` results in the given expected value. */ def typecheck(env: ScriptEnv, x: String, expected: SValue = null): SType = { try { val builder = TransformingSigmaBuilder From a5321af4e41f6f6d25fceaec079d16a8786caf38 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 28 May 2024 20:44:27 +0200 Subject: [PATCH 05/78] v6.0-serialize: added CoreByteWriter.checkCostLimit --- .../sigma/serialization/CoreByteWriter.scala | 150 ++++++++++++++---- .../sigma/serialization/CoreSerializer.scala | 2 +- .../sigma/serialization/SigmaByteWriter.scala | 65 ++++++-- .../sigma/serialization/SigmaSerializer.scala | 7 +- 4 files changed, 178 insertions(+), 46 deletions(-) diff --git a/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala index aa4255449c..302663ba92 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala @@ -3,25 +3,47 @@ package sigma.serialization import scorex.util.serialization.Writer.Aux import scorex.util.serialization.{VLQByteBufferWriter, Writer} import sigma.ast.SType -import sigma.serialization.CoreByteWriter.{Bits, DataInfo, U, Vlq, ZigZag} +import sigma.serialization.CoreByteWriter.{Bits, CostLimitChecker, DataInfo, U, Vlq, ZigZag} /** Implementation of [[Writer]] provided by `sigma-core` module. - * @param w destination [[Writer]] to which all the call got delegated. + * + * @param w destination [[Writer]] to which all the call got delegated. + * @param checkCostLimitOpt callback to check if the cost limit at current writing position + * is reached. The callback will throw an exception if the limit is reached. + * Note, the cost of serialization is approximated using formula + * `this.length * CostPerByte + InitialCost` */ -class CoreByteWriter(val w: Writer) extends Writer { +class CoreByteWriter(val w: Writer, val checkCostLimitOpt: Option[CostLimitChecker]) extends Writer { type CH = w.CH + /** Check the current writer length against the cost limit. */ + @inline protected def checkCostLimit(): Unit = { + if (checkCostLimitOpt.isDefined) + checkCostLimitOpt.get(this.length()) + } + @inline override def length(): Int = w.length() @inline override def newWriter(): Aux[CH] = w.newWriter() - @inline override def putChunk(chunk: CH): this.type = { w.putChunk(chunk); this } + @inline override def putChunk(chunk: CH): this.type = { + w.putChunk(chunk); + checkCostLimit() + this + } @inline override def result(): CH = w.result() - @inline def put(x: Byte): this.type = { w.put(x); this } + @inline def put(x: Byte): this.type = { + w.put(x); + checkCostLimit() + this + } + @inline def put(x: Byte, info: DataInfo[Byte]): this.type = { - w.put(x); this + w.put(x); + checkCostLimit() + this } override def putUByte(x: Int): this.type = { @@ -31,49 +53,105 @@ class CoreByteWriter(val w: Writer) extends Writer { super.putUByte(x) } - @inline def putBoolean(x: Boolean): this.type = { w.putBoolean(x); this } + @inline def putBoolean(x: Boolean): this.type = { + w.putBoolean(x); + checkCostLimit() + this + } + @inline def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = { - w.putBoolean(x); this + w.putBoolean(x); + checkCostLimit() + this + } + + @inline def putShort(x: Short): this.type = { + w.putShort(x); + checkCostLimit() + this } - @inline def putShort(x: Short): this.type = { w.putShort(x); this } @inline def putShort(x: Short, info: DataInfo[Short]): this.type = { - w.putShort(x); this + w.putShort(x); + checkCostLimit() + this + } + + @inline def putUShort(x: Int): this.type = { + w.putUShort(x); + checkCostLimit() + this } - @inline def putUShort(x: Int): this.type = { w.putUShort(x); this } @inline def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = { - w.putUShort(x); this + w.putUShort(x); + checkCostLimit() + this + } + + @inline def putInt(x: Int): this.type = { + w.putInt(x); + checkCostLimit() + this } - @inline def putInt(x: Int): this.type = { w.putInt(x); this } @inline def putInt(x: Int, info: DataInfo[Int]): this.type = { - w.putInt(x); this + w.putInt(x); + checkCostLimit() + this } - @inline def putUInt(x: Long): this.type = { w.putUInt(x); this } + @inline def putUInt(x: Long): this.type = { + w.putUInt(x); + checkCostLimit() + this + } @inline def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = { - w.putUInt(x); this + w.putUInt(x); + checkCostLimit() + this } - @inline def putLong(x: Long): this.type = { w.putLong(x); this } + @inline def putLong(x: Long): this.type = { + w.putLong(x); + checkCostLimit() + this + } @inline def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = { - w.putLong(x); this + w.putLong(x); + checkCostLimit() + this + } + + @inline def putULong(x: Long): this.type = { + w.putULong(x); + checkCostLimit() + this } - @inline def putULong(x: Long): this.type = { w.putULong(x); this } @inline def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = { - w.putULong(x); this + w.putULong(x); + checkCostLimit() + this } override def putBytes(xs: Array[Byte], offset: Int, length: Int): this.type = { - w.putBytes(xs, offset, length); this + w.putBytes(xs, offset, length); + checkCostLimit() + this + } + + @inline def putBytes(xs: Array[Byte]): this.type = { + w.putBytes(xs); + checkCostLimit() + this } - @inline def putBytes(xs: Array[Byte]): this.type = { w.putBytes(xs); this } @inline def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = { - w.putBytes(xs); this + w.putBytes(xs); + checkCostLimit() + this } /** Put the two bytes of the big-endian representation of the Short value into the @@ -81,12 +159,18 @@ class CoreByteWriter(val w: Writer) extends Writer { @inline def putShortBytes(value: Short): this.type = { w.put((value >> 8).toByte) w.put(value.toByte) + checkCostLimit() this } - @inline def putBits(xs: Array[Boolean]): this.type = { w.putBits(xs); this } + @inline def putBits(xs: Array[Boolean]): this.type = { + w.putBits(xs); + checkCostLimit() + this + } @inline def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = { w.putBits(xs); + checkCostLimit() this } @@ -94,19 +178,28 @@ class CoreByteWriter(val w: Writer) extends Writer { w.putOption(x) { (_, v) => putValueC(this, v) } + checkCostLimit() this } - @inline def putShortString(s: String): this.type = { w.putShortString(s); this } + @inline def putShortString(s: String): this.type = { + w.putShortString(s); + checkCostLimit() + this + } // TODO refactor: move to Writer @inline def toBytes: Array[Byte] = w match { case wr: VLQByteBufferWriter => wr.toBytes } - @inline def putType[T <: SType](x: T): this.type = { TypeSerializer.serialize(x, this); this } + @inline def putType[T <: SType](x: T): this.type = { + TypeSerializer.serialize(x, this); // the cost is checked in TypeSerializer + this + } @inline def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = { - TypeSerializer.serialize(x, this); this + TypeSerializer.serialize(x, this); // the cost is checked in TypeSerializer + this } } @@ -114,6 +207,9 @@ class CoreByteWriter(val w: Writer) extends Writer { object CoreByteWriter { import scala.language.implicitConversions + /** Callback type of cost limit checker. */ + type CostLimitChecker = Int => Unit + /** Format descriptor type family. */ trait FormatDescriptor[T] { /** Size formula associated with this format */ diff --git a/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala index 938d3f22c1..2ec3ee5c1a 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala @@ -66,7 +66,7 @@ object CoreSerializer { def startWriter(): CoreByteWriter = { val b = new ByteArrayBuilder() val wi = new VLQByteBufferWriter(b) - val w = new CoreByteWriter(wi) + val w = new CoreByteWriter(wi, None) w } diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala index 35d5e0c9b9..87b115e942 100644 --- a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala @@ -4,15 +4,28 @@ import scorex.util.serialization.Writer import sigma.ast.syntax._ import sigma.ast._ import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo, FormatDescriptor, SeqFmt} - -class SigmaByteWriter(override val w: Writer, - val constantExtractionStore: Option[ConstantStore]) - extends CoreByteWriter(w) { +import CoreByteWriter.CostLimitChecker + +/** Implementation of [[Writer]] provided by `sigma-data` module. + * + * @param w destination [[Writer]] to which all the call got delegated. + * @param constantExtractionStore optional store to segregate constants to while + * replacing them with placeholders. + * @param checkCostLimitOpt callback to check if the cost limit at current writing position + * is reached. The callback will throw an exception if the limit is reached. + */ +class SigmaByteWriter( + override val w: Writer, + val constantExtractionStore: Option[ConstantStore], + override val checkCostLimitOpt: Option[CostLimitChecker] +) + extends CoreByteWriter(w, checkCostLimitOpt) { import CoreByteWriter._ import ValueSerializer._ override def put(x: Byte, info: DataInfo[Byte]): this.type = { ValueSerializer.addArgInfo(info) + checkCostLimit() w.put(x); this } @@ -23,59 +36,81 @@ class SigmaByteWriter(override val w: Writer, @inline override def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = { ValueSerializer.addArgInfo(info) - w.putBoolean(x); this + w.putBoolean(x); + checkCostLimit() + this } @inline override def putShort(x: Short, info: DataInfo[Short]): this.type = { ValueSerializer.addArgInfo(info) - w.putShort(x); this + w.putShort(x); + checkCostLimit() + this } @inline override def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = { ValueSerializer.addArgInfo(info) - w.putUShort(x); this + w.putUShort(x); + checkCostLimit() + this } @inline override def putInt(x: Int, info: DataInfo[Int]): this.type = { ValueSerializer.addArgInfo(info) - w.putInt(x); this + w.putInt(x); + checkCostLimit() + this } @inline override def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = { ValueSerializer.addArgInfo(info) - w.putUInt(x); this + w.putUInt(x); + checkCostLimit() + this } @inline override def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = { ValueSerializer.addArgInfo(info) - w.putLong(x); this + w.putLong(x); + checkCostLimit() + this } @inline override def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = { ValueSerializer.addArgInfo(info) - w.putULong(x); this + w.putULong(x); + checkCostLimit() + this } @inline override def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = { ValueSerializer.addArgInfo(info) - w.putBytes(xs); this + w.putBytes(xs); + checkCostLimit() + this } @inline override def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = { ValueSerializer.addArgInfo(info) w.putBits(xs); + checkCostLimit() this } @inline override def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = { ValueSerializer.addArgInfo(info) - TypeSerializer.serialize(x, this); this + TypeSerializer.serialize(x, this); // the cost is checked in TypeSerializer + this } - @inline def putValue[T <: SType](x: Value[T]): this.type = { ValueSerializer.serialize(x, this); this } + @inline def putValue[T <: SType](x: Value[T]): this.type = { + ValueSerializer.serialize(x, this) // the cost is checked in ValueSerializer + this + } @inline def putValue[T <: SType](x: Value[T], info: DataInfo[SValue]): this.type = { ValueSerializer.addArgInfo(info) - ValueSerializer.serialize(x, this); this + ValueSerializer.serialize(x, this); // the cost is checked in ValueSerializer + this } @inline def putValues[T <: SType](xs: Seq[Value[T]]): this.type = { putUInt(xs.length) diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala index 3765adb029..e842258f80 100644 --- a/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala @@ -4,6 +4,7 @@ import java.nio.ByteBuffer import scorex.util.ByteArrayBuilder import scorex.util.serialization._ import sigma.data.SigmaConstants +import sigma.serialization.CoreByteWriter.CostLimitChecker import sigma.serialization.ValueCodes.OpCode object SigmaSerializer { @@ -51,14 +52,14 @@ object SigmaSerializer { def startWriter(): SigmaByteWriter = { val b = new ByteArrayBuilder() val wi = new VLQByteBufferWriter(b) - val w = new SigmaByteWriter(wi, constantExtractionStore = None) + val w = new SigmaByteWriter(wi, constantExtractionStore = None, checkCostLimitOpt = None) w } - def startWriter(constantExtractionStore: ConstantStore): SigmaByteWriter = { + def startWriter(constantExtractionStore: ConstantStore, checkCostLimit: Option[CostLimitChecker] = None): SigmaByteWriter = { val b = new ByteArrayBuilder() val wi = new VLQByteBufferWriter(b) - val w = new SigmaByteWriter(wi, constantExtractionStore = Some(constantExtractionStore)) + val w = new SigmaByteWriter(wi, constantExtractionStore = Some(constantExtractionStore), checkCostLimit) w } } From 432addca9dbef1dc2d1f44e797bfbe2bab376c7a Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 May 2024 17:59:28 +0200 Subject: [PATCH 06/78] v6.0-serialize: cost accumulation in SigmaByteWriter and serialize_eval method --- .../sigma/serialization/CoreByteWriter.scala | 115 +++------ .../sigma/serialization/CoreSerializer.scala | 2 +- .../src/main/scala/sigma/ast/ErgoTree.scala | 2 +- .../src/main/scala/sigma/ast/methods.scala | 15 +- .../serialization/ErgoTreeSerializer.scala | 2 +- .../sigma/serialization/SigmaByteWriter.scala | 236 ++++++++++++++---- .../sigma/serialization/SigmaSerializer.scala | 12 +- .../test/scala/sigma/SigmaDslTesting.scala | 12 +- 8 files changed, 243 insertions(+), 153 deletions(-) diff --git a/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala index 302663ba92..37d1455ba3 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala @@ -3,155 +3,106 @@ package sigma.serialization import scorex.util.serialization.Writer.Aux import scorex.util.serialization.{VLQByteBufferWriter, Writer} import sigma.ast.SType -import sigma.serialization.CoreByteWriter.{Bits, CostLimitChecker, DataInfo, U, Vlq, ZigZag} +import sigma.serialization.CoreByteWriter._ /** Implementation of [[Writer]] provided by `sigma-core` module. * * @param w destination [[Writer]] to which all the call got delegated. - * @param checkCostLimitOpt callback to check if the cost limit at current writing position - * is reached. The callback will throw an exception if the limit is reached. - * Note, the cost of serialization is approximated using formula - * `this.length * CostPerByte + InitialCost` */ -class CoreByteWriter(val w: Writer, val checkCostLimitOpt: Option[CostLimitChecker]) extends Writer { +class CoreByteWriter(val w: Writer) extends Writer { type CH = w.CH - /** Check the current writer length against the cost limit. */ - @inline protected def checkCostLimit(): Unit = { - if (checkCostLimitOpt.isDefined) - checkCostLimitOpt.get(this.length()) - } - @inline override def length(): Int = w.length() @inline override def newWriter(): Aux[CH] = w.newWriter() @inline override def putChunk(chunk: CH): this.type = { - w.putChunk(chunk); - checkCostLimit() - this + w.putChunk(chunk); this } @inline override def result(): CH = w.result() - @inline def put(x: Byte): this.type = { - w.put(x); - checkCostLimit() - this + @inline override def put(x: Byte): this.type = { + w.put(x); this } @inline def put(x: Byte, info: DataInfo[Byte]): this.type = { - w.put(x); - checkCostLimit() - this + w.put(x); this } override def putUByte(x: Int): this.type = { super.putUByte(x) } + def putUByte(x: Int, info: DataInfo[U[Byte]]): this.type = { super.putUByte(x) } @inline def putBoolean(x: Boolean): this.type = { - w.putBoolean(x); - checkCostLimit() - this + w.putBoolean(x); this } @inline def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = { - w.putBoolean(x); - checkCostLimit() - this + w.putBoolean(x); this } @inline def putShort(x: Short): this.type = { - w.putShort(x); - checkCostLimit() - this + w.putShort(x); this } @inline def putShort(x: Short, info: DataInfo[Short]): this.type = { - w.putShort(x); - checkCostLimit() - this + w.putShort(x); this } @inline def putUShort(x: Int): this.type = { - w.putUShort(x); - checkCostLimit() - this + w.putUShort(x); this } @inline def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = { - w.putUShort(x); - checkCostLimit() - this + w.putUShort(x); this } @inline def putInt(x: Int): this.type = { - w.putInt(x); - checkCostLimit() - this + w.putInt(x); this } @inline def putInt(x: Int, info: DataInfo[Int]): this.type = { - w.putInt(x); - checkCostLimit() - this + w.putInt(x); this } @inline def putUInt(x: Long): this.type = { - w.putUInt(x); - checkCostLimit() - this + w.putUInt(x); this } @inline def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = { - w.putUInt(x); - checkCostLimit() - this + w.putUInt(x); this } @inline def putLong(x: Long): this.type = { - w.putLong(x); - checkCostLimit() - this + w.putLong(x); this } @inline def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = { - w.putLong(x); - checkCostLimit() - this + w.putLong(x); this } @inline def putULong(x: Long): this.type = { - w.putULong(x); - checkCostLimit() - this + w.putULong(x); this } @inline def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = { - w.putULong(x); - checkCostLimit() - this + w.putULong(x); this } override def putBytes(xs: Array[Byte], offset: Int, length: Int): this.type = { - w.putBytes(xs, offset, length); - checkCostLimit() - this + w.putBytes(xs, offset, length); this } @inline def putBytes(xs: Array[Byte]): this.type = { - w.putBytes(xs); - checkCostLimit() - this + w.putBytes(xs); this } @inline def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = { - w.putBytes(xs); - checkCostLimit() - this + w.putBytes(xs); this } /** Put the two bytes of the big-endian representation of the Short value into the @@ -159,32 +110,25 @@ class CoreByteWriter(val w: Writer, val checkCostLimitOpt: Option[CostLimitCheck @inline def putShortBytes(value: Short): this.type = { w.put((value >> 8).toByte) w.put(value.toByte) - checkCostLimit() this } @inline def putBits(xs: Array[Boolean]): this.type = { - w.putBits(xs); - checkCostLimit() - this + w.putBits(xs); this } @inline def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = { - w.putBits(xs); - checkCostLimit() - this + w.putBits(xs); this } @inline def putOption[T](x: Option[T])(putValueC: (this.type, T) => Unit): this.type = { w.putOption(x) { (_, v) => putValueC(this, v) } - checkCostLimit() this } @inline def putShortString(s: String): this.type = { w.putShortString(s); - checkCostLimit() this } @@ -194,11 +138,11 @@ class CoreByteWriter(val w: Writer, val checkCostLimitOpt: Option[CostLimitCheck } @inline def putType[T <: SType](x: T): this.type = { - TypeSerializer.serialize(x, this); // the cost is checked in TypeSerializer + TypeSerializer.serialize(x, this) this } @inline def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = { - TypeSerializer.serialize(x, this); // the cost is checked in TypeSerializer + TypeSerializer.serialize(x, this) this } @@ -207,9 +151,6 @@ class CoreByteWriter(val w: Writer, val checkCostLimitOpt: Option[CostLimitCheck object CoreByteWriter { import scala.language.implicitConversions - /** Callback type of cost limit checker. */ - type CostLimitChecker = Int => Unit - /** Format descriptor type family. */ trait FormatDescriptor[T] { /** Size formula associated with this format */ diff --git a/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala index 2ec3ee5c1a..938d3f22c1 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala @@ -66,7 +66,7 @@ object CoreSerializer { def startWriter(): CoreByteWriter = { val b = new ByteArrayBuilder() val wi = new VLQByteBufferWriter(b) - val w = new CoreByteWriter(wi, None) + val w = new CoreByteWriter(wi) w } diff --git a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala index 68d69abd91..8d731e1c67 100644 --- a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala +++ b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala @@ -381,7 +381,7 @@ object ErgoTree { * */ def withSegregation(header: HeaderType, prop: SigmaPropValue): ErgoTree = { val constantStore = new ConstantStore() - val w = SigmaSerializer.startWriter(constantStore) + val w = SigmaSerializer.startWriter(Some(constantStore)) // serialize value and segregate constants into constantStore ValueSerializer.serialize(prop, w) val extractedConstants = constantStore.getAll diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 262a3266dd..fd9024ee62 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -12,6 +12,7 @@ import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConsta import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} import sigma.reflection.RClass import sigma.serialization.CoreByteWriter.ArgInfo +import sigma.serialization.{DataSerializer, SigmaSerializer} import sigma.utils.SparseArrayContainer import scala.annotation.unused @@ -1532,9 +1533,17 @@ case object SGlobalMethods extends MonoTypeMethods { */ def serialize_eval(mc: MethodCall, G: SigmaDslBuilder, value: SType#WrappedType) (implicit E: ErgoTreeEvaluator): Coll[Byte] = { - // TODO v6.0: accumulate cost - val t = Evaluation.stypeToRType(mc.args(0).tpe) - G.serialize(value)(t) + + val addFixedCostCallback = { (costInfo: OperationCostInfo[FixedCost]) => + E.addCost(costInfo) + } + val addPerItemCostCallback = { (info: OperationCostInfo[PerItemCost], nItems: Int) => + E.addSeqCostNoOp(info.costKind, nItems, info.opDesc) + } + val w = SigmaSerializer.startWriter(None, + Some(addFixedCostCallback), Some(addPerItemCostCallback)) + DataSerializer.serialize(value, mc.args(0).tpe, w) + Colls.fromArray(w.toBytes) } protected override def getMethods() = super.getMethods() ++ { diff --git a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala index 43e41f91ff..ce7d1241c3 100644 --- a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala @@ -347,7 +347,7 @@ class ErgoTreeSerializer { val newVal = newVals(positions.indexOf(i)) // we need to get newVal's serialized constant value (see ProveDlogSerializer for example) val constantStore = new ConstantStore() - val valW = SigmaSerializer.startWriter(constantStore) + val valW = SigmaSerializer.startWriter(Some(constantStore)) valW.putValue(newVal) val newConsts = constantStore.getAll require(newConsts.length == 1) diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala index 87b115e942..93e078e319 100644 --- a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala @@ -4,28 +4,52 @@ import scorex.util.serialization.Writer import sigma.ast.syntax._ import sigma.ast._ import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo, FormatDescriptor, SeqFmt} -import CoreByteWriter.CostLimitChecker +import SigmaByteWriter._ /** Implementation of [[Writer]] provided by `sigma-data` module. * - * @param w destination [[Writer]] to which all the call got delegated. - * @param constantExtractionStore optional store to segregate constants to while - * replacing them with placeholders. - * @param checkCostLimitOpt callback to check if the cost limit at current writing position - * is reached. The callback will throw an exception if the limit is reached. + * @param w destination [[Writer]] to which all the call got delegated. + * @param constantExtractionStore optional store to segregate constants to while + * replacing them with placeholders. + * @param addFixedCostCallbackOpt optional callback to accumulate fixed costs. + * @param addPerItemCostCallbackOpt optional callback to accumulate per-item costs. */ class SigmaByteWriter( override val w: Writer, val constantExtractionStore: Option[ConstantStore], - override val checkCostLimitOpt: Option[CostLimitChecker] -) - extends CoreByteWriter(w, checkCostLimitOpt) { + val addFixedCostCallbackOpt: Option[FixedCostCallback], + val addPerItemCostCallbackOpt: Option[PerItemCostCallback] +) extends CoreByteWriter(w) { import CoreByteWriter._ import ValueSerializer._ + /** Adds the given cost to the callback if it is defined. */ + @inline private def addFixedCost(cost: OperationCostInfo[FixedCost]): Unit = { + if (addFixedCostCallbackOpt.isDefined) + addFixedCostCallbackOpt.get(cost) + } + + /** Adds the given cost to the callback if it is defined. */ + @inline private def addPerItemCost(cost: OperationCostInfo[PerItemCost], nItems: Int): Unit = { + if (addPerItemCostCallbackOpt.isDefined) + addPerItemCostCallbackOpt.get(cost, nItems) + } + + override def putChunk(chunk: w.CH): SigmaByteWriter.this.type = { + val start = length() + super.putChunk(chunk) + addPerItemCost(PutChunkCost, length() - start) + this + } + + override def put(x: Byte): this.type = { + addFixedCost(PutByteCost) + super.put(x) + } + override def put(x: Byte, info: DataInfo[Byte]): this.type = { ValueSerializer.addArgInfo(info) - checkCostLimit() + addFixedCost(PutByteCost) w.put(x); this } @@ -34,90 +58,160 @@ class SigmaByteWriter( super.putUByte(x) } - @inline override def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = { + override def putBoolean(x: Boolean): this.type = { + addFixedCost(PutByteCost) + super.putBoolean(x) + } + + override def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = { ValueSerializer.addArgInfo(info) - w.putBoolean(x); - checkCostLimit() - this + addFixedCost(PutByteCost) + w.putBoolean(x); this + } + + override def putShort(x: Short): this.type = { + addFixedCost(PutSignedNumericCost) + super.putShort(x) } - @inline override def putShort(x: Short, info: DataInfo[Short]): this.type = { + override def putShort(x: Short, info: DataInfo[Short]): this.type = { ValueSerializer.addArgInfo(info) - w.putShort(x); - checkCostLimit() - this + addFixedCost(PutSignedNumericCost) + w.putShort(x); this + } + + override def putUShort(x: Int): this.type = { + addFixedCost(PutUnsignedNumericCost) + super.putUShort(x) } - @inline override def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = { + override def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = { ValueSerializer.addArgInfo(info) - w.putUShort(x); - checkCostLimit() - this + addFixedCost(PutUnsignedNumericCost) + w.putUShort(x); this } - @inline override def putInt(x: Int, info: DataInfo[Int]): this.type = { + override def putInt(x: Int): this.type = { + addFixedCost(PutSignedNumericCost) + super.putInt(x) + } + + override def putInt(x: Int, info: DataInfo[Int]): this.type = { ValueSerializer.addArgInfo(info) - w.putInt(x); - checkCostLimit() - this + addFixedCost(PutSignedNumericCost) + w.putInt(x); this } - @inline override def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = { + override def putUInt(x: Long): SigmaByteWriter.this.type = { + super.putUInt(x) + } + + override def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = { ValueSerializer.addArgInfo(info) - w.putUInt(x); - checkCostLimit() - this + addFixedCost(PutUnsignedNumericCost) + w.putUInt(x); this } - @inline override def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = { + override def putLong(x: Long): SigmaByteWriter.this.type = { + addFixedCost(PutSignedNumericCost) + super.putLong(x) + } + + override def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = { ValueSerializer.addArgInfo(info) - w.putLong(x); - checkCostLimit() - this + addFixedCost(PutSignedNumericCost) + w.putLong(x); this } - @inline override def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = { + override def putULong(x: Long): SigmaByteWriter.this.type = { + addFixedCost(PutUnsignedNumericCost) + super.putULong(x) + } + + override def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = { ValueSerializer.addArgInfo(info) - w.putULong(x); - checkCostLimit() - this + addFixedCost(PutUnsignedNumericCost) + w.putULong(x); this + } + + override def putBytes(xs: Array[Byte], offset: Int, length: Int): this.type = { + addPerItemCost(PutChunkCost, length) + super.putBytes(xs, offset, length) + } + + override def putBytes(xs: Array[Byte]): SigmaByteWriter.this.type = { + addPerItemCost(PutChunkCost, xs.length) + super.putBytes(xs) } - @inline override def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = { + override def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = { ValueSerializer.addArgInfo(info) - w.putBytes(xs); - checkCostLimit() - this + addPerItemCost(PutChunkCost, xs.length) + w.putBytes(xs); this + } + + /** Put the two bytes of the big-endian representation of the Short value into the + * writer. */ + override def putShortBytes(value: Short): SigmaByteWriter.this.type = { + addPerItemCost(PutChunkCost, 2) + super.putShortBytes(value) } - @inline override def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = { + override def putBits(xs: Array[Boolean]): SigmaByteWriter.this.type = { + addPerItemCost(PutChunkCost, xs.length) // number of bits + super.putBits(xs) + } + + override def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = { ValueSerializer.addArgInfo(info) - w.putBits(xs); - checkCostLimit() - this + addPerItemCost(PutChunkCost, xs.length) // number of bits + w.putBits(xs); this } - @inline override def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = { + override def putOption[T](x: Option[T])(putValueC: (this.type, T) => Unit): this.type = { + addFixedCost(PutByteCost) // cost of option tag byte + super.putOption(x)(putValueC) + } + + override def putShortString(s: String): SigmaByteWriter.this.type = { + addPerItemCost(PutChunkCost, s.length) + super.putShortString(s) + } + + override def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = { ValueSerializer.addArgInfo(info) - TypeSerializer.serialize(x, this); // the cost is checked in TypeSerializer + TypeSerializer.serialize(x, this); // the cost is added in TypeSerializer this } - @inline def putValue[T <: SType](x: Value[T]): this.type = { - ValueSerializer.serialize(x, this) // the cost is checked in ValueSerializer + /** Serializes the given expression using [[ValueSerializer]]. */ + def putValue[T <: SType](x: Value[T]): this.type = { + ValueSerializer.serialize(x, this) // the cost is added in ValueSerializer this } - @inline def putValue[T <: SType](x: Value[T], info: DataInfo[SValue]): this.type = { + + /** Serializes the given expression using [[ValueSerializer]]. + * @param x the ErgoTree expression to serialize + * @param info additional information about the data being serialized + */ + def putValue[T <: SType](x: Value[T], info: DataInfo[SValue]): this.type = { ValueSerializer.addArgInfo(info) - ValueSerializer.serialize(x, this); // the cost is checked in ValueSerializer + ValueSerializer.serialize(x, this); // the cost is added in ValueSerializer this } - @inline def putValues[T <: SType](xs: Seq[Value[T]]): this.type = { + + /** Serializes the given sequence of expressions using [[ValueSerializer]]. */ + def putValues[T <: SType](xs: Seq[Value[T]]): this.type = { putUInt(xs.length) xs.foreach(putValue(_)) this } - @inline def putValues[T <: SType](xs: Seq[Value[T]], info: DataInfo[Seq[SValue]], itemInfo: DataInfo[SValue]): this.type = { + + /** Serializes the given sequence of expressions using [[ValueSerializer]]. + * @param xs the sequence of ErgoTree expressions to serialize + * @param info additional information about the data being serialized + */ + def putValues[T <: SType](xs: Seq[Value[T]], info: DataInfo[Seq[SValue]], itemInfo: DataInfo[SValue]): this.type = { putUInt(xs.length, valuesLengthInfo) foreach("\\#items", xs) { x => putValue(x, itemInfo) @@ -127,6 +221,40 @@ class SigmaByteWriter( } object SigmaByteWriter { + + /** Callback to accumulate fixed costs. */ + type FixedCostCallback = OperationCostInfo[FixedCost] => Unit + + /** Callback to accumulate per-item costs (chunked cost). */ + type PerItemCostCallback = (OperationCostInfo[PerItemCost], Int) => Unit + + /** Cost of writing single byte without any encoding. + * This also include overhead of method calls. + * This is the minimal possible JitCost value + */ + val PutByteCost = OperationCostInfo(FixedCost(JitCost(1)), NamedDesc("SigmaByteWriter.put")) + + /** Cost of writing a signed numeric including: + * 1) allocation of VLQ buffer array (see putULong in [[scorex.util.serialization.VLQWriter]]) + * 2) VLQ encoding + * 3) overhead of method calls. + */ + val PutUnsignedNumericCost = OperationCostInfo(FixedCost(JitCost(3)), NamedDesc("SigmaByteWriter.putUNumeric")) + + /** Cost of writing a signed numeric including: + * 1) ZigZag encoding. + * 2) allocation of VLQ buffer array (see putULong in [[scorex.util.serialization.VLQWriter]]) + * 3) VLQ encoding + * 4) overhead of method calls. + */ + val PutSignedNumericCost = OperationCostInfo(FixedCost(JitCost(3)), NamedDesc("SigmaByteWriter.putNumeric")) + + /** Cost of writing a chunk of bytes: + * 1) method call overhead + * 2) 1 cost unit per byte + */ + val PutChunkCost = OperationCostInfo(PerItemCost(JitCost(3), JitCost(1), 1), NamedDesc("SigmaByteWriter.putChunk")) + implicit case object ValueFmt extends FormatDescriptor[SValue] { override def size: String = "[1, *]" override def toString: String = "Expr" diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala index e842258f80..7da7ec1606 100644 --- a/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala @@ -4,7 +4,7 @@ import java.nio.ByteBuffer import scorex.util.ByteArrayBuilder import scorex.util.serialization._ import sigma.data.SigmaConstants -import sigma.serialization.CoreByteWriter.CostLimitChecker +import sigma.serialization.SigmaByteWriter.{FixedCostCallback, PerItemCostCallback} import sigma.serialization.ValueCodes.OpCode object SigmaSerializer { @@ -52,14 +52,18 @@ object SigmaSerializer { def startWriter(): SigmaByteWriter = { val b = new ByteArrayBuilder() val wi = new VLQByteBufferWriter(b) - val w = new SigmaByteWriter(wi, constantExtractionStore = None, checkCostLimitOpt = None) + val w = new SigmaByteWriter(wi, constantExtractionStore = None, addFixedCostCallbackOpt = None, addPerItemCostCallbackOpt = None) w } - def startWriter(constantExtractionStore: ConstantStore, checkCostLimit: Option[CostLimitChecker] = None): SigmaByteWriter = { + def startWriter( + constantExtractionStore: Option[ConstantStore], + addFixedCostCallback: Option[FixedCostCallback] = None, + addPerItemCostCallback: Option[PerItemCostCallback] = None + ): SigmaByteWriter = { val b = new ByteArrayBuilder() val wi = new VLQByteBufferWriter(b) - val w = new SigmaByteWriter(wi, constantExtractionStore = Some(constantExtractionStore), checkCostLimit) + val w = new SigmaByteWriter(wi, constantExtractionStore = constantExtractionStore, addFixedCostCallback, addPerItemCostCallback) w } } diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index f14ba275ff..5e43eb18e1 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -255,13 +255,21 @@ class SigmaDslTesting extends AnyPropSpec fail( s"""Should succeed with the same value or fail with the same exception, but was: - |First result: $b1 - |Second result: $b2 + |First result: ${errorWithStack(b1)} + |Second result: ${errorWithStack(b2)} |Root cause: $cause |""".stripMargin) } } + private def errorWithStack[A](e: Try[A]): String = e match { + case Failure(t) => + val sw = new java.io.StringWriter + t.printStackTrace(new java.io.PrintWriter(sw)) + sw.toString + case _ => e.toString + } + /** Creates a new ErgoLikeContext using given [[CContext]] as template. * Copies most of the data from ctx and the missing data is taken from the args. * This is a helper method to be used in tests only. From 97377c2f93e2eef527af5343a784819091409f39 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 3 Jun 2024 11:29:37 +0200 Subject: [PATCH 07/78] v6.0-serialize: cleanup + ScalaDocs --- .../sigma/serialization/CoreByteWriter.scala | 101 +++++++++++++++--- .../sigma/serialization/SigmaByteWriter.scala | 2 +- 2 files changed, 90 insertions(+), 13 deletions(-) diff --git a/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala index 37d1455ba3..92d85ed9d8 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala @@ -7,7 +7,7 @@ import sigma.serialization.CoreByteWriter._ /** Implementation of [[Writer]] provided by `sigma-core` module. * - * @param w destination [[Writer]] to which all the call got delegated. + * @param w destination [[Writer]] to which all the call got delegated. */ class CoreByteWriter(val w: Writer) extends Writer { type CH = w.CH @@ -26,6 +26,10 @@ class CoreByteWriter(val w: Writer) extends Writer { w.put(x); this } + /** Put the given byte into the writer. + * @param x the byte to put into the writer + * @param info meta information about the data being put into the writer + */ @inline def put(x: Byte, info: DataInfo[Byte]): this.type = { w.put(x); this } @@ -34,60 +38,109 @@ class CoreByteWriter(val w: Writer) extends Writer { super.putUByte(x) } + /** Encode integer as an unsigned byte asserting the range check + * @param x integer value to encode (should be in the range of unsigned byte) + * @param info meta information about the data being put into the writer + * @return + * @throws AssertionError if x is outside of the unsigned byte range + */ def putUByte(x: Int, info: DataInfo[U[Byte]]): this.type = { super.putUByte(x) } - @inline def putBoolean(x: Boolean): this.type = { + @inline override def putBoolean(x: Boolean): this.type = { w.putBoolean(x); this } + /** Encode boolean by delegating to the underlying writer. + * @param x boolean value to encode + * @param info meta information about the data being put into the writer + * @return + */ @inline def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = { w.putBoolean(x); this } - @inline def putShort(x: Short): this.type = { + @inline override def putShort(x: Short): this.type = { w.putShort(x); this } + /** Encode signed Short by delegating to the underlying writer. + * + * Use [[putUShort]] to encode values that are positive. + * @param x short value to encode + * @param info meta information about the data being put into the writer + */ @inline def putShort(x: Short, info: DataInfo[Short]): this.type = { w.putShort(x); this } - @inline def putUShort(x: Int): this.type = { + @inline override def putUShort(x: Int): this.type = { w.putUShort(x); this } + /** Encode Short that are positive by delegating to the underlying writer. + * + * Use [[putShort]] to encode values that might be negative. + * @param x unsigned short value (represented as Int) to encode + * @param info meta information about the data being put into the writer + */ @inline def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = { w.putUShort(x); this } - @inline def putInt(x: Int): this.type = { + @inline override def putInt(x: Int): this.type = { w.putInt(x); this } + /** Encode signed Int by delegating to the underlying writer. + * Use [[putUInt]] to encode values that are positive. + * + * @param x integer value to encode + * @param info meta information about the data being put into the writer + */ @inline def putInt(x: Int, info: DataInfo[Int]): this.type = { w.putInt(x); this } - @inline def putUInt(x: Long): this.type = { + @inline override def putUInt(x: Long): this.type = { w.putUInt(x); this } + + /** Encode Int that are positive by delegating to the underlying writer. + * Use [[putInt]] to encode values that might be negative. + * + * @param x unsigned integer value (represented as Long) to encode + * @param info meta information about the data being put into the writer + */ @inline def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = { w.putUInt(x); this } - @inline def putLong(x: Long): this.type = { + @inline override def putLong(x: Long): this.type = { w.putLong(x); this } + + /** Encode signed Long by delegating to the underlying writer. + * Use [[putULong]] to encode values that are positive. + * + * @param x long value to encode + * @param info meta information about the data being put into the writer + */ @inline def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = { w.putLong(x); this } - @inline def putULong(x: Long): this.type = { + @inline override def putULong(x: Long): this.type = { w.putULong(x); this } + /** Encode Long that are positive by delegating to the underlying writer. + * Use [[putLong]] to encode values that might be negative. + * + * @param x unsigned long value to encode + * @param info meta information about the data being put into the writer + */ @inline def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = { w.putULong(x); this } @@ -98,9 +151,14 @@ class CoreByteWriter(val w: Writer) extends Writer { w.putBytes(xs, offset, length); this } - @inline def putBytes(xs: Array[Byte]): this.type = { + @inline override def putBytes(xs: Array[Byte]): this.type = { w.putBytes(xs); this } + + /** Encode an array of bytes by delegating to the underlying writer. + * @param xs array of bytes to encode + * @param info meta information about the data being put into the writer + */ @inline def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = { w.putBytes(xs); this } @@ -113,21 +171,27 @@ class CoreByteWriter(val w: Writer) extends Writer { this } - @inline def putBits(xs: Array[Boolean]): this.type = { + @inline override def putBits(xs: Array[Boolean]): this.type = { w.putBits(xs); this } + + /** Encode an array of boolean values as a bit array (packing bits into bytes) + * + * @param xs array of boolean values + * @param info meta information about the data being put into the writer + */ @inline def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = { w.putBits(xs); this } - @inline def putOption[T](x: Option[T])(putValueC: (this.type, T) => Unit): this.type = { + @inline override def putOption[T](x: Option[T])(putValueC: (this.type, T) => Unit): this.type = { w.putOption(x) { (_, v) => putValueC(this, v) } this } - @inline def putShortString(s: String): this.type = { + @inline override def putShortString(s: String): this.type = { w.putShortString(s); this } @@ -137,10 +201,18 @@ class CoreByteWriter(val w: Writer) extends Writer { case wr: VLQByteBufferWriter => wr.toBytes } + /** Serialize the given type into the writer using [[TypeSerializer]]. + * @param x the type to put into the writer + */ @inline def putType[T <: SType](x: T): this.type = { TypeSerializer.serialize(x, this) this } + + /** Serialize the given type into the writer using [[TypeSerializer]]. + * @param x the type to put into the writer + * @param info meta information about the data being put into the writer + */ @inline def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = { TypeSerializer.serialize(x, this) this @@ -263,6 +335,11 @@ object CoreByteWriter { * @param description argument description. */ case class ArgInfo(name: String, description: String) + /** Represents meta information about serialized data. + * Passed as additional argument of serializer methods. + * Can be used to automatically generate format specifications based on + * the actual collected method invocations. + */ case class DataInfo[T](info: ArgInfo, format: FormatDescriptor[T]) object DataInfo { diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala index 93e078e319..7bda855005 100644 --- a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala @@ -192,7 +192,7 @@ class SigmaByteWriter( /** Serializes the given expression using [[ValueSerializer]]. * @param x the ErgoTree expression to serialize - * @param info additional information about the data being serialized + * @param info meta information about the data being serialized */ def putValue[T <: SType](x: Value[T], info: DataInfo[SValue]): this.type = { ValueSerializer.addArgInfo(info) From 767fbb4af9e420c42a0d43e8d7c2945a46046749 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 3 Jun 2024 15:54:54 +0200 Subject: [PATCH 08/78] v6.0-serialize: tests for cost trace --- .../src/main/scala/sigma/ast/methods.scala | 5 +- .../sigma/serialization/SigmaByteWriter.scala | 6 ++ .../scala/sigma/LanguageSpecificationV6.scala | 56 +++++++++++---- .../test/scala/sigma/SigmaDslTesting.scala | 71 +++++++++++++------ 4 files changed, 101 insertions(+), 37 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index fd9024ee62..ef548d6185 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -12,7 +12,7 @@ import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConsta import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} import sigma.reflection.RClass import sigma.serialization.CoreByteWriter.ArgInfo -import sigma.serialization.{DataSerializer, SigmaSerializer} +import sigma.serialization.{DataSerializer, SigmaByteWriter, SigmaSerializer} import sigma.utils.SparseArrayContainer import scala.annotation.unused @@ -1534,6 +1534,8 @@ case object SGlobalMethods extends MonoTypeMethods { def serialize_eval(mc: MethodCall, G: SigmaDslBuilder, value: SType#WrappedType) (implicit E: ErgoTreeEvaluator): Coll[Byte] = { + E.addCost(SigmaByteWriter.StartWriterCost) + val addFixedCostCallback = { (costInfo: OperationCostInfo[FixedCost]) => E.addCost(costInfo) } @@ -1542,6 +1544,7 @@ case object SGlobalMethods extends MonoTypeMethods { } val w = SigmaSerializer.startWriter(None, Some(addFixedCostCallback), Some(addPerItemCostCallback)) + DataSerializer.serialize(value, mc.args(0).tpe, w) Colls.fromArray(w.toBytes) } diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala index 7bda855005..db9312240f 100644 --- a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala @@ -228,6 +228,12 @@ object SigmaByteWriter { /** Callback to accumulate per-item costs (chunked cost). */ type PerItemCostCallback = (OperationCostInfo[PerItemCost], Int) => Unit + /** Cost of instantiating a new serializer. + * This also include overhead of method calls. + * This is the minimal possible JitCost value + */ + val StartWriterCost = OperationCostInfo(FixedCost(JitCost(10)), NamedDesc("SigmaByteWriter.startWriter")) + /** Cost of writing single byte without any encoding. * This also include overhead of method calls. * This is the minimal possible JitCost value diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 20faabe128..e4ae10cac6 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,8 +1,8 @@ package sigma -import sigma.ast.{Downcast, FuncValue, Global, MethodCall, SBigInt, SByte, SGlobalMethods, SInt, SLong, SShort, STypeVar, ValUse} +import sigma.ast.{Apply, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, Global, JitCost, MethodCall, NamedDesc, OptionGet, SBigInt, SByte, SGlobalMethods, SInt, SLong, SShort, STypeVar, ValUse} import sigma.data.{CBigInt, ExactNumeric, RType} -import sigma.eval.SigmaDsl +import sigma.eval.{SigmaDsl, TracedCost} import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound @@ -17,6 +17,8 @@ import scala.util.Success class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => override def languageVersion: Byte = VersionContext.V6SoftForkVersion + implicit override def evalSettings = super.evalSettings.copy(printTestVectors = true) + def mkSerializeFeature[A: RType]: Feature[A, Coll[Byte]] = { val tA = RType[A] val tpe = Evaluation.rtypeToSType(tA) @@ -35,28 +37,54 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => sinceVersion = VersionContext.V6SoftForkVersion) } + val baseTrace = Array( + FixedCostItem(Apply), + FixedCostItem(FuncValue), + FixedCostItem(GetVar), + FixedCostItem(OptionGet), + FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))) + ) + property("Global.serialize[Byte]") { lazy val serializeByte = mkSerializeFeature[Byte] + val expectedCostTrace = TracedCost( + baseTrace ++ Array( + FixedCostItem(Global), + FixedCostItem(MethodCall), + FixedCostItem(ValUse), + FixedCostItem(NamedDesc("SigmaByteWriter.startWriter"), FixedCost(JitCost(10))), + FixedCostItem(NamedDesc("SigmaByteWriter.put"), FixedCost(JitCost(1))) + ) + ) val cases = Seq( - (-128.toByte, Success(Coll(-128.toByte))), - (-1.toByte, Success(Coll(-1.toByte))), - (0.toByte, Success(Coll(0.toByte))), - (1.toByte, Success(Coll(1.toByte))), - (127.toByte, Success(Coll(127.toByte))) + (-128.toByte, Expected(Success(Coll(-128.toByte)), expectedCostTrace)), + (-1.toByte, Expected(Success(Coll(-1.toByte)), expectedCostTrace)), + (0.toByte, Expected(Success(Coll(0.toByte)), expectedCostTrace)), + (1.toByte, Expected(Success(Coll(1.toByte)), expectedCostTrace)), + (127.toByte, Expected(Success(Coll(127.toByte)), expectedCostTrace)) ) - testCases(cases, serializeByte) + verifyCases(cases, serializeByte, preGeneratedSamples = None) } property("Global.serialize[Short]") { lazy val serializeShort = mkSerializeFeature[Short] + val expectedCostTrace = TracedCost( + baseTrace ++ Array( + FixedCostItem(Global), + FixedCostItem(MethodCall), + FixedCostItem(ValUse), + FixedCostItem(NamedDesc("SigmaByteWriter.startWriter"), FixedCost(JitCost(10))), + FixedCostItem(NamedDesc("SigmaByteWriter.putNumeric"), FixedCost(JitCost(3))) + ) + ) val cases = Seq( - (Short.MinValue, Success(Coll[Byte](0xFF.toByte, 0xFF.toByte, 0x03.toByte))), - (-1.toShort, Success(Coll(1.toByte))), - (0.toShort, Success(Coll(0.toByte))), - (1.toShort, Success(Coll(2.toByte))), - (Short.MaxValue, Success(Coll(-2.toByte, -1.toByte, 3.toByte))) + (Short.MinValue, Expected(Success(Coll[Byte](0xFF.toByte, 0xFF.toByte, 0x03.toByte)), expectedCostTrace)), + (-1.toShort, Expected(Success(Coll(1.toByte)), expectedCostTrace)), + (0.toShort, Expected(Success(Coll(0.toByte)), expectedCostTrace)), + (1.toShort, Expected(Success(Coll(2.toByte)), expectedCostTrace)), + (Short.MaxValue, Expected(Success(Coll(-2.toByte, -1.toByte, 3.toByte)), expectedCostTrace)) ) - testCases(cases, serializeShort) + verifyCases(cases, serializeShort, preGeneratedSamples = None) } // TODO v6.0: implement serialization roundtrip tests after merge with deserializeTo diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 5e43eb18e1..37fb0e6503 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -179,6 +179,34 @@ class SigmaDslTesting extends AnyPropSpec true } + /** Checks the result of feature execution against expected result. + * If settings.failOnTestVectors == true, then print out actual cost results + * + * @param res the result of feature execution + * @param expected the expected result + */ + protected def checkResultAgainstExpected(res: Try[(B, CostDetails)], expected: Expected[B]): Unit = { + val newRes = expected.newResults(ergoTreeVersionInTests) + val expectedTrace = newRes._2.fold(Seq.empty[CostItem])(_.trace) + if (expectedTrace.isEmpty) { + // new cost expectation is missing, print out actual cost results + if (evalSettings.printTestVectors) { + res.foreach { case (_, newDetails) => + printCostDetails(script, newDetails) + } + } + } + else { + // new cost expectation is specified, compare it with the actual result + res.foreach { case (_, newDetails) => + if (newDetails.trace != expectedTrace) { + printCostDetails(script, newDetails) + newDetails.trace shouldBe expectedTrace + } + } + } + } + /** v3 and v4 implementation*/ private var _oldF: Try[CompiledFunc[A, B]] = _ def oldF: CompiledFunc[A, B] = { @@ -624,28 +652,10 @@ class SigmaDslTesting extends AnyPropSpec checkResult(funcRes.map(_._1), expected.value, failOnTestVectors, "ExistingFeature#verifyCase: ") - val newRes = expected.newResults(ergoTreeVersionInTests) - val expectedTrace = newRes._2.fold(Seq.empty[CostItem])(_.trace) - if (expectedTrace.isEmpty) { - // new cost expectation is missing, print out actual cost results - if (evalSettings.printTestVectors) { - funcRes.foreach { case (_, newDetails) => - printCostDetails(script, newDetails) - } - } - } - else { - // new cost expectation is specified, compare it with the actual result - funcRes.foreach { case (_, newDetails) => - if (newDetails.trace != expectedTrace) { - printCostDetails(script, newDetails) - newDetails.trace shouldBe expectedTrace - } - } - } - + checkResultAgainstExpected(funcRes, expected) checkVerify(input, expected) } + } /** Descriptor of a language feature which is changed in v5.0. @@ -921,8 +931,11 @@ class SigmaDslTesting extends AnyPropSpec printTestCases: Boolean, failOnTestVectors: Boolean): Unit = { val funcRes = checkEquality(input, printTestCases) - funcRes.isFailure shouldBe true - Try(scalaFunc(input)) shouldBe expected.value + if (this.isSupportedIn(VersionContext.current)) { + checkResultAgainstExpected(funcRes, expected) + } else + funcRes.isFailure shouldBe true + Try(scalaFuncNew(input)) shouldBe expected.value } } @@ -990,6 +1003,20 @@ class SigmaDslTesting extends AnyPropSpec } } + /** Used when the old and new value are the same for all versions + * and the expected costs are not specified. + * + * @param value expected result of tested function + * @param expectedDetails expected cost details for all versions + */ + def apply[A](value: Try[A], expectedDetails: CostDetails): Expected[A] = + new Expected(ExpectedResult(value, None)) { + override val newResults = defaultNewResults.map { + case (ExpectedResult(v, _), _) => + (ExpectedResult(v, None), Some(expectedDetails)) + } + } + /** Used when the old and new value and costs are the same for all versions. * * @param value expected result of tested function From 744d8a58bc7c86c84f2aa1c5694db935a3172260 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 3 Jun 2024 22:23:54 +0300 Subject: [PATCH 09/78] conversion test stub --- .../src/main/scala/sigma/ast/SigmaPredef.scala | 7 ++++++- .../sigmastate/utxo/BasicOpsSpecification.scala | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index d8c65a7afe..752b69f636 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -196,7 +196,12 @@ object SigmaPredef { Lambda(Array("input" -> SString), SUnsignedBigInt, None), PredefFuncInfo( { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => - UnsignedBigIntConstant(new BigInteger(arg.value)) + val bi = new BigInteger(arg.value) + if (bi.compareTo(BigInteger.ZERO) >= 0) { + UnsignedBigIntConstant(bi) + } else { + throw new InvalidArguments(s"Negative argument for unsignedBigInt()") + } }), OperationInfo(Constant, """Parsing string literal argument as a 256-bit unsigned big integer.""".stripMargin, diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 28084be987..e6f08f75fc 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -169,7 +169,21 @@ class BasicOpsSpecification extends CompilerTestingCommons deserTest() } - property("signed <-> unsigned bigint conversion") { + property("signed <-> unsigned bigint conversion - positive bigint") { + val b = new BigInteger("9280562930080889354892980449861222646750586663683904599823322027983929189860") + val ub = new BigInteger(1, b.toByteArray) + + // todo: how to upcast? + def deserTest() = {test("restoring", env, ext, + s"{ val b = bigInt(\"${ub.toString}\"); b > 1 }", + null, + true + )} + + deserTest() + } + + property("signed <-> unsigned bigint conversion - negative bigint") { } From 2acc6db89d96659d1efcb6aa3ddae2ec562a132f Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 4 Jun 2024 11:08:17 +0300 Subject: [PATCH 10/78] more todos --- core/shared/src/main/scala/sigma/ast/SType.scala | 2 +- data/shared/src/main/scala/sigma/ast/methods.scala | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index c72ba65e73..4be9087ca7 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -340,7 +340,7 @@ trait SNumericType extends SProduct with STypeCompanion { object SNumericType extends STypeCompanion { /** Array of all numeric types ordered by number of bytes in the representation. */ - final val allNumericTypes = Array(SByte, SShort, SInt, SLong, SBigInt) + final val allNumericTypes = Array(SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt) // TODO v6.0: this typeId is now shadowed by SGlobal.typeId // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index e4cf0007e0..4656d3361e 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -210,6 +210,8 @@ object SNumericTypeMethods extends MethodsContainer { .withCost(costOfNumericCast) .withInfo(PropertyCall, "Converts this numeric value to \\lst{BigInt}") + // todo: ToUnsignedBigInt + /** Cost of: 1) creating Byte collection from a numeric value */ val ToBytes_CostKind = FixedCost(JitCost(5)) @@ -316,7 +318,10 @@ case object SBigIntMethods extends SNumericTypeMethods { val ToNBits = SMethod(this, "nbits", SFunc(this.ownerType, SLong), 8, ToNBitsCostInfo.costKind) .withInfo(ModQ, "Encode this big integer value as NBits") - /** The following `modQ` methods are not fully implemented in v4.x and this descriptors. + /** + * todo: remove + * + * 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. From 37ac9212daf09fe899dd9b7981668f1c0a69e979 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 6 Jun 2024 16:34:40 +0300 Subject: [PATCH 11/78] BasicOpsSpecification tests for serialize --- .../utxo/BasicOpsSpecification.scala | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..29443406c5 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -2,7 +2,10 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ +import scorex.util.encode.Base16 import sigma.Extensions.ArrayOps +import sigma.SigmaTestingData +import sigma.VersionContext.V6SoftForkVersion import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -19,6 +22,7 @@ import sigmastate.interpreter.Interpreter._ import sigma.ast.Apply import sigma.eval.EvalSettings import sigma.exceptions.InvalidType +import sigmastate.utils.Helpers import sigmastate.utils.Helpers._ import java.math.BigInteger @@ -157,6 +161,108 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("serialize - collection of boxes") { + def deserTest() = test("serialize", env, ext, + s"""{ + val boxes = INPUTS; + Global.serialize(boxes).size > 0 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.exceptions.TyperException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("serialize - optional collection") { + def deserTest() = test("serialize", env, ext, + s"""{ + val opt = SELF.R1[Coll[Byte]]; + Global.serialize(opt).size > SELF.R1[Coll[Byte]].get.size + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.exceptions.TyperException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + // the test shows that serialize(groupElement) is the same as groupElement.getEncoded + property("serialize - group element - equivalence with .getEncoded") { + val ge = Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b") + // val ba = Base16.encode(ge.getEncoded.toArray) + def deserTest() = test("serialize", env, Seq(21.toByte -> GroupElementConstant(ge)), + s"""{ + val ge = getVar[GroupElement](21).get + val ba = serialize(ge); + ba == ge.getEncoded + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [Exception] should be thrownBy deserTest() + } else { + deserTest() + } + } + + // the test shows that serialize(sigmaProp) is the same as sigmaProp.propBytes without first 2 bytes + property("serialize and .propBytes correspondence") { + def deserTest() = test("deserializeTo", env, ext, + s"""{ + val p1 = getVar[SigmaProp]($propVar1).get + val bytes = p1.propBytes + val ba = bytes.slice(2, bytes.size) + val ba2 = serialize(p1) + ba == ba2 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [Exception] should be thrownBy deserTest() + } else { + deserTest() + } + } + + // todo: failing, needs for Header (de)serialization support from https://github.com/ScorexFoundation/sigmastate-interpreter/pull/972 + property("serialize - collection of collection of headers") { + val td = new SigmaTestingData {} + val h1 = td.TestData.h1 + + val customExt = Seq(21.toByte -> HeaderConstant(h1)) + + def deserTest() = test("serialize", env, customExt, + s"""{ + val h1 = getVar[Header](21).get; + val c = Coll(Coll(h1)) + Global.serialize(c).size > 0 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.exceptions.TyperException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + // todo: roundtrip tests with deserializeTo from https://github.com/ScorexFoundation/sigmastate-interpreter/pull/979 + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 074d609ac854b9c42498efba46bcca22d0d5cdbc Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 6 Jun 2024 17:08:23 +0300 Subject: [PATCH 12/78] MethodCall deserialization round trip for Global.serialize --- .../MethodCallSerializerSpecification.scala | 23 +++++++++++++++++++ .../utxo/BasicOpsSpecification.scala | 18 ++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala index 1db166c685..e78518f8b3 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala @@ -1,6 +1,8 @@ package sigma.serialization import sigma.VersionContext +import sigma.ast.SCollection.SByteArray +import sigma.ast.SType.tT import sigma.ast._ import sigma.validation.ValidationException @@ -45,4 +47,25 @@ class MethodCallSerializerSpecification extends SerializationSpecification { } ) } + + property("MethodCall deserialization round trip for Global.serialize") { + def code = { + val b = ByteArrayConstant(Array(1.toByte, 2.toByte, 3.toByte)) + val expr = MethodCall(Global, + SGlobalMethods.serializeMethod.withConcreteTypes(Map(STypeVar("T") -> SByteArray)), + Vector(b), + Map() + ) + roundTripTest(expr) + } + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + code + } + + an[Exception] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + code + }) + } } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 29443406c5..13bf74b5f6 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -2,7 +2,6 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ -import scorex.util.encode.Base16 import sigma.Extensions.ArrayOps import sigma.SigmaTestingData import sigma.VersionContext.V6SoftForkVersion @@ -161,6 +160,23 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("serialize - byte array") { + def deserTest() = test("serialize", env, ext, + s"""{ + val ba = fromBase16("c0ffee"); + Global.serialize(ba).size > ba.size + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.exceptions.TyperException] should be thrownBy deserTest() + } else { + deserTest() + } + } + property("serialize - collection of boxes") { def deserTest() = test("serialize", env, ext, s"""{ From bf53a6b67e22101bb13e8867c43ab6a4ad327302 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 6 Jun 2024 18:20:33 +0300 Subject: [PATCH 13/78] spam tests, test for serialize(long) producing different result from longToByteArray() --- .../utxo/BasicOpsSpecification.scala | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 13bf74b5f6..52756b640f 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -20,7 +20,7 @@ import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings import sigmastate.interpreter.Interpreter._ import sigma.ast.Apply import sigma.eval.EvalSettings -import sigma.exceptions.InvalidType +import sigma.exceptions.{CostLimitException, InvalidType} import sigmastate.utils.Helpers import sigmastate.utils.Helpers._ @@ -211,6 +211,25 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("serialize(long) is producing different result from longToByteArray()") { + def deserTest() = test("serialize", env, ext, + s"""{ + val l = -1000L + val ba1 = Global.serialize(l); + val ba2 = longToByteArray(l) + ba1 != ba2 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.exceptions.TyperException] should be thrownBy deserTest() + } else { + deserTest() + } + } + // the test shows that serialize(groupElement) is the same as groupElement.getEncoded property("serialize - group element - equivalence with .getEncoded") { val ge = Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b") @@ -279,6 +298,52 @@ class BasicOpsSpecification extends CompilerTestingCommons // todo: roundtrip tests with deserializeTo from https://github.com/ScorexFoundation/sigmastate-interpreter/pull/979 + // todo: move spam tests to dedicated test suite? + property("serialize - not spam") { + val customExt = Seq(21.toByte -> ShortArrayConstant((1 to Short.MaxValue).map(_.toShort).toArray), + 22.toByte -> ByteArrayConstant(Array.fill(1)(1.toByte))) + def deserTest() = test("serialize", env, customExt, + s"""{ + val indices = getVar[Coll[Short]](21).get + val base = getVar[Coll[Byte]](22).get + + def check(index:Short): Boolean = { serialize(base) != base } + indices.forall(check) + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("serialize - spam attempt") { + val customExt = Seq(21.toByte -> ShortArrayConstant((1 to Short.MaxValue).map(_.toShort).toArray), + 22.toByte -> ByteArrayConstant(Array.fill(16000)(1.toByte))) + def deserTest() = test("serialize", env, customExt, + s"""{ + val indices = getVar[Coll[Short]](21).get + val base = getVar[Coll[Byte]](22).get + + def check(index:Short): Boolean = { serialize(base) != base } + indices.forall(check) + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy deserTest() + } else { + // we have wrapped CostLimitException here + an[Exception] should be thrownBy deserTest() + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 15e21a4af57a45ae9a9136ad14278ac7951611ab Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 7 Jun 2024 16:00:54 +0300 Subject: [PATCH 14/78] importing new test for ErgoTreeSpecification from i486 --- .../scala/sigmastate/ErgoTreeSpecification.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 7539bd5e48..21966aa0a9 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -21,14 +21,14 @@ import sigma.compiler.CompilerSettings import sigma.eval.EvalSettings import sigma.exceptions.{CostLimitException, InterpreterException} import sigma.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.Plus +import sigmastate.{CrossVersionProps, Plus} import sigmastate.utils.Helpers.TryOps /** Regression tests with ErgoTree related test vectors. * This test vectors verify various constants which are consensus critical and should not change. */ -class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { +class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with CrossVersionProps { property("Value.sourceContext") { val srcCtx = SourceContext.fromParserIndex(0, "") @@ -313,10 +313,12 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { */ case class MInfo(methodId: Byte, method: SMethod, isResolvableFromIds: Boolean = true) + def isV6Activated = VersionContext.current.isV6SoftForkActivated + // NOTE, the type code constants are checked above // The methodId codes as checked here, they MUST be PRESERVED. // The following table should be made dependent on HF activation - val methods = Table( + def methods = Table( ("typeId", "methods", "CanHaveMethods"), (SBoolean.typeId, Seq.empty[MInfo], true), (SByte.typeId, Seq.empty[MInfo], false), @@ -419,7 +421,10 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { { import SGlobalMethods._ (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) - ), true) + ) ++ (if (isV6Activated) Seq( + // methods added in v6.0 + MInfo(3, serializeMethod) + ) else Seq.empty), true) }, { import SCollectionMethods._ (SCollection.typeId, Seq( From 15f4f67ba83489dff1d2ea7df232b5bea15c01a4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 7 Jun 2024 21:50:00 +0300 Subject: [PATCH 15/78] removing modq methods --- .../src/main/scala/sigma/ast/methods.scala | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 4656d3361e..fc2e5f11d0 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -318,32 +318,9 @@ case object SBigIntMethods extends SNumericTypeMethods { val ToNBits = SMethod(this, "nbits", SFunc(this.ownerType, SLong), 8, ToNBitsCostInfo.costKind) .withInfo(ModQ, "Encode this big integer value as NBits") - /** - * todo: remove - * - * 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() ++ Seq(ToNBits) - // ModQMethod, - // PlusModQMethod, - // MinusModQMethod, - // TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - // MultModQMethod, } else { super.getMethods() } From 303f96986e56ac0f0c5863e328eb9d4771fb989f Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 8 Jun 2024 21:31:27 +0300 Subject: [PATCH 16/78] expUnsigned impl (test still failing) --- .../src/main/scala/sigma/SigmaDsl.scala | 2 ++ .../main/scala/sigma/data/CGroupElement.scala | 5 +++- .../sigma/reflection/ReflectionData.scala | 3 ++ .../src/main/scala/sigma/ast/methods.scala | 28 +++++++++++++++---- .../sigma/compiler/ir/GraphBuilding.scala | 3 ++ .../ir/wrappers/sigma/SigmaDslUnit.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 14 ++++++++++ .../sigma/compiler/phases/SigmaBinder.scala | 4 +++ .../sigma/compiler/phases/SigmaTyper.scala | 5 ++++ 9 files changed, 58 insertions(+), 7 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index cc102b6b36..723ce03bc6 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -298,6 +298,8 @@ trait GroupElement { */ def exp(k: BigInt): GroupElement + def expUnsigned(k: UnsignedBigInt): GroupElement + /** Group operation. */ def multiply(that: GroupElement): GroupElement diff --git a/core/shared/src/main/scala/sigma/data/CGroupElement.scala b/core/shared/src/main/scala/sigma/data/CGroupElement.scala index ed4849f0d7..c5483797cf 100644 --- a/core/shared/src/main/scala/sigma/data/CGroupElement.scala +++ b/core/shared/src/main/scala/sigma/data/CGroupElement.scala @@ -3,7 +3,7 @@ package sigma.data import sigma.crypto.{CryptoFacade, Ecp} import sigma.serialization.GroupElementSerializer import sigma.util.Extensions.EcpOps -import sigma.{BigInt, Coll, Colls, GroupElement} +import sigma.{BigInt, Coll, Colls, GroupElement, UnsignedBigInt} /** A default implementation of [[GroupElement]] interface. * @@ -21,6 +21,9 @@ case class CGroupElement(override val wrappedValue: Ecp) extends GroupElement wi override def exp(k: BigInt): GroupElement = CGroupElement(CryptoFacade.exponentiatePoint(wrappedValue, k.asInstanceOf[CBigInt].wrappedValue)) + override def expUnsigned(k: UnsignedBigInt): GroupElement = + CGroupElement(CryptoFacade.exponentiatePoint(wrappedValue, k.asInstanceOf[CUnsignedBigInt].wrappedValue)) + override def multiply(that: GroupElement): GroupElement = CGroupElement(CryptoFacade.multiplyPoints(wrappedValue, that.asInstanceOf[CGroupElement].wrappedValue)) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index 028e68bf72..e0e890abca 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -286,6 +286,9 @@ object ReflectionData { mkMethod(clazz, "exp", Array[Class[_]](classOf[BigInt])) { (obj, args) => obj.asInstanceOf[GroupElement].exp(args(0).asInstanceOf[BigInt]) }, + mkMethod(clazz, "expUnsigned", Array[Class[_]](classOf[UnsignedBigInt])) { (obj, args) => + obj.asInstanceOf[GroupElement].expUnsigned(args(0).asInstanceOf[UnsignedBigInt]) + }, mkMethod(clazz, "multiply", Array[Class[_]](classOf[GroupElement])) { (obj, args) => obj.asInstanceOf[GroupElement].multiply(args(0).asInstanceOf[GroupElement]) }, diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index fc2e5f11d0..8499ecd46e 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -364,6 +364,11 @@ case object SGroupElementMethods extends MonoTypeMethods { "Exponentiate this \\lst{GroupElement} to the given number. Returns this to the power of k", ArgInfo("k", "The power")) + lazy val ExponentiateUnsignedMethod: SMethod = SMethod( + this, "expUnsigned", SFunc(Array(this.ownerType, SUnsignedBigInt), this.ownerType), 6, Exponentiate.costKind) // todo: recheck costing + .withInfo("Exponentiate this \\lst{GroupElement} to the given number. Returns this to the power of k", + ArgInfo("k", "The power")) + lazy val MultiplyMethod: SMethod = SMethod( this, "multiply", SFunc(Array(this.ownerType, SGroupElement), this.ownerType), 4, MultiplyGroup.costKind) .withIRInfo({ case (builder, obj, _, Seq(arg), _) => @@ -379,16 +384,27 @@ case object SGroupElementMethods extends MonoTypeMethods { .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, "Inverse element of the group.") - protected override def getMethods(): Seq[SMethod] = super.getMethods() ++ Seq( + protected override def getMethods(): Seq[SMethod] = { /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 SMethod(this, "isIdentity", SFunc(this, SBoolean), 1) .withInfo(PropertyCall, "Checks if this value is identity element of the eliptic curve group."), */ - GetEncodedMethod, - ExponentiateMethod, - MultiplyMethod, - NegateMethod - ) + val v5Methods = Seq( + GetEncodedMethod, + ExponentiateMethod, + MultiplyMethod, + NegateMethod) + + super.getMethods() ++ (if (VersionContext.current.isV6SoftForkActivated) { + v5Methods + } else { + v5Methods ++ Seq(ExponentiateUnsignedMethod) + }) + } + + def expUnsigned_eval(mc: MethodCall, power: UnsignedBigInt)(implicit E: ErgoTreeEvaluator): GroupElement = { + ??? + } } /** Methods of type `SigmaProp` which represent sigma-protocol propositions. */ 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 3e3b576c3e..e2350b8338 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1024,6 +1024,9 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SGroupElementMethods.ExponentiateMethod.name => val k = asRep[BigInt](argsV(0)) ge.exp(k) + case SGroupElementMethods.ExponentiateUnsignedMethod.name => + val k = asRep[UnsignedBigInt](argsV(0)) + ge.expUnsigned(k) case _ => throwError } case (box: Ref[Box]@unchecked, SBoxMethods) => method.name match { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index 8616822dd1..b3be395a74 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -25,6 +25,7 @@ import scalan._ }; trait GroupElement extends Def[GroupElement] { def exp(k: Ref[BigInt]): Ref[GroupElement]; + def expUnsigned(k: Ref[UnsignedBigInt]): Ref[GroupElement]; def multiply(that: Ref[GroupElement]): Ref[GroupElement]; def negate: Ref[GroupElement]; def getEncoded: Ref[Coll[Byte]] diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index 060759099e..27ebaa717c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -444,6 +444,13 @@ object GroupElement extends EntityObject("GroupElement") { true, false, element[GroupElement])) } + override def expUnsigned(k: Ref[UnsignedBigInt]): Ref[GroupElement] = { + asRep[GroupElement](mkMethodCall(self, + GroupElementClass.getMethod("expUnsigned", classOf[Sym]), + Array[AnyRef](k), + true, false, element[GroupElement])) + } + override def multiply(that: Ref[GroupElement]): Ref[GroupElement] = { asRep[GroupElement](mkMethodCall(self, GroupElementClass.getMethod("multiply", classOf[Sym]), @@ -491,6 +498,13 @@ object GroupElement extends EntityObject("GroupElement") { true, true, element[GroupElement])) } + def expUnsigned(k: Ref[UnsignedBigInt]): Ref[GroupElement] = { + asRep[GroupElement](mkMethodCall(source, + GroupElementClass.getMethod("expUnsigned", classOf[Sym]), + Array[AnyRef](k), + true, true, element[GroupElement])) + } + def multiply(that: Ref[GroupElement]): Ref[GroupElement] = { asRep[GroupElement](mkMethodCall(source, GroupElementClass.getMethod("multiply", classOf[Sym]), diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala index af5be938be..1877131718 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala @@ -56,6 +56,10 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder, case _ @ Apply(ApplyTypes(Ident("Coll", _), Seq(tpe)), args) => Some(mkConcreteCollection(args, tpe)) + // hack to make possible to write g.exp(ubi) for both unsigned and signed big integers + case Apply(Select(obj, n, resType), args) if n == "exp" && args(0).isInstanceOf[Value[SUnsignedBigInt.type]] => + Some(Apply(Select(obj, "expUnsigned", resType), args)) + // Rule: Coll(...) --> case Apply(Ident("Coll", _), args) => val tpe = if (args.isEmpty) NoType else args(0).tpe diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index 679d98a18f..3d62ff8dd8 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -168,6 +168,11 @@ class SigmaTyper(val builder: SigmaBuilder, case app @ Apply(sel @ Select(obj, n, _), args) => val newSel = assignType(env, sel) val newArgs = args.map(assignType(env, _)) + if(n=="expUnsigned") { + println(app) + println("newSel: " + newSel) + println("newArgs: " + newArgs) + } newSel.tpe match { case genFunTpe @ SFunc(argTypes, _, _) => // If it's a function then the application has type of that function's return type. From 982f32f4920f176c6f6d3310e1dfc843791d1503 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 9 Jun 2024 15:48:00 +0300 Subject: [PATCH 17/78] schnorr sig example passed --- .../src/main/scala/sigma/ast/SType.scala | 4 ++-- .../src/main/scala/sigma/ast/methods.scala | 5 +++-- .../src/main/scala/sigmastate/lang/Types.scala | 1 + .../sigma/compiler/phases/SigmaBinder.scala | 4 ---- .../sigma/compiler/phases/SigmaTyper.scala | 18 ++++++++++++------ .../utxo/BasicOpsSpecification.scala | 2 +- 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 4be9087ca7..411ab1e8c7 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -105,7 +105,7 @@ object SType { /** All pre-defined types should be listed here. Note, NoType is not listed. * Should be in sync with sigmastate.lang.Types.predefTypes. */ val allPredefTypes: Seq[SType] = Array[SType]( - SBoolean, SByte, SShort, SInt, SLong, SBigInt, SContext, + SBoolean, SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt, SContext, SGlobal, SHeader, SPreHeader, SAvlTree, SGroupElement, SSigmaProp, SString, SBox, SUnit, SAny) @@ -220,7 +220,7 @@ trait STypeCompanion { /** Special type to represent untyped values. * Interpreter raises an error when encounter a Value with this type. - * All Value nodes with this type should be elimitanted during typing. + * All Value nodes with this type should be eliminated during typing. * If no specific type can be assigned statically during typing, * then either error should be raised or type SAny should be assigned * which is interpreted as dynamic typing. */ diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 8499ecd46e..179e21485c 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -366,6 +366,7 @@ case object SGroupElementMethods extends MonoTypeMethods { lazy val ExponentiateUnsignedMethod: SMethod = SMethod( this, "expUnsigned", SFunc(Array(this.ownerType, SUnsignedBigInt), this.ownerType), 6, Exponentiate.costKind) // todo: recheck costing + .withIRInfo(MethodCallIrBuilder) .withInfo("Exponentiate this \\lst{GroupElement} to the given number. Returns this to the power of k", ArgInfo("k", "The power")) @@ -396,9 +397,9 @@ case object SGroupElementMethods extends MonoTypeMethods { NegateMethod) super.getMethods() ++ (if (VersionContext.current.isV6SoftForkActivated) { - v5Methods - } else { v5Methods ++ Seq(ExponentiateUnsignedMethod) + } else { + v5Methods }) } diff --git a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala index 06683f6e96..d928e36f61 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala @@ -34,6 +34,7 @@ trait Types extends Core { "Int" -> SInt, "Long" -> SLong, "BigInt" -> SBigInt, + "UnsignedBigInt" -> SUnsignedBigInt, "AvlTree" -> SAvlTree, "Context" -> SContext, "GroupElement" -> SGroupElement, diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala index 1877131718..af5be938be 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala @@ -56,10 +56,6 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder, case _ @ Apply(ApplyTypes(Ident("Coll", _), Seq(tpe)), args) => Some(mkConcreteCollection(args, tpe)) - // hack to make possible to write g.exp(ubi) for both unsigned and signed big integers - case Apply(Select(obj, n, resType), args) if n == "exp" && args(0).isInstanceOf[Value[SUnsignedBigInt.type]] => - Some(Apply(Select(obj, "expUnsigned", resType), args)) - // Rule: Coll(...) --> case Apply(Ident("Coll", _), args) => val tpe = if (args.isEmpty) NoType else args(0).tpe diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index 3d62ff8dd8..bad5c92d61 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -165,14 +165,20 @@ class SigmaTyper(val builder: SigmaBuilder, error(s"Cannot get field '$n' in in the object $obj of non-product type ${obj.tpe}", sel.sourceContext) } - case app @ Apply(sel @ Select(obj, n, _), args) => - val newSel = assignType(env, sel) + case app @ Apply(selOriginal @ Select(obj, nOriginal, resType), args) => val newArgs = args.map(assignType(env, _)) - if(n=="expUnsigned") { - println(app) - println("newSel: " + newSel) - println("newArgs: " + newArgs) + + // hack to make possible to write g.exp(ubi) for both unsigned and signed big integers + // could be useful for other use cases where the same front-end code could be + // translated to different methods under the hood, based on argument types + // todo: consider better place for it + val (n, sel) = if (nOriginal == "exp" && newArgs(0).tpe.isInstanceOf[SUnsignedBigInt.type]) { + val newName = "expUnsigned" + (newName, Select(obj, newName, resType)) + } else { + (nOriginal, selOriginal) } + val newSel = assignType(env, sel) newSel.tpe match { case genFunTpe @ SFunc(argTypes, _, _) => // If it's a function then the application has type of that function's return type. diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index e6f08f75fc..9a829f6829 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -251,7 +251,7 @@ class BasicOpsSpecification extends CompilerTestingCommons )} if (activatedVersionInTests < V6SoftForkVersion) { - deserTest() + an[Exception] should be thrownBy deserTest() } else { deserTest() } From eb47d172e83958225b44f8c0cdb4228e55fd0873 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 9 Jun 2024 20:42:32 +0300 Subject: [PATCH 18/78] eq test, DataValueComparer --- .../scala/sigma/data/DataValueComparer.scala | 6 ++++ .../utxo/BasicOpsSpecification.scala | 31 +++++++------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/DataValueComparer.scala b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala index 21ca85012f..39022a2e7e 100644 --- a/data/shared/src/main/scala/sigma/data/DataValueComparer.scala +++ b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala @@ -344,6 +344,12 @@ object DataValueComparer { okEqual = bi == r } + // todo: check costing + case ubi: UnsignedBigInt => /** case 5 (see [[EQ_BigInt]]) */ + E.addFixedCost(EQ_BigInt) { + okEqual = ubi == r + } + case sp1: SigmaProp => E.addCost(MatchType) // for second match below okEqual = r match { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 9a829f6829..c3e732cd2b 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -140,28 +140,21 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } - property("group order serialization") { - val value = SecP256K1Group.q.divide(new BigInteger("2")) - def deserTest() = {test("big int - q", env, ext, - s"{ val b = bigInt(\"${value.toString}\"); b > 1 }", - null, - true - )} + property("group order deserialization") { + val b = SecP256K1Group.q - if (activatedVersionInTests < V6SoftForkVersion) { - deserTest() - } else { - deserTest() - } - } - - property("restoring unsigned 256 bits") { - val b = new BigInteger("92805629300808893548929804498612226467505866636839045998233220279839291898608") - val ub = new BigInteger(1, b.toByteArray) + val customExt: Seq[(Byte, EvaluatedValue[_ <: SType])] = Map( + 0.toByte -> UnsignedBigIntConstant(b) + ).toSeq - def deserTest() = {test("restoring", env, ext, - s"{ val b = unsignedBigInt(\"${ub.toString}\"); b > 1 }", + def deserTest() = {test("restoring", env, customExt, + s"""{ + | val b1 = unsignedBigInt(\"${b.toString}\") + | val b2 = getVar[UnsignedBigInt](0).get + | b1 == b2 + |} + | """.stripMargin, null, true )} From 33033fc3b9d02934dde612a3efd6bfb2fa63bf48 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 10 Jun 2024 13:48:55 +0300 Subject: [PATCH 19/78] importing CheckMinimalErgoTreeVersion from i994 --- .../validation/ValidationRules.scala | 17 ++++++++++++++++- .../src/main/scala/sigma/ast/methods.scala | 2 ++ .../sigmastate/utxo/BasicOpsSpecification.scala | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala index 9d4de47a99..07fe8db0ee 100644 --- a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala +++ b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala @@ -155,6 +155,20 @@ object ValidationRules { override protected lazy val settings: SigmaValidationSettings = currentSettings } + object CheckMinimalErgoTreeVersion extends ValidationRule(1016, + "ErgoTree should have at least required version") with SoftForkWhenReplaced { + override protected lazy val settings: SigmaValidationSettings = currentSettings + + final def apply(currentVersion: Byte, minVersion: Byte): Unit = { + checkRule() + if (currentVersion < minVersion) { + throwValidationException( + new SigmaException(s"ErgoTree should have at least $minVersion version, but was $currentVersion"), + Array(currentVersion, minVersion)) + } + } + } + val ruleSpecs: Seq[ValidationRule] = Seq( CheckDeserializedScriptType, CheckDeserializedScriptIsSigmaProp, @@ -171,7 +185,8 @@ object ValidationRules { CheckHeaderSizeBit, CheckCostFuncOperation, CheckPositionLimit, - CheckLoopLevelInCostFunction + CheckLoopLevelInCostFunction, + CheckMinimalErgoTreeVersion ) /** Validation settings that correspond to the current version of the ErgoScript implementation. diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index ef548d6185..2cfecea15d 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1,6 +1,7 @@ package sigma.ast import org.ergoplatform._ +import org.ergoplatform.validation.ValidationRules.CheckMinimalErgoTreeVersion import org.ergoplatform.validation._ import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} @@ -1533,6 +1534,7 @@ case object SGlobalMethods extends MonoTypeMethods { */ def serialize_eval(mc: MethodCall, G: SigmaDslBuilder, value: SType#WrappedType) (implicit E: ErgoTreeEvaluator): Coll[Byte] = { + CheckMinimalErgoTreeVersion(E.context.currentErgoTreeVersion, VersionContext.V6SoftForkVersion) E.addCost(SigmaByteWriter.StartWriterCost) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 52756b640f..76d3d84ced 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -20,7 +20,7 @@ import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings import sigmastate.interpreter.Interpreter._ import sigma.ast.Apply import sigma.eval.EvalSettings -import sigma.exceptions.{CostLimitException, InvalidType} +import sigma.exceptions.InvalidType import sigmastate.utils.Helpers import sigmastate.utils.Helpers._ From dd62bd7475f3a4f4d1399bdca8e9dd2361e36255 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 11 Jun 2024 17:15:07 +0300 Subject: [PATCH 20/78] failing tests --- .../utxo/BasicOpsSpecification.scala | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..87a3697462 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import sigma.Extensions.ArrayOps +import sigma.VersionContext import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -157,6 +158,47 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Lazy evaluation of default in Option.getOrElse") { + val customExt = Map ( + 1.toByte -> IntConstant(5) + ).toSeq + def optTest() = test("getOrElse", env, customExt, + """{ + | getVar[Int](1).getOrElse(getVar[Int](44).get) > 0 + |} + | + |""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + optTest() + } else { + an[Exception] shouldBe thrownBy(optTest()) + } + } + + property("Lazy evaluation of default in Coll.getOrElse") { + val customExt = Map ( + 1.toByte -> IntConstant(5) + ).toSeq + def optTest() = test("getOrElse", env, customExt, + """{ + | val c = Coll[Int](1) + | c.getOrElse(0, getVar[Int](44).get) > 0 + |} + | + |""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + optTest() + } else { + an[Exception] shouldBe thrownBy(optTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From aaef77e76f8b4be4c97520a56cc6b1492bb70791 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 11 Jun 2024 17:46:20 +0300 Subject: [PATCH 21/78] close #906: lazy evaluation of default in Option.getOrElse and Coll.getOrElse --- .../main/scala/sigma/ast/transformers.scala | 43 ++++++++++++++----- .../utxo/BasicOpsSpecification.scala | 10 ++--- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/transformers.scala b/data/shared/src/main/scala/sigma/ast/transformers.scala index 939da79d98..8d7e689a18 100644 --- a/data/shared/src/main/scala/sigma/ast/transformers.scala +++ b/data/shared/src/main/scala/sigma/ast/transformers.scala @@ -10,7 +10,7 @@ import sigma.eval.ErgoTreeEvaluator.DataEnv import sigma.serialization.CoreByteWriter.ArgInfo import sigma.serialization.OpCodes import sigma.serialization.ValueCodes.OpCode -import sigma.{Box, Coll, Evaluation} +import sigma.{Box, Coll, Evaluation, VersionContext} // TODO refactor: remove this trait as it doesn't have semantic meaning @@ -258,10 +258,22 @@ case class ByIndex[V <: SType](input: Value[SCollection[V]], val indexV = index.evalTo[Int](env) default match { case Some(d) => - val dV = d.evalTo[V#WrappedType](env) - Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased - addCost(ByIndex.costKind) - inputV.getOrElse(indexV, dV) + if (VersionContext.current.isV6SoftForkActivated) { + // lazy evaluation of default in 6.0 + addCost(ByIndex.costKind) + if (inputV.isDefinedAt(indexV)) { + inputV.apply(indexV) + } else { + val dV = d.evalTo[V#WrappedType](env) + Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased + inputV.getOrElse(indexV, dV) + } + } else { + val dV = d.evalTo[V#WrappedType](env) + Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased + addCost(ByIndex.costKind) + inputV.getOrElse(indexV, dV) + } case _ => addCost(ByIndex.costKind) inputV.apply(indexV) @@ -613,11 +625,22 @@ case class OptionGetOrElse[V <: SType](input: Value[SOption[V]], default: Value[ override val opType = SFunc(IndexedSeq(input.tpe, tpe), tpe) override def tpe: V = input.tpe.elemType protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - val inputV = input.evalTo[Option[V#WrappedType]](env) - val dV = default.evalTo[V#WrappedType](env) // TODO v6.0: execute lazily (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/906) - Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased - addCost(OptionGetOrElse.costKind) - inputV.getOrElse(dV) + if(VersionContext.current.isV6SoftForkActivated) { + // lazy evaluation of default in 6.0 + val inputV = input.evalTo[Option[V#WrappedType]](env) + addCost(OptionGetOrElse.costKind) + inputV.getOrElse { + val dV = default.evalTo[V#WrappedType](env) + Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased + dV + } + } else { + val inputV = input.evalTo[Option[V#WrappedType]](env) + val dV = default.evalTo[V#WrappedType](env) + Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased + addCost(OptionGetOrElse.costKind) + inputV.getOrElse(dV) + } } } object OptionGetOrElse extends ValueCompanion with FixedCostValueCompanion { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 87a3697462..c1489355e6 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -166,7 +166,6 @@ class BasicOpsSpecification extends CompilerTestingCommons """{ | getVar[Int](1).getOrElse(getVar[Int](44).get) > 0 |} - | |""".stripMargin, null ) @@ -179,15 +178,12 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("Lazy evaluation of default in Coll.getOrElse") { - val customExt = Map ( - 1.toByte -> IntConstant(5) - ).toSeq - def optTest() = test("getOrElse", env, customExt, + def optTest() = test("getOrElse", env, ext, """{ | val c = Coll[Int](1) - | c.getOrElse(0, getVar[Int](44).get) > 0 + | c.getOrElse(0, getVar[Int](44).get) > 0 && + | c.getOrElse(1, c.getOrElse(0, getVar[Int](44).get)) > 0 |} - | |""".stripMargin, null ) From ea468c9629d84d7260730208018257b76f0ccb35 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 11 Jun 2024 21:03:20 +0300 Subject: [PATCH 22/78] initial stubs along first todos for bulletproofs contracts --- .../utxo/BasicOpsSpecification.scala | 170 +++++++++++++++--- 1 file changed, 143 insertions(+), 27 deletions(-) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index c3e732cd2b..64fa69bf24 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -148,7 +148,7 @@ class BasicOpsSpecification extends CompilerTestingCommons 0.toByte -> UnsignedBigIntConstant(b) ).toSeq - def deserTest() = {test("restoring", env, customExt, + def deserTest() = {test("restoring q", env, customExt, s"""{ | val b1 = unsignedBigInt(\"${b.toString}\") | val b2 = getVar[UnsignedBigInt](0).get @@ -218,35 +218,151 @@ class BasicOpsSpecification extends CompilerTestingCommons 2.toByte -> UnsignedBigIntConstant(z.bigInteger) ).toSeq - def deserTest() = {test("schnorr", env, customExt, - s"""{ - | - | val g: GroupElement = groupGenerator - | val holder = getVar[GroupElement](0).get - | - | val message = fromBase16("${Base16.encode(message)}") - | val e: Coll[Byte] = blake2b256(message) // weak Fiat-Shamir - | val eInt = byteArrayToBigInt(e) // challenge as big integer - | - | // a of signature in (a, z) - | val a = getVar[GroupElement](1).get - | val aBytes = a.getEncoded - | - | // z of signature in (a, z) - | val z = getVar[UnsignedBigInt](2).get - | - | // Signature is valid if g^z = a * x^e - | val properSignature = g.exp(z) == a.multiply(holder.exp(eInt)) - | sigmaProp(properSignature) - |}""".stripMargin, - null, - true - )} + def schnorrTest() = { + test("schnorr", env, customExt, + s"""{ + | + | val g: GroupElement = groupGenerator + | val holder = getVar[GroupElement](0).get + | + | val message = fromBase16("${Base16.encode(message)}") + | val e: Coll[Byte] = blake2b256(message) // weak Fiat-Shamir + | val eInt = byteArrayToBigInt(e) // challenge as big integer + | + | // a of signature in (a, z) + | val a = getVar[GroupElement](1).get + | val aBytes = a.getEncoded + | + | // z of signature in (a, z) + | val z = getVar[UnsignedBigInt](2).get + | + | // Signature is valid if g^z = a * x^e + | val properSignature = g.exp(z) == a.multiply(holder.exp(eInt)) + | sigmaProp(properSignature) + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy schnorrTest() + } else { + schnorrTest() + } + } + + property("Bulletproof verification for a circuit proof") { + + val g = CGroupElement(SecP256K1Group.generator) + + def circuitTest() = { + test("schnorr", env, ext, + s"""{ + | // circuit data - should be provided via data input likely + | val lWeights: Coll[UnsignedBigInt] + | val rWeights: Coll[UnsignedBigInt] + | val oWeights: Coll[UnsignedBigInt] + | val commitmentWeights: Coll[UnsignedBigInt] + | + | val cs: Coll[UnsignedBigInt] + | val commitments: Coll[GroupElement] + | + | // proof data + | val ai: GroupElement + | val ao: GroupElement + | val s: GroupElement + | val tCommits: Coll[GroupElement] + | val tauX: UnsignedBigInt + | val mu: UnsignedBigInt + | val t: UnsignedBigInt + | + | // inner product proof + | val L: Coll[GroupElement] + | val R: Coll[GroupElement] + | val a: UnsignedBigInt + | val b: UnsignedBigInt + | + | // proof verification: + | val Q = lWeights.size + | + | val q // group order + | + | val yBytes = sha256(q.toBytes ++ aI.getEncoded ++ aO.getEncoded ++ s.getEncoded) + | + | val y = byteArrayToBigInt(yBytes) // should be to unsigned bigint + | + | val z = byteArrayToBigInt(sha256(y ++ q.toBytes)) + | + | + | + | sigmaProp(properSignature) + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy circuitTest() + } else { + circuitTest() + } + } + + property("Bulletproof verification for a range proof") { + + val g = CGroupElement(SecP256K1Group.generator) + + def circuitTest() = { + test("range proof", env, ext, + s"""{ + | // circuit data - should be provided via data input likely + | val input: GroupElement + | + | // proof data + | val ai: GroupElement + | val s: GroupElement + | val tCommits: Coll[GroupElement] + | val tauX: UnsignedBigInt + | val mu: UnsignedBigInt + | val t: UnsignedBigInt + | + | // inner product proof + | val L: Coll[GroupElement] + | val R: Coll[GroupElement] + | val a: UnsignedBigInt + | val b: UnsignedBigInt + | + | // proof verification: + | val Q = lWeights.size + | + | val q // group order + | + | val yBytes = sha256(q.toBytes ++ input.getEncoded ++ aI.getEncoded ++ s.getEncoded) + | + | val y = byteArrayToBigInt(yBytes) mod q // should be to unsigned bigint + | + | val z = byteArrayToBigInt(sha256(q.toBytes ++ yBytes)) mod q + | + | val zSquared = z * z mod q + | val zCubed = z * z * z mod q // todo: what to do here? + | + | def times() : // todo: implement + | + | // ops needed: modInverse, mod ops + | + | sigmaProp(properSignature) + |}""".stripMargin, + null, + true + ) + } if (activatedVersionInTests < V6SoftForkVersion) { - an[Exception] should be thrownBy deserTest() + an[Exception] should be thrownBy circuitTest() } else { - deserTest() + circuitTest() } } From e9b3253dd4c4220be2f87a1ee66240ad67fd45fd Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 12 Jun 2024 13:13:18 +0300 Subject: [PATCH 23/78] original code --- .../utxo/BasicOpsSpecification.scala | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 64fa69bf24..4d7d3718e9 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -311,6 +311,66 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("Bulletproof verification for a range proof") { + /* + * Original range proof verifier code by Benedikt Bunz: + * + VectorBase vectorBase = params.getVectorBase(); + PeddersenBase base = params.getBase(); + int n = vectorBase.getGs().size(); + T a = proof.getaI(); + T s = proof.getS(); + + BigInteger q = params.getGroup().groupOrder(); + BigInteger y; + + if (salt.isPresent()) { + y = ProofUtils.computeChallenge(q, salt.get(), input, a, s); + } else { + y = ProofUtils.computeChallenge(q, input, a, s); + + } + FieldVector ys = FieldVector.from(VectorX.iterate(n, BigInteger.ONE, y::multiply), q); + + BigInteger z = ProofUtils.challengeFromints(q, y); + + BigInteger zSquared = z.pow(2).mod(q); + BigInteger zCubed = z.pow(3).mod(q); + + FieldVector twos = FieldVector.from(VectorX.iterate(n, BigInteger.ONE, bi -> bi.shiftLeft(1)), q); + FieldVector twoTimesZSquared = twos.times(zSquared); + GeneratorVector tCommits = proof.gettCommits(); + + BigInteger x = ProofUtils.computeChallenge(q,z, tCommits); + + BigInteger tauX = proof.getTauX(); + BigInteger mu = proof.getMu(); + BigInteger t = proof.getT(); + BigInteger k = ys.sum().multiply(z.subtract(zSquared)).subtract(zCubed.shiftLeft(n).subtract(zCubed)); + T lhs = base.commit(t.subtract(k), tauX); + T rhs = tCommits.commit(Arrays.asList(x, x.pow(2))).add(input.multiply(zSquared)); + System.out.println("y " + y); + System.out.println("z " + z); + + System.out.println("x " + x);pow + equal(lhs, rhs, "Polynomial identity check failed, LHS: %s, RHS %s"); + BigInteger uChallenge = ProofUtils.challengeFromints(q, x, tauX, mu, t); + System.out.println("u " + uChallenge); + T u = base.g.multiply(uChallenge); + GeneratorVector hs = vectorBase.getHs(); + GeneratorVector gs = vectorBase.getGs(); + GeneratorVector hPrimes = hs.haddamard(ys.invert()); + FieldVector hExp = ys.times(z).add(twoTimesZSquared); + T P = a.add(s.multiply(x)).add(gs.sum().multiply(z.negate())).add(hPrimes.commit(hExp)).subtract(base.h.multiply(mu)).add(u.multiply(t)); + VectorBase primeBase = new VectorBase<>(gs, hPrimes, u); + // System.out.println("PVerify "+P.normalize()); + // System.out.println("XVerify" +x); + // System.out.println("YVerify" +y); + // System.out.println("ZVerify" +z); + // System.out.println("uVerify" +u); + + EfficientInnerProductVerifier verifier = new EfficientInnerProductVerifier<>(); + verifier.verify(primeBase, P, proof.getProductProof(), uChallenge); + */ val g = CGroupElement(SecP256K1Group.generator) @@ -346,7 +406,7 @@ class BasicOpsSpecification extends CompilerTestingCommons | val z = byteArrayToBigInt(sha256(q.toBytes ++ yBytes)) mod q | | val zSquared = z * z mod q - | val zCubed = z * z * z mod q // todo: what to do here? + | val zCubed = zSquared * z mod q | | def times() : // todo: implement | From 7ccce9a0f9eb702e58d5c70da18fbe53b80c7f47 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 12 Jun 2024 15:20:17 +0300 Subject: [PATCH 24/78] distinct and reverse (w. no costing) --- .../src/main/scala/sigma/ast/methods.scala | 107 ++++++++++++++---- .../sigma/compiler/ir/GraphBuilding.scala | 4 + .../ir/wrappers/sigma/CollsUnit.scala | 2 + .../ir/wrappers/sigma/impl/CollsImpl.scala | 28 +++++ .../utxo/BasicOpsSpecification.scala | 35 ++++++ 5 files changed, 155 insertions(+), 21 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index e4cf0007e0..34f69eb965 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -890,7 +890,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { SFunc(Array(ThisType, tIV, SInt), SInt, paramIVSeq), 26, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(10), chunkSize = 2)) .withIRInfo(MethodCallIrBuilder, javaMethodOf[Coll[_], Any, Int]("indexOf")) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Returns index of a collection element, or -1 if not found") /** Implements evaluation of Coll.indexOf method call ErgoTree node. * Called via reflection based on naming convention. @@ -939,29 +939,94 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + + // 6.0 methods below + val ReverseMethod = SMethod(this, "reverse", + SFunc(Array(ThisType), ThisType, paramIVSeq), + 30, Zip_CostKind) // todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + /** Implements evaluation of Coll.reverse method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def reverse_eval[A](mc: MethodCall, xs: Coll[A]) + (implicit E: ErgoTreeEvaluator): Coll[A] = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.reverse + } + } + + val DistinctMethod = SMethod(this, "distinct", + SFunc(Array(ThisType), ThisType, paramIVSeq), + 31, Zip_CostKind) // todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + /** Implements evaluation of Coll.reverse method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def distinct_eval[A](mc: MethodCall, xs: Coll[A]) + (implicit E: ErgoTreeEvaluator): Coll[A] = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.distinct + } + } + + val StartsWithMethod = SMethod(this, "startsWith", + SFunc(Array(ThisType), ThisType, paramIVSeq), + 32, Zip_CostKind) // todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + val EndsWithMethod = SMethod(this, "endsWith", + SFunc(Array(ThisType), ThisType, paramIVSeq), + 33, Zip_CostKind) // todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + private val v5Methods = Seq( + SizeMethod, + GetOrElseMethod, + MapMethod, + ExistsMethod, + FoldMethod, + ForallMethod, + SliceMethod, + FilterMethod, + AppendMethod, + ApplyMethod, + IndicesMethod, + FlatMapMethod, + PatchMethod, + UpdatedMethod, + UpdateManyMethod, + IndexOfMethod, + ZipMethod + ) + + private val v6Methods = Seq( + ReverseMethod, + DistinctMethod, + StartsWithMethod, + EndsWithMethod + ) + /** This method should be overriden in derived classes to add new methods in addition to inherited. * Typical override: `super.getMethods() ++ Seq(m1, m2, m3)` */ - override protected def getMethods(): Seq[SMethod] = super.getMethods() ++ - Seq( - SizeMethod, - GetOrElseMethod, - MapMethod, - ExistsMethod, - FoldMethod, - ForallMethod, - SliceMethod, - FilterMethod, - AppendMethod, - ApplyMethod, - IndicesMethod, - FlatMapMethod, - PatchMethod, - UpdatedMethod, - UpdateManyMethod, - IndexOfMethod, - ZipMethod - ) + override protected def getMethods(): Seq[SMethod] = { + if(VersionContext.current.isV6SoftForkActivated) { + super.getMethods() ++ v5Methods ++ v6Methods + } else { + super.getMethods() ++ v5Methods + } + } + } object STupleMethods extends MethodsContainer { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 7c7b80d39a..29200bfaeb 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -985,6 +985,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val i = asRep[Int](argsV(0)) val d = asRep[t](argsV(1)) xs.getOrElse(i, d) + case SCollectionMethods.ReverseMethod.name => + xs.reverse + case SCollectionMethods.DistinctMethod.name => + xs.distinct case _ => throwError } case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala index 9b4a002a14..8fe6ff847c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala @@ -29,6 +29,8 @@ import sigma.compiler.ir.{Base, IRContext} def updateMany(indexes: Ref[Coll[Int]], values: Ref[Coll[A]]): Ref[Coll[A]]; def slice(from: Ref[Int], until: Ref[Int]): Ref[Coll[A]]; def append(other: Ref[Coll[A]]): Ref[Coll[A]]; + def reverse: Ref[Coll[A]] + def distinct: Ref[Coll[A]] }; trait CollBuilder extends Def[CollBuilder] { def fromItems[T](items: Ref[T]*)(implicit cT: Elem[T]): Ref[Coll[T]]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala index 0a18ea586a..0ce4c0a178 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala @@ -164,6 +164,20 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](other), true, false, element[Coll[A]])) } + + override def reverse: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(self, + CollClass.getMethod("reverse"), + Array[AnyRef](), + true, false, element[Coll[A]])) + } + + override def distinct: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(self, + CollClass.getMethod("distinct"), + Array[AnyRef](), + true, false, element[Coll[A]])) + } } case class LiftableColl[SA, A](lA: Liftable[SA, A]) @@ -311,6 +325,20 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](other), true, true, element[Coll[A]])) } + + def reverse: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(source, + CollClass.getMethod("reverse"), + Array[AnyRef](), + true, true, element[Coll[A]])) + } + + def distinct: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(source, + CollClass.getMethod("distinct"), + Array[AnyRef](), + true, true, element[Coll[A]])) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..f1545a8867 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import sigma.Extensions.ArrayOps +import sigma.VersionContext import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -157,6 +158,40 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Coll.reverse"){ + def reverseTest() = test("reverse", env, ext, + """{ + | val c1 = Coll(1, 2, 3) + | val c2 = Coll(3, 2, 1) + | c1.reverse == c2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.distinct"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll(1, 2, 3, 3, 2) + | val c2 = Coll(3, 2, 1) + | c1.distinct.reverse == c2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 2fa224bf13db51e77d50481d772224395f3615db Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 12 Jun 2024 19:06:54 +0300 Subject: [PATCH 25/78] startsWith and endsWith --- core/shared/src/main/scala/sigma/Colls.scala | 8 ++ .../scala/sigma/data/CollsOverArrays.scala | 8 ++ .../src/main/scala/sigma/ast/methods.scala | 28 ++++- .../sigma/compiler/ir/GraphBuilding.scala | 6 + .../ir/wrappers/sigma/CollsUnit.scala | 2 + .../ir/wrappers/sigma/impl/CollsImpl.scala | 28 +++++ .../utxo/BasicOpsSpecification.scala | 108 +++++++++++++++++- 7 files changed, 184 insertions(+), 4 deletions(-) diff --git a/core/shared/src/main/scala/sigma/Colls.scala b/core/shared/src/main/scala/sigma/Colls.scala index 625120deac..a74da8bf58 100644 --- a/core/shared/src/main/scala/sigma/Colls.scala +++ b/core/shared/src/main/scala/sigma/Colls.scala @@ -76,6 +76,14 @@ trait Coll[@specialized A] { * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ def zip[@specialized B](ys: Coll[B]): Coll[(A, B)] + /** For this collection (x0, ..., xN) and other collection (y0, ..., yM) + * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ + def startsWith(ys: Coll[A]): Boolean + + /** For this collection (x0, ..., xN) and other collection (y0, ..., yM) + * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ + def endsWith(ys: Coll[A]): Boolean + /** Tests whether a predicate holds for at least one element of this collection. * @param p the predicate used to test elements. * @return `true` if the given predicate `p` is satisfied by at least one element of this collection, otherwise `false` diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index 2413f7f427..506dc001ea 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -39,6 +39,10 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil @inline def zip[@specialized B](ys: Coll[B]): PairColl[A, B] = builder.pairColl(this, ys) + @inline def startsWith(ys: Coll[A]): Boolean = toArray.startsWith(ys.toArray) + + @inline def endsWith(ys: Coll[A]): Boolean = toArray.endsWith(ys.toArray) + def append(other: Coll[A]): Coll[A] = { if (toArray.length <= 0) return other val result = if (VersionContext.current.isJitActivated) { @@ -350,6 +354,10 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R def zip[@specialized B](ys: Coll[B]): PairColl[(L,R), B] = builder.pairColl(this, ys) + def startsWith(ys: Coll[(L, R)]): Boolean = toArray.startsWith(ys.toArray) + + def endsWith(ys: Coll[(L, R)]): Boolean = toArray.endsWith(ys.toArray) + override def indices: Coll[Int] = if (ls.length <= rs.length) ls.indices else rs.indices override def flatMap[B: RType](f: ((L, R)) => Coll[B]): Coll[B] = diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 34f69eb965..d73df90999 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -978,17 +978,41 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val StartsWithMethod = SMethod(this, "startsWith", - SFunc(Array(ThisType), ThisType, paramIVSeq), + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") + /** Implements evaluation of Coll.zip method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def startsWith_eval[A](mc: MethodCall, xs: Coll[A], ys: Coll[A]) + (implicit E: ErgoTreeEvaluator): Boolean = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.startsWith(ys) + } + } + val EndsWithMethod = SMethod(this, "endsWith", - SFunc(Array(ThisType), ThisType, paramIVSeq), + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") + /** Implements evaluation of Coll.zip method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def endsWith_eval[A](mc: MethodCall, xs: Coll[A], ys: Coll[A]) + (implicit E: ErgoTreeEvaluator): Boolean = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.endsWith(ys) + } + } + private val v5Methods = Seq( SizeMethod, GetOrElseMethod, diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 29200bfaeb..4a2c8428c1 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -989,6 +989,12 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => xs.reverse case SCollectionMethods.DistinctMethod.name => xs.distinct + case SCollectionMethods.StartsWithMethod.name => + val ys = asRep[Coll[t]](argsV(0)) + xs.startsWith(ys) + case SCollectionMethods.EndsWithMethod.name => + val ys = asRep[Coll[t]](argsV(0)) + xs.endsWith(ys) case _ => throwError } case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala index 8fe6ff847c..a2d89ddb55 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala @@ -31,6 +31,8 @@ import sigma.compiler.ir.{Base, IRContext} def append(other: Ref[Coll[A]]): Ref[Coll[A]]; def reverse: Ref[Coll[A]] def distinct: Ref[Coll[A]] + def startsWith(ys: Ref[Coll[A]]): Ref[Boolean]; + def endsWith(ys: Ref[Coll[A]]): Ref[Boolean]; }; trait CollBuilder extends Def[CollBuilder] { def fromItems[T](items: Ref[T]*)(implicit cT: Elem[T]): Ref[Coll[T]]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala index 0ce4c0a178..4862c90a41 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala @@ -178,6 +178,20 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](), true, false, element[Coll[A]])) } + + def startsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(self, + CollClass.getMethod("startsWith", classOf[Sym]), + Array[AnyRef](ys), + true, false, element[Boolean])) + } + + def endsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(self, + CollClass.getMethod("endsWith", classOf[Sym]), + Array[AnyRef](ys), + true, false, element[Boolean])) + } } case class LiftableColl[SA, A](lA: Liftable[SA, A]) @@ -339,6 +353,20 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](), true, true, element[Coll[A]])) } + + def startsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(source, + CollClass.getMethod("startsWith", classOf[Sym]), + Array[AnyRef](ys), + true, true, element[Boolean])) + } + + def endsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(source, + CollClass.getMethod("endsWith", classOf[Sym]), + Array[AnyRef](ys), + true, true, element[Boolean])) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index f1545a8867..aa502db7d0 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -163,7 +163,11 @@ class BasicOpsSpecification extends CompilerTestingCommons """{ | val c1 = Coll(1, 2, 3) | val c2 = Coll(3, 2, 1) - | c1.reverse == c2 + | + | val b1 = Coll(INPUTS(0), OUTPUTS(0)) + | val b2 = Coll(OUTPUTS(0), INPUTS(0)) + | + | c1.reverse == c2 && b1.reverse == b2 | }""".stripMargin, null ) @@ -180,7 +184,107 @@ class BasicOpsSpecification extends CompilerTestingCommons """{ | val c1 = Coll(1, 2, 3, 3, 2) | val c2 = Coll(3, 2, 1) - | c1.distinct.reverse == c2 + | + | val h1 = Coll(INPUTS(0), INPUTS(0)) + | val h2 = Coll(INPUTS(0)) + | + | c1.distinct.reverse == c2 && h1.distinct == h2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.startsWith"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll(1, 2, 3) + | val c2 = Coll(1, 2) + | val c3 = Coll(1, 3) + | val c4 = Coll[Int]() + | val c5 = Coll(1, 2, 3, 4) + | + | val b1 = c1.startsWith(c3) + | val b2 = c1.startsWith(c5) + | + | c1.startsWith(c2) && c1.startsWith(c4) && c1.startsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.startsWith - tuples"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll((1, 2), (3, 4), (5, 6)) + | val c2 = Coll((1, 2), (3, 4)) + | val c3 = Coll((1, 3)) + | val c4 = Coll[(Int, Int)]() + | val c5 = Coll((1, 2), (3, 4), (5, 6), (7, 8)) + | + | val b1 = c1.startsWith(c3) + | val b2 = c1.startsWith(c5) + | + | c1.startsWith(c2) && c1.startsWith(c4) && c1.startsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.endsWith"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll(1, 2, 3) + | val c2 = Coll(2, 3) + | val c3 = Coll(2, 2) + | val c4 = Coll[Int]() + | val c5 = Coll(1, 2, 3, 4) + | + | val b1 = c1.endsWith(c3) + | val b2 = c1.endsWith(c5) + | + | c1.endsWith(c2) && c1.endsWith(c4) && c1.endsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[Exception] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.endsWith - tuples"){ + def reverseTest() = test("endsWith tuples", env, ext, + """{ + | val c1 = Coll((1, 2), (2, 3)) + | val c2 = Coll((2, 3)) + | val c3 = Coll((2, 2)) + | val c4 = Coll[(Int, Int)]() + | val c5 = Coll((0, 2), (2, 3)) + | + | val b1 = c1.endsWith(c3) + | val b2 = c1.endsWith(c5) + | + | c1.endsWith(c2) && c1.endsWith(c4) && c1.endsWith(c1) && !b1 && !b2 | }""".stripMargin, null ) From b17c8c81f1dc256276efda7e90c7a762de6999f5 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 12 Jun 2024 22:04:03 +0300 Subject: [PATCH 26/78] get --- core/shared/src/main/scala/sigma/Colls.scala | 16 +++++++++++++++ .../src/main/scala/sigma/ast/methods.scala | 20 ++++++++++++++++++- .../sigma/compiler/ir/GraphBuilding.scala | 3 +++ .../ir/wrappers/sigma/CollsUnit.scala | 1 + .../ir/wrappers/sigma/impl/CollsImpl.scala | 15 ++++++++++++++ .../utxo/BasicOpsSpecification.scala | 18 +++++++++++++++++ 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/core/shared/src/main/scala/sigma/Colls.scala b/core/shared/src/main/scala/sigma/Colls.scala index a74da8bf58..f1633a68a4 100644 --- a/core/shared/src/main/scala/sigma/Colls.scala +++ b/core/shared/src/main/scala/sigma/Colls.scala @@ -45,6 +45,22 @@ trait Coll[@specialized A] { */ def apply(i: Int): A + /** The element at given index. + * Indices start at `0`; `xs.apply(0)` is the first element of collection `xs`. + * Note the indexing syntax `xs(i)` is a shorthand for `xs.apply(i)`. + * + * @param i the index + * @return the element at the given index + * @throws ArrayIndexOutOfBoundsException if `i < 0` or `length <= i` + */ + def get(i: Int): Option[A] = { + if (isDefinedAt(i)) { + Some(apply(i)) + } else { + None + } + } + /** Tests whether this $coll contains given index. * * The implementations of methods `apply` and `isDefinedAt` turn a `Coll[A]` into diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index d73df90999..5cab1118ef 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1013,6 +1013,23 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + val GetMethod = SMethod(this, "get", + SFunc(Array(ThisType, SInt), SOption(tIV), Array[STypeParam](tIV)), 34, ByIndex.costKind) //todo: costing + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + /** Implements evaluation of Coll.zip method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def get_eval[A](mc: MethodCall, xs: Coll[A], index: Int) + (implicit E: ErgoTreeEvaluator): Option[A] = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => // todo: costing + xs.get(index) + } + } + private val v5Methods = Seq( SizeMethod, GetOrElseMethod, @@ -1037,7 +1054,8 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { ReverseMethod, DistinctMethod, StartsWithMethod, - EndsWithMethod + EndsWithMethod, + GetMethod ) /** This method should be overriden in derived classes to add new methods in addition to inherited. diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 4a2c8428c1..81092ee0c7 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -995,6 +995,9 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SCollectionMethods.EndsWithMethod.name => val ys = asRep[Coll[t]](argsV(0)) xs.endsWith(ys) + case SCollectionMethods.GetMethod.name => + val idx = asRep[Int](argsV(0)) + xs.get(idx) case _ => throwError } case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala index a2d89ddb55..fdc9fadcba 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala @@ -14,6 +14,7 @@ import sigma.compiler.ir.{Base, IRContext} implicit def eA: Elem[A]; def length: Ref[Int]; def apply(i: Ref[Int]): Ref[A]; + def get(index: Ref[Int]): Ref[WOption[A]]; def getOrElse(index: Ref[Int], default: Ref[A]): Ref[A]; def map[B](f: Ref[scala.Function1[A, B]]): Ref[Coll[B]]; def zip[B](ys: Ref[Coll[B]]): Ref[Coll[scala.Tuple2[A, B]]]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala index 4862c90a41..04a7070acf 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala @@ -63,6 +63,14 @@ class CollCls extends EntityObject("Coll") { true, false, element[A])) } + override def get(index: Ref[Int]): Ref[WOption[A]] = { + asRep[WOption[A]](mkMethodCall(self, + CollClass.getMethod("get", classOf[Sym]), + Array[AnyRef](index), + true, false, element[WOption[A]])) + } + + override def map[B](f: Ref[A => B]): Ref[Coll[B]] = { implicit val eB = f.elem.eRange asRep[Coll[B]](mkMethodCall(self, @@ -238,6 +246,13 @@ class CollCls extends EntityObject("Coll") { true, true, element[A])) } + def get(index: Ref[Int]): Ref[WOption[A]] = { + asRep[WOption[A]](mkMethodCall(source, + CollClass.getMethod("get", classOf[Sym]), + Array[AnyRef](index), + true, true, element[WOption[A]])) + } + def map[B](f: Ref[A => B]): Ref[Coll[B]] = { implicit val eB = f.elem.eRange asRep[Coll[B]](mkMethodCall(source, diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index aa502db7d0..8784f4b3c5 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -296,6 +296,24 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Coll.get"){ + def getTest() = test("get", env, ext, + """{ + | val c1 = Coll(1) + | val c2 = Coll[Int]() + | + | c2.get(0).getOrElse(c1.get(0).get) == c1.get(0).get + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + getTest() + } else { + an[Exception] shouldBe thrownBy(getTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From fc3be57fa2223d12312ba8e838d8d4341a8e1c70 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 12 Jun 2024 22:35:35 +0300 Subject: [PATCH 27/78] non-execution of get_eval todo --- data/shared/src/main/scala/sigma/ast/methods.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 5cab1118ef..84fc4955a5 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1024,10 +1024,9 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { */ def get_eval[A](mc: MethodCall, xs: Coll[A], index: Int) (implicit E: ErgoTreeEvaluator): Option[A] = { - val m = mc.method - E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => // todo: costing - xs.get(index) - } + E.addCost(ByIndex.costKind, mc.method.opDesc) //todo: costing + ??? // todo: this get is not actually executed, why? + xs.get(index) } private val v5Methods = Seq( From f7f31e3614cbce8e8694d12773f2e17b2b391cf3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 13 Jun 2024 13:01:57 +0300 Subject: [PATCH 28/78] fix w/out tests (only manual tests are done) --- .../scala/sigma/data/CollsOverArrays.scala | 19 ++++++++++++++++--- .../utxo/BasicOpsSpecification.scala | 7 +++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index 2413f7f427..01cad08471 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -135,7 +135,13 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil override def equals(obj: scala.Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match { case obj: CollOverArray[_] if obj.tItem == this.tItem => - java.util.Objects.deepEquals(obj.toArray, toArray) + java.util.Objects.deepEquals(obj.toArray, this.toArray) + case obj: PairColl[_, _] if obj.tItem == this.tItem => + if(VersionContext.current.isV6SoftForkActivated) { + java.util.Objects.deepEquals(obj.toArray, this.toArray) + } else { + false + } case _ => false }) @@ -237,8 +243,15 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R]) extends PairColl[L,R] { - override def equals(that: scala.Any) = (this eq that.asInstanceOf[AnyRef]) || (that match { - case that: PairColl[_,_] if that.tItem == this.tItem => ls == that.ls && rs == that.rs + override def equals(that: scala.Any): Boolean = (this eq that.asInstanceOf[AnyRef]) || (that match { + case that: PairColl[_,_] if that.tItem == this.tItem => + ls == that.ls && rs == that.rs + case that: CollOverArray[_] if that.tItem == this.tItem => + if (VersionContext.current.isV6SoftForkActivated) { + java.util.Objects.deepEquals(that.toArray, this.toArray) + } else { + false + } case _ => false }) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..705d83b56a 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -157,6 +157,13 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("coll equality") { + test("R1", env, ext, + "{ SELF.tokens == Coll[(Coll[Byte], Long)]() }", + null + ) + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From e71d7c2a2e06aec1a301c5de87419db63d7f9050 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 13 Jun 2024 21:00:07 +0300 Subject: [PATCH 29/78] modInverse --- .../src/main/scala/sigma/SigmaDsl.scala | 6 +- .../src/main/scala/sigma/ast/SType.scala | 3 +- .../src/main/scala/sigma/data/CBigInt.scala | 4 + .../sigma/reflection/ReflectionData.scala | 1 + .../src/main/scala/sigma/ast/methods.scala | 74 +++++++++++++++++-- .../sigma/compiler/ir/GraphBuilding.scala | 5 ++ .../ir/wrappers/sigma/SigmaDslUnit.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 21 ++++++ .../utxo/BasicOpsSpecification.scala | 20 +++++ 9 files changed, 126 insertions(+), 9 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 723ce03bc6..3366bddfac 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -273,15 +273,15 @@ trait UnsignedBigInt { def and(that: UnsignedBigInt): UnsignedBigInt def &(that: UnsignedBigInt): UnsignedBigInt = and(that) - /** Returns a BigInteger whose value is `(this | that)`. (This - * method returns a negative BigInteger if and only if either `this` or `that`` is - * negative.) + /** Returns a BigInteger whose value is `(this | that)`. * * @param that value to be OR'ed with this BigInteger. * @return `this | that` */ def or(that: UnsignedBigInt): UnsignedBigInt def |(that: UnsignedBigInt): UnsignedBigInt = or(that) + + def modInverse(m: UnsignedBigInt): UnsignedBigInt } diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 411ab1e8c7..1ce1663859 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -490,7 +490,7 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM case object SUnsignedBigInt extends SPrimType with SEmbeddable with SNumericType with SMonoType { override type WrappedType = UnsignedBigInt override val typeCode: TypeCode = 9: Byte - override val reprClass: RClass[_] = RClass(classOf[BigInt]) + override val reprClass: RClass[_] = RClass(classOf[UnsignedBigInt]) override def typeId = typeCode /** Type of Relation binary op like GE, LE, etc. */ @@ -501,6 +501,7 @@ case object SUnsignedBigInt extends SPrimType with SEmbeddable with SNumericType override def numericTypeIndex: Int = 5 + // todo: consider upcast and downcast rules override def upcast(v: AnyVal): UnsignedBigInt = { val bi = v match { case x: Byte => BigInteger.valueOf(x.toLong) diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 5ae617feba..b9134ef892 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -90,4 +90,8 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign override def and(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.and(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) override def or(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.or(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) + + override def modInverse(m: UnsignedBigInt): UnsignedBigInt = { + CUnsignedBigInt(wrappedValue.modInverse(m.asInstanceOf[CUnsignedBigInt].wrappedValue)) + } } diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index e0e890abca..dde155c7f3 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -128,6 +128,7 @@ object ReflectionData { ) ) } + //todo: add UnsignedBigInt { val clazz = classOf[CollBuilder] registerClassEntry(clazz, diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 179e21485c..c8d8cacfbb 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -90,7 +90,7 @@ sealed trait MethodsContainer { } object MethodsContainer { - private val containers = new SparseArrayContainer[MethodsContainer](Array( + private val methodsV5 = Array( SByteMethods, SShortMethods, SIntMethods, @@ -111,11 +111,29 @@ object MethodsContainer { STupleMethods, SUnitMethods, SAnyMethods - ).map(m => (m.typeId, m))) + ) + + private val methodsV6 = methodsV5 ++ Seq(SUnsignedBigIntMethods) + + private val containersV5 = new SparseArrayContainer[MethodsContainer](methodsV5.map(m => (m.typeId, m))) - def contains(typeId: TypeCode): Boolean = containers.contains(typeId) + private val containersV6 = new SparseArrayContainer[MethodsContainer](methodsV6.map(m => (m.typeId, m))) - def apply(typeId: TypeCode): MethodsContainer = containers(typeId) + def contains(typeId: TypeCode): Boolean = { + if (VersionContext.current.isV6SoftForkActivated) { + containersV6.contains(typeId) + } else { + containersV5.contains(typeId) + } + } + + def apply(typeId: TypeCode): MethodsContainer = { + if (VersionContext.current.isV6SoftForkActivated) { + containersV6(typeId) + } else { + containersV5(typeId) + } + } /** Finds the method of the give type. * @@ -127,7 +145,11 @@ object MethodsContainer { case tup: STuple => STupleMethods.getTupleMethod(tup, methodName) case _ => - containers.get(tpe.typeCode).flatMap(_.method(methodName)) + if (VersionContext.current.isV6SoftForkActivated) { + containersV6.get(tpe.typeCode).flatMap(_.method(methodName)) + } else { + containersV5.get(tpe.typeCode).flatMap(_.method(methodName)) + } } } @@ -335,6 +357,48 @@ case object SBigIntMethods extends SNumericTypeMethods { } +/** Methods of UnsignedBigInt type. Implemented using [[java.math.BigInteger]]. */ +case object SUnsignedBigIntMethods extends SNumericTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = SUnsignedBigInt + + final val ToNBitsCostInfo = OperationCostInfo( + FixedCost(JitCost(5)), NamedDesc("NBitsMethodCall")) + + //id = 8 to make it after toBits + val ToNBits = SMethod(this, "nbits", SFunc(this.ownerType, SLong), 8, ToNBitsCostInfo.costKind) + .withInfo(ModQ, "Encode this big integer value as NBits") + + // todo: costing + final val ModInverseCostInfo = ToNBitsCostInfo + + // todo: check ids before and after merging with other PRs introducing new methods for Numeric + val ModInverseMethod = SMethod(this, "modInverse", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 9, ModInverseCostInfo.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + def modInverse_eval(mc: MethodCall, bi: UnsignedBigInt, m: UnsignedBigInt) + (implicit E: ErgoTreeEvaluator): UnsignedBigInt = { + E.addCost(ModInverseCostInfo.costKind, mc.method.opDesc) + bi.modInverse(m) + } + + // no 6.0 versioning here as it is done in method containers + protected override def getMethods(): Seq[SMethod] = { + super.getMethods() ++ Seq( + ModInverseMethod + ) + } + + /** + * + */ + def nbits_eval(mc: MethodCall, bi: sigma.BigInt)(implicit E: ErgoTreeEvaluator): Long = { + ??? + } + +} + /** Methods of type `String`. */ case object SStringMethods extends MonoTypeMethods { /** Type for which this container defines methods. */ 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 e2350b8338..f9fc30625d 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1013,6 +1013,11 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => opt.filter(asRep[t => Boolean](argsV(0))) case _ => throwError } + case (ubi: Ref[UnsignedBigInt]@unchecked, SUnsignedBigIntMethods) => method.name match { + case SUnsignedBigIntMethods.ModInverseMethod.name => + val m = asRep[UnsignedBigInt](argsV(0)) + ubi.modInverse(m) + } case (ge: Ref[GroupElement]@unchecked, SGroupElementMethods) => method.name match { case SGroupElementMethods.GetEncodedMethod.name => ge.getEncoded diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index b3be395a74..e3afee48be 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -22,6 +22,7 @@ import scalan._ def mod(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; def min(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; def max(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; + def modInverse(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] }; trait GroupElement extends Def[GroupElement] { def exp(k: Ref[BigInt]): Ref[GroupElement]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index 27ebaa717c..a81962346a 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -332,6 +332,13 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { Array[AnyRef](that), true, false, element[UnsignedBigInt])) } + + override def modInverse(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("modInverse", classOf[Sym]), + Array[AnyRef](m), + true, false, element[UnsignedBigInt])) + } } @@ -402,6 +409,20 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { Array[AnyRef](that), true, true, element[UnsignedBigInt])) } + + def modInverse(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("modInverse", classOf[Sym]), + Array[AnyRef](m), + true, true, element[UnsignedBigInt])) + } + } + + // entityUnref: single unref method for each type family + implicit final def unrefUnsignedBigInt(p: Ref[UnsignedBigInt]): UnsignedBigInt = { + if (p.node.isInstanceOf[UnsignedBigInt]) p.node.asInstanceOf[UnsignedBigInt] + else + UnsignedBigIntAdapter(p) } class UnsignedBigIntElem[To <: UnsignedBigInt] diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 4d7d3718e9..df339f5661 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -252,6 +252,26 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("modInverse") { + def miTest() = { + test("modInverse", env, ext, + s"""{ + | val bi = unsignedBigInt("248486720836984554860790790898080606") + | val m = unsignedBigInt("575879797") + | bi.modInverse(m) > 0 + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy miTest() + } else { + miTest() + } + } + property("Bulletproof verification for a circuit proof") { val g = CGroupElement(SecP256K1Group.generator) From e989665dd3422d981e020d9ba37b395ac447bbde Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 13 Jun 2024 22:10:11 +0300 Subject: [PATCH 30/78] plusMod --- .../src/main/scala/sigma/SigmaDsl.scala | 1 + .../src/main/scala/sigma/data/CBigInt.scala | 6 + .../src/main/scala/sigma/ast/methods.scala | 14 ++- .../sigma/compiler/ir/GraphBuilding.scala | 4 + .../ir/wrappers/sigma/SigmaDslUnit.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 14 +++ .../utxo/BasicOpsSpecification.scala | 113 +++++++++++------- 7 files changed, 106 insertions(+), 47 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 3366bddfac..bd4c352b56 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -282,6 +282,7 @@ trait UnsignedBigInt { def |(that: UnsignedBigInt): UnsignedBigInt = or(that) def modInverse(m: UnsignedBigInt): UnsignedBigInt + def plusMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt } diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index b9134ef892..448cb97e54 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -94,4 +94,10 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign override def modInverse(m: UnsignedBigInt): UnsignedBigInt = { CUnsignedBigInt(wrappedValue.modInverse(m.asInstanceOf[CUnsignedBigInt].wrappedValue)) } + + override def plusMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt = { + val thatBi = that.asInstanceOf[CUnsignedBigInt].wrappedValue + val mBi = m.asInstanceOf[CUnsignedBigInt].wrappedValue + CUnsignedBigInt(wrappedValue.add(thatBi).mod(mBi)) + } } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index c8d8cacfbb..5a1deb184b 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -383,10 +383,22 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods { bi.modInverse(m) } + // todo: costing + val PlusModMethod = SMethod(this, "plusMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 10, ModInverseCostInfo.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + def plusMod_eval(mc: MethodCall, bi: UnsignedBigInt, bi2: UnsignedBigInt, m: UnsignedBigInt) + (implicit E: ErgoTreeEvaluator): UnsignedBigInt = { + E.addCost(ModInverseCostInfo.costKind, mc.method.opDesc) // todo: costing + bi.plusMod(bi2, m) + } + // no 6.0 versioning here as it is done in method containers protected override def getMethods(): Seq[SMethod] = { super.getMethods() ++ Seq( - ModInverseMethod + ModInverseMethod, + PlusModMethod ) } 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 f9fc30625d..d24eac2895 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1017,6 +1017,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SUnsignedBigIntMethods.ModInverseMethod.name => val m = asRep[UnsignedBigInt](argsV(0)) ubi.modInverse(m) + case SUnsignedBigIntMethods.PlusModMethod.name => + val that = asRep[UnsignedBigInt](argsV(0)) + val m = asRep[UnsignedBigInt](argsV(1)) + ubi.plusMod(that, m) } case (ge: Ref[GroupElement]@unchecked, SGroupElementMethods) => method.name match { case SGroupElementMethods.GetEncodedMethod.name => diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index e3afee48be..c6c24091ee 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -23,6 +23,7 @@ import scalan._ def min(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; def max(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; def modInverse(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] + def plusMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] }; trait GroupElement extends Def[GroupElement] { def exp(k: Ref[BigInt]): Ref[GroupElement]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index a81962346a..a34e3dbd52 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -339,6 +339,13 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { Array[AnyRef](m), true, false, element[UnsignedBigInt])) } + + override def plusMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("plusMod", classOf[Sym], classOf[Sym]), + Array[AnyRef](that, m), + true, false, element[UnsignedBigInt])) + } } @@ -416,6 +423,13 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { Array[AnyRef](m), true, true, element[UnsignedBigInt])) } + + def plusMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("plusMod", classOf[Sym], classOf[Sym]), + Array[AnyRef](that, m), + true, true, element[UnsignedBigInt])) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index df339f5661..8f942d0ea5 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -272,51 +272,14 @@ class BasicOpsSpecification extends CompilerTestingCommons } } - property("Bulletproof verification for a circuit proof") { - - val g = CGroupElement(SecP256K1Group.generator) - - def circuitTest() = { - test("schnorr", env, ext, + property("mod ops - plus") { + def miTest() = { + test("modInverse", env, ext, s"""{ - | // circuit data - should be provided via data input likely - | val lWeights: Coll[UnsignedBigInt] - | val rWeights: Coll[UnsignedBigInt] - | val oWeights: Coll[UnsignedBigInt] - | val commitmentWeights: Coll[UnsignedBigInt] - | - | val cs: Coll[UnsignedBigInt] - | val commitments: Coll[GroupElement] - | - | // proof data - | val ai: GroupElement - | val ao: GroupElement - | val s: GroupElement - | val tCommits: Coll[GroupElement] - | val tauX: UnsignedBigInt - | val mu: UnsignedBigInt - | val t: UnsignedBigInt - | - | // inner product proof - | val L: Coll[GroupElement] - | val R: Coll[GroupElement] - | val a: UnsignedBigInt - | val b: UnsignedBigInt - | - | // proof verification: - | val Q = lWeights.size - | - | val q // group order - | - | val yBytes = sha256(q.toBytes ++ aI.getEncoded ++ aO.getEncoded ++ s.getEncoded) - | - | val y = byteArrayToBigInt(yBytes) // should be to unsigned bigint - | - | val z = byteArrayToBigInt(sha256(y ++ q.toBytes)) - | - | - | - | sigmaProp(properSignature) + | val bi1 = unsignedBigInt("248486720836984554860790790898080606") + | val bi2 = unsignedBigInt("2484867208369845548607907908980997780606") + | val m = unsignedBigInt("575879797") + | bi1.plusMod(bi2, m) > 0 |}""".stripMargin, null, true @@ -324,9 +287,9 @@ class BasicOpsSpecification extends CompilerTestingCommons } if (activatedVersionInTests < V6SoftForkVersion) { - an[Exception] should be thrownBy circuitTest() + an[Exception] should be thrownBy miTest() } else { - circuitTest() + miTest() } } @@ -446,6 +409,64 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Bulletproof verification for a circuit proof") { + + val g = CGroupElement(SecP256K1Group.generator) + + def circuitTest() = { + test("schnorr", env, ext, + s"""{ + | // circuit data - should be provided via data input likely + | val lWeights = Coll[UnsignedBigInt] + | val rWeights: Coll[UnsignedBigInt] + | val oWeights: Coll[UnsignedBigInt] + | val commitmentWeights: Coll[UnsignedBigInt] + | + | val cs: Coll[UnsignedBigInt] + | val commitments: Coll[GroupElement] + | + | // proof data + | val ai: GroupElement + | val ao: GroupElement + | val s: GroupElement + | val tCommits: Coll[GroupElement] + | val tauX: UnsignedBigInt + | val mu: UnsignedBigInt + | val t: UnsignedBigInt + | + | // inner product proof + | val L: Coll[GroupElement] + | val R: Coll[GroupElement] + | val a: UnsignedBigInt + | val b: UnsignedBigInt + | + | // proof verification: + | val Q = lWeights.size + | + | val q // group order + | + | val yBytes = sha256(q.toBytes ++ aI.getEncoded ++ aO.getEncoded ++ s.getEncoded) + | + | val y = byteArrayToBigInt(yBytes) // should be to unsigned bigint + | + | val z = byteArrayToBigInt(sha256(y ++ q.toBytes)) + | + | + | + | sigmaProp(properSignature) + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy circuitTest() + } else { + circuitTest() + } + } + property("Unit register") { // TODO frontend: implement missing Unit support in compiler // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 From d776db3256371c16944aa6c5824d89be4bb1cf51 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 13 Jun 2024 22:27:28 +0300 Subject: [PATCH 31/78] multiplyMod --- .../src/main/scala/sigma/SigmaDsl.scala | 1 + .../src/main/scala/sigma/data/CBigInt.scala | 6 ++ .../src/main/scala/sigma/ast/methods.scala | 13 ++++- .../sigma/compiler/ir/GraphBuilding.scala | 4 ++ .../ir/wrappers/sigma/SigmaDslUnit.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 14 +++++ .../utxo/BasicOpsSpecification.scala | 58 +++++++++++++------ 7 files changed, 78 insertions(+), 19 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index bd4c352b56..251b94b288 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -283,6 +283,7 @@ trait UnsignedBigInt { def modInverse(m: UnsignedBigInt): UnsignedBigInt def plusMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt + def multiplyMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt } diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 448cb97e54..60f87e7cf7 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -100,4 +100,10 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign val mBi = m.asInstanceOf[CUnsignedBigInt].wrappedValue CUnsignedBigInt(wrappedValue.add(thatBi).mod(mBi)) } + + override def multiplyMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt = { + val thatBi = that.asInstanceOf[CUnsignedBigInt].wrappedValue + val mBi = m.asInstanceOf[CUnsignedBigInt].wrappedValue + CUnsignedBigInt(wrappedValue.multiply(thatBi).mod(mBi)) + } } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 5a1deb184b..35700f799d 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -394,11 +394,22 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods { bi.plusMod(bi2, m) } + val MultiplyModMethod = SMethod(this, "multiplyMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 11, ModInverseCostInfo.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + def multiplyMod_eval(mc: MethodCall, bi: UnsignedBigInt, bi2: UnsignedBigInt, m: UnsignedBigInt) + (implicit E: ErgoTreeEvaluator): UnsignedBigInt = { + E.addCost(ModInverseCostInfo.costKind, mc.method.opDesc) // todo: costing + bi.multiplyMod(bi2, m) + } + // no 6.0 versioning here as it is done in method containers protected override def getMethods(): Seq[SMethod] = { super.getMethods() ++ Seq( ModInverseMethod, - PlusModMethod + PlusModMethod, + MultiplyModMethod ) } 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 d24eac2895..a330ae405b 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1021,6 +1021,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val that = asRep[UnsignedBigInt](argsV(0)) val m = asRep[UnsignedBigInt](argsV(1)) ubi.plusMod(that, m) + case SUnsignedBigIntMethods.MultiplyModMethod.name => + val that = asRep[UnsignedBigInt](argsV(0)) + val m = asRep[UnsignedBigInt](argsV(1)) + ubi.multiplyMod(that, m) } case (ge: Ref[GroupElement]@unchecked, SGroupElementMethods) => method.name match { case SGroupElementMethods.GetEncodedMethod.name => diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index c6c24091ee..5a9b6903c1 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -24,6 +24,7 @@ import scalan._ def max(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; def modInverse(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] def plusMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] + def multiplyMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] }; trait GroupElement extends Def[GroupElement] { def exp(k: Ref[BigInt]): Ref[GroupElement]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index a34e3dbd52..05f799dbc5 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -346,6 +346,13 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { Array[AnyRef](that, m), true, false, element[UnsignedBigInt])) } + + override def multiplyMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("multiplyMod", classOf[Sym], classOf[Sym]), + Array[AnyRef](that, m), + true, false, element[UnsignedBigInt])) + } } @@ -430,6 +437,13 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { Array[AnyRef](that, m), true, true, element[UnsignedBigInt])) } + + def multiplyMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("multiplyMod", classOf[Sym], classOf[Sym]), + Array[AnyRef](that, m), + true, true, element[UnsignedBigInt])) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 8f942d0ea5..8b0b8a1367 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -168,7 +168,7 @@ class BasicOpsSpecification extends CompilerTestingCommons // todo: how to upcast? def deserTest() = {test("restoring", env, ext, - s"{ val b = bigInt(\"${ub.toString}\"); b > 1 }", + s"{ val b = bigInt(\"${ub.toString}\").toUnsigned; b > 1 }", null, true )} @@ -293,6 +293,27 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("mod ops - multiply") { + def miTest() = { + test("modInverse", env, ext, + s"""{ + | val bi1 = unsignedBigInt("248486720836984554860790790898080606") + | val bi2 = unsignedBigInt("2484867208369845548607907908980997780606") + | val m = unsignedBigInt("575879797") + | bi1.multiplyMod(bi2, m) > 0 + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy miTest() + } else { + miTest() + } + } + property("Bulletproof verification for a range proof") { /* * Original range proof verifier code by Benedikt Bunz: @@ -357,30 +378,30 @@ class BasicOpsSpecification extends CompilerTestingCommons val g = CGroupElement(SecP256K1Group.generator) - def circuitTest() = { + def rangeTest() = { test("range proof", env, ext, s"""{ - | // circuit data - should be provided via data input likely - | val input: GroupElement + | // range proof input data + | val input: GroupElement = getVar[GroupElement](0).get | | // proof data - | val ai: GroupElement - | val s: GroupElement - | val tCommits: Coll[GroupElement] - | val tauX: UnsignedBigInt - | val mu: UnsignedBigInt - | val t: UnsignedBigInt + | val ai: GroupElement = getVar[GroupElement](1).get + | val s: GroupElement = getVar[GroupElement](2).get + | val tCommits: Coll[GroupElement] = getVar[Coll[GroupElement]](3).get + | val tauX: UnsignedBigInt = getVar[UnsignedBigInt](4).get + | val mu: UnsignedBigInt = getVar[UnsignedBigInt](5).get + | val t: UnsignedBigInt = getVar[UnsignedBigInt](6).get | | // inner product proof - | val L: Coll[GroupElement] - | val R: Coll[GroupElement] - | val a: UnsignedBigInt - | val b: UnsignedBigInt + | val L: Coll[GroupElement] = getVar[Coll[GroupElement]](7).get + | val R: Coll[GroupElement] = getVar[Coll[GroupElement]](8)).get + | val a: UnsignedBigInt = getVar[UnsignedBigInt](9).get + | val b: UnsignedBigInt = getVar[UnsignedBigInt](10).get | | // proof verification: | val Q = lWeights.size | - | val q // group order + | val q // group order = getVar[UnsignedBigInt](11).get | | val yBytes = sha256(q.toBytes ++ input.getEncoded ++ aI.getEncoded ++ s.getEncoded) | @@ -403,13 +424,14 @@ class BasicOpsSpecification extends CompilerTestingCommons } if (activatedVersionInTests < V6SoftForkVersion) { - an[Exception] should be thrownBy circuitTest() + an[Exception] should be thrownBy rangeTest() } else { - circuitTest() + rangeTest() } } - property("Bulletproof verification for a circuit proof") { + // todo: complete + ignore("Bulletproof verification for a circuit proof") { val g = CGroupElement(SecP256K1Group.generator) From 72db85b0f0e1e49557368aa028801a4048b4213e Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 14 Jun 2024 23:11:04 +0300 Subject: [PATCH 32/78] toUnsigned & toUnsignedMod --- .../src/main/scala/sigma/SigmaDsl.scala | 4 ++ .../src/main/scala/sigma/data/CBigInt.scala | 12 +++++ .../src/main/scala/sigma/ast/methods.scala | 29 +++++++++- .../sigma/compiler/ir/GraphBuilding.scala | 7 +++ .../ir/wrappers/sigma/SigmaDslUnit.scala | 2 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 34 +++++++++++- .../utxo/BasicOpsSpecification.scala | 54 ++++++++++++++++--- 7 files changed, 133 insertions(+), 9 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 251b94b288..d7749f825b 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -152,6 +152,10 @@ trait BigInt { */ def or(that: BigInt): BigInt def |(that: BigInt): BigInt = or(that) + + def toUnsigned: UnsignedBigInt + + def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt } diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 60f87e7cf7..1951f55486 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -49,6 +49,18 @@ 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)) + + def toUnsigned: UnsignedBigInt = { + if(this.wrappedValue.compareTo(BigInteger.ZERO) < 0){ + throw new ArithmeticException("BigInteger argument for .toUnsigned is negative in"); + } else { + CUnsignedBigInt(this.wrappedValue) + } + } + + def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt = { + CUnsignedBigInt(this.wrappedValue.mod(m.asInstanceOf[CUnsignedBigInt].wrappedValue)) + } } /** A default implementation of [[BigInt]] interface. diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 35700f799d..b34c256449 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -6,6 +6,7 @@ import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} import sigma.ast.SType.TypeCode +import sigma.ast.SUnsignedBigIntMethods.ModInverseCostInfo import sigma.ast.syntax.{SValue, ValueOps} import sigma.data.OverloadHack.Overloaded1 import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} @@ -336,13 +337,39 @@ case object SBigIntMethods extends SNumericTypeMethods { final val ToNBitsCostInfo = OperationCostInfo( FixedCost(JitCost(5)), NamedDesc("NBitsMethodCall")) + // todo: check ids after merging w. other numeric methods + //id = 8 to make it after toBits val ToNBits = SMethod(this, "nbits", SFunc(this.ownerType, SLong), 8, ToNBitsCostInfo.costKind) .withInfo(ModQ, "Encode this big integer value as NBits") + //id = 8 to make it after toBits + val ToUnsigned = SMethod(this, "toUnsigned", SFunc(this.ownerType, SUnsignedBigInt), 9, ToNBitsCostInfo.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + def toUnsigned_eval(mc: MethodCall, bi: BigInt) + (implicit E: ErgoTreeEvaluator): UnsignedBigInt = { + E.addCost(ModInverseCostInfo.costKind, mc.method.opDesc) + bi.toUnsigned + } + + + val ToUnsignedMod = SMethod(this, "toUnsignedMod", SFunc(Array(this.ownerType, SUnsignedBigInt), SUnsignedBigInt), 10, ToNBitsCostInfo.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + def toUnsignedMod_eval(mc: MethodCall, bi: BigInt, m: UnsignedBigInt) + (implicit E: ErgoTreeEvaluator): UnsignedBigInt = { + E.addCost(ModInverseCostInfo.costKind, mc.method.opDesc) + bi.toUnsignedMod(m) + } + + + protected override def getMethods(): Seq[SMethod] = { if (VersionContext.current.isV6SoftForkActivated) { - super.getMethods() ++ Seq(ToNBits) + super.getMethods() ++ Seq(ToNBits, ToUnsigned, ToUnsignedMod) } else { super.getMethods() } 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 a330ae405b..b637294d0c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1013,6 +1013,13 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => opt.filter(asRep[t => Boolean](argsV(0))) case _ => throwError } + case (bi: Ref[BigInt]@unchecked, SBigIntMethods) => method.name match { + case SBigIntMethods.ToUnsigned.name => + bi.toUnsigned() + case SBigIntMethods.ToUnsignedMod.name => + val m = asRep[UnsignedBigInt](argsV(0)) + bi.toUnsignedMod(m) + } case (ubi: Ref[UnsignedBigInt]@unchecked, SUnsignedBigIntMethods) => method.name match { case SUnsignedBigIntMethods.ModInverseMethod.name => val m = asRep[UnsignedBigInt](argsV(0)) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index 5a9b6903c1..595880694c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -13,6 +13,8 @@ import scalan._ def mod(m: Ref[BigInt]): Ref[BigInt]; def min(that: Ref[BigInt]): Ref[BigInt]; def max(that: Ref[BigInt]): Ref[BigInt]; + def toUnsigned(): Ref[UnsignedBigInt]; + def toUnsignedMod(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] }; trait UnsignedBigInt extends Def[UnsignedBigInt] { def add(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index 05f799dbc5..ef5894fc71 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -97,6 +97,22 @@ object BigInt extends EntityObject("BigInt") { Array[AnyRef](that), true, false, element[BigInt])) } + + import UnsignedBigInt.unsignedBigIntElement + + override def toUnsigned(): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + BigIntClass.getMethod("toUnsigned"), + Array[AnyRef](), + true, false, element[UnsignedBigInt](unsignedBigIntElement))) + } + + override def toUnsignedMod(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + BigIntClass.getMethod("toUnsignedMod", classOf[Sym]), + Array[AnyRef](m), + true, false, element[UnsignedBigInt](unsignedBigIntElement))) + } } implicit object LiftableBigInt @@ -165,6 +181,22 @@ object BigInt extends EntityObject("BigInt") { Array[AnyRef](that), true, true, element[BigInt])) } + + import UnsignedBigInt.unsignedBigIntElement + + def toUnsigned(): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + BigIntClass.getMethod("toUnsigned"), + Array[AnyRef](), + true, true, element[UnsignedBigInt](unsignedBigIntElement))) + } + + def toUnsignedMod(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + BigIntClass.getMethod("UnsignedBigInt", classOf[Sym]), + Array[AnyRef](that), + true, true, element[UnsignedBigInt](unsignedBigIntElement))) + } } // entityUnref: single unref method for each type family @@ -182,7 +214,7 @@ object BigInt extends EntityObject("BigInt") { override protected def collectMethods: Map[RMethod, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[BigInt]), RClass(classOf[SBigInt]), Set( - "add", "subtract", "multiply", "divide", "mod", "min", "max" + "add", "subtract", "multiply", "divide", "mod", "min", "max", "toUnsigned", "toUnsignedMod" )) } } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 8b0b8a1367..49b565dac1 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -159,25 +159,65 @@ class BasicOpsSpecification extends CompilerTestingCommons true )} - deserTest() + deserTest() // todo: should fail < 6.0 } - property("signed <-> unsigned bigint conversion - positive bigint") { + property("signed -> unsigned bigint conversion - positive bigint") { val b = new BigInteger("9280562930080889354892980449861222646750586663683904599823322027983929189860") val ub = new BigInteger(1, b.toByteArray) - // todo: how to upcast? - def deserTest() = {test("restoring", env, ext, - s"{ val b = bigInt(\"${ub.toString}\").toUnsigned; b > 1 }", + def conversionTest() = {test("restoring", env, ext, + s"""{ + | val b = bigInt(\"${ub.toString}\") + | val ub = b.toUnsigned + | ub > 1 + | } """.stripMargin, null, true )} - deserTest() + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + conversionTest() + } } - property("signed <-> unsigned bigint conversion - negative bigint") { + property("signed -> unsigned bigint conversion - negative bigint") { + def conversionTest() = {test("restoring", env, ext, + s"""{ + | val b = bigInt("-1") + | val ub = b.toUnsigned + | ub > 0 + | } """.stripMargin, + null, + true + )} + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + an[Exception] should be thrownBy conversionTest() + } + } + + property("signed -> unsigned bigint conversion - negative bigint - mod") { + def conversionTest() = {test("restoring", env, ext, + s"""{ + | val b = bigInt("-1") + | val m = unsignedBigInt("5") + | val ub = b.toUnsignedMod(m) + | ub >= 0 + | } """.stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + conversionTest() + } } property("schnorr sig check") { From fc6d8562d39a7e6ca9b59a92ae4d132d0e5938d1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 17 Jun 2024 19:33:39 +0300 Subject: [PATCH 33/78] mod, toSigned, subtractMod --- .../src/main/scala/sigma/SigmaDsl.scala | 13 +-- .../src/main/scala/sigma/data/CBigInt.scala | 12 ++- .../src/main/scala/sigma/ast/methods.scala | 37 +++++++- .../sigma/compiler/ir/GraphBuilding.scala | 9 ++ .../ir/wrappers/sigma/SigmaDslUnit.scala | 2 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 28 ++++++ .../utxo/BasicOpsSpecification.scala | 88 +++++++++++++++---- 7 files changed, 158 insertions(+), 31 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index d7749f825b..86730a705e 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -242,16 +242,6 @@ trait UnsignedBigInt { def mod(m: UnsignedBigInt): UnsignedBigInt def %(m: UnsignedBigInt): UnsignedBigInt = mod(m) - /** - * Returns a BigInt whose value is {@code (this % that)}. - * - * @param that value by which this BigInt is to be divided, and the - * remainder computed. - * @return { @code this % that} - * @throws ArithmeticException if { @code that} is zero. - */ - def remainder(that: UnsignedBigInt): UnsignedBigInt - /** * Returns the minimum of this BigInteger and {@code val}. * @@ -287,7 +277,10 @@ trait UnsignedBigInt { def modInverse(m: UnsignedBigInt): UnsignedBigInt def plusMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt + def subtractMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt def multiplyMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt + + def toSigned(): BigInt } diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 1951f55486..0ebf57ab6c 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -93,8 +93,6 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign override def mod(m: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.mod(m.asInstanceOf[CUnsignedBigInt].wrappedValue)) - override def remainder(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.remainder(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) - override def min(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.min(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) override def max(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.max(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) @@ -113,9 +111,19 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign CUnsignedBigInt(wrappedValue.add(thatBi).mod(mBi)) } + override def subtractMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt = { + val thatBi = that.asInstanceOf[CUnsignedBigInt].wrappedValue + val mBi = m.asInstanceOf[CUnsignedBigInt].wrappedValue + CUnsignedBigInt(wrappedValue.subtract(thatBi).mod(mBi)) + } + override def multiplyMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt = { val thatBi = that.asInstanceOf[CUnsignedBigInt].wrappedValue val mBi = m.asInstanceOf[CUnsignedBigInt].wrappedValue CUnsignedBigInt(wrappedValue.multiply(thatBi).mod(mBi)) } + + override def toSigned(): BigInt = { + CBigInt(wrappedValue.to256BitValueExact) + } } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index b34c256449..bcd959ebc2 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -421,7 +421,17 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods { bi.plusMod(bi2, m) } - val MultiplyModMethod = SMethod(this, "multiplyMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 11, ModInverseCostInfo.costKind) + val SubtractModMethod = SMethod(this, "subtractMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 11, ModInverseCostInfo.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + def subtractMod_eval(mc: MethodCall, bi: UnsignedBigInt, bi2: UnsignedBigInt, m: UnsignedBigInt) + (implicit E: ErgoTreeEvaluator): UnsignedBigInt = { + E.addCost(ModInverseCostInfo.costKind, mc.method.opDesc) // todo: costing + bi.subtractMod(bi2, m) + } + + val MultiplyModMethod = SMethod(this, "multiplyMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 12, ModInverseCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -431,12 +441,35 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods { bi.multiplyMod(bi2, m) } + val ModMethod = SMethod(this, "mod", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 13, ModInverseCostInfo.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + def mod_eval(mc: MethodCall, bi: UnsignedBigInt, m: UnsignedBigInt) + (implicit E: ErgoTreeEvaluator): UnsignedBigInt = { + E.addCost(ModInverseCostInfo.costKind, mc.method.opDesc) // todo: costing + bi.mod(m) + } + + val ToSignedMethod = SMethod(this, "toSigned", SFunc(Array(this.ownerType), SBigInt), 14, ModInverseCostInfo.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + def toSigned_eval(mc: MethodCall, bi: UnsignedBigInt) + (implicit E: ErgoTreeEvaluator): BigInt = { + E.addCost(ModInverseCostInfo.costKind, mc.method.opDesc) // todo: costing + bi.toSigned() + } + // no 6.0 versioning here as it is done in method containers protected override def getMethods(): Seq[SMethod] = { super.getMethods() ++ Seq( ModInverseMethod, PlusModMethod, - MultiplyModMethod + SubtractModMethod, + MultiplyModMethod, + ModMethod, + ToSignedMethod ) } 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 b637294d0c..80433d593d 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1021,6 +1021,9 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => bi.toUnsignedMod(m) } case (ubi: Ref[UnsignedBigInt]@unchecked, SUnsignedBigIntMethods) => method.name match { + case SUnsignedBigIntMethods.ModMethod.name => + val m = asRep[UnsignedBigInt](argsV(0)) + ubi.mod(m) case SUnsignedBigIntMethods.ModInverseMethod.name => val m = asRep[UnsignedBigInt](argsV(0)) ubi.modInverse(m) @@ -1028,10 +1031,16 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val that = asRep[UnsignedBigInt](argsV(0)) val m = asRep[UnsignedBigInt](argsV(1)) ubi.plusMod(that, m) + case SUnsignedBigIntMethods.SubtractModMethod.name => + val that = asRep[UnsignedBigInt](argsV(0)) + val m = asRep[UnsignedBigInt](argsV(1)) + ubi.subtractMod(that, m) case SUnsignedBigIntMethods.MultiplyModMethod.name => val that = asRep[UnsignedBigInt](argsV(0)) val m = asRep[UnsignedBigInt](argsV(1)) ubi.multiplyMod(that, m) + case SUnsignedBigIntMethods.ToSignedMethod.name => + ubi.toSigned } case (ge: Ref[GroupElement]@unchecked, SGroupElementMethods) => method.name match { case SGroupElementMethods.GetEncodedMethod.name => diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index 595880694c..a1d7c9c56b 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -26,7 +26,9 @@ import scalan._ def max(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]; def modInverse(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] def plusMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] + def subtractMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] def multiplyMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] + def toSigned: Ref[BigInt] }; trait GroupElement extends Def[GroupElement] { def exp(k: Ref[BigInt]): Ref[GroupElement]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index ef5894fc71..6a62007523 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -379,12 +379,26 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { true, false, element[UnsignedBigInt])) } + override def subtractMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("subtractMod", classOf[Sym], classOf[Sym]), + Array[AnyRef](that, m), + true, false, element[UnsignedBigInt])) + } + override def multiplyMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { asRep[UnsignedBigInt](mkMethodCall(self, UnsignedBigIntClass.getMethod("multiplyMod", classOf[Sym], classOf[Sym]), Array[AnyRef](that, m), true, false, element[UnsignedBigInt])) } + + override def toSigned: Ref[BigInt] = { + asRep[BigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("toSigned"), + Array[AnyRef](), + true, false, element[BigInt])) + } } @@ -470,12 +484,26 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { true, true, element[UnsignedBigInt])) } + def subtractMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("subtractMod", classOf[Sym], classOf[Sym]), + Array[AnyRef](that, m), + true, true, element[UnsignedBigInt])) + } + def multiplyMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { asRep[UnsignedBigInt](mkMethodCall(source, UnsignedBigIntClass.getMethod("multiplyMod", classOf[Sym], classOf[Sym]), Array[AnyRef](that, m), true, true, element[UnsignedBigInt])) } + + def toSigned: Ref[BigInt] = { + asRep[BigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("toSigned"), + Array[AnyRef](), + true, true, element[BigInt])) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 49b565dac1..920fa8ba2c 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -166,7 +166,7 @@ class BasicOpsSpecification extends CompilerTestingCommons val b = new BigInteger("9280562930080889354892980449861222646750586663683904599823322027983929189860") val ub = new BigInteger(1, b.toByteArray) - def conversionTest() = {test("restoring", env, ext, + def conversionTest() = {test("conversion", env, ext, s"""{ | val b = bigInt(\"${ub.toString}\") | val ub = b.toUnsigned @@ -184,7 +184,7 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("signed -> unsigned bigint conversion - negative bigint") { - def conversionTest() = {test("restoring", env, ext, + def conversionTest() = {test("conversion", env, ext, s"""{ | val b = bigInt("-1") | val ub = b.toUnsigned @@ -202,7 +202,7 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("signed -> unsigned bigint conversion - negative bigint - mod") { - def conversionTest() = {test("restoring", env, ext, + def conversionTest() = {test("conversion", env, ext, s"""{ | val b = bigInt("-1") | val m = unsignedBigInt("5") @@ -220,6 +220,24 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("unsigned -> signed bigint conversion") { + def conversionTest() = {test("conversion", env, ext, + s"""{ + | val ub = unsignedBigInt("10") + | val b = ub.toSigned + | b - 11 == bigInt("-1") + | } """.stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + conversionTest() + } + } + property("schnorr sig check") { val g = CGroupElement(SecP256K1Group.generator) @@ -292,6 +310,26 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("mod") { + def miTest() = { + test("mod", env, ext, + s"""{ + | val bi = unsignedBigInt("248486720836984554860790790898080606") + | val m = unsignedBigInt("575879797") + | bi.mod(m) < bi + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy miTest() + } else { + miTest() + } + } + property("modInverse") { def miTest() = { test("modInverse", env, ext, @@ -333,6 +371,27 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("mod ops - subtract") { + def miTest() = { + test("subtractMod", env, ext, + s"""{ + | val bi1 = unsignedBigInt("2") + | val bi2 = unsignedBigInt("4") + | val m = unsignedBigInt("575879797") + | bi1.subtractMod(bi2, m) > 0 + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy miTest() + } else { + miTest() + } + } + property("mod ops - multiply") { def miTest() = { test("modInverse", env, ext, @@ -365,17 +424,11 @@ class BasicOpsSpecification extends CompilerTestingCommons T s = proof.getS(); BigInteger q = params.getGroup().groupOrder(); - BigInteger y; - - if (salt.isPresent()) { - y = ProofUtils.computeChallenge(q, salt.get(), input, a, s); - } else { - y = ProofUtils.computeChallenge(q, input, a, s); + BigInteger y = ProofUtils.computeChallenge(q, input, a, s); - } FieldVector ys = FieldVector.from(VectorX.iterate(n, BigInteger.ONE, y::multiply), q); - BigInteger z = ProofUtils.challengeFromints(q, y); + BigInteger z = ProofUtils.challengeFromints(q, y); BigInteger zSquared = z.pow(2).mod(q); BigInteger zCubed = z.pow(3).mod(q); @@ -445,18 +498,19 @@ class BasicOpsSpecification extends CompilerTestingCommons | | val yBytes = sha256(q.toBytes ++ input.getEncoded ++ aI.getEncoded ++ s.getEncoded) | - | val y = byteArrayToBigInt(yBytes) mod q // should be to unsigned bigint + | val y = byteArrayToBigInt(yBytes).toUnsignedMod(q) | - | val z = byteArrayToBigInt(sha256(q.toBytes ++ yBytes)) mod q + | val ys = | - | val zSquared = z * z mod q - | val zCubed = zSquared * z mod q + | val z = byteArrayToBigInt(sha256(q.toBytes ++ yBytes)).toUnsignedMod(q) + | val zSquared = z.multiplyMod(z, q) + | val zCubed = zSquared.multiplyMod(z, q) | - | def times() : // todo: implement + | // def times() : // todo: implement | | // ops needed: modInverse, mod ops | - | sigmaProp(properSignature) + | sigmaProp(zCubed > 0) |}""".stripMargin, null, true From 62fd2a363976797051bba876704b0ea86f54bd75 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 17 Jun 2024 22:07:59 +0300 Subject: [PATCH 34/78] initial stub and roundtrip test with longToByteArray --- .../src/main/scala/sigma/SigmaDsl.scala | 5 ++- .../src/main/scala/sigma/ast/methods.scala | 31 +++++++++++++++++-- .../scala/sigma/data/CSigmaDslBuilder.scala | 15 ++++++++- .../utxo/BasicOpsSpecification.scala | 18 +++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index df2b419273..18211c7187 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -1,7 +1,8 @@ package sigma -import java.math.BigInteger +import sigma.ast.SType +import java.math.BigInteger import sigma.data._ /** @@ -729,5 +730,7 @@ trait SigmaDslBuilder { /** Returns a byte-wise XOR of the two collections of bytes. */ def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte] + + def fromBigEndianBytes[T](tpe: SType, bytes: Coll[Byte])(implicit cT: RType[T]): T } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index e4cf0007e0..4d5ccb4e02 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,10 +2,11 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ +import sigma.Evaluation.stypeToRType import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} -import sigma.ast.SType.TypeCode +import sigma.ast.SType.{TypeCode, paramT, tT} import sigma.ast.syntax.{SValue, ValueOps} import sigma.data.OverloadHack.Overloaded1 import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} @@ -1519,9 +1520,35 @@ case object SGlobalMethods extends MonoTypeMethods { Xor.xorWithCosting(ls, rs) } - protected override def getMethods() = super.getMethods() ++ Seq( + lazy val fromBigEndianBytesMethod = SMethod( + this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 3, Xor.costKind) // todo: id, cossting + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) // todo: desc + + def fromBigEndianBytes_eval(mc: MethodCall, G: SigmaDslBuilder, bytes: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Any = { + val tpe = mc.tpe + val cT = stypeToRType(tpe) + E.addSeqCost(Xor.costKind, bytes.length, Xor.opDesc) { () => // todo: cost + G.fromBigEndianBytes(tpe, bytes)(cT) + } + } + + private val v5Methods = super.getMethods() ++ Seq( groupGeneratorMethod, xorMethod ) + + private val v6Methods = v5Methods ++ Seq( + fromBigEndianBytesMethod + ) + + protected override def getMethods() = { + if (VersionContext.current.isV6SoftForkActivated) { + v6Methods + } else { + v5Methods + } + } } diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 3938feacd3..b91184ccdb 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -5,7 +5,7 @@ import org.ergoplatform.ErgoBox import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} import scorex.utils.Longs -import sigma.ast.{AtLeast, SubstConstants} +import sigma.ast.{AtLeast, SByte, SLong, SType, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps import sigma.serialization.{GroupElementSerializer, SigmaSerializer} @@ -200,6 +200,19 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => val p = GroupElementSerializer.parse(r) this.GroupElement(p) } + + override def fromBigEndianBytes[T](tpe: SType, bytes: Coll[Byte])(implicit cT: RType[T]): T = { + tpe match { + case SByte => if (bytes.length != 1) { + throw new IllegalArgumentException("To deserialize SByte with fromBigEndianBytes, exactly one byte should be provided") + } else { + bytes.apply(0).asInstanceOf[T] + } + case SLong => Longs.fromByteArray(bytes.toArray).asInstanceOf[T] + case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") + // todo: more types + } + } } /** Default singleton instance of Global object, which implements global ErgoTree functions. */ diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..46fa24de96 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import sigma.Extensions.ArrayOps +import sigma.VersionContext import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -157,6 +158,23 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Global.fromBigEndianBytes") { + def fromTest() = test("R1", env, ext, + s"""{ + | val l = -1000L + | val ba = longToByteArray(l) + | Global.fromBigEndianBytes[Long](ba) == l + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 8bcc1efbbfe34cc034720395dc3699235804e9e7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 18 Jun 2024 22:57:07 +0300 Subject: [PATCH 35/78] impl and tests done without costing etc --- .../src/main/scala/sigma/ast/methods.scala | 2 +- .../scala/sigma/data/CSigmaDslBuilder.scala | 32 ++++++-- .../sigma/compiler/ir/GraphBuilding.scala | 6 +- .../sigma/compiler/ir/TreeBuilding.scala | 19 ++++- .../ir/wrappers/sigma/SigmaDslUnit.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 14 ++++ .../utxo/BasicOpsSpecification.scala | 75 ++++++++++++++++++- 7 files changed, 136 insertions(+), 13 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 4d5ccb4e02..c1a59fea2c 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1521,7 +1521,7 @@ case object SGlobalMethods extends MonoTypeMethods { } lazy val fromBigEndianBytesMethod = SMethod( - this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 3, Xor.costKind) // todo: id, cossting + this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 30, Xor.costKind, Seq(tT)) // todo: id, cossting .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) // todo: desc diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index b91184ccdb..2e0b3912fc 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -4,11 +4,11 @@ import debox.cfor import org.ergoplatform.ErgoBox import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} -import scorex.utils.Longs -import sigma.ast.{AtLeast, SByte, SLong, SType, SubstConstants} +import scorex.utils.{Ints, Longs, Shorts} +import sigma.ast.{AtLeast, SBigInt, SByte, SInt, SLong, SShort, SType, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps -import sigma.serialization.{GroupElementSerializer, SigmaSerializer} +import sigma.serialization.{CoreDataSerializer, DataSerializer, GroupElementSerializer, SerializerException, SigmaSerializer} import sigma.util.Extensions.BigIntegerOps import sigma.validation.SigmaValidationSettings import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext} @@ -208,9 +208,31 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => } else { bytes.apply(0).asInstanceOf[T] } - case SLong => Longs.fromByteArray(bytes.toArray).asInstanceOf[T] + case SShort => if (bytes.length != 2) { + throw new IllegalArgumentException("To deserialize SShort with fromBigEndianBytes, exactly two bytes should be provided") + } else { + val b0 = bytes(0) + val b1 = bytes(1) + ((b0 & 0xFF) << 8 | (b1 & 0xFF)).toShort.asInstanceOf[T] + } + case SInt => if (bytes.length != 4) { + throw new IllegalArgumentException("To deserialize SInt with fromBigEndianBytes, exactly four bytes should be provided") + } else { + Ints.fromByteArray(bytes.toArray).asInstanceOf[T] + } + case SLong => if (bytes.length != 8) { + throw new IllegalArgumentException("To deserialize SLong with fromBigEndianBytes, exactly eight bytes should be provided") + } else { + Longs.fromByteArray(bytes.toArray).asInstanceOf[T] + } + case SBigInt => + if (bytes.length > SBigInt.MaxSizeInBytes) { + throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") + } + CBigInt(new BigInteger(bytes.toArray)).asInstanceOf[T] + // todo: UnsignedBitInt case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") - // todo: more types + } } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 7c7b80d39a..23abf72d06 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -928,7 +928,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => sigmaDslBuilder.decodePoint(bytes) // fallback rule for MethodCall, should be the last case in the list - case sigma.ast.MethodCall(obj, method, args, _) => + case sigma.ast.MethodCall(obj, method, args, typeSubst) => val objV = eval(obj) val argsV = args.map(eval) (objV, method.objType) match { @@ -1146,6 +1146,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val c1 = asRep[Coll[Byte]](argsV(0)) val c2 = asRep[Coll[Byte]](argsV(1)) g.xor(c1, c2) + case SGlobalMethods.fromBigEndianBytesMethod.name => + val bytes = asRep[Coll[Byte]](argsV(0)) + val cT = stypeToElem(method.stype.tRange.withSubstTypes(typeSubst)) + g.fromBigEndianBytes(bytes)(cT) case _ => throwError } case _ => throwError diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala index 725e3b1d19..ffe8ab1460 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -1,6 +1,8 @@ package sigma.compiler.ir import org.ergoplatform._ +import sigma.Evaluation.{rtypeToSType, stypeToRType} +import sigma.ast.SType.tT import sigma.ast._ import sigma.ast.syntax.{ValueOps, _} import sigma.data.{ProveDHTuple, ProveDlog} @@ -399,13 +401,24 @@ trait TreeBuilding extends Base { IR: IRContext => mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement) // Fallback MethodCall rule: should be the last in this list of cases - case Def(MethodCall(objSym, m, argSyms, _)) => + case Def(mc @ MethodCall(objSym, m, argSyms, _)) => val obj = recurse[SType](objSym) val args = argSyms.collect { case argSym: Sym => recurse[SType](argSym) } MethodsContainer.getMethod(obj.tpe, m.getName) match { case Some(method) => - val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)) - builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, Map()) + + val typeSubst: STypeSubst = { + if (method.hasExplicitTypeArgs) { + val cT = rtypeToSType(mc.resultType.sourceType) + Map(tT -> cT) + } else { + Map.empty + } + } + + val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)).withConcreteTypes(typeSubst) + + builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, typeSubst) case None => error(s"Cannot find method ${m.getName} in object $obj") } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index 2a6a341686..b5b4287950 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -114,6 +114,7 @@ import scalan._ /** This method will be used in v6.0 to handle CreateAvlTree operation in GraphBuilding */ def avlTree(operationFlags: Ref[Byte], digest: Ref[Coll[Byte]], keyLength: Ref[Int], valueLengthOpt: Ref[WOption[Int]]): Ref[AvlTree]; def xor(l: Ref[Coll[Byte]], r: Ref[Coll[Byte]]): Ref[Coll[Byte]] + def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] }; trait CostModelCompanion; trait BigIntCompanion; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index c113cb7de3..ad0450a1a7 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -1945,6 +1945,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { Array[AnyRef](l, r), true, false, element[Coll[Byte]])) } + + override def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = { + asRep[T](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](bytes, cT), + true, false, cT)) + } } implicit object LiftableSigmaDslBuilder @@ -2104,6 +2111,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { Array[AnyRef](l, r), true, true, element[Coll[Byte]])) } + + def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = { + asRep[T](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](bytes, cT), + true, true, cT)) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 46fa24de96..fc8221f983 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -2,6 +2,8 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ +import scorex.util.encode.Base16 +import scorex.utils.Ints import sigma.Extensions.ArrayOps import sigma.VersionContext import sigma.ast.SCollection.SByteArray @@ -158,10 +160,58 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } - property("Global.fromBigEndianBytes") { - def fromTest() = test("R1", env, ext, + property("Global.fromBigEndianBytes - byte") { + def fromTest() = test("fromBigEndianBytes - byte", env, ext, s"""{ - | val l = -1000L + | val ba = Coll(5.toByte) + | Global.fromBigEndianBytes[Byte](ba) == 5 + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - short") { + def fromTest() = test("fromBigEndianBytes - short", env, ext, + s"""{ + | val ba = Coll(5.toByte, 5.toByte) + | Global.fromBigEndianBytes[Short](ba) != 0 + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - int") { + def fromTest() = test("fromBigEndianBytes - int", env, ext, + s"""{ + | val ba = fromBase16("${Base16.encode(Ints.toByteArray(Int.MaxValue))}") + | Global.fromBigEndianBytes[Int](ba) == ${Int.MaxValue} + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - long") { + def fromTest() = test("fromBigEndianBytes - long", env, ext, + s"""{ + | val l = 1088800L | val ba = longToByteArray(l) | Global.fromBigEndianBytes[Long](ba) == l |} @@ -175,6 +225,25 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Global.fromBigEndianBytes - bigInt") { + val bi = new BigInteger("9785856985394593489356430476450674590674598659865986594859056865984690568904") + def fromTest() = test("fromBigEndianBytes - bigInt", env, ext, + s"""{ + | val ba = fromBase16("${Base16.encode(bi.toByteArray)}") + | Global.fromBigEndianBytes[BigInt](ba) == bigInt("$bi") + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + + // todo: roundtrip with .toBytes + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From b7e2a38132c43054fb73ada852ecba86e45cce32 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 19 Jun 2024 12:39:33 +0300 Subject: [PATCH 36/78] first failing test --- .../src/main/scala/sigma/ast/methods.scala | 21 ++++++++++++++++++- .../sigma/compiler/phases/SigmaTyper.scala | 5 +++++ .../utxo/BasicOpsSpecification.scala | 19 +++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index e4cf0007e0..d114bb8f5f 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1418,16 +1418,35 @@ case object SContextMethods extends MonoTypeMethods { lazy val selfBoxIndexMethod = propertyCall("selfBoxIndex", SInt, 8, FixedCost(JitCost(20))) lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) + lazy val getVarMethod = SMethod( this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind) .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", ArgInfo("varId", "\\lst{Byte} identifier of context variable")) - protected override def getMethods() = super.getMethods() ++ Seq( + lazy val getVarFromInputMethod = SMethod( + this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) + .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", + ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + + private lazy val v5Methods = super.getMethods() ++ Seq( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod ) + private lazy val v6Methods = super.getMethods() ++ Seq( + dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, + selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod, getVarFromInputMethod + ) + + protected override def getMethods(): Seq[SMethod] = { + if(VersionContext.current.isV6SoftForkActivated) { + v6Methods + } else { + v5Methods + } + } + /** Names of methods which provide blockchain context. * This value can be reused where necessary to avoid allocations. */ val BlockchainContextMethodNames: IndexedSeq[String] = Array( diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index ac30a6cd0a..1348dff993 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -221,6 +221,11 @@ class SigmaTyper(val builder: SigmaBuilder, case (Ident(GetVarFunc.name | ExecuteFromVarFunc.name, _), Seq(id: Constant[SNumericType]@unchecked)) if id.tpe.isNumType => Seq(ByteConstant(SByte.downcast(id.value.asInstanceOf[AnyVal])).withSrcCtx(id.sourceContext)) + case (Ident(SContextMethods.getVarFromInputMethod.name, _), + Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) + if inputId.tpe.isNumType && varId.tpe.isNumType => + Seq(ShortConstant(SShort.downcast(inputId.value.asInstanceOf[AnyVal])).withSrcCtx(inputId.sourceContext), + ByteConstant(SByte.downcast(varId.value.asInstanceOf[AnyVal])).withSrcCtx(varId.sourceContext)) case _ => typedArgs } val actualTypes = adaptedTypedArgs.map(_.tpe) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..5ec8336deb 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import sigma.Extensions.ArrayOps +import sigma.VersionContext import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -157,6 +158,24 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("getVarFromInput") { + def getVarTest() = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ CONTEXT.getVarFromInput[Int](0, 1) == 5 }", + null + ) + } + + if(VersionContext.current.isV6SoftForkActivated) { + getVarTest() + } else { + an[Exception] should be thrownBy getVarTest() + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 828ba270b0f413a0c336814aba0fcd0eebd03873 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 27 Jun 2024 18:13:27 +0300 Subject: [PATCH 37/78] passing test from getVar from another input --- .../src/main/scala/sigma/SigmaDsl.scala | 2 ++ .../sigma/reflection/ReflectionData.scala | 3 +++ .../src/main/scala/sigma/ast/methods.scala | 14 +++++++++++-- .../src/main/scala/sigma/ast/values.scala | 2 +- .../sigma/interpreter/ContextExtension.scala | 5 ++++- .../org/ergoplatform/ErgoLikeContext.scala | 2 +- .../main/scala/sigmastate/eval/CContext.scala | 12 +++++++++++ .../special/sigma/ContractsTestkit.scala | 2 +- .../sigma/compiler/ir/GraphBuilding.scala | 8 +++++++- .../scala/sigma/compiler/ir/IRContext.scala | 2 +- .../scala/sigma/compiler/ir/MethodCalls.scala | 7 ++++--- .../sigma/compiler/ir/TreeBuilding.scala | 7 ++++--- .../ir/wrappers/sigma/SigmaDslUnit.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 20 ++++++++++++++++++- .../sigma/compiler/phases/SigmaTyper.scala | 18 +++++++++++++---- .../scala/sigma/SigmaDslSpecification.scala | 1 + .../utxo/BasicOpsSpecification.scala | 13 ++++++++---- 17 files changed, 96 insertions(+), 23 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index df2b419273..ab06306635 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -557,6 +557,8 @@ trait Context { */ def getVar[T](id: Byte)(implicit cT: RType[T]): Option[T] + def getVarFromInput[T](inputId: Short, id: Byte)(implicit cT: RType[T]): Option[T] + def vars: Coll[AnyValue] /** Maximum version of ErgoTree currently activated on the network. diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index 028e68bf72..76072a2fa4 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -273,6 +273,9 @@ object ReflectionData { mkMethod(clazz, "getVar", Array[Class[_]](classOf[Byte], classOf[RType[_]])) { (obj, args) => obj.asInstanceOf[Context].getVar(args(0).asInstanceOf[Byte])(args(1).asInstanceOf[RType[_]]) }, + mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Short], classOf[Byte], classOf[RType[_]])) { (obj, args) => + obj.asInstanceOf[Context].getVarFromInput(args(0).asInstanceOf[Byte], args(1).asInstanceOf[Byte])(args(2).asInstanceOf[RType[_]]) + }, mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[Context].headers } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index d114bb8f5f..9f6c323524 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,6 +2,7 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ +import sigma.Evaluation.stypeToRType import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} @@ -1424,10 +1425,19 @@ case object SContextMethods extends MonoTypeMethods { .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + // todo: costing, desc lazy val getVarFromInputMethod = SMethod( this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) - .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", - ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) + + def getVarFromInput_eval[T](mc: MethodCall, ctx: sigma.Context, inputId: Short, varId: Byte) + (implicit E: ErgoTreeEvaluator): Option[T] = { + // E.addCost(getVarFromInputMethod.costKind) + val rt = stypeToRType(mc.typeSubst.get(tT).get) + val res = ctx.getVarFromInput(inputId, varId)(rt).asInstanceOf[Option[T]] + res + } private lazy val v5Methods = super.getMethods() ++ Seq( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala index 87c661a00a..dace767d9f 100644 --- a/data/shared/src/main/scala/sigma/ast/values.scala +++ b/data/shared/src/main/scala/sigma/ast/values.scala @@ -1312,7 +1312,7 @@ case class MethodCall( val objV = obj.evalTo[Any](env) addCost(MethodCall.costKind) // MethodCall overhead method.costKind match { - case fixed: FixedCost => + case fixed: FixedCost if method.explicitTypeArgs.isEmpty => val extra = method.extraDescriptors val extraLen = extra.length val len = args.length diff --git a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala index e8cdb7d709..fd269c177c 100644 --- a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala +++ b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala @@ -16,8 +16,11 @@ import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} * @param values internal container of the key-value pairs */ case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ <: SType]]) { - def add(bindings: VarBinding*): ContextExtension = + def add(bindings: VarBinding*): ContextExtension = { ContextExtension(values ++ bindings) + } + + def get(varId: Byte): Option[EvaluatedValue[_ <: SType]] = values.get(varId) } object ContextExtension { diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala index e421e8fdfe..4b1366d9fd 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala @@ -168,7 +168,7 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData, syntax.error(s"Undefined context property: currentErgoTreeVersion")) CContext( dataInputs, headers, preHeader, inputs, outputs, preHeader.height, selfBox, selfIndex, avlTree, - preHeader.minerPk.getEncoded, vars, activatedScriptVersion, ergoTreeVersion) + preHeader.minerPk.getEncoded, vars, spendingTransaction, activatedScriptVersion, ergoTreeVersion) } diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala index 2b076403ad..1c464fed37 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala @@ -1,8 +1,11 @@ package sigmastate.eval import debox.cfor +import org.ergoplatform.{ErgoLikeTransactionTemplate, UnsignedInput} +import sigma.Evaluation.{stypeToRType, toDslTuple} import sigma.Extensions.ArrayOps import sigma._ +import sigma.ast.SType import sigma.data._ import sigma.exceptions.InvalidType @@ -24,6 +27,7 @@ case class CContext( lastBlockUtxoRootHash: AvlTree, _minerPubKey: Coll[Byte], vars: Coll[AnyValue], + spendingTransaction: ErgoLikeTransactionTemplate[_ <: UnsignedInput], override val activatedScriptVersion: Byte, override val currentErgoTreeVersion: Byte ) extends Context { @@ -69,6 +73,14 @@ case class CContext( } else None } + override def getVarFromInput[T](inputId: Short, id: Byte)(implicit tT: RType[T]): Option[T] = { + spendingTransaction.inputs.unapply(inputId).flatMap(_.extension.get(id)) match { + case Some(v) if stypeToRType[SType](v.tpe) == tT => Some(v.value.asInstanceOf[T]) + case _ => + None + } + } + /** Return a new context instance with variables collection updated. * @param bindings a new binding of the context variables with new values. * @return a new instance (if `bindings` non-empty) with the specified bindings. diff --git a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala index b04e9c150f..ba04df1347 100644 --- a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala +++ b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala @@ -47,7 +47,7 @@ trait ContractsTestkit { new CContext( noInputs.toColl, noHeaders, dummyPreHeader, inputs.toColl, outputs.toColl, height, self, inputs.indexOf(self), tree, - minerPk.toColl, vars.toColl, activatedScriptVersion, currErgoTreeVersion) + minerPk.toColl, vars.toColl, null, activatedScriptVersion, currErgoTreeVersion) def newContext( height: Int, diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 7c7b80d39a..3ed0e414b8 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.ast.SType.tT import sigma.ast.TypeCodes.LastConstantCode import sigma.ast.Value.Typed import sigma.ast.syntax.{SValue, ValueOps} @@ -928,7 +929,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => sigmaDslBuilder.decodePoint(bytes) // fallback rule for MethodCall, should be the last case in the list - case sigma.ast.MethodCall(obj, method, args, _) => + case sigma.ast.MethodCall(obj, method, args, typeSubst) => val objV = eval(obj) val argsV = args.map(eval) (objV, method.objType) match { @@ -1040,6 +1041,11 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => ctx.LastBlockUtxoRootHash case SContextMethods.minerPubKeyMethod.name => ctx.minerPubKey + case SContextMethods.getVarFromInputMethod.name => + val c1 = asRep[Short](argsV(0)) + val c2 = asRep[Byte](argsV(1)) + val c3 = stypeToElem(typeSubst.apply(tT)) + ctx.getVarFromInput(c1, c2)(c3) case _ => throwError } case (tree: Ref[AvlTree]@unchecked, SAvlTreeMethods) => method.name match { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala b/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala index c60bc0882f..a22962f987 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala @@ -153,7 +153,7 @@ trait IRContext override def invokeUnlifted(e: Elem[_], mc: MethodCall, dataEnv: DataEnv): Any = e match { case _: CollElem[_,_] => mc match { case CollMethods.map(_, f) => - val newMC = mc.copy(args = mc.args :+ f.elem.eRange)(mc.resultType, mc.isAdapterCall) + val newMC = mc.copy(args = mc.args :+ f.elem.eRange)(mc.resultType, mc.isAdapterCall, mc.typeSubst) super.invokeUnlifted(e, newMC, dataEnv) case _ => super.invokeUnlifted(e, mc, dataEnv) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala index 876f0e9d7e..48248f2165 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala @@ -1,6 +1,7 @@ package sigma.compiler.ir import debox.{cfor, Buffer => DBuffer} +import sigma.ast.{SType, STypeVar} import sigma.compiler.DelayInvokeException import sigma.reflection.RMethod import sigma.util.CollectionUtil.TraversableOps @@ -26,7 +27,7 @@ trait MethodCalls extends Base { self: IRContext => * given `method`. */ case class MethodCall private[MethodCalls](receiver: Sym, method: RMethod, args: Seq[AnyRef], neverInvoke: Boolean) - (val resultType: Elem[Any], val isAdapterCall: Boolean = false) extends Def[Any] { + (val resultType: Elem[Any], val isAdapterCall: Boolean = false, val typeSubst: Map[STypeVar, SType] = Map()) extends Def[Any] { override def mirror(t: Transformer): Ref[Any] = { val len = args.length @@ -100,8 +101,8 @@ trait MethodCalls extends Base { self: IRContext => /** Creates new MethodCall node and returns its node ref. */ def mkMethodCall(receiver: Sym, method: RMethod, args: Seq[AnyRef], - neverInvoke: Boolean, isAdapterCall: Boolean, resultElem: Elem[_]): Sym = { - reifyObject(MethodCall(receiver, method, args, neverInvoke)(asElem[Any](resultElem), isAdapterCall)) + neverInvoke: Boolean, isAdapterCall: Boolean, resultElem: Elem[_], typeSubst: Map[STypeVar, SType] = Map.empty): Sym = { + reifyObject(MethodCall(receiver, method, args, neverInvoke)(asElem[Any](resultElem), isAdapterCall, typeSubst)) } @tailrec diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala index 725e3b1d19..6826321ccd 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -399,13 +399,14 @@ trait TreeBuilding extends Base { IR: IRContext => mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement) // Fallback MethodCall rule: should be the last in this list of cases - case Def(MethodCall(objSym, m, argSyms, _)) => + case Def(mc @ MethodCall(objSym, m, argSyms, _)) => val obj = recurse[SType](objSym) val args = argSyms.collect { case argSym: Sym => recurse[SType](argSym) } MethodsContainer.getMethod(obj.tpe, m.getName) match { case Some(method) => - val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)) - builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, Map()) + val typeSubst = mc.typeSubst + val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)).withConcreteTypes(typeSubst) + builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, typeSubst) case None => error(s"Cannot find method ${m.getName} in object $obj") } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index 2a6a341686..91013f9071 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -91,6 +91,7 @@ import scalan._ def preHeader: Ref[PreHeader]; def minerPubKey: Ref[Coll[Byte]]; def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]]; + def getVarFromInput[T](inputId: Ref[Short], id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]]; }; trait SigmaDslBuilder extends Def[SigmaDslBuilder] { def Colls: Ref[CollBuilder]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index c113cb7de3..d5bd87d306 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -8,6 +8,8 @@ import sigma.compiler.ir.wrappers.sigma.impl.SigmaDslDefs import scala.collection.compat.immutable.ArraySeq package impl { + import sigma.Evaluation + import sigma.ast.SType.tT import sigma.compiler.ir.meta.ModuleInfo import sigma.compiler.ir.wrappers.sigma.SigmaDsl import sigma.compiler.ir.{Base, GraphIRReflection, IRContext} @@ -1614,6 +1616,14 @@ object Context extends EntityObject("Context") { true, false, element[WOption[T]])) } + override def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) + asRep[WOption[T]](mkMethodCall(self, + ContextClass.getMethod("getVarFromInput", classOf[Sym], classOf[Sym], classOf[Elem[_]]), + Array[AnyRef](inputId, varId, cT), + true, false, element[WOption[T]], Map(tT -> st))) + } + } implicit object LiftableContext @@ -1710,6 +1720,14 @@ object Context extends EntityObject("Context") { Array[AnyRef](id, cT), true, true, element[WOption[T]])) } + + def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) + asRep[WOption[T]](mkMethodCall(source, + ContextClass.getMethod("getVarFromInput", classOf[Sym], classOf[Sym], classOf[Elem[_]]), + Array[AnyRef](inputId, varId, cT), + true, true, element[WOption[T]], Map(tT -> st))) + } } // entityUnref: single unref method for each type family @@ -1727,7 +1745,7 @@ object Context extends EntityObject("Context") { override protected def collectMethods: Map[RMethod, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[Context]), RClass(classOf[SContext]), Set( - "OUTPUTS", "INPUTS", "dataInputs", "HEIGHT", "SELF", "selfBoxIndex", "LastBlockUtxoRootHash", "headers", "preHeader", "minerPubKey", "getVar", "vars" + "OUTPUTS", "INPUTS", "dataInputs", "HEIGHT", "SELF", "selfBoxIndex", "LastBlockUtxoRootHash", "headers", "preHeader", "minerPubKey", "getVar", "getVarFromInput", "vars" )) } } diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index 1348dff993..0d53802239 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -134,8 +134,18 @@ class SigmaTyper(val builder: SigmaBuilder, res case Apply(ApplyTypes(sel @ Select(obj, n, _), Seq(rangeTpe)), args) => + val nArgs = if (n == SContextMethods.getVarFromInputMethod.name && + args.length == 2 && + args(0).isInstanceOf[Constant[_]] && + args(1).isInstanceOf[Constant[_]] && + args(0).tpe.isNumType && + args(1).tpe.isNumType) { + IndexedSeq(ShortConstant(SShort.downcast(args(0).asInstanceOf[Constant[SNumericType]].value.asInstanceOf[AnyVal])).withSrcCtx(args(0).sourceContext), + ByteConstant(SByte.downcast(args(1).asInstanceOf[Constant[SNumericType]].value.asInstanceOf[AnyVal])).withSrcCtx(args(1).sourceContext)) + } else args + val newObj = assignType(env, obj) - val newArgs = args.map(assignType(env, _)) + val newArgs = nArgs.map(assignType(env, _)) obj.tpe match { case p: SProduct => MethodsContainer.getMethod(p, n) match { @@ -222,10 +232,10 @@ class SigmaTyper(val builder: SigmaBuilder, if id.tpe.isNumType => Seq(ByteConstant(SByte.downcast(id.value.asInstanceOf[AnyVal])).withSrcCtx(id.sourceContext)) case (Ident(SContextMethods.getVarFromInputMethod.name, _), - Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) - if inputId.tpe.isNumType && varId.tpe.isNumType => + Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) + if inputId.tpe.isNumType && varId.tpe.isNumType => Seq(ShortConstant(SShort.downcast(inputId.value.asInstanceOf[AnyVal])).withSrcCtx(inputId.sourceContext), - ByteConstant(SByte.downcast(varId.value.asInstanceOf[AnyVal])).withSrcCtx(varId.sourceContext)) + ByteConstant(SByte.downcast(varId.value.asInstanceOf[AnyVal])).withSrcCtx(varId.sourceContext)) case _ => typedArgs } val actualTypes = adaptedTypedArgs.map(_.tpe) diff --git a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala b/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala index c820e65e73..b7ce178d6c 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala @@ -4762,6 +4762,7 @@ class SigmaDslSpecification extends SigmaDslTesting .append(Coll[AnyValue]( CAnyValue(Helpers.decodeBytes("00")), CAnyValue(true))), + spendingTransaction = null, activatedScriptVersion = activatedVersionInTests, currentErgoTreeVersion = ergoTreeVersionInTests ) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 5ec8336deb..83d405a1a1 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -2,6 +2,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ +import org.scalatest.Assertion import sigma.Extensions.ArrayOps import sigma.VersionContext import sigma.ast.SCollection.SByteArray @@ -20,9 +21,11 @@ import sigmastate.interpreter.Interpreter._ import sigma.ast.Apply import sigma.eval.EvalSettings import sigma.exceptions.InvalidType +import sigma.interpreter.{ContextExtension, ProverResult} import sigmastate.utils.Helpers._ import java.math.BigInteger +import scala.collection.compat.immutable.ArraySeq class BasicOpsSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -102,7 +105,8 @@ class BasicOpsSpecification extends CompilerTestingCommons val newBox1 = testBox(10, tree, creationHeight = 0, boxIndex = 0, additionalRegisters = Map( reg1 -> IntConstant(1), reg2 -> IntConstant(10))) - val tx = createTransaction(newBox1) + val ce = ContextExtension(prover.contextExtenders) + val tx = new ErgoLikeTransaction(IndexedSeq(Input(boxToSpend.id, ProverResult(Array.empty, ce))), ArraySeq.empty, IndexedSeq(newBox1)) val ctx = ErgoLikeContextTesting(currentHeight = 0, lastBlockUtxoRoot = AvlTreeData.dummy, ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(boxToSpend), @@ -159,17 +163,17 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("getVarFromInput") { - def getVarTest() = { + def getVarTest(): Assertion = { val customExt = Map( 1.toByte -> IntConstant(5) ).toSeq test("R1", env, customExt, - "{ CONTEXT.getVarFromInput[Int](0, 1) == 5 }", + "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 1).get == 5) }", null ) } - if(VersionContext.current.isV6SoftForkActivated) { + if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { an[Exception] should be thrownBy getVarTest() @@ -740,4 +744,5 @@ class BasicOpsSpecification extends CompilerTestingCommons true ) } + } From b80ef294039826cd43a5ccef7e9ae946f6f448dc Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 28 Jun 2024 14:07:08 +0300 Subject: [PATCH 38/78] invalid tests --- .../utxo/BasicOpsSpecification.scala | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 83d405a1a1..74a51e63d0 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -180,6 +180,42 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("getVarFromInput - invalid input") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ sigmaProp(CONTEXT.getVarFromInput[Int](1, 1).get == 5) }", + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + an[Exception] should be thrownBy getVarTest() + } else { + an[Exception] should be thrownBy getVarTest() + } + } + + property("getVarFromInput - invalid var") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 2).get == 5) }", + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + an[Exception] should be thrownBy getVarTest() + } else { + an[Exception] should be thrownBy getVarTest() + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 6a3cbac7540baf0d15dcfbbb11f0c42fb9432eeb Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 29 Jun 2024 20:00:52 +0300 Subject: [PATCH 39/78] initial failing test & impl --- .../src/main/scala/sigma/ast/methods.scala | 20 +++++++++++++------ .../sigmastate/ErgoTreeSpecification.scala | 2 +- .../utxo/BasicOpsSpecification.scala | 16 +++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 9f6c323524..aecf63e588 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1420,11 +1420,16 @@ case object SContextMethods extends MonoTypeMethods { lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) - lazy val getVarMethod = SMethod( + lazy val getVarV5Method = SMethod( this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind) .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + lazy val getVarV6Method = SMethod( + this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind, Seq(tT)) + .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", + ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + // todo: costing, desc lazy val getVarFromInputMethod = SMethod( this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) @@ -1439,18 +1444,21 @@ case object SContextMethods extends MonoTypeMethods { res } - private lazy val v5Methods = super.getMethods() ++ Seq( + private lazy val commonMethods = Array( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, - selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod + selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod + ) + + private lazy val v5Methods = super.getMethods() ++ Seq( + getVarV5Method ) private lazy val v6Methods = super.getMethods() ++ Seq( - dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, - selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod, getVarFromInputMethod + getVarV6Method, getVarFromInputMethod ) protected override def getMethods(): Seq[SMethod] = { - if(VersionContext.current.isV6SoftForkActivated) { + if (VersionContext.current.isV6SoftForkActivated) { v6Methods } else { v5Methods diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 7539bd5e48..c39a5269bc 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -413,7 +413,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(1, dataInputsMethod), MInfo(2, headersMethod), MInfo(3, preHeaderMethod), MInfo(4, inputsMethod), MInfo(5, outputsMethod), MInfo(6, heightMethod), MInfo(7, selfMethod), MInfo(8, selfBoxIndexMethod), MInfo(9, lastBlockUtxoRootHashMethod), - MInfo(10, minerPubKeyMethod), MInfo(11, getVarMethod) + MInfo(10, minerPubKeyMethod), MInfo(11, getVarV5Method) ), true) }, { import SGlobalMethods._ diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 74a51e63d0..5a89ebe360 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -454,6 +454,22 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Context.getVar") { + def varTest() = { + test("GetVar1", env, ext, + "{ CONTEXT.getVar[Int](intVar2).get == 2 }", + null + ) + } + + if(VersionContext.current.isV6SoftForkActivated) { + varTest() + } else { + an[Exception] should be thrownBy(varTest()) + } + + } + property("GetVar") { test("GetVar1", env, ext, "{ getVar[Int](intVar2).get == 2 }", From af92a3fba5c4c0079f770809fdf1c565cfa03d70 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 29 Jun 2024 22:55:31 +0300 Subject: [PATCH 40/78] test passing --- data/shared/src/main/scala/sigma/ast/methods.scala | 12 ++++++------ .../main/scala/sigma/compiler/ir/GraphBuilding.scala | 4 ++++ .../sigmastate/utxo/BasicOpsSpecification.scala | 1 - 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index aecf63e588..8d8e4e9056 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1427,8 +1427,8 @@ case object SContextMethods extends MonoTypeMethods { lazy val getVarV6Method = SMethod( this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind, Seq(tT)) - .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", - ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") // todo: desc // todo: costing, desc lazy val getVarFromInputMethod = SMethod( @@ -1449,19 +1449,19 @@ case object SContextMethods extends MonoTypeMethods { selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod ) - private lazy val v5Methods = super.getMethods() ++ Seq( + private lazy val v5Methods = commonMethods ++ Seq( getVarV5Method ) - private lazy val v6Methods = super.getMethods() ++ Seq( + private lazy val v6Methods = commonMethods ++ Seq( getVarV6Method, getVarFromInputMethod ) protected override def getMethods(): Seq[SMethod] = { if (VersionContext.current.isV6SoftForkActivated) { - v6Methods + super.getMethods() ++ v6Methods } else { - v5Methods + super.getMethods() ++ v5Methods } } 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 3ed0e414b8..82cfcb4469 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1041,6 +1041,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => ctx.LastBlockUtxoRootHash case SContextMethods.minerPubKeyMethod.name => ctx.minerPubKey + case SContextMethods.getVarV6Method.name => + val c2 = asRep[Byte](argsV(0)) + val c3 = stypeToElem(typeSubst.apply(tT)) + ctx.getVar(c2)(c3) case SContextMethods.getVarFromInputMethod.name => val c1 = asRep[Short](argsV(0)) val c2 = asRep[Byte](argsV(1)) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 5a89ebe360..5b6f71ecfc 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -467,7 +467,6 @@ class BasicOpsSpecification extends CompilerTestingCommons } else { an[Exception] should be thrownBy(varTest()) } - } property("GetVar") { From 4c63a60e981743fd10c29a4b87b8e996ed5b749d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 30 Jul 2024 19:30:54 +0300 Subject: [PATCH 41/78] LSV5 & LSV6 tests --- .../scala/sigma/LanguageSpecificationV5.scala | 146 +++++++++--------- .../scala/sigma/LanguageSpecificationV6.scala | 60 ++++++- 2 files changed, 134 insertions(+), 72 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index 700b48fd13..347a7b90f4 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -7957,77 +7957,81 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) ) - verifyCases( - // (coll, (index, default)) - { - def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773) - Seq( - ((Coll[Int](), (0, default)), success(default)), - ((Coll[Int](), (-1, default)), success(default)), - ((Coll[Int](1), (0, default)), success(1)), - ((Coll[Int](1), (1, default)), success(default)), - ((Coll[Int](1), (-1, default)), success(default)), - ((Coll[Int](1, 2), (0, default)), success(1)), - ((Coll[Int](1, 2), (1, default)), success(2)), - ((Coll[Int](1, 2), (2, default)), success(default)), - ((Coll[Int](1, 2), (-1, default)), success(default)) - ) - }, - existingFeature((x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2), - "{ (x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2) }", - if (lowerMethodCallsInTests) - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[Value[STuple]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 2.toByte + + if(!VersionContext.current.isV6SoftForkActivated) { + verifyCases( + // (coll, (index, default)) + { + def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773) + + Seq( + ((Coll[Int](), (0, default)), success(default)), + ((Coll[Int](), (-1, default)), success(default)), + ((Coll[Int](1), (0, default)), success(1)), + ((Coll[Int](1), (1, default)), success(default)), + ((Coll[Int](1), (-1, default)), success(default)), + ((Coll[Int](1, 2), (0, default)), success(1)), + ((Coll[Int](1, 2), (1, default)), success(2)), + ((Coll[Int](1, 2), (2, default)), success(default)), + ((Coll[Int](1, 2), (-1, default)), success(default)) + ) + }, + existingFeature((x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2), + "{ (x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2) }", + if (lowerMethodCallsInTests) + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), + BlockValue( + Vector( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 2.toByte + ) ) - ) - ), - ByIndex( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 1.toByte ), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), - Some(SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte)) + ByIndex( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 1.toByte + ), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), + Some(SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte)) + ) ) ) - ) - else - FuncValue( - Array((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), - BlockValue( - Array( - ValDef( - 3, - List(), - SelectField.typed[Value[STuple]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 2.toByte + else + FuncValue( + Array((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), + BlockValue( + Array( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 2.toByte + ) ) - ) - ), - MethodCall.typed[Value[SInt.type]]( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 1.toByte ), - SCollectionMethods.getMethodByName("getOrElse").withConcreteTypes(Map(STypeVar("IV") -> SInt)), - Vector( - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) - ), - Map() + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 1.toByte + ), + SCollectionMethods.getMethodByName("getOrElse").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + Vector( + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) + ), + Map() + ) ) ) - ) - )) + )) + } } property("Tuple size method equivalence") { @@ -8591,13 +8595,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => "{ (x: Option[Long]) => x.isDefined }", FuncValue(Vector((1, SOption(SLong))), OptionIsDefined(ValUse(1, SOption(SLong)))))) - verifyCases( - Seq( - (None -> Expected(Success(1L), 1766, costDetails3, 1766)), - (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766))), - existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, - "{ (x: Option[Long]) => x.getOrElse(1L) }", - FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) + if (!VersionContext.current.isV6SoftForkActivated) { + verifyCases( + Seq( + (None -> Expected(Success(1L), 1766, costDetails3, 1766)), + (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766))), + existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, + "{ (x: Option[Long]) => x.getOrElse(1L) }", + FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) + } verifyCases( Seq( diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 3b3de407b5..612f0e33c3 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,8 +1,9 @@ package sigma -import sigma.ast.{Apply, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, JitCost, OptionGet, SBigInt, SByte, SInt, SLong, SShort, ValUse} +import sigma.ast.{Apply, ArithOp, BlockValue, ByIndex, CompanionDesc, Constant, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, IntConstant, JitCost, LongConstant, MethodCall, OptionGet, OptionGetOrElse, PerItemCost, SBigInt, SByte, SCollection, SCollectionMethods, SCollectionType, SInt, SLong, SOption, SPair, SShort, STuple, STypeVar, SelectField, ValDef, ValUse, Value} import sigma.data.{CBigInt, ExactNumeric} -import sigma.eval.SigmaDsl +import sigma.eval.{SigmaDsl, TracedCost} +import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound @@ -168,7 +169,62 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) } } + } + + property("Option.getOrElse with lazy default") { + def getOrElse = newFeature( + { (x: Option[Long]) => x.getOrElse(1 / 0L) }, + "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", + FuncValue( + Array((1, SOption(SLong))), + OptionGetOrElse( + ValUse(1, SOption(SLong)), + ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) + ) + ) + ) + if (VersionContext.current.isV6SoftForkActivated) { + forAll { x: Option[Long] => + Seq(getOrElse).map(_.checkEquality(x)) + } + } else { + forAll { x: Option[Long] => + if (x.isEmpty) { + Seq(getOrElse).map(_.checkEquality(x)) + } + } + } + } + + property("Coll getOrElse with lazy default") { + def getOrElse = newFeature( + (x: (Coll[Int], Int)) => x._1.toArray.unapply(x._2).getOrElse(1 / 0), + "{ (x: (Coll[Int], Int)) => x._1.getOrElse(x._2, 1 / 0) }", + FuncValue( + Array((1, SPair(SCollectionType(SInt), SInt))), + ByIndex( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SInt)), + 1.toByte + ), + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SCollectionType(SInt), SInt)), 2.toByte), + Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) + ) + ) + ) + + if (VersionContext.current.isV6SoftForkActivated) { + forAll { x: (Coll[Int], Int) => + Seq(getOrElse).map(_.checkEquality(x)) + } + } else { + forAll { x: (Coll[Int], Int) => + if (x._1.isEmpty) { + Seq(getOrElse).map(_.checkEquality(x)) + } + } + } } property("BigInt methods equivalence (new features)") { From 61f90cd339b9a79ce20154f45f3db2980be9dc4c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 31 Jul 2024 14:04:30 +0300 Subject: [PATCH 42/78] assertEXceptionThrown --- sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala | 2 +- .../test/scala/sigmastate/utxo/BasicOpsSpecification.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 612f0e33c3..b4fb5b12fb 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -199,7 +199,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Coll getOrElse with lazy default") { def getOrElse = newFeature( - (x: (Coll[Int], Int)) => x._1.toArray.unapply(x._2).getOrElse(1 / 0), + (x: (Coll[Int], Int)) => x._1.toArray.toIndexedSeq.unapply(x._2).getOrElse(1 / 0), "{ (x: (Coll[Int], Int)) => x._1.getOrElse(x._2, 1 / 0) }", FuncValue( Array((1, SPair(SCollectionType(SInt), SInt))), diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index c1489355e6..3d25326660 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -170,10 +170,10 @@ class BasicOpsSpecification extends CompilerTestingCommons null ) - if(VersionContext.current.isV6SoftForkActivated) { + if (VersionContext.current.isV6SoftForkActivated) { optTest() } else { - an[Exception] shouldBe thrownBy(optTest()) + assertExceptionThrown(optTest(), _.isInstanceOf[NoSuchElementException]) } } From 26809c94f0ed8aca3d56fabb10995fb30e2889e0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 2 Aug 2024 13:58:07 +0300 Subject: [PATCH 43/78] passing but improper changedFeature test --- .gitignore | 1 + .../scala/sigma/LanguageSpecificationV6.scala | 48 +++++++++---------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index df18ce8ab1..d328645cde 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.fdb_latexmk *.gz +yarn.lock *.log docs/spec/out/ test-out/ diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index b4fb5b12fb..9e9827df1f 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,14 +1,14 @@ package sigma -import sigma.ast.{Apply, ArithOp, BlockValue, ByIndex, CompanionDesc, Constant, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, IntConstant, JitCost, LongConstant, MethodCall, OptionGet, OptionGetOrElse, PerItemCost, SBigInt, SByte, SCollection, SCollectionMethods, SCollectionType, SInt, SLong, SOption, SPair, SShort, STuple, STypeVar, SelectField, ValDef, ValUse, Value} +import sigma.ast.{Apply, ByIndex, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, IntConstant, JitCost, LongConstant, MethodCall, OptionGet, OptionGetOrElse, PerItemCost, SBigInt, SByte, SCollection, SCollectionMethods, SCollectionType, SInt, SLong, SOption, SPair, SShort, STuple, STypeVar, SelectField, ValDef, ValUse, Value} import sigma.data.{CBigInt, ExactNumeric} -import sigma.eval.{SigmaDsl, TracedCost} +import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound 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. @@ -172,29 +172,29 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => } property("Option.getOrElse with lazy default") { - def getOrElse = newFeature( - { (x: Option[Long]) => x.getOrElse(1 / 0L) }, - "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", - FuncValue( - Array((1, SOption(SLong))), - OptionGetOrElse( - ValUse(1, SOption(SLong)), - ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) - ) + verifyCases( + Seq( + Some(0L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, CostDetails.ZeroCost, 1793, + newVersionedResults = { + Seq(0 -> (ExpectedResult(Success(6L), Some(1793)) -> None)) + } ), + None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6) + ), + changedFeature( + { (x: Option[Long]) => val default = 1 / 0L; x.getOrElse(default) }, + { (x: Option[Long]) => if (VersionContext.current.isV6SoftForkActivated) {x.getOrElse(1 / 0L)} else {val default = 1 / 0L; x.getOrElse(default)} }, + "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", + FuncValue( + Array((1, SOption(SLong))), + OptionGetOrElse( + ValUse(1, SOption(SLong)), + ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) + ) + ), + allowNewToSucceed = true, + changedIn = VersionContext.V6SoftForkVersion ) ) - - if (VersionContext.current.isV6SoftForkActivated) { - forAll { x: Option[Long] => - Seq(getOrElse).map(_.checkEquality(x)) - } - } else { - forAll { x: Option[Long] => - if (x.isEmpty) { - Seq(getOrElse).map(_.checkEquality(x)) - } - } - } } property("Coll getOrElse with lazy default") { From 8da35750e295b0fa95e440c21316d147c45b1f1d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 9 Aug 2024 17:33:12 +0300 Subject: [PATCH 44/78] fixed tests in LSV6 --- .../scala/sigma/LanguageSpecificationV6.scala | 79 ++++++++++++------- .../test/scala/sigma/SigmaDslTesting.scala | 13 ++- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 4e6641984b..d63d6aae65 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -467,7 +467,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Option.getOrElse with lazy default") { - val someTrace = TracedCost( + val trace = TracedCost( Array( FixedCostItem(Apply), FixedCostItem(FuncValue), @@ -481,11 +481,11 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => verifyCases( Seq( - Some(2L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, someTrace, 1793, + Some(2L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793, newVersionedResults = { - expectedSuccessForAllTreeVersions(2L, 2015, someTrace) + expectedSuccessForAllTreeVersions(2L, 2015, trace) } ), - // None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6) + None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793) ), changedFeature( changedInVersion = VersionContext.V6SoftForkVersion, @@ -503,35 +503,56 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) ) } -/* + property("Coll getOrElse with lazy default") { - def getOrElse = newFeature( - (x: (Coll[Int], Int)) => x._1.toArray.toIndexedSeq.unapply(x._2).getOrElse(1 / 0), - "{ (x: (Coll[Int], Int)) => x._1.getOrElse(x._2, 1 / 0) }", - FuncValue( - Array((1, SPair(SCollectionType(SInt), SInt))), - ByIndex( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SInt)), - 1.toByte - ), - SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SCollectionType(SInt), SInt)), 2.toByte), - Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) - ) + + val trace = TracedCost( + Array( + FixedCostItem(Apply), + FixedCostItem(FuncValue), + FixedCostItem(GetVar), + FixedCostItem(OptionGet), + FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), + FixedCostItem(ValUse), + FixedCostItem(Constant), + FixedCostItem(ByIndex) ) ) - if (VersionContext.current.isV6SoftForkActivated) { - forAll { x: (Coll[Int], Int) => - Seq(getOrElse).map(_.checkEquality(x)) - } - } else { - forAll { x: (Coll[Int], Int) => - if (x._1.isEmpty) { - Seq(getOrElse).map(_.checkEquality(x)) - } - } + def scalaFuncNew(x: Coll[Int]) = { + if (VersionContext.current.isV6SoftForkActivated) { + x.toArray.toIndexedSeq.headOption.getOrElse(1 / 0) + } else scalaFuncOld(x) + } + + def scalaFuncOld(x: Coll[Int]) = { + x.getOrElse(0, 1 / 0) } + + verifyCases( + Seq( + Coll(1) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793, + newVersionedResults = { + expectedSuccessForAllTreeVersions(1, 2029, trace) + } ), + Coll[Int]() -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793) + ), + changedFeature( + changedInVersion = VersionContext.V6SoftForkVersion, + scalaFuncOld, + scalaFuncNew, + "{ (x: Coll[Int]) => x.getOrElse(0, 1 / 0) }", + FuncValue( + Array((1, SCollectionType(SInt))), + ByIndex( + ValUse(1, SCollectionType(SInt)), + IntConstant(0), + Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) + ) + ), + allowNewToSucceed = true + ) + ) } - */ + } diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 58873449b4..cf2fd98ebf 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -261,6 +261,7 @@ class SigmaDslTesting extends AnyPropSpec s"""Should succeed with the same value or fail with the same exception, but was: |First result: $b1 |Second result: $b2 + |Input: $x |Root cause: $cause |""".stripMargin) } @@ -715,11 +716,17 @@ class SigmaDslTesting extends AnyPropSpec override def checkEquality(input: A, logInputOutput: Boolean = false): Try[(B, CostDetails)] = { // check the old implementation against Scala semantic function var oldRes: Try[(B, CostDetails)] = null - if (ergoTreeVersionInTests < VersionContext.JitActivationVersion) oldRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { try checkEq(scalaFunc)(oldF)(input) catch { - case e: TestFailedException => throw e + case e: TestFailedException => + if(activatedVersionInTests < changedInVersion) { + throw e + } else { + // old ergoscript may succeed in new version while old scalafunc may fail, + // see e.g. "Option.getOrElse with lazy default" test + Failure(e) + } case t: Throwable => Failure(t) } @@ -764,7 +771,7 @@ class SigmaDslTesting extends AnyPropSpec override def checkExpected(input: A, expected: Expected[B]): Unit = { // check the new implementation with Scala semantic function val newRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { - checkEq(scalaFuncNew)(newF)(input) + checkEq(scalaFuncNew)(newF)(input) } if (VersionContext.current.activatedVersion < changedInVersion) { From 12e888d1b776d276e4013ea21c4187f1f32f4417 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 12 Aug 2024 14:38:54 +0300 Subject: [PATCH 45/78] scala 2.11 compilation fix --- .../shared/src/main/scala/sigmastate/eval/CContext.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala index 1c464fed37..bed0c4b013 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala @@ -2,7 +2,7 @@ package sigmastate.eval import debox.cfor import org.ergoplatform.{ErgoLikeTransactionTemplate, UnsignedInput} -import sigma.Evaluation.{stypeToRType, toDslTuple} +import sigma.Evaluation.stypeToRType import sigma.Extensions.ArrayOps import sigma._ import sigma.ast.SType @@ -74,7 +74,7 @@ case class CContext( } override def getVarFromInput[T](inputId: Short, id: Byte)(implicit tT: RType[T]): Option[T] = { - spendingTransaction.inputs.unapply(inputId).flatMap(_.extension.get(id)) match { + spendingTransaction.inputs.lift(inputId).flatMap(_.extension.get(id)) match { case Some(v) if stypeToRType[SType](v.tpe) == tT => Some(v.value.asInstanceOf[T]) case _ => None From b0fbfbbd152bf3c7565135d9e31fd21874c97be7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 13 Aug 2024 16:54:09 +0300 Subject: [PATCH 46/78] fixing getVar typing during tree building, simpler execution for getVarFromInput --- .../src/main/scala/sigma/SigmaDsl.scala | 9 +++ .../sigma/reflection/ReflectionData.scala | 2 +- .../src/main/scala/sigma/ast/SMethod.scala | 5 ++ .../src/main/scala/sigma/ast/methods.scala | 25 +++---- .../src/main/scala/sigma/ast/values.scala | 2 +- .../sigma/compiler/ir/GraphIRReflection.scala | 3 + .../scala/sigma/compiler/ir/MethodCalls.scala | 2 +- .../sigma/compiler/ir/TreeBuilding.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 6 +- .../sigma/compiler/phases/SigmaTyper.scala | 7 +- .../scala/sigma/SigmaDslStaginTests.scala | 2 - .../sigmastate/lang/SigmaTyperTest.scala | 4 ++ .../utxo/BasicOpsSpecification.scala | 68 +++++++++---------- .../utxo/examples/AssetsPartialFilling.scala | 2 - 14 files changed, 75 insertions(+), 63 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index ab06306635..8aa154b34e 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -557,6 +557,15 @@ trait Context { */ def getVar[T](id: Byte)(implicit cT: RType[T]): Option[T] + /** + * A variant of `getVar` to extract a context variable by id and type from any input + * + * @param inputId - input index + * @param id - context variable id + * @tparam T - expected type of the variable + * @return Some(value) if the variable is defined in the context AND has the given type. + * None otherwise + */ def getVarFromInput[T](inputId: Short, id: Byte)(implicit cT: RType[T]): Option[T] def vars: Coll[AnyValue] diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index 76072a2fa4..af9b9135d1 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -274,7 +274,7 @@ object ReflectionData { obj.asInstanceOf[Context].getVar(args(0).asInstanceOf[Byte])(args(1).asInstanceOf[RType[_]]) }, mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Short], classOf[Byte], classOf[RType[_]])) { (obj, args) => - obj.asInstanceOf[Context].getVarFromInput(args(0).asInstanceOf[Byte], args(1).asInstanceOf[Byte])(args(2).asInstanceOf[RType[_]]) + obj.asInstanceOf[Context].getVarFromInput(args(0).asInstanceOf[Short], args(1).asInstanceOf[Byte])(args(2).asInstanceOf[RType[_]]) }, mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[Context].headers diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala index 5a17038c54..828be052b4 100644 --- a/data/shared/src/main/scala/sigma/ast/SMethod.scala +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -300,6 +300,11 @@ object SMethod { (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2]): RMethod = RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass) + def javaMethodOf[T, A1, A2, A3] + (methodName: String) + (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2], cA3: ClassTag[A3]): RMethod = + RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass, cA3.runtimeClass) + /** Default fallback method call recognizer which builds MethodCall ErgoTree nodes. */ val MethodCallIrBuilder: PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue] = { case (builder, obj, method, args, tparamSubst) => diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 8d8e4e9056..3dbe2d707a 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,7 +2,6 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ -import sigma.Evaluation.stypeToRType import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} @@ -1427,24 +1426,22 @@ case object SContextMethods extends MonoTypeMethods { lazy val getVarV6Method = SMethod( this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind, Seq(tT)) - .withIRInfo(MethodCallIrBuilder) + .withIRInfo( + MethodCallIrBuilder, + javaMethodOf[Context, Byte, RType[_]]("getVar"), + { mtype => Array(mtype.tRange.asOption[SType].elemType) }) .withInfo(MethodCall, "") // todo: desc // todo: costing, desc lazy val getVarFromInputMethod = SMethod( this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) - .withIRInfo(MethodCallIrBuilder) + .withIRInfo( + MethodCallIrBuilder, + javaMethodOf[Context, Short, Byte, RType[_]]("getVarFromInput"), + { mtype => Array(mtype.tRange.asOption[SType].elemType) }) .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) - def getVarFromInput_eval[T](mc: MethodCall, ctx: sigma.Context, inputId: Short, varId: Byte) - (implicit E: ErgoTreeEvaluator): Option[T] = { - // E.addCost(getVarFromInputMethod.costKind) - val rt = stypeToRType(mc.typeSubst.get(tT).get) - val res = ctx.getVarFromInput(inputId, varId)(rt).asInstanceOf[Option[T]] - res - } - - private lazy val commonMethods = Array( + private lazy val commonMethods = super.getMethods() ++ Array( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod ) @@ -1459,9 +1456,9 @@ case object SContextMethods extends MonoTypeMethods { protected override def getMethods(): Seq[SMethod] = { if (VersionContext.current.isV6SoftForkActivated) { - super.getMethods() ++ v6Methods + v6Methods } else { - super.getMethods() ++ v5Methods + v5Methods } } diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala index dace767d9f..87c661a00a 100644 --- a/data/shared/src/main/scala/sigma/ast/values.scala +++ b/data/shared/src/main/scala/sigma/ast/values.scala @@ -1312,7 +1312,7 @@ case class MethodCall( val objV = obj.evalTo[Any](env) addCost(MethodCall.costKind) // MethodCall overhead method.costKind match { - case fixed: FixedCost if method.explicitTypeArgs.isEmpty => + case fixed: FixedCost => val extra = method.extraDescriptors val extraLen = extra.length val len = args.length diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala index 69736a0224..9d1f6c045c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -332,6 +332,9 @@ object GraphIRReflection { mkMethod(clazz, "getVar", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => obj.asInstanceOf[ctx.Context].getVar(args(0).asInstanceOf[ctx.Ref[Byte]])(args(1).asInstanceOf[ctx.Elem[_]]) }, + mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Context].getVarFromInput(args(0).asInstanceOf[ctx.Ref[Short]], args(1).asInstanceOf[ctx.Ref[Byte]])(args(2).asInstanceOf[ctx.Elem[_]]) + }, mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, args) => obj.asInstanceOf[ctx.Context].headers } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala index 48248f2165..7e7840ab23 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala @@ -27,7 +27,7 @@ trait MethodCalls extends Base { self: IRContext => * given `method`. */ case class MethodCall private[MethodCalls](receiver: Sym, method: RMethod, args: Seq[AnyRef], neverInvoke: Boolean) - (val resultType: Elem[Any], val isAdapterCall: Boolean = false, val typeSubst: Map[STypeVar, SType] = Map()) extends Def[Any] { + (val resultType: Elem[Any], val isAdapterCall: Boolean = false, val typeSubst: Map[STypeVar, SType]) extends Def[Any] { override def mirror(t: Transformer): Ref[Any] = { val len = args.length 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 6826321ccd..41559d53ff 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -1,6 +1,7 @@ package sigma.compiler.ir import org.ergoplatform._ +import sigma.VersionContext import sigma.ast._ import sigma.ast.syntax.{ValueOps, _} import sigma.data.{ProveDHTuple, ProveDlog} diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index d5bd87d306..dad43e318a 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -1610,10 +1610,11 @@ object Context extends EntityObject("Context") { } override def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) asRep[WOption[T]](mkMethodCall(self, ContextClass.getMethod("getVar", classOf[Sym], classOf[Elem[_]]), Array[AnyRef](id, cT), - true, false, element[WOption[T]])) + true, false, element[WOption[T]], Map(tT -> st))) } override def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { @@ -1715,10 +1716,11 @@ object Context extends EntityObject("Context") { } def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) asRep[WOption[T]](mkMethodCall(source, ContextClass.getMethod("getVar", classOf[Sym], classOf[Elem[_]]), Array[AnyRef](id, cT), - true, true, element[WOption[T]])) + true, true, element[WOption[T]], Map(tT -> st))) } def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index 0d53802239..51805b2dea 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -165,7 +165,7 @@ class SigmaTyper(val builder: SigmaBuilder, .getOrElse(mkMethodCall(newObj, method, newArgs, subst)) } else { val newSelect = mkSelect(newObj, n, Some(concrFunTpe)).withSrcCtx(sel.sourceContext) - mkApply(newSelect, newArgs.toArray[SValue]) + mkApply(newSelect, newArgs) } case Some(method) => error(s"Don't know how to handle method $method in obj $p", sel.sourceContext) @@ -424,11 +424,6 @@ class SigmaTyper(val builder: SigmaBuilder, error(s"Invalid application of type arguments $app: function $input doesn't have type parameters", input.sourceContext) } -// case app @ ApplyTypes(in, targs) => -// val newIn = assignType(env, in) -// ApplyTypes(newIn, targs) -// error(s"Invalid application of type arguments $app: expression doesn't have type parameters") - case If(c, t, e) => val c1 = assignType(env, c).asValue[SBoolean.type] val t1 = assignType(env, t) diff --git a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala index bcd4b21129..266c5e66e5 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala @@ -34,8 +34,6 @@ class SigmaDslStaginTests extends BaseCtxTests with ErgoScriptTestkit with BaseL val ctx: SContext = newContext(10, boxA1, VersionContext.MaxSupportedScriptVersion, VersionContext.MaxSupportedScriptVersion) .withInputs(boxA2) .withVariables(Map(1 -> toAnyValue(30), 2 -> toAnyValue(40))) - val p1: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(true)) - val p2: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(false)) cake.check(dsl, { env: EnvRep[RSigmaDslBuilder] => for { dsl <- env; arg <- lifted(true) } yield dsl.sigmaProp(arg) }, dsl.sigmaProp(true)) diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index c68c37a4dc..4989a265d1 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -640,6 +640,10 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "CONTEXT.dataInputs") shouldBe SCollection(SBox) } + property("SContext.getVar") { + typecheck(env, "CONTEXT.getVar[Int](1.toByte).get") shouldBe SInt + } + property("SAvlTree.digest") { typecheck(env, "getVar[AvlTree](1).get.digest") shouldBe SByteArray } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index f5c9e1cd32..da6c9f361d 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -142,26 +142,19 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } - property("Unit register") { - // TODO frontend: implement missing Unit support in compiler - // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 - test("R1", env, ext, - script = "", /* means cannot be compiled - the corresponding script is { SELF.R4[Unit].isDefined } */ - ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).isDefined.toSigmaProp, - additionalRegistersOpt = Some(Map( - reg1 -> UnitConstant.instance - )) - ) + property("Context.getVar") { + def varTest() = { + test("GetVar1", env, ext, + "{ CONTEXT.getVar[Int](intVar2.toByte).get == 2 }", + null + ) + } - test("R2", env, ext, - script = "", /* means cannot be compiled - the corresponding script is "{ SELF.R4[Unit].get == () }" */ - EQ(ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).get, UnitConstant.instance).toSigmaProp, - additionalRegistersOpt = Some(Map( - reg1 -> UnitConstant.instance - )) - ) + if(VersionContext.current.isV6SoftForkActivated) { + varTest() + } else { + an[Exception] should be thrownBy(varTest()) + } } property("getVarFromInput") { @@ -456,21 +449,6 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } - property("Context.getVar") { - def varTest() = { - test("GetVar1", env, ext, - "{ CONTEXT.getVar[Int](intVar2).get == 2 }", - null - ) - } - - if(VersionContext.current.isV6SoftForkActivated) { - varTest() - } else { - an[Exception] should be thrownBy(varTest()) - } - } - property("GetVar") { test("GetVar1", env, ext, "{ getVar[Int](intVar2).get == 2 }", @@ -839,4 +817,26 @@ class BasicOpsSpecification extends CompilerTestingCommons test("subst", env, ext, hostScript, null) } + property("Unit register") { + // TODO frontend: implement missing Unit support in compiler + // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 + test("R1", env, ext, + script = "", /* means cannot be compiled + the corresponding script is { SELF.R4[Unit].isDefined } */ + ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).isDefined.toSigmaProp, + additionalRegistersOpt = Some(Map( + reg1 -> UnitConstant.instance + )) + ) + + test("R2", env, ext, + script = "", /* means cannot be compiled + the corresponding script is "{ SELF.R4[Unit].get == () }" */ + EQ(ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).get, UnitConstant.instance).toSigmaProp, + additionalRegistersOpt = Some(Map( + reg1 -> UnitConstant.instance + )) + ) + } + } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala index 969439fe59..5e89915384 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala @@ -68,11 +68,9 @@ case class AssetsPartialFilling[Spec <: ContractSpec] val out = OUTPUTS(outIdx) val tokenData = out.R2[Coll[(Coll[Byte], Long)]].get(0) - val tokenId = tokenData._1 val tokenValue = tokenData._2 val selfTokenData = SELF.R2[Coll[(Coll[Byte], Long)]].get(0) - val selfTokenId = selfTokenData._1 val selfTokenValue = selfTokenData._2 val selfValue = SELF.value From 65144a9b9e3745d68effbc494171b47c4b472e04 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 13 Aug 2024 18:56:34 +0300 Subject: [PATCH 47/78] merging w. 6.0.0 --- .../src/main/scala/sigma/VersionContext.scala | 2 +- .../src/main/scala/sigma/ast/SType.scala | 30 +- .../validation/ValidationRules.scala | 17 +- .../src/main/scala/sigma/ast/SMethod.scala | 25 +- .../main/scala/sigma/ast/SigmaPredef.scala | 1 - .../src/main/scala/sigma/ast/methods.scala | 2 - .../src/main/scala/sigma/ast/values.scala | 2 + .../serialization/ErgoTreeSerializer.scala | 48 +- .../sigmastate/interpreter/Interpreter.scala | 7 +- .../MethodCallSerializerSpecification.scala | 1 - .../sigma/compiler/phases/SigmaTyper.scala | 3 +- .../sigma/LanguageSpecificationBase.scala | 24 +- .../scala/sigma/LanguageSpecificationV5.scala | 1890 +++++++++-------- .../scala/sigma/LanguageSpecificationV6.scala | 161 +- .../test/scala/sigma/SigmaDslTesting.scala | 84 +- 15 files changed, 1352 insertions(+), 945 deletions(-) diff --git a/core/shared/src/main/scala/sigma/VersionContext.scala b/core/shared/src/main/scala/sigma/VersionContext.scala index 19a4857086..6ede0a1556 100644 --- a/core/shared/src/main/scala/sigma/VersionContext.scala +++ b/core/shared/src/main/scala/sigma/VersionContext.scala @@ -23,7 +23,7 @@ case class VersionContext(activatedVersion: Byte, ergoTreeVersion: Byte) { def isJitActivated: Boolean = activatedVersion >= JitActivationVersion /** @return true, if the activated script version of Ergo protocol on the network is - * including Evolution update. */ + * including v6.0 update. */ def isV6SoftForkActivated: Boolean = activatedVersion >= V6SoftForkVersion } diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 6656ede3c9..717439bcbb 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -7,7 +7,7 @@ import sigma.data.OverloadHack.Overloaded1 import sigma.data.{CBigInt, Nullable, SigmaConstants} import sigma.reflection.{RClass, RMethod, ReflectionData} import sigma.util.Extensions.{IntOps, LongOps, ShortOps} -import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp} +import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp, VersionContext} import java.math.BigInteger @@ -375,6 +375,7 @@ case object SByte extends SPrimType with SEmbeddable with SNumericType with SMon case s: Short => s.toByteExact case i: Int => i.toByteExact case l: Long => l.toByteExact + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toByte // toByteExact from int is called under the hood case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -396,6 +397,7 @@ case object SShort extends SPrimType with SEmbeddable with SNumericType with SMo case s: Short => s case i: Int => i.toShortExact case l: Long => l.toShortExact + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toShort // toShortExact from int is called under the hood case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -419,6 +421,7 @@ case object SInt extends SPrimType with SEmbeddable with SNumericType with SMono case s: Short => s.toInt case i: Int => i case l: Long => l.toIntExact + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toInt case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -444,6 +447,7 @@ case object SLong extends SPrimType with SEmbeddable with SNumericType with SMon case s: Short => s.toLong case i: Int => i.toLong case l: Long => l + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toLong case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -465,24 +469,24 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM override def numericTypeIndex: Int = 4 override def upcast(v: AnyVal): BigInt = { - val bi = v match { - case x: Byte => BigInteger.valueOf(x.toLong) - case x: Short => BigInteger.valueOf(x.toLong) - case x: Int => BigInteger.valueOf(x.toLong) - case x: Long => BigInteger.valueOf(x) + v match { + case x: Byte => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Short => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Int => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Long => CBigInt(BigInteger.valueOf(x)) + case x: BigInt if VersionContext.current.isV6SoftForkActivated => x case _ => sys.error(s"Cannot upcast value $v to the type $this") } - CBigInt(bi) } override def downcast(v: AnyVal): BigInt = { - val bi = v match { - case x: Byte => BigInteger.valueOf(x.toLong) - case x: Short => BigInteger.valueOf(x.toLong) - case x: Int => BigInteger.valueOf(x.toLong) - case x: Long => BigInteger.valueOf(x) + v match { + case x: Byte => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Short => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Int => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Long => CBigInt(BigInteger.valueOf(x)) + case x: BigInt if VersionContext.current.isV6SoftForkActivated => x case _ => sys.error(s"Cannot downcast value $v to the type $this") } - CBigInt(bi) } } diff --git a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala index 07fe8db0ee..9d4de47a99 100644 --- a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala +++ b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala @@ -155,20 +155,6 @@ object ValidationRules { override protected lazy val settings: SigmaValidationSettings = currentSettings } - object CheckMinimalErgoTreeVersion extends ValidationRule(1016, - "ErgoTree should have at least required version") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = currentSettings - - final def apply(currentVersion: Byte, minVersion: Byte): Unit = { - checkRule() - if (currentVersion < minVersion) { - throwValidationException( - new SigmaException(s"ErgoTree should have at least $minVersion version, but was $currentVersion"), - Array(currentVersion, minVersion)) - } - } - } - val ruleSpecs: Seq[ValidationRule] = Seq( CheckDeserializedScriptType, CheckDeserializedScriptIsSigmaProp, @@ -185,8 +171,7 @@ object ValidationRules { CheckHeaderSizeBit, CheckCostFuncOperation, CheckPositionLimit, - CheckLoopLevelInCostFunction, - CheckMinimalErgoTreeVersion + CheckLoopLevelInCostFunction ) /** Validation settings that correspond to the current version of the ErgoScript implementation. diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala index 47f6e744b0..11e656b1be 100644 --- a/data/shared/src/main/scala/sigma/ast/SMethod.scala +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -63,6 +63,7 @@ case class MethodIRInfo( * @param docInfo optional human readable method description data * @param costFunc optional specification of how the cost should be computed for the * given method call (See ErgoTreeEvaluator.calcCost method). + * @param userDefinedInvoke optional custom method evaluation function */ case class SMethod( objType: MethodsContainer, @@ -73,7 +74,9 @@ case class SMethod( explicitTypeArgs: Seq[STypeVar], irInfo: MethodIRInfo, docInfo: Option[OperationInfo], - costFunc: Option[MethodCostFunc]) { + costFunc: Option[MethodCostFunc], + userDefinedInvoke: Option[SMethod.InvokeHandler] +) { /** Operation descriptor of this method. */ lazy val opDesc = MethodDesc(this) @@ -114,7 +117,12 @@ case class SMethod( /** Invoke this method on the given object with the arguments. * This is used for methods with FixedCost costKind. */ def invokeFixed(obj: Any, args: Array[Any]): Any = { - javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) + userDefinedInvoke match { + case Some(h) => + h(this, obj, args) + case None => + javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) + } } // TODO optimize: avoid lookup when this SMethod is created via `specializeFor` @@ -154,6 +162,11 @@ case class SMethod( m } + /** Create a new instance with the given user-defined invoke handler. */ + def withUserDefinedInvoke(handler: SMethod.InvokeHandler): SMethod = { + copy(userDefinedInvoke = Some(handler)) + } + /** Create a new instance with the given stype. */ def withSType(newSType: SFunc): SMethod = copy(stype = newSType) @@ -263,6 +276,12 @@ object SMethod { */ type InvokeDescBuilder = SFunc => Seq[SType] + /** Type of user-defined function which is called to handle method invocation. + * Instances of this type can be attached to [[SMethod]] instances. + * @see SNumericTypeMethods.ToBytesMethod + */ + type InvokeHandler = (SMethod, Any, Array[Any]) => Any + /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. * @param methodName the name of the method to lookup * @param cT the class where to search the methodName @@ -297,7 +316,7 @@ object SMethod { ): SMethod = { SMethod( objType, name, stype, methodId, costKind, explicitTypeArgs, - MethodIRInfo(None, None, None), None, None) + MethodIRInfo(None, None, None), None, None, None) } diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 7d6da0a5f9..8b89851938 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -5,7 +5,6 @@ import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} import scorex.util.encode.{Base16, Base58, Base64} import sigma.ast.SCollection.{SByteArray, SIntArray} import sigma.ast.SOption.SIntOption -import sigma.ast.SigmaPropConstant import sigma.ast.syntax._ import sigma.data.Nullable import sigma.exceptions.InvalidArguments diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 2cfecea15d..ef548d6185 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1,7 +1,6 @@ package sigma.ast import org.ergoplatform._ -import org.ergoplatform.validation.ValidationRules.CheckMinimalErgoTreeVersion import org.ergoplatform.validation._ import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} @@ -1534,7 +1533,6 @@ case object SGlobalMethods extends MonoTypeMethods { */ def serialize_eval(mc: MethodCall, G: SigmaDslBuilder, value: SType#WrappedType) (implicit E: ErgoTreeEvaluator): Coll[Byte] = { - CheckMinimalErgoTreeVersion(E.context.currentErgoTreeVersion, VersionContext.V6SoftForkVersion) E.addCost(SigmaByteWriter.StartWriterCost) diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala index dd1f10d9be..ff5da32ec7 100644 --- a/data/shared/src/main/scala/sigma/ast/values.scala +++ b/data/shared/src/main/scala/sigma/ast/values.scala @@ -1298,8 +1298,10 @@ case class MethodCall( method: SMethod, args: IndexedSeq[Value[SType]], typeSubst: Map[STypeVar, SType]) extends Value[SType] { + require(method.explicitTypeArgs.forall(tyArg => typeSubst.contains(tyArg)), s"Generic method call should have concrete type for each explicit type parameter, but was: $this") + override def companion = if (args.isEmpty) PropertyCall else MethodCall override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe) diff --git a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala index ce7d1241c3..5122ee940c 100644 --- a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala @@ -287,6 +287,9 @@ class ErgoTreeSerializer { * allow to use serialized scripts as pre-defined templates. * See [[SubstConstants]] for details. * + * Note, this operation doesn't require (de)serialization of ErgoTree expression, + * thus it is more efficient than serialization roundtrip. + * * @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1. * @param positions zero based indexes in ErgoTree.constants array which * should be replaced with new values @@ -304,21 +307,23 @@ class ErgoTreeSerializer { s"expected positions and newVals to have the same length, got: positions: ${positions.toSeq},\n newVals: ${newVals.toSeq}") val r = SigmaSerializer.startReader(scriptBytes) val (header, _, constants, treeBytes) = deserializeHeaderWithTreeBytes(r) - val w = SigmaSerializer.startWriter() - w.put(header) + val nConstants = constants.length + + val resBytes = if (VersionContext.current.isJitActivated) { + // need to measure the serialized size of the new constants + // by serializing them into a separate writer + val constW = SigmaSerializer.startWriter() - if (VersionContext.current.isJitActivated) { // The following `constants.length` should not be serialized when segregation is off // in the `header`, because in this case there is no `constants` section in the // ErgoTree serialization format. Thus, applying this `substituteConstants` for // non-segregated trees will return non-parsable ErgoTree bytes (when // `constants.length` is put in `w`). if (ErgoTree.isConstantSegregation(header)) { - w.putUInt(constants.length) + constW.putUInt(constants.length) } // The following is optimized O(nConstants + position.length) implementation - val nConstants = constants.length if (nConstants > 0) { val backrefs = getPositionsBackref(positions, nConstants) cfor(0)(_ < nConstants, _ + 1) { i => @@ -326,17 +331,38 @@ class ErgoTreeSerializer { val iPos = backrefs(i) // index to `positions` if (iPos == -1) { // no position => no substitution, serialize original constant - constantSerializer.serialize(c, w) + constantSerializer.serialize(c, constW) } else { - assert(positions(iPos) == i) // INV: backrefs and positions are mutually inverse + require(positions(iPos) == i) // INV: backrefs and positions are mutually inverse val newConst = newVals(iPos) require(c.tpe == newConst.tpe, s"expected new constant to have the same ${c.tpe} tpe, got ${newConst.tpe}") - constantSerializer.serialize(newConst, w) + constantSerializer.serialize(newConst, constW) } } } + + val constBytes = constW.toBytes // nConstants + serialized new constants + + // start composing the resulting tree bytes + val w = SigmaSerializer.startWriter() + w.put(header) // header byte + + if (VersionContext.current.isV6SoftForkActivated) { + // fix in v6.0 to save tree size to respect size bit of the original tree + if (ErgoTree.hasSize(header)) { + val size = constBytes.length + treeBytes.length + w.putUInt(size) // tree size + } + } + + w.putBytes(constBytes) // constants section + w.putBytes(treeBytes) // tree section + w.toBytes } else { + val w = SigmaSerializer.startWriter() + w.put(header) + // for v4.x compatibility we save constants.length here (see the above comment to // understand the consequences) w.putUInt(constants.length) @@ -357,10 +383,12 @@ class ErgoTreeSerializer { case (c, _) => constantSerializer.serialize(c, w) } + + w.putBytes(treeBytes) + w.toBytes } - w.putBytes(treeBytes) - (w.toBytes, constants.length) + (resBytes, nConstants) } } diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala index 9a1ab11f0e..c54e8bfc46 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala @@ -247,7 +247,12 @@ trait Interpreter { val currCost = addCostChecked(context.initCost, deserializeSubstitutionCost, context.costLimit) val context1 = context.withInitCost(currCost).asInstanceOf[CTX] val (propTree, context2) = trySoftForkable[(SigmaPropValue, CTX)](whenSoftFork = (TrueSigmaProp, context1)) { - applyDeserializeContextJITC(context, prop) + // Before ErgoTree V3 the deserialization cost was not added to the total cost + applyDeserializeContextJITC(if (VersionContext.current.activatedVersion >= VersionContext.V6SoftForkVersion) { + context1 + } else { + context + }, prop) } // here we assume that when `propTree` is TrueProp then `reduceToCrypto` always succeeds diff --git a/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala index e78518f8b3..603b75511d 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala @@ -2,7 +2,6 @@ package sigma.serialization import sigma.VersionContext import sigma.ast.SCollection.SByteArray -import sigma.ast.SType.tT import sigma.ast._ import sigma.validation.ValidationException diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index f9b52fe061..ac30a6cd0a 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -139,7 +139,8 @@ class SigmaTyper(val builder: SigmaBuilder, obj.tpe match { case p: SProduct => MethodsContainer.getMethod(p, n) match { - case Some(method @ SMethod(_, _, genFunTpe @ SFunc(_, _, _), _, _, _, _, _, _)) => + case Some(method: SMethod) => + val genFunTpe = method.stype val subst = Map(genFunTpe.tpeParams.head.ident -> rangeTpe) val concrFunTpe = applySubst(genFunTpe, subst) val expectedArgs = concrFunTpe.asFunc.tDom.tail diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationBase.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationBase.scala index 2bb44fc910..7be79546e7 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationBase.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationBase.scala @@ -1,10 +1,11 @@ package sigma import org.scalatest.BeforeAndAfterAll -import sigma.ast.JitCost -import sigma.eval.{EvalSettings, Profiler} +import sigma.ast.{Apply, FixedCostItem, FuncValue, GetVar, JitCost, OptionGet, ValUse} +import sigma.eval.{CostDetails, EvalSettings, Profiler} import sigmastate.CompilerCrossVersionProps import sigmastate.interpreter.CErgoTreeEvaluator + import scala.util.Success /** Base class for language test suites (one suite for each language version: 5.0, 6.0, etc.) @@ -123,4 +124,23 @@ abstract class LanguageSpecificationBase extends SigmaDslTesting prepareSamples[(PreHeader, PreHeader)] prepareSamples[(Header, Header)] } + + ///===================================================== + /// CostDetails shared among test cases + ///----------------------------------------------------- + val traceBase = Array( + FixedCostItem(Apply), + FixedCostItem(FuncValue), + FixedCostItem(GetVar), + FixedCostItem(OptionGet), + FixedCostItem(FuncValue.AddToEnvironmentDesc, FuncValue.AddToEnvironmentDesc_CostKind), + FixedCostItem(ValUse) + ) + + /** Helper method to create the given expected results for all tree versions. */ + def expectedSuccessForAllTreeVersions[A](value: A, cost: Int, costDetails: CostDetails) = { + val res = ExpectedResult(Success(value), Some(cost)) -> Some(costDetails) + Seq(0, 1, 2, 3).map(version => version -> res) + } + } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index 700b48fd13..af4f93d861 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -10,6 +10,7 @@ import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId import sigma.Extensions.{ArrayOps, CollOps} +import sigma.VersionContext.V6SoftForkVersion import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} import sigma.ast.SCollection._ import sigma.ast.syntax._ @@ -47,17 +48,6 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => import TestData._ - ///===================================================== - /// CostDetails shared among test cases - ///----------------------------------------------------- - val traceBase = Array( - FixedCostItem(Apply), - FixedCostItem(FuncValue), - FixedCostItem(GetVar), - FixedCostItem(OptionGet), - FixedCostItem(FuncValue.AddToEnvironmentDesc, FuncValue.AddToEnvironmentDesc_CostKind), - FixedCostItem(ValUse) - ) def upcastCostDetails(tpe: SType) = TracedCost(traceBase :+ TypeBasedCostItem(Upcast, tpe)) def downcastCostDetails(tpe: SType) = TracedCost(traceBase :+ TypeBasedCostItem(Downcast, tpe)) def arithOpsCostDetails(tpe: SType) = CostDetails( @@ -144,7 +134,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) val newCost = 1768 - def success(b: Boolean) = Expected(Success(b), 1768, newDetails, newCost) + + def success(b: Boolean) = Expected(Success(b), 1768, newDetails, newCost, Seq.fill(4)(2012)) + val cases = Seq( (true, true) -> success(false), (true, false) -> success(true), @@ -173,14 +165,14 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val expectedCost = 1768 val newCost = 1768 val cases = Seq( - (true, true) -> Expected(Success(false), expectedCost, newDetails, newCost) + (true, true) -> Expected(Success(false), expectedCost, newDetails, newCost, Seq.fill(4)(2012)) ) verifyCases(cases, feature) val initCost = 100 initialCostInTests.withValue(initCost) { val cases = Seq( - (true, true) -> Expected(Success(false), expectedCost + initCost, newDetails, newCost + initCost) + (true, true) -> Expected(Success(false), expectedCost + initCost, newDetails, newCost + initCost, Seq.fill(4)(2012 + initCost)) ) verifyCases(cases, feature) } @@ -209,7 +201,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => FixedCostItem(BinXor) ) ) - def success(b: Boolean) = Expected(Success(b), 1769, newDetails, 1769) + + def success(b: Boolean) = Expected(Success(b), 1769, newDetails, 1769, Seq.fill(4)(2027)) + val cases = Seq( (1095564593, true) -> success(true), (-901834021, true) -> success(true), @@ -246,10 +240,10 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) val cases = Seq( - (false, true) -> Expected(Success(false), 1766, newDetails1, 1766), - (false, false) -> Expected(Success(false), 1766, newDetails1, 1766), - (true, true) -> Expected(Success(true), 1768, newDetails2, 1768), - (true, false) -> Expected(Success(false), 1768, newDetails2, 1768) + (false, true) -> Expected(Success(false), 1766, newDetails1, 1766, Seq.fill(4)(2010)), + (false, false) -> Expected(Success(false), 1766, newDetails1, 1766, Seq.fill(4)(2010)), + (true, true) -> Expected(Success(true), 1768, newDetails2, 1768, Seq.fill(4)(2012)), + (true, false) -> Expected(Success(false), 1768, newDetails2, 1768, Seq.fill(4)(2012)) ) verifyCases(cases, eq) } @@ -279,10 +273,10 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) val cases = Seq( - (true, false) -> Expected(Success(true), 1766, newDetails1, 1766), - (true, true) -> Expected(Success(true), 1766, newDetails1, 1766), - (false, false) -> Expected(Success(false), 1768, newDetails2, 1768), - (false, true) -> Expected(Success(true), 1768, newDetails2, 1768) + (true, false) -> Expected(Success(true), 1766, newDetails1, 1766, Seq.fill(4)(2010)), + (true, true) -> Expected(Success(true), 1766, newDetails1, 1766, Seq.fill(4)(2010)), + (false, false) -> Expected(Success(false), 1768, newDetails2, 1768, Seq.fill(4)(2012)), + (false, true) -> Expected(Success(true), 1768, newDetails2, 1768, Seq.fill(4)(2012)) ) verifyCases(cases, eq) } @@ -327,7 +321,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( Seq( - (true, Expected(Success(true), 1765, newDetails1, 1765)), + (true, Expected(Success(true), 1765, newDetails1, 1765, Seq.fill(4)(2027))), (false, Expected(new ArithmeticException("/ by zero"))) ), existingFeature((x: Boolean) => x || (1 / 0 == 1), @@ -343,7 +337,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( (true, Expected(new ArithmeticException("/ by zero"))), - (false, Expected(Success(false), 1765, newDetails2, 1765)) + (false, Expected(Success(false), 1765, newDetails2, 1765, Seq.fill(4)(2027))) ), existingFeature((x: Boolean) => x && (1 / 0 == 1), "{ (x: Boolean) => x && (1 / 0 == 1) }", @@ -357,8 +351,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (false, Expected(Success(false), 1765, newDetails2, 1765)), - (true, Expected(Success(true), 1768, newDetails3, 1768)) + (false, Expected(Success(false), 1765, newDetails2, 1765, Seq.fill(4)(2033))), + (true, Expected(Success(true), 1768, newDetails3, 1768, Seq.fill(4)(2036))) ), existingFeature((x: Boolean) => x && (x || (1 / 0 == 1)), "{ (x: Boolean) => x && (x || (1 / 0 == 1)) }", @@ -375,8 +369,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (false, Expected(Success(false), 1765, newDetails2, 1765)), - (true, Expected(Success(true), 1770, newDetails4, 1770)) + (false, Expected(Success(false), 1765, newDetails2, 1765, Seq.fill(4)(2039))), + (true, Expected(Success(true), 1770, newDetails4, 1770, Seq.fill(4)(2044))) ), existingFeature((x: Boolean) => x && (x && (x || (1 / 0 == 1))), "{ (x: Boolean) => x && (x && (x || (1 / 0 == 1))) }", @@ -396,8 +390,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (false, Expected(Success(false), 1765, newDetails2, 1765)), - (true, Expected(Success(true), 1773, newDetails5, 1773)) + (false, Expected(Success(false), 1765, newDetails2, 1765, Seq.fill(4)(2045))), + (true, Expected(Success(true), 1773, newDetails5, 1773, Seq.fill(4)(2053))) ), existingFeature((x: Boolean) => x && (x && (x && (x || (1 / 0 == 1)))), "{ (x: Boolean) => x && (x && (x && (x || (1 / 0 == 1)))) }", @@ -431,7 +425,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( (false, Expected(new ArithmeticException("/ by zero"))), - (true, Expected(Success(true), 1773, newDetails6, 1773)) + (true, Expected(Success(true), 1773, newDetails6, 1773, Seq.fill(4)(2075))) ), existingFeature((x: Boolean) => !(!x && (1 / 0 == 1)) && (x || (1 / 0 == 1)), "{ (x: Boolean) => !(!x && (1 / 0 == 1)) && (x || (1 / 0 == 1)) }", @@ -460,7 +454,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( Seq( - (true, Expected(Success(true), 1768, newDetails7, 1768)), + (true, Expected(Success(true), 1768, newDetails7, 1768, Seq.fill(4)(2036))), (false, Expected(new ArithmeticException("/ by zero"))) ), existingFeature((x: Boolean) => (x || (1 / 0 == 1)) && x, @@ -486,7 +480,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( Seq( - (true, Expected(Success(true), 1770, newDetails8, 1770)), + (true, Expected(Success(true), 1770, newDetails8, 1770, Seq.fill(4)(2068))), (false, Expected(new ArithmeticException("/ by zero"))) ), existingFeature((x: Boolean) => (x || (1 / 0 == 1)) && (x || (1 / 0 == 1)), @@ -518,7 +512,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( Seq( - (true, Expected(Success(true), 1775, newDetails9, 1775)), + (true, Expected(Success(true), 1775, newDetails9, 1775, Seq.fill(4)(2107))), (false, Expected(new ArithmeticException("/ by zero"))) ), existingFeature( @@ -568,7 +562,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( (false, Expected(new ArithmeticException("/ by zero"))), - (true, Expected(Success(true), 1780, newDetails10, 1780)) + (true, Expected(Success(true), 1780, newDetails10, 1780, Seq.fill(4)(2156))) ), existingFeature( (x: Boolean) => (!(!x && (1 / 0 == 1)) || (1 / 0 == 0)) && (!(!x && (1 / 0 == 1)) || (1 / 0 == 1)), @@ -611,7 +605,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def expect(v: Byte) = Expected(Success(v), 1763, TracedCost(traceBase), 1763) + def expect(v: Byte) = Expected(Success(v), 1763, TracedCost(traceBase), 1763, Seq.fill(4)(1993)) + Seq( (0.toByte, expect(0.toByte)), (1.toByte, expect(1.toByte)), @@ -628,7 +623,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def expected(v: Short) = Expected(Success(v), 1764, upcastCostDetails(SShort), 1764) + def expected(v: Short) = Expected(Success(v), 1764, upcastCostDetails(SShort), 1764, Seq.fill(4)(1998)) + Seq( (0.toByte, expected(0.toShort)), (1.toByte, expected(1.toShort)), @@ -645,7 +641,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def expected(v: Int) = Expected(Success(v), 1764, upcastCostDetails(SInt), 1764) + def expected(v: Int) = Expected(Success(v), 1764, upcastCostDetails(SInt), 1764, Seq.fill(4)(1998)) + Seq( (0.toByte, expected(0)), (1.toByte, expected(1)), @@ -662,7 +659,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def expected(v: Long) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764) + def expected(v: Long) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764, Seq.fill(4)(1998)) + Seq( (0.toByte, expected(0L)), (1.toByte, expected(1L)), @@ -679,7 +677,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def expected(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767) + def expected(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767, Seq.fill(4)(2001)) + Seq( (0.toByte, expected(CBigInt(new BigInteger("0", 16)))), (1.toByte, expected(CBigInt(new BigInteger("1", 16)))), @@ -697,7 +696,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val n = ExactIntegral.ByteIsExactIntegral verifyCases( { - def success[T](v: (T, (T, (T, (T, T))))) = Expected(Success(v), 1788, arithOpsCostDetails(SByte), 1788) + def success[T](v: (T, (T, (T, (T, T))))) = Expected(Success(v), 1788, arithOpsCostDetails(SByte), 1788, Seq.fill(4)(2116)) + Seq( ((-128.toByte, -128.toByte), Expected(new ArithmeticException("Byte overflow"))), ((-128.toByte, 0.toByte), Expected(new ArithmeticException("/ by zero"))), @@ -796,6 +796,12 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ((y, x), Expected(res.value, cost, newCostDetails, cost)) } + def swapArgs[A](cases: Seq[((A, A), Expected[Boolean])], cost: Int, newCostDetails: CostDetails, + expectedV3Costs: Seq[Int]) = + cases.map { case ((x, y), res) => + ((y, x), Expected(res.value, cost, newCostDetails, cost, expectedV3Costs)) + } + def newCasesFrom[A, R]( cases: Seq[(A, A)] )( @@ -814,12 +820,19 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ((x, y), Expected(Success(getExpectedRes(x, y)), cost = cost, expectedDetails = newCostDetails, expectedNewCost = cost)) } - def verifyOp[A: Ordering: Arbitrary] - (cases: Seq[((A, A), Expected[Boolean])], - opName: String, - op: (SValue, SValue) => SValue) - (expectedFunc: (A, A) => Boolean, generateCases: Boolean = true) - (implicit tA: RType[A], sampled: Sampled[(A, A)], evalSettings: EvalSettings) = { + def newCasesFrom3[A, R](cases: Seq[(A, A)]) + (getExpectedRes: (A, A) => R, cost: Int, newCostDetails: CostDetails, + expectedV3Costs: Seq[Int]) = + cases.map { case (x, y) => + ((x, y), Expected(Success(getExpectedRes(x, y)), cost = cost, expectedDetails = newCostDetails, expectedNewCost = cost, expectedV3Costs)) + } + + def verifyOp[A: Ordering : Arbitrary] + (cases: Seq[((A, A), Expected[Boolean])], + opName: String, + op: (SValue, SValue) => SValue) + (expectedFunc: (A, A) => Boolean, generateCases: Boolean = true) + (implicit tA: RType[A], sampled: Sampled[(A, A)], evalSettings: EvalSettings) = { val nameA = RType[A].name val tpeA = Evaluation.rtypeToSType(tA) verifyCases(cases, @@ -844,7 +857,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("Byte LT, GT, NEQ") { val o = ExactOrdering.ByteIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SByte), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SByte), 1768, Seq.fill(4)(2012)) + val LT_cases: Seq[((Byte, Byte), Expected[Boolean])] = Seq( (-128.toByte, -128.toByte) -> expect(false), (-128.toByte, -127.toByte) -> expect(true), @@ -885,16 +900,18 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LT_cases, "<", LT.apply)(_ < _) verifyOp( - swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SByte)), + swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SByte), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(_ > _) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost), expectedV3Costs = Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } property("Byte LE, GE") { val o = ExactOrdering.ByteIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SByte), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SByte), 1768, Seq.fill(4)(2012)) + val LE_cases: Seq[((Byte, Byte), Expected[Boolean])] = Seq( (-128.toByte, -128.toByte) -> expect(true), (-128.toByte, -127.toByte) -> expect(true), @@ -936,7 +953,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LE_cases, "<=", LE.apply)(_ <= _) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SByte)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SByte), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(_ >= _) } property("Short methods equivalence") { @@ -945,7 +962,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764, Seq.fill(4)(1998)) + Seq( (Short.MinValue, Expected(new ArithmeticException("Byte overflow"))), (-21626.toShort, Expected(new ArithmeticException("Byte overflow"))), @@ -964,7 +982,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763) + def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763, Seq.fill(4)(1993)) + Seq( (-32768.toShort, success(-32768.toShort)), (-27798.toShort, success(-27798.toShort)), @@ -981,7 +1000,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SInt), 1764) + def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SInt), 1764, Seq.fill(4)(1998)) + Seq( (-32768.toShort, success(-32768)), (-21064.toShort, success(-21064)), @@ -998,7 +1018,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764) + def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764, Seq.fill(4)(1998)) + Seq( (-32768.toShort, success(-32768L)), (-23408.toShort, success(-23408L)), @@ -1015,7 +1036,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767) + def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767, Seq.fill(4)(2001)) + Seq( (-32768.toShort, success(CBigInt(new BigInteger("-8000", 16)))), (-26248.toShort, success(CBigInt(new BigInteger("-6688", 16)))), @@ -1033,7 +1055,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val n = ExactIntegral.ShortIsExactIntegral verifyCases( { - def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SShort), 1788) + def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SShort), 1788, Seq.fill(4)(2116)) + Seq( ((-32768.toShort, 1.toShort), Expected(new ArithmeticException("Short overflow"))), ((-32768.toShort, 4006.toShort), Expected(new ArithmeticException("Short overflow"))), @@ -1125,7 +1148,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("Short LT, GT, NEQ") { val o = ExactOrdering.ShortIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SShort), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SShort), 1768, Seq.fill(4)(2012)) + val LT_cases: Seq[((Short, Short), Expected[Boolean])] = Seq( (Short.MinValue, Short.MinValue) -> expect(false), (Short.MinValue, (Short.MinValue + 1).toShort) -> expect(true), @@ -1165,15 +1190,17 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LT_cases, "<", LT.apply)(_ < _) - verifyOp(swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SShort)), ">", GT.apply)(_ > _) + verifyOp(swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SShort), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(_ > _) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost), expectedV3Costs = Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } property("Short LE, GE") { val o = ExactOrdering.ShortIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SShort), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SShort), 1768, Seq.fill(4)(2012)) + val LE_cases: Seq[((Short, Short), Expected[Boolean])] = Seq( (Short.MinValue, Short.MinValue) -> expect(true), (Short.MinValue, (Short.MinValue + 1).toShort) -> expect(true), @@ -1215,7 +1242,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LE_cases, "<=", LE.apply)(_ <= _) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SShort)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SShort), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(_ >= _) } @@ -1225,7 +1252,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764, Seq.fill(4)(1998)) + Seq( (Int.MinValue, Expected(new ArithmeticException("Byte overflow"))), (-2014394379, Expected(new ArithmeticException("Byte overflow"))), @@ -1244,7 +1272,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SShort), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SShort), 1764, Seq.fill(4)(1998)) + Seq( (Int.MinValue, Expected(new ArithmeticException("Short overflow"))), (Short.MinValue - 1, Expected(new ArithmeticException("Short overflow"))), @@ -1263,7 +1292,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763) + def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763, Seq.fill(4)(1993)) + Seq( (Int.MinValue, success(Int.MinValue)), (-1, success(-1)), @@ -1278,7 +1308,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764) + def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764, Seq.fill(4)(1998)) + Seq( (Int.MinValue, success(Int.MinValue.toLong)), (-1, success(-1L)), @@ -1293,7 +1324,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767) + def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767, Seq.fill(4)(2001)) + Seq( (Int.MinValue, success(CBigInt(new BigInteger("-80000000", 16)))), (-1937187314, success(CBigInt(new BigInteger("-737721f2", 16)))), @@ -1310,57 +1342,59 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val n = ExactNumeric.IntIsExactNumeric verifyCases( - { - def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SInt), 1788) - Seq( - ((Int.MinValue, 449583993), Expected(new ArithmeticException("integer overflow"))), - ((-1589633733, 2147483647), Expected(new ArithmeticException("integer overflow"))), - ((-1585471506, -1), success((-1585471507, (-1585471505, (1585471506, (1585471506, 0)))))), - ((-1569005179, 1230236634), Expected(new ArithmeticException("integer overflow"))), - ((-1493733356, -1319619597), Expected(new ArithmeticException("integer overflow"))), - ((-1100263120, -880052091), Expected(new ArithmeticException("integer overflow"))), - ((-1055955857, 309147303), Expected(new ArithmeticException("integer overflow"))), - ((-569807371, 0), Expected(new ArithmeticException("/ by zero"))), - ((-522264843, 2147483647), Expected(new ArithmeticException("integer overflow"))), - ((-109552389, 0), Expected(new ArithmeticException("/ by zero"))), - ((-1, -2147483648), Expected(new ArithmeticException("integer overflow"))), - ((-1, -1), success((-2, (0, (1, (1, 0)))))), - ((-1, 0), Expected(new ArithmeticException("/ by zero"))), - ((0, -2147483648), Expected(new ArithmeticException("integer overflow"))), - ((1, -1525049432), success((-1525049431, (1525049433, (-1525049432, (0, 1)))))), - ((1, 0), Expected(new ArithmeticException("/ by zero"))), - ((1, 805353746), success((805353747, (-805353745, (805353746, (0, 1)))))), - ((1, 2147483647), Expected(new ArithmeticException("integer overflow"))), - ((475797978, 0), Expected(new ArithmeticException("/ by zero"))), - ((782343922, -1448560539), Expected(new ArithmeticException("integer overflow"))), - ((928769361, 542647292), Expected(new ArithmeticException("integer overflow"))), - ((1568062151, 0), Expected(new ArithmeticException("/ by zero"))), - ((1698252401, -1), success((1698252400, (1698252402, (-1698252401, (-1698252401, 0)))))), - ((1949795740, -1575667037), Expected(new ArithmeticException("integer overflow"))), - ((Int.MaxValue, -1), Expected(new ArithmeticException("integer overflow"))), - ((Int.MaxValue, 1), Expected(new ArithmeticException("integer overflow"))), - ((Int.MaxValue, 1738276576), Expected(new ArithmeticException("integer overflow"))) - ) - }, - existingFeature( - { (x: (Int, Int)) => - val a = x._1; val b = x._2 - val plus = n.plus(a, b) - val minus = n.minus(a, b) - val mul = n.times(a, b) - val div = a / b - val mod = a % b - (plus, (minus, (mul, (div, mod)))) + { + def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SInt), 1788, Seq.fill(4)(2116)) + + Seq( + ((Int.MinValue, 449583993), Expected(new ArithmeticException("integer overflow"))), + ((-1589633733, 2147483647), Expected(new ArithmeticException("integer overflow"))), + ((-1585471506, -1), success((-1585471507, (-1585471505, (1585471506, (1585471506, 0)))))), + ((-1569005179, 1230236634), Expected(new ArithmeticException("integer overflow"))), + ((-1493733356, -1319619597), Expected(new ArithmeticException("integer overflow"))), + ((-1100263120, -880052091), Expected(new ArithmeticException("integer overflow"))), + ((-1055955857, 309147303), Expected(new ArithmeticException("integer overflow"))), + ((-569807371, 0), Expected(new ArithmeticException("/ by zero"))), + ((-522264843, 2147483647), Expected(new ArithmeticException("integer overflow"))), + ((-109552389, 0), Expected(new ArithmeticException("/ by zero"))), + ((-1, -2147483648), Expected(new ArithmeticException("integer overflow"))), + ((-1, -1), success((-2, (0, (1, (1, 0)))))), + ((-1, 0), Expected(new ArithmeticException("/ by zero"))), + ((0, -2147483648), Expected(new ArithmeticException("integer overflow"))), + ((1, -1525049432), success((-1525049431, (1525049433, (-1525049432, (0, 1)))))), + ((1, 0), Expected(new ArithmeticException("/ by zero"))), + ((1, 805353746), success((805353747, (-805353745, (805353746, (0, 1)))))), + ((1, 2147483647), Expected(new ArithmeticException("integer overflow"))), + ((475797978, 0), Expected(new ArithmeticException("/ by zero"))), + ((782343922, -1448560539), Expected(new ArithmeticException("integer overflow"))), + ((928769361, 542647292), Expected(new ArithmeticException("integer overflow"))), + ((1568062151, 0), Expected(new ArithmeticException("/ by zero"))), + ((1698252401, -1), success((1698252400, (1698252402, (-1698252401, (-1698252401, 0)))))), + ((1949795740, -1575667037), Expected(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, -1), Expected(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, 1), Expected(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, 1738276576), Expected(new ArithmeticException("integer overflow"))) + ) }, - """{ (x: (Int, Int)) => - | val a = x._1; val b = x._2 - | val plus = a + b - | val minus = a - b - | val mul = a * b - | val div = a / b - | val mod = a % b - | (plus, (minus, (mul, (div, mod)))) - |}""".stripMargin, + existingFeature( + { (x: (Int, Int)) => + val a = x._1; + val b = x._2 + val plus = n.plus(a, b) + val minus = n.minus(a, b) + val mul = n.times(a, b) + val div = a / b + val mod = a % b + (plus, (minus, (mul, (div, mod)))) + }, + """{ (x: (Int, Int)) => + | val a = x._1; val b = x._2 + | val plus = a + b + | val minus = a - b + | val mul = a * b + | val div = a / b + | val mod = a % b + | (plus, (minus, (mul, (div, mod)))) + |}""".stripMargin, FuncValue( Vector((1, STuple(Vector(SInt, SInt)))), BlockValue( @@ -1403,7 +1437,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("Int LT, GT, NEQ") { val o = ExactOrdering.IntIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SInt), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SInt), 1768, Seq.fill(4)(2012)) + val LT_cases: Seq[((Int, Int), Expected[Boolean])] = Seq( (Int.MinValue, Int.MinValue) -> expect(false), (Int.MinValue, (Int.MinValue + 1).toInt) -> expect(true), @@ -1444,16 +1480,16 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LT_cases, "<", LT.apply)(_ < _) verifyOp( - swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SInt)), + swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SInt), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(_ > _) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost), expectedV3Costs = Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } property("Int LE, GE") { val o = ExactOrdering.IntIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SInt), 1768) + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SInt), 1768, Seq.fill(4)(2012)) val LE_cases: Seq[((Int, Int), Expected[Boolean])] = Seq( (Int.MinValue, Int.MinValue) -> expect(true), (Int.MinValue, (Int.MinValue + 1).toInt) -> expect(true), @@ -1495,7 +1531,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LE_cases, "<=", LE.apply)(_ <= _) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SInt)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SInt), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(_ >= _) } @@ -1509,7 +1545,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("Long.toByte method") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764, Seq.fill(4)(1998)) Seq( (Long.MinValue, Expected(new ArithmeticException("Byte overflow"))), (Byte.MinValue.toLong - 1, Expected(new ArithmeticException("Byte overflow"))), @@ -1530,7 +1566,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("Long.toShort method") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SShort), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SShort), 1764, Seq.fill(4)(1998)) Seq( (Long.MinValue, Expected(new ArithmeticException("Short overflow"))), (Short.MinValue.toLong - 1, Expected(new ArithmeticException("Short overflow"))), @@ -1551,7 +1587,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("Long.toInt method") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SInt), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SInt), 1764, Seq.fill(4)(1998)) + Seq( (Long.MinValue, Expected(new ArithmeticException("Int overflow"))), (Int.MinValue.toLong - 1, Expected(new ArithmeticException("Int overflow"))), @@ -1572,7 +1609,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("Long.toLong method") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763) + def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763, Seq.fill(4)(1993)) Seq( (Long.MinValue, success(Long.MinValue)), (-1L, success(-1L)), @@ -1589,7 +1626,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("Long.toBigInt method") { verifyCases( { - def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767) + def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767, Seq.fill(4)(2001)) Seq( (Long.MinValue, success(CBigInt(new BigInteger("-8000000000000000", 16)))), (-1074651039980347209L, success(CBigInt(new BigInteger("-ee9ed6d57885f49", 16)))), @@ -1609,83 +1646,85 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val n = ExactNumeric.LongIsExactNumeric verifyCases( - { - def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SLong), 1788) - Seq( - ((Long.MinValue, -4677100190307931395L), Expected(new ArithmeticException("long overflow"))), - ((Long.MinValue, -1L), Expected(new ArithmeticException("long overflow"))), - ((Long.MinValue, 1L), Expected(new ArithmeticException("long overflow"))), - ((-9223372036854775808L, 0L), Expected(new ArithmeticException("/ by zero"))), - ((-5828066432064138816L, 9105034716270510411L), Expected(new ArithmeticException("long overflow"))), - ((-4564956247298949325L, -1L), success( - (-4564956247298949326L, (-4564956247298949324L, (4564956247298949325L, (4564956247298949325L, 0L)))) - )), - ((-1499553565058783253L, -3237683216870282569L), Expected(new ArithmeticException("long overflow"))), - ((-1368457031689886112L, 9223372036854775807L), Expected(new ArithmeticException("long overflow"))), - ((-1L, -4354407074688367443L), success((-4354407074688367444L, (4354407074688367442L, (4354407074688367443L, (0L, -1L)))))), - ((-1L, -1L), success((-2L, (0L, (1L, (1L, 0L)))))), - ((-1L, 5665019549505434695L), success((5665019549505434694L, (-5665019549505434696L, (-5665019549505434695L, (0L, -1L)))))), - ((0L, -1L), success((-1L, (1L, (0L, (0L, 0L)))))), - ((0L, 0L), Expected(new ArithmeticException("/ by zero"))), - ((0L, 2112386634269044172L), success((2112386634269044172L, (-2112386634269044172L, (0L, (0L, 0L)))))), - ((2254604056782701370L, -5878231674026236574L), Expected(new ArithmeticException("long overflow"))), - ((2903872550238813643L, 1L), success( - (2903872550238813644L, (2903872550238813642L, (2903872550238813643L, (2903872550238813643L, 0L)))) - )), - ((5091129735284641762L, -427673944382373638L), Expected(new ArithmeticException("long overflow"))), - ((6029085020194630780L, 2261786144956037939L), Expected(new ArithmeticException("long overflow"))), - ((8126382074515995418L, -4746652047588907829L), Expected(new ArithmeticException("long overflow"))), - ((Long.MaxValue, 1L), Expected(new ArithmeticException("long overflow"))), - ((Long.MaxValue, -1L), Expected(new ArithmeticException("long overflow"))) - ) - }, - existingFeature( - { (x: (Long, Long)) => - val a = x._1; val b = x._2 - val plus = n.plus(a, b) - val minus = n.minus(a, b) - val mul = n.times(a, b) - val div = a / b - val mod = a % b - (plus, (minus, (mul, (div, mod)))) + { + def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SLong), 1788, Seq.fill(4)(2116)) + Seq( + ((Long.MinValue, -4677100190307931395L), Expected(new ArithmeticException("long overflow"))), + ((Long.MinValue, -1L), Expected(new ArithmeticException("long overflow"))), + ((Long.MinValue, 1L), Expected(new ArithmeticException("long overflow"))), + ((-9223372036854775808L, 0L), Expected(new ArithmeticException("/ by zero"))), + ((-5828066432064138816L, 9105034716270510411L), Expected(new ArithmeticException("long overflow"))), + ((-4564956247298949325L, -1L), success( + (-4564956247298949326L, (-4564956247298949324L, (4564956247298949325L, (4564956247298949325L, 0L)))) + )), + ((-1499553565058783253L, -3237683216870282569L), Expected(new ArithmeticException("long overflow"))), + ((-1368457031689886112L, 9223372036854775807L), Expected(new ArithmeticException("long overflow"))), + ((-1L, -4354407074688367443L), success((-4354407074688367444L, (4354407074688367442L, (4354407074688367443L, (0L, -1L)))))), + ((-1L, -1L), success((-2L, (0L, (1L, (1L, 0L)))))), + ((-1L, 5665019549505434695L), success((5665019549505434694L, (-5665019549505434696L, (-5665019549505434695L, (0L, -1L)))))), + ((0L, -1L), success((-1L, (1L, (0L, (0L, 0L)))))), + ((0L, 0L), Expected(new ArithmeticException("/ by zero"))), + ((0L, 2112386634269044172L), success((2112386634269044172L, (-2112386634269044172L, (0L, (0L, 0L)))))), + ((2254604056782701370L, -5878231674026236574L), Expected(new ArithmeticException("long overflow"))), + ((2903872550238813643L, 1L), success( + (2903872550238813644L, (2903872550238813642L, (2903872550238813643L, (2903872550238813643L, 0L)))) + )), + ((5091129735284641762L, -427673944382373638L), Expected(new ArithmeticException("long overflow"))), + ((6029085020194630780L, 2261786144956037939L), Expected(new ArithmeticException("long overflow"))), + ((8126382074515995418L, -4746652047588907829L), Expected(new ArithmeticException("long overflow"))), + ((Long.MaxValue, 1L), Expected(new ArithmeticException("long overflow"))), + ((Long.MaxValue, -1L), Expected(new ArithmeticException("long overflow"))) + ) }, - """{ (x: (Long, Long)) => - | val a = x._1; val b = x._2 - | val plus = a + b - | val minus = a - b - | val mul = a * b - | val div = a / b - | val mod = a % b - | (plus, (minus, (mul, (div, mod)))) - |}""".stripMargin, - FuncValue( - Vector((1, STuple(Vector(SLong, SLong)))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 1.toByte) - ), - ValDef( - 4, - List(), - SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 2.toByte) - ) - ), - Tuple( + existingFeature( + { (x: (Long, Long)) => + val a = x._1; + val b = x._2 + val plus = n.plus(a, b) + val minus = n.minus(a, b) + val mul = n.times(a, b) + val div = a / b + val mod = a % b + (plus, (minus, (mul, (div, mod)))) + }, + """{ (x: (Long, Long)) => + | val a = x._1; val b = x._2 + | val plus = a + b + | val minus = a - b + | val mul = a * b + | val div = a / b + | val mod = a % b + | (plus, (minus, (mul, (div, mod)))) + |}""".stripMargin, + FuncValue( + Vector((1, STuple(Vector(SLong, SLong)))), + BlockValue( Vector( - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-102.toByte)), - Tuple( - Vector( - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-103.toByte)), - Tuple( - Vector( - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-100.toByte)), - Tuple( - Vector( - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-99.toByte)), - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-98.toByte)) + ValDef( + 3, + List(), + SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 1.toByte) + ), + ValDef( + 4, + List(), + SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 2.toByte) + ) + ), + Tuple( + Vector( + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-102.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-103.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-100.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-99.toByte)), + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-98.toByte)) + ) ) ) ) @@ -1694,13 +1733,12 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) ) - ) - ))) + ))) } property("Long LT, GT, NEQ") { val o = ExactOrdering.LongIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SLong), 1768) + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SLong), 1768, Seq.fill(4)(2012)) val LT_cases: Seq[((Long, Long), Expected[Boolean])] = Seq( (Long.MinValue, Long.MinValue) -> expect(false), (Long.MinValue, (Long.MinValue + 1).toLong) -> expect(true), @@ -1741,16 +1779,16 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LT_cases, "<", LT.apply)(_ < _) verifyOp( - swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SLong)), + swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SLong), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(_ > _) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost), Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } property("Long LE, GE") { val o = ExactOrdering.LongIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SLong), 1768) + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SLong), 1768, Seq.fill(4)(2012)) val LE_cases: Seq[((Long, Long), Expected[Boolean])] = Seq( (Long.MinValue, Long.MinValue) -> expect(true), (Long.MinValue, (Long.MinValue + 1).toLong) -> expect(true), @@ -1792,14 +1830,14 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LE_cases, "<=", LE.apply)(_ <= _) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SLong)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SLong), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(_ >= _) } property("BigInt methods equivalence") { verifyCases( { - def success(v: BigInt) = Expected(Success(v), 1764, TracedCost(traceBase), 1764) + def success(v: BigInt) = Expected(Success(v), 1764, TracedCost(traceBase), 1764, Seq.fill(4)(1994)) Seq( (CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)), success( CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)) @@ -1820,11 +1858,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val n = NumericOps.BigIntIsExactIntegral verifyCases( - { - def success(v: (BigInt, (BigInt, (BigInt, (BigInt, BigInt))))) = - Expected(Success(v), 1793, arithOpsCostDetails(SBigInt), 1793) - Seq( - ((CBigInt(new BigInteger("-8683d1cd99d5fcf0e6eff6295c285c36526190e13dbde008c49e5ae6fddc1c", 16)), + { + def success(v: (BigInt, (BigInt, (BigInt, (BigInt, BigInt))))) = + Expected(Success(v), 1793, arithOpsCostDetails(SBigInt), 1793, Seq.fill(4)(2121)) + Seq( + ((CBigInt(new BigInteger("-8683d1cd99d5fcf0e6eff6295c285c36526190e13dbde008c49e5ae6fddc1c", 16)), CBigInt(new BigInteger("-2ef55db3f245feddacf0182e299dd", 16))), Expected(new ArithmeticException("BigInteger out of 256 bit range"))), @@ -1946,8 +1984,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("BigInt LT, GT, NEQ") { val o = NumericOps.BigIntIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SBigInt), 1768) - + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SBigInt), 1768, Seq.fill(4)(2012)) val LT_cases: Seq[((BigInt, BigInt), Expected[Boolean])] = Seq( (BigIntMinValue, BigIntMinValue) -> expect(false), (BigIntMinValue, BigIntMinValue.add(1.toBigInt)) -> expect(true), @@ -1990,11 +2027,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LT_cases, "<", LT.apply)(o.lt(_, _)) verifyOp( - swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SBigInt)), + swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SBigInt), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(o.gt(_, _)) val constBigIntCost = Array[CostItem](FixedCostItem(NamedDesc("EQ_BigInt"), FixedCost(JitCost(5)))) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constBigIntCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constBigIntCost), expectedV3Costs = Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } @@ -2005,8 +2042,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val BigIntMaxValue = CBigInt(new BigInteger("7F" + "ff" * 31, 16)) val BigIntOverlimit = CBigInt(new BigInteger("7F" + "ff" * 33, 16)) - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SBigInt), 1768) - + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SBigInt), 1768, Seq.fill(4)(2012)) val LE_cases: Seq[((BigInt, BigInt), Expected[Boolean])] = Seq( (BigIntMinValue, BigIntMinValue) -> expect(true), (BigIntMinValue, BigIntMinValue.add(1.toBigInt)) -> expect(true), @@ -2050,7 +2086,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyOp(LE_cases, "<=", LE.apply)(o.lteq(_, _)) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SBigInt)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SBigInt), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(o.gteq(_, _)) } @@ -2059,12 +2095,12 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => * @param cost the expected cost of `verify` (the same for all cases) */ def verifyNeq[A: Ordering: Arbitrary: RType] - (x: A, y: A, cost: Int, neqCost: Seq[CostItem] = ArraySeq.empty, newCost: Int) + (x: A, y: A, cost: Int, neqCost: Seq[CostItem] = ArraySeq.empty, newCost: Int, expectedV3Costs: Seq[Int]) (copy: A => A, generateCases: Boolean = true) (implicit sampled: Sampled[(A, A)], evalSettings: EvalSettings) = { val copied_x = copy(x) val newCostDetails = if (neqCost.isEmpty) CostDetails.ZeroCost else costNEQ(neqCost) - def expected(v: Boolean) = Expected(Success(v), cost, newCostDetails, newCost) + def expected(v: Boolean) = Expected(Success(v), cost, newCostDetails, newCost, expectedV3Costs) def expectedNoCost(v: Boolean) = new Expected(ExpectedResult(Success(v), None)) verifyOp(Seq( (x, y) -> expected(true), // check cost only for this test case, because the trace depends in x and y @@ -2077,11 +2113,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => } property("NEQ of pre-defined types") { - verifyNeq(ge1, ge2, 1783, Array[CostItem](FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172)))), 1783)(_.asInstanceOf[CGroupElement].copy()) - verifyNeq(t1, t2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_AvlTree"), FixedCost(JitCost(6)))), 1767)(_.asInstanceOf[CAvlTree].copy()) - verifyNeq(b1, b2, 1767, Array[CostItem](), 1767)(_.asInstanceOf[CBox].copy()) - verifyNeq(preH1, preH2, 1766, Array[CostItem](FixedCostItem(NamedDesc("EQ_PreHeader"), FixedCost(JitCost(4)))), 1766)(_.asInstanceOf[CPreHeader].copy()) - verifyNeq(h1, h2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6)))), 1767)(_.asInstanceOf[CHeader].copy()) + verifyNeq(ge1, ge2, 1783, Array[CostItem](FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172)))), 1783, Seq.fill(4)(2027))(_.asInstanceOf[CGroupElement].copy()) + verifyNeq(t1, t2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_AvlTree"), FixedCost(JitCost(6)))), 1767, Seq.fill(4)(2019))(_.asInstanceOf[CAvlTree].copy()) + verifyNeq(b1, b2, 1767, Array[CostItem](), 1767, Seq.fill(4)(2019))(_.asInstanceOf[CBox].copy()) + verifyNeq(preH1, preH2, 1766, Array[CostItem](FixedCostItem(NamedDesc("EQ_PreHeader"), FixedCost(JitCost(4)))), 1766, Seq.fill(4)(2018))(_.asInstanceOf[CPreHeader].copy()) + verifyNeq(h1, h2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6)))), 1767, Seq.fill(4)(2019))(_.asInstanceOf[CHeader].copy()) } property("NEQ of tuples of numerics") { @@ -2089,14 +2125,14 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_Prim"), FixedCost(JitCost(3))) ) - verifyNeq((0.toByte, 1.toByte), (1.toByte, 1.toByte), 1767, tuplesNeqCost, 1767)(_.copy()) - verifyNeq((0.toShort, 1.toByte), (1.toShort, 1.toByte), 1767, tuplesNeqCost, 1767)(_.copy()) - verifyNeq((0, 1.toByte), (1, 1.toByte), 1767, tuplesNeqCost, 1767)(_.copy()) - verifyNeq((0.toLong, 1.toByte), (1.toLong, 1.toByte), 1767, tuplesNeqCost, 1767)(_.copy()) + verifyNeq((0.toByte, 1.toByte), (1.toByte, 1.toByte), 1767, tuplesNeqCost, 1767, Seq.fill(4)(2019))(_.copy()) + verifyNeq((0.toShort, 1.toByte), (1.toShort, 1.toByte), 1767, tuplesNeqCost, 1767, Seq.fill(4)(2029))(_.copy()) + verifyNeq((0, 1.toByte), (1, 1.toByte), 1767, tuplesNeqCost, 1767, Seq.fill(4)(2029))(_.copy()) + verifyNeq((0.toLong, 1.toByte), (1.toLong, 1.toByte), 1767, tuplesNeqCost, 1767, Seq.fill(4)(2029))(_.copy()) verifyNeq((0.toBigInt, 1.toByte), (1.toBigInt, 1.toByte), 1767, Array( FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_BigInt"), FixedCost(JitCost(5))) - ), 1767)(_.copy()) + ), 1767, Seq.fill(4)(2029))(_.copy()) } property("NEQ of tuples of pre-defined types") { @@ -2105,30 +2141,29 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172))), FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172))) ) - verifyNeq((ge1, ge1), (ge1, ge2), 1801, groupNeqCost, 1801)(_.copy()) + verifyNeq((ge1, ge1), (ge1, ge2), 1801, groupNeqCost, 1801, Seq.fill(4)(2053))(_.copy()) val treeNeqCost = Array( FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_AvlTree"), FixedCost(JitCost(6))), FixedCostItem(NamedDesc("EQ_AvlTree"), FixedCost(JitCost(6))) ) - verifyNeq((t1, t1), (t1, t2), 1768, treeNeqCost, 1768)(_.copy()) - - verifyNeq((b1, b1), (b1, b2), 1768, Array[CostItem](), 1768)(_.copy()) + verifyNeq((t1, t1), (t1, t2), 1768, treeNeqCost, 1768, Seq.fill(4)(2038))(_.copy()) + verifyNeq((b1, b1), (b1, b2), 1768, Array[CostItem](), 1768, Seq.fill(4)(2038))(_.copy()) val preHeaderNeqCost = Array( FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_PreHeader"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_PreHeader"), FixedCost(JitCost(4))) ) - verifyNeq((preH1, preH1), (preH1, preH2), 1767, preHeaderNeqCost, 1767)(_.copy()) + verifyNeq((preH1, preH1), (preH1, preH2), 1767, preHeaderNeqCost, 1767, Seq.fill(4)(2037))(_.copy()) val headerNeqCost = Array( FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6))), FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6))) ) - verifyNeq((h1, h1), (h1, h2), 1768, headerNeqCost, 1768)(_.copy()) + verifyNeq((h1, h1), (h1, h2), 1768, headerNeqCost, 1768, Seq.fill(4)(2038))(_.copy()) } property("NEQ of nested tuples") { @@ -2212,15 +2247,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6))), FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6))) ) - verifyNeq((ge1, (t1, t1)), (ge1, (t1, t2)), 1785, nestedTuplesNeqCost1, 1785)(_.copy()) - verifyNeq((ge1, (t1, (b1, b1))), (ge1, (t1, (b1, b2))), 1786, nestedTuplesNeqCost2, 1786)(_.copy()) - verifyNeq((ge1, (t1, (b1, (preH1, preH1)))), (ge1, (t1, (b1, (preH1, preH2)))), 1787, nestedTuplesNeqCost3, 1787)(_.copy()) - verifyNeq((ge1, (t1, (b1, (preH1, (h1, h1))))), (ge1, (t1, (b1, (preH1, (h1, h2))))), 1788, nestedTuplesNeqCost4, 1788)(_.copy()) + verifyNeq((ge1, (t1, t1)), (ge1, (t1, t2)), 1785, nestedTuplesNeqCost1, 1785, Seq.fill(4)(2063))(_.copy()) + verifyNeq((ge1, (t1, (b1, b1))), (ge1, (t1, (b1, b2))), 1786, nestedTuplesNeqCost2, 1786, Seq.fill(4)(2080))(_.copy()) + verifyNeq((ge1, (t1, (b1, (preH1, preH1)))), (ge1, (t1, (b1, (preH1, preH2)))), 1787, nestedTuplesNeqCost3, 1787, Seq.fill(4)(2097))(_.copy()) + verifyNeq((ge1, (t1, (b1, (preH1, (h1, h1))))), (ge1, (t1, (b1, (preH1, (h1, h2))))), 1788, nestedTuplesNeqCost4, 1788, Seq.fill(4)(2114))(_.copy()) - verifyNeq(((ge1, t1), t1), ((ge1, t1), t2), 1785, nestedTuplesNeqCost5, 1785)(_.copy()) - verifyNeq((((ge1, t1), b1), b1), (((ge1, t1), b1), b2), 1786, nestedTuplesNeqCost6, 1786)(_.copy()) - verifyNeq((((ge1, t1), b1), (preH1, preH1)), (((ge1, t1), b1), (preH1, preH2)), 1787, nestedTuplesNeqCost7, 1787)(_.copy()) - verifyNeq((((ge1, t1), b1), (preH1, (h1, h1))), (((ge1, t1), b1), (preH1, (h1, h2))), 1788, nestedTuplesNeqCost8, 1788)(_.copy()) + verifyNeq(((ge1, t1), t1), ((ge1, t1), t2), 1785, nestedTuplesNeqCost5, 1785, Seq.fill(4)(2063))(_.copy()) + verifyNeq((((ge1, t1), b1), b1), (((ge1, t1), b1), b2), 1786, nestedTuplesNeqCost6, 1786, Seq.fill(4)(2080))(_.copy()) + verifyNeq((((ge1, t1), b1), (preH1, preH1)), (((ge1, t1), b1), (preH1, preH2)), 1787, nestedTuplesNeqCost7, 1787, Seq.fill(4)(2097))(_.copy()) + verifyNeq((((ge1, t1), b1), (preH1, (h1, h1))), (((ge1, t1), b1), (preH1, (h1, h2))), 1788, nestedTuplesNeqCost8, 1788, Seq.fill(4)(2114))(_.copy()) } property("NEQ of collections of pre-defined types") { @@ -2232,63 +2267,71 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ast.SeqCostItem(NamedDesc("EQ_COA_Box"), PerItemCost(JitCost(15), JitCost(5), 1), 0) ) implicit val evalSettings = suite.evalSettings.copy(isMeasureOperationTime = false) - verifyNeq(Coll[Byte](), Coll(1.toByte), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Byte](), Coll(1.toByte), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[Byte](0, 1), Coll(1.toByte, 1.toByte), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Byte"), PerItemCost(JitCost(15), JitCost(2), 128), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) - verifyNeq(Coll[Short](), Coll(1.toShort), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Short](), Coll(1.toShort), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[Short](0), Coll(1.toShort), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Short"), PerItemCost(JitCost(15), JitCost(2), 96), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) - verifyNeq(Coll[Int](), Coll(1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Int](), Coll(1), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[Int](0), Coll(1), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Int"), PerItemCost(JitCost(15), JitCost(2), 64), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) - verifyNeq(Coll[Long](), Coll(1.toLong), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Long](), Coll(1.toLong), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[Long](0), Coll(1.toLong), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Long"), PerItemCost(JitCost(15), JitCost(2), 48), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) prepareSamples[Coll[BigInt]] - verifyNeq(Coll[BigInt](), Coll(1.toBigInt), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[BigInt](), Coll(1.toBigInt), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[BigInt](0.toBigInt), Coll(1.toBigInt), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_BigInt"), PerItemCost(JitCost(15), JitCost(7), 5), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) prepareSamples[Coll[GroupElement]] - verifyNeq(Coll[GroupElement](), Coll(ge1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[GroupElement](), Coll(ge1), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[GroupElement](ge1), Coll(ge2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_GroupElement"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) prepareSamples[Coll[AvlTree]] - verifyNeq(Coll[AvlTree](), Coll(t1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[AvlTree](), Coll(t1), 1766, collNeqCost1, 1766, Seq.fill(4)(2028))(cloneColl(_)) + verifyNeq(Coll[AvlTree](t1), Coll(t2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_AvlTree"), PerItemCost(JitCost(15), JitCost(5), 2), 1)), - 1768 + 1768, + Seq.fill(4)(2030) )(cloneColl(_)) { // since SBox.isConstantSize = false, the cost is different among cases @@ -2297,38 +2340,40 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val y = Coll(b1) val copied_x = cloneColl(x) verifyOp(Seq( - (x, x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768), - (x, copied_x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768), - (copied_x, x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768), - (x, y) -> Expected(Success(true), 1766, costNEQ(collNeqCost1), 1766), - (y, x) -> Expected(Success(true), 1766, costNEQ(collNeqCost1), 1766) - ), + (x, x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768, Seq.fill(4)(2030)), + (x, copied_x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768, Seq.fill(4)(2030)), + (copied_x, x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768, Seq.fill(4)(2030)), + (x, y) -> Expected(Success(true), 1766, costNEQ(collNeqCost1), 1766, Seq.fill(4)(2028)), + (y, x) -> Expected(Success(true), 1766, costNEQ(collNeqCost1), 1766, Seq.fill(4)(2028)) + ), "!=", NEQ.apply)(_ != _, generateCases = false) verifyNeq(Coll[Box](b1), Coll(b2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Box"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), - 1768 + 1768, + Seq.fill(4)(2030) )(cloneColl(_), generateCases = false) } prepareSamples[Coll[PreHeader]] - verifyNeq(Coll[PreHeader](), Coll(preH1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[PreHeader](), Coll(preH1), 1766, collNeqCost1, 1766, Seq.fill(4)(2028))(cloneColl(_)) verifyNeq(Coll[PreHeader](preH1), Coll(preH2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_PreHeader"), PerItemCost(JitCost(15), JitCost(3), 1), 1)), - 1768 + 1768, + Seq.fill(4)(2030) )(cloneColl(_)) prepareSamples[Coll[Header]] - verifyNeq(Coll[Header](), Coll(h1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Header](), Coll(h1), 1766, collNeqCost1, 1766, Seq.fill(4)(2028))(cloneColl(_)) verifyNeq(Coll[Header](h1), Coll(h2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Header"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), - 1768 + 1768, Seq.fill(4)(2030) )(cloneColl(_)) } @@ -2352,10 +2397,10 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ast.SeqCostItem(NamedDesc("EQ_COA_Int"), PerItemCost(JitCost(15), JitCost(2), 64), 1), ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) ) - verifyNeq(Coll[Coll[Int]](), Coll(Coll[Int]()), 1766, nestedNeq1, 1766)(cloneColl(_)) - verifyNeq(Coll(Coll[Int]()), Coll(Coll[Int](1)), 1767, nestedNeq2, 1767)(cloneColl(_)) - verifyNeq(Coll(Coll[Int](1)), Coll(Coll[Int](2)), 1769, nestedNeq3, 1769)(cloneColl(_)) - verifyNeq(Coll(Coll[Int](1)), Coll(Coll[Int](1, 2)), 1767, nestedNeq2, 1767)(cloneColl(_)) + verifyNeq(Coll[Coll[Int]](), Coll(Coll[Int]()), 1766, nestedNeq1, 1766, Seq.fill(4)(2018))(cloneColl(_)) + verifyNeq(Coll(Coll[Int]()), Coll(Coll[Int](1)), 1767, nestedNeq2, 1767, Seq.fill(4)(2019))(cloneColl(_)) + verifyNeq(Coll(Coll[Int](1)), Coll(Coll[Int](2)), 1769, nestedNeq3, 1769, Seq.fill(4)(2021))(cloneColl(_)) + verifyNeq(Coll(Coll[Int](1)), Coll(Coll[Int](1, 2)), 1767, nestedNeq2, 1767, Seq.fill(4)(2019))(cloneColl(_)) prepareSamples[Coll[(Int, BigInt)]] prepareSamples[Coll[Coll[(Int, BigInt)]]] @@ -2398,8 +2443,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) ) - verifyNeq(Coll(Coll((1, 10.toBigInt))), Coll(Coll((1, 11.toBigInt))), 1770, nestedNeq4, 1770)(cloneColl(_)) - verifyNeq(Coll(Coll(Coll((1, 10.toBigInt)))), Coll(Coll(Coll((1, 11.toBigInt)))), 1771, nestedNeq5, 1771)(cloneColl(_)) + verifyNeq(Coll(Coll((1, 10.toBigInt))), Coll(Coll((1, 11.toBigInt))), 1770, nestedNeq4, 1770, Seq.fill(4)(2048))(cloneColl(_)) + verifyNeq(Coll(Coll(Coll((1, 10.toBigInt)))), Coll(Coll(Coll((1, 11.toBigInt)))), 1771, nestedNeq5, 1771, Seq.fill(4)(2057))(cloneColl(_)) verifyNeq( (Coll( (Coll( @@ -2413,14 +2458,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ), preH1), 1774, nestedNeq6, - 1774 + 1774, + Seq.fill(4)(2100) )(x => (cloneColl(x._1), x._2)) } property("GroupElement.getEncoded equivalence") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1790, methodCostDetails(SGroupElementMethods.GetEncodedMethod, 250), 1790) + def success[T](v: T) = Expected(Success(v), 1790, methodCostDetails(SGroupElementMethods.GetEncodedMethod, 250), 1790, Seq.fill(4)(2026)) Seq( (ge1, success(Helpers.decodeBytes(ge1str))), (ge2, success(Helpers.decodeBytes(ge2str))), @@ -2451,7 +2497,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172))) ) ) - def success[T](v: T) = Expected(Success(v), 1837, costDetails, 1837) + def success[T](v: T) = Expected(Success(v), 1837, costDetails, 1837, Seq.fill(4)(2081)) Seq( (ge1, success(true)), (ge2, success(true)), @@ -2481,7 +2527,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("GroupElement.negate equivalence") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1785, methodCostDetails(SGroupElementMethods.NegateMethod, 45), 1785) + def success[T](v: T) = Expected(Success(v), 1785, methodCostDetails(SGroupElementMethods.NegateMethod, 45), 1785, Seq.fill(4)(2021)) Seq( (ge1, success(Helpers.decodeGroupElement("02358d53f01276211f92d0aefbd278805121d4ff6eb534b777af1ee8abae5b2056"))), (ge2, success(Helpers.decodeGroupElement("03dba7b94b111f3894e2f9120b577da595ec7d58d488485adf73bf4e153af63575"))), @@ -2499,8 +2545,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => } property("GroupElement.exp equivalence") { - def cases(cost: Int, details: CostDetails) = { - def success[T](v: T) = Expected(Success(v), cost, details, cost) + def cases(cost: Int, details: CostDetails, expectedV3Costs: Seq[Int]) = { + def success[T](v: T) = Expected(Success(v), cost, details, cost, expectedV3Costs) + Seq( ((ge1, CBigInt(new BigInteger("-25c80b560dd7844e2efd10f80f7ee57d", 16))), success(Helpers.decodeGroupElement("023a850181b7b73f92a5bbfa0bfc78f5bbb6ff00645ddde501037017e1a2251e2e"))), @@ -2523,7 +2570,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => FixedCostItem(Exponentiate) ) ) - verifyCases(cases(1873, costDetails), + verifyCases(cases(1873, costDetails, Seq.fill(4)(2121)), existingFeature( scalaFunc, script, @@ -2550,7 +2597,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => FixedCostItem(SGroupElementMethods.ExponentiateMethod, FixedCost(JitCost(900))) ) ) - verifyCases(cases(1873, costDetails), + verifyCases(cases(1873, costDetails, Seq.fill(4)(2121)), existingFeature( scalaFunc, script, @@ -2597,7 +2644,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) ) - def success[T](v: T) = Expected(Success(v), 1787, costDetails, 1787) + def success[T](v: T) = Expected(Success(v), 1787, costDetails, 1787, Seq.fill(4)(2031)) Seq( ((ge1, Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), success(Helpers.decodeGroupElement("02bc48937b4a66f249a32dfb4d2efd0743dc88d46d770b8c5d39fd03325ba211df"))), @@ -2660,7 +2707,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => } verifyCases( { - def success[T](v: T) = Expected(Success(v), 1767, methodCostDetails(SAvlTreeMethods.digestMethod, 15), 1767) + def success[T](v: T) = Expected(Success(v), 1767, methodCostDetails(SAvlTreeMethods.digestMethod, 15), 1767, Seq.fill(4)(2003)) Seq( (t1, success(Helpers.decodeBytes("000183807f66b301530120ff7fc6bd6601ff01ff7f7d2bedbbffff00187fe89094"))), (t2, success(Helpers.decodeBytes("ff000d937f80ffd731ed802d24358001ff8080ff71007f00ad37e0a7ae43fff95b"))), @@ -2673,7 +2720,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.enabledOperationsMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.enabledOperationsMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(6.toByte)), (t2, success(0.toByte)), @@ -2686,7 +2733,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.keyLengthMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.keyLengthMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(1)), (t2, success(32)), @@ -2699,11 +2746,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, methodCostDetails(SAvlTreeMethods.valueLengthOptMethod, 15), newCost) + def success[T](v: T, newCost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), newCost, methodCostDetails(SAvlTreeMethods.valueLengthOptMethod, 15), newCost, expectedV3Costs) Seq( - (t1, success(Some(1), 1766)), - (t2, success(Some(64), 1766)), - (t3, success(None, 1765)) + (t1, success(Some(1), 1766, Seq.fill(4)(2002))), + (t2, success(Some(64), 1766, Seq.fill(4)(2002))), + (t3, success(None, 1765, Seq.fill(4)(2001))) ) }, existingFeature((t: AvlTree) => t.valueLengthOpt, @@ -2712,7 +2759,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isInsertAllowedMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isInsertAllowedMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(false)), (t2, success(false)), @@ -2725,7 +2772,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isUpdateAllowedMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isUpdateAllowedMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(true)), (t2, success(false)), @@ -2738,7 +2785,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isRemoveAllowedMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isRemoveAllowedMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(true)), (t2, success(false)), @@ -2993,7 +3040,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) - getMany.checkExpected(input, Expected(Success(expRes), 1845, costDetails, 1845)) + getMany.checkExpected(input, Expected(Success(expRes), 1845, costDetails, 1845, Seq.fill(4)(2139))) } val key = Colls.fromArray(Array[Byte](-16,-128,99,86,1,-128,-36,-83,109,72,-124,-114,1,-32,15,127,-30,125,127,1,-102,-53,-53,-128,-107,0,64,8,1,127,22,1)) @@ -3071,8 +3118,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => // positive test { val input = (tree, (key, proof)) - contains.checkExpected(input, Expected(Success(okContains), 1790, costDetails(105 + additionalDetails), 1790)) - get.checkExpected(input, Expected(Success(valueOpt), 1790 + additionalCost, costDetails(105 + additionalDetails), 1790 + additionalCost)) + val expectedV3Costs: Seq[Int] = Seq.fill(4)(2082) + contains.checkExpected(input, Expected(Success(okContains), 1790, costDetails(105 + additionalDetails), 1790, expectedV3Costs)) + get.checkExpected(input, Expected(Success(valueOpt), 1790 + additionalCost, costDetails(105 + additionalDetails), 1790 + additionalCost, expectedV3Costs.map(additionalCost + _))) } val keys = Colls.fromItems(key) @@ -3080,14 +3128,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => { val input = (tree, (keys, proof)) - getMany.checkExpected(input, Expected(Success(expRes), 1791 + additionalCost, costDetails(105 + additionalDetails), 1791 + additionalCost)) + val expectedV3Costs: Seq[Int] = Seq.fill(4)(2085 + additionalCost) + getMany.checkExpected(input, Expected(Success(expRes), 1791 + additionalCost, costDetails(105 + additionalDetails), 1791 + additionalCost, expectedV3Costs)) } { val input = (tree, digest) val (res, _) = updateDigest.checkEquality(input).getOrThrow res.digest shouldBe digest - updateDigest.checkExpected(input, Expected(Success(res), 1771, updateDigestCostDetails, 1771)) + updateDigest.checkExpected(input, Expected(Success(res), 1771, updateDigestCostDetails, 1771, Seq.fill(4)(2029))) } val newOps = 1.toByte @@ -3096,7 +3145,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val input = (tree, newOps) val (res,_) = updateOperations.checkEquality(input).getOrThrow res.enabledOperations shouldBe newOps - updateOperations.checkExpected(input, Expected(Success(res), 1771, updateOperationsCostDetails, 1771)) + updateOperations.checkExpected(input, Expected(Success(res), 1771, updateOperationsCostDetails, 1771, Seq.fill(4)(2025))) } // negative tests: invalid proof @@ -3106,7 +3155,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val input = (tree, (key, invalidProof)) val (res, _) = contains.checkEquality(input).getOrThrow res shouldBe false - contains.checkExpected(input, Expected(Success(res), 1790, costDetails(105 + additionalDetails), 1790)) + contains.checkExpected(input, Expected(Success(res), 1790, costDetails(105 + additionalDetails), 1790, Seq.fill(4)(2082))) } { @@ -3259,7 +3308,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val input = (preInsertTree, (kvs, insertProof)) val (res, _) = insert.checkEquality(input).getOrThrow res.isDefined shouldBe true - insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796)) + insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796, Seq.fill(4)(2102))) } { // negative: readonly tree @@ -3267,7 +3316,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val input = (readonlyTree, (kvs, insertProof)) val (res, _) = insert.checkEquality(input).getOrThrow res.isDefined shouldBe false - insert.checkExpected(input, Expected(Success(res), 1772, costDetails1, 1772)) + insert.checkExpected(input, Expected(Success(res), 1772, costDetails1, 1772, Seq.fill(4)(2078))) } { // negative: invalid key @@ -3277,7 +3326,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val input = (tree, (invalidKvs, insertProof)) val (res, _) = insert.checkEquality(input).getOrThrow res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908) - insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796)) + insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796, Seq.fill(4)(2102))) } { // negative: invalid proof @@ -3407,7 +3456,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val endTree = preUpdateTree.updateDigest(endDigest) val input = (preUpdateTree, (kvs, updateProof)) val res = Some(endTree) - update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805)) + update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805, Seq.fill(4)(2111))) } { // positive: update to the same value (identity operation) @@ -3415,13 +3464,13 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val keys = Colls.fromItems((key -> value)) val input = (tree, (keys, updateProof)) val res = Some(tree) - update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805)) + update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805, Seq.fill(4)(2111))) } { // negative: readonly tree val readonlyTree = createTree(preUpdateDigest) val input = (readonlyTree, (kvs, updateProof)) - update.checkExpected(input, Expected(Success(None), 1772, costDetails1, 1772)) + update.checkExpected(input, Expected(Success(None), 1772, costDetails1, 1772, Seq.fill(4)(2078))) } { // negative: invalid key @@ -3429,7 +3478,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val invalidKey = key.map(x => (-x).toByte) // any other different from key val invalidKvs = Colls.fromItems((invalidKey -> newValue)) val input = (tree, (invalidKvs, updateProof)) - update.checkExpected(input, Expected(Success(None), 1801, costDetails3, 1801)) + update.checkExpected(input, Expected(Success(None), 1801, costDetails3, 1801, Seq.fill(4)(2107))) } { // negative: invalid value (different from the value in the proof) @@ -3438,15 +3487,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val invalidKvs = Colls.fromItems((key -> invalidValue)) val input = (tree, (invalidKvs, updateProof)) val (res, _) = update.checkEquality(input).getOrThrow - res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908) - update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805)) + res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908) + update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805, Seq.fill(4)(2111))) } { // negative: invalid proof val tree = createTree(preUpdateDigest, updateAllowed = true) val invalidProof = updateProof.map(x => (-x).toByte) // any other different from proof val input = (tree, (kvs, invalidProof)) - update.checkExpected(input, Expected(Success(None), 1801, costDetails3, 1801)) + update.checkExpected(input, Expected(Success(None), 1801, costDetails3, 1801, Seq.fill(4)(2107))) } } @@ -3557,7 +3606,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val endTree = preRemoveTree.updateDigest(endDigest) val input = (preRemoveTree, (Colls.fromArray(keysToRemove), removeProof)) val res = Some(endTree) - remove.checkExpected(input, Expected(Success(res), 1832, costDetails1, 1832)) + remove.checkExpected(input, Expected(Success(res), 1832, costDetails1, 1832, Seq.fill(4)(2126))) } { @@ -3574,13 +3623,13 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val endTree = preRemoveTree.updateDigest(endDigest) val input = (preRemoveTree, (keys, removeProof)) val res = Some(endTree) - remove.checkExpected(input, Expected(Success(res), 1806, costDetails2, 1806)) + remove.checkExpected(input, Expected(Success(res), 1806, costDetails2, 1806, Seq.fill(4)(2100))) } { // negative: readonly tree val readonlyTree = createTree(preRemoveDigest) val input = (readonlyTree, (keys, removeProof)) - remove.checkExpected(input, Expected(Success(None), 1772, costDetails3, 1772)) + remove.checkExpected(input, Expected(Success(None), 1772, costDetails3, 1772, Seq.fill(4)(2066))) } { // negative: invalid key @@ -3588,14 +3637,14 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val invalidKey = key.map(x => (-x).toByte) // any other different from `key` val invalidKeys = Colls.fromItems(invalidKey) val input = (tree, (invalidKeys, removeProof)) - remove.checkExpected(input, Expected(Success(None), 1802, costDetails4, 1802)) + remove.checkExpected(input, Expected(Success(None), 1802, costDetails4, 1802, Seq.fill(4)(2096))) } { // negative: invalid proof val tree = createTree(preRemoveDigest, removeAllowed = true) val invalidProof = removeProof.map(x => (-x).toByte) // any other different from `removeProof` val input = (tree, (keys, invalidProof)) - remove.checkExpected(input, Expected(Success(None), 1802, costDetails4, 1802)) + remove.checkExpected(input, Expected(Success(None), 1802, costDetails4, 1802, Seq.fill(4)(2096))) } } } @@ -3604,7 +3653,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(LongToByteArray), FixedCost(JitCost(17)))) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767) + def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767, Seq.fill(4)(1999)) Seq( (-9223372036854775808L, success(Helpers.decodeBytes("8000000000000000"))), (-1148502660425090565L, success(Helpers.decodeBytes("f00fb2ea55c579fb"))), @@ -3624,20 +3673,20 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ByteArrayToBigInt), FixedCost(JitCost(30)))) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767) + def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767, Seq.fill(4)(1999)) Seq( (Helpers.decodeBytes(""), Expected(new NumberFormatException("Zero length BigInteger"))), (Helpers.decodeBytes("00"), - success(CBigInt(new BigInteger("0", 16)))), + success(CBigInt(new BigInteger("0", 16)))), (Helpers.decodeBytes("01"), - success(CBigInt(new BigInteger("1", 16)))), + success(CBigInt(new BigInteger("1", 16)))), (Helpers.decodeBytes("ff"), - success(CBigInt(new BigInteger("-1", 16)))), + success(CBigInt(new BigInteger("-1", 16)))), (Helpers.decodeBytes("80d6c201"), - Expected(Success(CBigInt(new BigInteger("-7f293dff", 16))), 1767, costDetails, 1767)), + Expected(Success(CBigInt(new BigInteger("-7f293dff", 16))), 1767, costDetails, 1767, Seq.fill(4)(1999))), (Helpers.decodeBytes("70d6c20170d6c201"), - Expected(Success(CBigInt(new BigInteger("70d6c20170d6c201", 16))), 1767, costDetails, 1767)), + Expected(Success(CBigInt(new BigInteger("70d6c20170d6c201", 16))), 1767, costDetails, 1767, Seq.fill(4)(1999))), (Helpers.decodeBytes( "80e0ff7f02807fff72807f0a00ff7fb7c57f75c11ba2802970fd250052807fc37f6480ffff007fff18eeba44" ), Expected(new ArithmeticException("BigInteger out of 256 bit range"))) @@ -3652,7 +3701,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ByteArrayToLong), FixedCost(JitCost(16)))) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, costDetails, 1765) + def success[T](v: T) = Expected(Success(v), 1765, costDetails, 1765, Seq.fill(4)(1997)) Seq( (Helpers.decodeBytes(""), Expected(new IllegalArgumentException("array too small: 0 < 8"))), (Helpers.decodeBytes("81"), Expected(new IllegalArgumentException("array too small: 1 < 8"))), @@ -3674,7 +3723,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractId), FixedCost(JitCost(12)))) - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (b1, success(Helpers.decodeBytes("5ee78f30ae4e770e44900a46854e9fecb6b12e8112556ef1cd19aef633b4421e"))), (b2, success(Helpers.decodeBytes("3a0089be265460e29ca47d26e5b55a6f3e3ffaf5b4aed941410a2437913848ad"))) @@ -3687,7 +3736,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractAmount), FixedCost(JitCost(8)))) - def success[T](v: T) = Expected(Success(v), 1764, costDetails, 1764) + def success[T](v: T) = Expected(Success(v), 1764, costDetails, 1764, Seq.fill(4)(1996)) Seq( (b1, success(9223372036854775807L)), (b2, success(12345L)) @@ -3700,7 +3749,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractScriptBytes), FixedCost(JitCost(10)))) - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (b1, success(Helpers.decodeBytes( "100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e7300" @@ -3715,7 +3764,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractBytes), FixedCost(JitCost(12)))) - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (b1, success(Helpers.decodeBytes( "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080bcb001" @@ -3732,7 +3781,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractBytesWithNoRef), FixedCost(JitCost(12)))) - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (b1, success(Helpers.decodeBytes( "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff" @@ -3749,7 +3798,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractCreationInfo), FixedCost(JitCost(16)))) - def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767) + def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767, Seq.fill(4)(2001)) Seq( (b1, success(( 677407, @@ -3772,8 +3821,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => b1 -> Expected(Success(Coll[(Coll[Byte], Long)]( (Helpers.decodeBytes("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001"), 10000000L), (Helpers.decodeBytes("a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600"), 500L) - ).map(identity)), 1772, methodCostDetails(SBoxMethods.tokensMethod, 15), 1772), - b2 -> Expected(Success(Coll[(Coll[Byte], Long)]().map(identity)), 1766, methodCostDetails(SBoxMethods.tokensMethod, 15), 1766) + ).map(identity)), 1772, methodCostDetails(SBoxMethods.tokensMethod, 15), 1772, Seq.fill(4)(2012)), + b2 -> Expected(Success(Coll[(Coll[Byte], Long)]().map(identity)), 1766, methodCostDetails(SBoxMethods.tokensMethod, 15), 1766, Seq.fill(4)(2006)) ), existingFeature({ (x: Box) => x.tokens }, "{ (x: Box) => x.tokens }", @@ -3842,11 +3891,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (box1, Expected(Success(1024.toShort), 1774, expCostDetails1, 1774)), + (box1, Expected(Success(1024.toShort), 1774, expCostDetails1, 1774, Seq.fill(4)(2042))), (box2, Expected( new InvalidType("Cannot getReg[Short](5): invalid type of value TestValue(1048576) at id=5") )), - (box3, Expected(Success(0.toShort), 1772, expCostDetails2, 1772)) + (box3, Expected(Success(0.toShort), 1772, expCostDetails2, 1772, Seq.fill(4)(2040))) ), existingFeature( { (x: Box) => @@ -3971,10 +4020,10 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (box1, Expected(Success(1024), cost = 1785, expCostDetails3, 1785)), - (box2, Expected(Success(1024 * 1024), cost = 1786, expCostDetails4, 1786)), - (box3, Expected(Success(0), cost = 1779, expCostDetails5, 1779)), - (box4, Expected(Success(-1), cost = 1772, expCostDetails2, 1772)) + (box1, Expected(Success(1024), cost = 1785, expCostDetails3, 1785, Seq.fill(4)(2129))), + (box2, Expected(Success(1024 * 1024), cost = 1786, expCostDetails4, 1786, Seq.fill(4)(2130))), + (box3, Expected(Success(0), cost = 1779, expCostDetails5, 1779, Seq.fill(4)(2123))), + (box4, Expected(Success(-1), cost = 1772, expCostDetails2, 1772, Seq.fill(4)(2116))) ), existingFeature( { (x: Box) => @@ -4068,7 +4117,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( Seq( - (box1, Expected(Success(1.toByte), cost = 1770, expCostDetails, 1770)), + (box1, Expected(Success(1.toByte), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))), (box2, Expected(new InvalidType("Cannot getReg[Byte](4): invalid type of value Value(Coll(1)) at id=4"))) ), existingFeature((x: Box) => x.R4[Byte].get, @@ -4080,7 +4129,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (box1, Expected(Success(1024.toShort), cost = 1770, expCostDetails, 1770)), + (box1, Expected(Success(1024.toShort), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))), (box2, Expected(new NoSuchElementException("None.get"))) ), existingFeature((x: Box) => x.R5[Short].get, @@ -4092,7 +4141,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (box1, Expected(Success(1024 * 1024), cost = 1770, expCostDetails, 1770)) + (box1, Expected(Success(1024 * 1024), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))) ), existingFeature((x: Box) => x.R6[Int].get, "{ (x: Box) => x.R6[Int].get }", @@ -4103,7 +4152,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (box1, Expected(Success(1024.toLong), cost = 1770, expCostDetails, 1770)) + (box1, Expected(Success(1024.toLong), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))) ), existingFeature((x: Box) => x.R7[Long].get, "{ (x: Box) => x.R7[Long].get }", @@ -4114,7 +4163,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( - (box1, Expected(Success(CBigInt(BigInteger.valueOf(222L))), cost = 1770, expCostDetails, 1770)) + (box1, Expected(Success(CBigInt(BigInteger.valueOf(222L))), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))) ), existingFeature((x: Box) => x.R8[BigInt].get, "{ (x: Box) => x.R8[BigInt].get }", @@ -4133,10 +4182,10 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => None ) )), - cost = 1770, - expCostDetails, - 1770) - )), + cost = 1770, + expCostDetails, + 1770, Seq.fill(4)(2008)) + )), existingFeature((x: Box) => x.R9[AvlTree].get, "{ (x: Box) => x.R9[AvlTree].get }", FuncValue( @@ -4208,35 +4257,35 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( - Seq((preH1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SPreHeaderMethods.versionMethod, 10), 1765))), + Seq((preH1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SPreHeaderMethods.versionMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("version", { (x: PreHeader) => x.version })) verifyCases( Seq((preH1, Expected(Success( Helpers.decodeBytes("7fff7fdd6f62018bae0001006d9ca888ff7f56ff8006573700a167f17f2c9f40")), - cost = 1766, methodCostDetails(SPreHeaderMethods.parentIdMethod, 10), 1766))), + cost = 1766, methodCostDetails(SPreHeaderMethods.parentIdMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("parentId", { (x: PreHeader) => x.parentId })) verifyCases( - Seq((preH1, Expected(Success(6306290372572472443L), cost = 1765, methodCostDetails(SPreHeaderMethods.timestampMethod, 10), 1765))), + Seq((preH1, Expected(Success(6306290372572472443L), cost = 1765, methodCostDetails(SPreHeaderMethods.timestampMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("timestamp", { (x: PreHeader) => x.timestamp })) verifyCases( - Seq((preH1, Expected(Success(-3683306095029417063L), cost = 1765, methodCostDetails(SPreHeaderMethods.nBitsMethod, 10), 1765))), + Seq((preH1, Expected(Success(-3683306095029417063L), cost = 1765, methodCostDetails(SPreHeaderMethods.nBitsMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("nBits", { (x: PreHeader) => x.nBits })) verifyCases( - Seq((preH1, Expected(Success(1), cost = 1765, methodCostDetails(SPreHeaderMethods.heightMethod, 10), 1765))), + Seq((preH1, Expected(Success(1), cost = 1765, methodCostDetails(SPreHeaderMethods.heightMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("height", { (x: PreHeader) => x.height })) verifyCases( Seq((preH1, Expected(Success( Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b")), - cost = 1782, methodCostDetails(SPreHeaderMethods.minerPkMethod, 10), 1782))), + cost = 1782, methodCostDetails(SPreHeaderMethods.minerPkMethod, 10), 1782, Seq.fill(4)(2018)))), existingPropTest("minerPk", { (x: PreHeader) => x.minerPk })) verifyCases( - Seq((preH1, Expected(Success(Helpers.decodeBytes("ff8087")), cost = 1766, methodCostDetails(SPreHeaderMethods.votesMethod,10), 1766))), + Seq((preH1, Expected(Success(Helpers.decodeBytes("ff8087")), cost = 1766, methodCostDetails(SPreHeaderMethods.votesMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("votes", { (x: PreHeader) => x.votes })) } @@ -4244,81 +4293,81 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("957f008001808080ffe4ffffc8f3802401df40006aa05e017fa8d3f6004c804a")), - cost = 1766, methodCostDetails(SHeaderMethods.idMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.idMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("id", { (x: Header) => x.id })) verifyCases( - Seq((h1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SHeaderMethods.versionMethod, 10), 1765))), + Seq((h1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SHeaderMethods.versionMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("version", { (x: Header) => x.version })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("0180dd805b0000ff5400b997fd7f0b9b00de00fb03c47e37806a8186b94f07ff")), - cost = 1766, methodCostDetails(SHeaderMethods.parentIdMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.parentIdMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("parentId", { (x: Header) => x.parentId })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("01f07f60d100ffb970c3007f60ff7f24d4070bb8fffa7fca7f34c10001ffe39d")), - cost = 1766, methodCostDetails(SHeaderMethods.ADProofsRootMethod, 10), 1766))), - existingPropTest("ADProofsRoot", { (x: Header) => x.ADProofsRoot})) + cost = 1766, methodCostDetails(SHeaderMethods.ADProofsRootMethod, 10), 1766, Seq.fill(4)(2002)))), + existingPropTest("ADProofsRoot", { (x: Header) => x.ADProofsRoot })) verifyCases( - Seq((h1, Expected(Success(CAvlTree(createAvlTreeData())), cost = 1765, methodCostDetails(SHeaderMethods.stateRootMethod, 10), 1765))), + Seq((h1, Expected(Success(CAvlTree(createAvlTreeData())), cost = 1765, methodCostDetails(SHeaderMethods.stateRootMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("stateRoot", { (x: Header) => x.stateRoot })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("804101ff01000080a3ffbd006ac080098df132a7017f00649311ec0e00000100")), - cost = 1766, methodCostDetails(SHeaderMethods.transactionsRootMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.transactionsRootMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("transactionsRoot", { (x: Header) => x.transactionsRoot })) verifyCases( - Seq((h1, Expected(Success(1L), cost = 1765, methodCostDetails(SHeaderMethods.timestampMethod, 10), 1765))), + Seq((h1, Expected(Success(1L), cost = 1765, methodCostDetails(SHeaderMethods.timestampMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("timestamp", { (x: Header) => x.timestamp })) verifyCases( - Seq((h1, Expected(Success(-1L), cost = 1765, methodCostDetails(SHeaderMethods.nBitsMethod, 10), 1765))), + Seq((h1, Expected(Success(-1L), cost = 1765, methodCostDetails(SHeaderMethods.nBitsMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("nBits", { (x: Header) => x.nBits })) verifyCases( - Seq((h1, Expected(Success(1), cost = 1765, methodCostDetails(SHeaderMethods.heightMethod, 10), 1765))), + Seq((h1, Expected(Success(1), cost = 1765, methodCostDetails(SHeaderMethods.heightMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("height", { (x: Header) => x.height })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("e57f80885601b8ff348e01808000bcfc767f2dd37f0d01015030ec018080bc62")), - cost = 1766, methodCostDetails(SHeaderMethods.extensionRootMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.extensionRootMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("extensionRoot", { (x: Header) => x.extensionRoot })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeGroupElement("039bdbfa0b49cc6bef58297a85feff45f7bbeb500a9d2283004c74fcedd4bd2904")), - cost = 1782, methodCostDetails(SHeaderMethods.minerPkMethod, 10), 1782))), + cost = 1782, methodCostDetails(SHeaderMethods.minerPkMethod, 10), 1782, Seq.fill(4)(2018)))), existingPropTest("minerPk", { (x: Header) => x.minerPk })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeGroupElement("0361299207fa392231e23666f6945ae3e867b978e021d8d702872bde454e9abe9c")), - cost = 1782, methodCostDetails(SHeaderMethods.powOnetimePkMethod, 10), 1782))), + cost = 1782, methodCostDetails(SHeaderMethods.powOnetimePkMethod, 10), 1782, Seq.fill(4)(2018)))), existingPropTest("powOnetimePk", { (x: Header) => x.powOnetimePk })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("7f4f09012a807f01")), - cost = 1766, methodCostDetails(SHeaderMethods.powNonceMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.powNonceMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("powNonce", { (x: Header) => x.powNonce })) verifyCases( Seq((h1, Expected(Success( CBigInt(new BigInteger("-e24990c47e15ed4d0178c44f1790cc72155d516c43c3e8684e75db3800a288", 16))), - cost = 1765, methodCostDetails(SHeaderMethods.powDistanceMethod, 10), 1765))), + cost = 1765, methodCostDetails(SHeaderMethods.powDistanceMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("powDistance", { (x: Header) => x.powDistance })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("7f0180")), - cost = 1766, methodCostDetails(SHeaderMethods.votesMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.votesMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("votes", { (x: Header) => x.votes })) } @@ -4532,7 +4581,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val costDetails = TracedCost(testTraceBase) verifyCases( Seq( - (ctx, Expected(Success(dataBox), cost = 1769, costDetails, 1769)), + (ctx, Expected(Success(dataBox), cost = 1769, costDetails, 1769, Seq.fill(4)(2017))), (ctx.copy(_dataInputs = Coll()), Expected(new ArrayIndexOutOfBoundsException("0"))) ), existingFeature({ (x: Context) => x.dataInputs(0) }, @@ -4557,7 +4606,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Seq( (ctx, Expected(Success( Helpers.decodeBytes("7da4b55971f19a78d007638464580f91a020ab468c0dbe608deb1f619e245bc3")), - cost = 1772, idCostDetails, 1772)) + cost = 1772, idCostDetails, 1772, Seq.fill(4)(2022))) ), existingFeature({ (x: Context) => x.dataInputs(0).id }, "{ (x: Context) => x.dataInputs(0).id }", @@ -4618,7 +4667,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) verifyCases( - Seq(ctx -> Expected(Success(ctx.HEIGHT), cost = 1766, heightCostDetails, 1766)), + Seq(ctx -> Expected(Success(ctx.HEIGHT), cost = 1766, heightCostDetails, 1766, Seq.fill(4)(1994))), existingFeature( { (x: Context) => x.HEIGHT }, "{ (x: Context) => x.HEIGHT }", @@ -4655,7 +4704,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => )) verifyCases( - Seq((ctx, Expected(Success(Coll[Long](80946L)), cost = 1770, inputsCostDetails1, 1770))), + Seq((ctx, Expected(Success(Coll[Long](80946L)), cost = 1770, inputsCostDetails1, 1770, Seq.fill(4)(2014)))), existingFeature( { (x: Context) => x.INPUTS.map { (b: Box) => b.value } }, "{ (x: Context) => x.INPUTS.map { (b: Box) => b.value } }", @@ -4715,7 +4764,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) verifyCases( - Seq((ctx, Expected(Success(Coll((80946L, 80946L))), cost = 1774, inputsCostDetails2, 1774))), + Seq((ctx, Expected(Success(Coll((80946L, 80946L))), cost = 1774, inputsCostDetails2, 1774, Seq.fill(4)(2042)))), existingFeature( { (x: Context) => x.INPUTS.map { (b: Box) => (b.value, b.value) } }, """{ (x: Context) => @@ -4807,11 +4856,20 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => expectedDetails = CostDetails.ZeroCost, newCost = 1766, newVersionedResults = { + val expectedV3Costs = Seq.fill(4)(2002) + // V3 activation will have different costs due to deserialization cost + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + expectedV3Costs + } else { + Seq.fill(4)(1766) + } val res = (ExpectedResult(Success(0), Some(1766)) -> Some(selfCostDetails)) - Seq(0, 1, 2, 3).map(version => version -> res) + Seq(0, 1, 2, 3).map(version => version -> (ExpectedResult(Success(0), Some(costs(version))) -> Some(selfCostDetails))) })) ), - changedFeature({ (x: Context) => x.selfBoxIndex }, + changedFeature( + changedInVersion = VersionContext.JitActivationVersion, + { (x: Context) => x.selfBoxIndex }, { (x: Context) => x.selfBoxIndex }, // see versioning in selfBoxIndex implementation "{ (x: Context) => x.selfBoxIndex }", FuncValue( @@ -4837,7 +4895,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => } verifyCases( - Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash), cost = 1766, methodCostDetails(SContextMethods.lastBlockUtxoRootHashMethod, 15), 1766)), + Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash), cost = 1766, methodCostDetails(SContextMethods.lastBlockUtxoRootHashMethod, 15), 1766, Seq.fill(4)(2002))), existingPropTest("LastBlockUtxoRootHash", { (x: Context) => x.LastBlockUtxoRootHash }), preGeneratedSamples = Some(samples)) @@ -4850,7 +4908,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) verifyCases( - Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash.isUpdateAllowed), cost = 1767, isUpdateAllowedCostDetails, 1767)), + Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash.isUpdateAllowed), cost = 1767, isUpdateAllowedCostDetails, 1767, Seq.fill(4)(2009))), existingFeature( { (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }, "{ (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }", @@ -4871,7 +4929,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => preGeneratedSamples = Some(samples)) verifyCases( - Seq(ctx -> Expected(Success(ctx.minerPubKey), cost = 1767, methodCostDetails(SContextMethods.minerPubKeyMethod, 20), 1767)), + Seq(ctx -> Expected(Success(ctx.minerPubKey), cost = 1767, methodCostDetails(SContextMethods.minerPubKeyMethod, 20), 1767, Seq.fill(4)(2003))), existingPropTest("minerPubKey", { (x: Context) => x.minerPubKey }), preGeneratedSamples = Some(samples)) @@ -4911,7 +4969,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => FixedCostItem(GetVar), FixedCostItem(OptionGet))) verifyCases( - Seq((ctx, Expected(Success(true), cost = 1765, getVarCostDetails, 1765))), + Seq((ctx, Expected(Success(true), cost = 1765, getVarCostDetails, 1765, Seq.fill(4)(1999)))), existingFeature((x: Context) => x.getVar[Boolean](11).get, "{ (x: Context) => getVar[Boolean](11).get }", FuncValue(Vector((1, SContext)), OptionGet(GetVar(11.toByte, SOption(SBoolean))))), @@ -4945,7 +5003,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( Seq( - ctx -> Expected(Success(-135729055492651903L), 1779, registerIsDefinedCostDetails, 1779) + ctx -> Expected(Success(-135729055492651903L), 1779, registerIsDefinedCostDetails, 1779, Seq.fill(4)(2065)) ), existingFeature( { (x: Context) => @@ -5007,11 +5065,19 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Seq( ctx -> Expected(Failure(expectedError), 0, CostDetails.ZeroCost, 1793, newVersionedResults = { - Seq.tabulate(4)(v => v -> (ExpectedResult(Success(true), Some(1793)) -> None)) + val expectedV3Costs = Seq.fill(4)(2121) + // V3 activation will have different costs due to deserialization cost + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + expectedV3Costs + } else { + Seq.fill(4)(1793) + } + Seq.tabulate(4)(v => v -> (ExpectedResult(Success(true), Some(costs(v))) -> None)) } ) ), changedFeature( + changedInVersion = VersionContext.JitActivationVersion, scalaFunc = { (x: Context) => // this error is expected in v3.x, v4.x throw expectedError @@ -5173,12 +5239,12 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( Seq( - ctx -> Expected(Success(5008366408131208436L), 1791, registerTagCostDetails1, 1791), + ctx -> Expected(Success(5008366408131208436L), 1791, registerTagCostDetails1, 1791, Seq.fill(4)(2153)), ctxWithRegsInOutput(ctx, Map( ErgoBox.R5 -> LongConstant(0L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(10L), 1790, registerTagCostDetails2, 1790), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(10L), 1790, registerTagCostDetails2, 1790, Seq.fill(4)(2152)), ctxWithRegsInOutput(ctx, Map( - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1777, registerTagCostDetails3, 1777) + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1777, registerTagCostDetails3, 1777, Seq.fill(4)(2139)) ), existingFeature( { (x: Context) => @@ -5381,22 +5447,22 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( // case 1L - ctx -> Expected(Success(5008366408131289382L), 1794, tagRegisterCostDetails1, 1794), + ctx -> Expected(Success(5008366408131289382L), 1794, tagRegisterCostDetails1, 1794, Seq.fill(4)(2168)), // case 0L ctxWithRegsInOutput(ctx, Map( ErgoBox.R5 -> LongConstant(0L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(80956L), 1793, tagRegisterCostDetails2, 1793), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(80956L), 1793, tagRegisterCostDetails2, 1793, Seq.fill(4)(2167)), // case returning 0L ctxWithRegsInOutput(ctx, Map( ErgoBox.R5 -> LongConstant(2L), // note R4 is required to avoid // "RuntimeException: Set of non-mandatory indexes is not densely packed" - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1784, tagRegisterCostDetails3, 1784), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1784, tagRegisterCostDetails3, 1784, Seq.fill(4)(2158)), // case returning -1L ctxWithRegsInOutput(ctx, Map( - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1777, tagRegisterCostDetails4, 1777) + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1777, tagRegisterCostDetails4, 1777, Seq.fill(4)(2151)) ), existingFeature( { (x: Context) => @@ -5614,15 +5680,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Seq( ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(1L), - ErgoBox.R4 -> LongConstant(10))) -> Expected(Success(10L), 1792, tagRegisterCostDetails1, 1792), + ErgoBox.R4 -> LongConstant(10))) -> Expected(Success(10L), 1792, tagRegisterCostDetails1, 1792, Seq.fill(4)(2162)), ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(0L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(10L), 1791, tagRegisterCostDetails2, 1791), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(10L), 1791, tagRegisterCostDetails2, 1791, Seq.fill(4)(2161)), ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(2L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1786, tagRegisterCostDetails3, 1786), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1786, tagRegisterCostDetails3, 1786, Seq.fill(4)(2156)), ctxWithRegsInDataInput(ctx, Map( - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1779, tagRegisterCostDetails4, 1779) + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1779, tagRegisterCostDetails4, 1779, Seq.fill(4)(2149)) ), existingFeature( { (x: Context) => @@ -5847,15 +5913,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Seq( ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(1L), - ErgoBox.R4 -> LongConstant(10))) -> Expected(Success(80956L), 1796, costDetails1, 1796), + ErgoBox.R4 -> LongConstant(10))) -> Expected(Success(80956L), 1796, costDetails1, 1796, Seq.fill(4)(2178)), ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(0L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(80956L), 1794, costDetails2, 1794), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(80956L), 1794, costDetails2, 1794, Seq.fill(4)(2176)), ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(2L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1786, costDetails3, 1786), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1786, costDetails3, 1786, Seq.fill(4)(2168)), ctxWithRegsInDataInput(ctx, Map( - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1779, costDetails4, 1779) + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1779, costDetails4, 1779, Seq.fill(4)(2161)) ), existingFeature( { (x: Context) => @@ -5960,7 +6026,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => cost = c, expectedDetails = CostDetails.ZeroCost, newCost = 1766, - newVersionedResults = Seq(0, 1, 2, 3).map(i => i -> (ExpectedResult(Success(newV), Some(1766)) -> Some(cd))) + newVersionedResults = { + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(1998) + } + else { + Seq.fill(4)(1766) + } + Seq.tabulate(4)(i => i -> (ExpectedResult(Success(newV), Some(costs(i))) -> Some(cd))) + } ) Seq( (Coll[Boolean](), successNew(false, 1766, newV = false, costDetails(0))), @@ -5985,6 +6059,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) }, changedFeature( + changedInVersion = VersionContext.JitActivationVersion, (x: Coll[Boolean]) => SigmaDsl.xorOf(x), (x: Coll[Boolean]) => SigmaDsl.xorOf(x), "{ (x: Coll[Boolean]) => xorOf(x) }", @@ -5995,8 +6070,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val costDetails = TracedCost(traceBase :+ FixedCostItem(LogicalNot)) verifyCases( Seq( - (true, Expected(Success(false), 1765, costDetails, 1765)), - (false, Expected(Success(true), 1765, costDetails, 1765))), + (true, Expected(Success(false), 1765, costDetails, 1765, Seq.fill(4)(1997))), + (false, Expected(Success(true), 1765, costDetails, 1765, Seq.fill(4)(1997)))), existingFeature((x: Boolean) => !x, "{ (x: Boolean) => !x }", FuncValue(Vector((1, SBoolean)), LogicalNot(ValUse(1, SBoolean))))) @@ -6006,7 +6081,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val costDetails = TracedCost(traceBase :+ FixedCostItem(Negation)) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) + Seq( (Byte.MinValue, success(Byte.MinValue)), // !!! ((Byte.MinValue + 1).toByte, success(Byte.MaxValue)), @@ -6025,7 +6101,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (Short.MinValue, success(Short.MinValue)), // special case! ((Short.MinValue + 1).toShort, success(Short.MaxValue)), @@ -6043,7 +6119,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) + Seq( (Int.MinValue, success(Int.MinValue)), // special case! (Int.MinValue + 1, success(Int.MaxValue)), @@ -6060,7 +6137,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) + Seq( (Long.MinValue, success(Long.MinValue)), // special case! (Long.MinValue + 1, success(Long.MaxValue)), @@ -6077,7 +6155,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767) + def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767, Seq.fill(4)(1999)) + Seq( (CBigInt(new BigInteger("-1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)), success(CBigInt(new BigInteger("1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)))), (CBigInt(new BigInteger("-1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)), success(CBigInt(new BigInteger("1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)))), @@ -6115,7 +6194,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1782, costDetails, 1782) + def success[T](v: T) = Expected(Success(v), 1782, costDetails, 1782, Seq.fill(4)(2016)) Seq( (-1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), (1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))) @@ -6134,7 +6213,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T) = Expected(Success(v), 1782, costDetails, 1782) + def success[T](v: T) = Expected(Success(v), 1782, costDetails, 1782, Seq.fill(4)(2016)) Seq( (-1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), (1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))) @@ -6159,37 +6238,38 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) verifyCases( - { - def success[T](v: T) = Expected(Success(v), 1872, expCostDetails, 1872) - Seq( - (CBigInt(new BigInteger("-e5c1a54694c85d644fa30a6fc5f3aa209ed304d57f72683a0ebf21038b6a9d", 16)), success(Helpers.decodeGroupElement("023395bcba3d7cf21d73c50f8af79d09a8c404c15ce9d04f067d672823bae91a54"))), - (CBigInt(new BigInteger("-bc2d08f935259e0eebf272c66c6e1dbd484c6706390215", 16)), success(Helpers.decodeGroupElement("02ddcf4c48105faf3c16f7399b5dbedd82ab0bb50ae292d8f88f49a3f86e78974e"))), - (CBigInt(new BigInteger("-35cbe9a7a652e5fe85f735ee9909fdd8", 16)), success(Helpers.decodeGroupElement("03b110ec9c7a8c20ed873818e976a0e96e5a17be979d3422d59b362de2a3ae043e"))), - (CBigInt(new BigInteger("-3f05ffca6bd4b15c", 16)), success(Helpers.decodeGroupElement("02acf2657d0714cef8d65ae15c362faa09c0934c0bce872a23398e564c090b85c8"))), - (CBigInt(new BigInteger("-80000001", 16)), success(Helpers.decodeGroupElement("0391b418fd1778356ce947a5cbb46539fd29842aea168486fae91fc5317177a575"))), - (CBigInt(new BigInteger("-80000000", 16)), success(Helpers.decodeGroupElement("025318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), - (CBigInt(new BigInteger("-1", 16)), success(Helpers.decodeGroupElement("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (CBigInt(new BigInteger("0", 16)), success(Helpers.decodeGroupElement("000000000000000000000000000000000000000000000000000000000000000000"))), - (CBigInt(new BigInteger("1", 16)), success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (CBigInt(new BigInteger("80000000", 16)), success(Helpers.decodeGroupElement("035318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), - (CBigInt(new BigInteger("1251b7fcd8a01e95", 16)), success(Helpers.decodeGroupElement("030fde7238b8dddfafab8f5481dc17b880505d6bacbe3cdf2ce975afdcadf66354"))), - (CBigInt(new BigInteger("12f6bd76d8fe1d035bdb14bf2f696e52", 16)), success(Helpers.decodeGroupElement("028f2ccf13669461cb3cfbea281e2db08fbb67b38493a1628855203d3f69b82763"))), - (CBigInt(new BigInteger("102bb404f5e36bdba004fdefa34df8cfa02e7912f3caf79", 16)), success(Helpers.decodeGroupElement("03ce82f431d115d45ad555084f8b2861ce5c4561d154e931e9f778594896e46a25")))) - }, - existingFeature({ (n: BigInt) => SigmaDsl.groupGenerator.exp(n) }, - "{ (n: BigInt) => groupGenerator.exp(n) }", - FuncValue( - Vector((1, SBigInt)), - Exponentiate( - MethodCall.typed[Value[SGroupElement.type]]( - Global, - SGlobalMethods.getMethodByName("groupGenerator"), - Vector(), - Map() - ), - ValUse(1, SBigInt) - ) - ))) + { + def success[T](v: T) = Expected(Success(v), 1872, expCostDetails, 1872, Seq.fill(4)(2112)) + + Seq( + (CBigInt(new BigInteger("-e5c1a54694c85d644fa30a6fc5f3aa209ed304d57f72683a0ebf21038b6a9d", 16)), success(Helpers.decodeGroupElement("023395bcba3d7cf21d73c50f8af79d09a8c404c15ce9d04f067d672823bae91a54"))), + (CBigInt(new BigInteger("-bc2d08f935259e0eebf272c66c6e1dbd484c6706390215", 16)), success(Helpers.decodeGroupElement("02ddcf4c48105faf3c16f7399b5dbedd82ab0bb50ae292d8f88f49a3f86e78974e"))), + (CBigInt(new BigInteger("-35cbe9a7a652e5fe85f735ee9909fdd8", 16)), success(Helpers.decodeGroupElement("03b110ec9c7a8c20ed873818e976a0e96e5a17be979d3422d59b362de2a3ae043e"))), + (CBigInt(new BigInteger("-3f05ffca6bd4b15c", 16)), success(Helpers.decodeGroupElement("02acf2657d0714cef8d65ae15c362faa09c0934c0bce872a23398e564c090b85c8"))), + (CBigInt(new BigInteger("-80000001", 16)), success(Helpers.decodeGroupElement("0391b418fd1778356ce947a5cbb46539fd29842aea168486fae91fc5317177a575"))), + (CBigInt(new BigInteger("-80000000", 16)), success(Helpers.decodeGroupElement("025318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), + (CBigInt(new BigInteger("-1", 16)), success(Helpers.decodeGroupElement("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (CBigInt(new BigInteger("0", 16)), success(Helpers.decodeGroupElement("000000000000000000000000000000000000000000000000000000000000000000"))), + (CBigInt(new BigInteger("1", 16)), success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (CBigInt(new BigInteger("80000000", 16)), success(Helpers.decodeGroupElement("035318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), + (CBigInt(new BigInteger("1251b7fcd8a01e95", 16)), success(Helpers.decodeGroupElement("030fde7238b8dddfafab8f5481dc17b880505d6bacbe3cdf2ce975afdcadf66354"))), + (CBigInt(new BigInteger("12f6bd76d8fe1d035bdb14bf2f696e52", 16)), success(Helpers.decodeGroupElement("028f2ccf13669461cb3cfbea281e2db08fbb67b38493a1628855203d3f69b82763"))), + (CBigInt(new BigInteger("102bb404f5e36bdba004fdefa34df8cfa02e7912f3caf79", 16)), success(Helpers.decodeGroupElement("03ce82f431d115d45ad555084f8b2861ce5c4561d154e931e9f778594896e46a25")))) + }, + existingFeature({ (n: BigInt) => SigmaDsl.groupGenerator.exp(n) }, + "{ (n: BigInt) => groupGenerator.exp(n) }", + FuncValue( + Vector((1, SBigInt)), + Exponentiate( + MethodCall.typed[Value[SGroupElement.type]]( + Global, + SGlobalMethods.getMethodByName("groupGenerator"), + Vector(), + Map() + ), + ValUse(1, SBigInt) + ) + ))) } } @@ -6225,7 +6305,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1769, cd, 1769) + def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1769, cd, 1769, Seq.fill(4)(2021)) Seq( ((Helpers.decodeBytes(""), Helpers.decodeBytes("")), success(Helpers.decodeBytes(""), costDetails(0))), ((Helpers.decodeBytes("01"), Helpers.decodeBytes("01")), success(Helpers.decodeBytes("00"), costDetails(1))), @@ -6236,9 +6316,16 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => cost = 0, expectedDetails = CostDetails.ZeroCost, newCost = 1769, - newVersionedResults = { - val res = (ExpectedResult(Success(Helpers.decodeBytes("00")), Some(1769)), Some(costDetails(1))) - Seq(0, 1, 2, 3).map(version => version -> res) + newVersionedResults = { + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2021) + } else { + Seq.fill(4)(1769) + } + Seq.tabulate(4) { version => + val res = (ExpectedResult(Success(Helpers.decodeBytes("00")), Some(costs(version))), Some(costDetails(1))) + version -> res + } } )), ((Helpers.decodeBytes("800136fe89afff802acea67128a0ff007fffe3498c8001806080012b"), @@ -6247,6 +6334,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) }, changedFeature( + changedInVersion = VersionContext.JitActivationVersion, (x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), (x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), "{ (x: (Coll[Byte], Coll[Byte])) => xor(x._1, x._2) }", @@ -6380,11 +6468,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( { - def success[T](v: T, c: Int, costDetails: CostDetails, newCost: Int) = Expected(Success(v), c, costDetails, newCost) + def success[T](v: T, c: Int, costDetails: CostDetails, newCost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), c, costDetails, newCost, expectedV3Costs) Seq( - (Coll[Box](), success(Coll[Box](), 1767, costDetails, 1767)), - (Coll[Box](b1), success(Coll[Box](), 1772, costDetails2, 1772)), - (Coll[Box](b1, b2), success(Coll[Box](b2), 1776, costDetails3, 1776)) + (Coll[Box](), success(Coll[Box](), 1767, costDetails, 1767, Seq.fill(4)(2031))), + (Coll[Box](b1), success(Coll[Box](), 1772, costDetails2, 1772, Seq.fill(4)(2036))), + (Coll[Box](b1, b2), success(Coll[Box](b2), 1776, costDetails3, 1776, Seq.fill(4)(2040))) ) }, existingFeature({ (x: Coll[Box]) => x.filter({ (b: Box) => b.value > 1 }) }, @@ -6437,13 +6525,13 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { Seq( - (Coll[Box](), Expected(Success(Coll[Byte]()), 1773, costDetails1, 1773)), + (Coll[Box](), Expected(Success(Coll[Byte]()), 1773, costDetails1, 1773, Seq.fill(4)(2029))), (Coll[Box](b1), Expected(Success(Helpers.decodeBytes( "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd5" - )), 1791, costDetails2, 1791)), + )), 1791, costDetails2, 1791, Seq.fill(4)(2047))), (Coll[Box](b1, b2), Expected(Success(Helpers.decodeBytes( "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd500d197830201010096850200" - )), 1795, costDetails3, 1795)) + )), 1795, costDetails3, 1795, Seq.fill(4)(2051))) ) }, existingFeature({ (x: Coll[Box]) => x.flatMap({ (b: Box) => b.propositionBytes }) }, @@ -6476,11 +6564,12 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T, c: Int, cd: CostDetails, nc: Int) = Expected(Success(v), c, cd, nc) + def success[T](v: T, c: Int, cd: CostDetails, nc: Int, v3Costs: Seq[Int]) = Expected(Success(v), c, cd, nc, v3Costs) + Seq( - (Coll[Box](), success(Coll[(Box, Box)](), 1766, costDetails(0), 1766)), - (Coll[Box](b1), success(Coll[(Box, Box)]((b1, b1)), 1768, costDetails(1), 1768)), - (Coll[Box](b1, b2), success(Coll[(Box, Box)]((b1, b1), (b2, b2)), 1770, costDetails(2), 1770)) + (Coll[Box](), success(Coll[(Box, Box)](), 1766, costDetails(0), 1766, Seq.fill(4)(2018))), + (Coll[Box](b1), success(Coll[(Box, Box)]((b1, b1)), 1768, costDetails(1), 1768, Seq.fill(4)(2020))), + (Coll[Box](b1, b2), success(Coll[(Box, Box)]((b1, b1), (b2, b2)), 1770, costDetails(2), 1770, Seq.fill(4)(2022))) ) }, existingFeature({ (x: Coll[Box]) => x.zip(x) }, @@ -6506,7 +6595,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val costDetails = TracedCost(traceBase :+ FixedCostItem(SizeOf)) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, costDetails, 1765) + def success[T](v: T) = Expected(Success(v), 1765, costDetails, 1765, Seq.fill(4)(2001)) Seq( (Coll[Box](), success(0)), (Coll[Box](b1), success(1)), @@ -6531,7 +6620,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( { - def success[T](v: T, i: Int) = Expected(Success(v), 1768, costDetails(i), 1768) + def success[T](v: T, i: Int) = Expected(Success(v), 1768, costDetails(i), 1768, Seq.fill(4)(2008)) Seq( (Coll[Box](), success(Coll[Int](), 0)), (Coll[Box](b1), success(Coll[Int](0), 1)), @@ -6587,9 +6676,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) def cases = { Seq( - (Coll[Box](), Expected(Success(true), 1764, costDetails1, 1764)), - (Coll[Box](b1), Expected(Success(false), 1769, costDetails2, 1769)), - (Coll[Box](b1, b2), Expected(Success(false), 1769, costDetails3, 1769)) + (Coll[Box](), Expected(Success(true), 1764, costDetails1, 1764, Seq.fill(4)(2026))), + (Coll[Box](b1), Expected(Success(false), 1769, costDetails2, 1769, Seq.fill(4)(2031))), + (Coll[Box](b1, b2), Expected(Success(false), 1769, costDetails3, 1769, Seq.fill(4)(2031))) ) } if (lowerMethodCallsInTests) { @@ -6666,9 +6755,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { Seq( - (Coll[Box](), Expected(Success(false), 1764, costDetails1, 1764)), - (Coll[Box](b1), Expected(Success(false), 1769, costDetails2, 1769)), - (Coll[Box](b1, b2), Expected(Success(true), 1773, costDetails3, 1773)) + (Coll[Box](), Expected(Success(false), 1764, costDetails1, 1764, Seq.fill(4)(2026))), + (Coll[Box](b1), Expected(Success(false), 1769, costDetails2, 1769, Seq.fill(4)(2031))), + (Coll[Box](b1, b2), Expected(Success(true), 1773, costDetails3, 1773, Seq.fill(4)(2035))) ) }, existingFeature({ (x: Coll[Box]) => x.exists({ (b: Box) => b.value > 1 }) }, @@ -6759,11 +6848,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { Seq( - (Coll[BigInt](), Expected(Success(false), 1764, costDetails1, 1764)), - (Coll[BigInt](BigIntZero), Expected(Success(false), 1769, costDetails2, 1769)), - (Coll[BigInt](BigIntOne), Expected(Success(true), 1772, costDetails3, 1772)), - (Coll[BigInt](BigIntZero, BigIntOne), Expected(Success(true), 1777, costDetails4, 1777)), - (Coll[BigInt](BigIntZero, BigInt10), Expected(Success(false), 1777, costDetails4, 1777)) + (Coll[BigInt](), Expected(Success(false), 1764, costDetails1, 1764, Seq.fill(4)(2048))), + (Coll[BigInt](BigIntZero), Expected(Success(false), 1769, costDetails2, 1769, Seq.fill(4)(2053))), + (Coll[BigInt](BigIntOne), Expected(Success(true), 1772, costDetails3, 1772, Seq.fill(4)(2056))), + (Coll[BigInt](BigIntZero, BigIntOne), Expected(Success(true), 1777, costDetails4, 1777, Seq.fill(4)(2061))), + (Coll[BigInt](BigIntZero, BigInt10), Expected(Success(false), 1777, costDetails4, 1777, Seq.fill(4)(2061))) ) }, existingFeature( @@ -6874,11 +6963,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { Seq( - (Coll[BigInt](), Expected(Success(true), 1764, costDetails1, 1764)), - (Coll[BigInt](BigIntMinusOne), Expected(Success(false), 1769, costDetails2, 1769)), - (Coll[BigInt](BigIntOne), Expected(Success(true), 1772, costDetails3, 1772)), - (Coll[BigInt](BigIntZero, BigIntOne), Expected(Success(true), 1779, costDetails4, 1779)), - (Coll[BigInt](BigIntZero, BigInt11), Expected(Success(false), 1779, costDetails4, 1779)) + (Coll[BigInt](), Expected(Success(true), 1764, costDetails1, 1764, Seq.fill(4)(2048))), + (Coll[BigInt](BigIntMinusOne), Expected(Success(false), 1769, costDetails2, 1769, Seq.fill(4)(2053))), + (Coll[BigInt](BigIntOne), Expected(Success(true), 1772, costDetails3, 1772, Seq.fill(4)(2056))), + (Coll[BigInt](BigIntZero, BigIntOne), Expected(Success(true), 1779, costDetails4, 1779, Seq.fill(4)(2063))), + (Coll[BigInt](BigIntZero, BigInt11), Expected(Success(false), 1779, costDetails4, 1779, Seq.fill(4)(2063))) ) }, existingFeature( @@ -7008,28 +7097,34 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T, c: Int, cd: CostDetails, newCost: Int) = Expected(Success(v), c, cd, newCost) + def success[T](v: T, c: Int, cd: CostDetails, newCost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), c, cd, newCost, expectedV3Costs) + Seq( - Coll[GroupElement]() -> Expected(Success(Coll[Byte]()), 1773, CostDetails.ZeroCost, 1773, - newVersionedResults = { - val res = ExpectedResult(Success(Coll[Byte]()), Some(1773)) - Seq.tabulate(3)(v => - v -> (res -> Some(costDetails0)) - ) - }), + Coll[GroupElement]() -> Expected(Success(Coll[Byte]()), 1773, CostDetails.ZeroCost, 1773, + newVersionedResults = { + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2029) + } + else { + Seq.fill(4)(1773) + } + Seq.tabulate(4)(v => + v -> (ExpectedResult(Success(Coll[Byte]()), Some(costs(v))) -> Some(costDetails0)) + ) + }), Coll[GroupElement]( Helpers.decodeGroupElement("02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee587"), Helpers.decodeGroupElement("0390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa")) -> - success(Helpers.decodeBytes( - "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa" - ), 1834, costDetails2, 1834), + success(Helpers.decodeBytes( + "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa" + ), 1834, costDetails2, 1834, Seq.fill(4)(2090)), Coll[GroupElement]( Helpers.decodeGroupElement("02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee587"), Helpers.decodeGroupElement("0390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa"), Helpers.decodeGroupElement("03bd839b969b02d218fd1192f2c80cbda9c6ce9c7ddb765f31b748f4666203df85")) -> - success(Helpers.decodeBytes( - "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa03bd839b969b02d218fd1192f2c80cbda9c6ce9c7ddb765f31b748f4666203df85" - ), 1864, costDetails3, 1864) + success(Helpers.decodeBytes( + "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa03bd839b969b02d218fd1192f2c80cbda9c6ce9c7ddb765f31b748f4666203df85" + ), 1864, costDetails3, 1864, Seq.fill(4)(2120)) ) }, existingFeature( @@ -7067,11 +7162,17 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) -> Expected(res, 1840, expectedDetails = CostDetails.ZeroCost, newCost = 1840, - newVersionedResults = (0 to 2).map(version => + newVersionedResults = (0 to 3).map({ version => + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2104) + } + else { + Seq.fill(4)(1840) + } // successful result for each version version -> (ExpectedResult(res, - verificationCost = Some(1840)) -> Some(costDetails4)) - )) + verificationCost = Some(costs(version))) -> Some(costDetails4)) + })) ) } val f = existingFeature( @@ -7137,7 +7238,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1776, cd, 1776) + def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1776, cd, 1776, Seq.fill(4)(2072)) Seq( ((Coll[Int](), (0, 0)), success(Coll[Int](), costDetails(0))), ((Coll[Int](1), (0, 0)), success(Coll[Int](1, 1), costDetails(2))), @@ -7226,7 +7327,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( // (coll, (index, elem)) { - def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1774, cd, 1774) + def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1774, cd, 1774, Seq.fill(4)(2058)) Seq( ((Coll[Int](), (0, 0)), Expected(new IndexOutOfBoundsException("0"))), ((Coll[Int](1), (0, 0)), success(Coll[Int](0), costDetails(1))), @@ -7303,7 +7404,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( // (coll, (indexes, values)) { - def success[T](v: T, i: Int) = Expected(Success(v), 1774, costDetails(i), 1774) + def success[T](v: T, i: Int) = Expected(Success(v), 1774, costDetails(i), 1774, Seq.fill(4)(2066)) Seq( ((Coll[Int](), (Coll(0), Coll(0))), Expected(new IndexOutOfBoundsException("0"))), ((Coll[Int](), (Coll(0, 1), Coll(0, 0))), Expected(new IndexOutOfBoundsException("0"))), @@ -7450,15 +7551,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => // (coll, initState) { Seq( - ((Coll[Byte](), 0), Expected(Success(0), 1767, costDetails1, 1767)), - ((Coll[Byte](), Int.MaxValue), Expected(Success(Int.MaxValue), 1767, costDetails1, 1767)), - ((Coll[Byte](1), Int.MaxValue - 1), Expected(Success(Int.MaxValue), 1773, costDetails2, 1773)), - ((Coll[Byte](1), Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), - ((Coll[Byte](-1), Int.MinValue + 1), Expected(Success(Int.MinValue), 1773, costDetails2, 1773)), - ((Coll[Byte](-1), Int.MinValue), Expected(new ArithmeticException("integer overflow"))), - ((Coll[Byte](1, 2), 0), Expected(Success(3), 1779, costDetails3, 1779)), - ((Coll[Byte](1, -1), 0), Expected(Success(0), 1779, costDetails3, 1779)), - ((Coll[Byte](1, -1, 1), 0), Expected(Success(1), 1785, costDetails4, 1785)) + ((Coll[Byte](), 0), Expected(Success(0), 1767, costDetails1, 1767, Seq.fill(4)(2049))), + ((Coll[Byte](), Int.MaxValue), Expected(Success(Int.MaxValue), 1767, costDetails1, 1767, Seq.fill(4)(2049))), + ((Coll[Byte](1), Int.MaxValue - 1), Expected(Success(Int.MaxValue), 1773, costDetails2, 1773, Seq.fill(4)(2055))), + ((Coll[Byte](1), Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), + ((Coll[Byte](-1), Int.MinValue + 1), Expected(Success(Int.MinValue), 1773, costDetails2, 1773, Seq.fill(4)(2055))), + ((Coll[Byte](-1), Int.MinValue), Expected(new ArithmeticException("integer overflow"))), + ((Coll[Byte](1, 2), 0), Expected(Success(3), 1779, costDetails3, 1779, Seq.fill(4)(2061))), + ((Coll[Byte](1, -1), 0), Expected(Success(0), 1779, costDetails3, 1779, Seq.fill(4)(2061))), + ((Coll[Byte](1, -1, 1), 0), Expected(Success(1), 1785, costDetails4, 1785, Seq.fill(4)(2067))) ) }, existingFeature( @@ -7711,15 +7812,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => // (coll, initState) { Seq( - ((Coll[Byte](), 0), Expected(Success(0), 1767, costDetails1, 1767)), - ((Coll[Byte](), Int.MaxValue), Expected(Success(Int.MaxValue), 1767, costDetails1, 1767)), - ((Coll[Byte](1), Int.MaxValue - 1), Expected(Success(Int.MaxValue), 1779, costDetails2, 1779)), - ((Coll[Byte](1), Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), - ((Coll[Byte](-1), Int.MinValue + 1), Expected(Success(Int.MinValue + 1), 1777, costDetails3, 1777)), - ((Coll[Byte](-1), Int.MinValue), Expected(Success(Int.MinValue), 1777, costDetails3, 1777)), - ((Coll[Byte](1, 2), 0), Expected(Success(3), 1791, costDetails4, 1791)), - ((Coll[Byte](1, -1), 0), Expected(Success(1), 1789, costDetails5, 1789)), - ((Coll[Byte](1, -1, 1), 0), Expected(Success(2), 1801, costDetails6, 1801)) + ((Coll[Byte](), 0), Expected(Success(0), 1767, costDetails1, 1767, Seq.fill(4)(2089))), + ((Coll[Byte](), Int.MaxValue), Expected(Success(Int.MaxValue), 1767, costDetails1, 1767, Seq.fill(4)(2089))), + ((Coll[Byte](1), Int.MaxValue - 1), Expected(Success(Int.MaxValue), 1779, costDetails2, 1779, Seq.fill(4)(2101))), + ((Coll[Byte](1), Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), + ((Coll[Byte](-1), Int.MinValue + 1), Expected(Success(Int.MinValue + 1), 1777, costDetails3, 1777, Seq.fill(4)(2099))), + ((Coll[Byte](-1), Int.MinValue), Expected(Success(Int.MinValue), 1777, costDetails3, 1777, Seq.fill(4)(2099))), + ((Coll[Byte](1, 2), 0), Expected(Success(3), 1791, costDetails4, 1791, Seq.fill(4)(2113))), + ((Coll[Byte](1, -1), 0), Expected(Success(1), 1789, costDetails5, 1789, Seq.fill(4)(2111))), + ((Coll[Byte](1, -1, 1), 0), Expected(Success(2), 1801, costDetails6, 1801, Seq.fill(4)(2123))) ) }, existingFeature( @@ -7830,11 +7931,16 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( // (coll, (elem: Byte, from: Int)) { - def success0[T](v: T) = Expected(Success(v), 1773, costDetails(0), 1773) - def success1[T](v: T) = Expected(Success(v), 1773, costDetails(1), 1773) - def success2[T](v: T) = Expected(Success(v), 1774, costDetails(2), 1774) - def success3[T](v: T) = Expected(Success(v), 1775, costDetails(3), 1775) - def success12[T](v: T) = Expected(Success(v), 1782, costDetails(12), 1782) + def success0[T](v: T) = Expected(Success(v), 1773, costDetails(0), 1773, Seq.fill(4)(2061)) + + def success1[T](v: T) = Expected(Success(v), 1773, costDetails(1), 1773, Seq.fill(4)(2061)) + + def success2[T](v: T) = Expected(Success(v), 1774, costDetails(2), 1774, Seq.fill(4)(2062)) + + def success3[T](v: T) = Expected(Success(v), 1775, costDetails(3), 1775, Seq.fill(4)(2063)) + + def success12[T](v: T) = Expected(Success(v), 1782, costDetails(12), 1782, Seq.fill(4)(2070)) + Seq( ((Coll[Byte](), (0.toByte, 0)), success0(-1)), ((Coll[Byte](), (0.toByte, -1)), success0(-1)), @@ -7896,7 +8002,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1769, costDetails, 1769) + def success[T](v: T) = Expected(Success(v), 1769, costDetails, 1769, Seq.fill(4)(2019)) + Seq( ((Coll[Int](), 0), Expected(new ArrayIndexOutOfBoundsException("0"))), ((Coll[Int](), -1), Expected(new ArrayIndexOutOfBoundsException("-1"))), @@ -7960,7 +8067,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( // (coll, (index, default)) { - def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773) + def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773, Seq.fill(4)(2053)) Seq( ((Coll[Int](), (0, default)), success(default)), ((Coll[Int](), (-1, default)), success(default)), @@ -8043,7 +8150,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1763, costDetails, 1763) + def success[T](v: T) = Expected(Success(v), 1763, costDetails, 1763, Seq.fill(4)(1997)) Seq( ((0, 0), success(2)), ((1, 2), success(2)) @@ -8058,7 +8165,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val samples = genSamples[(Int, Int)](DefaultMinSuccessful) val costDetails = TracedCost(traceBase :+ FixedCostItem(SelectField)) verifyCases( - Seq(((1, 2), Expected(Success(1), cost = 1764, costDetails, 1764))), + Seq(((1, 2), Expected(Success(1), cost = 1764, costDetails, 1764, Seq.fill(4)(1998)))), existingFeature((x: (Int, Int)) => x._1, "{ (x: (Int, Int)) => x(0) }", FuncValue( @@ -8067,7 +8174,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => )), preGeneratedSamples = Some(samples)) verifyCases( - Seq(((1, 2), Expected(Success(2), cost = 1764, costDetails, 1764))), + Seq(((1, 2), Expected(Success(2), cost = 1764, costDetails, 1764, Seq.fill(4)(1998)))), existingFeature((x: (Int, Int)) => x._2, "{ (x: (Int, Int)) => x(1) }", FuncValue( @@ -8109,9 +8216,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => { def success[T](v: T, c: Int) = Expected(Success(v), c) Seq( - (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768)), - (Coll[Int](1), Expected(Success(Coll[Int](2)), 1771, costDetails(1), 1771)), - (Coll[Int](1, 2), Expected(Success(Coll[Int](2, 3)), 1774, costDetails(2), 1774)), + (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768, Seq.fill(4)(2022))), + (Coll[Int](1), Expected(Success(Coll[Int](2)), 1771, costDetails(1), 1771, Seq.fill(4)(2025))), + (Coll[Int](1, 2), Expected(Success(Coll[Int](2, 3)), 1774, costDetails(2), 1774, Seq.fill(4)(2028))), (Coll[Int](1, 2, Int.MaxValue), Expected(new ArithmeticException("integer overflow"))) ) }, @@ -8205,10 +8312,10 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => { def success[T](v: T, c: Int) = Expected(Success(v), c) Seq( - (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails1, 1768)), - (Coll[Int](1), Expected(Success(Coll[Int](2)), 1775, costDetails2, 1775)), - (Coll[Int](-1), Expected(Success(Coll[Int](1)), 1775, costDetails3, 1775)), - (Coll[Int](1, -2), Expected(Success(Coll[Int](2, 2)), 1782, costDetails4, 1782)), + (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails1, 1768, Seq.fill(4)(2054))), + (Coll[Int](1), Expected(Success(Coll[Int](2)), 1775, costDetails2, 1775, Seq.fill(4)(2061))), + (Coll[Int](-1), Expected(Success(Coll[Int](1)), 1775, costDetails3, 1775, Seq.fill(4)(2061))), + (Coll[Int](1, -2), Expected(Success(Coll[Int](2, 2)), 1782, costDetails4, 1782, Seq.fill(4)(2068))), (Coll[Int](1, 2, Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), (Coll[Int](1, 2, Int.MinValue), Expected(new ArithmeticException("integer overflow"))) ) @@ -8256,24 +8363,24 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val o = ExactOrdering.IntIsExactOrdering verifyCases( - { - Seq( - (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768)), - (Coll[Int](1), Expected(Success(Coll[Int](1)), 1771, costDetails(1), 1771)), - (Coll[Int](1, 2), Expected(Success(Coll[Int](1, 2)), 1775, costDetails(2), 1775)), - (Coll[Int](1, 2, -1), Expected(Success(Coll[Int](1, 2)), 1778, costDetails(3), 1778)), - (Coll[Int](1, -1, 2, -2), Expected(Success(Coll[Int](1, 2)), 1782, costDetails(4), 1782)) - ) - }, - existingFeature((x: Coll[Int]) => x.filter({ (v: Int) => o.gt(v, 0) }), - "{ (x: Coll[Int]) => x.filter({ (v: Int) => v > 0 }) }", - FuncValue( - Array((1, SCollectionType(SInt))), - Filter( - ValUse(1, SCollectionType(SInt)), - FuncValue(Array((3, SInt)), GT(ValUse(3, SInt), IntConstant(0))) + { + Seq( + (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768, Seq.fill(4)(2022))), + (Coll[Int](1), Expected(Success(Coll[Int](1)), 1771, costDetails(1), 1771, Seq.fill(4)(2025))), + (Coll[Int](1, 2), Expected(Success(Coll[Int](1, 2)), 1775, costDetails(2), 1775, Seq.fill(4)(2029))), + (Coll[Int](1, 2, -1), Expected(Success(Coll[Int](1, 2)), 1778, costDetails(3), 1778, Seq.fill(4)(2032))), + (Coll[Int](1, -1, 2, -2), Expected(Success(Coll[Int](1, 2)), 1782, costDetails(4), 1782, Seq.fill(4)(2036))) ) - ))) + }, + existingFeature((x: Coll[Int]) => x.filter({ (v: Int) => o.gt(v, 0) }), + "{ (x: Coll[Int]) => x.filter({ (v: Int) => v > 0 }) }", + FuncValue( + Array((1, SCollectionType(SInt))), + Filter( + ValUse(1, SCollectionType(SInt)), + FuncValue(Array((3, SInt)), GT(ValUse(3, SInt), IntConstant(0))) + ) + ))) } property("Coll filter with nested If") { @@ -8331,29 +8438,30 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val o = ExactOrdering.IntIsExactOrdering verifyCases( - { - def success[T](v: T, c: Int) = Expected(Success(v), c) - Seq( - (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768)), - (Coll[Int](1), Expected(Success(Coll[Int](1)), 1775, costDetails(1), 1775)), - (Coll[Int](10), Expected(Success(Coll[Int]()), 1775, costDetails(1), 1775)), - (Coll[Int](1, 2), Expected(Success(Coll[Int](1, 2)), 1783, costDetails(2), 1783)), - (Coll[Int](1, 2, 0), Expected(Success(Coll[Int](1, 2)), 1788, costDetails3, 1788)), - (Coll[Int](1, -1, 2, -2, 11), Expected(Success(Coll[Int](1, 2)), 1800, costDetails5, 1800)) - ) - }, - existingFeature((x: Coll[Int]) => x.filter({ (v: Int) => if (o.gt(v, 0)) v < 10 else false }), - "{ (x: Coll[Int]) => x.filter({ (v: Int) => if (v > 0) v < 10 else false }) }", - FuncValue( - Array((1, SCollectionType(SInt))), - Filter( - ValUse(1, SCollectionType(SInt)), - FuncValue( - Array((3, SInt)), - If(GT(ValUse(3, SInt), IntConstant(0)), LT(ValUse(3, SInt), IntConstant(10)), FalseLeaf) - ) + { + def success[T](v: T, c: Int) = Expected(Success(v), c) + + Seq( + (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768, Seq.fill(4)(2048))), + (Coll[Int](1), Expected(Success(Coll[Int](1)), 1775, costDetails(1), 1775, Seq.fill(4)(2055))), + (Coll[Int](10), Expected(Success(Coll[Int]()), 1775, costDetails(1), 1775, Seq.fill(4)(2055))), + (Coll[Int](1, 2), Expected(Success(Coll[Int](1, 2)), 1783, costDetails(2), 1783, Seq.fill(4)(2063))), + (Coll[Int](1, 2, 0), Expected(Success(Coll[Int](1, 2)), 1788, costDetails3, 1788, Seq.fill(4)(2068))), + (Coll[Int](1, -1, 2, -2, 11), Expected(Success(Coll[Int](1, 2)), 1800, costDetails5, 1800, Seq.fill(4)(2080))) ) - ))) + }, + existingFeature((x: Coll[Int]) => x.filter({ (v: Int) => if (o.gt(v, 0)) v < 10 else false }), + "{ (x: Coll[Int]) => x.filter({ (v: Int) => if (v > 0) v < 10 else false }) }", + FuncValue( + Array((1, SCollectionType(SInt))), + Filter( + ValUse(1, SCollectionType(SInt)), + FuncValue( + Array((3, SInt)), + If(GT(ValUse(3, SInt), IntConstant(0)), LT(ValUse(3, SInt), IntConstant(10)), FalseLeaf) + ) + ) + ))) } property("Coll slice method equivalence") { @@ -8380,48 +8488,49 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val samples = genSamples(collWithRangeGen, DefaultMinSuccessful) if (lowerMethodCallsInTests) { verifyCases( - { - val cost = 1772 - val newCost = 1772 - Seq( - // (coll, (from, until)) - ((Coll[Int](), (-1, 0)), Expected(Success(Coll[Int]()), cost, costDetails(1), newCost)), - ((Coll[Int](), (0, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1), (0, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1), (0, -1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1), (1, 1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1), (-1, 1)), Expected(Success(Coll[Int](1)), cost, costDetails(2), newCost)), - ((Coll[Int](1, 2), (1, 1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1, 2), (1, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1, 2), (1, 2)), Expected(Success(Coll[Int](2)), cost, costDetails(1), newCost)), - ((Coll[Int](1, 2, 3, 4), (1, 3)), Expected(Success(Coll[Int](2, 3)), cost, costDetails(2), newCost)) - ) - }, - existingFeature((x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2), - "{ (x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2) }", - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[Value[STuple]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 2.toByte + { + val cost = 1772 + val newCost = 1772 + val v3Costs = Seq.fill(4)(2050) + Seq( + // (coll, (from, until)) + ((Coll[Int](), (-1, 0)), Expected(Success(Coll[Int]()), cost, costDetails(1), newCost, v3Costs)), + ((Coll[Int](), (0, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1), (0, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1), (0, -1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1), (1, 1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1), (-1, 1)), Expected(Success(Coll[Int](1)), cost, costDetails(2), newCost, v3Costs)), + ((Coll[Int](1, 2), (1, 1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1, 2), (1, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1, 2), (1, 2)), Expected(Success(Coll[Int](2)), cost, costDetails(1), newCost, v3Costs)), + ((Coll[Int](1, 2, 3, 4), (1, 3)), Expected(Success(Coll[Int](2, 3)), cost, costDetails(2), newCost, v3Costs)) + ) + }, + existingFeature((x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2), + "{ (x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2) }", + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), + BlockValue( + Vector( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 2.toByte + ) ) - ) - ), - Slice( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 1.toByte ), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) + Slice( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 1.toByte + ), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) + ) ) - ) - )), + )), preGeneratedSamples = Some(samples)) } else { def error = new java.lang.NoSuchMethodException("sigmastate.SCollection$.slice_eval(sigmastate.lang.Terms$MethodCall,sigma.Coll,int,int,sigmastate.interpreter.ErgoTreeEvaluator))") @@ -8471,37 +8580,38 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) if (lowerMethodCallsInTests) { verifyCases( - { - def success[T](v: T, size: Int) = Expected(Success(v), 1770, costDetails(size), 1770) - val arr1 = Gen.listOfN(100, arbitrary[Int]).map(_.toArray).sample.get - val arr2 = Gen.listOfN(200, arbitrary[Int]).map(_.toArray).sample.get - Seq( - (Coll[Int](), Coll[Int]()) -> success(Coll[Int](), 0), - (Coll[Int](), Coll[Int](1)) -> success(Coll[Int](1), 1), - (Coll[Int](1), Coll[Int]()) -> success(Coll[Int](1), 1), - (Coll[Int](1), Coll[Int](2)) -> success(Coll[Int](1, 2), 2), - (Coll[Int](1), Coll[Int](2, 3)) -> success(Coll[Int](1, 2, 3), 3), - (Coll[Int](1, 2), Coll[Int](3)) -> success(Coll[Int](1, 2, 3), 3), - (Coll[Int](1, 2), Coll[Int](3, 4)) -> success(Coll[Int](1, 2, 3, 4), 4), - (Coll[Int](arr1:_*), Coll[Int](arr2:_*)) -> Expected(Success(Coll[Int](arr1 ++ arr2:_*)), 1771, costDetails(300), 1771) - ) - }, - existingFeature( - { (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }, - "{ (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }", - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SCollectionType(SInt)))), - Append( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), - 1.toByte - ), - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), - 2.toByte - ) + { + def success[T](v: T, size: Int) = Expected(Success(v), 1770, costDetails(size), 1770, Seq.fill(4)(2022)) + + val arr1 = Gen.listOfN(100, arbitrary[Int]).map(_.toArray).sample.get + val arr2 = Gen.listOfN(200, arbitrary[Int]).map(_.toArray).sample.get + Seq( + (Coll[Int](), Coll[Int]()) -> success(Coll[Int](), 0), + (Coll[Int](), Coll[Int](1)) -> success(Coll[Int](1), 1), + (Coll[Int](1), Coll[Int]()) -> success(Coll[Int](1), 1), + (Coll[Int](1), Coll[Int](2)) -> success(Coll[Int](1, 2), 2), + (Coll[Int](1), Coll[Int](2, 3)) -> success(Coll[Int](1, 2, 3), 3), + (Coll[Int](1, 2), Coll[Int](3)) -> success(Coll[Int](1, 2, 3), 3), + (Coll[Int](1, 2), Coll[Int](3, 4)) -> success(Coll[Int](1, 2, 3, 4), 4), + (Coll[Int](arr1: _*), Coll[Int](arr2: _*)) -> Expected(Success(Coll[Int](arr1 ++ arr2: _*)), 1771, costDetails(300), 1771, Seq.fill(4)(2023)) ) - ))) + }, + existingFeature( + { (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }, + "{ (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }", + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SCollectionType(SInt)))), + Append( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 1.toByte + ), + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 2.toByte + ) + ) + ))) } else { def error = new java.lang.NoSuchMethodException("sigmastate.SCollection$.append_eval(sigmastate.lang.Terms$MethodCall,sigma.Coll,sigma.Coll,sigmastate.interpreter.ErgoTreeEvaluator))") verifyCases( @@ -8578,33 +8688,33 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( (None -> Expected(new NoSuchElementException("None.get"))), - (Some(10L) -> Expected(Success(10L), 1765, costDetails1, 1765))), + (Some(10L) -> Expected(Success(10L), 1765, costDetails1, 1765, Seq.fill(4)(1997)))), existingFeature({ (x: Option[Long]) => x.get }, "{ (x: Option[Long]) => x.get }", FuncValue(Vector((1, SOption(SLong))), OptionGet(ValUse(1, SOption(SLong)))))) verifyCases( Seq( - (None -> Expected(Success(false), 1764, costDetails2, 1764)), - (Some(10L) -> Expected(Success(true), 1764, costDetails2, 1764))), + (None -> Expected(Success(false), 1764, costDetails2, 1764, Seq.fill(4)(1996))), + (Some(10L) -> Expected(Success(true), 1764, costDetails2, 1764, Seq.fill(4)(1996)))), existingFeature({ (x: Option[Long]) => x.isDefined }, "{ (x: Option[Long]) => x.isDefined }", FuncValue(Vector((1, SOption(SLong))), OptionIsDefined(ValUse(1, SOption(SLong)))))) verifyCases( Seq( - (None -> Expected(Success(1L), 1766, costDetails3, 1766)), - (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766))), + (None -> Expected(Success(1L), 1766, costDetails3, 1766, Seq.fill(4)(2006))), + (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766, Seq.fill(4)(2006)))), existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, "{ (x: Option[Long]) => x.getOrElse(1L) }", FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) verifyCases( Seq( - (None -> Expected(Success(None), 1766, costDetails4, 1766)), - (Some(10L) -> Expected(Success(None), 1768, costDetails5, 1768)), - (Some(1L) -> Expected(Success(Some(1L)), 1769, costDetails5, 1769))), - existingFeature({ (x: Option[Long]) => x.filter({ (v: Long) => v == 1} ) }, + (None -> Expected(Success(None), 1766, costDetails4, 1766, Seq.fill(4)(2028))), + (Some(10L) -> Expected(Success(None), 1768, costDetails5, 1768, Seq.fill(4)(2030))), + (Some(1L) -> Expected(Success(Some(1L)), 1769, costDetails5, 1769, Seq.fill(4)(2031)))), + existingFeature({ (x: Option[Long]) => x.filter({ (v: Long) => v == 1 }) }, "{ (x: Option[Long]) => x.filter({ (v: Long) => v == 1 }) }", FuncValue( Vector((1, SOption(SLong))), @@ -8619,8 +8729,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val n = ExactNumeric.LongIsExactNumeric verifyCases( Seq( - (None -> Expected(Success(None), 1766, costDetails6, 1766)), - (Some(10L) -> Expected(Success(Some(11L)), 1770, costDetails7, 1770)), + (None -> Expected(Success(None), 1766, costDetails6, 1766, Seq.fill(4)(2028))), + (Some(10L) -> Expected(Success(Some(11L)), 1770, costDetails7, 1770, Seq.fill(4)(2032))), (Some(Long.MaxValue) -> Expected(new ArithmeticException("long overflow")))), existingFeature({ (x: Option[Long]) => x.map( (v: Long) => n.plus(v, 1) ) }, "{ (x: Option[Long]) => x.map({ (v: Long) => v + 1 }) }", @@ -8682,10 +8792,10 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val o = ExactOrdering.LongIsExactOrdering verifyCases( Seq( - (None -> Expected(Success(None), 1766, costDetails1, 1766)), - (Some(0L) -> Expected(Success(None), 1771, costDetails2, 1771)), - (Some(10L) -> Expected(Success(Some(10L)), 1774, costDetails3, 1774)), - (Some(11L) -> Expected(Success(None), 1774, costDetails3, 1774))), + (None -> Expected(Success(None), 1766, costDetails1, 1766, Seq.fill(4)(2052))), + (Some(0L) -> Expected(Success(None), 1771, costDetails2, 1771, Seq.fill(4)(2057))), + (Some(10L) -> Expected(Success(Some(10L)), 1774, costDetails3, 1774, Seq.fill(4)(2060))), + (Some(11L) -> Expected(Success(None), 1774, costDetails3, 1774, Seq.fill(4)(2060)))), existingFeature( { (x: Option[Long]) => x.filter({ (v: Long) => if (o.gt(v, 0L)) v <= 10 else false } ) }, "{ (x: Option[Long]) => x.filter({ (v: Long) => if (v > 0) v <= 10 else false }) }", @@ -8746,10 +8856,10 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val n = ExactNumeric.LongIsExactNumeric verifyCases( Seq( - (None -> Expected(Success(None), 1766, costDetails4, 1766)), - (Some(0L) -> Expected(Success(Some(0L)), 1772, costDetails5, 1772)), - (Some(10L) -> Expected(Success(Some(10L)), 1772, costDetails5, 1772)), - (Some(-1L) -> Expected(Success(Some(-2L)), 1774, costDetails6, 1774)), + (None -> Expected(Success(None), 1766, costDetails4, 1766, Seq.fill(4)(2048))), + (Some(0L) -> Expected(Success(Some(0L)), 1772, costDetails5, 1772, Seq.fill(4)(2054))), + (Some(10L) -> Expected(Success(Some(10L)), 1772, costDetails5, 1772, Seq.fill(4)(2054))), + (Some(-1L) -> Expected(Success(Some(-2L)), 1774, costDetails6, 1774, Seq.fill(4)(2056))), (Some(Long.MinValue) -> Expected(new ArithmeticException("long overflow")))), existingFeature( { (x: Option[Long]) => x.map( (v: Long) => if (o.lt(v, 0)) n.minus(v, 1) else v ) }, @@ -8802,20 +8912,30 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( Seq( (None -> Expected( - value = Failure(new NoSuchElementException("None.get")), - cost = 0, - expectedDetails = CostDetails.ZeroCost, - newCost = 1766, - newVersionedResults = Seq.tabulate(4)(v => v -> (ExpectedResult(Success(5L), Some(1766)) -> Some(costDetails1))) - )), + value = Failure(new NoSuchElementException("None.get")), + cost = 0, + expectedDetails = CostDetails.ZeroCost, + newCost = 1766, + newVersionedResults = Seq.tabulate(4)({ v => + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2042) + } + else { + Seq.fill(4)(1766) + } + v -> (ExpectedResult(Success(5L), Some(costs(v))) -> Some(costDetails1)) + }) + )), (Some(0L) -> Expected( Success(1L), cost = 1774, expectedDetails = costDetails2, - expectedNewCost = 1774)), + expectedNewCost = 1774, + expectedV3Costs = Seq.fill(4)(2050))), (Some(Long.MaxValue) -> Expected(new ArithmeticException("long overflow"))) ), changedFeature( + changedInVersion = VersionContext.JitActivationVersion, scalaFunc = { (x: Option[Long]) => def f(opt: Long): Long = n.plus(opt, 1) if (x.isDefined) f(x.get) @@ -8880,19 +9000,22 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Success(Helpers.decodeBytes("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8")), 1768, costDetailsBlake(0), - 1768 + 1768, + Seq.fill(4)(2000) ), Helpers.decodeBytes("e0ff0105ffffac31010017ff33") -> Expected( Success(Helpers.decodeBytes("33707eed9aab64874ff2daa6d6a378f61e7da36398fb36c194c7562c9ff846b5")), 1768, costDetailsBlake(13), - 1768 + 1768, + Seq.fill(4)(2000) ), Colls.replicate(1024, 1.toByte) -> Expected( Success(Helpers.decodeBytes("45d8456fc5d41d1ec1124cb92e41192c1c3ec88f0bf7ae2dc6e9cf75bec22045")), 1773, costDetailsBlake(1024), - 1773 + 1773, + Seq.fill(4)(2005) ) ), existingFeature((x: Coll[Byte]) => SigmaDsl.blake2b256(x), @@ -8905,19 +9028,22 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Success(Helpers.decodeBytes("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")), 1774, costDetailsSha(0), - 1774 + 1774, + Seq.fill(4)(2006) ), Helpers.decodeBytes("e0ff0105ffffac31010017ff33") -> Expected( Success(Helpers.decodeBytes("367d0ec2cdc14aac29d5beb60c2bfc86d5a44a246308659af61c1b85fa2ca2cc")), 1774, costDetailsSha(13), - 1774 + 1774, + Seq.fill(4)(2006) ), Colls.replicate(1024, 1.toByte) -> Expected( Success(Helpers.decodeBytes("5a648d8015900d89664e00e125df179636301a2d8fa191c1aa2bd9358ea53a69")), 1786, costDetailsSha(1024), - 1786 + 1786, + Seq.fill(4)(2018) ) ), existingFeature((x: Coll[Byte]) => SigmaDsl.sha256(x), @@ -8927,10 +9053,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("sigmaProp equivalence") { val costDetails = TracedCost(traceBase :+ FixedCostItem(BoolToSigmaProp)) + val v3Costs = Seq.fill(4)(1997) verifyCases( Seq( - (false, Expected(Success(CSigmaProp(TrivialProp.FalseProp)), 1765, costDetails, 1765)), - (true, Expected(Success(CSigmaProp(TrivialProp.TrueProp)), 1765, costDetails, 1765))), + (false, Expected(Success(CSigmaProp(TrivialProp.FalseProp)), 1765, costDetails, 1765, v3Costs)), + (true, Expected(Success(CSigmaProp(TrivialProp.TrueProp)), 1765, costDetails, 1765, v3Costs))), existingFeature((x: Boolean) => sigmaProp(x), "{ (x: Boolean) => sigmaProp(x) }", FuncValue(Vector((1, SBoolean)), BoolToSigmaProp(ValUse(1, SBoolean))))) @@ -8957,7 +9084,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Helpers.decodeECPoint("02614b14a8c6c6b4b7ce017d72fbca7f9218b72c16bdd88f170ffb300b106b9014"), Helpers.decodeECPoint("034cc5572276adfa3e283a3f1b0f0028afaadeaa362618c5ec43262d8cefe7f004") ) - )) -> Expected(Success(CSigmaProp(TrivialProp.TrueProp)), 1770, costDetails(1), 1770), + )) -> Expected(Success(CSigmaProp(TrivialProp.TrueProp)), 1770, costDetails(1), 1770, Seq.fill(4)(2018)), Coll[SigmaProp]( CSigmaProp( ProveDHTuple( @@ -8985,7 +9112,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) ) - ), 1873, costDetails(3), 1873), + ), 1873, costDetails(3), 1873, Seq.fill(4)(2121)), Colls.replicate[SigmaProp](AtLeast.MaxChildrenCount + 1, CSigmaProp(TrivialProp.TrueProp)) -> Expected(new IllegalArgumentException("Expected input elements count should not exceed 255, actual: 256")) ), @@ -9017,31 +9144,32 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => verifyCases( { - def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, costDetails1, newCost) + def success[T](v: T, newCost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), newCost, costDetails1, newCost, expectedV3Costs) + Seq( (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success( - CSigmaProp( - CAND( - Seq( - ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), - ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) - ) + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success( + CSigmaProp( + CAND( + Seq( + ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), + ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) ) - ), 1802), + ) + ), 1802, Seq.fill(4)(2048)), (CSigmaProp(TrivialProp.TrueProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784, Seq.fill(4)(2030)), (CSigmaProp(TrivialProp.FalseProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success(CSigmaProp(TrivialProp.FalseProp), 1767), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(TrivialProp.FalseProp), 1767, Seq.fill(4)(2013)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.TrueProp)) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784), + CSigmaProp(TrivialProp.TrueProp)) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784, Seq.fill(4)(2030)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.FalseProp)) -> - success(CSigmaProp(TrivialProp.FalseProp), 1767) + CSigmaProp(TrivialProp.FalseProp)) -> + success(CSigmaProp(TrivialProp.FalseProp), 1767, Seq.fill(4)(2013)) ) }, existingFeature( @@ -9061,9 +9189,9 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => { Seq( (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> - Expected(Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), 1786, costDetails2, 1786), + Expected(Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), 1786, costDetails2, 1786, Seq.fill(4)(2038)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> - Expected(Success(CSigmaProp(TrivialProp.FalseProp)), 1769, costDetails2, 1769) + Expected(Success(CSigmaProp(TrivialProp.FalseProp)), 1769, costDetails2, 1769, Seq.fill(4)(2021)) ) }, existingFeature( @@ -9092,32 +9220,33 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => val costDetails1 = TracedCost(testTraceBase :+ ast.SeqCostItem(CompanionDesc(SigmaOr), PerItemCost(JitCost(10), JitCost(2), 1), 2)) verifyCases( { - def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, costDetails1, newCost) + def success[T](v: T, newCost: Int, v3Costs: Seq[Int]) = Expected(Success(v), newCost, costDetails1, newCost, v3Costs) + Seq( (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success( - CSigmaProp( - COR( - Seq( - ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), - ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) - ) + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success( + CSigmaProp( + COR( + Seq( + ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), + ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) ) - ), - 1802), + ) + ), + 1802, Seq.fill(4)(2048)), (CSigmaProp(TrivialProp.FalseProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784, Seq.fill(4)(2030)), (CSigmaProp(TrivialProp.TrueProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success(CSigmaProp(TrivialProp.TrueProp), 1767), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(TrivialProp.TrueProp), 1767, Seq.fill(4)(2013)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.FalseProp)) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784), + CSigmaProp(TrivialProp.FalseProp)) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784, Seq.fill(4)(2030)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.TrueProp)) -> - success(CSigmaProp(TrivialProp.TrueProp), 1767) + CSigmaProp(TrivialProp.TrueProp)) -> + success(CSigmaProp(TrivialProp.TrueProp), 1767, Seq.fill(4)(2013)) ) }, existingFeature( @@ -9141,12 +9270,13 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( { - def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, costDetails2, newCost) + def success[T](v: T, newCost: Int, v3Costs: Seq[Int]) = Expected(Success(v), newCost, costDetails2, newCost, v3Costs) + Seq( (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1786), + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1786, Seq.fill(4)(2038)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> - success(CSigmaProp(TrivialProp.TrueProp), 1769) + success(CSigmaProp(TrivialProp.TrueProp), 1769, Seq.fill(4)(2021)) ) }, existingFeature( @@ -9184,25 +9314,25 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Helpers.decodeBytes( "0008ce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) - ), cost = 1771, newDetails(4), expectedNewCost = 1771), + ), cost = 1771, newDetails(4), expectedNewCost = 1771, Seq.fill(4)(2003)), CSigmaProp(pk) -> Expected(Success( Helpers.decodeBytes("0008cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6f")), - cost = 1769, newDetails(1), expectedNewCost = 1769), + cost = 1769, newDetails(1), expectedNewCost = 1769, Seq.fill(4)(2001)), CSigmaProp(and) -> Expected(Success( Helpers.decodeBytes( "00089602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) - ), cost = 1772, newDetails(6), expectedNewCost = 1772), + ), cost = 1772, newDetails(6), expectedNewCost = 1772, Seq.fill(4)(2004)), CSigmaProp(threshold) -> Expected(Success( Helpers.decodeBytes( "0008980204cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419702cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) - ), cost = 1780, newDetails(18), expectedNewCost = 1780), + ), cost = 1780, newDetails(18), expectedNewCost = 1780, Seq.fill(4)(2012)), CSigmaProp(data.COR(Array(pk, dht, and, or, threshold))) -> Expected(Success( Helpers.decodeBytes( "00089705cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419702cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441980204cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419702cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) - ), cost = 1791, newDetails(36), expectedNewCost = 1791) + ), cost = 1791, newDetails(36), expectedNewCost = 1791, Seq.fill(4)(2023)) ) }, existingFeature((x: SigmaProp) => x.propBytes, @@ -9213,19 +9343,20 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("allOf equivalence") { def costDetails(i: Int) = TracedCost(traceBase :+ ast.SeqCostItem(CompanionDesc(AND), PerItemCost(JitCost(10), JitCost(5), 32), i)) + val v3Costs = Seq.fill(4)(1997) verifyCases( Seq( - (Coll[Boolean]() -> Expected(Success(true), 1765, costDetails(0), 1765)), - (Coll[Boolean](true) -> Expected(Success(true), 1765, costDetails(1), 1765)), - (Coll[Boolean](false) -> Expected(Success(false), 1765, costDetails(1), 1765)), - (Coll[Boolean](false, false) -> Expected(Success(false), 1765, costDetails(1), 1765)), - (Coll[Boolean](false, true) -> Expected(Success(false), 1765, costDetails(1), 1765)), - (Coll[Boolean](true, false) -> Expected(Success(false), 1765, costDetails(2), 1765)), - (Coll[Boolean](true, true) -> Expected(Success(true), 1765, costDetails(2), 1765)), - (Coll[Boolean](true, false, false) -> Expected(Success(false), 1765, costDetails(2), 1765)), - (Coll[Boolean](true, false, true) -> Expected(Success(false), 1765, costDetails(2), 1765)), - (Coll[Boolean](true, true, false) -> Expected(Success(false), 1765, costDetails(3), 1765)), - (Coll[Boolean](true, true, true) -> Expected(Success(true), 1765, costDetails(3), 1765)) + (Coll[Boolean]() -> Expected(Success(true), 1765, costDetails(0), 1765, v3Costs)), + (Coll[Boolean](true) -> Expected(Success(true), 1765, costDetails(1), 1765, v3Costs)), + (Coll[Boolean](false) -> Expected(Success(false), 1765, costDetails(1), 1765, v3Costs)), + (Coll[Boolean](false, false) -> Expected(Success(false), 1765, costDetails(1), 1765, v3Costs)), + (Coll[Boolean](false, true) -> Expected(Success(false), 1765, costDetails(1), 1765, v3Costs)), + (Coll[Boolean](true, false) -> Expected(Success(false), 1765, costDetails(2), 1765, v3Costs)), + (Coll[Boolean](true, true) -> Expected(Success(true), 1765, costDetails(2), 1765, v3Costs)), + (Coll[Boolean](true, false, false) -> Expected(Success(false), 1765, costDetails(2), 1765, v3Costs)), + (Coll[Boolean](true, false, true) -> Expected(Success(false), 1765, costDetails(2), 1765, v3Costs)), + (Coll[Boolean](true, true, false) -> Expected(Success(false), 1765, costDetails(3), 1765, v3Costs)), + (Coll[Boolean](true, true, true) -> Expected(Success(true), 1765, costDetails(3), 1765, v3Costs)) ), existingFeature((x: Coll[Boolean]) => SigmaDsl.allOf(x), "{ (x: Coll[Boolean]) => allOf(x) }", @@ -9234,19 +9365,20 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("anyOf equivalence") { def costDetails(i: Int) = TracedCost(traceBase :+ ast.SeqCostItem(CompanionDesc(OR), PerItemCost(JitCost(5), JitCost(5), 64), i)) + val v3Costs = Seq.fill(4)(1996) verifyCases( Seq( - (Coll[Boolean]() -> Expected(Success(false), 1764, costDetails(0), 1764)), - (Coll[Boolean](true) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](false) -> Expected(Success(false), 1764, costDetails(1), 1764)), - (Coll[Boolean](false, false) -> Expected(Success(false), 1764, costDetails(2), 1764)), - (Coll[Boolean](false, true) -> Expected(Success(true), 1764, costDetails(2), 1764)), - (Coll[Boolean](true, false) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, true) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, false, false) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, false, true) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, true, false) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, true, true) -> Expected(Success(true), 1764, costDetails(1), 1764)) + (Coll[Boolean]() -> Expected(Success(false), 1764, costDetails(0), 1764, v3Costs)), + (Coll[Boolean](true) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](false) -> Expected(Success(false), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](false, false) -> Expected(Success(false), 1764, costDetails(2), 1764, v3Costs)), + (Coll[Boolean](false, true) -> Expected(Success(true), 1764, costDetails(2), 1764, v3Costs)), + (Coll[Boolean](true, false) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, true) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, false, false) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, false, true) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, true, false) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, true, true) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)) ), existingFeature((x: Coll[Boolean]) => SigmaDsl.anyOf(x), "{ (x: Coll[Boolean]) => anyOf(x) }", @@ -9259,10 +9391,11 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Seq( (Helpers.decodeGroupElement("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69") -> Expected(Success( - CSigmaProp(ProveDlog(Helpers.decodeECPoint("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69")))), - cost = 1782, - costDetails, - 1782)) + CSigmaProp(ProveDlog(Helpers.decodeECPoint("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69")))), + cost = 1782, + costDetails, + 1782, + Seq.fill(4)(2014))) ), existingFeature({ (x: GroupElement) => SigmaDsl.proveDlog(x) }, "{ (x: GroupElement) => proveDlog(x) }", @@ -9282,18 +9415,19 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => Seq( (Helpers.decodeGroupElement("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") -> Expected(Success( - CSigmaProp( - ProveDHTuple( - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") - ) - )), - cost = 1836, - costDetails, - 1836 - )) + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") + ) + )), + cost = 1836, + costDetails, + 1836, + Seq.fill(4)(2080) + )) ), existingFeature({ (x: GroupElement) => SigmaDsl.proveDHTuple(x, x, x, x) }, "{ (x: GroupElement) => proveDHTuple(x, x, x, x) }", @@ -9334,7 +9468,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) verifyCases( { - def success[T](v: T, cd: CostDetails, cost: Int) = Expected(Success(v), cost, cd, cost) + def success[T](v: T, cd: CostDetails, cost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), cost, cd, cost, expectedV3Costs) Seq( (Helpers.decodeBytes(""), 0) -> Expected(new java.nio.BufferUnderflowException()), @@ -9346,8 +9480,16 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => expectedDetails = CostDetails.ZeroCost, newCost = 1783, newVersionedResults = { - val res = (ExpectedResult(Success(Helpers.decodeBytes("0008d3")), Some(1783)) -> Some(costDetails(0))) - Seq(0, 1, 2, 3).map(version => version -> res) + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2055) + } + else { + Seq.fill(4)(1783) + } + Seq(0, 1, 2, 3).map({ version => + val res = (ExpectedResult(Success(Helpers.decodeBytes("0008d3")), Some(costs(version))) -> Some(costDetails(0))) + version -> res + }) }), (Helpers.decodeBytes("000008d3"), 0) -> Expected( @@ -9356,21 +9498,30 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => expectedDetails = CostDetails.ZeroCost, newCost = 1783, newVersionedResults = { - // since the tree without constant segregation, substitution has no effect - val res = (ExpectedResult(Success(Helpers.decodeBytes("000008d3")), Some(1783)) -> Some(costDetails(0))) - Seq(0, 1, 2, 3).map(version => version -> res) + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2055) + } + else { + Seq.fill(4)(1783) + } + Seq(0, 1, 2, 3).map({ version => + // since the tree without constant segregation, substitution has no effect + val res = (ExpectedResult(Success(Helpers.decodeBytes("000008d3")), Some(costs(version))) -> Some(costDetails(0))) + version -> res + }) }), // tree with segregation flag, empty constants array - (Coll(t2.bytes:_*), 0) -> success(Helpers.decodeBytes("100008d3"), costDetails(0), 1783), - (Helpers.decodeBytes("100008d3"), 0) -> success(Helpers.decodeBytes("100008d3"), costDetails(0), 1783), + (Coll(t2.bytes: _*), 0) -> success(Helpers.decodeBytes("100008d3"), costDetails(0), 1783, Seq.fill(4)(2055)), + (Helpers.decodeBytes("100008d3"), 0) -> success(Helpers.decodeBytes("100008d3"), costDetails(0), 1783, Seq.fill(4)(2055)), // tree with one segregated constant - (Coll(t3.bytes:_*), 0) -> success(Helpers.decodeBytes("100108d27300"), costDetails(1), 1793), - (Helpers.decodeBytes("100108d37300"), 0) -> success(Helpers.decodeBytes("100108d27300"), costDetails(1), 1793), - (Coll(t3.bytes:_*), 1) -> success(Helpers.decodeBytes("100108d37300"), costDetails(1), 1793), - (Coll(t4.bytes:_*), 0) -> Expected(new IllegalArgumentException("requirement failed: expected new constant to have the same SInt$ tpe, got SSigmaProp")) + (Coll(t3.bytes: _*), 0) -> success(Helpers.decodeBytes("100108d27300"), costDetails(1), 1793, Seq.fill(4)(2065)), + (Helpers.decodeBytes("100108d37300"), 0) -> success(Helpers.decodeBytes("100108d27300"), costDetails(1), 1793, Seq.fill(4)(2065)), + (Coll(t3.bytes: _*), 1) -> success(Helpers.decodeBytes("100108d37300"), costDetails(1), 1793, Seq.fill(4)(2065)), + (Coll(t4.bytes: _*), 0) -> Expected(new IllegalArgumentException("requirement failed: expected new constant to have the same SInt$ tpe, got SSigmaProp")) ) }, changedFeature( + changedInVersion = VersionContext.JitActivationVersion, { (x: (Coll[Byte], Int)) => SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) }, @@ -9422,6 +9573,12 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => if (lowerMethodCallsInTests) { val error = new RuntimeException("any exception") + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2144) + } + else { + Seq.fill(4)(1776) + } verifyCases( Seq( ctx -> Expected( @@ -9429,107 +9586,110 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => cost = 1776, expectedDetails = CostDetails.ZeroCost, newCost = 1776, - newVersionedResults = Seq(0, 1, 2, 3).map(i => i -> (ExpectedResult(Success(true), Some(1776)) -> Some(costDetails))) + newVersionedResults = (0 to 3).map({ i => + i -> (ExpectedResult(Success(true), Some(costs(i))) -> Some(costDetails)) + }) ) ), changedFeature( - { (x: Context) => - throw error - true - }, - { (x: Context) => - val headers = x.headers - val ids = headers.map({ (h: Header) => h.id }) - val parentIds = headers.map({ (h: Header) => h.parentId }) - headers.indices.slice(0, headers.size - 1).forall({ (i: Int) => - val parentId = parentIds(i) - val id = ids(i + 1) - parentId == id - }) - }, - """{ - |(x: Context) => - | val headers = x.headers - | val ids = headers.map({(h: Header) => h.id }) - | val parentIds = headers.map({(h: Header) => h.parentId }) - | headers.indices.slice(0, headers.size - 1).forall({ (i: Int) => - | val parentId = parentIds(i) - | val id = ids(i + 1) - | parentId == id - | }) - |}""".stripMargin, - FuncValue( - Array((1, SContext)), - BlockValue( - Array( - ValDef( - 3, - List(), - MethodCall.typed[Value[SCollection[SHeader.type]]]( - ValUse(1, SContext), - SContextMethods.getMethodByName("headers"), - Vector(), - Map() - ) - ) - ), - ForAll( - Slice( - MethodCall.typed[Value[SCollection[SInt.type]]]( - ValUse(3, SCollectionType(SHeader)), - SCollectionMethods.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SHeader)), - Vector(), - Map() - ), - IntConstant(0), - ArithOp( - SizeOf(ValUse(3, SCollectionType(SHeader))), - IntConstant(1), - OpCode @@ (-103.toByte) + changedInVersion = VersionContext.JitActivationVersion, + { (x: Context) => + throw error + true + }, + { (x: Context) => + val headers = x.headers + val ids = headers.map({ (h: Header) => h.id }) + val parentIds = headers.map({ (h: Header) => h.parentId }) + headers.indices.slice(0, headers.size - 1).forall({ (i: Int) => + val parentId = parentIds(i) + val id = ids(i + 1) + parentId == id + }) + }, + """{ + |(x: Context) => + | val headers = x.headers + | val ids = headers.map({(h: Header) => h.id }) + | val parentIds = headers.map({(h: Header) => h.parentId }) + | headers.indices.slice(0, headers.size - 1).forall({ (i: Int) => + | val parentId = parentIds(i) + | val id = ids(i + 1) + | parentId == id + | }) + |}""".stripMargin, + FuncValue( + Array((1, SContext)), + BlockValue( + Array( + ValDef( + 3, + List(), + MethodCall.typed[Value[SCollection[SHeader.type]]]( + ValUse(1, SContext), + SContextMethods.getMethodByName("headers"), + Vector(), + Map() + ) ) ), - FuncValue( - Array((4, SInt)), - EQ( - ByIndex( - MapCollection( - ValUse(3, SCollectionType(SHeader)), - FuncValue( - Array((6, SHeader)), - MethodCall.typed[Value[SCollection[SByte.type]]]( - ValUse(6, SHeader), - SHeaderMethods.getMethodByName("parentId"), - Vector(), - Map() - ) - ) - ), - ValUse(4, SInt), - None + ForAll( + Slice( + MethodCall.typed[Value[SCollection[SInt.type]]]( + ValUse(3, SCollectionType(SHeader)), + SCollectionMethods.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SHeader)), + Vector(), + Map() ), - ByIndex( - MapCollection( - ValUse(3, SCollectionType(SHeader)), - FuncValue( - Array((6, SHeader)), - MethodCall.typed[Value[SCollection[SByte.type]]]( - ValUse(6, SHeader), - SHeaderMethods.getMethodByName("id"), - Vector(), - Map() + IntConstant(0), + ArithOp( + SizeOf(ValUse(3, SCollectionType(SHeader))), + IntConstant(1), + OpCode @@ (-103.toByte) + ) + ), + FuncValue( + Array((4, SInt)), + EQ( + ByIndex( + MapCollection( + ValUse(3, SCollectionType(SHeader)), + FuncValue( + Array((6, SHeader)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(6, SHeader), + SHeaderMethods.getMethodByName("parentId"), + Vector(), + Map() + ) ) - ) + ), + ValUse(4, SInt), + None ), - ArithOp(ValUse(4, SInt), IntConstant(1), OpCode @@ (-102.toByte)), - None + ByIndex( + MapCollection( + ValUse(3, SCollectionType(SHeader)), + FuncValue( + Array((6, SHeader)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(6, SHeader), + SHeaderMethods.getMethodByName("id"), + Vector(), + Map() + ) + ) + ), + ArithOp(ValUse(4, SInt), IntConstant(1), OpCode @@ (-102.toByte)), + None + ) ) ) ) ) - ) - ), - allowDifferentErrors = true, - allowNewToSucceed = true + ), + allowDifferentErrors = true, + allowNewToSucceed = true ), preGeneratedSamples = Some(ArraySeq.empty) ) @@ -9540,8 +9700,8 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => property("nested loops: map inside fold") { val keys = Colls.fromArray(Array(Coll[Byte](1, 2, 3, 4, 5))) val initial = Coll[Byte](0, 0, 0, 0, 0) - val cases = Seq( - (keys, initial) -> Expected(Success(Coll[Byte](1, 2, 3, 4, 5)), cost = 1801, expectedDetails = CostDetails.ZeroCost, 1801) + val cases = Seq( + (keys, initial) -> Expected(Success(Coll[Byte](1, 2, 3, 4, 5)), cost = 1801, expectedDetails = CostDetails.ZeroCost, 1801, Seq.fill(4)(2119)) ) val scalaFunc = { (x: (Coll[Coll[Byte]], Coll[Byte])) => x._1.foldLeft(x._2, { (a: (Coll[Byte], Coll[Byte])) => diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index e4ae10cac6..0b9e1eb4de 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,10 +1,14 @@ package sigma -import sigma.ast.{Apply, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, Global, JitCost, MethodCall, NamedDesc, OptionGet, SBigInt, SByte, SGlobalMethods, SInt, SLong, SShort, STypeVar, ValUse} +import sigma.ast.ErgoTree.ZeroHeader +import sigma.ast.SCollection.SByteArray +import sigma.ast.syntax.TrueSigmaProp +import sigma.ast._ import sigma.data.{CBigInt, ExactNumeric, RType} -import sigma.eval.{SigmaDsl, TracedCost} +import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound +import sigmastate.utils.Helpers import java.math.BigInteger import scala.util.Success @@ -234,21 +238,26 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => } property("BigInt methods equivalence (new features)") { - // TODO v6.0: the behavior of `upcast` for BigInt is different from all other Numeric types (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/877) - // The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. - // It makes sense to fix this inconsistency as part of upcoming forks - assertExceptionThrown( - SBigInt.upcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), - _.getMessage.contains("Cannot upcast value") - ) + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + // The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. + // Fixed in 6.0 + assertExceptionThrown( + SBigInt.upcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot upcast value") + ) - // TODO v6.0: the behavior of `downcast` for BigInt is different from all other Numeric types (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/877) - // The `Downcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. - // It makes sense to fix this inconsistency as part of HF - assertExceptionThrown( - SBigInt.downcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), - _.getMessage.contains("Cannot downcast value") - ) + // The `Downcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. + // Fixed in 6.0 + assertExceptionThrown( + 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 @@ -287,6 +296,44 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => forAll { x: (BigInt, BigInt) => Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) } + + forAll { x: Long => + assertExceptionThrown( + SLong.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + forAll { x: Int => + assertExceptionThrown( + SInt.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + forAll { x: Byte => + assertExceptionThrown( + SByte.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + forAll { x: Short => + assertExceptionThrown( + SShort.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + } else { + forAll { x: Long => + SLong.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + forAll { x: Int => + SInt.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + forAll { x: Byte => + SByte.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + forAll { x: Short => + SShort.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } } } @@ -406,5 +453,87 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => } } + property("Fix substConstants in v6.0 for ErgoTree version > 0") { + // tree with one segregated constant and v0 + val t1 = ErgoTree( + header = ErgoTree.setConstantSegregation(ZeroHeader), + constants = Vector(TrueSigmaProp), + ConstantPlaceholder(0, SSigmaProp)) + + // tree with one segregated constant and max supported version + val t2 = ErgoTree( + header = ErgoTree.setConstantSegregation( + ErgoTree.headerWithVersion(ZeroHeader, VersionContext.MaxSupportedScriptVersion) + ), + Vector(TrueSigmaProp), + ConstantPlaceholder(0, SSigmaProp)) + + def costDetails(nItems: Int) = TracedCost( + traceBase ++ Array( + FixedCostItem(SelectField), + FixedCostItem(ConcreteCollection), + FixedCostItem(ValUse), + FixedCostItem(SelectField), + FixedCostItem(ConcreteCollection), + FixedCostItem(Constant), + FixedCostItem(BoolToSigmaProp), + ast.SeqCostItem(CompanionDesc(SubstConstants), PerItemCost(JitCost(100), JitCost(100), 1), nItems) + ) + ) + + val expectedTreeBytes_beforeV6 = Helpers.decodeBytes("1b0108d27300") + val expectedTreeBytes_V6 = Helpers.decodeBytes("1b050108d27300") + + verifyCases( + Seq( + // for tree v0, the result is the same for all versions + (Coll(t1.bytes: _*), 0) -> Expected( + Success(Helpers.decodeBytes("100108d27300")), + cost = 1793, + expectedDetails = CostDetails.ZeroCost, + newCost = 2065, + newVersionedResults = expectedSuccessForAllTreeVersions(Helpers.decodeBytes("100108d27300"), 2065, costDetails(1)) + ), + // for tree version > 0, the result depend on activated version + (Coll(t2.bytes: _*), 0) -> Expected( + Success(expectedTreeBytes_beforeV6), + cost = 1793, + expectedDetails = CostDetails.ZeroCost, + newCost = 2065, + newVersionedResults = expectedSuccessForAllTreeVersions(expectedTreeBytes_V6, 2065, costDetails(1))) + ), + changedFeature( + changedInVersion = VersionContext.V6SoftForkVersion, + { (x: (Coll[Byte], Int)) => + SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) + }, + { (x: (Coll[Byte], Int)) => + SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) + }, + "{ (x: (Coll[Byte], Int)) => substConstants[Any](x._1, Coll[Int](x._2), Coll[Any](sigmaProp(false))) }", + FuncValue( + Vector((1, SPair(SByteArray, SInt))), + SubstConstants( + SelectField.typed[Value[SCollection[SByte.type]]](ValUse(1, SPair(SByteArray, SInt)), 1.toByte), + ConcreteCollection( + Array(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByteArray, SInt)), 2.toByte)), + SInt + ), + ConcreteCollection(Array(BoolToSigmaProp(FalseLeaf)), SSigmaProp) + ) + ) + ) + ) + + // before v6.0 the expected tree is not parsable + ErgoTree.fromBytes(expectedTreeBytes_beforeV6.toArray).isRightParsed shouldBe false + + // in v6.0 the expected tree should be parsable and similar to the original tree + val tree = ErgoTree.fromBytes(expectedTreeBytes_V6.toArray) + tree.isRightParsed shouldBe true + tree.header shouldBe t2.header + tree.constants.length shouldBe t2.constants.length + tree.root shouldBe t2.root + } } diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 37fb0e6503..b2446b6795 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -14,6 +14,7 @@ import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scalan.Platform.threadSleepOrNoOp import sigma.Extensions.ArrayOps import sigma.data.{CBox, CollType, OptionType, PairType, ProveDlog, RType, SigmaLeaf} +import sigma.VersionContext.V6SoftForkVersion import sigma.util.BenchmarkUtil import sigma.util.CollectionUtil._ import sigma.util.Extensions._ @@ -126,6 +127,9 @@ class SigmaDslTesting extends AnyPropSpec /** Checks if this feature is supported in the given version context. */ def isSupportedIn(vc: VersionContext): Boolean + /** Version in which the feature is first implemented of changed. */ + def sinceVersion: Byte + /** Script containing this feature. */ def script: String @@ -362,14 +366,16 @@ class SigmaDslTesting extends AnyPropSpec // Compile script the same way it is performed by applications (i.e. via Ergo Appkit) val prop = compile(env, code)(IR).asSigmaProp - // Add additional oparations which are not yet implemented in ErgoScript compiler + // Add additional operations which are not yet implemented in ErgoScript compiler val multisig = AtLeast( IntConstant(2), Array( pkAlice, DeserializeRegister(ErgoBox.R5, SSigmaProp), // deserialize pkBob DeserializeContext(2, SSigmaProp))) // deserialize pkCarol - val header = ErgoTree.headerWithVersion(ZeroHeader, ergoTreeVersionInTests) + // We set size for trees v0 as well, to have the same size and so the same cost in V6 interpreter + // (where tree size is accounted in cost) + val header = ErgoTree.setSizeBit(ErgoTree.headerWithVersion(ZeroHeader, ergoTreeVersionInTests)) ErgoTree.withSegregation(header, SigmaOr(prop, multisig)) } @@ -433,7 +439,7 @@ class SigmaDslTesting extends AnyPropSpec ctx } - val (expectedResult, expectedCost) = if (activatedVersionInTests < VersionContext.JitActivationVersion) + val (expectedResult, expectedCost) = if (activatedVersionInTests < sinceVersion) (expected.oldResult, expected.verificationCostOpt) else { val res = expected.newResults(ergoTreeVersionInTests) @@ -492,7 +498,7 @@ class SigmaDslTesting extends AnyPropSpec val verificationCost = cost.toIntExact if (expectedCost.isDefined) { assertResult(expectedCost.get, - s"Actual verify() cost $cost != expected ${expectedCost.get}")(verificationCost) + s"Actual verify() cost $cost != expected ${expectedCost.get} (version: ${VersionContext.current.activatedVersion})")(verificationCost) } case Failure(t) => throw t @@ -540,6 +546,8 @@ class SigmaDslTesting extends AnyPropSpec implicit val cs = compilerSettingsInTests + override def sinceVersion: Byte = 0 + override def isSupportedIn(vc: VersionContext): Boolean = true /** in v5.x the old and the new interpreters are the same */ @@ -652,16 +660,35 @@ class SigmaDslTesting extends AnyPropSpec checkResult(funcRes.map(_._1), expected.value, failOnTestVectors, "ExistingFeature#verifyCase: ") - checkResultAgainstExpected(funcRes, expected) + val newRes = expected.newResults(ergoTreeVersionInTests) + val expectedTrace = newRes._2.fold(Seq.empty[CostItem])(_.trace) + if (expectedTrace.isEmpty) { + // new cost expectation is missing, print out actual cost results + if (evalSettings.printTestVectors) { + funcRes.foreach { case (_, newDetails) => + printCostDetails(script, newDetails) + } + } + } + else { + // new cost expectation is specified, compare it with the actual result + funcRes.foreach { case (_, newDetails) => + if (newDetails.trace != expectedTrace) { + printCostDetails(script, newDetails) + newDetails.trace shouldBe expectedTrace + } + } + } + checkVerify(input, expected) } - } - /** Descriptor of a language feature which is changed in v5.0. + /** Descriptor of a language feature which is changed in the specified version. * * @tparam A type of an input test data * @tparam B type of an output of the feature function + * @param changedInVersion version in which the feature behaviour is changed * @param script script of the feature function (see Feature trait) * @param scalaFunc feature function written in Scala and used to simulate the behavior * of the script @@ -681,6 +708,7 @@ class SigmaDslTesting extends AnyPropSpec * @param allowDifferentErrors if true, allow v4.x and v5.0 to fail with different error */ case class ChangedFeature[A, B]( + changedInVersion: Byte, script: String, scalaFunc: A => B, override val scalaFuncNew: A => B, @@ -694,6 +722,8 @@ class SigmaDslTesting extends AnyPropSpec implicit val cs = compilerSettingsInTests + override def sinceVersion: Byte = changedInVersion + override def isSupportedIn(vc: VersionContext): Boolean = true /** Apply given function to the context variable 1 */ @@ -773,7 +803,7 @@ class SigmaDslTesting extends AnyPropSpec checkEq(scalaFuncNew)(newF)(input) } - if (!VersionContext.current.isJitActivated) { + if (VersionContext.current.activatedVersion < changedInVersion) { // check the old implementation with Scala semantic val expectedOldRes = expected.value @@ -1035,6 +1065,30 @@ class SigmaDslTesting extends AnyPropSpec } } + /** Used when the old and new value and costs are the same for all versions, but Version 3 (Ergo 6.0) will have a different cost due to deserialization cost being added. + * Different versions of ErgoTree can have different deserialization costs as well + * + * @param value expected result of tested function + * @param cost expected verification cost + * @param expectedDetails expected cost details for all versions <= V3 + * @param expectedNewCost expected new verification cost for all versions <= V3 + * @param expectedV3Cost expected cost for >=V3 + */ + def apply[A](value: Try[A], + cost: Int, + expectedDetails: CostDetails, + expectedNewCost: Int, + expectedV3Costs: Seq[Int] + )(implicit dummy: DummyImplicit): Expected[A] = + new Expected(ExpectedResult(value, Some(cost))) { + override val newResults = defaultNewResults.zipWithIndex.map { + case ((ExpectedResult(v, _), _), version) => { + var cost = if (activatedVersionInTests >= V6SoftForkVersion) expectedV3Costs(version) else expectedNewCost + (ExpectedResult(v, Some(cost)), Some(expectedDetails)) + } + } + } + /** Used when operation semantics changes in new versions. For those versions expected * test vectors can be specified. * @@ -1045,8 +1099,10 @@ class SigmaDslTesting extends AnyPropSpec * @param newVersionedResults new results returned by each changed feature function in * v5.+ for each ErgoTree version. */ - def apply[A](value: Try[A], cost: Int, - expectedDetails: CostDetails, newCost: Int, + def apply[A](value: Try[A], + cost: Int, + expectedDetails: CostDetails, + newCost: Int, newVersionedResults: Seq[(Int, (ExpectedResult[A], Option[CostDetails]))]): Expected[A] = new Expected[A](ExpectedResult(value, Some(cost))) { override val newResults = { @@ -1089,14 +1145,16 @@ class SigmaDslTesting extends AnyPropSpec * various ways */ def changedFeature[A: RType, B: RType] - (scalaFunc: A => B, + (changedInVersion: Byte, + scalaFunc: A => B, scalaFuncNew: A => B, script: String, expectedExpr: SValue = null, allowNewToSucceed: Boolean = false, - allowDifferentErrors: Boolean = false) + allowDifferentErrors: Boolean = false + ) (implicit IR: IRContext, evalSettings: EvalSettings): Feature[A, B] = { - ChangedFeature(script, scalaFunc, scalaFuncNew, Option(expectedExpr), + ChangedFeature(changedInVersion, script, scalaFunc, scalaFuncNew, Option(expectedExpr), allowNewToSucceed = allowNewToSucceed, allowDifferentErrors = allowDifferentErrors) } From 649069b6b10241258c6535bb27dfe181db8158aa Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 19 Aug 2024 14:24:02 +0300 Subject: [PATCH 48/78] fixed cost, improved args list for SigmaDslBuilder.fromBigEndianBytes --- .../src/main/scala/sigma/SigmaDsl.scala | 3 ++- .../src/main/scala/sigma/ast/methods.scala | 21 ++++++++----------- .../scala/sigma/data/CSigmaDslBuilder.scala | 21 +++++++++---------- .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 4 +++- .../utxo/BasicOpsSpecification.scala | 1 - 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 18211c7187..eaf21b5007 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -731,6 +731,7 @@ trait SigmaDslBuilder { /** Returns a byte-wise XOR of the two collections of bytes. */ def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte] - def fromBigEndianBytes[T](tpe: SType, bytes: Coll[Byte])(implicit cT: RType[T]): T + /** Returns a number decoded from provided big-endian bytes array. */ + def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index c1a59fea2c..5e4a1752c9 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1520,19 +1520,16 @@ case object SGlobalMethods extends MonoTypeMethods { Xor.xorWithCosting(ls, rs) } + private val BigEndianBytesCostKind = FixedCost(JitCost(10)) + lazy val fromBigEndianBytesMethod = SMethod( - this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 30, Xor.costKind, Seq(tT)) // todo: id, cossting - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) // todo: desc - - def fromBigEndianBytes_eval(mc: MethodCall, G: SigmaDslBuilder, bytes: Coll[Byte]) - (implicit E: ErgoTreeEvaluator): Any = { - val tpe = mc.tpe - val cT = stypeToRType(tpe) - E.addSeqCost(Xor.costKind, bytes.length, Xor.opDesc) { () => // todo: cost - G.fromBigEndianBytes(tpe, bytes)(cT) - } - } + this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 10, BigEndianBytesCostKind, Seq(tT)) // todo: id + .withIRInfo(MethodCallIrBuilder, + javaMethodOf[SigmaDslBuilder, Coll[Byte], RType[_]]("fromBigEndianBytes"), + { mtype => Array(mtype.tRange) }) + .withInfo(MethodCall, + "Decode a number from big endian bytes.", + ArgInfo("first", "Bytes which are big-endian encoded number.")) private val v5Methods = super.getMethods() ++ Seq( groupGeneratorMethod, diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 2e0b3912fc..b74b39c654 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -4,11 +4,11 @@ import debox.cfor import org.ergoplatform.ErgoBox import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} -import scorex.utils.{Ints, Longs, Shorts} -import sigma.ast.{AtLeast, SBigInt, SByte, SInt, SLong, SShort, SType, SubstConstants} +import scorex.utils.{Ints, Longs} +import sigma.ast.{AtLeast, SBigInt, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps -import sigma.serialization.{CoreDataSerializer, DataSerializer, GroupElementSerializer, SerializerException, SigmaSerializer} +import sigma.serialization.{GroupElementSerializer, SerializerException, SigmaSerializer} import sigma.util.Extensions.BigIntegerOps import sigma.validation.SigmaValidationSettings import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext} @@ -201,38 +201,37 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => this.GroupElement(p) } - override def fromBigEndianBytes[T](tpe: SType, bytes: Coll[Byte])(implicit cT: RType[T]): T = { - tpe match { - case SByte => if (bytes.length != 1) { + override def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T = { + cT match { + case sigma.ByteType => if (bytes.length != 1) { throw new IllegalArgumentException("To deserialize SByte with fromBigEndianBytes, exactly one byte should be provided") } else { bytes.apply(0).asInstanceOf[T] } - case SShort => if (bytes.length != 2) { + case sigma.ShortType => if (bytes.length != 2) { throw new IllegalArgumentException("To deserialize SShort with fromBigEndianBytes, exactly two bytes should be provided") } else { val b0 = bytes(0) val b1 = bytes(1) ((b0 & 0xFF) << 8 | (b1 & 0xFF)).toShort.asInstanceOf[T] } - case SInt => if (bytes.length != 4) { + case sigma.IntType => if (bytes.length != 4) { throw new IllegalArgumentException("To deserialize SInt with fromBigEndianBytes, exactly four bytes should be provided") } else { Ints.fromByteArray(bytes.toArray).asInstanceOf[T] } - case SLong => if (bytes.length != 8) { + case sigma.LongType => if (bytes.length != 8) { throw new IllegalArgumentException("To deserialize SLong with fromBigEndianBytes, exactly eight bytes should be provided") } else { Longs.fromByteArray(bytes.toArray).asInstanceOf[T] } - case SBigInt => + case sigma.BigIntRType => if (bytes.length > SBigInt.MaxSizeInBytes) { throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") } CBigInt(new BigInteger(bytes.toArray)).asInstanceOf[T] // todo: UnsignedBitInt case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") - } } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index ad0450a1a7..c42533993c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -2135,7 +2135,9 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { override protected def collectMethods: Map[RMethod, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[SigmaDslBuilder]), RClass(classOf[SSigmaDslBuilder]), Set( - "Colls", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants", "decodePoint", "avlTree", "xor" + "Colls", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256", + "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants", + "decodePoint", "avlTree", "xor", "fromBigEndianBytes" )) } } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index ed97bf3f27..37141585c4 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,7 +3,6 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import scorex.util.encode.Base16 -import scorex.util.encode.Base16 import scorex.utils.Ints import sigma.Extensions.ArrayOps import sigma.VersionContext From bcfb24df6ed5bd95753e9164faa7e17efbddf0e8 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 26 Aug 2024 00:36:20 +0300 Subject: [PATCH 49/78] removing unused CSigmaDslBuilder.validationSettings --- data/shared/src/main/scala/sigma/ast/trees.scala | 4 ++-- data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/trees.scala b/data/shared/src/main/scala/sigma/ast/trees.scala index 7be73ad55a..33567868fd 100644 --- a/data/shared/src/main/scala/sigma/ast/trees.scala +++ b/data/shared/src/main/scala/sigma/ast/trees.scala @@ -653,7 +653,7 @@ case class SubstConstants[T <: SType](scriptBytes: Value[SByteArray], positions: val (newBytes, nConstants) = SubstConstants.eval( scriptBytes = scriptBytesV.toArray, positions = positionsV.toArray, - newVals = typedNewVals)(SigmaDsl.validationSettings) + newVals = typedNewVals) res = Colls.fromArray(newBytes) nConstants @@ -684,7 +684,7 @@ object SubstConstants extends ValueCompanion { */ def eval(scriptBytes: Array[Byte], positions: Array[Int], - newVals: Array[Constant[SType]])(implicit vs: SigmaValidationSettings): (Array[Byte], Int) = + newVals: Array[Constant[SType]]): (Array[Byte], Int) = ErgoTreeSerializer.DefaultSerializer.substituteConstants(scriptBytes, positions, newVals) } diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 4a3842e250..e5bb8920ae 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -20,7 +20,6 @@ import java.math.BigInteger * @see [[SigmaDslBuilder]] for detailed descriptions */ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => - implicit val validationSettings: SigmaValidationSettings = ValidationRules.currentSettings override val Colls: CollBuilder = sigma.Colls @@ -193,7 +192,7 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => case e: Throwable => throw new RuntimeException(s"Cannot evaluate substConstants($scriptBytes, $positions, $newValues)", e) } - val (res, _) = SubstConstants.eval(scriptBytes.toArray, positions.toArray, constants)(validationSettings) + val (res, _) = SubstConstants.eval(scriptBytes.toArray, positions.toArray, constants) Colls.fromArray(res) } From 877ed789ead40ae29811969e3b0f25feb0a68209 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 30 Aug 2024 19:30:44 +0300 Subject: [PATCH 50/78] adding new methods to GraphIRReflection --- .../src/main/scala/sigma/ast/methods.scala | 38 ++++++------------- .../sigma/compiler/ir/GraphIRReflection.scala | 16 ++++++++ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 84fc4955a5..0b69b4a2c0 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -922,8 +922,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { baseCost = JitCost(10), perChunkCost = JitCost(1), chunkSize = 10) val ZipMethod = SMethod(this, "zip", - SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)), - 29, Zip_CostKind) + SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)), 29, Zip_CostKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -939,11 +938,10 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + // ======== 6.0 methods below =========== - // 6.0 methods below val ReverseMethod = SMethod(this, "reverse", - SFunc(Array(ThisType), ThisType, paramIVSeq), - 30, Zip_CostKind) // todo: costing + SFunc(Array(ThisType), ThisType, paramIVSeq), 30, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -960,8 +958,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val DistinctMethod = SMethod(this, "distinct", - SFunc(Array(ThisType), ThisType, paramIVSeq), - 31, Zip_CostKind) // todo: costing + SFunc(Array(ThisType), ThisType, paramIVSeq), 31, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -978,8 +975,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val StartsWithMethod = SMethod(this, "startsWith", - SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), - 32, Zip_CostKind) // todo: costing + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -996,8 +992,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val EndsWithMethod = SMethod(this, "endsWith", - SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), - 33, Zip_CostKind) // todo: costing + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, Zip_CostKind) // todo: costing .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -1018,18 +1013,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") - /** Implements evaluation of Coll.zip method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def get_eval[A](mc: MethodCall, xs: Coll[A], index: Int) - (implicit E: ErgoTreeEvaluator): Option[A] = { - E.addCost(ByIndex.costKind, mc.method.opDesc) //todo: costing - ??? // todo: this get is not actually executed, why? - xs.get(index) - } - - private val v5Methods = Seq( + private val v5Methods = super.getMethods() ++ Seq( SizeMethod, GetOrElseMethod, MapMethod, @@ -1049,7 +1033,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { ZipMethod ) - private val v6Methods = Seq( + private val v6Methods = v5Methods ++ Seq( ReverseMethod, DistinctMethod, StartsWithMethod, @@ -1061,10 +1045,10 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { * Typical override: `super.getMethods() ++ Seq(m1, m2, m3)` */ override protected def getMethods(): Seq[SMethod] = { - if(VersionContext.current.isV6SoftForkActivated) { - super.getMethods() ++ v5Methods ++ v6Methods + if (VersionContext.current.isV6SoftForkActivated) { + v6Methods } else { - super.getMethods() ++ v5Methods + v5Methods } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala index 69736a0224..829a37f871 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -202,6 +202,22 @@ object GraphIRReflection { }, mkMethod(clazz, "exists", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.Coll[Any]].exists(args(0).asInstanceOf[ctx.Ref[Any => Boolean]]) + }, + // V6 methods + mkMethod(clazz, "reverse", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[ctx.Coll[Any]].reverse + }, + mkMethod(clazz, "distinct", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[ctx.Coll[Any]].distinct + }, + mkMethod(clazz, "startsWith", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Coll[Any]].startsWith(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Any]]]) + }, + mkMethod(clazz, "endsWith", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Coll[Any]].endsWith(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Any]]]) + }, + mkMethod(clazz, "get", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Coll[_]].apply(args(0).asInstanceOf[ctx.Ref[Int]]) } ) ) From 7e3b67625223a1db2f3af52c2a66b1eb8e13357b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 16 Sep 2024 16:49:38 +0300 Subject: [PATCH 51/78] fixing method calls check test --- .../sigmastate/ErgoTreeSpecification.scala | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 2f7217bc4c..df15125488 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -326,13 +326,19 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C (SInt.typeId, Seq.empty[MInfo], false), (SLong.typeId, Seq.empty[MInfo], false), - { // SNumericType.typeId is erroneously shadowed by SGlobal.typeId - // this should be preserved in v3.x and fixed in v4.0 - (SNumericType.typeId, Seq( - MInfo(methodId = 1, SGlobalMethods.groupGeneratorMethod), - MInfo(2, SGlobalMethods.xorMethod) - ), true) - }, + (if(!isV6Activated) { + { // 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) + } + } 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) + }), { // SBigInt inherit methods from SNumericType.methods // however they are not resolvable via SBigInt.typeId @@ -370,7 +376,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C MInfo(5, IdMethod), MInfo(6, creationInfoMethod), MInfo(8, tokensMethod) - ) ++ (if (VersionContext.current.isV6SoftForkActivated) { + ) ++ (if (isV6Activated) { Seq(MInfo(7, getRegMethodV6)) } else { Seq(MInfo(7, getRegMethodV5)) @@ -404,8 +410,8 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C MInfo(7, timestampMethod), MInfo(8, nBitsMethod), MInfo(9, heightMethod), MInfo(10, extensionRootMethod), MInfo(11, minerPkMethod), MInfo(12, powOnetimePkMethod), MInfo(13, powNonceMethod), MInfo(14, powDistanceMethod), MInfo(15, votesMethod) - ) ++ (if (VersionContext.current.isV6SoftForkActivated) { - Seq(MInfo(16, checkPowMethod)) + ) ++ (if (isV6Activated) { + Seq(MInfo(16, checkPowMethod)) // methods added in v6.0 } else { Seq.empty[MInfo] }), true) @@ -428,10 +434,11 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C { import SGlobalMethods._ (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) - ) ++ (if (isV6Activated) Seq( - // methods added in v6.0 - MInfo(3, serializeMethod) - ) else Seq.empty), true) + ) ++ (if (isV6Activated) { + Seq(MInfo(3, serializeMethod)) // methods added in v6.0 + } else { + Seq.empty[MInfo] + }), true) }, { import SCollectionMethods._ (SCollection.typeId, Seq( @@ -496,6 +503,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C assert(canHaveMethods, s"Type $tyDesc should NOT have methods") val mc = MethodsContainer(tyDesc.typeId) + mc.methods.length shouldBe methods.length for (expectedMethod <- methods) { if (expectedMethod.isResolvableFromIds) { From 4343bc9d0bbaacebbc11dcbc4f476f8f73b3c40b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 17 Sep 2024 13:01:56 +0300 Subject: [PATCH 52/78] fix for longToByteArray support in JS --- .../shared/src/main/scala/sigma/SigmaDataReflection.scala | 8 ++++++++ .../scala/sigmastate/utxo/BasicOpsSpecification.scala | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala index a6e5de2a26..c64bdee877 100644 --- a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala +++ b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala @@ -86,6 +86,14 @@ object SigmaDataReflection { ) ) + registerClassEntry(classOf[LongToByteArray], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new LongToByteArray(args(0).asInstanceOf[Value[SLong.type]]) + } + ) + ) + registerClassEntry(classOf[CalcBlake2b256], constructors = Array( mkConstructor(Array(classOf[Value[_]])) { args => diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 72f4063642..4a5a9be59e 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -213,8 +213,8 @@ class BasicOpsSpecification extends CompilerTestingCommons deserTest() } } - - property("serialize(long) is producing different result from longToByteArray()") { + + property("serialize(long) is producing different result from longToByteArray()") { def deserTest() = test("serialize", env, ext, s"""{ val l = -1000L From 1c2b99df06c840955f9dc7b57164ec6ee11979a0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 17 Sep 2024 17:35:17 +0300 Subject: [PATCH 53/78] more polishing in UnsignedBigInt impl --- .../src/main/scala/sigma/SigmaDsl.scala | 24 +++++++ .../src/main/scala/sigma/ast/SType.scala | 70 +++++++++++-------- .../src/main/scala/sigma/data/CBigInt.scala | 34 ++++++--- .../sigma/reflection/ReflectionData.scala | 30 +++++++- .../main/scala/sigma/util/Extensions.scala | 11 ++- .../scala/sigma/data/CSigmaDslBuilder.scala | 2 +- 6 files changed, 126 insertions(+), 45 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 8c8a3c2be6..3f060c1af2 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -170,8 +170,15 @@ trait BigInt { */ def shiftRight(n: Int): BigInt + /** + * @return unsigned representation of this BigInt, or exception if its value is negative + */ def toUnsigned: UnsignedBigInt + /** + * @return unsigned representation of this BigInt modulo `m`. Cryptographic mod operation is done, ie result is + * non-negative always + */ def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt } @@ -297,6 +304,23 @@ trait UnsignedBigInt { def subtractMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt def multiplyMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt + /** + * @return a big integer whose value is `this xor that` + */ + def xor(that: UnsignedBigInt): UnsignedBigInt + + /** + * @return a 256-bit signed integer whose value is (this << n). The shift distance, n, may be negative, + * in which case this method performs a right shift. (Computes floor(this * 2n).) + */ + def shiftLeft(n: Int): UnsignedBigInt + + /** + * @return a 256-bit signed integer whose value is (this >> n). Sign extension is performed. The shift distance, n, + * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).) + */ + def shiftRight(n: Int): UnsignedBigInt + def toSigned(): BigInt } diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 815a70e222..c99b7b8e34 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -144,7 +144,7 @@ object SType { SBoolean, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, SAvlTree, SBox, SOption, SCollection, SBigInt ) - private val v6Types = v5Types ++ Seq(SByte, SShort, SInt, SLong) + private val v6Types = v5Types ++ Seq(SByte, SShort, SInt, SLong, SUnsignedBigInt) private val v5TypesMap = v5Types.map { t => (t.typeId, t) }.toMap @@ -398,6 +398,7 @@ case object SByte extends SPrimType with SEmbeddable with SNumericType with SMon case i: Int => i.toByteExact case l: Long => l.toByteExact case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toByte // toByteExact from int is called under the hood + case ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toByte // toByteExact from int is called under the hood case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -420,6 +421,7 @@ case object SShort extends SPrimType with SEmbeddable with SNumericType with SMo case i: Int => i.toShortExact case l: Long => l.toShortExact case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toShort // toShortExact from int is called under the hood + case ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toShort // toShortExact from int is called under the hood case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -444,6 +446,7 @@ case object SInt extends SPrimType with SEmbeddable with SNumericType with SMono case i: Int => i case l: Long => l.toIntExact case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toInt + case ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toInt case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -470,6 +473,7 @@ case object SLong extends SPrimType with SEmbeddable with SNumericType with SMon case i: Int => i.toLong case l: Long => l case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toLong + case ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toLong case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -687,15 +691,16 @@ object SOption extends STypeCompanion { override val reprClass: RClass[_] = RClass(classOf[Option[_]]) - type SBooleanOption = SOption[SBoolean.type] - type SByteOption = SOption[SByte.type] - type SShortOption = SOption[SShort.type] - type SIntOption = SOption[SInt.type] - type SLongOption = SOption[SLong.type] - type SBigIntOption = SOption[SBigInt.type] - type SGroupElementOption = SOption[SGroupElement.type] - type SBoxOption = SOption[SBox.type] - type SAvlTreeOption = SOption[SAvlTree.type] + type SBooleanOption = SOption[SBoolean.type] + type SByteOption = SOption[SByte.type] + type SShortOption = SOption[SShort.type] + type SIntOption = SOption[SInt.type] + type SLongOption = SOption[SLong.type] + type SBigIntOption = SOption[SBigInt.type] + type SUnsignedBigIntOption = SOption[SUnsignedBigInt.type] + type SGroupElementOption = SOption[SGroupElement.type] + type SBoxOption = SOption[SBox.type] + type SAvlTreeOption = SOption[SAvlTree.type] /** This descriptors are instantiated once here and then reused. */ implicit val SByteOption = SOption(SByte) @@ -704,6 +709,7 @@ object SOption extends STypeCompanion { implicit val SIntOption = SOption(SInt) implicit val SLongOption = SOption(SLong) implicit val SBigIntOption = SOption(SBigInt) + implicit val SUnsignedBigIntOption = SOption(SUnsignedBigInt) implicit val SBooleanOption = SOption(SBoolean) implicit val SAvlTreeOption = SOption(SAvlTree) implicit val SGroupElementOption = SOption(SGroupElement) @@ -764,29 +770,31 @@ object SCollection extends STypeCompanion { def apply[T <: SType](elemType: T): SCollection[T] = SCollectionType(elemType) def apply[T <: SType](implicit elemType: T, ov: Overloaded1): SCollection[T] = SCollectionType(elemType) - type SBooleanArray = SCollection[SBoolean.type] - type SByteArray = SCollection[SByte.type] - type SShortArray = SCollection[SShort.type] - type SIntArray = SCollection[SInt.type] - type SLongArray = SCollection[SLong.type] - type SBigIntArray = SCollection[SBigInt.type] - type SGroupElementArray = SCollection[SGroupElement.type] - type SBoxArray = SCollection[SBox.type] - type SAvlTreeArray = SCollection[SAvlTree.type] + type SBooleanArray = SCollection[SBoolean.type] + type SByteArray = SCollection[SByte.type] + type SShortArray = SCollection[SShort.type] + type SIntArray = SCollection[SInt.type] + type SLongArray = SCollection[SLong.type] + type SBigIntArray = SCollection[SBigInt.type] + type SUnsignedBigIntArray = SCollection[SUnsignedBigInt.type] + type SGroupElementArray = SCollection[SGroupElement.type] + type SBoxArray = SCollection[SBox.type] + type SAvlTreeArray = SCollection[SAvlTree.type] /** This descriptors are instantiated once here and then reused. */ - val SBooleanArray = SCollection(SBoolean) - val SByteArray = SCollection(SByte) - val SByteArray2 = SCollection(SCollection(SByte)) - val SShortArray = SCollection(SShort) - val SIntArray = SCollection(SInt) - val SLongArray = SCollection(SLong) - val SBigIntArray = SCollection(SBigInt) - val SGroupElementArray = SCollection(SGroupElement) - val SSigmaPropArray = SCollection(SSigmaProp) - val SBoxArray = SCollection(SBox) - val SAvlTreeArray = SCollection(SAvlTree) - val SHeaderArray = SCollection(SHeader) + val SBooleanArray = SCollection(SBoolean) + val SByteArray = SCollection(SByte) + val SByteArray2 = SCollection(SCollection(SByte)) + val SShortArray = SCollection(SShort) + val SIntArray = SCollection(SInt) + val SLongArray = SCollection(SLong) + val SBigIntArray = SCollection(SBigInt) + val SUnsignedBigIntArray = SCollection(SUnsignedBigInt) + val SGroupElementArray = SCollection(SGroupElement) + val SSigmaPropArray = SCollection(SSigmaProp) + val SBoxArray = SCollection(SBox) + val SAvlTreeArray = SCollection(SAvlTree) + val SHeaderArray = SCollection(SHeader) } /** Type descriptor of tuple type. */ diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 1d1478e0c4..8212955103 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -28,11 +28,11 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr override def signum: Int = wrappedValue.signum() - override def add(that: BigInt): BigInt = CBigInt(wrappedValue.add(that.asInstanceOf[CBigInt].wrappedValue).to256BitValueExact) + override def add(that: BigInt): BigInt = CBigInt(wrappedValue.add(that.asInstanceOf[CBigInt].wrappedValue).toSignedBigIntValueExact) - override def subtract(that: BigInt): BigInt = CBigInt(wrappedValue.subtract(that.asInstanceOf[CBigInt].wrappedValue).to256BitValueExact) + override def subtract(that: BigInt): BigInt = CBigInt(wrappedValue.subtract(that.asInstanceOf[CBigInt].wrappedValue).toSignedBigIntValueExact) - override def multiply(that: BigInt): BigInt = CBigInt(wrappedValue.multiply(that.asInstanceOf[CBigInt].wrappedValue).to256BitValueExact) + override def multiply(that: BigInt): BigInt = CBigInt(wrappedValue.multiply(that.asInstanceOf[CBigInt].wrappedValue).toSignedBigIntValueExact) override def divide(that: BigInt): BigInt = CBigInt(wrappedValue.divide(that.asInstanceOf[CBigInt].wrappedValue)) @@ -44,7 +44,7 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr override def max(that: BigInt): BigInt = CBigInt(wrappedValue.max(that.asInstanceOf[CBigInt].wrappedValue)) - override def negate(): BigInt = CBigInt(wrappedValue.negate().to256BitValueExact) + override def negate(): BigInt = CBigInt(wrappedValue.negate().toSignedBigIntValueExact) override def and(that: BigInt): BigInt = CBigInt(wrappedValue.and(that.asInstanceOf[CBigInt].wrappedValue)) @@ -52,9 +52,9 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr 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 shiftLeft(n: Int): BigInt = CBigInt(wrappedValue.shiftLeft(n).toSignedBigIntValueExact) - override def shiftRight(n: Int): BigInt = CBigInt(wrappedValue.shiftRight(n).to256BitValueExact) + override def shiftRight(n: Int): BigInt = CBigInt(wrappedValue.shiftRight(n).toSignedBigIntValueExact) def toUnsigned: UnsignedBigInt = { if(this.wrappedValue.compareTo(BigInteger.ZERO) < 0){ @@ -88,12 +88,13 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign override def compareTo(that: UnsignedBigInt): Int = wrappedValue.compareTo(that.asInstanceOf[CUnsignedBigInt].wrappedValue) - //todo: consider result's bits limit - override def add(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.add(that.asInstanceOf[CUnsignedBigInt].wrappedValue).to256BitValueExact) + override def add(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.add(that.asInstanceOf[CUnsignedBigInt].wrappedValue).toUnsignedBigIntValueExact) - override def subtract(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.subtract(that.asInstanceOf[CUnsignedBigInt].wrappedValue).to256BitValueExact) + override def subtract(that: UnsignedBigInt): UnsignedBigInt = { + CUnsignedBigInt(wrappedValue.subtract(that.asInstanceOf[CUnsignedBigInt].wrappedValue).toUnsignedBigIntValueExact) + } - override def multiply(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.multiply(that.asInstanceOf[CUnsignedBigInt].wrappedValue).to256BitValueExact) + override def multiply(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.multiply(that.asInstanceOf[CUnsignedBigInt].wrappedValue).toUnsignedBigIntValueExact) override def divide(that: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.divide(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) @@ -129,7 +130,18 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign CUnsignedBigInt(wrappedValue.multiply(thatBi).mod(mBi)) } + /** + * @return a big integer whose value is `this xor that` + */ + def xor(that: UnsignedBigInt): UnsignedBigInt = { + CUnsignedBigInt(wrappedValue.xor(that.asInstanceOf[CUnsignedBigInt].wrappedValue)) + } + + override def shiftLeft(n: Int): UnsignedBigInt = CUnsignedBigInt(wrappedValue.shiftLeft(n).toUnsignedBigIntValueExact) + + override def shiftRight(n: Int): UnsignedBigInt = CUnsignedBigInt(wrappedValue.shiftRight(n).toUnsignedBigIntValueExact) + override def toSigned(): BigInt = { - CBigInt(wrappedValue.to256BitValueExact) + CBigInt(wrappedValue.toSignedBigIntValueExact) } } diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index d94e7b7e0d..53d6c256f2 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -128,7 +128,35 @@ object ReflectionData { ) ) } - //todo: add UnsignedBigInt + { + val clazz = classOf[sigma.UnsignedBigInt] + val paramTypes = Array[Class[_]](clazz) + registerClassEntry(clazz, + methods = Map( + mkMethod(clazz, "add", paramTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].add(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "max", paramTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].max(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "min", paramTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].min(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "subtract", paramTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].subtract(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "multiply", paramTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].multiply(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "mod", paramTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "divide", paramTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].divide(args(0).asInstanceOf[UnsignedBigInt]) + } + ) + ) + } { val clazz = classOf[CollBuilder] registerClassEntry(clazz, diff --git a/core/shared/src/main/scala/sigma/util/Extensions.scala b/core/shared/src/main/scala/sigma/util/Extensions.scala index 624b3f5d6b..149d61f6c2 100644 --- a/core/shared/src/main/scala/sigma/util/Extensions.scala +++ b/core/shared/src/main/scala/sigma/util/Extensions.scala @@ -204,7 +204,7 @@ object Extensions { * not exactly fit in a 256 bit range. * @see BigInteger#longValueExact */ - @inline final def to256BitValueExact: BigInteger = { + @inline final def toSignedBigIntValueExact: BigInteger = { // Comparing with 255 is correct because bitLength() method excludes the sign bit. // For example, these are the boundary values: // (new BigInteger("80" + "00" * 31, 16)).bitLength() = 256 @@ -217,6 +217,15 @@ object Extensions { throw new ArithmeticException("BigInteger out of 256 bit range"); } + @inline final def toUnsignedBigIntValueExact: BigInteger = { + // todo: make the check soft-forkable + if (x.compareTo(BigInteger.ZERO) >= 0 && x.bitLength() <= 256) { + x + } else { + throw new ArithmeticException("Unsigned BigInteger out of 256 bit range or negative") + } + } + /** Converts `x` to [[sigma.BigInt]] */ def toBigInt: sigma.BigInt = CBigInt(x) } diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index e5bb8920ae..b74f91b133 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -150,7 +150,7 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => } override def byteArrayToBigInt(bytes: Coll[Byte]): BigInt = { - val bi = new BigInteger(bytes.toArray).to256BitValueExact + val bi = new BigInteger(bytes.toArray).toSignedBigIntValueExact this.BigInt(bi) } From cb51ba8bf3a100982a040d276356fd15f615c36a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 17 Sep 2024 19:07:01 +0300 Subject: [PATCH 54/78] removing access to type before 6.0, more tests --- .../src/main/scala/sigma/ast/SType.scala | 25 ++++++++--- .../utxo/BasicOpsSpecification.scala | 44 ++++++++++++++++++- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index c99b7b8e34..7673c11dec 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -102,12 +102,23 @@ object SType { /** Immutable empty IndexedSeq, can be used to avoid repeated allocations. */ val EmptySeq: IndexedSeq[SType] = EmptyArray + private val v5PredefTypes = Array[SType]( + SBoolean, SByte, SShort, SInt, SLong, SBigInt, SContext, + SGlobal, SHeader, SPreHeader, SAvlTree, SGroupElement, SSigmaProp, SString, SBox, + SUnit, SAny) + + private val v6PredefTypes = v5PredefTypes ++ Array(SUnsignedBigInt) + + /** All pre-defined types should be listed here. Note, NoType is not listed. * Should be in sync with sigmastate.lang.Types.predefTypes. */ - val allPredefTypes: Seq[SType] = Array[SType]( - SBoolean, SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt, SContext, - SGlobal, SHeader, SPreHeader, SAvlTree, SGroupElement, SSigmaProp, SString, SBox, - SUnit, SAny) + def allPredefTypes: Seq[SType] = { + if(VersionContext.current.isV6SoftForkActivated) { + v6PredefTypes + } else { + v5PredefTypes + } + } /** A mapping of object types supporting MethodCall operations. For each serialized * typeId this map contains a companion object which can be used to access the list of @@ -177,7 +188,7 @@ object SType { case SInt => x.isInstanceOf[Int] case SLong => x.isInstanceOf[Long] case SBigInt => x.isInstanceOf[BigInt] - case SUnsignedBigInt => x.isInstanceOf[UnsignedBigInt] + case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated => x.isInstanceOf[UnsignedBigInt] case SGroupElement => x.isInstanceOf[GroupElement] case SSigmaProp => x.isInstanceOf[SigmaProp] case SBox => x.isInstanceOf[Box] @@ -360,8 +371,6 @@ trait SNumericType extends SProduct with STypeCompanion { } object SNumericType extends STypeCompanion { - /** Array of all numeric types ordered by number of bytes in the representation. */ - final val allNumericTypes = Array(SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt) // TODO v6.0: this typeId is now shadowed by SGlobal.typeId // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 @@ -537,6 +546,7 @@ case object SUnsignedBigInt extends SPrimType with SEmbeddable with SNumericType case x: Short => BigInteger.valueOf(x.toLong) case x: Int => BigInteger.valueOf(x.toLong) case x: Long => BigInteger.valueOf(x) + case x: UnsignedBigInt => x.asInstanceOf[CUnsignedBigInt].wrappedValue case _ => sys.error(s"Cannot upcast value $v to the type $this") } CUnsignedBigInt(bi) @@ -547,6 +557,7 @@ case object SUnsignedBigInt extends SPrimType with SEmbeddable with SNumericType case x: Short => BigInteger.valueOf(x.toLong) case x: Int => BigInteger.valueOf(x.toLong) case x: Long => BigInteger.valueOf(x) + case x: UnsignedBigInt => x.asInstanceOf[CUnsignedBigInt].wrappedValue case _ => sys.error(s"Cannot downcast value $v to the type $this") } CUnsignedBigInt(bi) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 8c29783db5..2ecace1690 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -163,7 +163,11 @@ class BasicOpsSpecification extends CompilerTestingCommons true )} - deserTest() // todo: should fail < 6.0 + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy deserTest() + } else { + deserTest() + } } property("signed -> unsigned bigint conversion - positive bigint") { @@ -224,6 +228,44 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("unsigned bigint - add") { + def conversionTest() = {test("add", env, ext, + s"""{ + | val a = unsignedBigInt("5") + | val b = unsignedBigInt("10") + | val res = a + b + | res == 15 + | } """.stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + conversionTest() + } + } + + property("unsigned bigint - subtract with neg result") { + def conversionTest() = {test("subtract", env, ext, + s"""{ + | val a = unsignedBigInt("5") + | val b = unsignedBigInt("10") + | val res = a - b + | res >= 0 + | } """.stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + an[Exception] should be thrownBy conversionTest() + } + } + property("unsigned -> signed bigint conversion") { def conversionTest() = {test("conversion", env, ext, s"""{ From 43db8df7e01aceed7f5f6aebc13ecf23d44996cd Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 23 Sep 2024 13:02:22 +0300 Subject: [PATCH 55/78] scrypto 3.0.0 --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 85c59512d6..ab9d931172 100644 --- a/build.sbt +++ b/build.sbt @@ -82,9 +82,9 @@ ThisBuild / dynverSeparator := "-" val bouncycastleBcprov = "org.bouncycastle" % "bcprov-jdk15on" % "1.66" -val scrypto = "org.scorexfoundation" %% "scrypto" % "2.3.0-4-a0bc6176-SNAPSHOT" +val scrypto = "org.scorexfoundation" %% "scrypto" % "3.0.0" val scryptoDependency = - libraryDependencies += "org.scorexfoundation" %%% "scrypto" % "2.3.0-4-a0bc6176-SNAPSHOT" + libraryDependencies += "org.scorexfoundation" %%% "scrypto" % "3.0.0" val scorexUtil = "org.scorexfoundation" %% "scorex-util" % "0.2.1" val scorexUtilDependency = From 8136c8f620b9097cd6313d4971aafbc113507a50 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 24 Sep 2024 19:37:07 +0300 Subject: [PATCH 56/78] new methods added to ReflectionData --- .../scala/sigma/reflection/ReflectionData.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index a2de9dd8c5..bfe7a639e3 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -162,6 +162,9 @@ object ReflectionData { mkMethod(clazz, "apply", Array[Class[_]](classOf[Int])) { (obj, args) => obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int]) }, + mkMethod(clazz, "get", Array[Class[_]](classOf[Int])) { (obj, args) => + obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int]) + }, mkMethod(clazz, "append", Array[Class[_]](classOf[Coll[_]])) { (obj, args) => obj.asInstanceOf[Coll[Any]].append(args(0).asInstanceOf[Coll[Any]]) }, @@ -170,6 +173,18 @@ object ReflectionData { }, mkMethod(clazz, "map", Array[Class[_]](classOf[Function1[_, _]], classOf[RType[_]])) { (obj, args) => obj.asInstanceOf[Coll[Any]].map(args(0).asInstanceOf[Any => Any])(args(1).asInstanceOf[RType[Any]]) + }, + mkMethod(clazz, "reverse", Array[Class[_]]()) { (obj, args) => + obj.asInstanceOf[Coll[Any]].reverse + }, + mkMethod(clazz, "distinct", Array[Class[_]]()) { (obj, args) => + obj.asInstanceOf[Coll[Any]].distinct + }, + mkMethod(clazz, "startsWith", Array[Class[_]](classOf[Coll[_]])) { (obj, args) => + obj.asInstanceOf[Coll[Any]].startsWith(args(0).asInstanceOf[Coll[Any]]) + }, + mkMethod(clazz, "endsWith", Array[Class[_]](classOf[Coll[_]])) { (obj, args) => + obj.asInstanceOf[Coll[Any]].endsWith(args(0).asInstanceOf[Coll[Any]]) } ) ) From 586aaddc6c7b2aec1dd195f8d15020490060f6fa Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 26 Sep 2024 19:35:02 +0300 Subject: [PATCH 57/78] LSV6 tests, predefined method --- .../sigma/reflection/ReflectionData.scala | 3 + .../main/scala/sigma/ast/SigmaPredef.scala | 22 ++++- .../scala/sigma/LanguageSpecificationV6.scala | 94 ++++++++++++++++++- .../sigmastate/ErgoTreeSpecification.scala | 2 +- .../utxo/BasicOpsSpecification.scala | 19 +++- 5 files changed, 133 insertions(+), 7 deletions(-) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index a2de9dd8c5..ef0b37de10 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -450,6 +450,9 @@ object ReflectionData { }, mkMethod(clazz, "decodePoint", Array[Class[_]](cColl)) { (obj, args) => obj.asInstanceOf[SigmaDslBuilder].decodePoint(args(0).asInstanceOf[Coll[Byte]]) + }, + mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](cColl, classOf[RType[_]])) { (obj, args) => + obj.asInstanceOf[SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[Coll[Byte]])(args(1).asInstanceOf[RType[_]]) } ) ) diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 8b89851938..96573d095d 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -420,6 +420,25 @@ object SigmaPredef { ) ) + val FromBigEndianBytesFunc = PredefinedFunc("fromBigEndianBytes", + Lambda(Seq(paramT), Array("bytes" -> SByteArray), tT, None), + irInfo = PredefFuncInfo( + irBuilder = { case (u, args) => + val resType = u.opType.tRange.asInstanceOf[SFunc].tRange + MethodCall( + Global, + SGlobalMethods.fromBigEndianBytesMethod.withConcreteTypes(Map(tT -> resType)), + args.toIndexedSeq, + Map(tT -> resType) + ) + }), + docInfo = OperationInfo(MethodCall, + """Deserializes provided big endian bytes into a numeric value of given type. + """.stripMargin, + Seq(ArgInfo("bytes", "bytes to deserialize")) + ) + ) + val globalFuncs: Map[String, PredefinedFunc] = Seq( AllOfFunc, AnyOfFunc, @@ -448,7 +467,8 @@ object SigmaPredef { SubstConstantsFunc, ExecuteFromVarFunc, ExecuteFromSelfRegFunc, - SerializeFunc + SerializeFunc, + FromBigEndianBytesFunc ).map(f => f.name -> f).toMap def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 7605043cea..bbfd260f21 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -11,11 +11,10 @@ import sigma.ast.ErgoTree.ZeroHeader import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast.{SInt, _} -import sigma.data.{CBigInt, CBox, CHeader, ExactNumeric} +import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, RType} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode -import sigma.data.{RType} -import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} +import sigma.util.Extensions.{BooleanOps, IntOps} import sigmastate.exceptions.MethodNotFound import sigmastate.utils.Extensions.ByteOpsForSigma import sigmastate.utils.Helpers @@ -1523,4 +1522,93 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + + property("Global - fromBigEndianBytes") { + import sigma.data.OrderingOps.BigIntOrdering + + def byteFromBigEndianBytes: Feature[Byte, Boolean] = { + newFeature( + { (x: Byte) => CSigmaDslBuilder.fromBigEndianBytes[Byte](Colls.fromArray(Array(x))) == x}, + "{ (x: Byte) => fromBigEndianBytes[Byte](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + 5.toByte -> new Expected(ExpectedResult(Success(true), None)), + Byte.MaxValue -> new Expected(ExpectedResult(Success(true), None)), + Byte.MinValue -> new Expected(ExpectedResult(Success(true), None)) + ), + byteFromBigEndianBytes + ) + + def shortFromBigEndianBytes: Feature[Short, Boolean] = { + newFeature( + { (x: Short) => CSigmaDslBuilder.fromBigEndianBytes[Short](Colls.fromArray(Shorts.toByteArray(x))) == x}, + "{ (x: Short) => fromBigEndianBytes[Short](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + 5.toShort -> new Expected(ExpectedResult(Success(true), None)), + Short.MaxValue -> new Expected(ExpectedResult(Success(true), None)), + Short.MinValue -> new Expected(ExpectedResult(Success(true), None)) + ), + shortFromBigEndianBytes + ) + + def intFromBigEndianBytes: Feature[Int, Boolean] = { + newFeature( + { (x: Int) => CSigmaDslBuilder.fromBigEndianBytes[Int](Colls.fromArray(Ints.toByteArray(x))) == x}, + "{ (x: Int) => fromBigEndianBytes[Int](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + 5 -> new Expected(ExpectedResult(Success(true), None)), + Int.MaxValue -> new Expected(ExpectedResult(Success(true), None)) + ), + intFromBigEndianBytes + ) + + def longFromBigEndianBytes: Feature[Long, Boolean] = { + newFeature( + { (x: Long) => CSigmaDslBuilder.fromBigEndianBytes[Long](Colls.fromArray(Longs.toByteArray(x))) == x}, + "{ (x: Long) => fromBigEndianBytes[Long](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + 5L -> new Expected(ExpectedResult(Success(true), None)), + Long.MinValue -> new Expected(ExpectedResult(Success(true), None)) + ), + longFromBigEndianBytes + ) + + def bigIntFromBigEndianBytes: Feature[BigInt, Boolean] = { + newFeature( + { (x: BigInt) => CSigmaDslBuilder.fromBigEndianBytes[BigInt](x.toBytes) == x}, + "{ (x: BigInt) => Global.fromBigEndianBytes[BigInt](x.toBytes) == x }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + CBigInt(BigInteger.valueOf(50)) -> new Expected(ExpectedResult(Success(true), None)), + CBigInt(BigInteger.valueOf(-500000000000L)) -> new Expected(ExpectedResult(Success(true), None)), + CBigInt(sigma.crypto.CryptoConstants.groupOrder.divide(BigInteger.valueOf(2))) -> new Expected(ExpectedResult(Success(true), None)) + ), + bigIntFromBigEndianBytes + ) + + } + } diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 4403716e5a..fee93248f7 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -516,7 +516,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) ) ++ (if (isV6Activated) { - Seq(MInfo(3, serializeMethod)) // methods added in v6.0 + Seq(MInfo(3, serializeMethod), MInfo(5, fromBigEndianBytesMethod)) // methods added in v6.0 } else { Seq.empty[MInfo] }), true) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 47815ea41f..16ffaa3dbb 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -622,6 +622,23 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("Global.fromBigEndianBytes - Long.toBytes") { + def fromTest() = test("fromBigEndianBytes - long", env, ext, + s"""{ + | val l = 1088800L + | val ba = l.toBytes + | Global.fromBigEndianBytes[Long](ba) == l + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[Exception] should be thrownBy(fromTest()) + } + } + property("Global.fromBigEndianBytes - bigInt") { val bi = new BigInteger("9785856985394593489356430476450674590674598659865986594859056865984690568904") def fromTest() = test("fromBigEndianBytes - bigInt", env, ext, @@ -639,8 +656,6 @@ class BasicOpsSpecification extends CompilerTestingCommons } } - // todo: roundtrip with .toBytes - property("Int.toBytes") { def toBytesTest() = test("Int.toBytes", env, ext, """{ From 2cdc34eaeb57fc77824f65f4379887e4feb5ab76 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 27 Sep 2024 00:20:34 +0300 Subject: [PATCH 58/78] fixing JS tests --- .../src/main/scala/sigma/compiler/ir/GraphIRReflection.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala index dcdce623ac..79fba9bb96 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -1,5 +1,6 @@ package sigma.compiler.ir +import sigma.ast.SType import sigma.compiler.ir.primitives.Thunks import sigma.data.RType import sigma.reflection.ReflectionData.registerClassEntry @@ -510,6 +511,9 @@ object GraphIRReflection { }, mkMethod(clazz, "serialize", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.SigmaDslBuilder].serialize(args(0).asInstanceOf[ctx.Ref[Any]]) + }, + mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])(args(1).asInstanceOf[ctx.Elem[SType]]) } ) ) From d738ac54f5d4b96a5f5927e96bc1210fb9b02fc4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 27 Sep 2024 14:02:15 +0300 Subject: [PATCH 59/78] fixing JS tests --- .../scala/sigma/reflection/ReflectionData.scala | 2 +- .../main/scala/sigma/SigmaDataReflection.scala | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index bfe7a639e3..d2165711f6 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -163,7 +163,7 @@ object ReflectionData { obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int]) }, mkMethod(clazz, "get", Array[Class[_]](classOf[Int])) { (obj, args) => - obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int]) + obj.asInstanceOf[Coll[_]].get(args(0).asInstanceOf[Int]) }, mkMethod(clazz, "append", Array[Class[_]](classOf[Coll[_]])) { (obj, args) => obj.asInstanceOf[Coll[Any]].append(args(0).asInstanceOf[Coll[Any]]) diff --git a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala index c64bdee877..341ee647b3 100644 --- a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala +++ b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala @@ -317,6 +317,22 @@ object SigmaDataReflection { mkMethod(clazz, "flatMap_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Function1[_,_]], classOf[ErgoTreeEvaluator])) { (obj, args) => obj.asInstanceOf[SCollectionMethods.type].flatMap_eval(args(0).asInstanceOf[MethodCall], args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Any => Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "reverse_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].reverse_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "distinct_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].distinct_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "startsWith_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].startsWith_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "endsWith_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].endsWith_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) } ) ) From 1989d197dc9461683bd351e31be540271ac94bec Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 27 Sep 2024 15:15:07 +0300 Subject: [PATCH 60/78] LSV6 tests --- .../scala/sigma/LanguageSpecificationV6.scala | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 7605043cea..d9797e36bf 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1523,4 +1523,94 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + property("Coll.reverse") { + val f = newFeature[Coll[Int], Coll[Int]]( + { (xs: Coll[Int]) => xs.reverse }, + """{(xs: Coll[Int]) => xs.reverse }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + Coll(1, 2) -> Expected(ExpectedResult(Success(Coll(2, 1)), None)), + Coll[Int]() -> Expected(ExpectedResult(Success(Coll[Int]()), None)) + ), + f + ) + } + + property("Coll.distinct") { + val f = newFeature[Coll[Int], Coll[Int]]( + { (xs: Coll[Int]) => xs.distinct }, + """{(xs: Coll[Int]) => xs.distinct }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + Coll(1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll(1, 1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll(1, 2, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll[Int]() -> Expected(ExpectedResult(Success(Coll[Int]()), None)) + ), + f + ) + } + + property("Coll.startsWith") { + val f = newFeature[(Coll[Int], Coll[Int]), Boolean]( + { (xs: (Coll[Int], Coll[Int])) => xs._1.startsWith(xs._2) }, + """{(xs: (Coll[Int], Coll[Int])) => xs._1.startsWith(xs._2) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + (Coll(1, 2, 3), Coll(1, 2)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(1, 2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(1, 2, 3, 4)) -> Expected(ExpectedResult(Success(false), None)), + (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) + ), + f + ) + } + + property("Coll.endsWith") { + val f = newFeature[(Coll[Int], Coll[Int]), Boolean]( + { (xs: (Coll[Int], Coll[Int])) => xs._1.endsWith(xs._2) }, + """{(xs: (Coll[Int], Coll[Int])) => xs._1.endsWith(xs._2) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + (Coll(1, 2, 3), Coll(1, 2)) -> Expected(ExpectedResult(Success(false), None)), + (Coll(1, 2, 3), Coll(2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(2, 3, 4)) -> Expected(ExpectedResult(Success(false), None)), + (Coll(1, 2, 3), Coll(1, 2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) + ), + f + ) + } + + property("Coll.get") { + val f = newFeature[(Coll[Int], Int), Option[Int]]( + { (xs: (Coll[Int], Int)) => xs._1.get(xs._2) }, + """{(xs: (Coll[Int], Int)) => xs._1.get(xs._2) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + (Coll(1, 2), 0) -> Expected(ExpectedResult(Success(Some(1)), None)), + (Coll(1, 2), 1) -> Expected(ExpectedResult(Success(Some(2)), None)), + (Coll(1, 2), -1) -> Expected(ExpectedResult(Success(None), None)), + (Coll(1, 2), 2) -> Expected(ExpectedResult(Success(None), None)), + (Coll[Int](), 0) -> Expected(ExpectedResult(Success(None), None)) + ), + f + ) + } + } From 6459b07aa6b92ee6131af7a35fa9f63aa428b927 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 27 Sep 2024 16:06:10 +0300 Subject: [PATCH 61/78] fix ErgoTreeSpec --- .../src/test/scala/sigmastate/ErgoTreeSpecification.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 4403716e5a..f5db0217dc 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -561,7 +561,9 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C EndsWithMethod, MapReduceMethod, */ - ), true) + ) ++ (if (isV6Activated) { + Seq(MInfo(30, ReverseMethod), MInfo(31, DistinctMethod), MInfo(32, StartsWithMethod), MInfo(33, EndsWithMethod), MInfo(34, GetMethod)) + } else Seq.empty), true) }, { import SOptionMethods._ (SOption.typeId, Seq( From a3f25ef4353e0fc7bfc67b55d5fc3156b4471c16 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 30 Sep 2024 14:28:30 +0300 Subject: [PATCH 62/78] LSV6 tests --- .../scala/sigma/LanguageSpecificationV6.scala | 156 +++++++++++++++++- 1 file changed, 150 insertions(+), 6 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 7605043cea..822611ea71 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,24 +1,26 @@ package sigma -import org.ergoplatform.ErgoHeader +import org.ergoplatform.{ErgoBox, ErgoHeader, ErgoLikeTransaction, Input} import scorex.util.encode.Base16 import sigma.VersionContext.V6SoftForkVersion -import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.Token +import org.ergoplatform.settings.ErgoAlgos import scorex.util.ModifierId import scorex.utils.{Ints, Longs, Shorts} -import sigma.ast.ErgoTree.ZeroHeader +import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast.{SInt, _} -import sigma.data.{CBigInt, CBox, CHeader, ExactNumeric} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CHeader, CSigmaProp, ExactNumeric, ProveDHTuple, RType} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode -import sigma.data.{RType} -import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} +import sigma.util.Extensions.{BooleanOps, IntOps} +import sigmastate.eval.{CContext, CPreHeader} import sigmastate.exceptions.MethodNotFound import sigmastate.utils.Extensions.ByteOpsForSigma import sigmastate.utils.Helpers +import sigma.Extensions.{ArrayOps, CollOps} +import sigma.interpreter.{ContextExtension, ProverResult} import java.math.BigInteger import scala.util.{Failure, Success} @@ -1523,4 +1525,146 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + property("Context.getVar and getVarFromInput") { + + def contextData() = { + val input = CBox( + new ErgoBox( + 80946L, + new ErgoTree( + HeaderType @@ 16.toByte, + Vector( + SigmaPropConstant( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb"), + Helpers.decodeECPoint("023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d03"), + Helpers.decodeECPoint("03d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72"), + Helpers.decodeECPoint("037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441") + ) + ) + ) + ), + Right(ConstantPlaceholder(0, SSigmaProp)) + ), + Coll(), + Map( + ErgoBox.R4 -> ByteArrayConstant(Helpers.decodeBytes("34")), + ErgoBox.R5 -> TrueLeaf + ), + ModifierId @@ ("0000bfe96a7c0001e7a5ee00aafb80ff057fbe7f8c6680e33a3dc18001820100"), + 1.toShort, + 5 + ) + ) + + val tx = ErgoLikeTransaction( + IndexedSeq(), + IndexedSeq(input.wrappedValue) + ) + + val tx2 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(true)))))), + IndexedSeq(input.wrappedValue) + ) + + val tx3 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> IntConstant(0)))))), + IndexedSeq(input.wrappedValue) + ) + + val tx4 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(false)))))), + IndexedSeq(input.wrappedValue) + ) + + val ctx = CContext( + _dataInputs = Coll[Box](), + headers = Coll[Header](), + preHeader = CPreHeader( + 0.toByte, + Colls.fromArray(Array.fill(32)(0.toByte)), + -755484979487531112L, + 9223372036854775807L, + 11, + Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + Helpers.decodeBytes("007f00") + ), + inputs = Coll[Box](input), + outputs = Coll[Box](), + height = 11, + selfBox = input.copy(), // in 3.x, 4.x implementation selfBox is never the same instance as input (see toSigmaContext) + selfIndex = 0, + lastBlockUtxoRootHash = CAvlTree( + AvlTreeData( + ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17").toColl, + AvlTreeFlags(true, true, true), + 1211925457, + None + ) + ), + _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + vars = Colls + .replicate[AnyValue](10, null) // reserve 10 vars + .append(Coll[AnyValue]( + CAnyValue(Helpers.decodeBytes("00")), + CAnyValue(true))), + spendingTransaction = tx, + activatedScriptVersion = activatedVersionInTests, + currentErgoTreeVersion = ergoTreeVersionInTests + ) + val ctx2 = ctx.copy(spendingTransaction = tx2) + val ctx3 = ctx.copy(spendingTransaction = tx3, vars = ctx.vars.patch(11, Coll(CAnyValue(0)), 1)) + val ctx4 = ctx.copy(spendingTransaction = tx4, vars = ctx.vars.patch(11, Coll(CAnyValue(false)), 1)) + + (ctx, ctx2, ctx3, ctx4) + } + + def getVarFromInput = { + newFeature( + { (x: Context) => x.getVarFromInput[Boolean](0, 11)}, + "{ (x: Context) => x.getVarFromInput[Boolean](0, 11) }", + FuncValue( + Array((1, SContext)), + MethodCall.typed[Value[SOption[SBoolean.type]]]( + ValUse(1, SContext), + SContextMethods.getVarFromInputMethod.withConcreteTypes(Map(STypeVar("T") -> SBoolean)), + Array(ShortConstant(0.toShort), ByteConstant(11.toByte)), + Map(STypeVar("T") -> SBoolean) + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + val (ctx, ctx2, ctx3, ctx4) = contextData() + + verifyCases( + Seq( + ctx -> new Expected(ExpectedResult(Success(None), None)), // input with # provided does not exist + ctx2 -> new Expected(ExpectedResult(Success(Some(true)), None)), + ctx3 -> new Expected(ExpectedResult(Success(None), None)), // not expected type in context var + ctx4 -> new Expected(ExpectedResult(Success(Some(false)), None)) + ), + getVarFromInput + ) + + def getVar = { + newFeature( + { (x: Context) => x.getVar[Boolean](11)}, + "{ (x: Context) => CONTEXT.getVar[Boolean](11.toByte) }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + ctx2 -> new Expected(ExpectedResult(Success(Some(true)), None)), + ctx3 -> new Expected(ExpectedResult(Failure(new sigma.exceptions.InvalidType("Cannot getVar[Boolean](11): invalid type of value TestValue(0) at id=11")), None)), // not expected type in context var + ctx4 -> new Expected(ExpectedResult(Success(Some(false)), None)) + ), + getVar + ) + } + } From f24e9ad44614ed9c91949c7594eb68bb19f5569d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 30 Sep 2024 16:04:25 +0300 Subject: [PATCH 63/78] predefined fn, LangSpec notes, polishing PR --- .../src/main/scala/sigma/ast/SMethod.scala | 7 +++++ .../main/scala/sigma/ast/SigmaPredef.scala | 15 ++++++++- .../src/main/scala/sigma/ast/methods.scala | 8 +++-- .../sigma/interpreter/ContextExtension.scala | 4 +++ docs/LangSpec.md | 14 ++++++++- .../sigma/compiler/phases/SigmaTyper.scala | 1 + .../utxo/BasicOpsSpecification.scala | 31 ++++++++++++++++--- 7 files changed, 70 insertions(+), 10 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala index 5c3e5faf40..e5481cee5b 100644 --- a/data/shared/src/main/scala/sigma/ast/SMethod.scala +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -302,6 +302,13 @@ object SMethod { (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2]): RMethod = RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass) + /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. + * @param methodName the name of the method to lookup + * @param cT the class where to search the methodName + * @param cA1 the class of the method's first argument + * @param cA2 the class of the method's second argument + * @param cA3 the class of the method's third argument + */ def javaMethodOf[T, A1, A2, A3] (methodName: String) (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2], cA3: ClassTag[A3]): RMethod = diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 8b89851938..7a28ee6dd9 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -146,6 +146,18 @@ object SigmaPredef { Seq(ArgInfo("varId", "\\lst{Byte} identifier of context variable"))) ) + val GetVarFromInputFunc = PredefinedFunc("getVarFromInput", + Lambda(Array(paramT), Array("inputId" -> SShort, "varId" -> SByte), SOption(tT), None), + PredefFuncInfo( + { case (Ident(_, SFunc(_, SOption(rtpe), _)), Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) => + mkMethodCall(Context, SContextMethods.getVarFromInputMethod, IndexedSeq(SShort.downcast(inputId.value.asInstanceOf[AnyVal]), SByte.downcast(varId.value.asInstanceOf[AnyVal])), Map(tT -> rtpe)) + }), + OperationInfo(MethodCall, + "Get context variable with given \\lst{varId} and type.", + Seq(ArgInfo("inputId", "\\lst{Byte} index of input to read context variable from"), + ArgInfo("varId", "\\lst{Byte} identifier of context variable"))) + ) + def PKFunc(networkPrefix: NetworkPrefix) = PredefinedFunc("PK", Lambda(Array("input" -> SString), SSigmaProp, None), PredefFuncInfo( @@ -448,7 +460,8 @@ object SigmaPredef { SubstConstantsFunc, ExecuteFromVarFunc, ExecuteFromSelfRegFunc, - SerializeFunc + SerializeFunc, + GetVarFromInputFunc ).map(f => f.name -> f).toMap def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index ae05152630..33ffa9c9b2 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1586,16 +1586,18 @@ case object SContextMethods extends MonoTypeMethods { MethodCallIrBuilder, javaMethodOf[Context, Byte, RType[_]]("getVar"), { mtype => Array(mtype.tRange.asOption[SType].elemType) }) - .withInfo(MethodCall, "") // todo: desc + .withInfo(MethodCall, "Get context variable with given \\lst{varId} and type.") - // todo: costing, desc lazy val getVarFromInputMethod = SMethod( this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) .withIRInfo( MethodCallIrBuilder, javaMethodOf[Context, Short, Byte, RType[_]]("getVarFromInput"), { mtype => Array(mtype.tRange.asOption[SType].elemType) }) - .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) + .withInfo(MethodCall, "Get context variable with given \\lst{varId} and type.", + ArgInfo("inputIdx", "Index of input to read variable from."), + ArgInfo("varId", "Index of variable.") + ) private lazy val commonMethods = super.getMethods() ++ Array( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, diff --git a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala index fd269c177c..f03d076d43 100644 --- a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala +++ b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala @@ -20,6 +20,10 @@ case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ ContextExtension(values ++ bindings) } + /** + * @param varId - index of context variable + * @return context variable with provided index or None if it is not there + */ def get(varId: Byte): Option[EvaluatedValue[_ <: SType]] = values.get(varId) } diff --git a/docs/LangSpec.md b/docs/LangSpec.md index 5b72a8659d..7ec7dd7729 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -919,7 +919,7 @@ def longToByteArray(input: Long): Coll[Byte] def decodePoint(bytes: Coll[Byte]): GroupElement -/** Extracts Context variable by id and type. +/** Extracts Context variable from self input by id and type. * ErgoScript is typed, so accessing a the variables is an operation which involves * some expected type given in brackets. Thus `getVar[Int](id)` expression should * evaluate to a valid value of the `Option[Int]` type. @@ -976,6 +976,18 @@ def decodePoint(bytes: Coll[Byte]): GroupElement */ def getVar[T](tag: Int): Option[T] +/** Extracts Context variable from any input by input id, variable id and variable type. + * Unlike getVar, it is not throwing exception when expected type does not match real type of the variable. + * Thus it can be used to get context variable from self without exception, using selfBoxIndex, e.g. + *
+  *   {
+  *       val idx = CONTEXT.selfBoxIndex
+  *       sigmaProp(CONTEXT.getVarFromInput[Int](idx.toShort, 1.toByte).get == 5)
+  *   }
+  * 
+ */ +def getVarFromInput[T](inputId: Short, varId: Byte): Option[T] + /** Construct a new SigmaProp value representing public key of Diffie Hellman * signature protocol. When executed as part of Sigma protocol allow to provide * for a verifier a zero-knowledge proof of secret knowledge. diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index cc771cbac9..833bd413b9 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -134,6 +134,7 @@ class SigmaTyper(val builder: SigmaBuilder, res case Apply(ApplyTypes(sel @ Select(obj, n, _), Seq(rangeTpe)), args) => + // downcast getVarFromInput arguments to short and byte val nArgs = if (n == SContextMethods.getVarFromInputMethod.name && args.length == 2 && args(0).isInstanceOf[Constant[_]] && diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index df4e85de3b..b43fcdd90e 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -167,7 +167,7 @@ class BasicOpsSpecification extends CompilerTestingCommons 1.toByte -> IntConstant(5) ).toSeq test("R1", env, customExt, - "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 1).get == 5) }", + "{ sigmaProp(getVarFromInput[Int](0, 1).get == 5) }", null ) } @@ -179,19 +179,40 @@ class BasicOpsSpecification extends CompilerTestingCommons } } - property("getVarFromInput - invalid input") { + property("getVarFromInput - self index") { def getVarTest(): Assertion = { val customExt = Map( 1.toByte -> IntConstant(5) ).toSeq test("R1", env, customExt, - "{ sigmaProp(CONTEXT.getVarFromInput[Int](1, 1).get == 5) }", + """{ + | val idx = CONTEXT.selfBoxIndex + | sigmaProp(CONTEXT.getVarFromInput[Int](idx.toShort, 1.toByte).get == 5) + | }""".stripMargin, null ) } if (VersionContext.current.isV6SoftForkActivated) { + getVarTest() + } else { an[Exception] should be thrownBy getVarTest() + } + } + + property("getVarFromInput - invalid input") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ sigmaProp(CONTEXT.getVarFromInput[Int](1, 1).isDefined == false) }", + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + getVarTest() } else { an[Exception] should be thrownBy getVarTest() } @@ -593,13 +614,13 @@ class BasicOpsSpecification extends CompilerTestingCommons 1.toByte -> IntConstant(5) ).toSeq test("R1", env, customExt, - "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 2).get == 5) }", + "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 2).isDefined == false) }", null ) } if (VersionContext.current.isV6SoftForkActivated) { - an[Exception] should be thrownBy getVarTest() + getVarTest() } else { an[Exception] should be thrownBy getVarTest() } From edc91baa371048578d5fdeb08b5c15cf33526b32 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 30 Sep 2024 16:55:58 +0300 Subject: [PATCH 64/78] tests --- .../ErgoLikeTransactionSpec.scala | 32 ++++++++++++------- .../utxo/BasicOpsSpecification.scala | 7 ---- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala index 6a7ef5a512..3fc9c7fd3d 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala @@ -7,8 +7,7 @@ import org.ergoplatform.settings.ErgoAlgos import scorex.util.encode.Base16 import scorex.util.{ModifierId, Random} import sigma.Extensions._ -import sigma.SigmaDslTesting -import sigma.ast.SCollection.SByteArray +import sigma.{SigmaDslTesting, VersionContext} import sigma.ast.SType._ import sigma.ast.syntax.{ErgoBoxCandidateRType, TrueSigmaProp} import sigma.ast._ @@ -20,9 +19,11 @@ import sigmastate.helpers.TestingHelpers.copyTransaction import sigmastate.utils.Helpers import sigma.SigmaDslTesting import sigma.Extensions._ +import sigma.ast.SCollection.SByteArray +import sigmastate.CrossVersionProps import sigmastate.utils.Helpers.EitherOps // required for Scala 2.11 -class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { +class ErgoLikeTransactionSpec extends SigmaDslTesting with CrossVersionProps with JsonCodecs { property("ErgoBox test vectors") { val token1 = "6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001" @@ -99,14 +100,24 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { { // test case for R2 val res = b1.get(ErgoBox.R2).get - val exp = Coll( - (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, - (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token2).toColl) -> 500L - ).map(identity).toConstant - // TODO v6.0 (16h): fix collections equality and remove map(identity) - // (PairOfColl should be equal CollOverArray but now it is not) + + // We have versioned check here due to fixed collections equality in 6.0.0 + // (PairOfColl equal CollOverArray now) // see (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/909) - res shouldBe exp + if(VersionContext.current.isV6SoftForkActivated) { + val exp = Coll( + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token2).toColl) -> 500L + ).toConstant + res shouldBe exp + exp shouldBe res + } else { + val exp = Coll( + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token2).toColl) -> 500L + ).map(identity).toConstant + res shouldBe exp + } } { // test case for R3 @@ -470,7 +481,6 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { // test equivalence of "from Json" and "from bytes" deserialization tx2.id shouldBe tx.id tx2.id shouldBe "d5c0a7908bbb8eefe72ad70a9f668dd47b748239fd34378d3588d5625dd75c82" - println(tx2.id) } property("Tuple in register test vector") { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 431c5166e1..68fe1022e7 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -804,13 +804,6 @@ class BasicOpsSpecification extends CompilerTestingCommons } } - property("coll equality") { - def collTest() = test("R1", env, ext, - "{ SELF.tokens == Coll[(Coll[Byte], Long)]() }", - null - ) - } - property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From af792b53bac04e488aa55cbb6c4a318f796a7d7a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 1 Oct 2024 19:21:31 +0300 Subject: [PATCH 65/78] addressing review comments --- data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala | 2 +- sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 75fbe460b9..2ae4f73703 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -232,7 +232,7 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => if (bytes.length > SBigInt.MaxSizeInBytes) { throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") } - CBigInt(new BigInteger(bytes.toArray)).asInstanceOf[T] + CBigInt(new BigInteger(bytes.toArray).to256BitValueExact).asInstanceOf[T] // todo: UnsignedBitInt case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") } diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index fee93248f7..7a4b8de827 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -516,6 +516,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) ) ++ (if (isV6Activated) { + // id = 4 reserved for deserializeTo method Seq(MInfo(3, serializeMethod), MInfo(5, fromBigEndianBytesMethod)) // methods added in v6.0 } else { Seq.empty[MInfo] From 1d47d5ffc69be4f9fe12a9713bb277b2775cd988 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 7 Oct 2024 18:47:46 +0300 Subject: [PATCH 66/78] addressing review comments --- core/shared/src/main/scala/sigma/Colls.scala | 19 ++++++------ .../scala/sigma/data/CollsOverArrays.scala | 10 ++++-- .../src/test/scala/sigma/CollsTests.scala | 26 ++++++++++++++++ .../src/main/scala/sigma/ast/methods.scala | 31 +++++++++++++------ .../scala/sigma/LanguageSpecificationV6.scala | 8 +++-- 5 files changed, 72 insertions(+), 22 deletions(-) diff --git a/core/shared/src/main/scala/sigma/Colls.scala b/core/shared/src/main/scala/sigma/Colls.scala index f1633a68a4..d10026066b 100644 --- a/core/shared/src/main/scala/sigma/Colls.scala +++ b/core/shared/src/main/scala/sigma/Colls.scala @@ -45,13 +45,10 @@ trait Coll[@specialized A] { */ def apply(i: Int): A - /** The element at given index. - * Indices start at `0`; `xs.apply(0)` is the first element of collection `xs`. - * Note the indexing syntax `xs(i)` is a shorthand for `xs.apply(i)`. + /** The element at given index or None if there is no such element. Indices start at `0`. * * @param i the index - * @return the element at the given index - * @throws ArrayIndexOutOfBoundsException if `i < 0` or `length <= i` + * @return the element at the given index, or None if there is no such element */ def get(i: Int): Option[A] = { if (isDefinedAt(i)) { @@ -92,12 +89,16 @@ trait Coll[@specialized A] { * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ def zip[@specialized B](ys: Coll[B]): Coll[(A, B)] - /** For this collection (x0, ..., xN) and other collection (y0, ..., yM) - * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ + /** + * @return true if first elements of this collection form given `ys` collection, false otherwise. + * E.g. [1,2,3] starts with [1,2] + */ def startsWith(ys: Coll[A]): Boolean - /** For this collection (x0, ..., xN) and other collection (y0, ..., yM) - * produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */ + /** + * @return true if last elements of this collection form given `ys` collection, false otherwise. + * E.g. [1,2,3] ends with [2,3] + */ def endsWith(ys: Coll[A]): Boolean /** Tests whether a predicate holds for at least one element of this collection. diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index 506dc001ea..eb4c1e4931 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -354,9 +354,15 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R def zip[@specialized B](ys: Coll[B]): PairColl[(L,R), B] = builder.pairColl(this, ys) - def startsWith(ys: Coll[(L, R)]): Boolean = toArray.startsWith(ys.toArray) + def startsWith(ys: Coll[(L, R)]): Boolean = ys match { + case yp: PairOfCols[L, R] => ls.startsWith(yp.ls) && rs.startsWith(yp.rs) + case _ => toArray.startsWith(ys.toArray) + } - def endsWith(ys: Coll[(L, R)]): Boolean = toArray.endsWith(ys.toArray) + def endsWith(ys: Coll[(L, R)]): Boolean = ys match { + case yp: PairOfCols[L, R] => ls.endsWith(yp.ls) && rs.endsWith(yp.rs) + case _ => toArray.endsWith(ys.toArray) + } override def indices: Coll[Int] = if (ls.length <= rs.length) ls.indices else rs.indices diff --git a/core/shared/src/test/scala/sigma/CollsTests.scala b/core/shared/src/test/scala/sigma/CollsTests.scala index 4886112742..da427ba576 100644 --- a/core/shared/src/test/scala/sigma/CollsTests.scala +++ b/core/shared/src/test/scala/sigma/CollsTests.scala @@ -386,6 +386,32 @@ class CollsTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers } } + property("Coll.startsWith") { + val minSuccess = minSuccessful(50) + forAll(collGen, minSuccess) { col => + val n = col.length / 2 + val prefix = col.take(n) + val pairs = col.zip(col) + pairs.startsWith(prefix.zip(prefix)) shouldBe true + col.startsWith(prefix) shouldBe true + val pairOfCols = new PairOfCols[Int, Int](col, col) + pairOfCols.startsWith(pairOfCols.take(n)) shouldBe true + } + } + + property("Coll.endsWith") { + val minSuccess = minSuccessful(50) + forAll(collGen, minSuccess) { col => + val n = col.length / 2 + val suffix = col.slice(n, col.length) + col.endsWith(suffix) shouldBe true + val pairs = col.zip(col) + pairs.endsWith(suffix.zip(suffix)) shouldBe true + val pairOfCols = new PairOfCols[Int, Int](col, col) + pairOfCols.endsWith(pairOfCols.slice(n, col.length)) shouldBe true + } + } + property("Coll.equals") { def checkColls(repl: Coll[_], coll: Coll[_]) = { assert(coll == repl) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 855da4eb73..dc85205b37 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1070,8 +1070,10 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { // ======== 6.0 methods below =========== + private val reverseCostKind = Append.costKind + val ReverseMethod = SMethod(this, "reverse", - SFunc(Array(ThisType), ThisType, paramIVSeq), 30, Zip_CostKind) // todo: costing + SFunc(Array(ThisType), ThisType, paramIVSeq), 30, reverseCostKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "") @@ -1087,10 +1089,12 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + private val distinctCostKind = PerItemCost(baseCost = JitCost(60), perChunkCost = JitCost(5), chunkSize = 100) + val DistinctMethod = SMethod(this, "distinct", - SFunc(Array(ThisType), ThisType, paramIVSeq), 31, Zip_CostKind) // todo: costing + SFunc(Array(ThisType), ThisType, paramIVSeq), 31, distinctCostKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Returns inversed collection.") /** Implements evaluation of Coll.reverse method call ErgoTree node. * Called via reflection based on naming convention. @@ -1104,10 +1108,13 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + private val startsWithCostKind = Zip_CostKind + val StartsWithMethod = SMethod(this, "startsWith", - SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, Zip_CostKind) // todo: costing + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, startsWithCostKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Returns true if this collection starts with given one, false otherwise.", + ArgInfo("prefix", "Collection to be checked for being a prefix of this collection.")) /** Implements evaluation of Coll.zip method call ErgoTree node. * Called via reflection based on naming convention. @@ -1121,10 +1128,13 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } } + private val endsWithCostKind = Zip_CostKind + val EndsWithMethod = SMethod(this, "endsWith", - SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, Zip_CostKind) // todo: costing + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, endsWithCostKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Returns true if this collection ends with given one, false otherwise.", + ArgInfo("suffix", "Collection to be checked for being a suffix of this collection.")) /** Implements evaluation of Coll.zip method call ErgoTree node. * Called via reflection based on naming convention. @@ -1139,9 +1149,12 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { } val GetMethod = SMethod(this, "get", - SFunc(Array(ThisType, SInt), SOption(tIV), Array[STypeParam](tIV)), 34, ByIndex.costKind) //todo: costing + SFunc(Array(ThisType, SInt), SOption(tIV), Array[STypeParam](tIV)), 34, ByIndex.costKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, + "Returns Some(element) if there is an element at given index, None otherwise.", + ArgInfo("index", "Index of an element (starting from 0).") + ) private val v5Methods = super.getMethods() ++ Seq( SizeMethod, diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index ecd2c411ed..481c7101da 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -11,7 +11,7 @@ import sigma.ast.ErgoTree.ZeroHeader import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast.{SInt, _} -import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, RType} +import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, PairOfCols, RType} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, IntOps} @@ -1639,6 +1639,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Coll(1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), Coll(1, 1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), Coll(1, 2, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll(2, 2, 2) -> Expected(ExpectedResult(Success(Coll(2)), None)), + Coll(3, 1, 2, 2, 2, 4, 4, 1) -> Expected(ExpectedResult(Success(Coll(3, 1, 2, 4)), None)), Coll[Int]() -> Expected(ExpectedResult(Success(Coll[Int]()), None)) ), f @@ -1656,8 +1658,10 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq( (Coll(1, 2, 3), Coll(1, 2)) -> Expected(ExpectedResult(Success(true), None)), (Coll(1, 2, 3), Coll(1, 2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(1, 2, 4)) -> Expected(ExpectedResult(Success(false), None)), (Coll(1, 2, 3), Coll(1, 2, 3, 4)) -> Expected(ExpectedResult(Success(false), None)), - (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) + (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)), + (Coll[Int](1, 2), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) ), f ) From 09ec5f11d21792c99adb903f485540eef43b652b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 18 Oct 2024 13:48:50 +0300 Subject: [PATCH 67/78] ignoring bulletproof test, impoving comments --- core/shared/src/main/scala/sigma/ast/SType.scala | 7 +++++-- core/shared/src/main/scala/sigma/util/Extensions.scala | 1 - .../test/scala/sigmastate/utxo/BasicOpsSpecification.scala | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 7673c11dec..b69a988347 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -102,14 +102,15 @@ object SType { /** Immutable empty IndexedSeq, can be used to avoid repeated allocations. */ val EmptySeq: IndexedSeq[SType] = EmptyArray + // <= V5 types, see `allPredefTypes` scaladoc below private val v5PredefTypes = Array[SType]( SBoolean, SByte, SShort, SInt, SLong, SBigInt, SContext, SGlobal, SHeader, SPreHeader, SAvlTree, SGroupElement, SSigmaProp, SString, SBox, SUnit, SAny) + // V6 types, see `allPredefTypes` scaladoc below private val v6PredefTypes = v5PredefTypes ++ Array(SUnsignedBigInt) - /** All pre-defined types should be listed here. Note, NoType is not listed. * Should be in sync with sigmastate.lang.Types.predefTypes. */ def allPredefTypes: Seq[SType] = { @@ -147,6 +148,8 @@ object SType { * (SByte, SShort, SInt, SLong, SBigInt) and the generic tNum type parameter is * specialized accordingly. * + * Also, SUnsignedBigInt type is added in v6.0. + * * This difference in behaviour is tested by `property("MethodCall on numerics")`. * * The regression tests in `property("MethodCall Codes")` should pass. @@ -487,7 +490,7 @@ case object SLong extends SPrimType with SEmbeddable with SNumericType with SMon } } -/** Type of 256-bit signed integer values. Implemented using [[java.math.BigInteger]]. */ +/** Type of 256-bit signed 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/util/Extensions.scala b/core/shared/src/main/scala/sigma/util/Extensions.scala index 149d61f6c2..5d1d9b7da8 100644 --- a/core/shared/src/main/scala/sigma/util/Extensions.scala +++ b/core/shared/src/main/scala/sigma/util/Extensions.scala @@ -218,7 +218,6 @@ object Extensions { } @inline final def toUnsignedBigIntValueExact: BigInteger = { - // todo: make the check soft-forkable if (x.compareTo(BigInteger.ZERO) >= 0 && x.bitLength() <= 256) { x } else { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 07e5170af3..7a5f6ae668 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -463,7 +463,8 @@ class BasicOpsSpecification extends CompilerTestingCommons } } - property("Bulletproof verification for a range proof") { + // todo: finish the range proof verification script and test + ignore("Bulletproof verification for a range proof") { /* * Original range proof verifier code by Benedikt Bunz: * From 5cabedd6f73a3d0737a221919e51771a0c5653a2 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 18 Oct 2024 14:42:40 +0300 Subject: [PATCH 68/78] equalsPairCollWithCollOverArray --- .../scala/sigma/data/CollsOverArrays.scala | 86 +++++++++++++------ .../ErgoLikeTransactionSpec.scala | 2 +- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index dc73253628..2d6a4a5cdf 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -1,6 +1,8 @@ package sigma.data import debox.{Buffer, cfor} +import sigma.Evaluation.stypeToRType +import sigma.data.CollOverArray.equalsPairCollWithCollOverArray import sigma.data.RType._ import sigma.util.{CollectionUtil, MaxArrayLength, safeConcatArrays_v5} import sigma.{Coll, CollBuilder, PairColl, VersionContext, requireSameLength} @@ -12,7 +14,9 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil s"Cannot create collection with size ${toArray.length} greater than $MaxArrayLength") override def tItem: RType[A] = tA + @inline def length: Int = toArray.length + @inline def apply(i: Int): A = toArray.apply(i) override def isEmpty: Boolean = length == 0 @@ -29,8 +33,11 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil } def foreach(f: A => Unit): Unit = toArray.foreach(f) + def exists(p: A => Boolean): Boolean = toArray.exists(p) + def forall(p: A => Boolean): Boolean = toArray.forall(p) + def filter(p: A => Boolean): Coll[A] = builder.fromArray(toArray.filter(p)) def foldLeft[B](zero: B, op: ((B, A)) => B): B = toArray.foldLeft(zero)((b, a) => op((b, a))) @@ -117,12 +124,14 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil override def unionSet(that: Coll[A]): Coll[A] = { val set = debox.Set.ofSize[A](this.length) val res = Buffer.ofSize[A](this.length) + @inline def addItemToSet(x: A) = { if (!set(x)) { set.add(x) res += x } } + def addToSet(arr: Array[A]) = { val limit = arr.length cfor(0)(_ < limit, _ + 1) { i => @@ -140,19 +149,41 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil override def equals(obj: scala.Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match { case obj: CollOverArray[_] if obj.tItem == this.tItem => java.util.Objects.deepEquals(obj.toArray, this.toArray) - case obj: PairColl[_, _] if obj.tItem == this.tItem => - if(VersionContext.current.isV6SoftForkActivated) { - java.util.Objects.deepEquals(obj.toArray, this.toArray) + case obj: PairColl[Any, Any] if obj.tItem == this.tItem => + if (VersionContext.current.isV6SoftForkActivated) { + equalsPairCollWithCollOverArray(obj, this.asInstanceOf[CollOverArray[Any]]) } else { false } case _ => false }) - override def hashCode() = CollectionUtil.deepHashCode(toArray) + override def hashCode(): Int = CollectionUtil.deepHashCode(toArray) } -private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => +object CollOverArray { + + // comparing PairColl and CollOverArray instances + private[data] def equalsPairCollWithCollOverArray(pc: PairColl[Any, Any], coa: CollOverArray[Any]): Boolean = { + val ls = pc.ls + val rs = pc.rs + val ts = coa.toArray + if (ts.length == ls.length && ts.isInstanceOf[Array[(Any, Any)]]) { + val ta = ts.asInstanceOf[Array[(Any, Any)]] + var eq = true + cfor(0)(_ < ta.length && eq, _ + 1) { i => + eq = java.util.Objects.deepEquals(ta(i)._1, ls(i)) && java.util.Objects.deepEquals(ta(i)._2, rs(i)) + } + eq + } else { + false + } + } + +} + +private[sigma] class CollOverArrayBuilder extends CollBuilder { + builder => @inline override def pairColl[@specialized A, @specialized B](as: Coll[A], bs: Coll[B]): PairColl[A, B] = { if (VersionContext.current.isJitActivated) { @@ -176,12 +207,12 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => } } - private def fromBoxedPairs[A, B](seq: Seq[(A, B)])(implicit tA: RType[A], tB: RType[B]): PairColl[A,B] = { + private def fromBoxedPairs[A, B](seq: Seq[(A, B)])(implicit tA: RType[A], tB: RType[B]): PairColl[A, B] = { val len = seq.length val resA = Array.ofDim[A](len)(tA.classTag) val resB = Array.ofDim[B](len)(tB.classTag) cfor(0)(_ < len, _ + 1) { i => - val item = seq.apply(i).asInstanceOf[(A,B)] + val item = seq.apply(i).asInstanceOf[(A, B)] resA(i) = item._1 resB(i) = item._2 } @@ -189,7 +220,7 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => } override def fromItems[T](items: T*)(implicit cT: RType[T]): Coll[T] = cT match { - case pt: PairType[a,b] => + case pt: PairType[a, b] => val tA = pt.tFst val tB = pt.tSnd fromBoxedPairs(items)(tA, tB) @@ -198,16 +229,16 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => } override def fromArray[@specialized T: RType](arr: Array[T]): Coll[T] = RType[T] match { - case pt: PairType[a,b] => + case pt: PairType[a, b] => val tA = pt.tFst val tB = pt.tSnd - fromBoxedPairs[a,b](arr.asInstanceOf[Array[(a,b)]])(tA, tB) + fromBoxedPairs[a, b](arr.asInstanceOf[Array[(a, b)]])(tA, tB) case _ => new CollOverArray(arr, builder) } override def replicate[@specialized T: RType](n: Int, v: T): Coll[T] = RType[T] match { - case pt: PairType[a,b] => + case pt: PairType[a, b] => val tA = pt.tFst val tB = pt.tSnd val tuple = v.asInstanceOf[(a, b)] @@ -216,8 +247,8 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => fromArray(Array.fill(n)(v)) } - override def unzip[@specialized A, @specialized B](xs: Coll[(A,B)]): (Coll[A], Coll[B]) = xs match { - case pa: PairColl[_,_] => (pa.ls, pa.rs) + override def unzip[@specialized A, @specialized B](xs: Coll[(A, B)]): (Coll[A], Coll[B]) = xs match { + case pa: PairColl[_, _] => (pa.ls, pa.rs) case _ => val limit = xs.length implicit val tA = xs.tItem.tFst @@ -236,7 +267,7 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => left.zip(right).map { case (l, r) => (l ^ r).toByte } override def emptyColl[T](implicit cT: RType[T]): Coll[T] = cT match { - case pt: PairType[a,b] => + case pt: PairType[a, b] => val ls = emptyColl(pt.tFst) val rs = emptyColl(pt.tSnd) pairColl(ls, rs).asInstanceOf[Coll[T]] @@ -245,14 +276,14 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => } } -class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R]) extends PairColl[L,R] { +class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R]) extends PairColl[L, R] { override def equals(that: scala.Any): Boolean = (this eq that.asInstanceOf[AnyRef]) || (that match { - case that: PairColl[_,_] if that.tItem == this.tItem => + case that: PairColl[_, _] if that.tItem == this.tItem => ls == that.ls && rs == that.rs - case that: CollOverArray[_] if that.tItem == this.tItem => + case that: CollOverArray[Any] if that.tItem == this.tItem => if (VersionContext.current.isV6SoftForkActivated) { - java.util.Objects.deepEquals(that.toArray, this.toArray) + equalsPairCollWithCollOverArray(this.asInstanceOf[PairColl[Any, Any]], that) } else { false } @@ -260,7 +291,9 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R }) override def hashCode() = ls.hashCode() * 41 + rs.hashCode() + @inline implicit def tL: RType[L] = ls.tItem + @inline implicit def tR: RType[R] = rs.tItem override lazy val tItem: RType[(L, R)] = { @@ -268,8 +301,11 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R } override def builder: CollBuilder = ls.builder + override def toArray: Array[(L, R)] = ls.toArray.zip(rs.toArray) + @inline override def length: Int = if (ls.length <= rs.length) ls.length else rs.length + @inline override def apply(i: Int): (L, R) = (ls(i), rs(i)) override def isEmpty: Boolean = length == 0 @@ -317,7 +353,7 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R true } - override def filter(p: ((L, R)) => Boolean): Coll[(L,R)] = { + override def filter(p: ((L, R)) => Boolean): Coll[(L, R)] = { val len = ls.length val resL: Buffer[L] = Buffer.empty[L](ls.tItem.classTag) val resR: Buffer[R] = Buffer.empty[R](rs.tItem.classTag) @@ -346,9 +382,9 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R state } - override def slice(from: Int, until: Int): PairColl[L,R] = builder.pairColl(ls.slice(from, until), rs.slice(from, until)) + override def slice(from: Int, until: Int): PairColl[L, R] = builder.pairColl(ls.slice(from, until), rs.slice(from, until)) - def append(other: Coll[(L, R)]): Coll[(L,R)] = { + def append(other: Coll[(L, R)]): Coll[(L, R)] = { val arrs = builder.unzip(other) builder.pairColl(ls.append(arrs._1), rs.append(arrs._2)) } @@ -365,7 +401,7 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R } } - def zip[@specialized B](ys: Coll[B]): PairColl[(L,R), B] = builder.pairColl(this, ys) + def zip[@specialized B](ys: Coll[B]): PairColl[(L, R), B] = builder.pairColl(this, ys) def startsWith(ys: Coll[(L, R)]): Boolean = ys match { case yp: PairOfCols[L, R] => ls.startsWith(yp.ls) && rs.startsWith(yp.rs) @@ -421,18 +457,20 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R } override def unionSet(that: Coll[(L, R)]): Coll[(L, R)] = { - val set = new java.util.HashSet[(L,R)](32) + val set = new java.util.HashSet[(L, R)](32) implicit val ctL = ls.tItem.classTag implicit val ctR = rs.tItem.classTag val resL = Buffer.empty[L] val resR = Buffer.empty[R] - def addToSet(item: (L,R)) = { + + def addToSet(item: (L, R)) = { if (!set.contains(item)) { set.add(item) resL += item._1 resR += item._2 } } + var i = 0 val thisLen = math.min(ls.length, rs.length) while (i < thisLen) { diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala index 3fc9c7fd3d..4b3aa2eab5 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala @@ -23,7 +23,7 @@ import sigma.ast.SCollection.SByteArray import sigmastate.CrossVersionProps import sigmastate.utils.Helpers.EitherOps // required for Scala 2.11 -class ErgoLikeTransactionSpec extends SigmaDslTesting with CrossVersionProps with JsonCodecs { + class ErgoLikeTransactionSpec extends SigmaDslTesting with CrossVersionProps with JsonCodecs { property("ErgoBox test vectors") { val token1 = "6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001" From 9036aec8769a1dd724231d258d11eadf005cb400 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 21 Oct 2024 23:04:16 +0300 Subject: [PATCH 69/78] improving tests in BasicOpsSpecification, type test for getVarFromInput --- .../src/main/scala/sigma/SigmaDsl.scala | 4 +- .../main/scala/sigmastate/eval/CContext.scala | 4 +- .../sigmastate/lang/SigmaTyperTest.scala | 6 + .../utxo/BasicOpsSpecification.scala | 221 ++++++++++-------- 4 files changed, 134 insertions(+), 101 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 422e4b7969..16331febfd 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -592,13 +592,13 @@ trait Context { /** * A variant of `getVar` to extract a context variable by id and type from any input * - * @param inputId - input index + * @param inputIndex - input index * @param id - context variable id * @tparam T - expected type of the variable * @return Some(value) if the variable is defined in the context AND has the given type. * None otherwise */ - def getVarFromInput[T](inputId: Short, id: Byte)(implicit cT: RType[T]): Option[T] + def getVarFromInput[T](inputIndex: Short, id: Byte)(implicit cT: RType[T]): Option[T] def vars: Coll[AnyValue] diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala index bed0c4b013..b0e5b01186 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala @@ -73,8 +73,8 @@ case class CContext( } else None } - override def getVarFromInput[T](inputId: Short, id: Byte)(implicit tT: RType[T]): Option[T] = { - spendingTransaction.inputs.lift(inputId).flatMap(_.extension.get(id)) match { + override def getVarFromInput[T](inputIndex: Short, id: Byte)(implicit tT: RType[T]): Option[T] = { + spendingTransaction.inputs.lift(inputIndex).flatMap(_.extension.get(id)) match { case Some(v) if stypeToRType[SType](v.tpe) == tT => Some(v.value.asInstanceOf[T]) case _ => None diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 5bfb72cd4f..c86cb11afc 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -687,6 +687,12 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "CONTEXT.getVar[Int](1.toByte).get") shouldBe SInt } + property("SContext.getVarFromInput") { + runWithVersion(VersionContext.V6SoftForkVersion) { + typecheck(env, "CONTEXT.getVarFromInput[Int](1.toShort, 1.toByte).get") shouldBe SInt + } + } + property("SAvlTree.digest") { typecheck(env, "getVar[AvlTree](1).get.digest") shouldBe SByteArray } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index ea9539a65a..0eba3c9878 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -7,7 +7,7 @@ import scorex.util.encode.Base16 import scorex.utils.Ints import sigma.Extensions.ArrayOps import sigma.{SigmaTestingData, VersionContext} -import sigma.VersionContext.V6SoftForkVersion +import sigma.VersionContext.{V6SoftForkVersion, withVersions} import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -19,7 +19,7 @@ import sigmastate._ import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigma.interpreter.ContextExtension.VarBinding -import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings +import sigmastate.interpreter.CErgoTreeEvaluator.{DefaultEvalSettings, currentEvaluator} import sigmastate.interpreter.Interpreter._ import sigma.ast.Apply import sigma.eval.EvalSettings @@ -92,8 +92,11 @@ class BasicOpsSpecification extends CompilerTestingCommons // is not supported by ErgoScript Compiler) // In such cases we use expected property as the property to test propExp.asSigmaProp - } else - compile(env, script).asBoolValue.toSigmaProp + } else { + withVersions(VersionContext.MaxSupportedScriptVersion, ergoTreeVersionInTests) { + compile(env, script).asBoolValue.toSigmaProp + } + } if (propExp != null) prop shouldBe propExp @@ -145,20 +148,6 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } - property("Context.getVar") { - def varTest() = { - test("GetVar1", env, ext, - "{ CONTEXT.getVar[Int](intVar2.toByte).get == 2 }", - null - ) - } - - if(VersionContext.current.isV6SoftForkActivated) { - varTest() - } else { - an[Exception] should be thrownBy(varTest()) - } - } property("getVarFromInput") { def getVarTest(): Assertion = { @@ -174,7 +163,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { - an[Exception] should be thrownBy getVarTest() + an[sigma.validation.ValidationException] should be thrownBy getVarTest() } } @@ -195,7 +184,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { - an[Exception] should be thrownBy getVarTest() + an[sigma.validation.ValidationException] should be thrownBy getVarTest() } } @@ -213,13 +202,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { - an[Exception] should be thrownBy getVarTest() + an[sigma.validation.ValidationException] should be thrownBy getVarTest() } } + property("Byte.toBits") { - def toBitsTest() = test("Byte.toBits", env, ext, + val customExt = Map( + 1.toByte -> ByteConstant(1) + ).toSeq + def toBitsTest() = test("Byte.toBits", env, customExt, """{ - | val b = 1.toByte + | val b = getVar[Byte](1).get | b.toBits == Coll(false, false, false, false, false, false, false, true) |}""".stripMargin, null @@ -228,14 +221,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBitsTest() } else { - an[Exception] shouldBe thrownBy(toBitsTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBitsTest()) } } property("Long.toBits") { - def toBitsTest() = test("Long.toBits", env, ext, + val customExt = Map( + 1.toByte -> LongConstant(1) + ).toSeq + def toBitsTest() = test("Long.toBits", env, customExt, """{ - | val b = 1L + | val b = getVar[Long](1).get | val ba = b.toBits | | // only rightmost bit is set @@ -247,7 +243,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBitsTest() } else { - an[Exception] shouldBe thrownBy(toBitsTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBitsTest()) } } @@ -264,7 +260,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBitsTest() } else { - an[Exception] shouldBe thrownBy(toBitsTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBitsTest()) } } @@ -281,10 +277,11 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseInverseTest() } else { - an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) } } + property("Byte.bitwiseInverse") { def bitwiseInverseTest(): Assertion = test("Byte.bitwiseInverse", env, ext, s"""{ @@ -297,14 +294,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseInverseTest() } else { - an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) } } property("Long.bitwiseInverse") { - def bitwiseInverseTest(): Assertion = test("Long.bitwiseInverse", env, ext, + val customExt = Map( + 1.toByte -> LongConstant(9223372036854775807L) + ).toSeq + def bitwiseInverseTest(): Assertion = test("Long.bitwiseInverse", env, customExt, s"""{ - | val l = 9223372036854775807L + | val l = getVar[Long](1).get | val lb = l.bitwiseInverse | lb.bitwiseInverse == l |}""".stripMargin, @@ -314,14 +314,18 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseInverseTest() } else { - an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) } } + property("Byte.bitwiseOr") { - def bitwiseOrTest(): Assertion = test("Byte.bitwiseOrTest", env, ext, + val customExt = Map( + 1.toByte -> ByteConstant(127) + ).toSeq + def bitwiseOrTest(): Assertion = test("Byte.bitwiseOrTest", env, customExt, s"""{ - | val x = 127.toByte + | val x = getVar[Byte](1).get | val y = (-128).toByte | x.bitwiseOr(y) == -1 |}""".stripMargin, @@ -331,7 +335,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseOrTest() } else { - an[Exception] shouldBe thrownBy(bitwiseOrTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) } } @@ -347,7 +351,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseOrTest() } else { - an[Exception] shouldBe thrownBy(bitwiseOrTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) } } @@ -364,14 +368,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseAndTest() } else { - an[Exception] shouldBe thrownBy(bitwiseAndTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) } } property("Short.bitwiseAnd") { - def bitwiseAndTest(): Assertion = test("Short.bitwiseAnd", env, ext, + val customExt = Map( + 1.toByte -> ShortConstant(32767) + ).toSeq + def bitwiseAndTest(): Assertion = test("Short.bitwiseAnd", env, customExt, s"""{ - | val x = (32767).toShort + | val x = getVar[Short](1).get | val y = (-32768).toShort | x.bitwiseAnd(y) == 0 |}""".stripMargin, @@ -381,14 +388,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseAndTest() } else { - an[Exception] shouldBe thrownBy(bitwiseAndTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) } } property("Short.bitwiseXor") { - def bitwiseXorTest(): Assertion = test("Short.bitwiseXor", env, ext, + val customExt = Map( + 1.toByte -> ShortConstant(32767) + ).toSeq + def bitwiseXorTest(): Assertion = test("Short.bitwiseXor", env, customExt, s"""{ - | val x = (32767).toShort + | val x = getVar[Short](1).get | val y = (-32768).toShort | x.bitwiseXor(y) == -1 |}""".stripMargin, @@ -398,7 +408,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseXorTest() } else { - an[Exception] shouldBe thrownBy(bitwiseXorTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseXorTest()) } } @@ -415,7 +425,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftLeftTest() } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -432,7 +442,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[IllegalArgumentException] shouldBe thrownBy(shiftLeftTest()) } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -449,7 +459,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftLeftTest() } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -466,7 +476,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftLeftTest() } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -482,7 +492,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[ArithmeticException] shouldBe thrownBy(shiftLeftTest()) } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -499,7 +509,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftRightTest() } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -516,7 +526,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftRightTest() } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -533,10 +543,11 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } + property("Long.shiftRight - neg") { def shiftRightTest(): Assertion = test("Long.shiftRight", env, ext, s"""{ @@ -550,7 +561,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftRightTest() } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -567,7 +578,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -585,7 +596,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftRightTest() } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -603,7 +614,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -621,7 +632,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { - an[Exception] should be thrownBy getVarTest() + an[sigma.validation.ValidationException] should be thrownBy getVarTest() } } @@ -642,7 +653,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -663,7 +674,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -687,7 +698,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -711,7 +722,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -735,7 +746,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -759,7 +770,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -777,7 +788,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { getTest() } else { - an[Exception] shouldBe thrownBy(getTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(getTest()) } } @@ -793,7 +804,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } @@ -809,7 +820,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } @@ -825,7 +836,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } @@ -842,14 +853,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } property("Global.fromBigEndianBytes - Long.toBytes") { - def fromTest() = test("fromBigEndianBytes - long", env, ext, + val customExt = Map( + 1.toByte -> LongConstant(1088800L) + ).toSeq + def fromTest() = test("fromBigEndianBytes - long", env, customExt, s"""{ - | val l = 1088800L + | val l = getVar[Long](1).get | val ba = l.toBytes | Global.fromBigEndianBytes[Long](ba) == l |} @@ -859,7 +873,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } @@ -876,14 +890,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } property("Int.toBytes") { - def toBytesTest() = test("Int.toBytes", env, ext, + val customExt = Map( + 1.toByte -> IntConstant(1) + ).toSeq + def toBytesTest() = test("Int.toBytes", env, customExt, """{ - | val l = 1 + | val l = getVar[Int](1).get | l.toBytes == Coll(0.toByte, 0.toByte, 0.toByte, 1.toByte) | }""".stripMargin, null @@ -892,14 +909,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[Exception] shouldBe thrownBy(toBytesTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) } } property("Int.toBits") { - def toBytesTest() = test("Int.toBytes", env, ext, + val customExt = Map( + 1.toByte -> IntConstant(1477959696) + ).toSeq + def toBytesTest() = test("Int.toBytes", env, customExt, """{ - | val l = 1477959696 + | val l = getVar[Int](1).get | 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 @@ -908,14 +928,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[Exception] shouldBe thrownBy(toBytesTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) } } property("Byte.toBytes") { - def toBytesTest() = test("Byte.toBytes", env, ext, + val customExt = Map( + 1.toByte -> ByteConstant(10) + ).toSeq + def toBytesTest() = test("Byte.toBytes", env, customExt, """{ - | val l = 10.toByte + | val l = getVar[Byte](1).get | l.toBytes == Coll(10.toByte) | }""".stripMargin, null @@ -924,7 +947,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[Exception] shouldBe thrownBy(toBytesTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) } } @@ -941,7 +964,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[Exception] shouldBe thrownBy(toBytesTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) } } @@ -956,7 +979,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -973,7 +996,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -990,7 +1013,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1009,7 +1032,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1030,7 +1053,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [Exception] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1051,7 +1074,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [Exception] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1075,7 +1098,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1100,7 +1123,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an[Exception] should be thrownBy deserTest() + an[sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1122,7 +1145,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an[Exception] should be thrownBy deserTest() + an[sigma.validation.ValidationException] should be thrownBy deserTest() } else { // we have wrapped CostLimitException here an[Exception] should be thrownBy deserTest() @@ -1162,7 +1185,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { optTest() } else { - an[Exception] shouldBe thrownBy(optTest()) + assertExceptionThrown(optTest(), _.isInstanceOf[NoSuchElementException]) } } @@ -1485,7 +1508,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { holTest() } else { - an[Exception] shouldBe thrownBy(holTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(holTest()) } } @@ -1799,11 +1822,15 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("Box.getReg") { + val customExt = Map( + 1.toByte -> IntConstant(0) + ).toSeq def getRegTest(): Assertion = { - test("Box.getReg", env, ext, + test("Box.getReg", env, customExt, """{ + | val idx = getVar[Int](1).get | val x = SELF - | x.getReg[Long](0).get == SELF.value && + | x.getReg[Long](idx).get == SELF.value && | x.getReg[Coll[(Coll[Byte], Long)]](2).get == SELF.tokens && | x.getReg[Int](9).isEmpty |}""".stripMargin, @@ -1814,7 +1841,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getRegTest() } else { - an[Exception] should be thrownBy getRegTest() + an[sigma.exceptions.ConstraintFailed] should be thrownBy getRegTest() } } @@ -1835,7 +1862,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getRegTest() } else { - an[Exception] should be thrownBy getRegTest() + an[java.nio.BufferUnderflowException] should be thrownBy getRegTest() } } From 8ae51e45a58bdd7d611f7a7692c6b1883fdb7ac4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 21 Oct 2024 23:15:19 +0300 Subject: [PATCH 70/78] split in LSV6 --- .../scala/sigma/LanguageSpecificationV6.scala | 177 +++++++++--------- 1 file changed, 90 insertions(+), 87 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 34b156fc79..15ce673332 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1530,104 +1530,104 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } - property("Context.getVar and getVarFromInput") { - - def contextData() = { - val input = CBox( - new ErgoBox( - 80946L, - new ErgoTree( - HeaderType @@ 16.toByte, - Vector( - SigmaPropConstant( - CSigmaProp( - ProveDHTuple( - Helpers.decodeECPoint("03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb"), - Helpers.decodeECPoint("023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d03"), - Helpers.decodeECPoint("03d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72"), - Helpers.decodeECPoint("037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441") - ) + private def contextData() = { + val input = CBox( + new ErgoBox( + 80946L, + new ErgoTree( + HeaderType @@ 16.toByte, + Vector( + SigmaPropConstant( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb"), + Helpers.decodeECPoint("023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d03"), + Helpers.decodeECPoint("03d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72"), + Helpers.decodeECPoint("037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441") ) ) - ), - Right(ConstantPlaceholder(0, SSigmaProp)) - ), - Coll(), - Map( - ErgoBox.R4 -> ByteArrayConstant(Helpers.decodeBytes("34")), - ErgoBox.R5 -> TrueLeaf + ) ), - ModifierId @@ ("0000bfe96a7c0001e7a5ee00aafb80ff057fbe7f8c6680e33a3dc18001820100"), - 1.toShort, - 5 - ) + Right(ConstantPlaceholder(0, SSigmaProp)) + ), + Coll(), + Map( + ErgoBox.R4 -> ByteArrayConstant(Helpers.decodeBytes("34")), + ErgoBox.R5 -> TrueLeaf + ), + ModifierId @@ ("0000bfe96a7c0001e7a5ee00aafb80ff057fbe7f8c6680e33a3dc18001820100"), + 1.toShort, + 5 ) + ) - val tx = ErgoLikeTransaction( - IndexedSeq(), - IndexedSeq(input.wrappedValue) - ) + val tx = ErgoLikeTransaction( + IndexedSeq(), + IndexedSeq(input.wrappedValue) + ) - val tx2 = ErgoLikeTransaction( - IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(true)))))), - IndexedSeq(input.wrappedValue) - ) + val tx2 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(true)))))), + IndexedSeq(input.wrappedValue) + ) - val tx3 = ErgoLikeTransaction( - IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> IntConstant(0)))))), - IndexedSeq(input.wrappedValue) - ) + val tx3 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> IntConstant(0)))))), + IndexedSeq(input.wrappedValue) + ) - val tx4 = ErgoLikeTransaction( - IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(false)))))), - IndexedSeq(input.wrappedValue) - ) + val tx4 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(false)))))), + IndexedSeq(input.wrappedValue) + ) - val ctx = CContext( - _dataInputs = Coll[Box](), - headers = Coll[Header](), - preHeader = CPreHeader( - 0.toByte, - Colls.fromArray(Array.fill(32)(0.toByte)), - -755484979487531112L, - 9223372036854775807L, - 11, - Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), - Helpers.decodeBytes("007f00") - ), - inputs = Coll[Box](input), - outputs = Coll[Box](), - height = 11, - selfBox = input.copy(), // in 3.x, 4.x implementation selfBox is never the same instance as input (see toSigmaContext) - selfIndex = 0, - lastBlockUtxoRootHash = CAvlTree( - AvlTreeData( - ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17").toColl, - AvlTreeFlags(true, true, true), - 1211925457, - None - ) - ), - _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), - vars = Colls - .replicate[AnyValue](10, null) // reserve 10 vars - .append(Coll[AnyValue]( - CAnyValue(Helpers.decodeBytes("00")), - CAnyValue(true))), - spendingTransaction = tx, - activatedScriptVersion = activatedVersionInTests, - currentErgoTreeVersion = ergoTreeVersionInTests - ) - val ctx2 = ctx.copy(spendingTransaction = tx2) - val ctx3 = ctx.copy(spendingTransaction = tx3, vars = ctx.vars.patch(11, Coll(CAnyValue(0)), 1)) - val ctx4 = ctx.copy(spendingTransaction = tx4, vars = ctx.vars.patch(11, Coll(CAnyValue(false)), 1)) + val ctx = CContext( + _dataInputs = Coll[Box](), + headers = Coll[Header](), + preHeader = CPreHeader( + 0.toByte, + Colls.fromArray(Array.fill(32)(0.toByte)), + -755484979487531112L, + 9223372036854775807L, + 11, + Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + Helpers.decodeBytes("007f00") + ), + inputs = Coll[Box](input), + outputs = Coll[Box](), + height = 11, + selfBox = input.copy(), // in 3.x, 4.x implementation selfBox is never the same instance as input (see toSigmaContext) + selfIndex = 0, + lastBlockUtxoRootHash = CAvlTree( + AvlTreeData( + ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17").toColl, + AvlTreeFlags(true, true, true), + 1211925457, + None + ) + ), + _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + vars = Colls + .replicate[AnyValue](10, null) // reserve 10 vars + .append(Coll[AnyValue]( + CAnyValue(Helpers.decodeBytes("00")), + CAnyValue(true))), + spendingTransaction = tx, + activatedScriptVersion = activatedVersionInTests, + currentErgoTreeVersion = ergoTreeVersionInTests + ) + val ctx2 = ctx.copy(spendingTransaction = tx2) + val ctx3 = ctx.copy(spendingTransaction = tx3, vars = ctx.vars.patch(11, Coll(CAnyValue(0)), 1)) + val ctx4 = ctx.copy(spendingTransaction = tx4, vars = ctx.vars.patch(11, Coll(CAnyValue(false)), 1)) + + (ctx, ctx2, ctx3, ctx4) + } - (ctx, ctx2, ctx3, ctx4) - } + property("getVarFromInput") { def getVarFromInput = { newFeature( - { (x: Context) => x.getVarFromInput[Boolean](0, 11)}, + { (x: Context) => x.getVarFromInput[Boolean](0, 11) }, "{ (x: Context) => x.getVarFromInput[Boolean](0, 11) }", FuncValue( Array((1, SContext)), @@ -1653,6 +1653,9 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ), getVarFromInput ) + } + + property("Context.getVar") { def getVar = { newFeature( @@ -1662,6 +1665,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + val (_, ctx2, ctx3, ctx4) = contextData() + verifyCases( Seq( ctx2 -> new Expected(ExpectedResult(Success(Some(true)), None)), @@ -1672,8 +1677,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } - - property("Option.getOrElse with lazy default") { val trace = TracedCost( From 1fbd4e598cd5331fe71ebe2492f5946c8c4bb095 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 24 Oct 2024 19:54:12 +0300 Subject: [PATCH 71/78] Update docs/LangSpec.md Co-authored-by: Alexander Slesarenko --- docs/LangSpec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/LangSpec.md b/docs/LangSpec.md index 7ec7dd7729..f1aace7de2 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -919,7 +919,7 @@ def longToByteArray(input: Long): Coll[Byte] def decodePoint(bytes: Coll[Byte]): GroupElement -/** Extracts Context variable from self input by id and type. +/** Extracts Context variable from SELF input by id and type. * ErgoScript is typed, so accessing a the variables is an operation which involves * some expected type given in brackets. Thus `getVar[Int](id)` expression should * evaluate to a valid value of the `Option[Int]` type. From 208a191534b54a197d40bd4fd6c338fa06a337f1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 24 Oct 2024 19:54:22 +0300 Subject: [PATCH 72/78] Update docs/LangSpec.md Co-authored-by: Alexander Slesarenko --- docs/LangSpec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/LangSpec.md b/docs/LangSpec.md index f1aace7de2..16defff5ff 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -976,7 +976,7 @@ def decodePoint(bytes: Coll[Byte]): GroupElement */ def getVar[T](tag: Int): Option[T] -/** Extracts Context variable from any input by input id, variable id and variable type. +/** Extracts Context variable from any input by input index, variable id and variable type. * Unlike getVar, it is not throwing exception when expected type does not match real type of the variable. * Thus it can be used to get context variable from self without exception, using selfBoxIndex, e.g. *

From a160997209a0d9df7564079317aad6a089cb8744 Mon Sep 17 00:00:00 2001
From: Alexander Chepurnoy 
Date: Thu, 31 Oct 2024 15:44:18 +0300
Subject: [PATCH 73/78] UnsignedBigInt support in DataSerializerSpecification

---
 core/shared/src/main/scala/sigma/ast/SType.scala          | 2 +-
 core/shared/src/main/scala/sigma/util/Extensions.scala    | 8 ++++++++
 .../src/main/scala/sigma/data/DataValueComparer.scala     | 1 -
 .../sigma/serialization/DataSerializerSpecification.scala | 3 ++-
 .../sigma/serialization/generators/ObjectGenerators.scala | 2 ++
 .../sigma/serialization/generators/TypeGenerators.scala   | 8 +++++---
 .../src/test/scala/sigmastate/ErgoTreeSpecification.scala | 3 ++-
 7 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala
index b69a988347..17a5ef10d7 100644
--- a/core/shared/src/main/scala/sigma/ast/SType.scala
+++ b/core/shared/src/main/scala/sigma/ast/SType.scala
@@ -322,7 +322,7 @@ object SPrimType {
   def unapply(t: SType): Option[SType] = SType.allPredefTypes.find(_ == t)
 
   /** Type code of the last valid prim type so that (1 to LastPrimTypeCode) is a range of valid codes. */
-  final val LastPrimTypeCode: Byte = 8: Byte
+  final val LastPrimTypeCode: Byte = 9: Byte
 
   /** Upper limit of the interval of valid type codes for primitive types */
   final val MaxPrimTypeCode: Byte = 11: Byte
diff --git a/core/shared/src/main/scala/sigma/util/Extensions.scala b/core/shared/src/main/scala/sigma/util/Extensions.scala
index 5d1d9b7da8..e97241ca3d 100644
--- a/core/shared/src/main/scala/sigma/util/Extensions.scala
+++ b/core/shared/src/main/scala/sigma/util/Extensions.scala
@@ -227,6 +227,14 @@ object Extensions {
 
     /** Converts `x` to [[sigma.BigInt]] */
     def toBigInt: sigma.BigInt = CBigInt(x)
+
+    /** Converts `x` to [[sigma.UnsignedBigInt]] */
+    def toUnsignedBigInt: sigma.UnsignedBigInt = {
+      if(x.compareTo(BigInteger.ZERO) < 0){
+        throw new IllegalArgumentException("toUnsignedBigInt arg < 0")
+      }
+      CUnsignedBigInt(x)
+    }
   }
 
   implicit class BigIntOps(val x: sigma.BigInt) extends AnyVal {
diff --git a/data/shared/src/main/scala/sigma/data/DataValueComparer.scala b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala
index 39022a2e7e..e525b8f074 100644
--- a/data/shared/src/main/scala/sigma/data/DataValueComparer.scala
+++ b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala
@@ -344,7 +344,6 @@ object DataValueComparer {
           okEqual = bi == r
         }
 
-      // todo: check costing
       case ubi: UnsignedBigInt => /** case 5 (see [[EQ_BigInt]]) */
         E.addFixedCost(EQ_BigInt) {
           okEqual = ubi == r
diff --git a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala
index fe6f62dbe0..e17c21b03e 100644
--- a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala
@@ -66,7 +66,7 @@ class DataSerializerSpecification extends SerializationSpecification {
     implicit val tagT = tT.classTag
     implicit val tAny = sigma.AnyType
 
-    val withVersion = if (tpe == SHeader) {
+    val withVersion = if (tpe == SHeader || tpe == SUnsignedBigInt) {
       Some(VersionContext.V6SoftForkVersion)
     } else {
       None
@@ -148,6 +148,7 @@ class DataSerializerSpecification extends SerializationSpecification {
     forAll { x: Long => roundtrip[SLong.type](x, SLong) }
     forAll { x: String => roundtrip[SString.type](x, SString) }
     forAll { x: BigInteger => roundtrip[SBigInt.type](x.toBigInt, SBigInt) }
+    forAll { x: BigInteger => roundtrip[SUnsignedBigInt.type](x.abs().toUnsignedBigInt, SUnsignedBigInt, Some(VersionContext.V6SoftForkVersion)) }
     forAll { x: EcPointType => roundtrip[SGroupElement.type](x.toGroupElement, SGroupElement) }
     forAll { x: SigmaBoolean => roundtrip[SSigmaProp.type](x.toSigmaProp, SSigmaProp) }
     forAll { x: ErgoBox => roundtrip[SBox.type](x, SBox) }
diff --git a/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala
index db6cd87330..9a4668d8e0 100644
--- a/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala
+++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala
@@ -83,6 +83,7 @@ trait ObjectGenerators extends TypeGenerators
   implicit lazy val arbRegisterIdentifier: Arbitrary[RegisterId] = Arbitrary(registerIdentifierGen)
   implicit lazy val arbBigInteger: Arbitrary[BigInteger] = Arbitrary(Arbitrary.arbBigInt.arbitrary.map(_.bigInteger))
   implicit lazy val arbBigInt: Arbitrary[BigInt] = Arbitrary(arbBigInteger.arbitrary.map(SigmaDsl.BigInt(_)))
+  implicit lazy val arbUnsignedBigInt: Arbitrary[UnsignedBigInt] = Arbitrary(arbBigInteger.arbitrary.map(_.abs()).map(SigmaDsl.UnsignedBigInt(_)))
   implicit lazy val arbEcPointType: Arbitrary[dlogGroup.ElemType] = Arbitrary(Gen.const(()).flatMap(_ => CryptoConstants.dlogGroup.createRandomGenerator()))
   implicit lazy val arbGroupElement: Arbitrary[GroupElement] = Arbitrary(arbEcPointType.arbitrary.map(SigmaDsl.GroupElement(_)))
   implicit lazy val arbSigmaBoolean: Arbitrary[SigmaBoolean] = Arbitrary(Gen.oneOf(proveDHTGen, proveDHTGen))
@@ -305,6 +306,7 @@ trait ObjectGenerators extends TypeGenerators
     case SInt => arbInt
     case SLong => arbLong
     case SBigInt => arbBigInt
+    case SUnsignedBigInt => arbUnsignedBigInt
     case SGroupElement => arbGroupElement
     case SSigmaProp => arbSigmaProp
     case SBox => arbBox
diff --git a/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala
index 70a215e831..dc7962de0e 100644
--- a/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala
+++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala
@@ -11,6 +11,7 @@ trait TypeGenerators {
   implicit val intTypeGen: Gen[SInt.type] = Gen.const(SInt)
   implicit val longTypeGen: Gen[SLong.type] = Gen.const(SLong)
   implicit val bigIntTypeGen: Gen[SBigInt.type] = Gen.const(SBigInt)
+  implicit val unsignedBigIntTypeGen: Gen[SUnsignedBigInt.type] = Gen.const(SUnsignedBigInt)
   implicit val groupElementTypeGen: Gen[SGroupElement.type] = Gen.const(SGroupElement)
   implicit val sigmaPropTypeGen: Gen[SSigmaProp.type] = Gen.const(SSigmaProp)
   implicit val boxTypeGen: Gen[SBox.type] = Gen.const(SBox)
@@ -19,10 +20,10 @@ trait TypeGenerators {
   implicit val headerTypeGen: Gen[SHeader.type] = Gen.const(SHeader)
 
   implicit val primTypeGen: Gen[SPrimType] =
-    Gen.oneOf[SPrimType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp, SUnit)
+    Gen.oneOf[SPrimType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt, SGroupElement, SSigmaProp, SUnit)
   implicit val arbPrimType: Arbitrary[SPrimType] = Arbitrary(primTypeGen)
   implicit val predefTypeGen: Gen[SPredefType] =
-    Gen.oneOf[SPredefType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp, SUnit, SBox, SAvlTree, SHeader)
+    Gen.oneOf[SPredefType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt, SGroupElement, SSigmaProp, SUnit, SBox, SAvlTree, SHeader)
   implicit val arbPredefType: Arbitrary[SPredefType] = Arbitrary(predefTypeGen)
 
   implicit def genToArbitrary[T: Gen]: Arbitrary[T] = Arbitrary(implicitly[Gen[T]])
@@ -34,7 +35,8 @@ trait TypeGenerators {
       shortTypeGen,
       intTypeGen,
       longTypeGen,
-      bigIntTypeGen
+      bigIntTypeGen,
+      unsignedBigIntTypeGen
     ))
   } yield STuple(values.toIndexedSeq)
 
diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
index 1c38ca45a0..83870757e3 100644
--- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
@@ -264,7 +264,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C
 
   val typeCodes = Table(
     ("constant", "expectedValue"),
-    (SPrimType.LastPrimTypeCode, 8),
+    (SPrimType.LastPrimTypeCode, 9),
     (SPrimType.MaxPrimTypeCode, 11)
   )
   
@@ -285,6 +285,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C
     (SBigInt,  6, true, true, true),
     (SGroupElement, 7, true, true, false),
     (SSigmaProp,    8, true, true, false),
+    (SUnsignedBigInt, 9, true, true, true),
     (SBox,       99,  false, false, false),
     (SAvlTree,   100, false, false, false),
     (SContext,   101, false, false, false),

From fdf712e274c39719099354ee61e67fa6946b4962 Mon Sep 17 00:00:00 2001
From: Alexander Chepurnoy 
Date: Thu, 31 Oct 2024 17:59:12 +0300
Subject: [PATCH 74/78] ErgoTreeSpecification update

---
 .../src/main/scala/sigma/ast/methods.scala    | 16 +++----
 .../sigmastate/ErgoTreeSpecification.scala    | 48 +++++++++++++++++--
 2 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala
index f3517ae496..9b0e5e8584 100644
--- a/data/shared/src/main/scala/sigma/ast/methods.scala
+++ b/data/shared/src/main/scala/sigma/ast/methods.scala
@@ -497,7 +497,7 @@ case object SBigIntMethods extends SNumericTypeMethods {
   private val ToUnsignedCostKind = FixedCost(JitCost(5))
 
   //id = 8 to make it after toBits
-  val ToUnsigned = SMethod(this, "toUnsigned", SFunc(this.ownerType, SUnsignedBigInt), 19, ToUnsignedCostKind)
+  val ToUnsigned = SMethod(this, "toUnsigned", SFunc(this.ownerType, SUnsignedBigInt), 14, ToUnsignedCostKind)
     .withIRInfo(MethodCallIrBuilder)
     .withInfo(MethodCall, "")
 
@@ -508,7 +508,7 @@ case object SBigIntMethods extends SNumericTypeMethods {
   }
 
 
-  val ToUnsignedMod = SMethod(this, "toUnsignedMod", SFunc(Array(this.ownerType, SUnsignedBigInt), SUnsignedBigInt), 20, ToUnsignedCostKind)
+  val ToUnsignedMod = SMethod(this, "toUnsignedMod", SFunc(Array(this.ownerType, SUnsignedBigInt), SUnsignedBigInt), 15, ToUnsignedCostKind)
     .withIRInfo(MethodCallIrBuilder)
     .withInfo(MethodCall, "")
 
@@ -541,7 +541,7 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods {
   final val ModInverseCostInfo = ToNBitsCostInfo
 
   // todo: check ids before and after merging with other PRs introducing new methods for Numeric
-  val ModInverseMethod = SMethod(this, "modInverse", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 19, ModInverseCostInfo.costKind)
+  val ModInverseMethod = SMethod(this, "modInverse", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 14, ModInverseCostInfo.costKind)
     .withIRInfo(MethodCallIrBuilder)
     .withInfo(MethodCall, "")
 
@@ -552,7 +552,7 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods {
   }
 
   // todo: costing
-  val PlusModMethod = SMethod(this, "plusMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 20, ModInverseCostInfo.costKind)
+  val PlusModMethod = SMethod(this, "plusMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 15, ModInverseCostInfo.costKind)
     .withIRInfo(MethodCallIrBuilder)
     .withInfo(MethodCall, "")
 
@@ -562,7 +562,7 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods {
     bi.plusMod(bi2, m)
   }
 
-  val SubtractModMethod = SMethod(this, "subtractMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 21, ModInverseCostInfo.costKind)
+  val SubtractModMethod = SMethod(this, "subtractMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 16, ModInverseCostInfo.costKind)
     .withIRInfo(MethodCallIrBuilder)
     .withInfo(MethodCall, "")
 
@@ -572,7 +572,7 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods {
     bi.subtractMod(bi2, m)
   }
 
-  val MultiplyModMethod = SMethod(this, "multiplyMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 22, ModInverseCostInfo.costKind)
+  val MultiplyModMethod = SMethod(this, "multiplyMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 17, ModInverseCostInfo.costKind)
     .withIRInfo(MethodCallIrBuilder)
     .withInfo(MethodCall, "")
 
@@ -582,7 +582,7 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods {
     bi.multiplyMod(bi2, m)
   }
 
-  val ModMethod = SMethod(this, "mod", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 23, ModInverseCostInfo.costKind)
+  val ModMethod = SMethod(this, "mod", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 18, ModInverseCostInfo.costKind)
     .withIRInfo(MethodCallIrBuilder)
     .withInfo(MethodCall, "")
 
@@ -592,7 +592,7 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods {
     bi.mod(m)
   }
 
-  val ToSignedMethod = SMethod(this, "toSigned", SFunc(Array(this.ownerType), SBigInt), 24, ModInverseCostInfo.costKind)
+  val ToSignedMethod = SMethod(this, "toSigned", SFunc(Array(this.ownerType), SBigInt), 19, ModInverseCostInfo.costKind)
     .withIRInfo(MethodCallIrBuilder)
     .withInfo(MethodCall, "")
 
diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
index 83870757e3..79aaa97f89 100644
--- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
@@ -7,11 +7,13 @@ import sigma.ast.SCollection.SByteArray
 import sigma.ast._
 import sigma.ast.syntax.{SValue, SigmaPropValue, TrueSigmaProp}
 import sigma.data.RType.asType
-import sigma.data.{CBox, Nullable, RType, TrivialProp}
+import sigma.data.{Nullable, RType, TrivialProp}
 import sigma.validation.ValidationException
 import sigma.validation.ValidationRules.CheckTypeCode
 import ErgoTree.HeaderType
 import SCollectionMethods.checkValidFlatmap
+import sigma.ast.SBigIntMethods.{ToUnsigned, ToUnsignedMod}
+import sigma.ast.SUnsignedBigIntMethods.{ModInverseMethod, ModMethod, MultiplyModMethod, PlusModMethod, SubtractModMethod, ToSignedMethod}
 import sigmastate.eval.CProfiler
 import sigmastate.helpers.{ErgoLikeContextTesting, SigmaPPrint}
 import sigmastate.interpreter.Interpreter.ReductionResult
@@ -431,17 +433,55 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C
           MInfo(10, BitwiseAndMethod, isResolvableFromIds = true),
           MInfo(11, BitwiseXorMethod, isResolvableFromIds = true),
           MInfo(12, ShiftLeftMethod, isResolvableFromIds = true),
-          MInfo(13, ShiftRightMethod, isResolvableFromIds = true)
+          MInfo(13, ShiftRightMethod, isResolvableFromIds = true),
+          MInfo(14, ToUnsigned, isResolvableFromIds = true),
+          MInfo(15, ToUnsignedMod, isResolvableFromIds = true)
         ) else Seq.empty)
         , true)
     },
+      {
+        if (isV6Activated) {
+          // SBigInt inherit methods from SNumericType.methods
+          // however they are not resolvable via SBigInt.typeId before v6.0
+          import SNumericTypeMethods._
+          (SUnsignedBigInt.typeId, Seq(
+            MInfo(methodId = 1, ToByteMethod, isResolvableFromIds = true),
+            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),
+            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),
+            MInfo(14, ModInverseMethod, true),
+            MInfo(15, PlusModMethod, true),
+            MInfo(16, SubtractModMethod, true),
+            MInfo(17, MultiplyModMethod, true),
+            MInfo(18, ModMethod, true),
+            MInfo(19, ToSignedMethod, true)
+          ), true)
+        } else {
+          (SUnsignedBigInt.typeId, Seq.empty, false)
+        }
+      },
     { import SGroupElementMethods._
       (SGroupElement.typeId,  Seq(
         MInfo(2, GetEncodedMethod),
         MInfo(3, ExponentiateMethod),
         MInfo(4, MultiplyMethod),
         MInfo(5, NegateMethod)
-      ), true)
+      ) ++ {
+        if(VersionContext.current.isV6SoftForkActivated) {
+          Seq(MInfo(6, ExponentiateUnsignedMethod))
+        } else {
+          Seq.empty
+        }
+      }, true)
     },
     { import SSigmaPropMethods._
       (SSigmaProp.typeId,  Seq(
@@ -626,7 +666,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C
   }
 
   property("MethodCall on numerics") {
-    forAll(Table[STypeCompanion]("type", SByte, SShort, SInt, SLong, SBigInt)) { t =>
+    forAll(Table[STypeCompanion]("type", SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt)) { t =>
       // this methods are expected to fail resolution in before v6.0
       if (!isV6Activated) {
         (1 to 7).foreach { methodId =>

From dde7f6e3bb5d2f35dd28160a9bd6c35e3df9b4d5 Mon Sep 17 00:00:00 2001
From: Alexander Chepurnoy 
Date: Thu, 31 Oct 2024 23:36:16 +0300
Subject: [PATCH 75/78] fixing DataJsonEncoderSpecification

---
 .../org/ergoplatform/sdk/DataJsonEncoderSpecification.scala     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala
index 5835f399cb..2457aacfea 100644
--- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala
+++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala
@@ -44,7 +44,7 @@ class DataJsonEncoderSpecification extends SerializationSpecification {
     implicit val tT = Evaluation.stypeToRType(tpe)
     implicit val tagT = tT.classTag
 
-    val withVersion = if (tpe == SHeader) {
+    val withVersion = if (tpe == SHeader || tpe == SUnsignedBigInt) {
       Some(VersionContext.V6SoftForkVersion)
     } else {
       None

From a18967c96c9eee0f91c8e3cb8309f0083a5dbfc0 Mon Sep 17 00:00:00 2001
From: Alexander Chepurnoy 
Date: Fri, 1 Nov 2024 00:23:29 +0300
Subject: [PATCH 76/78] SigmaTyperTest fix

---
 .../DataSerializerSpecification.scala         |  3 ++-
 .../generators/TypeGenerators.scala           | 10 +++++--
 .../sdk/DataJsonEncoderSpecification.scala    | 26 +++++--------------
 3 files changed, 17 insertions(+), 22 deletions(-)

diff --git a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala
index e17c21b03e..9201214e4f 100644
--- a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala
@@ -67,10 +67,11 @@ class DataSerializerSpecification extends SerializationSpecification {
     implicit val tAny = sigma.AnyType
 
     val withVersion = if (tpe == SHeader || tpe == SUnsignedBigInt) {
-      Some(VersionContext.V6SoftForkVersion)
+      None // Some(VersionContext.V6SoftForkVersion)
     } else {
       None
     }
+
     forAll { xs: Array[T#WrappedType] =>
       roundtrip[SCollection[T]](xs.toColl, SCollection(tpe), withVersion)
       roundtrip[SType](xs.toColl.map(x => (x, x)).asWrappedType, SCollection(STuple(tpe, tpe)), withVersion)
diff --git a/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala
index dc7962de0e..699ef1c8f8 100644
--- a/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala
+++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala
@@ -2,6 +2,7 @@ package sigma.serialization.generators
 
 import org.scalacheck.{Arbitrary, Gen}
 import org.scalacheck.Arbitrary.arbString
+import sigma.VersionContext
 import sigma.ast._
 
 trait TypeGenerators {
@@ -22,8 +23,13 @@ trait TypeGenerators {
   implicit val primTypeGen: Gen[SPrimType] =
     Gen.oneOf[SPrimType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt, SGroupElement, SSigmaProp, SUnit)
   implicit val arbPrimType: Arbitrary[SPrimType] = Arbitrary(primTypeGen)
-  implicit val predefTypeGen: Gen[SPredefType] =
-    Gen.oneOf[SPredefType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt, SGroupElement, SSigmaProp, SUnit, SBox, SAvlTree, SHeader)
+  implicit val predefTypeGen: Gen[SPredefType] = {
+    if(VersionContext.current.isV6SoftForkActivated){
+      Gen.oneOf[SPredefType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt, SGroupElement, SSigmaProp, SUnit, SBox, SAvlTree, SHeader)
+    } else {
+      Gen.oneOf[SPredefType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp, SUnit, SBox, SAvlTree)
+    }
+  }
   implicit val arbPredefType: Arbitrary[SPredefType] = Arbitrary(predefTypeGen)
 
   implicit def genToArbitrary[T: Gen]: Arbitrary[T] = Arbitrary(implicitly[Gen[T]])
diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala
index 2457aacfea..7e73b6c93d 100644
--- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala
+++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala
@@ -44,26 +44,20 @@ class DataJsonEncoderSpecification extends SerializationSpecification {
     implicit val tT = Evaluation.stypeToRType(tpe)
     implicit val tagT = tT.classTag
 
-    val withVersion = if (tpe == SHeader || tpe == SUnsignedBigInt) {
-      Some(VersionContext.V6SoftForkVersion)
-    } else {
-      None
-    }
 
     forAll { xs: Array[T#WrappedType] =>
-      roundtrip[SCollection[T]](xs.toColl, SCollection(tpe), withVersion)
-      roundtrip[SType](xs.toColl.map(x => (x, x)).asWrappedType, SCollection(STuple(tpe, tpe)), withVersion)
+      roundtrip[SCollection[T]](xs.toColl, SCollection(tpe))
+      roundtrip[SType](xs.toColl.map(x => (x, x)).asWrappedType, SCollection(STuple(tpe, tpe)))
 
       val nested = xs.toColl.map(x => Colls.fromItems[T#WrappedType](x, x))
-      roundtrip[SCollection[SCollection[T]]](nested, SCollection(SCollection(tpe)), withVersion)
+      roundtrip[SCollection[SCollection[T]]](nested, SCollection(SCollection(tpe)))
 
       roundtrip[SType](
         xs.toColl.map { x =>
           val arr = Colls.fromItems[T#WrappedType](x, x)
           (arr, arr)
         }.asWrappedType,
-        SCollection(STuple(SCollection(tpe), SCollection(tpe))),
-        withVersion
+        SCollection(STuple(SCollection(tpe), SCollection(tpe)))
       )
     }
   }
@@ -74,17 +68,11 @@ class DataJsonEncoderSpecification extends SerializationSpecification {
     @nowarn implicit val tag     : ClassTag[T#WrappedType] = tT.classTag
     @nowarn implicit val tAny    : RType[Any]              = sigma.AnyType
 
-    val withVersion = if (tpe == SHeader) {
-      Some(VersionContext.V6SoftForkVersion)
-    } else {
-      None
-    }
-
     forAll { in: (T#WrappedType, T#WrappedType) =>
       val (x,y) = (in._1, in._2)
-      roundtrip[SType]((x, y).asWrappedType, STuple(tpe, tpe), withVersion)
-      roundtrip[SType](((x, y), (x, y)).asWrappedType, STuple(STuple(tpe, tpe), STuple(tpe, tpe)), withVersion)
-      roundtrip[SType](((x, y), ((x, y), (x, y))).asWrappedType, STuple(STuple(tpe, tpe), STuple(STuple(tpe, tpe), STuple(tpe, tpe))), withVersion)
+      roundtrip[SType]((x, y).asWrappedType, STuple(tpe, tpe))
+      roundtrip[SType](((x, y), (x, y)).asWrappedType, STuple(STuple(tpe, tpe), STuple(tpe, tpe)))
+      roundtrip[SType](((x, y), ((x, y), (x, y))).asWrappedType, STuple(STuple(tpe, tpe), STuple(STuple(tpe, tpe), STuple(tpe, tpe))))
     }
   }
 

From 2d8af9d9c045a42951f15de1274e0c2582c4f3e8 Mon Sep 17 00:00:00 2001
From: Alexander Chepurnoy 
Date: Fri, 1 Nov 2024 18:27:47 +0300
Subject: [PATCH 77/78] adding UnsignedBigInt to JS reflection , pt1

---
 .../main/scala/sigma/crypto/Platform.scala    |  1 +
 core/js/src/main/scala/sigma/js/Isos.scala    | 14 +++++-
 .../sigma/reflection/ReflectionData.scala     | 15 +++++++
 data/js/src/main/scala/sigma/Platform.scala   |  1 +
 data/js/src/main/scala/sigma/js/Value.scala   |  6 +++
 .../src/main/scala/sigma/ast/methods.scala    |  1 -
 .../sigma/compiler/ir/GraphIRReflection.scala | 44 +++++++++++++++++++
 .../ir/wrappers/sigma/impl/SigmaDslImpl.scala |  3 +-
 8 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/core/js/src/main/scala/sigma/crypto/Platform.scala b/core/js/src/main/scala/sigma/crypto/Platform.scala
index 88001ba140..777789ba24 100644
--- a/core/js/src/main/scala/sigma/crypto/Platform.scala
+++ b/core/js/src/main/scala/sigma/crypto/Platform.scala
@@ -253,6 +253,7 @@ object Platform {
     case _: Boolean => tpe == SBoolean
     case _: Byte | _: Short | _: Int | _: Long => tpe.isInstanceOf[SNumericType]
     case _: BigInt => tpe == SBigInt
+    case _: UnsignedBigInt => tpe == SUnsignedBigInt
     case _: String => tpe == SString
     case _: GroupElement => tpe.isGroupElement
     case _: SigmaProp => tpe.isSigmaProp
diff --git a/core/js/src/main/scala/sigma/js/Isos.scala b/core/js/src/main/scala/sigma/js/Isos.scala
index 767a358d62..bc88c46457 100644
--- a/core/js/src/main/scala/sigma/js/Isos.scala
+++ b/core/js/src/main/scala/sigma/js/Isos.scala
@@ -1,7 +1,7 @@
 package sigma.js
 
 import sigma.{Coll, Colls}
-import sigma.data.{CBigInt, Iso, RType}
+import sigma.data.{CBigInt, CUnsignedBigInt, Iso, RType}
 
 import java.math.BigInteger
 import scala.reflect.ClassTag
@@ -42,6 +42,18 @@ object Isos {
     }
   }
 
+  implicit val isoUnsignedBigInt: Iso[js.BigInt, sigma.UnsignedBigInt] = new Iso[js.BigInt, sigma.UnsignedBigInt] {
+    override def to(x: js.BigInt): sigma.UnsignedBigInt = {
+      CUnsignedBigInt(new BigInteger(x.toString(10)))
+    }
+
+    override def from(x: sigma.UnsignedBigInt): js.BigInt = {
+      val bi = x.asInstanceOf[CUnsignedBigInt].wrappedValue
+      val s  = bi.toString(10)
+      js.BigInt(s)
+    }
+  }
+
   implicit val isoBigIntToLong: Iso[js.BigInt, Long] = new Iso[js.BigInt, Long] {
     override def to(x: js.BigInt): Long = java.lang.Long.parseLong(x.toString(10))
 
diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala
index 12e1d0eaa2..ef8e52e6cd 100644
--- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala
+++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala
@@ -153,6 +153,21 @@ object ReflectionData {
         },
         mkMethod(clazz, "divide", paramTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].divide(args(0).asInstanceOf[UnsignedBigInt])
+        },
+        mkMethod(clazz, "plusMod", paramTypes) { (obj, args) =>
+          obj.asInstanceOf[UnsignedBigInt].plusMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt])
+        },
+        mkMethod(clazz, "subtractMod", paramTypes) { (obj, args) =>
+          obj.asInstanceOf[UnsignedBigInt].subtractMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt])
+        },
+        mkMethod(clazz, "multiplyMod", paramTypes) { (obj, args) =>
+          obj.asInstanceOf[UnsignedBigInt].multiplyMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt])
+        },
+        mkMethod(clazz, "mod", paramTypes) { (obj, args) =>
+          obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt])
+        },
+        mkMethod(clazz, "modInverse", paramTypes) { (obj, args) =>
+          obj.asInstanceOf[UnsignedBigInt].modInverse(args(0).asInstanceOf[UnsignedBigInt])
         }
       )
     )
diff --git a/data/js/src/main/scala/sigma/Platform.scala b/data/js/src/main/scala/sigma/Platform.scala
index 29c761c3f1..2fd4c937f0 100644
--- a/data/js/src/main/scala/sigma/Platform.scala
+++ b/data/js/src/main/scala/sigma/Platform.scala
@@ -28,6 +28,7 @@ object Platform {
       case v: Long => Nullable(mkConstant[SLong.type](v, SLong))
       case v: BigInteger => Nullable(mkConstant[SBigInt.type](CBigInt(v), SBigInt))
       case n: sigma.BigInt => Nullable(mkConstant[SBigInt.type](n, SBigInt))
+      case n: sigma.UnsignedBigInt => Nullable(mkConstant[SUnsignedBigInt.type](n, SUnsignedBigInt))
       case ge: GroupElement => Nullable(mkConstant[SGroupElement.type](ge, SGroupElement))
       case b: Boolean => Nullable(if (b) TrueLeaf else FalseLeaf)
       case v: String => Nullable(mkConstant[SString.type](v, SString))
diff --git a/data/js/src/main/scala/sigma/js/Value.scala b/data/js/src/main/scala/sigma/js/Value.scala
index a65156bd43..77d6c8e104 100644
--- a/data/js/src/main/scala/sigma/js/Value.scala
+++ b/data/js/src/main/scala/sigma/js/Value.scala
@@ -81,6 +81,9 @@ object Value extends js.Object {
     case sigma.BigIntRType =>
       val v = data.asInstanceOf[js.BigInt]
       CBigInt(new BigInteger(v.toString(16), 16))
+    case sigma.UnsignedBigIntRType =>
+      val v = data.asInstanceOf[js.BigInt]
+      CUnsignedBigInt(new BigInteger(v.toString(16), 16))
     case sigma.GroupElementRType =>
       val ge = data.asInstanceOf[GroupElement]
       CGroupElement(ge.point)
@@ -121,6 +124,9 @@ object Value extends js.Object {
     case sigma.BigIntRType =>
       val hex = value.asInstanceOf[sigma.BigInt].toBigInteger.toString(10)
       js.BigInt(hex)
+    case sigma.UnsignedBigIntRType =>
+      val hex = value.asInstanceOf[sigma.BigInt].toBigInteger.toString(10)
+      js.BigInt(hex)
     case sigma.GroupElementRType =>
       val point = value.asInstanceOf[CGroupElement].wrappedValue.asInstanceOf[Platform.Ecp]
       new GroupElement(point)
diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala
index 9b0e5e8584..73e21b4564 100644
--- a/data/shared/src/main/scala/sigma/ast/methods.scala
+++ b/data/shared/src/main/scala/sigma/ast/methods.scala
@@ -540,7 +540,6 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods {
   // todo: costing
   final val ModInverseCostInfo = ToNBitsCostInfo
 
-  // todo: check ids before and after merging with other PRs introducing new methods for Numeric
   val ModInverseMethod = SMethod(this, "modInverse", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 14, ModInverseCostInfo.costKind)
     .withIRInfo(MethodCallIrBuilder)
     .withInfo(MethodCall, "")
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
index 8f461e502e..a161a034f5 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
@@ -120,6 +120,50 @@ object GraphIRReflection {
     )
   }
 
+  { val clazz = classOf[SigmaDsl#UnsignedBigInt]
+    val ctx = null.asInstanceOf[SigmaDsl] // ok! type level only
+    registerClassEntry(clazz,
+      methods = Map(
+        mkMethod(clazz, "add", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].add(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "max", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].max(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "min", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].min(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "subtract", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].subtract(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "multiply", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].multiply(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "mod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].mod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "divide", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].divide(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "plusMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].plusMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "subtractMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].subtractMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "multiplyMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].multiplyMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "mod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].mod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        },
+        mkMethod(clazz, "modInverse", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+          obj.asInstanceOf[ctx.UnsignedBigInt].modInverse(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+        }
+      )
+    )
+  }
+
   { val clazz = classOf[sigma.compiler.ir.wrappers.sigma.Colls#CollBuilder]
     val ctx = null.asInstanceOf[IRContext] // ok! type level only
     registerClassEntry(clazz,
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala
index d9dbea493a..f82132d9a0 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala
@@ -403,7 +403,6 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") {
     }
   }
 
-
   implicit object LiftableUnsignedBigInt extends Liftable[SUnsignedBigInt, UnsignedBigInt] {
     lazy val eW: Elem[UnsignedBigInt] = unsignedBigIntElement
     lazy val sourceType: RType[SUnsignedBigInt] = {
@@ -522,7 +521,7 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") {
     override protected def collectMethods: Map[RMethod, MethodDesc] = {
       super.collectMethods ++
         Elem.declaredMethods(RClass(classOf[UnsignedBigInt]), RClass(classOf[UnsignedBigInt]), Set(
-          "add", "subtract", "multiply", "divide", "mod", "min", "max"
+          "add", "subtract", "multiply", "divide", "mod", "min", "max", "plusMod", "subtractMod", "multiplyMod"
         ))
     }
   }

From 933b2cc9e3854dc9746a6cbcefca8812b75f9c28 Mon Sep 17 00:00:00 2001
From: Alexander Chepurnoy 
Date: Sat, 2 Nov 2024 02:07:22 +0300
Subject: [PATCH 78/78] improving UnsignedBigInt support in JS

---
 core/js/src/main/scala/sigma/js/Type.scala    |  3 ++
 .../src/main/scala/sigma/data/CBigInt.scala   |  5 +--
 .../shared/src/main/scala/sigma/package.scala |  2 +-
 .../sigma/reflection/ReflectionData.scala     | 35 ++++++++++---------
 data/js/src/main/scala/sigma/js/Value.scala   |  2 ++
 .../src/main/scala/sigma/ast/methods.scala    |  5 ++-
 .../scala/sigma/data/CSigmaDslBuilder.scala   |  9 +++--
 .../scala/sigma/data/DataValueComparer.scala  |  1 +
 .../scala/sigma/serialization/OpCodes.scala   |  1 +
 .../org/ergoplatform/dsl/ContractSyntax.scala |  1 +
 .../sigma/compiler/ir/GraphIRReflection.scala |  6 ++--
 .../ir/wrappers/sigma/impl/SigmaDslImpl.scala |  3 +-
 .../test/scala/sigma/SigmaDslTesting.scala    |  3 +-
 .../utxo/BasicOpsSpecification.scala          |  1 -
 .../org/ergoplatform/sdk/JavaHelpers.scala    | 10 ------
 .../org/ergoplatform/sdk/utils/Zero.scala     |  5 ++-
 16 files changed, 49 insertions(+), 43 deletions(-)

diff --git a/core/js/src/main/scala/sigma/js/Type.scala b/core/js/src/main/scala/sigma/js/Type.scala
index b323273a0c..069cca3e1e 100644
--- a/core/js/src/main/scala/sigma/js/Type.scala
+++ b/core/js/src/main/scala/sigma/js/Type.scala
@@ -35,6 +35,9 @@ object Type extends js.Object {
   /** Descriptor of ErgoScript type BigInt. */
   val BigInt = new Type(sigma.BigIntRType)
 
+  /** Descriptor of ErgoScript type BigInt. */
+  val UnsignedBigInt = new Type(sigma.UnsignedBigIntRType)
+
   /** Descriptor of ErgoScript type GroupElement. */
   val GroupElement = new Type(sigma.GroupElementRType)
 
diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala
index 8212955103..3787e78a86 100644
--- a/core/shared/src/main/scala/sigma/data/CBigInt.scala
+++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala
@@ -69,9 +69,9 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr
   }
 }
 
-/** A default implementation of [[BigInt]] interface.
+/** A default implementation of [[UnsignedBigInt]] interface.
   *
-  * @see [[BigInt]] for detailed descriptions
+  * @see [[UnsignedBigInt]] for detailed descriptions
   */
 case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends UnsignedBigInt with WrapperOf[BigInteger] {
 
@@ -144,4 +144,5 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign
   override def toSigned(): BigInt = {
     CBigInt(wrappedValue.toSignedBigIntValueExact)
   }
+
 }
diff --git a/core/shared/src/main/scala/sigma/package.scala b/core/shared/src/main/scala/sigma/package.scala
index 4473bd338f..41f90b33bb 100644
--- a/core/shared/src/main/scala/sigma/package.scala
+++ b/core/shared/src/main/scala/sigma/package.scala
@@ -26,7 +26,7 @@ package object sigma {
   implicit val StringType : RType[String]  = GeneralType(StringClassTag)
 
   implicit val BigIntRType      : RType[BigInt]       = GeneralType(BigIntClassTag)
-  implicit val UnsignedBigIntRType      : RType[UnsignedBigInt]       = GeneralType(UnsignedBigIntClassTag)
+  implicit val UnsignedBigIntRType : RType[UnsignedBigInt] = GeneralType(UnsignedBigIntClassTag)
   implicit val GroupElementRType: RType[GroupElement] = GeneralType(GroupElementClassTag)
   implicit val SigmaPropRType   : RType[SigmaProp]    = GeneralType(SigmaPropClassTag)
   implicit val SigmaBooleanRType: RType[SigmaBoolean] = GeneralType(SigmaBooleanClassTag)
diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala
index ef8e52e6cd..204792ee50 100644
--- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala
+++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala
@@ -130,44 +130,45 @@ object ReflectionData {
   }
   {
     val clazz      = classOf[sigma.UnsignedBigInt]
-    val paramTypes = Array[Class[_]](clazz)
+    val oneParamTypes = Array[Class[_]](clazz)
+    val twoParamTypes = Array[Class[_]](clazz, clazz)
     registerClassEntry(clazz,
       methods = Map(
-        mkMethod(clazz, "add", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "add", oneParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].add(args(0).asInstanceOf[UnsignedBigInt])
         },
-        mkMethod(clazz, "max", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "max", oneParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].max(args(0).asInstanceOf[UnsignedBigInt])
         },
-        mkMethod(clazz, "min", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "min", oneParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].min(args(0).asInstanceOf[UnsignedBigInt])
         },
-        mkMethod(clazz, "subtract", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "subtract", oneParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].subtract(args(0).asInstanceOf[UnsignedBigInt])
         },
-        mkMethod(clazz, "multiply", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "multiply", oneParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].multiply(args(0).asInstanceOf[UnsignedBigInt])
         },
-        mkMethod(clazz, "mod", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "mod", oneParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt])
         },
-        mkMethod(clazz, "divide", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "divide", oneParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].divide(args(0).asInstanceOf[UnsignedBigInt])
         },
-        mkMethod(clazz, "plusMod", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "mod", oneParamTypes) { (obj, args) =>
+          obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt])
+        },
+        mkMethod(clazz, "modInverse", oneParamTypes) { (obj, args) =>
+          obj.asInstanceOf[UnsignedBigInt].modInverse(args(0).asInstanceOf[UnsignedBigInt])
+        },
+        mkMethod(clazz, "plusMod", twoParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].plusMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt])
         },
-        mkMethod(clazz, "subtractMod", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "subtractMod", twoParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].subtractMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt])
         },
-        mkMethod(clazz, "multiplyMod", paramTypes) { (obj, args) =>
+        mkMethod(clazz, "multiplyMod", twoParamTypes) { (obj, args) =>
           obj.asInstanceOf[UnsignedBigInt].multiplyMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt])
-        },
-        mkMethod(clazz, "mod", paramTypes) { (obj, args) =>
-          obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt])
-        },
-        mkMethod(clazz, "modInverse", paramTypes) { (obj, args) =>
-          obj.asInstanceOf[UnsignedBigInt].modInverse(args(0).asInstanceOf[UnsignedBigInt])
         }
       )
     )
diff --git a/data/js/src/main/scala/sigma/js/Value.scala b/data/js/src/main/scala/sigma/js/Value.scala
index 77d6c8e104..1fedb30250 100644
--- a/data/js/src/main/scala/sigma/js/Value.scala
+++ b/data/js/src/main/scala/sigma/js/Value.scala
@@ -164,6 +164,8 @@ object Value extends js.Object {
       n
     case sigma.BigIntRType =>
       data.asInstanceOf[js.BigInt]
+    case sigma.UnsignedBigIntRType =>
+      data.asInstanceOf[js.BigInt]
     case sigma.GroupElementRType =>
       data.asInstanceOf[GroupElement]
     case sigma.SigmaPropRType =>
diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala
index 73e21b4564..b64abfde1f 100644
--- a/data/shared/src/main/scala/sigma/ast/methods.scala
+++ b/data/shared/src/main/scala/sigma/ast/methods.scala
@@ -3,7 +3,7 @@ package sigma.ast
 import org.ergoplatform._
 import org.ergoplatform.validation._
 import sigma.Evaluation.stypeToRType
-import sigma._
+import sigma.{UnsignedBigInt, _}
 import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray}
 import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf}
 import sigma.ast.SType.TypeCode
@@ -536,7 +536,6 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods {
   final val ToNBitsCostInfo = OperationCostInfo(
     FixedCost(JitCost(5)), NamedDesc("NBitsMethodCall"))
 
-
   // todo: costing
   final val ModInverseCostInfo = ToNBitsCostInfo
 
@@ -1008,7 +1007,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply {
          | \lst{f} to each element of this collection and concatenating the results.
         """.stripMargin, ArgInfo("f", "the function to apply to each element."))
 
-  /** We assume all flatMap body patterns have similar executon cost. */
+  /** We assume all flatMap body patterns have similar execution cost. */
   final val CheckFlatmapBody_Info = OperationCostInfo(
     PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(20), chunkSize = 1),
     NamedDesc("CheckFlatmapBody"))
diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala
index aebc95dd00..661cd183a1 100644
--- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala
+++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala
@@ -5,9 +5,8 @@ import org.ergoplatform.ErgoBox
 import org.ergoplatform.validation.ValidationRules
 import scorex.crypto.hash.{Blake2b256, Sha256}
 import scorex.utils.{Ints, Longs}
-import sigma.ast.{AtLeast, SBigInt, SubstConstants}
+import sigma.ast.{AtLeast, SBigInt, SType, SUnsignedBigInt, SubstConstants}
 import scorex.utils.Longs
-import sigma.ast.{AtLeast, SType, SubstConstants}
 import sigma.crypto.{CryptoConstants, EcPointType, Ecp}
 import sigma.eval.Extensions.EvalCollOps
 import sigma.serialization.{DataSerializer, GroupElementSerializer, SigmaSerializer}
@@ -235,7 +234,11 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl =>
           throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes")
         }
         CBigInt(new BigInteger(bytes.toArray).toSignedBigIntValueExact).asInstanceOf[T]
-      // todo: UnsignedBitInt
+      case sigma.UnsignedBigIntRType =>
+        if (bytes.length > SUnsignedBigInt.MaxSizeInBytes) {
+          throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes")
+        }
+        CUnsignedBigInt(new BigInteger(bytes.toArray).toSignedBigIntValueExact).asInstanceOf[T]
       case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes")
     }
   }
diff --git a/data/shared/src/main/scala/sigma/data/DataValueComparer.scala b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala
index e525b8f074..d44f3f8d68 100644
--- a/data/shared/src/main/scala/sigma/data/DataValueComparer.scala
+++ b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala
@@ -139,6 +139,7 @@ object DataValueComparer {
   val descriptors: AVHashMap[RType[_], (OperationCostInfo[FixedCost], OperationCostInfo[PerItemCost])] =
     AVHashMap.fromSeq(Array[(RType[_], (OperationCostInfo[FixedCost], OperationCostInfo[PerItemCost]))](
       (BigIntRType, (EQ_BigInt, EQ_COA_BigInt)),
+      (UnsignedBigIntRType, (EQ_BigInt, EQ_COA_BigInt)),
       (GroupElementRType, (EQ_GroupElement, EQ_COA_GroupElement)),
       (AvlTreeRType, (EQ_AvlTree, EQ_COA_AvlTree)),
       (BoxRType, (EQ_Box, EQ_COA_Box)),
diff --git a/data/shared/src/main/scala/sigma/serialization/OpCodes.scala b/data/shared/src/main/scala/sigma/serialization/OpCodes.scala
index 70050d00ba..c4647669fa 100644
--- a/data/shared/src/main/scala/sigma/serialization/OpCodes.scala
+++ b/data/shared/src/main/scala/sigma/serialization/OpCodes.scala
@@ -153,6 +153,7 @@ object OpCodes {
   val OptionIsDefinedCode: OpCode = newOpCode(118)
 
   // Modular arithmetic operations codes
+  // todo: remove?
   val ModQCode     : OpCode = newOpCode(119)
   val PlusModQCode : OpCode = newOpCode(120)
   val MinusModQCode: OpCode = newOpCode(121)
diff --git a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala
index 11cbaff739..2554489340 100644
--- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala
+++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala
@@ -55,6 +55,7 @@ trait ContractSyntax { contract: SigmaContract =>
       case _: String => StringType
       case _: Unit => UnitType
       case _: sigma.BigInt => BigIntRType
+      case _: sigma.BigInt => UnsignedBigIntRType
       case _: GroupElement => GroupElementRType
       case _: ErgoBox => syntax.ErgoBoxRType // TODO remove this RType
       case _: Box => BoxRType
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
index a161a034f5..78e1c09ffa 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
@@ -145,13 +145,13 @@ object GraphIRReflection {
         mkMethod(clazz, "divide", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
           obj.asInstanceOf[ctx.UnsignedBigInt].divide(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
         },
-        mkMethod(clazz, "plusMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+        mkMethod(clazz, "plusMod", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) =>
           obj.asInstanceOf[ctx.UnsignedBigInt].plusMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
         },
-        mkMethod(clazz, "subtractMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+        mkMethod(clazz, "subtractMod", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) =>
           obj.asInstanceOf[ctx.UnsignedBigInt].subtractMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
         },
-        mkMethod(clazz, "multiplyMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+        mkMethod(clazz, "multiplyMod", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) =>
           obj.asInstanceOf[ctx.UnsignedBigInt].multiplyMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
         },
         mkMethod(clazz, "mod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala
index f82132d9a0..e0376b4c91 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala
@@ -521,7 +521,8 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") {
     override protected def collectMethods: Map[RMethod, MethodDesc] = {
       super.collectMethods ++
         Elem.declaredMethods(RClass(classOf[UnsignedBigInt]), RClass(classOf[UnsignedBigInt]), Set(
-          "add", "subtract", "multiply", "divide", "mod", "min", "max", "plusMod", "subtractMod", "multiplyMod"
+          "add", "subtract", "multiply", "divide", "mod", "modInverse",
+          "min", "max", "plusMod", "subtractMod", "multiplyMod"
         ))
     }
   }
diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala
index de9e080862..7f3f28b791 100644
--- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala
+++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala
@@ -1376,6 +1376,7 @@ class SigmaDslTesting extends AnyPropSpec
         case IntType => arbInt
         case LongType => arbLong
         case BigIntRType => arbBigInt
+        case UnsignedBigIntRType => arbUnsignedBigInt
         case GroupElementRType => arbGroupElement
         case SigmaPropRType => arbSigmaProp
         case BoxRType => arbBox
@@ -1404,7 +1405,7 @@ class SigmaDslTesting extends AnyPropSpec
     */
   def updateArbitrary[A](t: RType[A], sampled: Sampled[A]) = {
     t match {
-      case BigIntRType | GroupElementRType | SigmaPropRType |
+      case BigIntRType | UnsignedBigIntRType | GroupElementRType | SigmaPropRType |
            BoxRType | PreHeaderRType | HeaderRType | AvlTreeRType |
            _: CollType[_] | _: PairType[_,_] | _: OptionType[_] =>
         val newArb = Arbitrary(Gen.oneOf(sampled.samples))
diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala
index 11d5e9b00a..60dac9c16a 100644
--- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala
@@ -154,7 +154,6 @@ class BasicOpsSpecification extends CompilerTestingCommons
     flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true
   }
 
-
   property("getVarFromInput") {
     def getVarTest(): Assertion = {
       val customExt = Map(
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala
index 14cdf7f6bb..a46e7490db 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala
@@ -315,16 +315,6 @@ object JavaHelpers {
 
   def collRType[T](tItem: RType[T]): RType[Coll[T]] = sigma.collRType(tItem)
 
-  def BigIntRType: RType[sigma.BigInt] = sigma.BigIntRType
-
-  def GroupElementRType: RType[sigma.GroupElement] = sigma.GroupElementRType
-
-  def SigmaPropRType: RType[sigma.SigmaProp] = sigma.SigmaPropRType
-
-  def AvlTreeRType: RType[sigma.AvlTree] = sigma.AvlTreeRType
-
-  def BoxRType: RType[sigma.Box] = sigma.BoxRType
-
   def SigmaDsl: CSigmaDslBuilder = sigma.eval.SigmaDsl
 
   def collFrom(arr: Array[Byte]): Coll[Byte] = {
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala
index 78dfe2dc94..a337ad44a8 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala
@@ -1,7 +1,7 @@
 package org.ergoplatform.sdk.utils
 
 import org.ergoplatform.ErgoBox
-import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, CBigInt, CGroupElement, CSigmaProp, CollType, FuncType, OptionType, PairType, RType, TrivialProp, TupleType}
+import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, CBigInt, CGroupElement, CSigmaProp, CUnsignedBigInt, CollType, FuncType, OptionType, PairType, RType, TrivialProp, TupleType}
 import sigma.data.RType._
 import scorex.crypto.authds.avltree.batch.BatchAVLProver
 import scorex.crypto.hash.{Blake2b256, Digest32}
@@ -11,6 +11,7 @@ import sigma._
 import sigma.ast.ErgoTree
 import ErgoTree.HeaderType
 import sigma.crypto.CryptoConstants
+
 import java.math.BigInteger
 import scala.language.implicitConversions
 
@@ -48,6 +49,7 @@ object Zero extends ZeroLowPriority {
   implicit val IntIsZero: Zero[Int] = CZero(0)
   implicit val LongIsZero: Zero[Long] = CZero(0L)
   implicit val BigIntIsZero: Zero[BigInt] = CZero(CBigInt(BigInteger.ZERO))
+  implicit val UnsignedBigIntIsZero: Zero[UnsignedBigInt] = CZero(CUnsignedBigInt(BigInteger.ZERO))
   implicit val GroupElementIsZero: Zero[GroupElement] = CZero(CGroupElement(CryptoConstants.dlogGroup.identity))
   implicit val AvlTreeIsZero: Zero[AvlTree] = CZero({
     val avlProver = new BatchAVLProver[Digest32, Blake2b256.type](keyLength = 32, None)
@@ -88,6 +90,7 @@ object Zero extends ZeroLowPriority {
     case LongType => Zero[Long]
     case UnitType => Zero[Unit]
     case BigIntRType => Zero[BigInt]
+    case UnsignedBigIntRType => Zero[UnsignedBigInt]
     case BoxRType => Zero[Box]
     case GroupElementRType => Zero[GroupElement]
     case AvlTreeRType => Zero[AvlTree]