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/js/src/main/scala/sigma/js/Type.scala b/core/js/src/main/scala/sigma/js/Type.scala index b323273a0c..ff391eba66 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 UnsignedBigInt. */ + val UnsignedBigInt = new Type(sigma.UnsignedBigIntRType) + /** Descriptor of ErgoScript type GroupElement. */ val GroupElement = new Type(sigma.GroupElementRType) 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 58593d3619..e2870a3c8b 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -4,8 +4,8 @@ import java.math.BigInteger import sigma.data._ /** - * Functions defined for 256-bit signed integers - * */ + * Base class for signed 256-bits integers + */ trait BigInt { /** Convert this BigInt value to Byte. * @throws ArithmeticException if overflow happens. @@ -167,8 +167,188 @@ trait BigInt { * @return a 256-bit signed integer whose value is (this >> n). `n` should be in 0..255 range (inclusive). */ def shiftRight(n: Int): BigInt + + /** + * @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, so result is + * always non-negative + */ + 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. + */ + 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}}. + */ + 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)}, 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} + */ + def add(that: UnsignedBigInt): UnsignedBigInt + def +(that: UnsignedBigInt): UnsignedBigInt = add(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} + */ + def subtract(that: UnsignedBigInt): UnsignedBigInt + + def -(that: UnsignedBigInt): UnsignedBigInt = subtract(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}. + * @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 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)`. + * + * @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 + + /** + * @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 an unsigned big integer whose value is `this xor that` + */ + def xor(that: UnsignedBigInt): UnsignedBigInt + + /** + * @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 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 } + + /** Base class for points on elliptic curves. */ trait GroupElement { /** Checks if the provided element is an identity element. */ @@ -181,6 +361,12 @@ 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. */ def multiply(that: GroupElement): GroupElement @@ -775,6 +961,9 @@ 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*/ 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 d289f4067a..3e915cf304 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, VersionContext} +import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp, UnsignedBigInt, VersionContext} import java.math.BigInteger @@ -102,12 +102,24 @@ 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. 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. * Should be in sync with sigmastate.lang.Types.predefTypes. */ - val allPredefTypes: Seq[SType] = Array[SType]( - SBoolean, SByte, SShort, SInt, SLong, SBigInt, 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 @@ -136,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. @@ -144,7 +158,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 @@ -177,6 +191,7 @@ object SType { case SInt => x.isInstanceOf[Int] case SLong => x.isInstanceOf[Long] case SBigInt => x.isInstanceOf[BigInt] + case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated => x.isInstanceOf[UnsignedBigInt] case SGroupElement => x.isInstanceOf[GroupElement] case SSigmaProp => x.isInstanceOf[SigmaProp] case SBox => x.isInstanceOf[Box] @@ -240,7 +255,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. */ @@ -307,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 @@ -359,8 +374,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) // TODO v6.0: this typeId is now shadowed by SGlobal.typeId // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 @@ -397,6 +410,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") } } @@ -419,6 +433,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") } } @@ -443,6 +458,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") } } @@ -469,17 +485,17 @@ 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") } } -/** Type of 256 bit 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 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) @@ -489,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)) @@ -499,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)) @@ -511,6 +530,56 @@ 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[UnsignedBigInt]) + 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 + + // 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: UnsignedBigInt => x.asInstanceOf[CUnsignedBigInt].wrappedValue + case _ => sys.error(s"Cannot upcast value $v to the type $this") + } + if(bi.compareTo(BigInteger.ZERO) >= 0) { + CUnsignedBigInt(bi) + } else { + 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: UnsignedBigInt => x.asInstanceOf[CUnsignedBigInt].wrappedValue + case _ => sys.error(s"Cannot downcast value $v to the type $this") + } + if (bi.compareTo(BigInteger.ZERO) >= 0) { + CUnsignedBigInt(bi) + } else { + sys.error(s"Cannot upcast negative value $v to the type $this") + } + } +} + /** 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 { @@ -649,15 +718,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) @@ -666,6 +736,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) @@ -726,29 +797,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/data/shared/src/main/scala/sigma/crypto/BigIntegers.scala b/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala similarity index 92% rename from data/shared/src/main/scala/sigma/crypto/BigIntegers.scala rename to core/shared/src/main/scala/sigma/crypto/BigIntegers.scala index 4465184580..c7c7b0721e 100644 --- a/data/shared/src/main/scala/sigma/crypto/BigIntegers.scala +++ b/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala @@ -38,8 +38,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 +52,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 +64,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 +96,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 ea69174877..1fe1c2f503 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -1,7 +1,8 @@ package sigma.data +import sigma.crypto.BigIntegers import sigma.util.Extensions.BigIntegerOps -import sigma.{BigInt, Coll, Colls} +import sigma.{BigInt, Coll, Colls, UnsignedBigInt} import java.math.BigInteger @@ -28,11 +29,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 +45,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,7 +53,112 @@ 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).toSignedBigIntValueExact) + + override def toUnsigned: UnsignedBigInt = { + if(this.wrappedValue.compareTo(BigInteger.ZERO) < 0){ + throw new ArithmeticException("BigInteger argument for .toUnsigned is negative"); + } else { + CUnsignedBigInt(this.wrappedValue) + } + } + + override def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt = { + CUnsignedBigInt(this.wrappedValue.mod(m.asInstanceOf[CUnsignedBigInt].wrappedValue)) + } + +} + +/** A default implementation of [[UnsignedBigInt]] interface. + * + * @see [[UnsignedBigInt]] for detailed descriptions + */ +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") + } + + if (wrappedValue.bitLength() > 256) { + throw new IllegalArgumentException(s"Too big unsigned big int value $wrappedValue") + } + + 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(BigIntegers.asUnsignedByteArray(wrappedValue)) + + override def compareTo(that: UnsignedBigInt): Int = + wrappedValue.compareTo(that.asInstanceOf[CUnsignedBigInt].wrappedValue) + + 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).toUnsignedBigIntValueExact) + } + + 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)) + + override def mod(m: UnsignedBigInt): UnsignedBigInt = CUnsignedBigInt(wrappedValue.mod(m.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)) + + 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)) + } + + 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)) + } + + /** + * @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 bitwiseInverse(): UnsignedBigInt = { + val bytes = BigIntegers.asUnsignedByteArray(32, wrappedValue) + val res: Array[Byte] = bytes.map(b => (~b & 0xff).toByte) + CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(res)) + } + + override def toSigned(): BigInt = { + CBigInt(wrappedValue.toSignedBigIntValueExact) + } - override def shiftRight(n: Int): BigInt = CBigInt(wrappedValue.shiftRight(n).to256BitValueExact) } 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/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..41f90b33bb 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/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index 3a676ed81d..9765f8f58b 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -101,29 +101,85 @@ 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, + methods = Map( + mkMethod(clazz, "add", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].add(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "max", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].max(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "min", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].min(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "subtract", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].subtract(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "multiply", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].multiply(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "mod", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "divide", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].divide(args(0).asInstanceOf[UnsignedBigInt]) + }, + 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", twoParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].subtractMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt]) + }, + 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() } ) ) @@ -304,6 +360,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/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala index d33b340284..175e91fbcb 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, _} @@ -33,6 +34,10 @@ class CoreDataSerializer { val data = v.asInstanceOf[BigInt].toBigInteger.toByteArray w.putUShort(data.length) w.putBytes(data) + case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated => + val data = BigIntegers.asUnsignedByteArray(v.asInstanceOf[CUnsignedBigInt].wrappedValue) + w.putUShort(data.length) + w.putBytes(data) case SGroupElement => GroupElementSerializer.serialize(v.asInstanceOf[GroupElement].toECPoint, w) case SSigmaProp => @@ -108,6 +113,13 @@ class CoreDataSerializer { } val valueBytes = r.getBytes(size) CBigInt(new BigInteger(valueBytes)) + case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated => + val size: Short = r.getUShort().toShort + if (size > SBigInt.MaxSizeInBytes) { + throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size") + } + val valueBytes = r.getBytes(size) + CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(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 1936bbcd9a..aa5d43e229 100644 --- a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala @@ -242,6 +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. */ - val embeddableIdToType = Array[SType](null, SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp) + 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/core/shared/src/main/scala/sigma/util/Extensions.scala b/core/shared/src/main/scala/sigma/util/Extensions.scala index 624b3f5d6b..e97241ca3d 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,8 +217,24 @@ object Extensions { throw new ArithmeticException("BigInteger out of 256 bit range"); } + @inline final def toUnsignedBigIntValueExact: BigInteger = { + 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) + + /** 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/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/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..1fedb30250 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) @@ -158,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/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 5226d645ce..c88366d82b 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -203,6 +203,22 @@ object SigmaPredef { Seq(ArgInfo("", ""))) ) + val UBigIntFromStringFunc = PredefinedFunc("unsignedBigInt", + Lambda(Array("input" -> SString), SUnsignedBigInt, None), + PredefFuncInfo( + { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => + 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, + Seq(ArgInfo("", ""))) + ) + val FromBase16Func = PredefinedFunc("fromBase16", Lambda(Array("input" -> SString), SByteArray, None), PredefFuncInfo( @@ -458,7 +474,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) ) @@ -483,6 +499,7 @@ object SigmaPredef { GetVarFunc, DeserializeFunc, BigIntFromStringFunc, + UBigIntFromStringFunc, FromBase16Func, FromBase64Func, FromBase58Func, diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 65546c1888..a69654de7e 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,6 +2,7 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ +import sigma.{UnsignedBigInt, _} import sigma.{Coll, VersionContext, _} import sigma.Evaluation.stypeToRType import sigma._ @@ -14,6 +15,8 @@ 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.data.{CBigInt, DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} import sigma.pow.Autolykos2PowValidation @@ -99,7 +102,7 @@ sealed trait MethodsContainer { } object MethodsContainer { - private val containers = new SparseArrayContainer[MethodsContainer](Array( + private val methodsV5 = Array( SByteMethods, SShortMethods, SIntMethods, @@ -120,11 +123,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))) + + private val containersV6 = new SparseArrayContainer[MethodsContainer](methodsV6.map(m => (m.typeId, m))) - def contains(typeId: TypeCode): Boolean = containers.contains(typeId) + def contains(typeId: TypeCode): Boolean = { + if (VersionContext.current.isV6SoftForkActivated) { + containersV6.contains(typeId) + } else { + containersV5.contains(typeId) + } + } - def apply(typeId: TypeCode): MethodsContainer = containers(typeId) + def apply(typeId: TypeCode): MethodsContainer = { + if (VersionContext.current.isV6SoftForkActivated) { + containersV6(typeId) + } else { + containersV5(typeId) + } + } /** Finds the method of the give type. * @@ -136,7 +157,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)) + } } } @@ -252,6 +277,7 @@ 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 } }) .withInfo(PropertyCall, @@ -274,6 +300,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, @@ -294,6 +321,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. ") @@ -308,6 +336,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, @@ -324,6 +353,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, @@ -340,6 +370,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, @@ -356,6 +387,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, @@ -375,6 +407,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, @@ -473,14 +506,25 @@ case object SBigIntMethods extends SNumericTypeMethods { /** Type for which this container defines methods. */ override def ownerType: SMonoType = SBigInt + private val ToUnsignedCostKind = FixedCost(JitCost(5)) + + //id = 8 to make it after toBits + val ToUnsigned = SMethod(this, "toUnsigned", SFunc(this.ownerType, SUnsignedBigInt), 14, ToUnsignedCostKind) + .withIRInfo(MethodCallIrBuilder) + .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, ToUnsignedModCostKind) + .withIRInfo(MethodCallIrBuilder) + .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) { - super.getMethods() - // ModQMethod, - // PlusModQMethod, - // MinusModQMethod, - // TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - // MultModQMethod, + super.getMethods() ++ Seq(ToUnsigned, ToUnsignedMod) } else { super.getMethods() } @@ -488,6 +532,64 @@ 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 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) + .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") + ) + + 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")) + + 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")) + + 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")) + + 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")) + + 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).") + + // no 6.0 versioning here as it is done in method containers + protected override def getMethods(): Seq[SMethod] = { + super.getMethods() ++ Seq( + ModInverseMethod, + PlusModMethod, + SubtractModMethod, + MultiplyModMethod, + ModMethod, + ToSignedMethod + ) + } + +} + /** Methods of type `String`. */ case object SStringMethods extends MonoTypeMethods { /** Type for which this container defines methods. */ @@ -517,6 +619,12 @@ 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) + .withIRInfo(MethodCallIrBuilder) + .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), _) => @@ -532,16 +640,24 @@ 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 ++ Seq(ExponentiateUnsignedMethod) + } else { + v5Methods + }) + } + } /** Methods of type `SigmaProp` which represent sigma-protocol propositions. */ @@ -864,7 +980,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")) @@ -1868,7 +1984,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"), @@ -1954,7 +2070,7 @@ case object SGlobalMethods extends MonoTypeMethods { deserializeToMethod, encodeNBitsMethod, decodeNBitsMethod, - fromBigEndianBytesMethod, + FromBigEndianBytesMethod, someMethod, noneMethod ) 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/data/shared/src/main/scala/sigma/ast/trees.scala b/data/shared/src/main/scala/sigma/ast/trees.scala index fb9f84288e..4195ca1b2d 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} @@ -640,7 +641,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 @@ -671,7 +672,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) } @@ -863,7 +864,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 ff5da32ec7..b50bf70e18 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/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/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 6e1ef54616..6e599a3605 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -6,11 +6,12 @@ import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} import scorex.util.serialization.VLQByteBufferReader 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.Evaluation.rtypeToSType import sigma.ast.{AtLeast, SType, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} +import sigma.crypto.{BigIntegers, CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps import sigma.serialization.{ConstantStore, DataSerializer, GroupElementSerializer, SigmaByteReader, SigmaSerializer} import sigma.serialization.{DataSerializer, GroupElementSerializer, SigmaSerializer} @@ -20,6 +21,7 @@ import sigma.util.Extensions.BigIntegerOps import sigma.util.NBitsUtils import sigma.validation.SigmaValidationSettings import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, Evaluation, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext} +import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, UnsignedBigInt, VersionContext} import java.math.BigInteger import java.nio.ByteBuffer @@ -29,12 +31,13 @@ import java.nio.ByteBuffer * @see [[SigmaDslBuilder]] for detailed descriptions */ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => - implicit val validationSettings: SigmaValidationSettings = ValidationRules.currentSettings override val Colls: CollBuilder = sigma.Colls 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. */ @@ -158,7 +161,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) } @@ -208,7 +211,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) } @@ -246,8 +249,12 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => if (bytes.length > SBigInt.MaxSizeInBytes) { throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") } - CBigInt(new BigInteger(bytes.toArray).to256BitValueExact).asInstanceOf[T] - // todo: UnsignedBitInt + CBigInt(new BigInteger(bytes.toArray).toSignedBigIntValueExact).asInstanceOf[T] + case sigma.UnsignedBigIntRType => + if (bytes.length > SUnsignedBigInt.MaxSizeInBytes) { + throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") + } + 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/DataValueComparer.scala b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala index 21ca85012f..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)), @@ -344,6 +345,11 @@ object DataValueComparer { okEqual = bi == r } + 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/data/shared/src/main/scala/sigma/data/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala index 86a9bfffce..8adebc36d6 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -3,7 +3,7 @@ package sigma.data import sigma.{Coll, Colls} import sigma.util.Extensions.{ByteOps, ShortOps} -/** Type-class which defines the operations on Integral types (Byte, Short, Int, Long, BigInt) +/** 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..e628703deb --- /dev/null +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -0,0 +1,156 @@ +package sigma.data + +import debox.cfor +import scorex.util.encode.Base16 +import sigma._ +import sigma.crypto.BigIntegers +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 = x.toFloat + def toDouble(x: UnsignedBigInt): Double = x.toDouble + } + + /** + * The instance of Integral for UnsignedBigInt. + * Done similarly to BigIntIsIntegral. + */ + 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) + + /** Returns a big-endian representation of this value in a collection of bytes. + * For example, the `Int` value `0x12131415` would yield the + * collection of bytes [0x12, 0x13, 0x14, 0x15] + */ + override def toBigEndianBytes(x: UnsignedBigInt): Coll[Byte] = x.toBytes + + /** + * @return a numeric value which is inverse of `x` (every bit is flipped) + */ + override def bitwiseInverse(x: UnsignedBigInt): UnsignedBigInt = x.bitwiseInverse() + + /** + * @return a numeric value which is `this | that` + */ + 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 = { + 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 = { + 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, + * in which case this method performs a right shift. (Computes floor(this * 2n).) + */ + 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 = { + 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]]. */ + 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/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/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala index fe6f62dbe0..9201214e4f 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala @@ -66,11 +66,12 @@ class DataSerializerSpecification extends SerializationSpecification { implicit val tagT = tT.classTag implicit val tAny = sigma.AnyType - val withVersion = if (tpe == SHeader) { - Some(VersionContext.V6SoftForkVersion) + val withVersion = if (tpe == SHeader || tpe == SUnsignedBigInt) { + 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) @@ -148,6 +149,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/SelectFieldSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala index fb3bbcd326..1c67806dbc 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 TableSerializationSpecification with CrossVersionProps { 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/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/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala index db6cd87330..1af51d4eee 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)) @@ -142,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) @@ -305,6 +308,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 @@ -325,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 70a215e831..c27053ebf2 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 { @@ -11,6 +12,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 +21,15 @@ 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) + 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]]) @@ -34,7 +41,12 @@ trait TypeGenerators { shortTypeGen, intTypeGen, longTypeGen, - bigIntTypeGen + bigIntTypeGen, + if(VersionContext.current.isV6SoftForkActivated) { + unsignedBigIntTypeGen + } else { + bigIntTypeGen + } )) } yield STuple(values.toIndexedSeq) 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/parsers/shared/src/main/scala/sigmastate/lang/Types.scala b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala index 06683f6e96..4402eb949a 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, // added in 6.0, but put in this map "AvlTree" -> SAvlTree, "Context" -> SContext, "GroupElement" -> SGroupElement, 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..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,6 +55,7 @@ trait ContractSyntax { contract: SigmaContract => case _: String => StringType case _: Unit => UnitType case _: sigma.BigInt => BigIntRType + case _: sigma.UnsignedBigInt => 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/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 48ec9a45ba..f5235aba5e 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -16,10 +16,14 @@ import sigma.VersionContext import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} import sigma.data.ExactOrdering.{ByteIsExactOrdering, IntIsExactOrdering, LongIsExactOrdering, ShortIsExactOrdering} import sigma.data.{CSigmaDslBuilder, ExactIntegral, ExactNumeric, ExactOrdering, Lazy, Nullable} -import sigma.exceptions.GraphBuildingException -import sigma.serialization.OpCodes 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 +import sigma.{SigmaException, ast} +import sigma.VersionContext import scala.collection.mutable.ArrayBuffer @@ -35,6 +39,7 @@ import scala.collection.mutable.ArrayBuffer trait GraphBuilding extends Base with DefRewriting { IR: IRContext => import AvlTree._ import BigInt._ + import UnsignedBigInt._ import Box._ import Coll._ import CollBuilder._ @@ -260,6 +265,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => case SString => StringElement case SAny => AnyElement case SBigInt => bigIntElement + case SUnsignedBigInt => unsignedBigIntElement case SBox => boxElement case SContext => contextElement case SGlobal => sigmaDslBuilderElement @@ -286,6 +292,7 @@ trait GraphBuilding extends Base with DefRewriting { 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)) @@ -313,6 +320,7 @@ trait GraphBuilding extends Base with DefRewriting { 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) @@ -333,20 +341,24 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => (ShortElement, ShortIsExactIntegral), (IntElement, IntIsExactIntegral), (LongElement, LongIsExactIntegral), - (bigIntElement, BigIntIsExactIntegral) + (bigIntElement, BigIntIsExactIntegral), + (unsignedBigIntElement, UnsignedBigIntIsExactIntegral) ) private lazy val elemToExactIntegralMap = Map[Elem[_], ExactIntegral[_]]( (ByteElement, ByteIsExactIntegral), (ShortElement, ShortIsExactIntegral), (IntElement, IntIsExactIntegral), - (LongElement, LongIsExactIntegral) + (LongElement, LongIsExactIntegral), + (bigIntElement, BigIntIsExactIntegral), + (unsignedBigIntElement, UnsignedBigIntIsExactIntegral) ) protected lazy val elemToExactOrderingMap = Map[Elem[_], ExactOrdering[_]]( (ByteElement, ByteIsExactOrdering), (ShortElement, ShortIsExactOrdering), (IntElement, IntIsExactOrdering), (LongElement, LongIsExactOrdering), - (bigIntElement, BigIntIsExactOrdering) + (bigIntElement, BigIntIsExactOrdering), + (unsignedBigIntElement, UnsignedBigIntIsExactOrdering) ) /** @return [[ExactNumeric]] instance for the given type */ @@ -456,6 +468,10 @@ trait GraphBuilding extends Base with DefRewriting { 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) @@ -1031,6 +1047,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 { @@ -1206,7 +1225,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) @@ -1219,7 +1238,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => g.none()(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) @@ -1249,6 +1268,40 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val y = asRep[Int](argsV(0)) val op = NumericShiftRight(elemToExactNumeric(x.elem))(x.elem) ApplyBinOpDiffArgs(op, x, y) + case SBigIntMethods.ToUnsigned.name => // only bigint has toUnsigned method + val bi = asRep[BigInt](x) + bi.toUnsigned() + case SBigIntMethods.ToUnsignedMod.name => // only bigint has toUnsignedMod method + 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/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala index 5fc26011a9..a49dbe53a6 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -117,6 +117,59 @@ 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]]) + } + ) + ) + } + + { 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[_]], 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[_]], 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[_]], 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]]) + }, + mkMethod(clazz, "toSigned", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[ctx.UnsignedBigInt].toSigned } ) ) @@ -368,6 +421,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 9b7e3061c0..84c43f14dd 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,9 +13,26 @@ 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]; + 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]; + 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]; + 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 97af672a27..695f503246 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 @@ -99,6 +99,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 @@ -167,6 +183,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("toUnsignedMod", classOf[Sym]), + Array[AnyRef](that), + true, true, element[UnsignedBigInt](unsignedBigIntElement))) + } } // entityUnref: single unref method for each type family @@ -184,7 +216,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" )) } } @@ -269,6 +301,236 @@ 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])) + } + + override def modInverse(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(self, + UnsignedBigIntClass.getMethod("modInverse", classOf[Sym]), + 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])) + } + + 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])) + } + } + + 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])) + } + + def modInverse(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt] = { + asRep[UnsignedBigInt](mkMethodCall(source, + UnsignedBigIntClass.getMethod("modInverse", classOf[Sym]), + 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])) + } + + 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 + implicit final def unrefUnsignedBigInt(p: Ref[UnsignedBigInt]): UnsignedBigInt = { + if (p.node.isInstanceOf[UnsignedBigInt]) p.node.asInstanceOf[UnsignedBigInt] + else + UnsignedBigIntAdapter(p) + } + + 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", "modInverse", + "min", "max", "plusMod", "subtractMod", "multiplyMod", "toSigned" + )) + } + } + + 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._ @@ -293,6 +555,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]), @@ -340,6 +609,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/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index 1bbf1fc4f5..7d31b91c1e 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -178,9 +178,20 @@ class SigmaTyper(val builder: SigmaBuilder, error(s"Cannot get field '$n' in in the object $newObj of non-product type ${newObj.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, _)) + + // 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/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index abd89b6857..29b78d0e97 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -14,6 +14,7 @@ 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.CSigmaDslBuilder +import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CGroupElement, CHeader, CSigmaDslBuilder, CSigmaProp, CUnsignedBigInt, ExactNumeric, PairOfCols, ProveDHTuple, RType} import sigma.crypto.SecP256K1Group import sigma.data.{CBigInt, CBox, CGroupElement, CHeader, CSigmaDslBuilder, ExactNumeric, RType} import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, PairOfCols, RType} @@ -22,6 +23,9 @@ import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, IntOps} import sigmastate.eval.{CContext, CPreHeader} import sigma.util.Extensions.{BooleanOps, IntOps} +import sigma.serialization.ValueCodes.OpCode +import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} +import sigma.util.Extensions.{BooleanOps, IntOps} import sigma.data.RType import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} @@ -30,6 +34,8 @@ import sigmastate.exceptions.MethodNotFound import sigmastate.utils.Extensions.ByteOpsForSigma import sigmastate.utils.Helpers import sigma.Extensions.ArrayOps +import sigma.Extensions.{ArrayOps, CollOps} +import sigma.crypto.CryptoConstants import sigma.interpreter.{ContextExtension, ProverResult} import java.math.BigInteger @@ -113,7 +119,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => verifyCases(cases, serializeShort, preGeneratedSamples = None) } - property("Boolean.toByte") { val toByte = newFeature((x: Boolean) => x.toByte, "{ (x: Boolean) => x.toByte }", sinceVersion = V6SoftForkVersion @@ -1828,6 +1833,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 ) } @@ -1937,11 +1943,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 ) } @@ -1959,6 +1985,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 ) } @@ -1976,6 +2021,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 ) } @@ -1992,6 +2056,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 ) } @@ -2008,6 +2091,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 ) } @@ -2021,12 +2123,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 ) @@ -2043,6 +2190,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 ) @@ -2063,6 +2219,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 ) @@ -2083,7 +2256,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( @@ -2102,7 +2292,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( @@ -2158,6 +2362,476 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + property("BigInt.toUnsigned") { + import sigma.data.OrderingOps.BigIntOrdering + + val f = newFeature[BigInt, UnsignedBigInt]( + { (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 + ) + + 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)) + ), + 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)) + ), + f + ) + } + + 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.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(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 + ) + + + 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 + ) + + 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 + ) + + 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( + (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)) + ), + 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 + ) + + } + property("Global.some") { lazy val some = newFeature( { (x: Byte) => CSigmaDslBuilder.some[Byte](x) }, 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/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index ce02bf8e08..0504a79c65 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 @@ -264,7 +266,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 +287,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), @@ -430,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) { + // SUnsignedBigInt 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( @@ -519,7 +560,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) ) ++ (if (isV6Activated) { - Seq(MInfo(3, serializeMethod), MInfo(4, deserializeToMethod), MInfo(5, fromBigEndianBytesMethod), MInfo(6, encodeNBitsMethod), MInfo(7, decodeNBitsMethod), MInfo(8, powHitMethod), MInfo(9, someMethod), MInfo(10, noneMethod)) // methods added in v6.0 + Seq(MInfo(3, serializeMethod), MInfo(4, deserializeToMethod), MInfo(5, FromBigEndianBytesMethod), MInfo(6, encodeNBitsMethod), MInfo(7, decodeNBitsMethod), MInfo(8, powHitMethod), MInfo(9, someMethod), MInfo(10, noneMethod)) // methods added in v6.0 } else { Seq.empty[MInfo] }), true) @@ -624,7 +665,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 => diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 01473b47a1..2194414ab0 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -17,8 +17,13 @@ import sigma.{Colls, SigmaTestingData} import sigma.Extensions.ArrayOps import sigma.{SigmaTestingData, VersionContext} import sigma.VersionContext.{V6SoftForkVersion, withVersions} +import sigma.VersionContext.V6SoftForkVersion +import sigma.VersionContext +import sigma.GroupElement +import sigma.VersionContext.V6SoftForkVersion import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps +import sigma.data.{AvlTreeData, CAnyValue, CBigInt, CGroupElement, CSigmaDslBuilder} import sigma.data.{AvlTreeData, CAnyValue, CHeader, CSigmaDslBuilder} import sigma.data.{AvlTreeData, AvlTreeFlags, CAND, CAnyValue, CHeader, CSigmaDslBuilder, CSigmaProp} import sigma.util.StringUtil._ @@ -34,8 +39,9 @@ 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 sigma.validation.ValidationException import sigma.util.NBitsUtils import sigma.serialization.{DataSerializer, ErgoTreeSerializer, SigmaByteWriter} import sigma.util.Extensions @@ -45,6 +51,9 @@ import sigmastate.utils.Helpers._ 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 { @@ -173,7 +182,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( @@ -231,6 +239,573 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + + property("group order deserialization") { + val b = SecP256K1Group.q + + val customExt: Seq[(Byte, EvaluatedValue[_ <: SType])] = Map( + 0.toByte -> UnsignedBigIntConstant(b) + ).toSeq + + def deserTest() = {test("restoring q", env, customExt, + s"""{ + | val b1 = unsignedBigInt(\"${b.toString}\") + | val b2 = getVar[UnsignedBigInt](0).get + | b1 == b2 + |} + | """.stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("signed -> unsigned bigint conversion - positive bigint") { + val b = new BigInteger("9280562930080889354892980449861222646750586663683904599823322027983929189860") + val ub = new BigInteger(1, b.toByteArray) + + def conversionTest() = {test("conversion", env, ext, + s"""{ + | val b = bigInt(\"${ub.toString}\") + | val ub = b.toUnsigned + | ub > 1 + | } """.stripMargin, + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy conversionTest() + } else { + conversionTest() + } + } + + property("signed -> unsigned bigint conversion - negative bigint") { + def conversionTest() = {test("conversion", 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("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"""{ + | 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("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"""{ + | 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"""{ + | 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("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()) + // 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 + } + } + } + + 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))).mod(CryptoConstants.groupOrder) + + if(z.bitLength > 255) { + (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 -> UnsignedBigIntConstant(z.bigInteger) + ).toSeq + + 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("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, + 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, + 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("mod ops - plus") { + def miTest() = { + test("modInverse", env, ext, + s"""{ + | val bi1 = unsignedBigInt("248486720836984554860790790898080606") + | val bi2 = unsignedBigInt("2484867208369845548607907908980997780606") + | val m = unsignedBigInt("575879797") + | bi1.plusMod(bi2, m) > 0 + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy miTest() + } else { + miTest() + } + } + + 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, + 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() + } + } + + // todo: finish the range proof verification script and test + ignore("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 = 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) + + def rangeTest() = { + test("range proof", env, ext, + s"""{ + | // range proof input data + | val input: GroupElement = getVar[GroupElement](0).get + | + | // proof data + | 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] = 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 = getVar[UnsignedBigInt](11).get + | + | val yBytes = sha256(q.toBytes ++ input.getEncoded ++ aI.getEncoded ++ s.getEncoded) + | + | val y = byteArrayToBigInt(yBytes).toUnsignedMod(q) + | + | val ys = + | + | 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 + | + | // ops needed: modInverse, mod ops + | + | sigmaProp(zCubed > 0) + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy rangeTest() + } else { + rangeTest() + } + } + + // todo: complete + ignore("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("Byte.toBits") { val customExt = Map( 1.toByte -> ByteConstant(1) @@ -289,6 +864,42 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + + property("UnsignedBigInt.toBits") { + def toBitsTest() = test("UnsignedBigInt.toBits", env, ext, + s"""{ + | val b = unsignedBigInt("${CryptoConstants.groupOrder}") + | val ba = b.toBits + | ba.size == 256 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[ValidationException] shouldBe thrownBy(toBitsTest()) + } + } + + 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"""{ @@ -306,6 +917,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, @@ -380,6 +1008,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"""{ @@ -397,6 +1059,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) @@ -437,6 +1136,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"""{ @@ -505,6 +1224,56 @@ 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("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"""{ @@ -643,6 +1412,42 @@ 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("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( @@ -993,6 +1798,37 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("UnsignedBigInt.toBytes") { + 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[ValidationException] shouldBe thrownBy(toBytesTest()) + } + } + + 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"""{ 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( 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] 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..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) { - 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)))) } }