From 3d47f2ac29e975de1754b46ddf210708d8eee088 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 24 May 2024 20:50:25 +0300 Subject: [PATCH 01/52] 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/52] 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 744d8a58bc7c86c84f2aa1c5694db935a3172260 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 3 Jun 2024 22:23:54 +0300 Subject: [PATCH 03/52] 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 04/52] 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 15f4f67ba83489dff1d2ea7df232b5bea15c01a4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 7 Jun 2024 21:50:00 +0300 Subject: [PATCH 05/52] 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 06/52] 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 07/52] 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 08/52] 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 ea468c9629d84d7260730208018257b76f0ccb35 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 11 Jun 2024 21:03:20 +0300 Subject: [PATCH 09/52] 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 10/52] 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 e71d7c2a2e06aec1a301c5de87419db63d7f9050 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 13 Jun 2024 21:00:07 +0300 Subject: [PATCH 11/52] 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 12/52] 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 13/52] 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 14/52] 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 15/52] 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 bcfb24df6ed5bd95753e9164faa7e17efbddf0e8 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 26 Aug 2024 00:36:20 +0300 Subject: [PATCH 16/52] 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 1c2b99df06c840955f9dc7b57164ec6ee11979a0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 17 Sep 2024 17:35:17 +0300 Subject: [PATCH 17/52] 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 18/52] 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 09ec5f11d21792c99adb903f485540eef43b652b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 18 Oct 2024 13:48:50 +0300 Subject: [PATCH 19/52] 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 a160997209a0d9df7564079317aad6a089cb8744 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 31 Oct 2024 15:44:18 +0300 Subject: [PATCH 20/52] 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 21/52] 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 22/52] 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 23/52] 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 24/52] 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 25/52] 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] From 3a5c6d6d8d9029a5e002862a6e60c41973186a68 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 4 Nov 2024 15:06:46 +0300 Subject: [PATCH 26/52] JS tests fixed --- .../sigma/reflection/ReflectionData.scala | 27 +++++++++++++------ .../scala/sigma/reflection/StaticImpl.scala | 4 ++- .../sigma/compiler/ir/GraphIRReflection.scala | 12 +++++++++ .../ir/wrappers/sigma/SigmaDslUnit.scala | 2 +- .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 6 ++--- .../scala/sigma/LanguageSpecificationV6.scala | 2 ++ 6 files changed, 40 insertions(+), 13 deletions(-) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index 204792ee50..34bffbca34 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -101,35 +101,43 @@ object ReflectionData { } { val clazz = classOf[sigma.BigInt] - val paramTypes = Array[Class[_]](clazz) + val noParamTypes = Array[Class[_]]() + val oneParamTypes = Array[Class[_]](clazz) registerClassEntry(clazz, methods = Map( - mkMethod(clazz, "add", paramTypes) { (obj, args) => + mkMethod(clazz, "add", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].add(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "max", paramTypes) { (obj, args) => + mkMethod(clazz, "max", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].max(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "min", paramTypes) { (obj, args) => + mkMethod(clazz, "min", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].min(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "subtract", paramTypes) { (obj, args) => + mkMethod(clazz, "subtract", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].subtract(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "multiply", paramTypes) { (obj, args) => + mkMethod(clazz, "multiply", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].multiply(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "mod", paramTypes) { (obj, args) => + mkMethod(clazz, "mod", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].mod(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "divide", paramTypes) { (obj, args) => + mkMethod(clazz, "divide", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].divide(args(0).asInstanceOf[BigInt]) + }, + mkMethod(clazz, "toUnsigned", noParamTypes) { (obj, _) => + obj.asInstanceOf[BigInt].toUnsigned + }, + mkMethod(clazz, "toUnsignedMod", Array[Class[_]](classOf[sigma.UnsignedBigInt])) { (obj, args) => + obj.asInstanceOf[BigInt].toUnsignedMod(args(0).asInstanceOf[UnsignedBigInt]) } ) ) } { val clazz = classOf[sigma.UnsignedBigInt] + val noParamTypes = Array[Class[_]]() val oneParamTypes = Array[Class[_]](clazz) val twoParamTypes = Array[Class[_]](clazz, clazz) registerClassEntry(clazz, @@ -169,6 +177,9 @@ object ReflectionData { }, mkMethod(clazz, "multiplyMod", twoParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].multiplyMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "toSigned", noParamTypes) { (obj, _) => + obj.asInstanceOf[UnsignedBigInt].toSigned() } ) ) diff --git a/core/shared/src/main/scala/sigma/reflection/StaticImpl.scala b/core/shared/src/main/scala/sigma/reflection/StaticImpl.scala index e95eea1f59..6db911d9bc 100644 --- a/core/shared/src/main/scala/sigma/reflection/StaticImpl.scala +++ b/core/shared/src/main/scala/sigma/reflection/StaticImpl.scala @@ -72,7 +72,9 @@ class SRClass[T](val clazz: Class[T], parameterTypes: Class[_]*): RMethod = { methods.get((name, parameterTypes)) match { case Some(m) => m - case _ => throw new NoSuchMethodException(s"${clazz.getName}.$name(${parameterTypes.map(_.getName).mkString(",")})") + case _ => + println(methods) + throw new NoSuchMethodException(s"${clazz.getName}.$name(${parameterTypes.map(_.getName).mkString(",")})") } } 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 78e1c09ffa..ea665c247f 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -115,6 +115,12 @@ object GraphIRReflection { }, mkMethod(clazz, "divide", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.BigInt].divide(args(0).asInstanceOf[ctx.Ref[ctx.BigInt]]) + }, + mkMethod(clazz, "toUnsigned", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[ctx.BigInt].toUnsigned() + }, + mkMethod(clazz, "toUnsignedMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.BigInt].toUnsignedMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]]) } ) ) @@ -159,6 +165,9 @@ object GraphIRReflection { }, mkMethod(clazz, "modInverse", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.UnsignedBigInt].modInverse(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]]) + }, + mkMethod(clazz, "toSigned", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[ctx.UnsignedBigInt].toSigned } ) ) @@ -410,6 +419,9 @@ object GraphIRReflection { mkMethod(clazz, "exp", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.GroupElement].exp(args(0).asInstanceOf[ctx.Ref[ctx.BigInt]]) }, + mkMethod(clazz, "expUnsigned", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.GroupElement].expUnsigned(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]]) + }, mkMethod(clazz, "multiply", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.GroupElement].multiply(args(0).asInstanceOf[ctx.Ref[ctx.GroupElement]]) }, 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 491a196f8d..f719bb6f8f 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 @@ -28,7 +28,7 @@ import scalan._ 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] + 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 e0376b4c91..576f4138a3 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 @@ -395,7 +395,7 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { true, false, element[UnsignedBigInt])) } - override def toSigned: Ref[BigInt] = { + override def toSigned(): Ref[BigInt] = { asRep[BigInt](mkMethodCall(self, UnsignedBigIntClass.getMethod("toSigned"), Array[AnyRef](), @@ -499,7 +499,7 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { true, true, element[UnsignedBigInt])) } - def toSigned: Ref[BigInt] = { + def toSigned(): Ref[BigInt] = { asRep[BigInt](mkMethodCall(source, UnsignedBigIntClass.getMethod("toSigned"), Array[AnyRef](), @@ -522,7 +522,7 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[UnsignedBigInt]), RClass(classOf[UnsignedBigInt]), Set( "add", "subtract", "multiply", "divide", "mod", "modInverse", - "min", "max", "plusMod", "subtractMod", "multiplyMod" + "min", "max", "plusMod", "subtractMod", "multiplyMod", "toSigned" )) } } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 15ce673332..9750c56b01 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1950,4 +1950,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + + } From b1d2b1799f57b8314841b690957c27961eb8e706 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 4 Nov 2024 16:03:44 +0300 Subject: [PATCH 27/52] toUnsigned test in LSV6 --- .../src/main/scala/sigma/data/CBigInt.scala | 2 +- .../scala/sigma/LanguageSpecificationV6.scala | 22 ++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 3787e78a86..0313c197e5 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -58,7 +58,7 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr def toUnsigned: UnsignedBigInt = { if(this.wrappedValue.compareTo(BigInteger.ZERO) < 0){ - throw new ArithmeticException("BigInteger argument for .toUnsigned is negative in"); + throw new ArithmeticException("BigInteger argument for .toUnsigned is negative"); } else { CUnsignedBigInt(this.wrappedValue) } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 9750c56b01..77e353ca25 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -11,14 +11,12 @@ import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast.{SInt, _} -import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CHeader, CSigmaProp, ExactNumeric, ProveDHTuple, RType} -import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, PairOfCols, RType} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CHeader, CSigmaDslBuilder, CSigmaProp, CUnsignedBigInt, ExactNumeric, PairOfCols, ProveDHTuple, RType} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, IntOps} import sigmastate.eval.{CContext, CPreHeader} import sigma.util.Extensions.{BooleanOps, IntOps} -import sigma.data.{RType} import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound @@ -1950,6 +1948,24 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + property("BigInt.toUnsigned") { + import sigma.data.OrderingOps.BigIntOrdering + val f = newFeature[BigInt, UnsignedBigInt]( + { (xs: BigInt) => xs.toUnsigned }, + """{(xs: BigInt) => xs.toUnsigned }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + CBigInt(new BigInteger("5")) -> Expected(ExpectedResult(Success(CUnsignedBigInt(new BigInteger("5"))), None)), + CBigInt(new BigInteger("-5")) -> Expected(ExpectedResult(Failure(new ArithmeticException("BigInteger argument for .toUnsigned is negative")), None)), + CBigInt(new BigInteger("0")) -> Expected(ExpectedResult(Success(CUnsignedBigInt(new BigInteger("0"))), None)) + // , CBigInt(BigInteger.valueOf(Long.MinValue)) -> Expected(ExpectedResult(), None)) + ), + f + ) + } } From f84bce55ab481fe8f01da6b09461291463bb5730 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 4 Nov 2024 19:47:21 +0300 Subject: [PATCH 28/52] toUnsignedMod test in LSV6, more tests in BOS --- .../src/main/scala/sigma/ast/methods.scala | 4 +- .../main/scala/sigma/data/BigIntegerOps.scala | 5 ++ .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 2 +- .../scala/sigma/LanguageSpecificationV6.scala | 49 +++++++++++++++++-- .../utxo/BasicOpsSpecification.scala | 38 ++++++++++++++ 5 files changed, 92 insertions(+), 6 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index b64abfde1f..710cb9a0aa 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -258,8 +258,6 @@ 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)) @@ -273,6 +271,8 @@ object SNumericTypeMethods extends MethodsContainer { case SIntMethods => IntIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Int]) case SLongMethods => LongIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Long]) case SBigIntMethods => obj.asInstanceOf[BigInt].toBytes + case SUnsignedBigIntMethods => obj.asInstanceOf[UnsignedBigInt].toBytes + // todo: test } }) .withInfo(PropertyCall, diff --git a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 8d272439f4..d849479a17 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -13,6 +13,11 @@ object OrderingOps { def compare(x: BigInt, y: BigInt) = x.compareTo(y) } implicit object BigIntOrdering extends BigIntOrdering + + trait UnsignedBigIntOrdering extends Ordering[UnsignedBigInt] { + def compare(x: UnsignedBigInt, y: UnsignedBigInt) = x.compareTo(y) + } + implicit object UnsignedBigIntOrdering extends UnsignedBigIntOrdering } object NumericOps { 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 576f4138a3..4b64b51ab4 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 @@ -195,7 +195,7 @@ object BigInt extends EntityObject("BigInt") { def toUnsignedMod(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { asRep[UnsignedBigInt](mkMethodCall(source, - BigIntClass.getMethod("UnsignedBigInt", classOf[Sym]), + BigIntClass.getMethod("toUnsignedMod", classOf[Sym]), Array[AnyRef](that), true, true, element[UnsignedBigInt](unsignedBigIntElement))) } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 77e353ca25..f096a36eba 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1952,8 +1952,17 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => import sigma.data.OrderingOps.BigIntOrdering val f = newFeature[BigInt, UnsignedBigInt]( - { (xs: BigInt) => xs.toUnsigned }, - """{(xs: BigInt) => xs.toUnsigned }""".stripMargin, + { (x: BigInt) => x.toUnsigned }, + """{(x: BigInt) => x.toUnsigned }""".stripMargin, + FuncValue( + Array((1, SBigInt)), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SBigInt), + SBigIntMethods.ToUnsigned, + IndexedSeq(), + Map() + ) + ), sinceVersion = VersionContext.V6SoftForkVersion ) @@ -1962,7 +1971,41 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => CBigInt(new BigInteger("5")) -> Expected(ExpectedResult(Success(CUnsignedBigInt(new BigInteger("5"))), None)), CBigInt(new BigInteger("-5")) -> Expected(ExpectedResult(Failure(new ArithmeticException("BigInteger argument for .toUnsigned is negative")), None)), CBigInt(new BigInteger("0")) -> Expected(ExpectedResult(Success(CUnsignedBigInt(new BigInteger("0"))), None)) - // , CBigInt(BigInteger.valueOf(Long.MinValue)) -> Expected(ExpectedResult(), None)) + ), + f + ) + } + + + property("BigInt.toUnsignedMod") { + import sigma.data.OrderingOps.BigIntOrdering + import sigma.data.OrderingOps.UnsignedBigIntOrdering + + val f = newFeature[(BigInt, UnsignedBigInt), UnsignedBigInt]( + { (xs: (BigInt, UnsignedBigInt)) => xs._1.toUnsignedMod(xs._2) }, + """{ (xs: (BigInt, UnsignedBigInt)) => xs._1.toUnsignedMod(xs._2) }""".stripMargin, + FuncValue( + Array((1, SPair(SBigInt, SUnsignedBigInt))), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SUnsignedBigInt)), 1.toByte), + SBigIntMethods.ToUnsignedMod, + Array( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SPair(SBigInt, SUnsignedBigInt)), + 2.toByte + ) + ), + Map() + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + (CBigInt(new BigInteger("50")), CUnsignedBigInt(new BigInteger("10"))) -> Expected(ExpectedResult(Success(CUnsignedBigInt(new BigInteger("0"))), None)), + (CBigInt(new BigInteger("50")), CUnsignedBigInt(new BigInteger("0"))) -> Expected(ExpectedResult(Failure(new ArithmeticException("BigInteger: modulus not positive")), None)), + (CBigInt(new BigInteger("50")), CUnsignedBigInt(new BigInteger("-10"))) -> Expected(ExpectedResult(Failure(new ArithmeticException("BigInteger: modulus not positive")), None)) ), f ) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 60dac9c16a..9c5087e26a 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -276,6 +276,24 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("unsigned bigint - attempt to create from negative value") { + def conversionTest() = {test("conversion", env, ext, + s"""{ + | val m = unsignedBigInt("-5") + | m >= 0 + | } """.stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + an[sigma.exceptions.InvalidArguments] should be thrownBy conversionTest() + } + } + + property("signed -> unsigned bigint conversion - negative bigint - mod") { def conversionTest() = {test("conversion", env, ext, s"""{ @@ -295,6 +313,26 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("signed -> unsigned bigint conversion - negative bigint - mod - 2") { + def conversionTest() = {test("conversion", env, ext, + s"""{ + | val t = (bigInt("-1"), bigInt("5")) + | val b = t._1 + | val m = t._2 + | val ub = b.toUnsignedMod(m.toUnsigned) + | ub >= 0 + | } """.stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + conversionTest() + } + } + property("unsigned bigint - add") { def conversionTest() = {test("add", env, ext, s"""{ From 1496770a5004839ffde950a88c4e4fc72199d474 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 5 Nov 2024 19:59:46 +0300 Subject: [PATCH 29/52] tests for .toBytes, UnsignedBigInt support added to to new Numeric methods --- .../src/main/scala/sigma/ast/methods.scala | 8 +++++ .../utxo/BasicOpsSpecification.scala | 36 ++++++++++++++++++- 2 files changed, 43 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 710cb9a0aa..5d21e91b16 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -13,6 +13,7 @@ import sigma.ast.syntax.{SValue, ValueOps} import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} import sigma.data.NumericOps.BigIntIsExactIntegral import sigma.data.OverloadHack.Overloaded1 +import sigma.data.UnsignedBigIntNumericOps.UnsignedBigIntIsExactIntegral import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} import sigma.reflection.RClass @@ -295,6 +296,7 @@ object SNumericTypeMethods extends MethodsContainer { case SIntMethods => IntIsExactIntegral.toBits(obj.asInstanceOf[Int]) case SLongMethods => LongIsExactIntegral.toBits(obj.asInstanceOf[Long]) case SBigIntMethods => BigIntIsExactIntegral.toBits(obj.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.toBits(obj.asInstanceOf[UnsignedBigInt]) } }) .withInfo(PropertyCall, @@ -315,6 +317,7 @@ object SNumericTypeMethods extends MethodsContainer { case SIntMethods => IntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Int]) case SLongMethods => LongIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Long]) case SBigIntMethods => BigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[UnsignedBigInt]) } }) .withInfo(PropertyCall, desc = "Returns bitwise inverse of this numeric. ") @@ -329,6 +332,7 @@ object SNumericTypeMethods extends MethodsContainer { case SIntMethods => IntIsExactIntegral.bitwiseOr(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) case SLongMethods => LongIsExactIntegral.bitwiseOr(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) case SBigIntMethods => BigIntIsExactIntegral.bitwiseOr(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.bitwiseOr(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[UnsignedBigInt]) } }) .withInfo(MethodCall, @@ -345,6 +349,7 @@ object SNumericTypeMethods extends MethodsContainer { case SIntMethods => IntIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) case SLongMethods => LongIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) case SBigIntMethods => BigIntIsExactIntegral.bitwiseAnd(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.bitwiseAnd(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[UnsignedBigInt]) } }) .withInfo(MethodCall, @@ -361,6 +366,7 @@ object SNumericTypeMethods extends MethodsContainer { case SIntMethods => IntIsExactIntegral.bitwiseXor(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) case SLongMethods => LongIsExactIntegral.bitwiseXor(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) case SBigIntMethods => BigIntIsExactIntegral.bitwiseXor(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.bitwiseXor(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[UnsignedBigInt]) } }) .withInfo(MethodCall, @@ -377,6 +383,7 @@ object SNumericTypeMethods extends MethodsContainer { case SIntMethods => IntIsExactIntegral.shiftLeft(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) case SLongMethods => LongIsExactIntegral.shiftLeft(obj.asInstanceOf[Long], other.head.asInstanceOf[Int]) case SBigIntMethods => BigIntIsExactIntegral.shiftLeft(obj.asInstanceOf[BigInt], other.head.asInstanceOf[Int]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.shiftLeft(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[Int]) } }) .withInfo(MethodCall, @@ -396,6 +403,7 @@ object SNumericTypeMethods extends MethodsContainer { case SIntMethods => IntIsExactIntegral.shiftRight(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) case SLongMethods => LongIsExactIntegral.shiftRight(obj.asInstanceOf[Long], other.head.asInstanceOf[Int]) case SBigIntMethods => BigIntIsExactIntegral.shiftRight(obj.asInstanceOf[BigInt], other.head.asInstanceOf[Int]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.shiftRight(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[Int]) } }) .withInfo(MethodCall, diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 9c5087e26a..dc95969b08 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -28,7 +28,7 @@ import sigmastate.interpreter.Interpreter._ import sigma.ast.Apply import sigma.eval.EvalSettings import sigma.exceptions.InvalidType -import sigma.serialization.ErgoTreeSerializer +import sigma.serialization.{ErgoTreeSerializer, SerializerException} import sigma.interpreter.{ContextExtension, ProverResult} import sigmastate.utils.Helpers import sigmastate.utils.Helpers._ @@ -793,6 +793,24 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + + property("UnsignedBigInt.toBits") { + def toBitsTest() = test("UnsignedBigInt.toBits", env, ext, + s"""{ + | val b = bigInt("${CryptoConstants.groupOrder}") + | val ba = b.toBits + | ba.size == 256 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[SerializerException] shouldBe thrownBy(toBitsTest()) + } + } + property("BigInt.bitwiseInverse") { def bitwiseInverseTest(): Assertion = test("BigInt.bitwiseInverse", env, ext, s"""{ @@ -1497,6 +1515,22 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.toBytes") { + def toBytesTest() = test("UnsignedBigInt.toBytes", env, ext, + s"""{ + | val l = bigInt("${CryptoConstants.groupOrder}") + | l.toBytes.size == 32 + | }""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[SerializerException] shouldBe thrownBy(toBytesTest()) + } + } + property("serialize - byte array") { def deserTest() = test("serialize", env, ext, s"""{ From ac4bbbce2a1f8696356bbc1cce79129523b51b5d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 6 Nov 2024 00:41:28 +0300 Subject: [PATCH 30/52] unsigned encoding, .toBytes & .toBits tests passing --- .../main/scala/sigma/crypto/BigIntegers.scala | 16 +++--- .../src/main/scala/sigma/data/CBigInt.scala | 3 +- .../serialization/CoreDataSerializer.scala | 9 ++-- .../scala/sigma/data/CSigmaDslBuilder.scala | 4 +- .../sigma/data/UnsignedBigIntegerOps.scala | 2 +- .../sigma/compiler/ir/GraphBuilding.scala | 51 ++++++++++--------- .../utxo/BasicOpsSpecification.scala | 20 ++++---- 7 files changed, 57 insertions(+), 48 deletions(-) rename {data => core}/shared/src/main/scala/sigma/crypto/BigIntegers.scala (90%) diff --git a/data/shared/src/main/scala/sigma/crypto/BigIntegers.scala b/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala similarity index 90% rename from data/shared/src/main/scala/sigma/crypto/BigIntegers.scala rename to core/shared/src/main/scala/sigma/crypto/BigIntegers.scala index 4465184580..54a6927924 100644 --- a/data/shared/src/main/scala/sigma/crypto/BigIntegers.scala +++ b/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala @@ -13,8 +13,9 @@ object BigIntegers { private val MAX_ITERATIONS = 1000 /** Create the given number of random bits. + * * @param bitLength the number of random bits to create. - * @param random a source of randomness. + * @param random a source of randomness. * @return a byte array containing random bits. */ @throws[IllegalArgumentException] @@ -38,8 +39,8 @@ object BigIntegers { * @return a positive BigInteger */ def createRandomBigInteger( - bitLength: Int, - random: SecureRandom): BigInteger = { + bitLength: Int, + random: SecureRandom): BigInteger = { new BigInteger(1, createRandom(bitLength, random)) } @@ -52,9 +53,9 @@ object BigIntegers { * @return a random BigInteger value in the range [min,max] */ def createRandomInRange( - min: BigInteger, - max: BigInteger, - random: SecureRandom): BigInteger = { + min: BigInteger, + max: BigInteger, + random: SecureRandom): BigInteger = { val cmp = min.compareTo(max) if (cmp >= 0) { if (cmp > 0) throw new IllegalArgumentException("'min' may not be greater than 'max'") @@ -64,7 +65,7 @@ object BigIntegers { if (min.bitLength > max.bitLength / 2) return createRandomInRange(ZERO, max.subtract(min), random).add(min) - for ( _ <- 0 until MAX_ITERATIONS ) { + for (_ <- 0 until MAX_ITERATIONS) { val x = createRandomBigInteger(max.bitLength, random) if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0) return x } @@ -96,6 +97,7 @@ object BigIntegers { /** Converts a byte array to a BigInteger, treating the array as bits of the unsigned * integer. + * * @param buf the byte array to convert * @return the resulting positive BigInteger */ diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 0313c197e5..9292ada68a 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -1,5 +1,6 @@ package sigma.data +import sigma.crypto.BigIntegers import sigma.util.Extensions.BigIntegerOps import sigma.{BigInt, Coll, Colls, UnsignedBigInt} @@ -83,7 +84,7 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign override def toLong: Long = wrappedValue.toLongExact - override def toBytes: Coll[Byte] = Colls.fromArray(wrappedValue.toByteArray) + override def toBytes: Coll[Byte] = Colls.fromArray(BigIntegers.asUnsignedByteArray(wrappedValue)) override def compareTo(that: UnsignedBigInt): Int = wrappedValue.compareTo(that.asInstanceOf[CUnsignedBigInt].wrappedValue) diff --git a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala index 5aa7d7600a..0d705412fc 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala @@ -2,8 +2,9 @@ package sigma.serialization import debox.cfor import sigma.ast._ +import sigma.crypto.BigIntegers import sigma.data._ -import sigma.util.Extensions.{CoreAvlTreeOps, BigIntOps, GroupElementOps, SigmaPropOps} +import sigma.util.Extensions.{BigIntOps, CoreAvlTreeOps, GroupElementOps, SigmaPropOps} import sigma.validation.ValidationRules.CheckSerializableTypeCode import sigma.{Evaluation, _} @@ -34,7 +35,7 @@ class CoreDataSerializer { w.putUShort(data.length) w.putBytes(data) case SUnsignedBigInt => // todo: versioning - val data = v.asInstanceOf[CUnsignedBigInt].wrappedValue.toByteArray + val data = BigIntegers.asUnsignedByteArray(v.asInstanceOf[CUnsignedBigInt].wrappedValue) w.putUShort(data.length) w.putBytes(data) case SGroupElement => @@ -114,11 +115,11 @@ class CoreDataSerializer { 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 + if (size > SBigInt.MaxSizeInBytes) { throw new SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size") } val valueBytes = r.getBytes(size) - CUnsignedBigInt(new BigInteger(valueBytes)) + CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(valueBytes)) case SGroupElement => CGroupElement(GroupElementSerializer.parse(r)) case SSigmaProp => diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 661cd183a1..89fcffb531 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -7,7 +7,7 @@ import scorex.crypto.hash.{Blake2b256, Sha256} import scorex.utils.{Ints, Longs} import sigma.ast.{AtLeast, SBigInt, SType, SUnsignedBigInt, SubstConstants} import scorex.utils.Longs -import sigma.crypto.{CryptoConstants, EcPointType, Ecp} +import sigma.crypto.{BigIntegers, CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps import sigma.serialization.{DataSerializer, GroupElementSerializer, SigmaSerializer} import sigma.serialization.{GroupElementSerializer, SerializerException, SigmaSerializer} @@ -238,7 +238,7 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => 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] + CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(bytes.toArray)).asInstanceOf[T] case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") } } diff --git a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala index abbee376a5..e66740b1ea 100644 --- a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -95,7 +95,7 @@ object UnsignedBigIntNumericOps { * For example, the `Int` value `0x12131415` would yield the * collection of bytes [0x12, 0x13, 0x14, 0x15] */ - override def toBigEndianBytes(x: UnsignedBigInt): Coll[Byte] = ??? + override def toBigEndianBytes(x: UnsignedBigInt): Coll[Byte] = x.toBytes /** * @return a numeric value which is inverse of `x` (every bit, including sign, is flipped) 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 6048d884d1..caeded999c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1032,28 +1032,6 @@ 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.ModMethod.name => - val m = asRep[UnsignedBigInt](argsV(0)) - ubi.mod(m) - 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 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 => ge.getEncoded @@ -1226,7 +1204,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => g.fromBigEndianBytes(bytes)(cT) case _ => throwError() } - case (x: Ref[tNum], _: SNumericTypeMethods) => method.name match { + case (x: Ref[tNum], ms: SNumericTypeMethods) => method.name match { case SNumericTypeMethods.ToBytesMethod.name => val op = NumericToBigEndianBytes(elemToExactNumeric(x.elem)) ApplyUnOp(op, x) @@ -1263,6 +1241,33 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val bi = asRep[BigInt](x) val m = asRep[UnsignedBigInt](argsV(0)) bi.toUnsignedMod(m) + + case SUnsignedBigIntMethods.ModMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val m = asRep[UnsignedBigInt](argsV(0)) + ubi.mod(m) + case SUnsignedBigIntMethods.ModInverseMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val m = asRep[UnsignedBigInt](argsV(0)) + ubi.modInverse(m) + case SUnsignedBigIntMethods.PlusModMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val that = asRep[UnsignedBigInt](argsV(0)) + val m = asRep[UnsignedBigInt](argsV(1)) + ubi.plusMod(that, m) + case SUnsignedBigIntMethods.SubtractModMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val that = asRep[UnsignedBigInt](argsV(0)) + val m = asRep[UnsignedBigInt](argsV(1)) + ubi.subtractMod(that, m) + case SUnsignedBigIntMethods.MultiplyModMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val that = asRep[UnsignedBigInt](argsV(0)) + val m = asRep[UnsignedBigInt](argsV(1)) + ubi.multiplyMod(that, m) + case SUnsignedBigIntMethods.ToSignedMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + ubi.toSigned() case _ => throwError() } case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index dc95969b08..9a7afd40a4 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -30,6 +30,7 @@ import sigma.eval.EvalSettings import sigma.exceptions.InvalidType import sigma.serialization.{ErgoTreeSerializer, SerializerException} import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.validation.ValidationException import sigmastate.utils.Helpers import sigmastate.utils.Helpers._ @@ -797,7 +798,7 @@ class BasicOpsSpecification extends CompilerTestingCommons property("UnsignedBigInt.toBits") { def toBitsTest() = test("UnsignedBigInt.toBits", env, ext, s"""{ - | val b = bigInt("${CryptoConstants.groupOrder}") + | val b = unsignedBigInt("${CryptoConstants.groupOrder}") | val ba = b.toBits | ba.size == 256 |}""".stripMargin, @@ -807,7 +808,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBitsTest() } else { - an[SerializerException] shouldBe thrownBy(toBitsTest()) + an[ValidationException] shouldBe thrownBy(toBitsTest()) } } @@ -1516,18 +1517,17 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("UnsignedBigInt.toBytes") { - def toBytesTest() = test("UnsignedBigInt.toBytes", env, ext, - s"""{ - | val l = bigInt("${CryptoConstants.groupOrder}") - | l.toBytes.size == 32 - | }""".stripMargin, - null - ) + val script = s"""{ + | val l = unsignedBigInt("${CryptoConstants.groupOrder}") + | l.toBytes.size == 32 + | }""".stripMargin + + def toBytesTest() = test("UnsignedBigInt.toBytes", env, ext, script, null) if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[SerializerException] shouldBe thrownBy(toBytesTest()) + an[ValidationException] shouldBe thrownBy(toBytesTest()) } } From a87bfb307651b35d927e186d097512ddf8c675b3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 6 Nov 2024 01:06:09 +0300 Subject: [PATCH 31/52] bitwiseInverse --- .../sigma/data/UnsignedBigIntegerOps.scala | 9 +++++++-- .../sigmastate/utxo/BasicOpsSpecification.scala | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala index e66740b1ea..e0170b14df 100644 --- a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -1,6 +1,7 @@ package sigma.data import sigma._ +import sigma.crypto.BigIntegers import sigma.data.UnsignedBigIntOrderingOps.UnsignedBigIntOrdering import sigma.eval.Extensions.IntExt @@ -98,9 +99,13 @@ object UnsignedBigIntNumericOps { override def toBigEndianBytes(x: UnsignedBigInt): Coll[Byte] = x.toBytes /** - * @return a numeric value which is inverse of `x` (every bit, including sign, is flipped) + * @return a numeric value which is inverse of `x` (every bit is flipped) */ - override def bitwiseInverse(x: UnsignedBigInt): UnsignedBigInt = ??? + override def bitwiseInverse(x: UnsignedBigInt): UnsignedBigInt = { + val bytes = BigIntegers.asUnsignedByteArray(x.asInstanceOf[CUnsignedBigInt].wrappedValue) + val res: Array[Byte] = bytes.map(b => (b ^ Byte.MinValue).toByte) + CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(res)) + } /** * @return a numeric value which is `this | that` diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 9a7afd40a4..6c95071a0b 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -829,6 +829,23 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.bitwiseInverse") { + def bitwiseInverseTest(): Assertion = test("UnsignedBigInt.bitwiseInverse", env, ext, + s"""{ + | val b = unsignedBigInt("${CryptoConstants.groupOrder}") + | val bi = b.bitwiseInverse + | bi.bitwiseInverse == b + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseInverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) + } + } + property("Byte.bitwiseInverse") { def bitwiseInverseTest(): Assertion = test("Byte.bitwiseInverse", env, ext, From 77be8a6710f1a8214acbba85ca24414e141c719d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 11 Nov 2024 17:30:41 +0300 Subject: [PATCH 32/52] more .toBytes and .toBits tests --- .../utxo/BasicOpsSpecification.scala | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 6c95071a0b..210fcb690d 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -812,6 +812,24 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.toBits - 2") { + def toBitsTest() = test("UnsignedBigInt.toBits", env, ext, + s"""{ + | val b = unsignedBigInt("5") + | val ba = b.toBits + | ba.size == 8 && ba == Coll(false, false, false, false, false, true, false, true) + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[ValidationException] shouldBe thrownBy(toBitsTest()) + } + } + + property("BigInt.bitwiseInverse") { def bitwiseInverseTest(): Assertion = test("BigInt.bitwiseInverse", env, ext, s"""{ @@ -1548,6 +1566,22 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.toBytes - 2") { + val script = s"""{ + | val l = unsignedBigInt("5") + | val bs = l.toBytes + | bs.size == 1 && bs == Coll(5.toByte) + | }""".stripMargin + + def toBytesTest() = test("UnsignedBigInt.toBytes", env, ext, script, null) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[ValidationException] shouldBe thrownBy(toBytesTest()) + } + } + property("serialize - byte array") { def deserTest() = test("serialize", env, ext, s"""{ From 626edc89f58d99e4a54f72c97c7374e6ee845edc Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 11 Nov 2024 20:14:39 +0300 Subject: [PATCH 33/52] bitwise or/and/xor and tests --- .../sigma/data/UnsignedBigIntegerOps.scala | 20 +++- .../utxo/BasicOpsSpecification.scala | 91 +++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala index e0170b14df..dd66ca1b4f 100644 --- a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -1,5 +1,7 @@ package sigma.data +import debox.cfor +import scorex.util.encode.Base16 import sigma._ import sigma.crypto.BigIntegers import sigma.data.UnsignedBigIntOrderingOps.UnsignedBigIntOrdering @@ -110,17 +112,29 @@ object UnsignedBigIntNumericOps { /** * @return a numeric value which is `this | that` */ - override def bitwiseOr(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = ??? + override def bitwiseOr(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = { + val vx = x.asInstanceOf[CUnsignedBigInt].wrappedValue + val vy = y.asInstanceOf[CUnsignedBigInt].wrappedValue + CUnsignedBigInt(vx.or(vy)) + } /** * @return a numeric value which is `this && that` */ - override def bitwiseAnd(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = ??? + override def bitwiseAnd(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = { + val vx = x.asInstanceOf[CUnsignedBigInt].wrappedValue + val vy = y.asInstanceOf[CUnsignedBigInt].wrappedValue + CUnsignedBigInt(vx.and(vy)) + } /** * @return a numeric value which is `this xor that` */ - override def bitwiseXor(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = ??? + override def bitwiseXor(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = { + val vx = x.asInstanceOf[CUnsignedBigInt].wrappedValue + val vy = y.asInstanceOf[CUnsignedBigInt].wrappedValue + CUnsignedBigInt(vx.xor(vy)) + } /** * @return a value which is (this << n). The shift distance, n, may be negative, diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 210fcb690d..0ad8ac592a 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -938,6 +938,40 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.bitwiseOr") { + def bitwiseOrTest(): Assertion = test("BigInt.bitwiseOr", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | x.bitwiseOr(x) == x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseOrTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) + } + } + + property("UnsignedBigInt.bitwiseOr - 2") { + def bitwiseOrTest(): Assertion = test("BigInt.bitwiseOr", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = unsignedBigInt("121") + | val z = unsignedBigInt("115792089237316195423570985008687907852837564279074904382605163141518161494393") + | x.bitwiseOr(y) == z && y.bitwiseOr(x) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseOrTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) + } + } + property("BigInt.bitwiseAnd") { def bitwiseAndTest(): Assertion = test("BigInt.bitwiseAnd", env, ext, s"""{ @@ -955,6 +989,43 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.bitwiseAnd") { + def bitwiseAndTest(): Assertion = test("UnsignedBigInt.bitwiseAnd", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = 0.toBigInt.toUnsigned + | x.bitwiseAnd(y) == y + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) + } + } + + property("UnsignedBigInt.bitwiseAnd - 2") { + def bitwiseAndTest(): Assertion = test("UnsignedBigInt.bitwiseAnd", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = unsignedBigInt("1157920892373161954235709850086879078528375642790749043826051631415181614337") + | val z = unsignedBigInt("1157920892373161954235709850086879078522970439492889181512311797126516834561") + | + | // cross-checked with wolfram alpha + | x.bitwiseAnd(y) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) + } + } + property("Short.bitwiseAnd") { val customExt = Map( 1.toByte -> ShortConstant(32767) @@ -995,6 +1066,26 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.bitwiseXor") { + def bitwiseAndTest(): Assertion = test("UnsignedBigInt.bitwiseXor", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = unsignedBigInt("1157920892373161954235709850086879078528375642790749043826051631415181614337") + | val z = unsignedBigInt("114634168344943033469335275158601028774319999042879875063406591178680309439552") + | + | // cross-checked with wolfram alpha + | x.bitwiseXor(y) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) + } + } + property("Byte.shiftLeft") { def shiftLeftTest(): Assertion = test("Byte.shiftLeft", env, ext, s"""{ From aa246c3e6d441c6d72e7877052e0167c854166ac Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 11 Nov 2024 22:46:48 +0300 Subject: [PATCH 34/52] shiftLeft/shiftRight and tests --- .../sigma/data/UnsignedBigIntegerOps.scala | 16 +++++++-- .../utxo/BasicOpsSpecification.scala | 35 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala index dd66ca1b4f..6aebb485f8 100644 --- a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -140,13 +140,25 @@ object UnsignedBigIntNumericOps { * @return a value which is (this << n). The shift distance, n, may be negative, * in which case this method performs a right shift. (Computes floor(this * 2n).) */ - override def shiftLeft(x: UnsignedBigInt, bits: Int): UnsignedBigInt = ??? + override def shiftLeft(x: UnsignedBigInt, bits: Int): UnsignedBigInt = { + if (bits < 0 || bits >= 256) { + throw new IllegalArgumentException(s"Wrong argument in UnsignedBigInt.shiftLeft: bits < 0 || bits >= 256 ($bits)") + } else { + x.shiftLeft(bits) + } + } /** * @return a value which is (this >> n). Sign extension is performed. The shift distance, n, * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).) */ - override def shiftRight(x: UnsignedBigInt, bits: Int): UnsignedBigInt = ??? + override def shiftRight(x: UnsignedBigInt, bits: Int): UnsignedBigInt = { + if (bits < 0 || bits >= 256) { + throw new IllegalArgumentException(s"Wrong argument in UnsignedBigInt.shiftLeft: bits < 0 || bits >= 256 ($bits)") + } else { + x.shiftRight(bits) + } + } } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 0ad8ac592a..ae34c5bbfc 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -1154,6 +1154,23 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.shiftLeft") { + def shiftLeftTest(): Assertion = test("UnsignedBigInt.shiftLeft", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | val y = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | x.shiftLeft(2) == y + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftLeftTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + property("BigInt.shiftLeft over limits") { def shiftLeftTest(): Assertion = test("BigInt.shiftLeft", env, ext, s"""{ @@ -1292,6 +1309,24 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.shiftRight") { + def shiftRightTest(): Assertion = test("UnsignedBigInt.shiftRight", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = 3 + | val z = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | x.shiftRight(y) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + property("getVarFromInput - invalid var") { def getVarTest(): Assertion = { val customExt = Map( From 7fdcdbd8c80ac6a386dd93dea6b9928b4b1db7a1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 11 Nov 2024 23:02:46 +0300 Subject: [PATCH 35/52] more tests for shiftLeft/shiftRight --- .../utxo/BasicOpsSpecification.scala | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index ae34c5bbfc..f7a9fab2ba 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -1171,6 +1171,39 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.shiftLeft over limits") { + def shiftLeftTest(): Assertion = test("UnsignedBigInt.shiftLeft", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | x.shiftLeft(1) > x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[ArithmeticException] shouldBe thrownBy(shiftLeftTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + + + property("UnsignedBigInt.shiftLeft - neg shift") { + def shiftLeftTest(): Assertion = test("UnsignedBigInt.shiftLeft", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | x.shiftLeft(-1) > x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[java.lang.IllegalArgumentException] shouldBe thrownBy(shiftLeftTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + property("BigInt.shiftLeft over limits") { def shiftLeftTest(): Assertion = test("BigInt.shiftLeft", env, ext, s"""{ @@ -1327,6 +1360,24 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.shiftRight - neg shift") { + def shiftRightTest(): Assertion = test("UnsignedBigInt.shiftRight", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val y = -2 + | val z = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | z.shiftRight(y) == x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[java.lang.IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + property("getVarFromInput - invalid var") { def getVarTest(): Assertion = { val customExt = Map( From 95661777a14ce07e1addec25141d51ad3c116879 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 13 Nov 2024 14:15:31 +0300 Subject: [PATCH 36/52] LSV6 tests for UnsignedBigInt --- .../src/main/scala/sigma/SigmaDsl.scala | 2 + .../src/main/scala/sigma/ast/SType.scala | 14 +- .../src/main/scala/sigma/data/CBigInt.scala | 16 + .../src/test/scala/sigma/VersionTesting.scala | 5 +- .../main/scala/sigma/ast/SigmaPredef.scala | 2 +- .../src/main/scala/sigma/ast/methods.scala | 4 +- .../sigma/data/UnsignedBigIntegerOps.scala | 10 +- .../sigma/compiler/ir/GraphBuilding.scala | 2 +- .../scala/sigma/LanguageSpecificationV6.scala | 421 +++++++++++++++++- .../sigmastate/ErgoTreeSpecification.scala | 2 +- 10 files changed, 456 insertions(+), 22 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 92250ac615..099f27ae8a 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -321,6 +321,8 @@ trait UnsignedBigInt { */ def shiftRight(n: Int): UnsignedBigInt + def bitwiseInverse(): 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 17a5ef10d7..d255adca6b 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -549,10 +549,15 @@ 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: BigInt => x.asInstanceOf[CBigInt].wrappedValue case x: UnsignedBigInt => x.asInstanceOf[CUnsignedBigInt].wrappedValue case _ => sys.error(s"Cannot upcast value $v to the type $this") } - CUnsignedBigInt(bi) + if(bi.compareTo(BigInteger.ZERO) >= 0) { + CUnsignedBigInt(bi) + } else { + sys.error(s"Cannot upcast negative value $v to the type $this") + } } override def downcast(v: AnyVal): UnsignedBigInt = { val bi = v match { @@ -560,10 +565,15 @@ 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: BigInt => x.asInstanceOf[CBigInt].wrappedValue case x: UnsignedBigInt => x.asInstanceOf[CUnsignedBigInt].wrappedValue case _ => sys.error(s"Cannot downcast value $v to the type $this") } - CUnsignedBigInt(bi) + if(bi.compareTo(BigInteger.ZERO) >= 0) { + CUnsignedBigInt(bi) + } else { + sys.error(s"Cannot upcast negative value $v to the type $this") + } } } diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 9292ada68a..b8b3ad7a17 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -76,6 +76,11 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr */ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends UnsignedBigInt with WrapperOf[BigInteger] { + if (wrappedValue.compareTo(BigInteger.ZERO) < 0) { + throw new IllegalArgumentException(s"Attempt to create unsigned value from negative big integer $wrappedValue") + } + + override def toByte: Byte = wrappedValue.toByteExact override def toShort: Short = wrappedValue.toShortExact @@ -142,6 +147,17 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign override def shiftRight(n: Int): UnsignedBigInt = CUnsignedBigInt(wrappedValue.shiftRight(n).toUnsignedBigIntValueExact) + override def bitwiseInverse(): UnsignedBigInt = { + val bytes = if(wrappedValue.compareTo(BigInteger.ZERO) == 0) { + Array(0: Byte) + } else { + BigIntegers.asUnsignedByteArray(wrappedValue) + } + + val res: Array[Byte] = bytes.map(b => (~b & 0xff).toByte) + CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(res)) + } + override def toSigned(): BigInt = { CBigInt(wrappedValue.toSignedBigIntValueExact) } diff --git a/core/shared/src/test/scala/sigma/VersionTesting.scala b/core/shared/src/test/scala/sigma/VersionTesting.scala index 08053a6c48..a73452a838 100644 --- a/core/shared/src/test/scala/sigma/VersionTesting.scala +++ b/core/shared/src/test/scala/sigma/VersionTesting.scala @@ -72,8 +72,9 @@ trait VersionTesting { protected def testFun_Run(testName: String, testFun: => Any): Unit = { def msg = s"""property("$testName")(ActivatedVersion = $activatedVersionInTests; ErgoTree version = $ergoTreeVersionInTests)""" if (printVersions) println(msg) - try testFun - catch { + try { + testFun + } catch { case t: Throwable => if (!printVersions) { // wasn't printed, print it now diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index e976babbf2..28c51a706b 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -455,7 +455,7 @@ object SigmaPredef { val resType = u.opType.tRange.asInstanceOf[SFunc].tRange MethodCall( Global, - SGlobalMethods.fromBigEndianBytesMethod.withConcreteTypes(Map(tT -> resType)), + SGlobalMethods.FromBigEndianBytesMethod.withConcreteTypes(Map(tT -> resType)), args.toIndexedSeq, Map(tT -> resType) ) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 5d21e91b16..ce8e71eef7 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1989,7 +1989,7 @@ case object SGlobalMethods extends MonoTypeMethods { private val BigEndianBytesCostKind = FixedCost(JitCost(10)) // id = 4 is reserved for deserializeTo () - lazy val fromBigEndianBytesMethod = SMethod( + lazy val FromBigEndianBytesMethod = SMethod( this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 5, BigEndianBytesCostKind, Seq(tT)) .withIRInfo(MethodCallIrBuilder, javaMethodOf[SigmaDslBuilder, Coll[Byte], RType[_]]("fromBigEndianBytes"), @@ -2033,7 +2033,7 @@ case object SGlobalMethods extends MonoTypeMethods { groupGeneratorMethod, xorMethod, serializeMethod, - fromBigEndianBytesMethod + FromBigEndianBytesMethod ) } else { Seq( diff --git a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala index 6aebb485f8..bf3dd3e33b 100644 --- a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -50,8 +50,8 @@ object UnsignedBigIntNumericOps { 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 = ??? + def toFloat(x: UnsignedBigInt): Float = x.toFloat + def toDouble(x: UnsignedBigInt): Double = x.toDouble } /** The instance of Integral for BigInt. @@ -103,11 +103,7 @@ object UnsignedBigIntNumericOps { /** * @return a numeric value which is inverse of `x` (every bit is flipped) */ - override def bitwiseInverse(x: UnsignedBigInt): UnsignedBigInt = { - val bytes = BigIntegers.asUnsignedByteArray(x.asInstanceOf[CUnsignedBigInt].wrappedValue) - val res: Array[Byte] = bytes.map(b => (b ^ Byte.MinValue).toByte) - CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(res)) - } + override def bitwiseInverse(x: UnsignedBigInt): UnsignedBigInt = x.bitwiseInverse() /** * @return a numeric value which is `this | that` 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 caeded999c..18ce41497d 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1198,7 +1198,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SGlobalMethods.serializeMethod.name => val value = asRep[Any](argsV(0)) g.serialize(value) - case SGlobalMethods.fromBigEndianBytesMethod.name => + case SGlobalMethods.FromBigEndianBytesMethod.name => val bytes = asRep[Coll[Byte]](argsV(0)) val cT = stypeToElem(method.stype.tRange.withSubstTypes(typeSubst)) g.fromBigEndianBytes(bytes)(cT) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index f096a36eba..2c6aaaf74b 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -108,7 +108,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => // TODO v6.0: implement serialization roundtrip tests after merge with deserializeTo - property("Boolean.toByte") { val toByte = newFeature((x: Boolean) => x.toByte, "{ (x: Boolean) => x.toByte }", sinceVersion = V6SoftForkVersion @@ -1659,6 +1658,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => newFeature( { (x: Context) => x.getVar[Boolean](11)}, "{ (x: Context) => CONTEXT.getVar[Boolean](11.toByte) }", + FuncValue(Array((1, SContext)), GetVar(11.toByte, SOption(SBoolean))), sinceVersion = VersionContext.V6SoftForkVersion ) } @@ -1768,11 +1768,31 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Global - fromBigEndianBytes") { import sigma.data.OrderingOps.BigIntOrdering + import sigma.data.OrderingOps.UnsignedBigIntOrdering def byteFromBigEndianBytes: Feature[Byte, Boolean] = { newFeature( { (x: Byte) => CSigmaDslBuilder.fromBigEndianBytes[Byte](Colls.fromArray(Array(x))) == x}, "{ (x: Byte) => fromBigEndianBytes[Byte](x.toBytes) == x }", + FuncValue( + Array((1, SByte)), + EQ( + MethodCall.typed[Value[SByte.type]]( + Global, + SGlobalMethods.FromBigEndianBytesMethod.withConcreteTypes(Map(STypeVar("T") -> SByte)), + Array( + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SByte), + SByteMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + Map(STypeVar("T") -> SByte) + ), + ValUse(1, SByte) + ) + ), sinceVersion = VersionContext.V6SoftForkVersion ) } @@ -1790,6 +1810,25 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => newFeature( { (x: Short) => CSigmaDslBuilder.fromBigEndianBytes[Short](Colls.fromArray(Shorts.toByteArray(x))) == x}, "{ (x: Short) => fromBigEndianBytes[Short](x.toBytes) == x }", + FuncValue( + Array((1, SShort)), + EQ( + MethodCall.typed[Value[SShort.type]]( + Global, + SGlobalMethods.FromBigEndianBytesMethod.withConcreteTypes(Map(STypeVar("T") -> SShort)), + Array( + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SShort), + SShortMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + Map(STypeVar("T") -> SShort) + ), + ValUse(1, SShort) + ) + ), sinceVersion = VersionContext.V6SoftForkVersion ) } @@ -1807,6 +1846,25 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => newFeature( { (x: Int) => CSigmaDslBuilder.fromBigEndianBytes[Int](Colls.fromArray(Ints.toByteArray(x))) == x}, "{ (x: Int) => fromBigEndianBytes[Int](x.toBytes) == x }", + FuncValue( + Array((1, SInt)), + EQ( + MethodCall.typed[Value[SInt.type]]( + Global, + SGlobalMethods.FromBigEndianBytesMethod.withConcreteTypes(Map(STypeVar("T") -> SInt)), + Array( + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SInt), + SIntMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + Map(STypeVar("T") -> SInt) + ), + ValUse(1, SInt) + ) + ), sinceVersion = VersionContext.V6SoftForkVersion ) } @@ -1823,6 +1881,25 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => newFeature( { (x: Long) => CSigmaDslBuilder.fromBigEndianBytes[Long](Colls.fromArray(Longs.toByteArray(x))) == x}, "{ (x: Long) => fromBigEndianBytes[Long](x.toBytes) == x }", + FuncValue( + Array((1, SLong)), + EQ( + MethodCall.typed[Value[SLong.type]]( + Global, + SGlobalMethods.FromBigEndianBytesMethod.withConcreteTypes(Map(STypeVar("T") -> SLong)), + Array( + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SLong), + SLongMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + Map(STypeVar("T") -> SLong) + ), + ValUse(1, SLong) + ) + ), sinceVersion = VersionContext.V6SoftForkVersion ) } @@ -1839,6 +1916,25 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => newFeature( { (x: BigInt) => CSigmaDslBuilder.fromBigEndianBytes[BigInt](x.toBytes) == x}, "{ (x: BigInt) => Global.fromBigEndianBytes[BigInt](x.toBytes) == x }", + FuncValue( + Array((1, SBigInt)), + EQ( + MethodCall.typed[Value[SBigInt.type]]( + Global, + SGlobalMethods.FromBigEndianBytesMethod.withConcreteTypes(Map(STypeVar("T") -> SBigInt)), + Array( + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SBigInt), + SBigIntMethods.getMethodByName("toBytes"), + IndexedSeq(), + Map() + ) + ), + Map(STypeVar("T") -> SBigInt) + ), + ValUse(1, SBigInt) + ) + ), sinceVersion = VersionContext.V6SoftForkVersion ) } @@ -1852,12 +1948,57 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => bigIntFromBigEndianBytes ) + def unsignedBigIntFromBigEndianBytes: Feature[UnsignedBigInt, Boolean] = { + newFeature( + { (x: UnsignedBigInt) => CSigmaDslBuilder.fromBigEndianBytes[UnsignedBigInt](x.toBytes) == x}, + "{ (x: UnsignedBigInt) => Global.fromBigEndianBytes[UnsignedBigInt](x.toBytes) == x }", + FuncValue( + Array((1, SUnsignedBigInt)), + EQ( + MethodCall.typed[Value[SUnsignedBigInt.type]]( + Global, + SGlobalMethods.FromBigEndianBytesMethod.withConcreteTypes(Map(STypeVar("T") -> SUnsignedBigInt)), + Array( + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SUnsignedBigInt), + SUnsignedBigIntMethods.getMethodByName("toBytes"), + IndexedSeq(), + Map() + ) + ), + Map(STypeVar("T") -> SUnsignedBigInt) + ), + ValUse(1, SUnsignedBigInt) + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + CUnsignedBigInt(BigInteger.valueOf(50)) -> new Expected(ExpectedResult(Success(true), None)), + CUnsignedBigInt(sigma.crypto.CryptoConstants.groupOrder.divide(BigInteger.valueOf(2))) -> new Expected(ExpectedResult(Success(true), None)), + CUnsignedBigInt(sigma.crypto.CryptoConstants.groupOrder) -> new Expected(ExpectedResult(Success(true), None)) + ), + unsignedBigIntFromBigEndianBytes + ) + } property("Coll.reverse") { val f = newFeature[Coll[Int], Coll[Int]]( { (xs: Coll[Int]) => xs.reverse }, """{(xs: Coll[Int]) => xs.reverse }""".stripMargin, + FuncValue( + Array((1, SCollectionType(SInt))), + MethodCall.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SCollectionType(SInt)), + SCollectionMethods.ReverseMethod.withConcreteTypes(Map(STypeVar("IV") -> SInt)), + IndexedSeq(), + Map() + ) + ), sinceVersion = VersionContext.V6SoftForkVersion ) @@ -1874,6 +2015,15 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => val f = newFeature[Coll[Int], Coll[Int]]( { (xs: Coll[Int]) => xs.distinct }, """{(xs: Coll[Int]) => xs.distinct }""".stripMargin, + FuncValue( + Array((1, SCollectionType(SInt))), + MethodCall.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SCollectionType(SInt)), + SCollectionMethods.DistinctMethod.withConcreteTypes(Map(STypeVar("IV") -> SInt)), + IndexedSeq(), + Map() + ) + ), sinceVersion = VersionContext.V6SoftForkVersion ) @@ -1894,6 +2044,23 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => 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, + FuncValue( + Array((1, SPair(SCollectionType(SInt), SCollectionType(SInt)))), + MethodCall.typed[Value[SBoolean.type]]( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 1.toByte + ), + SCollectionMethods.StartsWithMethod.withConcreteTypes(Map(STypeVar("IV") -> SInt)), + Array( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 2.toByte + ) + ), + Map() + ) + ), sinceVersion = VersionContext.V6SoftForkVersion ) @@ -1914,7 +2081,24 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => 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 + FuncValue( + Array((1, SPair(SCollectionType(SInt), SCollectionType(SInt)))), + MethodCall.typed[Value[SBoolean.type]]( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 1.toByte + ), + SCollectionMethods.EndsWithMethod.withConcreteTypes(Map(STypeVar("IV") -> SInt)), + Array( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 2.toByte + ) + ), + Map() + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion ) verifyCases( @@ -1933,7 +2117,21 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => 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 + FuncValue( + Array((1, SPair(SCollectionType(SInt), SInt))), + MethodCall.typed[Value[SOption[SInt.type]]]( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SInt)), + 1.toByte + ), + SCollectionMethods.GetMethod.withConcreteTypes(Map(STypeVar("IV") -> SInt)), + Array( + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SCollectionType(SInt), SInt)), 2.toByte) + ), + Map() + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion ) verifyCases( @@ -1976,7 +2174,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } - property("BigInt.toUnsignedMod") { import sigma.data.OrderingOps.BigIntOrdering import sigma.data.OrderingOps.UnsignedBigIntOrdering @@ -2004,11 +2201,223 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => verifyCases( Seq( (CBigInt(new BigInteger("50")), CUnsignedBigInt(new BigInteger("10"))) -> Expected(ExpectedResult(Success(CUnsignedBigInt(new BigInteger("0"))), None)), - (CBigInt(new BigInteger("50")), CUnsignedBigInt(new BigInteger("0"))) -> Expected(ExpectedResult(Failure(new ArithmeticException("BigInteger: modulus not positive")), None)), - (CBigInt(new BigInteger("50")), CUnsignedBigInt(new BigInteger("-10"))) -> Expected(ExpectedResult(Failure(new ArithmeticException("BigInteger: modulus not positive")), None)) + (CBigInt(new BigInteger("50")), CUnsignedBigInt(new BigInteger("0"))) -> Expected(ExpectedResult(Failure(new ArithmeticException("BigInteger: modulus not positive")), None)) ), f ) } + property("UnsignedBigInt methods") { + import sigma.data.OrderingOps.UnsignedBigIntOrdering + + lazy val bitOr = newFeature[(UnsignedBigInt, UnsignedBigInt), UnsignedBigInt]( + { (x: (UnsignedBigInt, UnsignedBigInt)) => (x._1 | x._2)}, + "{ (x: (UnsignedBigInt, UnsignedBigInt)) => x._1.bitwiseOr(x._2) }", + if (VersionContext.current.isV6SoftForkActivated) { + FuncValue( + Array((1, SPair(SUnsignedBigInt, SUnsignedBigInt))), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 1.toByte + ), + SUnsignedBigIntMethods.getMethodByName("bitwiseOr"), + Vector( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 2.toByte + ) + ), + Map() + ) + ) + } else { + null + }, + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CUnsignedBigInt(BigInteger.valueOf(1)), CUnsignedBigInt(BigInteger.valueOf(2))) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(3))), None)), + (CUnsignedBigInt(BigInteger.valueOf(1001)), CUnsignedBigInt(BigInteger.valueOf(2002))) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(2043))), None)), + (CUnsignedBigInt(BigInteger.valueOf(100001)), CUnsignedBigInt(BigInteger.valueOf(20002))) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(118435))), None)) + ), + bitOr + ) + + lazy val bitNot = newFeature[UnsignedBigInt, UnsignedBigInt]( + { (x: UnsignedBigInt) => x.bitwiseInverse() }, + "{ (x: UnsignedBigInt) => x.bitwiseInverse }", + if (VersionContext.current.isV6SoftForkActivated) { + FuncValue( + Array((1, SUnsignedBigInt)), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SUnsignedBigInt), + SUnsignedBigIntMethods.getMethodByName("bitwiseInverse"), + Vector(), + Map() + ) + ) + } else { + null + }, + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + CUnsignedBigInt(BigInteger.valueOf(Byte.MaxValue)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(128))), None)), + CUnsignedBigInt(BigInteger.valueOf(0)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(255))), None)), + CUnsignedBigInt(BigInteger.valueOf(1)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(254))), None)), + CUnsignedBigInt(BigInteger.valueOf(2)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(253))), None)), + CUnsignedBigInt(BigInteger.valueOf(10001)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(55534))), None)), + CUnsignedBigInt(BigInteger.valueOf(Int.MaxValue)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(Int.MaxValue).add(BigInteger.ONE))), None)), + CUnsignedBigInt(BigInteger.valueOf(Long.MaxValue)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(Long.MaxValue).add(BigInteger.ONE))), None)) + ), + bitNot + ) + + + lazy val bitAnd = newFeature( + { (x: (UnsignedBigInt, UnsignedBigInt)) => x._1.asInstanceOf[CUnsignedBigInt].and(x._2.asInstanceOf[CUnsignedBigInt]) }, + "{ (x: (UnsignedBigInt, UnsignedBigInt)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SUnsignedBigInt, SUnsignedBigInt))), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SUnsignedBigInt.type]](ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)), 1.toByte), + SUnsignedBigIntMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SUnsignedBigInt.type]](ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CUnsignedBigInt(BigInteger.valueOf(3)), CUnsignedBigInt(BigInteger.valueOf(5))) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(1))), None)), + (CUnsignedBigInt(BigInteger.valueOf(10001)), CUnsignedBigInt(BigInteger.valueOf(2202))) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(16))), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (UnsignedBigInt, UnsignedBigInt)) => x._1.asInstanceOf[CUnsignedBigInt].xor(x._2.asInstanceOf[CUnsignedBigInt]) }, + "{ (x: (UnsignedBigInt, UnsignedBigInt)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SUnsignedBigInt, SUnsignedBigInt))), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SUnsignedBigInt.type]](ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)), 1.toByte), + SUnsignedBigIntMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SUnsignedBigInt.type]](ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CUnsignedBigInt(BigInteger.valueOf(3)), CUnsignedBigInt(BigInteger.valueOf(5))) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(6))), None)), + (CUnsignedBigInt(BigInteger.valueOf(10001)), CUnsignedBigInt(BigInteger.valueOf(2202))) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(12171))), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature[UnsignedBigInt, Coll[Byte]]( + { x: UnsignedBigInt => x.toBytes }, + "{ (x: UnsignedBigInt) => x.toBytes }", + FuncValue( + Array((1, SUnsignedBigInt)), + MethodCall.typed[Value[SCollection[SUnsignedBigInt.type]]]( + ValUse(1, SUnsignedBigInt), + SUnsignedBigIntMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + CUnsignedBigInt(BigInteger.valueOf(127)) -> new Expected(ExpectedResult(Success(Coll(127.toByte)), None)), + CUnsignedBigInt(BigInteger.valueOf(Short.MaxValue)) -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte)), None)), + CUnsignedBigInt(BigInteger.valueOf(Int.MaxValue)) -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte, (-1).toByte, (-1).toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[UnsignedBigInt, Coll[Boolean]]( + { x: UnsignedBigInt => x.toBytes.flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, + "{ (x: UnsignedBigInt) => x.toBits }", + FuncValue( + Array((1, SUnsignedBigInt)), + MethodCall.typed[Value[SCollection[SUnsignedBigInt.type]]]( + ValUse(1, SUnsignedBigInt), + SUnsignedBigIntMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + CUnsignedBigInt(BigInteger.valueOf(83)) -> new Expected(ExpectedResult(Success(Coll(false, true, false, true, false, false, true, true)), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (UnsignedBigInt, Int)) => if(x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[UnsignedBigInt].shiftLeft(x._2)) }, + "{ (x: (UnsignedBigInt, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SUnsignedBigInt, SInt))), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SUnsignedBigInt.type]](ValUse(1, SPair(SUnsignedBigInt, SInt)), 1.toByte), + SUnsignedBigIntMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SUnsignedBigInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CUnsignedBigInt(BigInteger.valueOf(3)), 3) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(24))), None)), + (CUnsignedBigInt(BigInteger.valueOf(3)), 8) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(768))), None)) + ), + shiftLeft, + preGeneratedSamples = Some(Seq()) + ) + + lazy val shiftRight = newFeature( + { (x: (UnsignedBigInt, Int)) => if(x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[UnsignedBigInt].shiftRight(x._2)) }, + "{ (x: (UnsignedBigInt, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SUnsignedBigInt, SInt))), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SUnsignedBigInt.type]](ValUse(1, SPair(SUnsignedBigInt, SInt)), 1.toByte), + SUnsignedBigIntMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SUnsignedBigInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CUnsignedBigInt(BigInteger.valueOf(24)), 3) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(3))), None)), + (CUnsignedBigInt(BigInteger.valueOf(1600)), 8) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(6))), None)), + (CUnsignedBigInt(BigInteger.valueOf(24)), -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (CUnsignedBigInt(BigInteger.valueOf(24)), 256) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) + } + } diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 79aaa97f89..477dfec309 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -561,7 +561,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C 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 + Seq(MInfo(3, serializeMethod), MInfo(5, FromBigEndianBytesMethod)) // methods added in v6.0 } else { Seq.empty[MInfo] }), true) From 503f0c723f82bffbfee332c09ddec4cff9c24cf4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 13 Nov 2024 20:01:09 +0300 Subject: [PATCH 37/52] bitwiseInverse fix, tests fixes --- .../src/main/scala/sigma/SigmaDsl.scala | 6 ++--- .../main/scala/sigma/crypto/BigIntegers.scala | 3 +-- .../src/main/scala/sigma/data/CBigInt.scala | 12 ++++------ .../main/scala/sigmastate/lang/Types.scala | 2 +- .../scala/sigma/LanguageSpecificationV6.scala | 12 ++++------ .../utxo/BasicOpsSpecification.scala | 23 +++++++++++++++++-- 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 099f27ae8a..ed593124f2 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -1,7 +1,5 @@ package sigma -import sigma.ast.SType - import java.math.BigInteger import sigma.data._ @@ -176,8 +174,8 @@ trait BigInt { def toUnsigned: UnsignedBigInt /** - * @return unsigned representation of this BigInt modulo `m`. Cryptographic mod operation is done, ie result is - * non-negative always + * @return unsigned representation of this BigInt modulo `m`. Cryptographic mod operation is done, so result is + * always non-negative */ def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt } diff --git a/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala b/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala index 54a6927924..c7c7b0721e 100644 --- a/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala +++ b/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala @@ -13,9 +13,8 @@ object BigIntegers { private val MAX_ITERATIONS = 1000 /** Create the given number of random bits. - * * @param bitLength the number of random bits to create. - * @param random a source of randomness. + * @param random a source of randomness. * @return a byte array containing random bits. */ @throws[IllegalArgumentException] diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index b8b3ad7a17..3ea57f0899 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -57,7 +57,7 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr override def shiftRight(n: Int): BigInt = CBigInt(wrappedValue.shiftRight(n).toSignedBigIntValueExact) - def toUnsigned: UnsignedBigInt = { + override def toUnsigned: UnsignedBigInt = { if(this.wrappedValue.compareTo(BigInteger.ZERO) < 0){ throw new ArithmeticException("BigInteger argument for .toUnsigned is negative"); } else { @@ -65,9 +65,10 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr } } - def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt = { + override def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt = { CUnsignedBigInt(this.wrappedValue.mod(m.asInstanceOf[CUnsignedBigInt].wrappedValue)) } + } /** A default implementation of [[UnsignedBigInt]] interface. @@ -148,12 +149,7 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign override def shiftRight(n: Int): UnsignedBigInt = CUnsignedBigInt(wrappedValue.shiftRight(n).toUnsignedBigIntValueExact) override def bitwiseInverse(): UnsignedBigInt = { - val bytes = if(wrappedValue.compareTo(BigInteger.ZERO) == 0) { - Array(0: Byte) - } else { - BigIntegers.asUnsignedByteArray(wrappedValue) - } - + val bytes = BigIntegers.asUnsignedByteArray(32, wrappedValue) val res: Array[Byte] = bytes.map(b => (~b & 0xff).toByte) CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(res)) } diff --git a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala index d928e36f61..4402eb949a 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala @@ -34,7 +34,7 @@ trait Types extends Core { "Int" -> SInt, "Long" -> SLong, "BigInt" -> SBigInt, - "UnsignedBigInt" -> SUnsignedBigInt, + "UnsignedBigInt" -> SUnsignedBigInt, // added in 6.0, but put in this map "AvlTree" -> SAvlTree, "Context" -> SContext, "GroupElement" -> SGroupElement, diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 2c6aaaf74b..2f7b7a8f19 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -2265,13 +2265,11 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => verifyCases( Seq( - CUnsignedBigInt(BigInteger.valueOf(Byte.MaxValue)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(128))), None)), - CUnsignedBigInt(BigInteger.valueOf(0)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(255))), None)), - CUnsignedBigInt(BigInteger.valueOf(1)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(254))), None)), - CUnsignedBigInt(BigInteger.valueOf(2)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(253))), None)), - CUnsignedBigInt(BigInteger.valueOf(10001)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(55534))), None)), - CUnsignedBigInt(BigInteger.valueOf(Int.MaxValue)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(Int.MaxValue).add(BigInteger.ONE))), None)), - CUnsignedBigInt(BigInteger.valueOf(Long.MaxValue)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(Long.MaxValue).add(BigInteger.ONE))), None)) + CUnsignedBigInt(BigInteger.valueOf(Byte.MaxValue)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639808"))), None)), + CUnsignedBigInt(BigInteger.valueOf(0)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(2).pow(256).subtract(BigInteger.ONE))), None)), + CUnsignedBigInt(BigInteger.valueOf(1)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(2).pow(256).subtract(BigInteger.valueOf(2)))), None)), + CUnsignedBigInt(BigInteger.valueOf(2)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(2).pow(256).subtract(BigInteger.valueOf(3)))), None)), + CUnsignedBigInt(BigInteger.valueOf(10001)) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(2).pow(256).subtract(BigInteger.valueOf(10002)))), None)) ), bitNot ) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index f7a9fab2ba..b83cdf0438 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -38,6 +38,7 @@ import java.math.BigInteger import scala.collection.compat.immutable.ArraySeq import java.security.SecureRandom import scala.annotation.tailrec +import scala.util.Try class BasicOpsSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -390,6 +391,24 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("unsigned -> signed overflow") { + def conversionTest() = {test("conversion", env, ext, + s"""{ + | val ub = unsignedBigInt("${CryptoConstants.groupOrder}") + | ub.toSigned > 0 + | } """.stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + val t = Try(conversionTest()) + t.failed.get.getCause.getMessage.startsWith("BigInteger out of 256 bit range") + } + } + property("schnorr sig check") { val g = CGroupElement(SecP256K1Group.generator) @@ -406,7 +425,7 @@ class BasicOpsSpecification extends CompilerTestingCommons val r = randBigInt val a: GroupElement = g.exp(CBigInt(r.bigInteger)) - val z = (r + secretKey * BigInt(scorex.crypto.hash.Blake2b256(msg))) % CryptoConstants.groupOrder + val z = (r + secretKey * BigInt(scorex.crypto.hash.Blake2b256(msg))).mod(CryptoConstants.groupOrder) if(z.bitLength > 255) { (a, z) @@ -420,7 +439,7 @@ class BasicOpsSpecification extends CompilerTestingCommons val message = Array.fill(5)(1.toByte) - val (a,z) = sign(message, holderSecret) + val (a, z) = sign(message, holderSecret) val customExt: Seq[(Byte, EvaluatedValue[_ <: SType])] = Map( 0.toByte -> GroupElementConstant(holderPk), From 6a15e5f30a3bf5370035874ee9b1fee35f60c1c7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 13 Nov 2024 23:37:34 +0300 Subject: [PATCH 38/52] expUnsigned test --- .../src/main/scala/sigma/ast/SType.scala | 2 +- .../scala/sigma/LanguageSpecificationV6.scala | 22 ++++++++++++++++++- 2 files changed, 22 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 d255adca6b..37e6c66ef3 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -108,7 +108,7 @@ object SType { SGlobal, SHeader, SPreHeader, SAvlTree, SGroupElement, SSigmaProp, SString, SBox, SUnit, SAny) - // V6 types, see `allPredefTypes` scaladoc below + // V6 types, see `allPredefTypes` scaladoc below. Contains SUnsignedBigInt type in addition to v5 types. private val v6PredefTypes = v5PredefTypes ++ Array(SUnsignedBigInt) /** All pre-defined types should be listed here. Note, NoType is not listed. diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 2f7b7a8f19..2c4ce559a3 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.{HeaderType, ZeroHeader} import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast.{SInt, _} -import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CHeader, CSigmaDslBuilder, CSigmaProp, CUnsignedBigInt, ExactNumeric, PairOfCols, ProveDHTuple, RType} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CGroupElement, CHeader, CSigmaDslBuilder, CSigmaProp, CUnsignedBigInt, ExactNumeric, PairOfCols, ProveDHTuple, RType} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, IntOps} @@ -23,6 +23,7 @@ import sigmastate.exceptions.MethodNotFound import sigmastate.utils.Extensions.ByteOpsForSigma import sigmastate.utils.Helpers import sigma.Extensions.{ArrayOps, CollOps} +import sigma.crypto.CryptoConstants import sigma.interpreter.{ContextExtension, ProverResult} import java.math.BigInteger @@ -2417,5 +2418,24 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => preGeneratedSamples = Some(Seq()) ) } + + property("GroupElement.expUnsigned") { + import sigma.data.OrderingOps.UnsignedBigIntOrdering + + val f = newFeature[(GroupElement, UnsignedBigInt), GroupElement]( + { (xs: (GroupElement, UnsignedBigInt)) => xs._1.expUnsigned(xs._2) }, + """{ (xs: (GroupElement, UnsignedBigInt)) => xs._1.expUnsigned(xs._2) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + (CGroupElement(CryptoConstants.dlogGroup.generator), CUnsignedBigInt(new BigInteger("1"))) -> Expected(ExpectedResult(Success(CGroupElement(CryptoConstants.dlogGroup.generator)), None)), + (CGroupElement(CryptoConstants.dlogGroup.generator), CUnsignedBigInt(new BigInteger("0"))) -> Expected(ExpectedResult(Success(CGroupElement(CryptoConstants.dlogGroup.identity)), None)), + (CGroupElement(CryptoConstants.dlogGroup.generator), CUnsignedBigInt(CryptoConstants.dlogGroup.order)) -> Expected(ExpectedResult(Success(CGroupElement(CryptoConstants.dlogGroup.identity)), None)) + ), + f + ) + } } From b0e41a87811c00b2174911d3257bd6da79d74ece Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 14 Nov 2024 16:38:45 +0300 Subject: [PATCH 39/52] mod ops tests --- .../scala/sigma/LanguageSpecificationV6.scala | 221 ++++++++++++++++-- 1 file changed, 200 insertions(+), 21 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 2c4ce559a3..b5d7d59a06 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -2208,11 +2208,30 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + property("GroupElement.expUnsigned") { + import sigma.data.OrderingOps.UnsignedBigIntOrdering + + val f = newFeature[(GroupElement, UnsignedBigInt), GroupElement]( + { (xs: (GroupElement, UnsignedBigInt)) => xs._1.expUnsigned(xs._2) }, + """{ (xs: (GroupElement, UnsignedBigInt)) => xs._1.expUnsigned(xs._2) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + (CGroupElement(CryptoConstants.dlogGroup.generator), CUnsignedBigInt(new BigInteger("1"))) -> Expected(ExpectedResult(Success(CGroupElement(CryptoConstants.dlogGroup.generator)), None)), + (CGroupElement(CryptoConstants.dlogGroup.generator), CUnsignedBigInt(new BigInteger("0"))) -> Expected(ExpectedResult(Success(CGroupElement(CryptoConstants.dlogGroup.identity)), None)), + (CGroupElement(CryptoConstants.dlogGroup.generator), CUnsignedBigInt(CryptoConstants.dlogGroup.order)) -> Expected(ExpectedResult(Success(CGroupElement(CryptoConstants.dlogGroup.identity)), None)) + ), + f + ) + } + property("UnsignedBigInt methods") { import sigma.data.OrderingOps.UnsignedBigIntOrdering lazy val bitOr = newFeature[(UnsignedBigInt, UnsignedBigInt), UnsignedBigInt]( - { (x: (UnsignedBigInt, UnsignedBigInt)) => (x._1 | x._2)}, + { (x: (UnsignedBigInt, UnsignedBigInt)) => (x._1 | x._2) }, "{ (x: (UnsignedBigInt, UnsignedBigInt)) => x._1.bitwiseOr(x._2) }", if (VersionContext.current.isV6SoftForkActivated) { FuncValue( @@ -2306,7 +2325,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => MethodCall.typed[Value[SUnsignedBigInt.type]]( SelectField.typed[Value[SUnsignedBigInt.type]](ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)), 1.toByte), SUnsignedBigIntMethods.v6Methods.find(_.name == "bitwiseXor").get, - Vector(SelectField.typed[Value[SUnsignedBigInt.type]](ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)),2.toByte)), + Vector(SelectField.typed[Value[SUnsignedBigInt.type]](ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)), 2.toByte)), Map() ) ), @@ -2350,7 +2369,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ((byte >> bit) & 1) == 1 lazy val toBits = newFeature[UnsignedBigInt, Coll[Boolean]]( - { x: UnsignedBigInt => x.toBytes.flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, + { x: UnsignedBigInt => x.toBytes.flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, "{ (x: UnsignedBigInt) => x.toBits }", FuncValue( Array((1, SUnsignedBigInt)), @@ -2371,7 +2390,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) lazy val shiftLeft = newFeature( - { (x: (UnsignedBigInt, Int)) => if(x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[UnsignedBigInt].shiftLeft(x._2)) }, + { (x: (UnsignedBigInt, Int)) => if (x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[UnsignedBigInt].shiftLeft(x._2)) }, "{ (x: (UnsignedBigInt, Int)) => x._1.shiftLeft(x._2) }", FuncValue( Array((1, SPair(SUnsignedBigInt, SInt))), @@ -2389,12 +2408,11 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (CUnsignedBigInt(BigInteger.valueOf(3)), 3) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(24))), None)), (CUnsignedBigInt(BigInteger.valueOf(3)), 8) -> new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(768))), None)) ), - shiftLeft, - preGeneratedSamples = Some(Seq()) + shiftLeft ) lazy val shiftRight = newFeature( - { (x: (UnsignedBigInt, Int)) => if(x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[UnsignedBigInt].shiftRight(x._2)) }, + { (x: (UnsignedBigInt, Int)) => if (x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[UnsignedBigInt].shiftRight(x._2)) }, "{ (x: (UnsignedBigInt, Int)) => x._1.shiftRight(x._2) }", FuncValue( Array((1, SPair(SUnsignedBigInt, SInt))), @@ -2414,28 +2432,189 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => (CUnsignedBigInt(BigInteger.valueOf(24)), -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), (CUnsignedBigInt(BigInteger.valueOf(24)), 256) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) ), - shiftRight, - preGeneratedSamples = Some(Seq()) + shiftRight ) - } - - property("GroupElement.expUnsigned") { - import sigma.data.OrderingOps.UnsignedBigIntOrdering - val f = newFeature[(GroupElement, UnsignedBigInt), GroupElement]( - { (xs: (GroupElement, UnsignedBigInt)) => xs._1.expUnsigned(xs._2) }, - """{ (xs: (GroupElement, UnsignedBigInt)) => xs._1.expUnsigned(xs._2) }""".stripMargin, - sinceVersion = VersionContext.V6SoftForkVersion + lazy val plusMod = newFeature( + { (x: (UnsignedBigInt, (UnsignedBigInt, UnsignedBigInt))) => x._1.asInstanceOf[UnsignedBigInt].plusMod(x._2._1, x._2._2) }, + "{ (x: (UnsignedBigInt, (UnsignedBigInt, UnsignedBigInt))) => x._1.plusMod(x._2._1, x._2._2) }", + FuncValue( + Array((1, SPair(SUnsignedBigInt, SPair(SUnsignedBigInt, SUnsignedBigInt)))), + BlockValue( + Array( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SUnsignedBigInt, SPair(SUnsignedBigInt, SUnsignedBigInt))), + 2.toByte + ) + ) + ), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SPair(SUnsignedBigInt, SPair(SUnsignedBigInt, SUnsignedBigInt))), + 1.toByte + ), + SUnsignedBigIntMethods.getMethodByName("plusMod"), + Array( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(3, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 1.toByte + ), + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(3, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 2.toByte + ) + ), + Map() + ) + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CUnsignedBigInt(BigInteger.valueOf(24)), + (CUnsignedBigInt(BigInteger.valueOf(24)), CUnsignedBigInt(BigInteger.valueOf(10)))) -> + new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(8))), None)), + (CUnsignedBigInt(BigInteger.valueOf(24)), + (CUnsignedBigInt(BigInteger.valueOf(24)), CUnsignedBigInt(BigInteger.valueOf(24)))) -> + new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(0))), None)), + (CUnsignedBigInt(CryptoConstants.groupOrder), + (CUnsignedBigInt(CryptoConstants.groupOrder), CUnsignedBigInt(CryptoConstants.groupOrder))) -> + new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(0))), None)) + ), + plusMod ) + lazy val subtractMod = newFeature( + { (x: (UnsignedBigInt, (UnsignedBigInt, UnsignedBigInt))) => x._1.asInstanceOf[UnsignedBigInt].subtractMod(x._2._1, x._2._2) }, + "{ (x: (UnsignedBigInt, (UnsignedBigInt, UnsignedBigInt))) => x._1.subtractMod(x._2._1, x._2._2) }", + FuncValue( + Array((1, SPair(SUnsignedBigInt, SPair(SUnsignedBigInt, SUnsignedBigInt)))), + BlockValue( + Array( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SUnsignedBigInt, SPair(SUnsignedBigInt, SUnsignedBigInt))), + 2.toByte + ) + ) + ), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SPair(SUnsignedBigInt, SPair(SUnsignedBigInt, SUnsignedBigInt))), + 1.toByte + ), + SUnsignedBigIntMethods.getMethodByName("subtractMod"), + Array( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(3, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 1.toByte + ), + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(3, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 2.toByte + ) + ), + Map() + ) + ) + ), + sinceVersion = V6SoftForkVersion) + verifyCases( Seq( - (CGroupElement(CryptoConstants.dlogGroup.generator), CUnsignedBigInt(new BigInteger("1"))) -> Expected(ExpectedResult(Success(CGroupElement(CryptoConstants.dlogGroup.generator)), None)), - (CGroupElement(CryptoConstants.dlogGroup.generator), CUnsignedBigInt(new BigInteger("0"))) -> Expected(ExpectedResult(Success(CGroupElement(CryptoConstants.dlogGroup.identity)), None)), - (CGroupElement(CryptoConstants.dlogGroup.generator), CUnsignedBigInt(CryptoConstants.dlogGroup.order)) -> Expected(ExpectedResult(Success(CGroupElement(CryptoConstants.dlogGroup.identity)), None)) + (CUnsignedBigInt(BigInteger.valueOf(0)), + (CUnsignedBigInt(BigInteger.valueOf(24)), CUnsignedBigInt(BigInteger.valueOf(10)))) -> + new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(6))), None)), + (CUnsignedBigInt(BigInteger.valueOf(24)), + (CUnsignedBigInt(BigInteger.valueOf(24)), CUnsignedBigInt(BigInteger.valueOf(24)))) -> + new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(0))), None)) ), - f + subtractMod ) + + lazy val multiplyMod = newFeature( + { (x: (UnsignedBigInt, (UnsignedBigInt, UnsignedBigInt))) => x._1.asInstanceOf[UnsignedBigInt].multiplyMod(x._2._1, x._2._2) }, + "{ (x: (UnsignedBigInt, (UnsignedBigInt, UnsignedBigInt))) => x._1.multiplyMod(x._2._1, x._2._2) }", + FuncValue( + Array((1, SPair(SUnsignedBigInt, SPair(SUnsignedBigInt, SUnsignedBigInt)))), + BlockValue( + Array( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SUnsignedBigInt, SPair(SUnsignedBigInt, SUnsignedBigInt))), + 2.toByte + ) + ) + ), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SPair(SUnsignedBigInt, SPair(SUnsignedBigInt, SUnsignedBigInt))), + 1.toByte + ), + SUnsignedBigIntMethods.getMethodByName("multiplyMod"), + Array( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(3, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 1.toByte + ), + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(3, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 2.toByte + ) + ), + Map() + ) + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CUnsignedBigInt(CryptoConstants.groupOrder), + (CUnsignedBigInt(CryptoConstants.groupOrder), CUnsignedBigInt(CryptoConstants.groupOrder))) -> + new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(0))), None)) + ), + multiplyMod + ) + + lazy val modInverse = newFeature( + { (x: (UnsignedBigInt, UnsignedBigInt)) => x._1.asInstanceOf[UnsignedBigInt].modInverse(x._2) }, + "{ (x: (UnsignedBigInt, UnsignedBigInt)) => x._1.modInverse(x._2) }", + FuncValue( + Array((1, SPair(SUnsignedBigInt, SUnsignedBigInt))), + MethodCall.typed[Value[SUnsignedBigInt.type]]( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 1.toByte + ), + SUnsignedBigIntMethods.getMethodByName("modInverse"), + Array( + SelectField.typed[Value[SUnsignedBigInt.type]]( + ValUse(1, SPair(SUnsignedBigInt, SUnsignedBigInt)), + 2.toByte + ) + ), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CUnsignedBigInt(BigInteger.valueOf(12)), CUnsignedBigInt(BigInteger.valueOf(5))) -> + new Expected(ExpectedResult(Success(CUnsignedBigInt(BigInteger.valueOf(3))), None)) + ), + modInverse + ) + } } From 8da5f8989ddd5624501399db7cac2ca1aba82c79 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 14 Nov 2024 16:54:40 +0300 Subject: [PATCH 40/52] _eval mathods removed --- .../src/main/scala/sigma/ast/methods.scala | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index ce8e71eef7..10475ad23c 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -551,63 +551,27 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods { .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) - } - // todo: costing val PlusModMethod = SMethod(this, "plusMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 15, 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) - } - val SubtractModMethod = SMethod(this, "subtractMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 16, 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), 17, 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) - } - val ModMethod = SMethod(this, "mod", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 18, 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), 19, 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( From 11bc3d0fac29406b0989632e801a074b818037bb Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 14 Nov 2024 18:01:43 +0300 Subject: [PATCH 41/52] More descriptions, some code cleaning --- .../src/main/scala/sigma/ast/methods.scala | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 10475ad23c..d2ad912bc6 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -541,36 +541,36 @@ 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")) - // todo: costing - final val ModInverseCostInfo = ToNBitsCostInfo + final val ModInverseCostInfo = OperationCostInfo( + FixedCost(JitCost(5)), NamedDesc("NBitsMethodCall")) val ModInverseMethod = SMethod(this, "modInverse", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 14, ModInverseCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, + "Computes modular inverse of a value. Modular inverse of A mod C is the B value that makes A * B mod C = 1.", + ArgInfo("m", "modulo value") + ) - // todo: costing val PlusModMethod = SMethod(this, "plusMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 15, ModInverseCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Modular addition", ArgInfo("that", "Addend") , ArgInfo("m", "modulo value")) val SubtractModMethod = SMethod(this, "subtractMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 16, ModInverseCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Modular subtraction", ArgInfo("that", "Subtrahend") , ArgInfo("m", "modulo value")) val MultiplyModMethod = SMethod(this, "multiplyMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 17, ModInverseCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Modular multiplication", ArgInfo("that", "Multiplier") , ArgInfo("m", "modulo value")) val ModMethod = SMethod(this, "mod", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 18, ModInverseCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Cryptographic modulo operation", ArgInfo("m", "Modulo value")) val ToSignedMethod = SMethod(this, "toSigned", SFunc(Array(this.ownerType), SBigInt), 19, ModInverseCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") + .withInfo(MethodCall, "Convert this unsigned big int to signed (with possible exception if leftmost bit is set to 1).") // no 6.0 versioning here as it is done in method containers protected override def getMethods(): Seq[SMethod] = { @@ -616,7 +616,7 @@ case object SGroupElementMethods extends MonoTypeMethods { ArgInfo("k", "The power")) lazy val ExponentiateUnsignedMethod: SMethod = SMethod( - this, "expUnsigned", SFunc(Array(this.ownerType, SUnsignedBigInt), this.ownerType), 6, Exponentiate.costKind) // todo: recheck costing + this, "expUnsigned", SFunc(Array(this.ownerType, SUnsignedBigInt), this.ownerType), 6, Exponentiate.costKind) .withIRInfo(MethodCallIrBuilder) .withInfo("Exponentiate this \\lst{GroupElement} to the given number. Returns this to the power of k", ArgInfo("k", "The power")) @@ -654,9 +654,6 @@ case object SGroupElementMethods extends MonoTypeMethods { }) } - def expUnsigned_eval(mc: MethodCall, power: UnsignedBigInt)(implicit E: ErgoTreeEvaluator): GroupElement = { - ??? - } } /** Methods of type `SigmaProp` which represent sigma-protocol propositions. */ From 8c5172be9c5c5191b4cded39b7e4aba620083a3a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 14 Nov 2024 18:23:10 +0300 Subject: [PATCH 42/52] costing --- .../src/main/scala/sigma/ast/methods.scala | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index d2ad912bc6..f3ee0ad8a9 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -541,9 +541,7 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods { /** Type for which this container defines methods. */ override def ownerType: SMonoType = SUnsignedBigInt - // todo: costing - final val ModInverseCostInfo = OperationCostInfo( - FixedCost(JitCost(5)), NamedDesc("NBitsMethodCall")) + final val ModInverseCostInfo = OperationCostInfo(FixedCost(JitCost(30)), NamedDesc("ModInverseMethodCall")) val ModInverseMethod = SMethod(this, "modInverse", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 14, ModInverseCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) @@ -552,23 +550,33 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods { ArgInfo("m", "modulo value") ) - val PlusModMethod = SMethod(this, "plusMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 15, ModInverseCostInfo.costKind) + final val PlusModCostInfo = OperationCostInfo(FixedCost(JitCost(30)), NamedDesc("ModInverseMethodCall")) + + val PlusModMethod = SMethod(this, "plusMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 15, PlusModCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "Modular addition", ArgInfo("that", "Addend") , ArgInfo("m", "modulo value")) - val SubtractModMethod = SMethod(this, "subtractMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 16, ModInverseCostInfo.costKind) + final val SubtractModCostInfo = OperationCostInfo(FixedCost(JitCost(30)), NamedDesc("SubtractModMethodCall")) + + val SubtractModMethod = SMethod(this, "subtractMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 16, SubtractModCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "Modular subtraction", ArgInfo("that", "Subtrahend") , ArgInfo("m", "modulo value")) - val MultiplyModMethod = SMethod(this, "multiplyMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 17, ModInverseCostInfo.costKind) + final val MultiplyModCostInfo = OperationCostInfo(FixedCost(JitCost(40)), NamedDesc("MultiplyModMethodCall")) + + val MultiplyModMethod = SMethod(this, "multiplyMod", SFunc(Array(this.ownerType, this.ownerType, this.ownerType), this.ownerType), 17, MultiplyModCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "Modular multiplication", ArgInfo("that", "Multiplier") , ArgInfo("m", "modulo value")) - val ModMethod = SMethod(this, "mod", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 18, ModInverseCostInfo.costKind) + final val ModCostInfo = OperationCostInfo(FixedCost(JitCost(20)), NamedDesc("ModMethodCall")) + + val ModMethod = SMethod(this, "mod", SFunc(Array(this.ownerType, this.ownerType), this.ownerType), 18, ModCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "Cryptographic modulo operation", ArgInfo("m", "Modulo value")) - val ToSignedMethod = SMethod(this, "toSigned", SFunc(Array(this.ownerType), SBigInt), 19, ModInverseCostInfo.costKind) + final val ToSignedCostInfo = OperationCostInfo(FixedCost(JitCost(10)), NamedDesc("ToSignedMethodCall")) + + val ToSignedMethod = SMethod(this, "toSigned", SFunc(Array(this.ownerType), SBigInt), 19, ToSignedCostInfo.costKind) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, "Convert this unsigned big int to signed (with possible exception if leftmost bit is set to 1).") From dd3364207921c34b5d12c41c424d0768b853b104 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 14 Nov 2024 18:39:43 +0300 Subject: [PATCH 43/52] arith test --- .../sigma/compiler/ir/GraphBuilding.scala | 4 +++- .../utxo/BasicOpsSpecification.scala | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) 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 18ce41497d..08fa21f7e9 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -346,7 +346,9 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => (ByteElement, ByteIsExactIntegral), (ShortElement, ShortIsExactIntegral), (IntElement, IntIsExactIntegral), - (LongElement, LongIsExactIntegral) + (LongElement, LongIsExactIntegral), + (bigIntElement, BigIntIsExactIntegral), + (unsignedBigIntElement, UnsignedBigIntIsExactIntegral) ) protected lazy val elemToExactOrderingMap = Map[Elem[_], ExactOrdering[_]]( (ByteElement, ByteIsExactOrdering), diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index b83cdf0438..d02b2666a1 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -481,6 +481,27 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("unsigned bigint - arith") { + def miTest() = { + test("modInverse", env, ext, + s"""{ + | val bi1 = unsignedBigInt("248486720836984554860790790898080606") + | val bi2 = unsignedBigInt("2484867208369845548607907908980997780606") + | val m = (bi1 * bi1 + bi2 * bi1) / bi1 - bi2 + | m > 0 + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy miTest() + } else { + miTest() + } + } + property("mod") { def miTest() = { test("mod", env, ext, From 8109a2887bfd43dff5b550ef7f6f661f731d0145 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 14 Nov 2024 19:42:00 +0300 Subject: [PATCH 44/52] embeddable type list / serializers versioning, code cleaning , toUnsigned costing --- .../src/main/scala/sigma/SigmaDsl.scala | 8 +++++- .../src/main/scala/sigma/ast/SType.scala | 11 +++++--- .../src/main/scala/sigma/data/CBigInt.scala | 3 +++ .../scala/sigma/reflection/StaticImpl.scala | 4 +-- .../serialization/CoreDataSerializer.scala | 6 ++--- .../sigma/serialization/TypeSerializer.scala | 13 ++++++++-- .../src/main/scala/sigma/ast/methods.scala | 26 +++++-------------- .../TypeSerializerSpecification.scala | 3 ++- .../sigma/compiler/ir/GraphBuilding.scala | 4 +-- 9 files changed, 42 insertions(+), 36 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index ed593124f2..50b5aea92c 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -180,7 +180,9 @@ trait BigInt { def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt } - +/** + * Base class for unsigned 256-bits integers + */ trait UnsignedBigInt { /** Convert this BigInt value to Byte. * @throws ArithmeticException if overflow happens. @@ -338,6 +340,10 @@ trait GroupElement { */ def exp(k: BigInt): GroupElement + /** Exponentiate this GroupElement to the given unsigned 256 bit integer. + * @param k The power. + * @return this to the power of k. + */ def expUnsigned(k: UnsignedBigInt): GroupElement /** Group operation. */ diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 37e6c66ef3..3e915cf304 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -505,6 +505,7 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM override def numericTypeIndex: Int = 4 + // no upcast to unsigned big int, use .toUnsigned / .toUnsignedMod instead override def upcast(v: AnyVal): BigInt = { v match { case x: Byte => CBigInt(BigInteger.valueOf(x.toLong)) @@ -515,6 +516,8 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM case _ => sys.error(s"Cannot upcast value $v to the type $this") } } + + // no downcast to unsigned big int, use .toUnsigned / .toUnsignedMod instead override def downcast(v: AnyVal): BigInt = { v match { case x: Byte => CBigInt(BigInteger.valueOf(x.toLong)) @@ -542,14 +545,13 @@ case object SUnsignedBigInt extends SPrimType with SEmbeddable with SNumericType override def numericTypeIndex: Int = 5 - // todo: consider upcast and downcast rules + // no upcast to signed big int, use .toSigned method 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 x: BigInt => x.asInstanceOf[CBigInt].wrappedValue case x: UnsignedBigInt => x.asInstanceOf[CUnsignedBigInt].wrappedValue case _ => sys.error(s"Cannot upcast value $v to the type $this") } @@ -559,17 +561,18 @@ case object SUnsignedBigInt extends SPrimType with SEmbeddable with SNumericType sys.error(s"Cannot upcast negative value $v to the type $this") } } + + // no downcast to signed big int, use .toSigned method 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 x: BigInt => x.asInstanceOf[CBigInt].wrappedValue case x: UnsignedBigInt => x.asInstanceOf[CUnsignedBigInt].wrappedValue case _ => sys.error(s"Cannot downcast value $v to the type $this") } - if(bi.compareTo(BigInteger.ZERO) >= 0) { + if (bi.compareTo(BigInteger.ZERO) >= 0) { CUnsignedBigInt(bi) } else { sys.error(s"Cannot upcast negative value $v to the type $this") diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 3ea57f0899..1fe1c2f503 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -81,6 +81,9 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign throw new IllegalArgumentException(s"Attempt to create unsigned value from negative big integer $wrappedValue") } + if (wrappedValue.bitLength() > 256) { + throw new IllegalArgumentException(s"Too big unsigned big int value $wrappedValue") + } override def toByte: Byte = wrappedValue.toByteExact diff --git a/core/shared/src/main/scala/sigma/reflection/StaticImpl.scala b/core/shared/src/main/scala/sigma/reflection/StaticImpl.scala index 6db911d9bc..e95eea1f59 100644 --- a/core/shared/src/main/scala/sigma/reflection/StaticImpl.scala +++ b/core/shared/src/main/scala/sigma/reflection/StaticImpl.scala @@ -72,9 +72,7 @@ class SRClass[T](val clazz: Class[T], parameterTypes: Class[_]*): RMethod = { methods.get((name, parameterTypes)) match { case Some(m) => m - case _ => - println(methods) - throw new NoSuchMethodException(s"${clazz.getName}.$name(${parameterTypes.map(_.getName).mkString(",")})") + case _ => throw new NoSuchMethodException(s"${clazz.getName}.$name(${parameterTypes.map(_.getName).mkString(",")})") } } diff --git a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala index 0d705412fc..f7c5e540f6 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala @@ -34,7 +34,7 @@ class CoreDataSerializer { val data = v.asInstanceOf[BigInt].toBigInteger.toByteArray w.putUShort(data.length) w.putBytes(data) - case SUnsignedBigInt => // todo: versioning + case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated => val data = BigIntegers.asUnsignedByteArray(v.asInstanceOf[CUnsignedBigInt].wrappedValue) w.putUShort(data.length) w.putBytes(data) @@ -113,10 +113,10 @@ class CoreDataSerializer { } val valueBytes = r.getBytes(size) CBigInt(new BigInteger(valueBytes)) - case SUnsignedBigInt => // todo: versioning + case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated => val size: Short = r.getUShort().toShort if (size > SBigInt.MaxSizeInBytes) { - throw new SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size") + throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size") } val valueBytes = r.getBytes(size) CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(valueBytes)) diff --git a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala index d5fb0047ee..aa5d43e229 100644 --- a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala @@ -242,7 +242,16 @@ 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. */ - //todo: versioning - val embeddableIdToType = Array[SType](null, SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp, SUnsignedBigInt) + def embeddableIdToType = { + if (VersionContext.current.isV6SoftForkActivated) { + embeddableV6 + } else { + embeddableV5 + } + } + + private val embeddableV5 = Array[SType](null, SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp) + + private val embeddableV6 = 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/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index f3ee0ad8a9..5c3c9a4483 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,12 +2,9 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ -import sigma.Evaluation.stypeToRType import sigma.{UnsignedBigInt, _} 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.SType.{TypeCode, paramT, tT} import sigma.ast.syntax.{SValue, ValueOps} import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} @@ -273,7 +270,6 @@ object SNumericTypeMethods extends MethodsContainer { case SLongMethods => LongIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Long]) case SBigIntMethods => obj.asInstanceOf[BigInt].toBytes case SUnsignedBigIntMethods => obj.asInstanceOf[UnsignedBigInt].toBytes - // todo: test } }) .withInfo(PropertyCall, @@ -507,24 +503,16 @@ case object SBigIntMethods extends SNumericTypeMethods { //id = 8 to make it after toBits val ToUnsigned = SMethod(this, "toUnsigned", SFunc(this.ownerType, SUnsignedBigInt), 14, ToUnsignedCostKind) .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") - - def toUnsigned_eval(mc: MethodCall, bi: BigInt) - (implicit E: ErgoTreeEvaluator): UnsignedBigInt = { - E.addCost(ModInverseCostInfo.costKind, mc.method.opDesc) - bi.toUnsigned - } + .withInfo(MethodCall, + "Converts non-negative big integer to unsigned type, throws exception on negative big integer.") + private val ToUnsignedModCostKind = FixedCost(JitCost(15)) - val ToUnsignedMod = SMethod(this, "toUnsignedMod", SFunc(Array(this.ownerType, SUnsignedBigInt), SUnsignedBigInt), 15, ToUnsignedCostKind) + val ToUnsignedMod = SMethod(this, "toUnsignedMod", SFunc(Array(this.ownerType, SUnsignedBigInt), SUnsignedBigInt), 15, ToUnsignedModCostKind) .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) - } + .withInfo(MethodCall, + "Converts non-negative big integer to unsigned type using cryptographic mod operation.", + ArgInfo("m", "modulo value")) protected override def getMethods(): Seq[SMethod] = { if (VersionContext.current.isV6SoftForkActivated) { diff --git a/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala index 6419faf364..ce28a712fd 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala @@ -3,8 +3,9 @@ package sigma.serialization import org.scalacheck.Arbitrary._ import org.scalatest.Assertion import sigma.ast._ +import sigmastate.CrossVersionProps -class TypeSerializerSpecification extends SerializationSpecification { +class TypeSerializerSpecification extends SerializationSpecification with CrossVersionProps { private def roundtrip[T <: SType](tpe: T, expected: Array[Byte]): Assertion = { val w = SigmaSerializer.startWriter() 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 08fa21f7e9..13918b08e6 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -20,9 +20,7 @@ import sigma.data.UnsignedBigIntNumericOps.{UnsignedBigIntIsExactIntegral, Unsig import sigma.exceptions.GraphBuildingException import sigma.serialization.OpCodes import sigma.{SigmaException, ast} -import sigma.util.Extensions.ByteOps -import sigma.{SigmaException, VersionContext, ast} -import sigmastate.interpreter.Interpreter.ScriptEnv +import sigma.VersionContext import scala.collection.mutable.ArrayBuffer From ae0421a3853250902ad48ad7e73b105c083a9ae3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 14 Nov 2024 23:49:19 +0300 Subject: [PATCH 45/52] fixing JS test --- .../test/scala/sigmastate/utxo/BasicOpsSpecification.scala | 5 ++++- 1 file changed, 4 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 d02b2666a1..5ff340c391 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -405,7 +405,10 @@ class BasicOpsSpecification extends CompilerTestingCommons an[Exception] should be thrownBy conversionTest() } else { val t = Try(conversionTest()) - t.failed.get.getCause.getMessage.startsWith("BigInteger out of 256 bit range") + t.failed.get match { + case e: java.lang.ArithmeticException => e.getMessage.startsWith("BigInteger out of 256 bit range") shouldBe true + case e: Throwable => e.getCause.getMessage.startsWith("BigInteger out of 256 bit range") shouldBe true + } } } From aa3fbbdacb057e6be95f2f013d585dc1142b8626 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 14 Nov 2024 23:53:57 +0300 Subject: [PATCH 46/52] JS test comment --- .../src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 5ff340c391..2271f6f6e2 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -405,6 +405,7 @@ class BasicOpsSpecification extends CompilerTestingCommons an[Exception] should be thrownBy conversionTest() } else { val t = Try(conversionTest()) + // on JS exception is ArithmeticException directly, on JVM, ArithmeticException wrapped into InvocationTargetException t.failed.get match { case e: java.lang.ArithmeticException => e.getMessage.startsWith("BigInteger out of 256 bit range") shouldBe true case e: Throwable => e.getCause.getMessage.startsWith("BigInteger out of 256 bit range") shouldBe true From cedcfbeeac7c1a261957e4b6a37a80a7ab6c0dc2 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 15 Nov 2024 13:58:48 +0300 Subject: [PATCH 47/52] fixing tupleGen --- data/shared/src/main/scala/sigma/ast/syntax.scala | 1 + .../serialization/SelectFieldSerializerSpecification.scala | 3 ++- .../sigma/serialization/TupleSerializerSpecification.scala | 3 ++- .../sigma/serialization/generators/ObjectGenerators.scala | 7 +++++++ .../sigma/serialization/generators/TypeGenerators.scala | 6 +++++- 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/syntax.scala b/data/shared/src/main/scala/sigma/ast/syntax.scala index 5a257481cb..a20c7dd274 100644 --- a/data/shared/src/main/scala/sigma/ast/syntax.scala +++ b/data/shared/src/main/scala/sigma/ast/syntax.scala @@ -40,6 +40,7 @@ object syntax { type LongConstant = Constant[SLong.type] type StringConstant = Constant[SString.type] type BigIntConstant = Constant[SBigInt.type] + type UnsignedBigIntConstant = Constant[SUnsignedBigInt.type] type BoxConstant = Constant[SBox.type] type GroupElementConstant = Constant[SGroupElement.type] type SigmaPropConstant = Constant[SSigmaProp.type] diff --git a/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala index fb3bbcd326..1e1856c676 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala @@ -4,8 +4,9 @@ import org.scalacheck.Gen import sigma.ast.syntax.CollectionOps import sigma.ast.{FalseLeaf, IntConstant, SelectField, Tuple} import sigma.serialization.OpCodes.{SelectFieldCode, TupleCode} +import sigmastate.CrossVersionProps -class SelectFieldSerializerSpecification extends TableSerializationSpecification { +class SelectFieldSerializerSpecification extends CrossVersionProps with TableSerializationSpecification { property("SelectField: Serializer round trip ") { forAll(tupleGen(2, 10)) { tuple: Tuple => diff --git a/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala index e72890cbb7..04b4cffdc9 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala @@ -1,8 +1,9 @@ package sigma.serialization import sigma.ast.{FalseLeaf, IntConstant, Tuple} +import sigmastate.CrossVersionProps -class TupleSerializerSpecification extends TableSerializationSpecification { +class TupleSerializerSpecification extends TableSerializationSpecification with CrossVersionProps { property("Tuple: Serializer round trip ") { forAll(tupleGen(1, 10)) { tuple: Tuple => 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 9a4668d8e0..1af51d4eee 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala @@ -143,6 +143,8 @@ trait ObjectGenerators extends TypeGenerators arbString.arbitrary.map { v => mkConstant[SString.type](v, SString) } lazy val bigIntConstGen: Gen[BigIntConstant] = arbBigInt.arbitrary.map { v => mkConstant[SBigInt.type](v, SBigInt) } + lazy val unsignedBigIntConstGen: Gen[UnsignedBigIntConstant] = + arbUnsignedBigInt.arbitrary.map { v => mkConstant[SUnsignedBigInt.type](v, SUnsignedBigInt) } lazy val byteArrayConstGen: Gen[CollectionConstant[SByte.type]] = for { bytes <- arrayOfRange(1, 100, arbByte.arbitrary) @@ -327,6 +329,11 @@ trait ObjectGenerators extends TypeGenerators longConstGen, booleanConstGen, bigIntConstGen, + if(VersionContext.current.isV6SoftForkActivated) { + unsignedBigIntConstGen + } else { + bigIntConstGen + }, groupElementConstGen, getVar[SInt.type], getVar[SLong.type], 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 699ef1c8f8..c27053ebf2 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala @@ -42,7 +42,11 @@ trait TypeGenerators { intTypeGen, longTypeGen, bigIntTypeGen, - unsignedBigIntTypeGen + if(VersionContext.current.isV6SoftForkActivated) { + unsignedBigIntTypeGen + } else { + bigIntTypeGen + } )) } yield STuple(values.toIndexedSeq) From a66ab5564ac0602ce49df76862976ccbda42a2c7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 16 Nov 2024 00:06:11 +0300 Subject: [PATCH 48/52] compilation err fix --- .../serialization/SelectFieldSerializerSpecification.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala index 1e1856c676..1c67806dbc 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala @@ -6,7 +6,7 @@ import sigma.ast.{FalseLeaf, IntConstant, SelectField, Tuple} import sigma.serialization.OpCodes.{SelectFieldCode, TupleCode} import sigmastate.CrossVersionProps -class SelectFieldSerializerSpecification extends CrossVersionProps with TableSerializationSpecification { +class SelectFieldSerializerSpecification extends TableSerializationSpecification with CrossVersionProps { property("SelectField: Serializer round trip ") { forAll(tupleGen(2, 10)) { tuple: Tuple => From 912a523c9fc521a51bbb9cf0930ff55d57b672f5 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 29 Nov 2024 19:19:01 +0300 Subject: [PATCH 49/52] Update core/js/src/main/scala/sigma/js/Type.scala Co-authored-by: Alexander Slesarenko --- core/js/src/main/scala/sigma/js/Type.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/js/src/main/scala/sigma/js/Type.scala b/core/js/src/main/scala/sigma/js/Type.scala index 069cca3e1e..ff391eba66 100644 --- a/core/js/src/main/scala/sigma/js/Type.scala +++ b/core/js/src/main/scala/sigma/js/Type.scala @@ -35,7 +35,7 @@ object Type extends js.Object { /** Descriptor of ErgoScript type BigInt. */ val BigInt = new Type(sigma.BigIntRType) - /** Descriptor of ErgoScript type BigInt. */ + /** Descriptor of ErgoScript type UnsignedBigInt. */ val UnsignedBigInt = new Type(sigma.UnsignedBigIntRType) /** Descriptor of ErgoScript type GroupElement. */ From 0af752d440e89eba20701c561a952482d35ecbfa Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 29 Nov 2024 19:20:01 +0300 Subject: [PATCH 50/52] Update core/shared/src/main/scala/sigma/SigmaDsl.scala Co-authored-by: Alexander Slesarenko --- core/shared/src/main/scala/sigma/SigmaDsl.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 40cd2e5f96..1f3ff20a6e 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -207,7 +207,6 @@ trait UnsignedBigInt { /** 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] From b545aa5eba4cfc86992a04c3d15fd650526d5f0a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 29 Nov 2024 19:31:42 +0300 Subject: [PATCH 51/52] Update sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala Co-authored-by: Alexander Slesarenko --- sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index cad5d4c152..0504a79c65 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -441,7 +441,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C }, { if (isV6Activated) { - // SBigInt inherit methods from SNumericType.methods + // SUnsignedBigInt inherit methods from SNumericType.methods // however they are not resolvable via SBigInt.typeId before v6.0 import SNumericTypeMethods._ (SUnsignedBigInt.typeId, Seq( From ad3506e3aaaceabc9db522273b9373c3564e7293 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 29 Nov 2024 21:35:53 +0300 Subject: [PATCH 52/52] addressing review comments --- .../src/main/scala/sigma/SigmaDsl.scala | 35 +++++++++++++++---- .../sigma/data/UnsignedBigIntegerOps.scala | 13 ++----- .../org/ergoplatform/dsl/ContractSyntax.scala | 2 +- .../org/ergoplatform/sdk/JavaHelpers.scala | 10 ++++++ 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 40cd2e5f96..566258dc9a 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -217,7 +217,8 @@ trait UnsignedBigInt { */ def compareTo(that: UnsignedBigInt): Int - /** Returns a BigInt whose value is {@code (this + that)}. + /** Returns a BigInt whose value is {@code (this + that)}, or exception if result does not fit into 256 bits + * (consider using plusMod to avoid exception) * * @param that value to be added to this BigInt. * @return { @code this + that} @@ -225,7 +226,8 @@ trait UnsignedBigInt { def add(that: UnsignedBigInt): UnsignedBigInt def +(that: UnsignedBigInt): UnsignedBigInt = add(that) - /** Returns a BigInt whose value is {@code (this - that)}. + /** Returns a BigInt whose value is {@code (this - that)}, or exception if result is negative + * (consider using plusMod to avoid exception) * * @param that value to be subtracted from this BigInt. * @return { @code this - that} @@ -234,7 +236,8 @@ trait UnsignedBigInt { def -(that: UnsignedBigInt): UnsignedBigInt = subtract(that) - /** Returns a BigInt whose value is {@code (this * that)}. + /** Returns a BigInt whose value is {@code (this * that)} , or exception if result does not fit into 256 bits + * (consider using multiplyMod to avoid exception) * * @implNote An implementation may offer better algorithmic * performance when { @code that == this}. @@ -300,29 +303,48 @@ trait UnsignedBigInt { def |(that: UnsignedBigInt): UnsignedBigInt = or(that) def modInverse(m: UnsignedBigInt): UnsignedBigInt + + /** + * @return this + that mod m , where mod is cryptographic mod operation + */ def plusMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt + + /** + * @return this - that mod m , where mod is cryptographic mod operation, so result is always non-negative + */ def subtractMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt + + /** + * @return this * that mod m , where mod is cryptographic mod operation + */ def multiplyMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt /** - * @return a big integer whose value is `this xor that` + * @return an unsigned 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, + * @return a 256-bit unsigned 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, + * @return a 256-bit unsigned 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 + /** + * @return an unsigned big integer value which is inverse of this (every bit is flipped) + */ def bitwiseInverse(): UnsignedBigInt + /** + * @return signed version of the same value, or exception if the value does not fit into signed type (since it is + * also about 256 bits, but one bit is encoding sign) + */ def toSigned(): BigInt } @@ -940,6 +962,7 @@ trait SigmaDslBuilder { /** Create DSL big integer from existing `java.math.BigInteger`*/ def BigInt(n: BigInteger): BigInt + /** Create DSL unsigned big integer from existing `java.math.BigInteger`*/ def UnsignedBigInt(n: BigInteger): UnsignedBigInt /** Extract `java.math.BigInteger` from DSL's `BigInt` type*/ diff --git a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala index bf3dd3e33b..e628703deb 100644 --- a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -54,16 +54,9 @@ object UnsignedBigIntNumericOps { def toDouble(x: UnsignedBigInt): Double = x.toDouble } - /** 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. + /** + * The instance of Integral for UnsignedBigInt. + * Done similarly to BigIntIsIntegral. */ object UnsignedBigIntIsIntegral extends UnsignedBigIntIsIntegral with UnsignedBigIntOrdering { def parseString(str: String): Option[UnsignedBigInt] = ??? 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 2554489340..34598627db 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala @@ -55,7 +55,7 @@ trait ContractSyntax { contract: SigmaContract => case _: String => StringType case _: Unit => UnitType case _: sigma.BigInt => BigIntRType - case _: sigma.BigInt => UnsignedBigIntRType + case _: sigma.UnsignedBigInt => UnsignedBigIntRType case _: GroupElement => GroupElementRType case _: ErgoBox => syntax.ErgoBoxRType // TODO remove this RType case _: Box => BoxRType 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 a46e7490db..14cdf7f6bb 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala @@ -315,6 +315,16 @@ 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] = {