diff --git a/.gitignore b/.gitignore
index 3570923b1d..b67ecf6995 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,7 +8,6 @@
*.fdb_latexmk
*.gz
-
yarn.lock
*.log
yarn.lock
diff --git a/build.sbt b/build.sbt
index 85c59512d6..ab9d931172 100644
--- a/build.sbt
+++ b/build.sbt
@@ -82,9 +82,9 @@ ThisBuild / dynverSeparator := "-"
val bouncycastleBcprov = "org.bouncycastle" % "bcprov-jdk15on" % "1.66"
-val scrypto = "org.scorexfoundation" %% "scrypto" % "2.3.0-4-a0bc6176-SNAPSHOT"
+val scrypto = "org.scorexfoundation" %% "scrypto" % "3.0.0"
val scryptoDependency =
- libraryDependencies += "org.scorexfoundation" %%% "scrypto" % "2.3.0-4-a0bc6176-SNAPSHOT"
+ libraryDependencies += "org.scorexfoundation" %%% "scrypto" % "3.0.0"
val scorexUtil = "org.scorexfoundation" %% "scorex-util" % "0.2.1"
val scorexUtilDependency =
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..069cca3e1e 100644
--- a/core/js/src/main/scala/sigma/js/Type.scala
+++ b/core/js/src/main/scala/sigma/js/Type.scala
@@ -35,6 +35,9 @@ object Type extends js.Object {
/** Descriptor of ErgoScript type BigInt. */
val BigInt = new Type(sigma.BigIntRType)
+ /** Descriptor of ErgoScript type BigInt. */
+ val UnsignedBigInt = new Type(sigma.UnsignedBigIntRType)
+
/** Descriptor of ErgoScript type GroupElement. */
val GroupElement = new Type(sigma.GroupElementRType)
diff --git a/core/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/Colls.scala b/core/shared/src/main/scala/sigma/Colls.scala
index 625120deac..d10026066b 100644
--- a/core/shared/src/main/scala/sigma/Colls.scala
+++ b/core/shared/src/main/scala/sigma/Colls.scala
@@ -45,6 +45,19 @@ trait Coll[@specialized A] {
*/
def apply(i: Int): A
+ /** The element at given index or None if there is no such element. Indices start at `0`.
+ *
+ * @param i the index
+ * @return the element at the given index, or None if there is no such element
+ */
+ def get(i: Int): Option[A] = {
+ if (isDefinedAt(i)) {
+ Some(apply(i))
+ } else {
+ None
+ }
+ }
+
/** Tests whether this $coll contains given index.
*
* The implementations of methods `apply` and `isDefinedAt` turn a `Coll[A]` into
@@ -76,6 +89,18 @@ trait Coll[@specialized A] {
* produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */
def zip[@specialized B](ys: Coll[B]): Coll[(A, B)]
+ /**
+ * @return true if first elements of this collection form given `ys` collection, false otherwise.
+ * E.g. [1,2,3] starts with [1,2]
+ */
+ def startsWith(ys: Coll[A]): Boolean
+
+ /**
+ * @return true if last elements of this collection form given `ys` collection, false otherwise.
+ * E.g. [1,2,3] ends with [2,3]
+ */
+ def endsWith(ys: Coll[A]): Boolean
+
/** Tests whether a predicate holds for at least one element of this collection.
* @param p the predicate used to test elements.
* @return `true` if the given predicate `p` is satisfied by at least one element of this collection, otherwise `false`
diff --git a/core/shared/src/main/scala/sigma/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 c2fd44550a..50b5aea92c 100644
--- a/core/shared/src/main/scala/sigma/SigmaDsl.scala
+++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala
@@ -1,12 +1,11 @@
package sigma
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.
@@ -168,8 +167,167 @@ 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}}.
+ * @since 2.0
+ */
+ def toBytes: Coll[Byte]
+
+
+ /** Compares this numeric with that numeric for order. Returns a negative integer, zero, or a positive integer as the
+ * `this` is less than, equal to, or greater than `that`.
+ */
+ def compareTo(that: UnsignedBigInt): Int
+
+ /** Returns a BigInt whose value is {@code (this + that)}.
+ *
+ * @param that value to be added to this BigInt.
+ * @return { @code this + that}
+ */
+ def add(that: UnsignedBigInt): UnsignedBigInt
+ def +(that: UnsignedBigInt): UnsignedBigInt = add(that)
+
+ /** Returns a BigInt whose value is {@code (this - that)}.
+ *
+ * @param that value to be subtracted from this BigInt.
+ * @return { @code this - that}
+ */
+ def subtract(that: UnsignedBigInt): UnsignedBigInt
+
+ def -(that: UnsignedBigInt): UnsignedBigInt = subtract(that)
+
+ /** Returns a BigInt whose value is {@code (this * that)}.
+ *
+ * @implNote An implementation may offer better algorithmic
+ * performance when { @code that == this}.
+ * @param that value to be multiplied by this BigInt.
+ * @return { @code this * that}
+ */
+ def multiply(that: UnsignedBigInt): UnsignedBigInt
+ def *(that: UnsignedBigInt): UnsignedBigInt = multiply(that)
+
+ /** Returns a BigInt whose value is {@code (this / that)}.
+ *
+ * @param that value by which this BigInt is to be divided.
+ * @return { @code this / that}
+ * @throws ArithmeticException if { @code that} is zero.
+ */
+ def divide(that: UnsignedBigInt): UnsignedBigInt
+ def /(that: UnsignedBigInt): UnsignedBigInt = divide(that)
+
+ /**
+ * Returns a BigInt whose value is {@code (this mod m}). This method
+ * differs from {@code remainder} in that it always returns a
+ * non-negative BigInteger.
+ *
+ * @param m the modulus.
+ * @return { @code this mod m}
+ * @throws ArithmeticException { @code m} ≤ 0
+ * @see #remainder
+ */
+ def mod(m: UnsignedBigInt): UnsignedBigInt
+ def %(m: UnsignedBigInt): UnsignedBigInt = mod(m)
+
+ /**
+ * Returns 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
+ def plusMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt
+ def subtractMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt
+ def multiplyMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt
+
+ /**
+ * @return a big integer whose value is `this xor that`
+ */
+ def xor(that: UnsignedBigInt): UnsignedBigInt
+
+ /**
+ * @return a 256-bit signed integer whose value is (this << n). The shift distance, n, may be negative,
+ * in which case this method performs a right shift. (Computes floor(this * 2n).)
+ */
+ def shiftLeft(n: Int): UnsignedBigInt
+
+ /**
+ * @return a 256-bit signed integer whose value is (this >> n). Sign extension is performed. The shift distance, n,
+ * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).)
+ */
+ def shiftRight(n: Int): UnsignedBigInt
+
+ def bitwiseInverse(): UnsignedBigInt
+
+ def toSigned(): BigInt
}
+
+
/** Base class for points on elliptic curves. */
trait GroupElement {
/** Checks if the provided element is an identity element. */
@@ -182,6 +340,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
@@ -588,6 +752,17 @@ trait Context {
*/
def getVar[T](id: Byte)(implicit cT: RType[T]): Option[T]
+ /**
+ * A variant of `getVar` to extract a context variable by id and type from any input
+ *
+ * @param inputIndex - input index
+ * @param id - context variable id
+ * @tparam T - expected type of the variable
+ * @return Some(value) if the variable is defined in the context AND has the given type.
+ * None otherwise
+ */
+ def getVarFromInput[T](inputIndex: Short, id: Byte)(implicit cT: RType[T]): Option[T]
+
def vars: Coll[AnyValue]
/** Maximum version of ErgoTree currently activated on the network.
@@ -752,13 +927,21 @@ trait SigmaDslBuilder {
/** Create DSL big integer from existing `java.math.BigInteger`*/
def BigInt(n: BigInteger): BigInt
+ def UnsignedBigInt(n: BigInteger): UnsignedBigInt
+
/** Extract `java.math.BigInteger` from DSL's `BigInt` type*/
def toBigInteger(n: BigInt): BigInteger
/** Construct a new authenticated dictionary with given parameters and tree root digest. */
def avlTree(operationFlags: Byte, digest: Coll[Byte], keyLength: Int, valueLengthOpt: Option[Int]): AvlTree
+ /** Serializes the given `value` into bytes using the default serialization format. */
+ def serialize[T](value: T)(implicit cT: RType[T]): Coll[Byte]
+
/** Returns a byte-wise XOR of the two collections of bytes. */
def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte]
+
+ /** Returns a number decoded from provided big-endian bytes array. */
+ def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T
}
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/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala
index 2413f7f427..2d6a4a5cdf 100644
--- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala
+++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala
@@ -1,6 +1,8 @@
package sigma.data
import debox.{Buffer, cfor}
+import sigma.Evaluation.stypeToRType
+import sigma.data.CollOverArray.equalsPairCollWithCollOverArray
import sigma.data.RType._
import sigma.util.{CollectionUtil, MaxArrayLength, safeConcatArrays_v5}
import sigma.{Coll, CollBuilder, PairColl, VersionContext, requireSameLength}
@@ -12,7 +14,9 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil
s"Cannot create collection with size ${toArray.length} greater than $MaxArrayLength")
override def tItem: RType[A] = tA
+
@inline def length: Int = toArray.length
+
@inline def apply(i: Int): A = toArray.apply(i)
override def isEmpty: Boolean = length == 0
@@ -29,8 +33,11 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil
}
def foreach(f: A => Unit): Unit = toArray.foreach(f)
+
def exists(p: A => Boolean): Boolean = toArray.exists(p)
+
def forall(p: A => Boolean): Boolean = toArray.forall(p)
+
def filter(p: A => Boolean): Coll[A] = builder.fromArray(toArray.filter(p))
def foldLeft[B](zero: B, op: ((B, A)) => B): B = toArray.foldLeft(zero)((b, a) => op((b, a)))
@@ -39,6 +46,10 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil
@inline def zip[@specialized B](ys: Coll[B]): PairColl[A, B] = builder.pairColl(this, ys)
+ @inline def startsWith(ys: Coll[A]): Boolean = toArray.startsWith(ys.toArray)
+
+ @inline def endsWith(ys: Coll[A]): Boolean = toArray.endsWith(ys.toArray)
+
def append(other: Coll[A]): Coll[A] = {
if (toArray.length <= 0) return other
val result = if (VersionContext.current.isJitActivated) {
@@ -113,12 +124,14 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil
override def unionSet(that: Coll[A]): Coll[A] = {
val set = debox.Set.ofSize[A](this.length)
val res = Buffer.ofSize[A](this.length)
+
@inline def addItemToSet(x: A) = {
if (!set(x)) {
set.add(x)
res += x
}
}
+
def addToSet(arr: Array[A]) = {
val limit = arr.length
cfor(0)(_ < limit, _ + 1) { i =>
@@ -135,14 +148,42 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil
override def equals(obj: scala.Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match {
case obj: CollOverArray[_] if obj.tItem == this.tItem =>
- java.util.Objects.deepEquals(obj.toArray, toArray)
+ java.util.Objects.deepEquals(obj.toArray, this.toArray)
+ case obj: PairColl[Any, Any] if obj.tItem == this.tItem =>
+ if (VersionContext.current.isV6SoftForkActivated) {
+ equalsPairCollWithCollOverArray(obj, this.asInstanceOf[CollOverArray[Any]])
+ } else {
+ false
+ }
case _ => false
})
- override def hashCode() = CollectionUtil.deepHashCode(toArray)
+ override def hashCode(): Int = CollectionUtil.deepHashCode(toArray)
+}
+
+object CollOverArray {
+
+ // comparing PairColl and CollOverArray instances
+ private[data] def equalsPairCollWithCollOverArray(pc: PairColl[Any, Any], coa: CollOverArray[Any]): Boolean = {
+ val ls = pc.ls
+ val rs = pc.rs
+ val ts = coa.toArray
+ if (ts.length == ls.length && ts.isInstanceOf[Array[(Any, Any)]]) {
+ val ta = ts.asInstanceOf[Array[(Any, Any)]]
+ var eq = true
+ cfor(0)(_ < ta.length && eq, _ + 1) { i =>
+ eq = java.util.Objects.deepEquals(ta(i)._1, ls(i)) && java.util.Objects.deepEquals(ta(i)._2, rs(i))
+ }
+ eq
+ } else {
+ false
+ }
+ }
+
}
-private[sigma] class CollOverArrayBuilder extends CollBuilder { builder =>
+private[sigma] class CollOverArrayBuilder extends CollBuilder {
+ builder =>
@inline override def pairColl[@specialized A, @specialized B](as: Coll[A], bs: Coll[B]): PairColl[A, B] = {
if (VersionContext.current.isJitActivated) {
@@ -166,12 +207,12 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder =>
}
}
- private def fromBoxedPairs[A, B](seq: Seq[(A, B)])(implicit tA: RType[A], tB: RType[B]): PairColl[A,B] = {
+ private def fromBoxedPairs[A, B](seq: Seq[(A, B)])(implicit tA: RType[A], tB: RType[B]): PairColl[A, B] = {
val len = seq.length
val resA = Array.ofDim[A](len)(tA.classTag)
val resB = Array.ofDim[B](len)(tB.classTag)
cfor(0)(_ < len, _ + 1) { i =>
- val item = seq.apply(i).asInstanceOf[(A,B)]
+ val item = seq.apply(i).asInstanceOf[(A, B)]
resA(i) = item._1
resB(i) = item._2
}
@@ -179,7 +220,7 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder =>
}
override def fromItems[T](items: T*)(implicit cT: RType[T]): Coll[T] = cT match {
- case pt: PairType[a,b] =>
+ case pt: PairType[a, b] =>
val tA = pt.tFst
val tB = pt.tSnd
fromBoxedPairs(items)(tA, tB)
@@ -188,16 +229,16 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder =>
}
override def fromArray[@specialized T: RType](arr: Array[T]): Coll[T] = RType[T] match {
- case pt: PairType[a,b] =>
+ case pt: PairType[a, b] =>
val tA = pt.tFst
val tB = pt.tSnd
- fromBoxedPairs[a,b](arr.asInstanceOf[Array[(a,b)]])(tA, tB)
+ fromBoxedPairs[a, b](arr.asInstanceOf[Array[(a, b)]])(tA, tB)
case _ =>
new CollOverArray(arr, builder)
}
override def replicate[@specialized T: RType](n: Int, v: T): Coll[T] = RType[T] match {
- case pt: PairType[a,b] =>
+ case pt: PairType[a, b] =>
val tA = pt.tFst
val tB = pt.tSnd
val tuple = v.asInstanceOf[(a, b)]
@@ -206,8 +247,8 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder =>
fromArray(Array.fill(n)(v))
}
- override def unzip[@specialized A, @specialized B](xs: Coll[(A,B)]): (Coll[A], Coll[B]) = xs match {
- case pa: PairColl[_,_] => (pa.ls, pa.rs)
+ override def unzip[@specialized A, @specialized B](xs: Coll[(A, B)]): (Coll[A], Coll[B]) = xs match {
+ case pa: PairColl[_, _] => (pa.ls, pa.rs)
case _ =>
val limit = xs.length
implicit val tA = xs.tItem.tFst
@@ -226,7 +267,7 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder =>
left.zip(right).map { case (l, r) => (l ^ r).toByte }
override def emptyColl[T](implicit cT: RType[T]): Coll[T] = cT match {
- case pt: PairType[a,b] =>
+ case pt: PairType[a, b] =>
val ls = emptyColl(pt.tFst)
val rs = emptyColl(pt.tSnd)
pairColl(ls, rs).asInstanceOf[Coll[T]]
@@ -235,15 +276,24 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder =>
}
}
-class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R]) extends PairColl[L,R] {
+class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R]) extends PairColl[L, R] {
- override def equals(that: scala.Any) = (this eq that.asInstanceOf[AnyRef]) || (that match {
- case that: PairColl[_,_] if that.tItem == this.tItem => ls == that.ls && rs == that.rs
+ override def equals(that: scala.Any): Boolean = (this eq that.asInstanceOf[AnyRef]) || (that match {
+ case that: PairColl[_, _] if that.tItem == this.tItem =>
+ ls == that.ls && rs == that.rs
+ case that: CollOverArray[Any] if that.tItem == this.tItem =>
+ if (VersionContext.current.isV6SoftForkActivated) {
+ equalsPairCollWithCollOverArray(this.asInstanceOf[PairColl[Any, Any]], that)
+ } else {
+ false
+ }
case _ => false
})
override def hashCode() = ls.hashCode() * 41 + rs.hashCode()
+
@inline implicit def tL: RType[L] = ls.tItem
+
@inline implicit def tR: RType[R] = rs.tItem
override lazy val tItem: RType[(L, R)] = {
@@ -251,8 +301,11 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R
}
override def builder: CollBuilder = ls.builder
+
override def toArray: Array[(L, R)] = ls.toArray.zip(rs.toArray)
+
@inline override def length: Int = if (ls.length <= rs.length) ls.length else rs.length
+
@inline override def apply(i: Int): (L, R) = (ls(i), rs(i))
override def isEmpty: Boolean = length == 0
@@ -300,7 +353,7 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R
true
}
- override def filter(p: ((L, R)) => Boolean): Coll[(L,R)] = {
+ override def filter(p: ((L, R)) => Boolean): Coll[(L, R)] = {
val len = ls.length
val resL: Buffer[L] = Buffer.empty[L](ls.tItem.classTag)
val resR: Buffer[R] = Buffer.empty[R](rs.tItem.classTag)
@@ -329,9 +382,9 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R
state
}
- override def slice(from: Int, until: Int): PairColl[L,R] = builder.pairColl(ls.slice(from, until), rs.slice(from, until))
+ override def slice(from: Int, until: Int): PairColl[L, R] = builder.pairColl(ls.slice(from, until), rs.slice(from, until))
- def append(other: Coll[(L, R)]): Coll[(L,R)] = {
+ def append(other: Coll[(L, R)]): Coll[(L, R)] = {
val arrs = builder.unzip(other)
builder.pairColl(ls.append(arrs._1), rs.append(arrs._2))
}
@@ -348,7 +401,17 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R
}
}
- def zip[@specialized B](ys: Coll[B]): PairColl[(L,R), B] = builder.pairColl(this, ys)
+ def zip[@specialized B](ys: Coll[B]): PairColl[(L, R), B] = builder.pairColl(this, ys)
+
+ def startsWith(ys: Coll[(L, R)]): Boolean = ys match {
+ case yp: PairOfCols[L, R] => ls.startsWith(yp.ls) && rs.startsWith(yp.rs)
+ case _ => toArray.startsWith(ys.toArray)
+ }
+
+ def endsWith(ys: Coll[(L, R)]): Boolean = ys match {
+ case yp: PairOfCols[L, R] => ls.endsWith(yp.ls) && rs.endsWith(yp.rs)
+ case _ => toArray.endsWith(ys.toArray)
+ }
override def indices: Coll[Int] = if (ls.length <= rs.length) ls.indices else rs.indices
@@ -394,18 +457,20 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R
}
override def unionSet(that: Coll[(L, R)]): Coll[(L, R)] = {
- val set = new java.util.HashSet[(L,R)](32)
+ val set = new java.util.HashSet[(L, R)](32)
implicit val ctL = ls.tItem.classTag
implicit val ctR = rs.tItem.classTag
val resL = Buffer.empty[L]
val resR = Buffer.empty[R]
- def addToSet(item: (L,R)) = {
+
+ def addToSet(item: (L, R)) = {
if (!set.contains(item)) {
set.add(item)
resL += item._1
resR += item._2
}
}
+
var i = 0
val thisLen = math.min(ls.length, rs.length)
while (i < thisLen) {
diff --git a/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 2a5e74e659..34bffbca34 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()
}
)
)
@@ -162,6 +218,9 @@ object ReflectionData {
mkMethod(clazz, "apply", Array[Class[_]](classOf[Int])) { (obj, args) =>
obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int])
},
+ mkMethod(clazz, "get", Array[Class[_]](classOf[Int])) { (obj, args) =>
+ obj.asInstanceOf[Coll[_]].get(args(0).asInstanceOf[Int])
+ },
mkMethod(clazz, "append", Array[Class[_]](classOf[Coll[_]])) { (obj, args) =>
obj.asInstanceOf[Coll[Any]].append(args(0).asInstanceOf[Coll[Any]])
},
@@ -170,6 +229,18 @@ object ReflectionData {
},
mkMethod(clazz, "map", Array[Class[_]](classOf[Function1[_, _]], classOf[RType[_]])) { (obj, args) =>
obj.asInstanceOf[Coll[Any]].map(args(0).asInstanceOf[Any => Any])(args(1).asInstanceOf[RType[Any]])
+ },
+ mkMethod(clazz, "reverse", Array[Class[_]]()) { (obj, args) =>
+ obj.asInstanceOf[Coll[Any]].reverse
+ },
+ mkMethod(clazz, "distinct", Array[Class[_]]()) { (obj, args) =>
+ obj.asInstanceOf[Coll[Any]].distinct
+ },
+ mkMethod(clazz, "startsWith", Array[Class[_]](classOf[Coll[_]])) { (obj, args) =>
+ obj.asInstanceOf[Coll[Any]].startsWith(args(0).asInstanceOf[Coll[Any]])
+ },
+ mkMethod(clazz, "endsWith", Array[Class[_]](classOf[Coll[_]])) { (obj, args) =>
+ obj.asInstanceOf[Coll[Any]].endsWith(args(0).asInstanceOf[Coll[Any]])
}
)
)
@@ -273,6 +344,9 @@ object ReflectionData {
mkMethod(clazz, "getVar", Array[Class[_]](classOf[Byte], classOf[RType[_]])) { (obj, args) =>
obj.asInstanceOf[Context].getVar(args(0).asInstanceOf[Byte])(args(1).asInstanceOf[RType[_]])
},
+ mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Short], classOf[Byte], classOf[RType[_]])) { (obj, args) =>
+ obj.asInstanceOf[Context].getVarFromInput(args(0).asInstanceOf[Short], args(1).asInstanceOf[Byte])(args(2).asInstanceOf[RType[_]])
+ },
mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, _) =>
obj.asInstanceOf[Context].headers
}
@@ -286,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])
},
@@ -444,8 +521,15 @@ object ReflectionData {
mkMethod(clazz, "sha256", Array[Class[_]](cColl)) { (obj, args) =>
obj.asInstanceOf[SigmaDslBuilder].sha256(args(0).asInstanceOf[Coll[Byte]])
},
+ mkMethod(clazz, "serialize", Array[Class[_]](classOf[Object], classOf[RType[_]])) { (obj, args) =>
+ obj.asInstanceOf[SigmaDslBuilder].serialize[Any](
+ args(0).asInstanceOf[Any])(args(1).asInstanceOf[RType[Any]])
+ },
mkMethod(clazz, "decodePoint", Array[Class[_]](cColl)) { (obj, args) =>
obj.asInstanceOf[SigmaDslBuilder].decodePoint(args(0).asInstanceOf[Coll[Byte]])
+ },
+ mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](cColl, classOf[RType[_]])) { (obj, args) =>
+ obj.asInstanceOf[SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[Coll[Byte]])(args(1).asInstanceOf[RType[_]])
}
)
)
diff --git a/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala
index aa4255449c..92d85ed9d8 100644
--- a/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala
+++ b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala
@@ -3,9 +3,10 @@ package sigma.serialization
import scorex.util.serialization.Writer.Aux
import scorex.util.serialization.{VLQByteBufferWriter, Writer}
import sigma.ast.SType
-import sigma.serialization.CoreByteWriter.{Bits, DataInfo, U, Vlq, ZigZag}
+import sigma.serialization.CoreByteWriter._
/** Implementation of [[Writer]] provided by `sigma-core` module.
+ *
* @param w destination [[Writer]] to which all the call got delegated.
*/
class CoreByteWriter(val w: Writer) extends Writer {
@@ -15,11 +16,20 @@ class CoreByteWriter(val w: Writer) extends Writer {
@inline override def newWriter(): Aux[CH] = w.newWriter()
- @inline override def putChunk(chunk: CH): this.type = { w.putChunk(chunk); this }
+ @inline override def putChunk(chunk: CH): this.type = {
+ w.putChunk(chunk); this
+ }
@inline override def result(): CH = w.result()
- @inline def put(x: Byte): this.type = { w.put(x); this }
+ @inline override def put(x: Byte): this.type = {
+ w.put(x); this
+ }
+
+ /** Put the given byte into the writer.
+ * @param x the byte to put into the writer
+ * @param info meta information about the data being put into the writer
+ */
@inline def put(x: Byte, info: DataInfo[Byte]): this.type = {
w.put(x); this
}
@@ -27,41 +37,110 @@ class CoreByteWriter(val w: Writer) extends Writer {
override def putUByte(x: Int): this.type = {
super.putUByte(x)
}
+
+ /** Encode integer as an unsigned byte asserting the range check
+ * @param x integer value to encode (should be in the range of unsigned byte)
+ * @param info meta information about the data being put into the writer
+ * @return
+ * @throws AssertionError if x is outside of the unsigned byte range
+ */
def putUByte(x: Int, info: DataInfo[U[Byte]]): this.type = {
super.putUByte(x)
}
- @inline def putBoolean(x: Boolean): this.type = { w.putBoolean(x); this }
+ @inline override def putBoolean(x: Boolean): this.type = {
+ w.putBoolean(x); this
+ }
+
+ /** Encode boolean by delegating to the underlying writer.
+ * @param x boolean value to encode
+ * @param info meta information about the data being put into the writer
+ * @return
+ */
@inline def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = {
w.putBoolean(x); this
}
- @inline def putShort(x: Short): this.type = { w.putShort(x); this }
+ @inline override def putShort(x: Short): this.type = {
+ w.putShort(x); this
+ }
+
+ /** Encode signed Short by delegating to the underlying writer.
+ *
+ * Use [[putUShort]] to encode values that are positive.
+ * @param x short value to encode
+ * @param info meta information about the data being put into the writer
+ */
@inline def putShort(x: Short, info: DataInfo[Short]): this.type = {
w.putShort(x); this
}
- @inline def putUShort(x: Int): this.type = { w.putUShort(x); this }
+ @inline override def putUShort(x: Int): this.type = {
+ w.putUShort(x); this
+ }
+
+ /** Encode Short that are positive by delegating to the underlying writer.
+ *
+ * Use [[putShort]] to encode values that might be negative.
+ * @param x unsigned short value (represented as Int) to encode
+ * @param info meta information about the data being put into the writer
+ */
@inline def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = {
w.putUShort(x); this
}
- @inline def putInt(x: Int): this.type = { w.putInt(x); this }
+ @inline override def putInt(x: Int): this.type = {
+ w.putInt(x); this
+ }
+
+ /** Encode signed Int by delegating to the underlying writer.
+ * Use [[putUInt]] to encode values that are positive.
+ *
+ * @param x integer value to encode
+ * @param info meta information about the data being put into the writer
+ */
@inline def putInt(x: Int, info: DataInfo[Int]): this.type = {
w.putInt(x); this
}
- @inline def putUInt(x: Long): this.type = { w.putUInt(x); this }
+ @inline override def putUInt(x: Long): this.type = {
+ w.putUInt(x); this
+ }
+
+ /** Encode Int that are positive by delegating to the underlying writer.
+ * Use [[putInt]] to encode values that might be negative.
+ *
+ * @param x unsigned integer value (represented as Long) to encode
+ * @param info meta information about the data being put into the writer
+ */
@inline def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = {
w.putUInt(x); this
}
- @inline def putLong(x: Long): this.type = { w.putLong(x); this }
+ @inline override def putLong(x: Long): this.type = {
+ w.putLong(x); this
+ }
+
+ /** Encode signed Long by delegating to the underlying writer.
+ * Use [[putULong]] to encode values that are positive.
+ *
+ * @param x long value to encode
+ * @param info meta information about the data being put into the writer
+ */
@inline def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = {
w.putLong(x); this
}
- @inline def putULong(x: Long): this.type = { w.putULong(x); this }
+ @inline override def putULong(x: Long): this.type = {
+ w.putULong(x); this
+ }
+
+ /** Encode Long that are positive by delegating to the underlying writer.
+ * Use [[putLong]] to encode values that might be negative.
+ *
+ * @param x unsigned long value to encode
+ * @param info meta information about the data being put into the writer
+ */
@inline def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = {
w.putULong(x); this
}
@@ -71,7 +150,15 @@ class CoreByteWriter(val w: Writer) extends Writer {
length: Int): this.type = {
w.putBytes(xs, offset, length); this
}
- @inline def putBytes(xs: Array[Byte]): this.type = { w.putBytes(xs); this }
+
+ @inline override def putBytes(xs: Array[Byte]): this.type = {
+ w.putBytes(xs); this
+ }
+
+ /** Encode an array of bytes by delegating to the underlying writer.
+ * @param xs array of bytes to encode
+ * @param info meta information about the data being put into the writer
+ */
@inline def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = {
w.putBytes(xs); this
}
@@ -84,29 +171,51 @@ class CoreByteWriter(val w: Writer) extends Writer {
this
}
- @inline def putBits(xs: Array[Boolean]): this.type = { w.putBits(xs); this }
+ @inline override def putBits(xs: Array[Boolean]): this.type = {
+ w.putBits(xs); this
+ }
+
+ /** Encode an array of boolean values as a bit array (packing bits into bytes)
+ *
+ * @param xs array of boolean values
+ * @param info meta information about the data being put into the writer
+ */
@inline def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = {
- w.putBits(xs);
- this
+ w.putBits(xs); this
}
- @inline def putOption[T](x: Option[T])(putValueC: (this.type, T) => Unit): this.type = {
+ @inline override def putOption[T](x: Option[T])(putValueC: (this.type, T) => Unit): this.type = {
w.putOption(x) { (_, v) =>
putValueC(this, v)
}
this
}
- @inline def putShortString(s: String): this.type = { w.putShortString(s); this }
+ @inline override def putShortString(s: String): this.type = {
+ w.putShortString(s);
+ this
+ }
// TODO refactor: move to Writer
@inline def toBytes: Array[Byte] = w match {
case wr: VLQByteBufferWriter => wr.toBytes
}
- @inline def putType[T <: SType](x: T): this.type = { TypeSerializer.serialize(x, this); this }
+ /** Serialize the given type into the writer using [[TypeSerializer]].
+ * @param x the type to put into the writer
+ */
+ @inline def putType[T <: SType](x: T): this.type = {
+ TypeSerializer.serialize(x, this)
+ this
+ }
+
+ /** Serialize the given type into the writer using [[TypeSerializer]].
+ * @param x the type to put into the writer
+ * @param info meta information about the data being put into the writer
+ */
@inline def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = {
- TypeSerializer.serialize(x, this); this
+ TypeSerializer.serialize(x, this)
+ this
}
}
@@ -226,6 +335,11 @@ object CoreByteWriter {
* @param description argument description. */
case class ArgInfo(name: String, description: String)
+ /** Represents meta information about serialized data.
+ * Passed as additional argument of serializer methods.
+ * Can be used to automatically generate format specifications based on
+ * the actual collected method invocations.
+ */
case class DataInfo[T](info: ArgInfo, format: FormatDescriptor[T])
object DataInfo {
diff --git a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala
index 233494392a..f7c5e540f6 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/CollsTests.scala b/core/shared/src/test/scala/sigma/CollsTests.scala
index 4886112742..da427ba576 100644
--- a/core/shared/src/test/scala/sigma/CollsTests.scala
+++ b/core/shared/src/test/scala/sigma/CollsTests.scala
@@ -386,6 +386,32 @@ class CollsTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers
}
}
+ property("Coll.startsWith") {
+ val minSuccess = minSuccessful(50)
+ forAll(collGen, minSuccess) { col =>
+ val n = col.length / 2
+ val prefix = col.take(n)
+ val pairs = col.zip(col)
+ pairs.startsWith(prefix.zip(prefix)) shouldBe true
+ col.startsWith(prefix) shouldBe true
+ val pairOfCols = new PairOfCols[Int, Int](col, col)
+ pairOfCols.startsWith(pairOfCols.take(n)) shouldBe true
+ }
+ }
+
+ property("Coll.endsWith") {
+ val minSuccess = minSuccessful(50)
+ forAll(collGen, minSuccess) { col =>
+ val n = col.length / 2
+ val suffix = col.slice(n, col.length)
+ col.endsWith(suffix) shouldBe true
+ val pairs = col.zip(col)
+ pairs.endsWith(suffix.zip(suffix)) shouldBe true
+ val pairOfCols = new PairOfCols[Int, Int](col, col)
+ pairOfCols.endsWith(pairOfCols.slice(n, col.length)) shouldBe true
+ }
+ }
+
property("Coll.equals") {
def checkColls(repl: Coll[_], coll: Coll[_]) = {
assert(coll == repl)
diff --git a/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/SigmaDataReflection.scala b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala
index 48939b1460..341ee647b3 100644
--- a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala
+++ b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala
@@ -86,6 +86,14 @@ object SigmaDataReflection {
)
)
+ registerClassEntry(classOf[LongToByteArray],
+ constructors = Array(
+ mkConstructor(Array(classOf[Value[_]])) { args =>
+ new LongToByteArray(args(0).asInstanceOf[Value[SLong.type]])
+ }
+ )
+ )
+
registerClassEntry(classOf[CalcBlake2b256],
constructors = Array(
mkConstructor(Array(classOf[Value[_]])) { args =>
@@ -309,6 +317,22 @@ object SigmaDataReflection {
mkMethod(clazz, "flatMap_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Function1[_,_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
obj.asInstanceOf[SCollectionMethods.type].flatMap_eval(args(0).asInstanceOf[MethodCall],
args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Any => Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator])
+ },
+ mkMethod(clazz, "reverse_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
+ obj.asInstanceOf[SCollectionMethods.type].reverse_eval(args(0).asInstanceOf[MethodCall],
+ args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator])
+ },
+ mkMethod(clazz, "distinct_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
+ obj.asInstanceOf[SCollectionMethods.type].distinct_eval(args(0).asInstanceOf[MethodCall],
+ args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator])
+ },
+ mkMethod(clazz, "startsWith_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
+ obj.asInstanceOf[SCollectionMethods.type].startsWith_eval(args(0).asInstanceOf[MethodCall],
+ args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator])
+ },
+ mkMethod(clazz, "endsWith_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
+ obj.asInstanceOf[SCollectionMethods.type].endsWith_eval(args(0).asInstanceOf[MethodCall],
+ args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator])
}
)
)
@@ -322,6 +346,11 @@ object SigmaDataReflection {
args(1).asInstanceOf[SigmaDslBuilder],
args(2).asInstanceOf[Coll[Byte]],
args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator])
+ },
+ mkMethod(clazz, "serialize_eval", Array[Class[_]](classOf[MethodCall], classOf[SigmaDslBuilder], classOf[Object], classOf[ErgoTreeEvaluator])) { (obj, args) =>
+ obj.asInstanceOf[SGlobalMethods.type].serialize_eval(args(0).asInstanceOf[MethodCall],
+ args(1).asInstanceOf[SigmaDslBuilder],
+ args(2).asInstanceOf[SType#WrappedType])(args(3).asInstanceOf[ErgoTreeEvaluator])
}
)
)
diff --git a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala
index 68d69abd91..8d731e1c67 100644
--- a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala
+++ b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala
@@ -381,7 +381,7 @@ object ErgoTree {
* */
def withSegregation(header: HeaderType, prop: SigmaPropValue): ErgoTree = {
val constantStore = new ConstantStore()
- val w = SigmaSerializer.startWriter(constantStore)
+ val w = SigmaSerializer.startWriter(Some(constantStore))
// serialize value and segregate constants into constantStore
ValueSerializer.serialize(prop, w)
val extractedConstants = constantStore.getAll
diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala
index 5a17038c54..e5481cee5b 100644
--- a/data/shared/src/main/scala/sigma/ast/SMethod.scala
+++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala
@@ -81,7 +81,9 @@ case class SMethod(
/** Operation descriptor of this method. */
lazy val opDesc = MethodDesc(this)
- /** Return true if this method has runtime type parameters */
+ /** Return true if this method has explicit type parameters, which need to be serialized
+ * as part of [[MethodCall]].
+ */
def hasExplicitTypeArgs: Boolean = explicitTypeArgs.nonEmpty
/** Finds and keeps the [[RMethod]] instance which corresponds to this method descriptor.
@@ -300,6 +302,18 @@ object SMethod {
(implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2]): RMethod =
RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass)
+ /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type.
+ * @param methodName the name of the method to lookup
+ * @param cT the class where to search the methodName
+ * @param cA1 the class of the method's first argument
+ * @param cA2 the class of the method's second argument
+ * @param cA3 the class of the method's third argument
+ */
+ def javaMethodOf[T, A1, A2, A3]
+ (methodName: String)
+ (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2], cA3: ClassTag[A3]): RMethod =
+ RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass, cA3.runtimeClass)
+
/** Default fallback method call recognizer which builds MethodCall ErgoTree nodes. */
val MethodCallIrBuilder: PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue] = {
case (builder, obj, method, args, tparamSubst) =>
diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala
index 068d955541..28c51a706b 100644
--- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala
+++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala
@@ -146,6 +146,18 @@ object SigmaPredef {
Seq(ArgInfo("varId", "\\lst{Byte} identifier of context variable")))
)
+ val GetVarFromInputFunc = PredefinedFunc("getVarFromInput",
+ Lambda(Array(paramT), Array("inputId" -> SShort, "varId" -> SByte), SOption(tT), None),
+ PredefFuncInfo(
+ { case (Ident(_, SFunc(_, SOption(rtpe), _)), Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) =>
+ mkMethodCall(Context, SContextMethods.getVarFromInputMethod, IndexedSeq(SShort.downcast(inputId.value.asInstanceOf[AnyVal]), SByte.downcast(varId.value.asInstanceOf[AnyVal])), Map(tT -> rtpe))
+ }),
+ OperationInfo(MethodCall,
+ "Get context variable with given \\lst{varId} and type.",
+ Seq(ArgInfo("inputId", "\\lst{Byte} index of input to read context variable from"),
+ ArgInfo("varId", "\\lst{Byte} identifier of context variable")))
+ )
+
def PKFunc(networkPrefix: NetworkPrefix) = PredefinedFunc("PK",
Lambda(Array("input" -> SString), SSigmaProp, None),
PredefFuncInfo(
@@ -191,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(
@@ -402,6 +430,43 @@ object SigmaPredef {
ArgInfo("default", "optional default value, if register is not available")))
)
+ val SerializeFunc = PredefinedFunc("serialize",
+ Lambda(Seq(paramT), Array("value" -> tT), SByteArray, None),
+ irInfo = PredefFuncInfo(
+ irBuilder = { case (_, args @ Seq(value)) =>
+ MethodCall.typed[Value[SCollection[SByte.type]]](
+ Global,
+ SGlobalMethods.serializeMethod.withConcreteTypes(Map(tT -> value.tpe)),
+ args.toIndexedSeq,
+ Map()
+ )
+ }),
+ docInfo = OperationInfo(MethodCall,
+ """Serializes the given `value` into bytes using the default serialization format.
+ """.stripMargin,
+ Seq(ArgInfo("value", "value to serialize"))
+ )
+ )
+
+ val FromBigEndianBytesFunc = PredefinedFunc("fromBigEndianBytes",
+ Lambda(Seq(paramT), Array("bytes" -> SByteArray), tT, None),
+ irInfo = PredefFuncInfo(
+ irBuilder = { case (u, args) =>
+ val resType = u.opType.tRange.asInstanceOf[SFunc].tRange
+ MethodCall(
+ Global,
+ SGlobalMethods.FromBigEndianBytesMethod.withConcreteTypes(Map(tT -> resType)),
+ args.toIndexedSeq,
+ Map(tT -> resType)
+ )
+ }),
+ docInfo = OperationInfo(MethodCall,
+ """Deserializes provided big endian bytes into a numeric value of given type.
+ """.stripMargin,
+ Seq(ArgInfo("bytes", "bytes to deserialize"))
+ )
+ )
+
val globalFuncs: Map[String, PredefinedFunc] = Seq(
AllOfFunc,
AnyOfFunc,
@@ -415,6 +480,7 @@ object SigmaPredef {
GetVarFunc,
DeserializeFunc,
BigIntFromStringFunc,
+ UBigIntFromStringFunc,
FromBase16Func,
FromBase64Func,
FromBase58Func,
@@ -429,7 +495,10 @@ object SigmaPredef {
AvlTreeFunc,
SubstConstantsFunc,
ExecuteFromVarFunc,
- ExecuteFromSelfRegFunc
+ ExecuteFromSelfRegFunc,
+ SerializeFunc,
+ GetVarFromInputFunc,
+ FromBigEndianBytesFunc
).map(f => f.name -> f).toMap
def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = {
diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala
index c980a22300..5c3c9a4483 100644
--- a/data/shared/src/main/scala/sigma/ast/methods.scala
+++ b/data/shared/src/main/scala/sigma/ast/methods.scala
@@ -2,18 +2,20 @@ package sigma.ast
import org.ergoplatform._
import org.ergoplatform.validation._
-import sigma._
+import sigma.{UnsignedBigInt, _}
import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray}
import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf}
-import sigma.ast.SType.TypeCode
+import sigma.ast.SType.{TypeCode, paramT, tT}
import sigma.ast.syntax.{SValue, ValueOps}
import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral}
import sigma.data.NumericOps.BigIntIsExactIntegral
import sigma.data.OverloadHack.Overloaded1
+import sigma.data.UnsignedBigIntNumericOps.UnsignedBigIntIsExactIntegral
import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants}
import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost}
import sigma.reflection.RClass
import sigma.serialization.CoreByteWriter.ArgInfo
+import sigma.serialization.{DataSerializer, SigmaByteWriter, SigmaSerializer}
import sigma.utils.SparseArrayContainer
import scala.annotation.unused
@@ -92,7 +94,7 @@ sealed trait MethodsContainer {
}
object MethodsContainer {
- private val containers = new SparseArrayContainer[MethodsContainer](Array(
+ private val methodsV5 = Array(
SByteMethods,
SShortMethods,
SIntMethods,
@@ -113,11 +115,29 @@ object MethodsContainer {
STupleMethods,
SUnitMethods,
SAnyMethods
- ).map(m => (m.typeId, m)))
+ )
+
+ private val methodsV6 = methodsV5 ++ Seq(SUnsignedBigIntMethods)
+
+ private val containersV5 = new SparseArrayContainer[MethodsContainer](methodsV5.map(m => (m.typeId, m)))
- def contains(typeId: TypeCode): Boolean = containers.contains(typeId)
+ private val containersV6 = new SparseArrayContainer[MethodsContainer](methodsV6.map(m => (m.typeId, m)))
- def apply(typeId: TypeCode): MethodsContainer = containers(typeId)
+ def contains(typeId: TypeCode): Boolean = {
+ if (VersionContext.current.isV6SoftForkActivated) {
+ containersV6.contains(typeId)
+ } else {
+ containersV5.contains(typeId)
+ }
+ }
+
+ def apply(typeId: TypeCode): MethodsContainer = {
+ if (VersionContext.current.isV6SoftForkActivated) {
+ containersV6(typeId)
+ } else {
+ containersV5(typeId)
+ }
+ }
/** Finds the method of the give type.
*
@@ -129,7 +149,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))
+ }
}
}
@@ -245,6 +269,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,
@@ -267,6 +292,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,
@@ -287,6 +313,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. ")
@@ -301,6 +328,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,
@@ -317,6 +345,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,
@@ -333,6 +362,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,
@@ -349,6 +379,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,
@@ -368,6 +399,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,
@@ -466,19 +498,87 @@ 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()
}
}
+}
+
+/** 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
+ )
+ }
}
@@ -511,6 +611,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), _) =>
@@ -526,16 +632,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. */
@@ -858,7 +972,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"))
@@ -1019,7 +1133,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply {
SFunc(Array(ThisType, tIV, SInt), SInt, paramIVSeq),
26, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(10), chunkSize = 2))
.withIRInfo(MethodCallIrBuilder, javaMethodOf[Coll[_], Any, Int]("indexOf"))
- .withInfo(MethodCall, "")
+ .withInfo(MethodCall, "Returns index of a collection element, or -1 if not found")
/** Implements evaluation of Coll.indexOf method call ErgoTree node.
* Called via reflection based on naming convention.
@@ -1051,8 +1165,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply {
baseCost = JitCost(10), perChunkCost = JitCost(1), chunkSize = 10)
val ZipMethod = SMethod(this, "zip",
- SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)),
- 29, Zip_CostKind)
+ SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)), 29, Zip_CostKind)
.withIRInfo(MethodCallIrBuilder)
.withInfo(MethodCall, "")
@@ -1068,29 +1181,133 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply {
}
}
+ // ======== 6.0 methods below ===========
+
+ private val reverseCostKind = Append.costKind
+
+ val ReverseMethod = SMethod(this, "reverse",
+ SFunc(Array(ThisType), ThisType, paramIVSeq), 30, reverseCostKind)
+ .withIRInfo(MethodCallIrBuilder)
+ .withInfo(MethodCall, "")
+
+ /** Implements evaluation of Coll.reverse method call ErgoTree node.
+ * Called via reflection based on naming convention.
+ * @see SMethod.evalMethod
+ */
+ def reverse_eval[A](mc: MethodCall, xs: Coll[A])
+ (implicit E: ErgoTreeEvaluator): Coll[A] = {
+ val m = mc.method
+ E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () =>
+ xs.reverse
+ }
+ }
+
+ private val distinctCostKind = PerItemCost(baseCost = JitCost(60), perChunkCost = JitCost(5), chunkSize = 100)
+
+ val DistinctMethod = SMethod(this, "distinct",
+ SFunc(Array(ThisType), ThisType, paramIVSeq), 31, distinctCostKind)
+ .withIRInfo(MethodCallIrBuilder)
+ .withInfo(MethodCall, "Returns inversed collection.")
+
+ /** Implements evaluation of Coll.reverse method call ErgoTree node.
+ * Called via reflection based on naming convention.
+ * @see SMethod.evalMethod
+ */
+ def distinct_eval[A](mc: MethodCall, xs: Coll[A])
+ (implicit E: ErgoTreeEvaluator): Coll[A] = {
+ val m = mc.method
+ E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () =>
+ xs.distinct
+ }
+ }
+
+ private val startsWithCostKind = Zip_CostKind
+
+ val StartsWithMethod = SMethod(this, "startsWith",
+ SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, startsWithCostKind)
+ .withIRInfo(MethodCallIrBuilder)
+ .withInfo(MethodCall, "Returns true if this collection starts with given one, false otherwise.",
+ ArgInfo("prefix", "Collection to be checked for being a prefix of this collection."))
+
+ /** Implements evaluation of Coll.zip method call ErgoTree node.
+ * Called via reflection based on naming convention.
+ * @see SMethod.evalMethod
+ */
+ def startsWith_eval[A](mc: MethodCall, xs: Coll[A], ys: Coll[A])
+ (implicit E: ErgoTreeEvaluator): Boolean = {
+ val m = mc.method
+ E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () =>
+ xs.startsWith(ys)
+ }
+ }
+
+ private val endsWithCostKind = Zip_CostKind
+
+ val EndsWithMethod = SMethod(this, "endsWith",
+ SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, endsWithCostKind)
+ .withIRInfo(MethodCallIrBuilder)
+ .withInfo(MethodCall, "Returns true if this collection ends with given one, false otherwise.",
+ ArgInfo("suffix", "Collection to be checked for being a suffix of this collection."))
+
+ /** Implements evaluation of Coll.zip method call ErgoTree node.
+ * Called via reflection based on naming convention.
+ * @see SMethod.evalMethod
+ */
+ def endsWith_eval[A](mc: MethodCall, xs: Coll[A], ys: Coll[A])
+ (implicit E: ErgoTreeEvaluator): Boolean = {
+ val m = mc.method
+ E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () =>
+ xs.endsWith(ys)
+ }
+ }
+
+ val GetMethod = SMethod(this, "get",
+ SFunc(Array(ThisType, SInt), SOption(tIV), Array[STypeParam](tIV)), 34, ByIndex.costKind)
+ .withIRInfo(MethodCallIrBuilder)
+ .withInfo(MethodCall,
+ "Returns Some(element) if there is an element at given index, None otherwise.",
+ ArgInfo("index", "Index of an element (starting from 0).")
+ )
+
+ private val v5Methods = super.getMethods() ++ Seq(
+ SizeMethod,
+ GetOrElseMethod,
+ MapMethod,
+ ExistsMethod,
+ FoldMethod,
+ ForallMethod,
+ SliceMethod,
+ FilterMethod,
+ AppendMethod,
+ ApplyMethod,
+ IndicesMethod,
+ FlatMapMethod,
+ PatchMethod,
+ UpdatedMethod,
+ UpdateManyMethod,
+ IndexOfMethod,
+ ZipMethod
+ )
+
+ private val v6Methods = v5Methods ++ Seq(
+ ReverseMethod,
+ DistinctMethod,
+ StartsWithMethod,
+ EndsWithMethod,
+ GetMethod
+ )
+
/** This method should be overriden in derived classes to add new methods in addition to inherited.
* Typical override: `super.getMethods() ++ Seq(m1, m2, m3)`
*/
- override protected def getMethods(): Seq[SMethod] = super.getMethods() ++
- Seq(
- SizeMethod,
- GetOrElseMethod,
- MapMethod,
- ExistsMethod,
- FoldMethod,
- ForallMethod,
- SliceMethod,
- FilterMethod,
- AppendMethod,
- ApplyMethod,
- IndicesMethod,
- FlatMapMethod,
- PatchMethod,
- UpdatedMethod,
- UpdateManyMethod,
- IndexOfMethod,
- ZipMethod
- )
+ override protected def getMethods(): Seq[SMethod] = {
+ if (VersionContext.current.isV6SoftForkActivated) {
+ v6Methods
+ } else {
+ v5Methods
+ }
+ }
+
}
object STupleMethods extends MethodsContainer {
@@ -1573,16 +1790,52 @@ case object SContextMethods extends MonoTypeMethods {
lazy val selfBoxIndexMethod = propertyCall("selfBoxIndex", SInt, 8, FixedCost(JitCost(20)))
lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash)
lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey)
- lazy val getVarMethod = SMethod(
+
+ lazy val getVarV5Method = SMethod(
this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind)
.withInfo(GetVar, "Get context variable with given \\lst{varId} and type.",
ArgInfo("varId", "\\lst{Byte} identifier of context variable"))
- protected override def getMethods() = super.getMethods() ++ Seq(
+ lazy val getVarV6Method = SMethod(
+ this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind, Seq(tT))
+ .withIRInfo(
+ MethodCallIrBuilder,
+ javaMethodOf[Context, Byte, RType[_]]("getVar"),
+ { mtype => Array(mtype.tRange.asOption[SType].elemType) })
+ .withInfo(MethodCall, "Get context variable with given \\lst{varId} and type.")
+
+ lazy val getVarFromInputMethod = SMethod(
+ this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT))
+ .withIRInfo(
+ MethodCallIrBuilder,
+ javaMethodOf[Context, Short, Byte, RType[_]]("getVarFromInput"),
+ { mtype => Array(mtype.tRange.asOption[SType].elemType) })
+ .withInfo(MethodCall, "Get context variable with given \\lst{varId} and type.",
+ ArgInfo("inputIdx", "Index of input to read variable from."),
+ ArgInfo("varId", "Index of variable.")
+ )
+
+ private lazy val commonMethods = super.getMethods() ++ Array(
dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod,
- selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod
+ selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod
)
+ private lazy val v5Methods = commonMethods ++ Seq(
+ getVarV5Method
+ )
+
+ private lazy val v6Methods = commonMethods ++ Seq(
+ getVarV6Method, getVarFromInputMethod
+ )
+
+ protected override def getMethods(): Seq[SMethod] = {
+ if (VersionContext.current.isV6SoftForkActivated) {
+ v6Methods
+ } else {
+ v5Methods
+ }
+ }
+
/** Names of methods which provide blockchain context.
* This value can be reused where necessary to avoid allocations. */
val BlockchainContextMethodNames: IndexedSeq[String] = Array(
@@ -1690,9 +1943,61 @@ case object SGlobalMethods extends MonoTypeMethods {
Xor.xorWithCosting(ls, rs)
}
- protected override def getMethods() = super.getMethods() ++ Seq(
- groupGeneratorMethod,
- xorMethod
- )
+ private val BigEndianBytesCostKind = FixedCost(JitCost(10))
+
+ // id = 4 is reserved for deserializeTo ()
+ 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"),
+ { mtype => Array(mtype.tRange) })
+ .withInfo(MethodCall,
+ "Decode a number from big endian bytes.",
+ ArgInfo("first", "Bytes which are big-endian encoded number."))
+
+ lazy val serializeMethod = SMethod(this, "serialize",
+ SFunc(Array(SGlobal, tT), SByteArray, Array(paramT)), 3, DynamicCost)
+ .withIRInfo(MethodCallIrBuilder)
+ .withInfo(MethodCall, "Serializes the given `value` into bytes using the default serialization format.",
+ ArgInfo("value", "value to be serialized"))
+
+
+ /** Implements evaluation of Global.serialize method call ErgoTree node.
+ * Called via reflection based on naming convention.
+ * @see SMethod.evalMethod
+ */
+ def serialize_eval(mc: MethodCall, G: SigmaDslBuilder, value: SType#WrappedType)
+ (implicit E: ErgoTreeEvaluator): Coll[Byte] = {
+
+ E.addCost(SigmaByteWriter.StartWriterCost)
+
+ val addFixedCostCallback = { (costInfo: OperationCostInfo[FixedCost]) =>
+ E.addCost(costInfo)
+ }
+ val addPerItemCostCallback = { (info: OperationCostInfo[PerItemCost], nItems: Int) =>
+ E.addSeqCostNoOp(info.costKind, nItems, info.opDesc)
+ }
+ val w = SigmaSerializer.startWriter(None,
+ Some(addFixedCostCallback), Some(addPerItemCostCallback))
+
+ DataSerializer.serialize(value, mc.args(0).tpe, w)
+ Colls.fromArray(w.toBytes)
+ }
+
+ protected override def getMethods() = super.getMethods() ++ {
+ if (VersionContext.current.isV6SoftForkActivated) {
+ Seq(
+ groupGeneratorMethod,
+ xorMethod,
+ serializeMethod,
+ FromBigEndianBytesMethod
+ )
+ } else {
+ Seq(
+ groupGeneratorMethod,
+ xorMethod
+ )
+ }
+ }
}
diff --git a/data/shared/src/main/scala/sigma/ast/transformers.scala b/data/shared/src/main/scala/sigma/ast/transformers.scala
index 939da79d98..8d7e689a18 100644
--- a/data/shared/src/main/scala/sigma/ast/transformers.scala
+++ b/data/shared/src/main/scala/sigma/ast/transformers.scala
@@ -10,7 +10,7 @@ import sigma.eval.ErgoTreeEvaluator.DataEnv
import sigma.serialization.CoreByteWriter.ArgInfo
import sigma.serialization.OpCodes
import sigma.serialization.ValueCodes.OpCode
-import sigma.{Box, Coll, Evaluation}
+import sigma.{Box, Coll, Evaluation, VersionContext}
// TODO refactor: remove this trait as it doesn't have semantic meaning
@@ -258,10 +258,22 @@ case class ByIndex[V <: SType](input: Value[SCollection[V]],
val indexV = index.evalTo[Int](env)
default match {
case Some(d) =>
- val dV = d.evalTo[V#WrappedType](env)
- Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased
- addCost(ByIndex.costKind)
- inputV.getOrElse(indexV, dV)
+ if (VersionContext.current.isV6SoftForkActivated) {
+ // lazy evaluation of default in 6.0
+ addCost(ByIndex.costKind)
+ if (inputV.isDefinedAt(indexV)) {
+ inputV.apply(indexV)
+ } else {
+ val dV = d.evalTo[V#WrappedType](env)
+ Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased
+ inputV.getOrElse(indexV, dV)
+ }
+ } else {
+ val dV = d.evalTo[V#WrappedType](env)
+ Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased
+ addCost(ByIndex.costKind)
+ inputV.getOrElse(indexV, dV)
+ }
case _ =>
addCost(ByIndex.costKind)
inputV.apply(indexV)
@@ -613,11 +625,22 @@ case class OptionGetOrElse[V <: SType](input: Value[SOption[V]], default: Value[
override val opType = SFunc(IndexedSeq(input.tpe, tpe), tpe)
override def tpe: V = input.tpe.elemType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
- val inputV = input.evalTo[Option[V#WrappedType]](env)
- val dV = default.evalTo[V#WrappedType](env) // TODO v6.0: execute lazily (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/906)
- Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased
- addCost(OptionGetOrElse.costKind)
- inputV.getOrElse(dV)
+ if(VersionContext.current.isV6SoftForkActivated) {
+ // lazy evaluation of default in 6.0
+ val inputV = input.evalTo[Option[V#WrappedType]](env)
+ addCost(OptionGetOrElse.costKind)
+ inputV.getOrElse {
+ val dV = default.evalTo[V#WrappedType](env)
+ Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased
+ dV
+ }
+ } else {
+ val inputV = input.evalTo[Option[V#WrappedType]](env)
+ val dV = default.evalTo[V#WrappedType](env)
+ Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased
+ addCost(OptionGetOrElse.costKind)
+ inputV.getOrElse(dV)
+ }
}
}
object OptionGetOrElse extends ValueCompanion with FixedCostValueCompanion {
diff --git a/data/shared/src/main/scala/sigma/ast/trees.scala b/data/shared/src/main/scala/sigma/ast/trees.scala
index 39e666a389..33567868fd 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}
@@ -652,7 +653,7 @@ case class SubstConstants[T <: SType](scriptBytes: Value[SByteArray], positions:
val (newBytes, nConstants) = SubstConstants.eval(
scriptBytes = scriptBytesV.toArray,
positions = positionsV.toArray,
- newVals = typedNewVals)(SigmaDsl.validationSettings)
+ newVals = typedNewVals)
res = Colls.fromArray(newBytes)
nConstants
@@ -683,7 +684,7 @@ object SubstConstants extends ValueCompanion {
*/
def eval(scriptBytes: Array[Byte],
positions: Array[Int],
- newVals: Array[Constant[SType]])(implicit vs: SigmaValidationSettings): (Array[Byte], Int) =
+ newVals: Array[Constant[SType]]): (Array[Byte], Int) =
ErgoTreeSerializer.DefaultSerializer.substituteConstants(scriptBytes, positions, newVals)
}
@@ -875,7 +876,8 @@ object ArithOp {
SShort -> new OperationImpl(ShortIsExactIntegral, ShortIsExactOrdering, SShort),
SInt -> new OperationImpl(IntIsExactIntegral, IntIsExactOrdering, SInt),
SLong -> new OperationImpl(LongIsExactIntegral, LongIsExactOrdering, SLong),
- SBigInt -> new OperationImpl(BigIntIsExactIntegral, BigIntIsExactOrdering, SBigInt)
+ SBigInt -> new OperationImpl(BigIntIsExactIntegral, BigIntIsExactOrdering, SBigInt),
+ SUnsignedBigInt -> new OperationImpl(UnsignedBigIntIsExactIntegral, UnsignedBigIntIsExactOrdering, SUnsignedBigInt)
).map { case (t, n) => (t.typeCode, n) })
/** Returns operation name for the given opCode. */
diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala
index 87c661a00a..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)
@@ -1298,6 +1312,10 @@ case class MethodCall(
method: SMethod,
args: IndexedSeq[Value[SType]],
typeSubst: Map[STypeVar, SType]) extends Value[SType] {
+
+ require(method.explicitTypeArgs.forall(tyArg => typeSubst.contains(tyArg)),
+ s"Generic method call should have concrete type for each explicit type parameter, but was: $this")
+
override def companion = if (args.isEmpty) PropertyCall else MethodCall
override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe)
diff --git a/data/shared/src/main/scala/sigma/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 3938feacd3..89fcffb531 100644
--- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala
+++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala
@@ -4,14 +4,17 @@ import debox.cfor
import org.ergoplatform.ErgoBox
import org.ergoplatform.validation.ValidationRules
import scorex.crypto.hash.{Blake2b256, Sha256}
+import scorex.utils.{Ints, Longs}
+import sigma.ast.{AtLeast, SBigInt, SType, SUnsignedBigInt, SubstConstants}
import scorex.utils.Longs
-import sigma.ast.{AtLeast, SubstConstants}
-import sigma.crypto.{CryptoConstants, EcPointType, Ecp}
+import sigma.crypto.{BigIntegers, CryptoConstants, EcPointType, Ecp}
import sigma.eval.Extensions.EvalCollOps
-import sigma.serialization.{GroupElementSerializer, SigmaSerializer}
+import sigma.serialization.{DataSerializer, GroupElementSerializer, SigmaSerializer}
+import sigma.serialization.{GroupElementSerializer, SerializerException, SigmaSerializer}
import sigma.util.Extensions.BigIntegerOps
import sigma.validation.SigmaValidationSettings
-import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext}
+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
@@ -20,12 +23,13 @@ import java.math.BigInteger
* @see [[SigmaDslBuilder]] for detailed descriptions
*/
class CSigmaDslBuilder extends SigmaDslBuilder { dsl =>
- implicit val validationSettings: SigmaValidationSettings = ValidationRules.currentSettings
override val Colls: CollBuilder = sigma.Colls
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. */
@@ -149,7 +153,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)
}
@@ -191,7 +195,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)
}
@@ -200,6 +204,52 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl =>
val p = GroupElementSerializer.parse(r)
this.GroupElement(p)
}
+
+ override def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T = {
+ cT match {
+ case sigma.ByteType => if (bytes.length != 1) {
+ throw new IllegalArgumentException("To deserialize SByte with fromBigEndianBytes, exactly one byte should be provided")
+ } else {
+ bytes.apply(0).asInstanceOf[T]
+ }
+ case sigma.ShortType => if (bytes.length != 2) {
+ throw new IllegalArgumentException("To deserialize SShort with fromBigEndianBytes, exactly two bytes should be provided")
+ } else {
+ val b0 = bytes(0)
+ val b1 = bytes(1)
+ ((b0 & 0xFF) << 8 | (b1 & 0xFF)).toShort.asInstanceOf[T]
+ }
+ case sigma.IntType => if (bytes.length != 4) {
+ throw new IllegalArgumentException("To deserialize SInt with fromBigEndianBytes, exactly four bytes should be provided")
+ } else {
+ Ints.fromByteArray(bytes.toArray).asInstanceOf[T]
+ }
+ case sigma.LongType => if (bytes.length != 8) {
+ throw new IllegalArgumentException("To deserialize SLong with fromBigEndianBytes, exactly eight bytes should be provided")
+ } else {
+ Longs.fromByteArray(bytes.toArray).asInstanceOf[T]
+ }
+ case sigma.BigIntRType =>
+ if (bytes.length > SBigInt.MaxSizeInBytes) {
+ throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes")
+ }
+ CBigInt(new BigInteger(bytes.toArray).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")
+ }
+ }
+
+ /** Serializes the given `value` into bytes using the default serialization format. */
+ override def serialize[T](value: T)(implicit cT: RType[T]): Coll[Byte] = {
+ val tpe = Evaluation.rtypeToSType(cT)
+ val w = SigmaSerializer.startWriter()
+ DataSerializer.serialize(value.asInstanceOf[SType#WrappedType], tpe, w)
+ Colls.fromArray(w.toBytes)
+ }
}
/** Default singleton instance of Global object, which implements global ErgoTree functions. */
diff --git a/data/shared/src/main/scala/sigma/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..bf3dd3e33b
--- /dev/null
+++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala
@@ -0,0 +1,163 @@
+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 BigInt.
+ *
+ * Note: ExactIntegral was not defined for [[sigma.BigInt]] in v4.x.
+ * This is because arithmetic BigInt operations were handled in a special way
+ * (see `case op: ArithOp[t] if op.tpe == SBigInt =>` in RuntimeCosting.scala).
+ * As result [[scalan.primitives.UnBinOps.ApplyBinOp]] nodes were not created for
+ * BigInt operations in v4.x., and hence operation descriptors such as
+ * [[scalan.primitives.NumericOps.IntegralDivide]] and
+ * [[scalan.primitives.NumericOps.IntegralMod]] were not used for BigInt.
+ * NOTE: this instance is used in the new v5.0 interpreter.
+ */
+ object UnsignedBigIntIsIntegral extends UnsignedBigIntIsIntegral with UnsignedBigIntOrdering {
+ def parseString(str: String): Option[UnsignedBigInt] = ???
+ }
+
+ /** The instance of [[ExactIntegral]] typeclass for [[BigInt]]. */
+ implicit object UnsignedBigIntIsExactIntegral extends ExactIntegral[UnsignedBigInt] {
+ val n = UnsignedBigIntIsIntegral
+ override def plus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = n.plus(x, y)
+ override def minus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = n.minus(x, y)
+ override def times(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = n.times(x, y)
+
+ override def quot(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.divide(y)
+
+ /** This method is used in ErgoTreeEvaluator based interpreter, to implement
+ * '%' operation of ErgoTree (i.e. `%: (T, T) => T` operation) for all
+ * numeric types T including BigInt.
+ *
+ * In the v4.x interpreter, however, the `%` operation is implemented using
+ * [[CBigInt]].mod method, which delegates to [[java.math.BigInteger]].mod method.
+ *
+ * Even though this method is called `divisionRemainder`, the semantics of ErgoTree
+ * language requires it to correspond to [[java.math.BigInteger]].mod method.
+ *
+ * For this reason we define implementation of this method using [[BigInt]].mod.
+ *
+ * NOTE: This method should not be used in v4.x
+ */
+ override def divisionRemainder(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.mod(y)
+
+ /** 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/interpreter/ContextExtension.scala b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala
index e8cdb7d709..f03d076d43 100644
--- a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala
+++ b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala
@@ -16,8 +16,15 @@ import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer}
* @param values internal container of the key-value pairs
*/
case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ <: SType]]) {
- def add(bindings: VarBinding*): ContextExtension =
+ def add(bindings: VarBinding*): ContextExtension = {
ContextExtension(values ++ bindings)
+ }
+
+ /**
+ * @param varId - index of context variable
+ * @return context variable with provided index or None if it is not there
+ */
+ def get(varId: Byte): Option[EvaluatedValue[_ <: SType]] = values.get(varId)
}
object ContextExtension {
diff --git a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala
index e7bb46429a..5122ee940c 100644
--- a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala
+++ b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala
@@ -373,7 +373,7 @@ class ErgoTreeSerializer {
val newVal = newVals(positions.indexOf(i))
// we need to get newVal's serialized constant value (see ProveDlogSerializer for example)
val constantStore = new ConstantStore()
- val valW = SigmaSerializer.startWriter(constantStore)
+ val valW = SigmaSerializer.startWriter(Some(constantStore))
valW.putValue(newVal)
val newConsts = constantStore.getAll
require(newConsts.length == 1)
diff --git a/data/shared/src/main/scala/sigma/serialization/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/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala
index 35d5e0c9b9..db9312240f 100644
--- a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala
+++ b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala
@@ -4,15 +4,52 @@ import scorex.util.serialization.Writer
import sigma.ast.syntax._
import sigma.ast._
import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo, FormatDescriptor, SeqFmt}
+import SigmaByteWriter._
-class SigmaByteWriter(override val w: Writer,
- val constantExtractionStore: Option[ConstantStore])
- extends CoreByteWriter(w) {
+/** Implementation of [[Writer]] provided by `sigma-data` module.
+ *
+ * @param w destination [[Writer]] to which all the call got delegated.
+ * @param constantExtractionStore optional store to segregate constants to while
+ * replacing them with placeholders.
+ * @param addFixedCostCallbackOpt optional callback to accumulate fixed costs.
+ * @param addPerItemCostCallbackOpt optional callback to accumulate per-item costs.
+ */
+class SigmaByteWriter(
+ override val w: Writer,
+ val constantExtractionStore: Option[ConstantStore],
+ val addFixedCostCallbackOpt: Option[FixedCostCallback],
+ val addPerItemCostCallbackOpt: Option[PerItemCostCallback]
+) extends CoreByteWriter(w) {
import CoreByteWriter._
import ValueSerializer._
+ /** Adds the given cost to the callback if it is defined. */
+ @inline private def addFixedCost(cost: OperationCostInfo[FixedCost]): Unit = {
+ if (addFixedCostCallbackOpt.isDefined)
+ addFixedCostCallbackOpt.get(cost)
+ }
+
+ /** Adds the given cost to the callback if it is defined. */
+ @inline private def addPerItemCost(cost: OperationCostInfo[PerItemCost], nItems: Int): Unit = {
+ if (addPerItemCostCallbackOpt.isDefined)
+ addPerItemCostCallbackOpt.get(cost, nItems)
+ }
+
+ override def putChunk(chunk: w.CH): SigmaByteWriter.this.type = {
+ val start = length()
+ super.putChunk(chunk)
+ addPerItemCost(PutChunkCost, length() - start)
+ this
+ }
+
+ override def put(x: Byte): this.type = {
+ addFixedCost(PutByteCost)
+ super.put(x)
+ }
+
override def put(x: Byte, info: DataInfo[Byte]): this.type = {
ValueSerializer.addArgInfo(info)
+ addFixedCost(PutByteCost)
w.put(x); this
}
@@ -21,68 +58,160 @@ class SigmaByteWriter(override val w: Writer,
super.putUByte(x)
}
- @inline override def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = {
+ override def putBoolean(x: Boolean): this.type = {
+ addFixedCost(PutByteCost)
+ super.putBoolean(x)
+ }
+
+ override def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = {
ValueSerializer.addArgInfo(info)
+ addFixedCost(PutByteCost)
w.putBoolean(x); this
}
- @inline override def putShort(x: Short, info: DataInfo[Short]): this.type = {
+ override def putShort(x: Short): this.type = {
+ addFixedCost(PutSignedNumericCost)
+ super.putShort(x)
+ }
+
+ override def putShort(x: Short, info: DataInfo[Short]): this.type = {
ValueSerializer.addArgInfo(info)
+ addFixedCost(PutSignedNumericCost)
w.putShort(x); this
}
- @inline override def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = {
+ override def putUShort(x: Int): this.type = {
+ addFixedCost(PutUnsignedNumericCost)
+ super.putUShort(x)
+ }
+
+ override def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = {
ValueSerializer.addArgInfo(info)
+ addFixedCost(PutUnsignedNumericCost)
w.putUShort(x); this
}
- @inline override def putInt(x: Int, info: DataInfo[Int]): this.type = {
+ override def putInt(x: Int): this.type = {
+ addFixedCost(PutSignedNumericCost)
+ super.putInt(x)
+ }
+
+ override def putInt(x: Int, info: DataInfo[Int]): this.type = {
ValueSerializer.addArgInfo(info)
+ addFixedCost(PutSignedNumericCost)
w.putInt(x); this
}
- @inline override def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = {
+ override def putUInt(x: Long): SigmaByteWriter.this.type = {
+ super.putUInt(x)
+ }
+
+ override def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = {
ValueSerializer.addArgInfo(info)
+ addFixedCost(PutUnsignedNumericCost)
w.putUInt(x); this
}
- @inline override def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = {
+ override def putLong(x: Long): SigmaByteWriter.this.type = {
+ addFixedCost(PutSignedNumericCost)
+ super.putLong(x)
+ }
+
+ override def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = {
ValueSerializer.addArgInfo(info)
+ addFixedCost(PutSignedNumericCost)
w.putLong(x); this
}
- @inline override def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = {
+ override def putULong(x: Long): SigmaByteWriter.this.type = {
+ addFixedCost(PutUnsignedNumericCost)
+ super.putULong(x)
+ }
+
+ override def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = {
ValueSerializer.addArgInfo(info)
+ addFixedCost(PutUnsignedNumericCost)
w.putULong(x); this
}
- @inline override def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = {
+ override def putBytes(xs: Array[Byte], offset: Int, length: Int): this.type = {
+ addPerItemCost(PutChunkCost, length)
+ super.putBytes(xs, offset, length)
+ }
+
+ override def putBytes(xs: Array[Byte]): SigmaByteWriter.this.type = {
+ addPerItemCost(PutChunkCost, xs.length)
+ super.putBytes(xs)
+ }
+
+ override def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = {
ValueSerializer.addArgInfo(info)
+ addPerItemCost(PutChunkCost, xs.length)
w.putBytes(xs); this
}
- @inline override def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = {
+ /** Put the two bytes of the big-endian representation of the Short value into the
+ * writer. */
+ override def putShortBytes(value: Short): SigmaByteWriter.this.type = {
+ addPerItemCost(PutChunkCost, 2)
+ super.putShortBytes(value)
+ }
+
+ override def putBits(xs: Array[Boolean]): SigmaByteWriter.this.type = {
+ addPerItemCost(PutChunkCost, xs.length) // number of bits
+ super.putBits(xs)
+ }
+
+ override def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = {
ValueSerializer.addArgInfo(info)
- w.putBits(xs);
- this
+ addPerItemCost(PutChunkCost, xs.length) // number of bits
+ w.putBits(xs); this
}
- @inline override def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = {
+ override def putOption[T](x: Option[T])(putValueC: (this.type, T) => Unit): this.type = {
+ addFixedCost(PutByteCost) // cost of option tag byte
+ super.putOption(x)(putValueC)
+ }
+
+ override def putShortString(s: String): SigmaByteWriter.this.type = {
+ addPerItemCost(PutChunkCost, s.length)
+ super.putShortString(s)
+ }
+
+ override def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = {
ValueSerializer.addArgInfo(info)
- TypeSerializer.serialize(x, this); this
+ TypeSerializer.serialize(x, this); // the cost is added in TypeSerializer
+ this
}
- @inline def putValue[T <: SType](x: Value[T]): this.type = { ValueSerializer.serialize(x, this); this }
- @inline def putValue[T <: SType](x: Value[T], info: DataInfo[SValue]): this.type = {
+ /** Serializes the given expression using [[ValueSerializer]]. */
+ def putValue[T <: SType](x: Value[T]): this.type = {
+ ValueSerializer.serialize(x, this) // the cost is added in ValueSerializer
+ this
+ }
+
+ /** Serializes the given expression using [[ValueSerializer]].
+ * @param x the ErgoTree expression to serialize
+ * @param info meta information about the data being serialized
+ */
+ def putValue[T <: SType](x: Value[T], info: DataInfo[SValue]): this.type = {
ValueSerializer.addArgInfo(info)
- ValueSerializer.serialize(x, this); this
+ ValueSerializer.serialize(x, this); // the cost is added in ValueSerializer
+ this
}
- @inline def putValues[T <: SType](xs: Seq[Value[T]]): this.type = {
+
+ /** Serializes the given sequence of expressions using [[ValueSerializer]]. */
+ def putValues[T <: SType](xs: Seq[Value[T]]): this.type = {
putUInt(xs.length)
xs.foreach(putValue(_))
this
}
- @inline def putValues[T <: SType](xs: Seq[Value[T]], info: DataInfo[Seq[SValue]], itemInfo: DataInfo[SValue]): this.type = {
+
+ /** Serializes the given sequence of expressions using [[ValueSerializer]].
+ * @param xs the sequence of ErgoTree expressions to serialize
+ * @param info additional information about the data being serialized
+ */
+ def putValues[T <: SType](xs: Seq[Value[T]], info: DataInfo[Seq[SValue]], itemInfo: DataInfo[SValue]): this.type = {
putUInt(xs.length, valuesLengthInfo)
foreach("\\#items", xs) { x =>
putValue(x, itemInfo)
@@ -92,6 +221,46 @@ class SigmaByteWriter(override val w: Writer,
}
object SigmaByteWriter {
+
+ /** Callback to accumulate fixed costs. */
+ type FixedCostCallback = OperationCostInfo[FixedCost] => Unit
+
+ /** Callback to accumulate per-item costs (chunked cost). */
+ type PerItemCostCallback = (OperationCostInfo[PerItemCost], Int) => Unit
+
+ /** Cost of instantiating a new serializer.
+ * This also include overhead of method calls.
+ * This is the minimal possible JitCost value
+ */
+ val StartWriterCost = OperationCostInfo(FixedCost(JitCost(10)), NamedDesc("SigmaByteWriter.startWriter"))
+
+ /** Cost of writing single byte without any encoding.
+ * This also include overhead of method calls.
+ * This is the minimal possible JitCost value
+ */
+ val PutByteCost = OperationCostInfo(FixedCost(JitCost(1)), NamedDesc("SigmaByteWriter.put"))
+
+ /** Cost of writing a signed numeric including:
+ * 1) allocation of VLQ buffer array (see putULong in [[scorex.util.serialization.VLQWriter]])
+ * 2) VLQ encoding
+ * 3) overhead of method calls.
+ */
+ val PutUnsignedNumericCost = OperationCostInfo(FixedCost(JitCost(3)), NamedDesc("SigmaByteWriter.putUNumeric"))
+
+ /** Cost of writing a signed numeric including:
+ * 1) ZigZag encoding.
+ * 2) allocation of VLQ buffer array (see putULong in [[scorex.util.serialization.VLQWriter]])
+ * 3) VLQ encoding
+ * 4) overhead of method calls.
+ */
+ val PutSignedNumericCost = OperationCostInfo(FixedCost(JitCost(3)), NamedDesc("SigmaByteWriter.putNumeric"))
+
+ /** Cost of writing a chunk of bytes:
+ * 1) method call overhead
+ * 2) 1 cost unit per byte
+ */
+ val PutChunkCost = OperationCostInfo(PerItemCost(JitCost(3), JitCost(1), 1), NamedDesc("SigmaByteWriter.putChunk"))
+
implicit case object ValueFmt extends FormatDescriptor[SValue] {
override def size: String = "[1, *]"
override def toString: String = "Expr"
diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala
index 3765adb029..7da7ec1606 100644
--- a/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala
+++ b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala
@@ -4,6 +4,7 @@ import java.nio.ByteBuffer
import scorex.util.ByteArrayBuilder
import scorex.util.serialization._
import sigma.data.SigmaConstants
+import sigma.serialization.SigmaByteWriter.{FixedCostCallback, PerItemCostCallback}
import sigma.serialization.ValueCodes.OpCode
object SigmaSerializer {
@@ -51,14 +52,18 @@ object SigmaSerializer {
def startWriter(): SigmaByteWriter = {
val b = new ByteArrayBuilder()
val wi = new VLQByteBufferWriter(b)
- val w = new SigmaByteWriter(wi, constantExtractionStore = None)
+ val w = new SigmaByteWriter(wi, constantExtractionStore = None, addFixedCostCallbackOpt = None, addPerItemCostCallbackOpt = None)
w
}
- def startWriter(constantExtractionStore: ConstantStore): SigmaByteWriter = {
+ def startWriter(
+ constantExtractionStore: Option[ConstantStore],
+ addFixedCostCallback: Option[FixedCostCallback] = None,
+ addPerItemCostCallback: Option[PerItemCostCallback] = None
+ ): SigmaByteWriter = {
val b = new ByteArrayBuilder()
val wi = new VLQByteBufferWriter(b)
- val w = new SigmaByteWriter(wi, constantExtractionStore = Some(constantExtractionStore))
+ val w = new SigmaByteWriter(wi, constantExtractionStore = constantExtractionStore, addFixedCostCallback, addPerItemCostCallback)
w
}
}
diff --git a/docs/LangSpec.md b/docs/LangSpec.md
index 1f05a3b403..16defff5ff 100644
--- a/docs/LangSpec.md
+++ b/docs/LangSpec.md
@@ -68,7 +68,7 @@ The following sections describe ErgoScript and its operations.
#### Operations and constructs overview
- Binary operations: `>, <, >=, <=, +, -, &&, ||, ==, !=, |, &, *, /, %, ^, ++`
-- predefined primitives: `blake2b256`, `byteArrayToBigInt`, `proveDlog` etc.
+- predefined primitives: `serialize`, `blake2b256`, `byteArrayToBigInt`, `proveDlog` etc.
- val declarations: `val h = blake2b256(pubkey)`
- if-then-else clause: `if (x > 0) 1 else 0`
- collection literals: `Coll(1, 2, 3, 4)`
@@ -919,7 +919,7 @@ def longToByteArray(input: Long): Coll[Byte]
def decodePoint(bytes: Coll[Byte]): GroupElement
-/** Extracts Context variable by id and type.
+/** Extracts Context variable from SELF input by id and type.
* ErgoScript is typed, so accessing a the variables is an operation which involves
* some expected type given in brackets. Thus `getVar[Int](id)` expression should
* evaluate to a valid value of the `Option[Int]` type.
@@ -976,6 +976,18 @@ def decodePoint(bytes: Coll[Byte]): GroupElement
*/
def getVar[T](tag: Int): Option[T]
+/** Extracts Context variable from any input by input index, variable id and variable type.
+ * Unlike getVar, it is not throwing exception when expected type does not match real type of the variable.
+ * Thus it can be used to get context variable from self without exception, using selfBoxIndex, e.g.
+ *
+ * { + * val idx = CONTEXT.selfBoxIndex + * sigmaProp(CONTEXT.getVarFromInput[Int](idx.toShort, 1.toByte).get == 5) + * } + *+ */ +def getVarFromInput[T](inputId: Short, varId: Byte): Option[T] + /** Construct a new SigmaProp value representing public key of Diffie Hellman * signature protocol. When executed as part of Sigma protocol allow to provide * for a verifier a zero-knowledge proof of secret knowledge. diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala index e421e8fdfe..4b1366d9fd 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala @@ -168,7 +168,7 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData, syntax.error(s"Undefined context property: currentErgoTreeVersion")) CContext( dataInputs, headers, preHeader, inputs, outputs, preHeader.height, selfBox, selfIndex, avlTree, - preHeader.minerPk.getEncoded, vars, activatedScriptVersion, ergoTreeVersion) + preHeader.minerPk.getEncoded, vars, spendingTransaction, activatedScriptVersion, ergoTreeVersion) } diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala index 2b076403ad..b0e5b01186 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala @@ -1,8 +1,11 @@ package sigmastate.eval import debox.cfor +import org.ergoplatform.{ErgoLikeTransactionTemplate, UnsignedInput} +import sigma.Evaluation.stypeToRType import sigma.Extensions.ArrayOps import sigma._ +import sigma.ast.SType import sigma.data._ import sigma.exceptions.InvalidType @@ -24,6 +27,7 @@ case class CContext( lastBlockUtxoRootHash: AvlTree, _minerPubKey: Coll[Byte], vars: Coll[AnyValue], + spendingTransaction: ErgoLikeTransactionTemplate[_ <: UnsignedInput], override val activatedScriptVersion: Byte, override val currentErgoTreeVersion: Byte ) extends Context { @@ -69,6 +73,14 @@ case class CContext( } else None } + override def getVarFromInput[T](inputIndex: Short, id: Byte)(implicit tT: RType[T]): Option[T] = { + spendingTransaction.inputs.lift(inputIndex).flatMap(_.extension.get(id)) match { + case Some(v) if stypeToRType[SType](v.tpe) == tT => Some(v.value.asInstanceOf[T]) + case _ => + None + } + } + /** Return a new context instance with variables collection updated. * @param bindings a new binding of the context variables with new values. * @return a new instance (if `bindings` non-empty) with the specified bindings. diff --git a/interpreter/shared/src/test/scala/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/MethodCallSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala index da88a944d4..793d3df959 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala @@ -1,6 +1,7 @@ package sigma.serialization import sigma.VersionContext +import sigma.ast.SCollection.SByteArray import sigma.ast._ import sigma.validation.ValidationException @@ -46,4 +47,25 @@ class MethodCallSerializerSpecification extends SerializationSpecification { ) } + property("MethodCall deserialization round trip for Global.serialize") { + def code = { + val b = ByteArrayConstant(Array(1.toByte, 2.toByte, 3.toByte)) + val expr = MethodCall(Global, + SGlobalMethods.serializeMethod.withConcreteTypes(Map(STypeVar("T") -> SByteArray)), + Vector(b), + Map() + ) + roundTripTest(expr) + } + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + code + } + + an[Exception] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + code + }) + } + } diff --git a/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..9a4668d8e0 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala @@ -83,6 +83,7 @@ trait ObjectGenerators extends TypeGenerators implicit lazy val arbRegisterIdentifier: Arbitrary[RegisterId] = Arbitrary(registerIdentifierGen) implicit lazy val arbBigInteger: Arbitrary[BigInteger] = Arbitrary(Arbitrary.arbBigInt.arbitrary.map(_.bigInteger)) implicit lazy val arbBigInt: Arbitrary[BigInt] = Arbitrary(arbBigInteger.arbitrary.map(SigmaDsl.BigInt(_))) + implicit lazy val arbUnsignedBigInt: Arbitrary[UnsignedBigInt] = Arbitrary(arbBigInteger.arbitrary.map(_.abs()).map(SigmaDsl.UnsignedBigInt(_))) implicit lazy val arbEcPointType: Arbitrary[dlogGroup.ElemType] = Arbitrary(Gen.const(()).flatMap(_ => CryptoConstants.dlogGroup.createRandomGenerator())) implicit lazy val arbGroupElement: Arbitrary[GroupElement] = Arbitrary(arbEcPointType.arbitrary.map(SigmaDsl.GroupElement(_))) implicit lazy val arbSigmaBoolean: Arbitrary[SigmaBoolean] = Arbitrary(Gen.oneOf(proveDHTGen, proveDHTGen)) @@ -305,6 +306,7 @@ trait ObjectGenerators extends TypeGenerators case SInt => arbInt case SLong => arbLong case SBigInt => arbBigInt + case SUnsignedBigInt => arbUnsignedBigInt case SGroupElement => arbGroupElement case SSigmaProp => arbSigmaProp case SBox => arbBox diff --git a/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala index 70a215e831..699ef1c8f8 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala @@ -2,6 +2,7 @@ package sigma.serialization.generators import org.scalacheck.{Arbitrary, Gen} import org.scalacheck.Arbitrary.arbString +import sigma.VersionContext import sigma.ast._ trait TypeGenerators { @@ -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,8 @@ trait TypeGenerators { shortTypeGen, intTypeGen, longTypeGen, - bigIntTypeGen + bigIntTypeGen, + unsignedBigIntTypeGen )) } 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/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala index b04e9c150f..ba04df1347 100644 --- a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala +++ b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala @@ -47,7 +47,7 @@ trait ContractsTestkit { new CContext( noInputs.toColl, noHeaders, dummyPreHeader, inputs.toColl, outputs.toColl, height, self, inputs.indexOf(self), tree, - minerPk.toColl, vars.toColl, activatedScriptVersion, currErgoTreeVersion) + minerPk.toColl, vars.toColl, null, activatedScriptVersion, currErgoTreeVersion) def newContext( height: Int, diff --git a/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/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala index dc63330f95..5a0f2b3465 100644 --- a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala @@ -906,6 +906,17 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat ) } + property("serialize") { + checkParsed("serialize(1)", Apply(Ident("serialize", NoType), Array(IntConstant(1)))) + checkParsed("serialize((1, 2L))", + Apply(Ident("serialize", NoType), Array(Tuple(Vector(IntConstant(1), LongConstant(2L)))))) + checkParsed("serialize(Coll(1, 2, 3))", + Apply( + Ident("serialize", NoType), + Array(Apply(Ident("Coll", NoType), Array(IntConstant(1), IntConstant(2), IntConstant(3)))) + )) + } + property("single name pattern fail") { fail("{val (a,b) = (1,2)}", 1, 6) } diff --git a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala index 11cbaff739..2554489340 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala @@ -55,6 +55,7 @@ trait ContractSyntax { contract: SigmaContract => case _: String => StringType case _: Unit => UnitType case _: sigma.BigInt => BigIntRType + case _: sigma.BigInt => UnsignedBigIntRType case _: GroupElement => GroupElementRType case _: ErgoBox => syntax.ErgoBoxRType // TODO remove this RType case _: Box => BoxRType diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 496ad7d20e..13918b08e6 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1,6 +1,7 @@ package sigma.compiler.ir import org.ergoplatform._ +import sigma.ast.SType.tT import sigma.Evaluation.stypeToRType import sigma.ast.SType.tT import sigma.ast.TypeCodes.LastConstantCode @@ -12,11 +13,14 @@ import sigma.crypto.EcPointType 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 sigma.{SigmaException, VersionContext, ast} 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 @@ -31,6 +35,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._ @@ -256,6 +261,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 @@ -282,6 +288,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)) @@ -309,6 +316,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) @@ -329,20 +337,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 */ @@ -452,6 +464,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) @@ -987,6 +1003,19 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val i = asRep[Int](argsV(0)) val d = asRep[t](argsV(1)) xs.getOrElse(i, d) + case SCollectionMethods.ReverseMethod.name => + xs.reverse + case SCollectionMethods.DistinctMethod.name => + xs.distinct + case SCollectionMethods.StartsWithMethod.name => + val ys = asRep[Coll[t]](argsV(0)) + xs.startsWith(ys) + case SCollectionMethods.EndsWithMethod.name => + val ys = asRep[Coll[t]](argsV(0)) + xs.endsWith(ys) + case SCollectionMethods.GetMethod.name => + val idx = asRep[Int](argsV(0)) + xs.get(idx) case _ => throwError() } case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match { @@ -1014,6 +1043,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 { @@ -1046,6 +1078,15 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => ctx.LastBlockUtxoRootHash case SContextMethods.minerPubKeyMethod.name => ctx.minerPubKey + case SContextMethods.getVarV6Method.name => + val c2 = asRep[Byte](argsV(0)) + val c3 = stypeToElem(typeSubst.apply(tT)) + ctx.getVar(c2)(c3) + case SContextMethods.getVarFromInputMethod.name => + val c1 = asRep[Short](argsV(0)) + val c2 = asRep[Byte](argsV(1)) + val c3 = stypeToElem(typeSubst.apply(tT)) + ctx.getVarFromInput(c1, c2)(c3) case _ => throwError() } case (tree: Ref[AvlTree]@unchecked, SAvlTreeMethods) => method.name match { @@ -1154,9 +1195,16 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val c1 = asRep[Coll[Byte]](argsV(0)) val c2 = asRep[Coll[Byte]](argsV(1)) g.xor(c1, c2) + case SGlobalMethods.serializeMethod.name => + val value = asRep[Any](argsV(0)) + g.serialize(value) + case SGlobalMethods.FromBigEndianBytesMethod.name => + val bytes = asRep[Coll[Byte]](argsV(0)) + val cT = stypeToElem(method.stype.tRange.withSubstTypes(typeSubst)) + g.fromBigEndianBytes(bytes)(cT) case _ => throwError() } - case (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) @@ -1186,6 +1234,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 d4512e1297..ea665c247f 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -1,5 +1,6 @@ package sigma.compiler.ir +import sigma.ast.SType import sigma.compiler.ir.primitives.Thunks import sigma.data.RType import sigma.reflection.ReflectionData.registerClassEntry @@ -114,6 +115,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 } ) ) @@ -202,6 +256,22 @@ object GraphIRReflection { }, mkMethod(clazz, "exists", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.Coll[Any]].exists(args(0).asInstanceOf[ctx.Ref[Any => Boolean]]) + }, + // V6 methods + mkMethod(clazz, "reverse", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[ctx.Coll[Any]].reverse + }, + mkMethod(clazz, "distinct", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[ctx.Coll[Any]].distinct + }, + mkMethod(clazz, "startsWith", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Coll[Any]].startsWith(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Any]]]) + }, + mkMethod(clazz, "endsWith", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Coll[Any]].endsWith(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Any]]]) + }, + mkMethod(clazz, "get", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Coll[_]].apply(args(0).asInstanceOf[ctx.Ref[Int]]) } ) ) @@ -332,6 +402,9 @@ object GraphIRReflection { mkMethod(clazz, "getVar", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => obj.asInstanceOf[ctx.Context].getVar(args(0).asInstanceOf[ctx.Ref[Byte]])(args(1).asInstanceOf[ctx.Elem[_]]) }, + mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Context].getVarFromInput(args(0).asInstanceOf[ctx.Ref[Short]], args(1).asInstanceOf[ctx.Ref[Byte]])(args(2).asInstanceOf[ctx.Elem[_]]) + }, mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, args) => obj.asInstanceOf[ctx.Context].headers } @@ -346,6 +419,9 @@ object GraphIRReflection { mkMethod(clazz, "exp", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.GroupElement].exp(args(0).asInstanceOf[ctx.Ref[ctx.BigInt]]) }, + mkMethod(clazz, "expUnsigned", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.GroupElement].expUnsigned(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]]) + }, mkMethod(clazz, "multiply", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.GroupElement].multiply(args(0).asInstanceOf[ctx.Ref[ctx.GroupElement]]) }, @@ -507,6 +583,12 @@ object GraphIRReflection { }, mkMethod(clazz, "decodePoint", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.SigmaDslBuilder].decodePoint(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]]) + }, + mkMethod(clazz, "serialize", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].serialize(args(0).asInstanceOf[ctx.Ref[Any]]) + }, + mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])(args(1).asInstanceOf[ctx.Elem[SType]]) } ) ) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala index aed197843c..37ec47f2dc 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -1,6 +1,9 @@ package sigma.compiler.ir import org.ergoplatform._ +import sigma.VersionContext +import sigma.Evaluation.{rtypeToSType, stypeToRType} +import sigma.ast.SType.tT import sigma.ast._ import sigma.ast.syntax.{ValueOps, _} import sigma.data.{ProveDHTuple, ProveDlog} diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala index 9b4a002a14..fdc9fadcba 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala @@ -14,6 +14,7 @@ import sigma.compiler.ir.{Base, IRContext} implicit def eA: Elem[A]; def length: Ref[Int]; def apply(i: Ref[Int]): Ref[A]; + def get(index: Ref[Int]): Ref[WOption[A]]; def getOrElse(index: Ref[Int], default: Ref[A]): Ref[A]; def map[B](f: Ref[scala.Function1[A, B]]): Ref[Coll[B]]; def zip[B](ys: Ref[Coll[B]]): Ref[Coll[scala.Tuple2[A, B]]]; @@ -29,6 +30,10 @@ import sigma.compiler.ir.{Base, IRContext} def updateMany(indexes: Ref[Coll[Int]], values: Ref[Coll[A]]): Ref[Coll[A]]; def slice(from: Ref[Int], until: Ref[Int]): Ref[Coll[A]]; def append(other: Ref[Coll[A]]): Ref[Coll[A]]; + def reverse: Ref[Coll[A]] + def distinct: Ref[Coll[A]] + def startsWith(ys: Ref[Coll[A]]): Ref[Boolean]; + def endsWith(ys: Ref[Coll[A]]): Ref[Boolean]; }; trait CollBuilder extends Def[CollBuilder] { def fromItems[T](items: Ref[T]*)(implicit cT: Elem[T]): Ref[Coll[T]]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index f38748bbe4..f719bb6f8f 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -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]] @@ -92,6 +109,7 @@ import scalan._ def preHeader: Ref[PreHeader]; def minerPubKey: Ref[Coll[Byte]]; def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]]; + def getVarFromInput[T](inputId: Ref[Short], id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]]; }; trait SigmaDslBuilder extends Def[SigmaDslBuilder] { def Colls: Ref[CollBuilder]; @@ -115,6 +133,8 @@ import scalan._ /** This method will be used in v6.0 to handle CreateAvlTree operation in GraphBuilding */ def avlTree(operationFlags: Ref[Byte], digest: Ref[Coll[Byte]], keyLength: Ref[Int], valueLengthOpt: Ref[WOption[Int]]): Ref[AvlTree]; def xor(l: Ref[Coll[Byte]], r: Ref[Coll[Byte]]): Ref[Coll[Byte]] + def serialize[T](value: Ref[T]): Ref[Coll[Byte]] + def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] }; trait CostModelCompanion; trait BigIntCompanion; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala index 0a18ea586a..04a7070acf 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala @@ -63,6 +63,14 @@ class CollCls extends EntityObject("Coll") { true, false, element[A])) } + override def get(index: Ref[Int]): Ref[WOption[A]] = { + asRep[WOption[A]](mkMethodCall(self, + CollClass.getMethod("get", classOf[Sym]), + Array[AnyRef](index), + true, false, element[WOption[A]])) + } + + override def map[B](f: Ref[A => B]): Ref[Coll[B]] = { implicit val eB = f.elem.eRange asRep[Coll[B]](mkMethodCall(self, @@ -164,6 +172,34 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](other), true, false, element[Coll[A]])) } + + override def reverse: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(self, + CollClass.getMethod("reverse"), + Array[AnyRef](), + true, false, element[Coll[A]])) + } + + override def distinct: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(self, + CollClass.getMethod("distinct"), + Array[AnyRef](), + true, false, element[Coll[A]])) + } + + def startsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(self, + CollClass.getMethod("startsWith", classOf[Sym]), + Array[AnyRef](ys), + true, false, element[Boolean])) + } + + def endsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(self, + CollClass.getMethod("endsWith", classOf[Sym]), + Array[AnyRef](ys), + true, false, element[Boolean])) + } } case class LiftableColl[SA, A](lA: Liftable[SA, A]) @@ -210,6 +246,13 @@ class CollCls extends EntityObject("Coll") { true, true, element[A])) } + def get(index: Ref[Int]): Ref[WOption[A]] = { + asRep[WOption[A]](mkMethodCall(source, + CollClass.getMethod("get", classOf[Sym]), + Array[AnyRef](index), + true, true, element[WOption[A]])) + } + def map[B](f: Ref[A => B]): Ref[Coll[B]] = { implicit val eB = f.elem.eRange asRep[Coll[B]](mkMethodCall(source, @@ -311,6 +354,34 @@ class CollCls extends EntityObject("Coll") { Array[AnyRef](other), true, true, element[Coll[A]])) } + + def reverse: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(source, + CollClass.getMethod("reverse"), + Array[AnyRef](), + true, true, element[Coll[A]])) + } + + def distinct: Ref[Coll[A]] = { + asRep[Coll[A]](mkMethodCall(source, + CollClass.getMethod("distinct"), + Array[AnyRef](), + true, true, element[Coll[A]])) + } + + def startsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(source, + CollClass.getMethod("startsWith", classOf[Sym]), + Array[AnyRef](ys), + true, true, element[Boolean])) + } + + def endsWith(ys: Ref[Coll[A]]): Ref[Boolean] = { + asRep[Boolean](mkMethodCall(source, + CollClass.getMethod("endsWith", classOf[Sym]), + Array[AnyRef](ys), + true, true, element[Boolean])) + } } // entityUnref: single unref method for each type family diff --git a/sc/shared/src/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 5c366d5b7c..4b64b51ab4 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -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]), @@ -1626,10 +1902,19 @@ object Context extends EntityObject("Context") { } override def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) asRep[WOption[T]](mkMethodCall(self, ContextClass.getMethod("getVar", classOf[Sym], classOf[Elem[_]]), Array[AnyRef](id, cT), - true, false, element[WOption[T]])) + true, false, element[WOption[T]], Map(tT -> st))) + } + + override def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) + asRep[WOption[T]](mkMethodCall(self, + ContextClass.getMethod("getVarFromInput", classOf[Sym], classOf[Sym], classOf[Elem[_]]), + Array[AnyRef](inputId, varId, cT), + true, false, element[WOption[T]], Map(tT -> st))) } } @@ -1723,10 +2008,19 @@ object Context extends EntityObject("Context") { } def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) asRep[WOption[T]](mkMethodCall(source, ContextClass.getMethod("getVar", classOf[Sym], classOf[Elem[_]]), Array[AnyRef](id, cT), - true, true, element[WOption[T]])) + true, true, element[WOption[T]], Map(tT -> st))) + } + + def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) + asRep[WOption[T]](mkMethodCall(source, + ContextClass.getMethod("getVarFromInput", classOf[Sym], classOf[Sym], classOf[Elem[_]]), + Array[AnyRef](inputId, varId, cT), + true, true, element[WOption[T]], Map(tT -> st))) } } @@ -1745,7 +2039,7 @@ object Context extends EntityObject("Context") { override protected def collectMethods: Map[RMethod, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[Context]), RClass(classOf[SContext]), Set( - "OUTPUTS", "INPUTS", "dataInputs", "HEIGHT", "SELF", "selfBoxIndex", "LastBlockUtxoRootHash", "headers", "preHeader", "minerPubKey", "getVar", "vars" + "OUTPUTS", "INPUTS", "dataInputs", "HEIGHT", "SELF", "selfBoxIndex", "LastBlockUtxoRootHash", "headers", "preHeader", "minerPubKey", "getVar", "getVarFromInput", "vars" )) } } @@ -1963,6 +2257,21 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { Array[AnyRef](l, r), true, false, element[Coll[Byte]])) } + + def serialize[T](value: Ref[T]): Ref[Coll[Byte]] = { + asRep[Coll[Byte]](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("serialize", classOf[Sym]), + Array[AnyRef](value), + true, false, element[Coll[Byte]])) + } + + override def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = { + asRep[T](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](bytes, cT, Map(tT -> Evaluation.rtypeToSType(cT.sourceType))), + true, false, cT)) + } + } implicit object LiftableSigmaDslBuilder @@ -2122,6 +2431,20 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { Array[AnyRef](l, r), true, true, element[Coll[Byte]])) } + + def serialize[T](value: Ref[T]): Ref[Coll[Byte]] = { + asRep[Coll[Byte]](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("serialize", classOf[Sym]), + Array[AnyRef](value), + true, true, element[Coll[Byte]])) + } + + def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = { + asRep[T](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](bytes, cT), + true, true, cT, Map(tT -> Evaluation.rtypeToSType(cT.sourceType)))) + } } // entityUnref: single unref method for each type family @@ -2139,7 +2462,9 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { override protected def collectMethods: Map[RMethod, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[SigmaDslBuilder]), RClass(classOf[SSigmaDslBuilder]), Set( - "Colls", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants", "decodePoint", "avlTree", "xor" + "Colls", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256", + "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants", + "decodePoint", "avlTree", "xor", "serialize", "fromBigEndianBytes" )) } } diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala index af5be938be..d4943ef892 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala @@ -105,6 +105,9 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder, case a @ Apply(PKFunc.symNoType, args) => Some(PKFunc.irInfo.irBuilder(PKFunc.sym, args).withPropagatedSrcCtx(a.sourceContext)) + case a @ Apply(predefFuncRegistry.SerializeFunc.symNoType, args) => + Some(predefFuncRegistry.SerializeFunc.irInfo.irBuilder(PKFunc.sym, args).withPropagatedSrcCtx(a.sourceContext)) + case sel @ Select(obj, "isEmpty", _) => Some(mkLogicalNot(mkSelect(obj, "isDefined").asBoolValue).withPropagatedSrcCtx(sel.sourceContext)) diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index 5f5e430e47..beb46554a3 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -134,8 +134,19 @@ class SigmaTyper(val builder: SigmaBuilder, res case Apply(ApplyTypes(sel @ Select(obj, n, _), Seq(rangeTpe)), args) => + // downcast getVarFromInput arguments to short and byte + val nArgs = if (n == SContextMethods.getVarFromInputMethod.name && + args.length == 2 && + args(0).isInstanceOf[Constant[_]] && + args(1).isInstanceOf[Constant[_]] && + args(0).tpe.isNumType && + args(1).tpe.isNumType) { + IndexedSeq(ShortConstant(SShort.downcast(args(0).asInstanceOf[Constant[SNumericType]].value.asInstanceOf[AnyVal])).withSrcCtx(args(0).sourceContext), + ByteConstant(SByte.downcast(args(1).asInstanceOf[Constant[SNumericType]].value.asInstanceOf[AnyVal])).withSrcCtx(args(1).sourceContext)) + } else args + val newObj = assignType(env, obj) - val newArgs = args.map(assignType(env, _)) + val newArgs = nArgs.map(assignType(env, _)) newObj.tpe match { case p: SProduct => MethodsContainer.getMethod(p, n) match { @@ -155,7 +166,7 @@ class SigmaTyper(val builder: SigmaBuilder, .getOrElse(mkMethodCall(newObj, method, newArgs, subst)) } else { val newSelect = mkSelect(newObj, n, Some(concrFunTpe)).withSrcCtx(sel.sourceContext) - mkApply(newSelect, newArgs.toArray[SValue]) + mkApply(newSelect, newArgs) } case Some(method) => error(s"Don't know how to handle method $method in obj $p", sel.sourceContext) @@ -166,9 +177,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. @@ -221,6 +243,11 @@ class SigmaTyper(val builder: SigmaBuilder, case (Ident(GetVarFunc.name | ExecuteFromVarFunc.name, _), Seq(id: Constant[SNumericType]@unchecked)) if id.tpe.isNumType => Seq(ByteConstant(SByte.downcast(id.value.asInstanceOf[AnyVal])).withSrcCtx(id.sourceContext)) + case (Ident(SContextMethods.getVarFromInputMethod.name, _), + Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) + if inputId.tpe.isNumType && varId.tpe.isNumType => + Seq(ShortConstant(SShort.downcast(inputId.value.asInstanceOf[AnyVal])).withSrcCtx(inputId.sourceContext), + ByteConstant(SByte.downcast(varId.value.asInstanceOf[AnyVal])).withSrcCtx(varId.sourceContext)) case _ => typedArgs } val actualTypes = adaptedTypedArgs.map(_.tpe) @@ -409,11 +436,6 @@ class SigmaTyper(val builder: SigmaBuilder, error(s"Invalid application of type arguments $app: function $input doesn't have type parameters", input.sourceContext) } -// case app @ ApplyTypes(in, targs) => -// val newIn = assignType(env, in) -// ApplyTypes(newIn, targs) -// error(s"Invalid application of type arguments $app: expression doesn't have type parameters") - case If(c, t, e) => val c1 = assignType(env, c).asValue[SBoolean.type] val t1 = assignType(env, t) diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala index 6a7ef5a512..4b3aa2eab5 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala @@ -7,8 +7,7 @@ import org.ergoplatform.settings.ErgoAlgos import scorex.util.encode.Base16 import scorex.util.{ModifierId, Random} import sigma.Extensions._ -import sigma.SigmaDslTesting -import sigma.ast.SCollection.SByteArray +import sigma.{SigmaDslTesting, VersionContext} import sigma.ast.SType._ import sigma.ast.syntax.{ErgoBoxCandidateRType, TrueSigmaProp} import sigma.ast._ @@ -20,9 +19,11 @@ import sigmastate.helpers.TestingHelpers.copyTransaction import sigmastate.utils.Helpers import sigma.SigmaDslTesting import sigma.Extensions._ +import sigma.ast.SCollection.SByteArray +import sigmastate.CrossVersionProps import sigmastate.utils.Helpers.EitherOps // required for Scala 2.11 -class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { + class ErgoLikeTransactionSpec extends SigmaDslTesting with CrossVersionProps with JsonCodecs { property("ErgoBox test vectors") { val token1 = "6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001" @@ -99,14 +100,24 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { { // test case for R2 val res = b1.get(ErgoBox.R2).get - val exp = Coll( - (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, - (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token2).toColl) -> 500L - ).map(identity).toConstant - // TODO v6.0 (16h): fix collections equality and remove map(identity) - // (PairOfColl should be equal CollOverArray but now it is not) + + // We have versioned check here due to fixed collections equality in 6.0.0 + // (PairOfColl equal CollOverArray now) // see (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/909) - res shouldBe exp + if(VersionContext.current.isV6SoftForkActivated) { + val exp = Coll( + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token2).toColl) -> 500L + ).toConstant + res shouldBe exp + exp shouldBe res + } else { + val exp = Coll( + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token2).toColl) -> 500L + ).map(identity).toConstant + res shouldBe exp + } } { // test case for R3 @@ -470,7 +481,6 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { // test equivalence of "from Json" and "from bytes" deserialization tx2.id shouldBe tx.id tx2.id shouldBe "d5c0a7908bbb8eefe72ad70a9f668dd47b748239fd34378d3588d5625dd75c82" - println(tx2.id) } property("Tuple in register test vector") { diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index bd86feb82b..0bb92f8249 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -4533,6 +4533,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => .append(Coll[AnyValue]( CAnyValue(Helpers.decodeBytes("00")), CAnyValue(true))), + spendingTransaction = null, activatedScriptVersion = activatedVersionInTests, currentErgoTreeVersion = ergoTreeVersionInTests ) @@ -8059,6 +8060,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) ) + if(!VersionContext.current.isV6SoftForkActivated) { verifyCases( // (coll, (index, default)) { @@ -8129,7 +8131,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) ) - )) + ))} } property("Tuple size method equivalence") { @@ -8696,13 +8698,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => "{ (x: Option[Long]) => x.isDefined }", FuncValue(Vector((1, SOption(SLong))), OptionIsDefined(ValUse(1, SOption(SLong)))))) - verifyCases( - Seq( - (None -> Expected(Success(1L), 1766, costDetails3, 1766, Seq.fill(4)(2006))), - (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766, Seq.fill(4)(2006)))), - existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, - "{ (x: Option[Long]) => x.getOrElse(1L) }", - FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) + if (!VersionContext.current.isV6SoftForkActivated) { + verifyCases( + Seq( + (None -> Expected(Success(1L), 1766, costDetails3, 1766, Seq.fill(4)(2006))), + (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766, Seq.fill(4)(2006)))), + existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, + "{ (x: Option[Long]) => x.getOrElse(1L) }", + FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) + } verifyCases( Seq( diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 4ca378c35e..b5d7d59a06 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,23 +1,30 @@ package sigma -import org.ergoplatform.ErgoHeader +import org.ergoplatform.{ErgoBox, ErgoHeader, ErgoLikeTransaction, Input} import scorex.util.encode.Base16 import sigma.VersionContext.V6SoftForkVersion -import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.Token +import org.ergoplatform.settings.ErgoAlgos import scorex.util.ModifierId import scorex.utils.{Ints, Longs, Shorts} -import sigma.ast.ErgoTree.ZeroHeader +import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast.{SInt, _} -import sigma.data.{CBigInt, CBox, CHeader, ExactNumeric} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CGroupElement, CHeader, CSigmaDslBuilder, CSigmaProp, CUnsignedBigInt, ExactNumeric, PairOfCols, ProveDHTuple, RType} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode +import sigma.util.Extensions.{BooleanOps, IntOps} +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 sigmastate.exceptions.MethodNotFound import sigmastate.utils.Extensions.ByteOpsForSigma import sigmastate.utils.Helpers +import sigma.Extensions.{ArrayOps, CollOps} +import sigma.crypto.CryptoConstants +import sigma.interpreter.{ContextExtension, ProverResult} import java.math.BigInteger import scala.util.{Failure, Success} @@ -30,6 +37,78 @@ import scala.util.{Failure, Success} class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => override def languageVersion: Byte = VersionContext.V6SoftForkVersion + implicit override def evalSettings = super.evalSettings.copy(printTestVectors = true) + + def mkSerializeFeature[A: RType]: Feature[A, Coll[Byte]] = { + val tA = RType[A] + val tpe = Evaluation.rtypeToSType(tA) + newFeature( + (x: A) => SigmaDsl.serialize(x), + s"{ (x: ${tA.name}) => serialize(x) }", + expectedExpr = FuncValue( + Array((1, tpe)), + MethodCall( + Global, + SGlobalMethods.serializeMethod.withConcreteTypes(Map(STypeVar("T") -> tpe)), + Array(ValUse(1, tpe)), + Map() + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion) + } + + val baseTrace = Array( + FixedCostItem(Apply), + FixedCostItem(FuncValue), + FixedCostItem(GetVar), + FixedCostItem(OptionGet), + FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))) + ) + + property("Global.serialize[Byte]") { + lazy val serializeByte = mkSerializeFeature[Byte] + val expectedCostTrace = TracedCost( + baseTrace ++ Array( + FixedCostItem(Global), + FixedCostItem(MethodCall), + FixedCostItem(ValUse), + FixedCostItem(NamedDesc("SigmaByteWriter.startWriter"), FixedCost(JitCost(10))), + FixedCostItem(NamedDesc("SigmaByteWriter.put"), FixedCost(JitCost(1))) + ) + ) + val cases = Seq( + (-128.toByte, Expected(Success(Coll(-128.toByte)), expectedCostTrace)), + (-1.toByte, Expected(Success(Coll(-1.toByte)), expectedCostTrace)), + (0.toByte, Expected(Success(Coll(0.toByte)), expectedCostTrace)), + (1.toByte, Expected(Success(Coll(1.toByte)), expectedCostTrace)), + (127.toByte, Expected(Success(Coll(127.toByte)), expectedCostTrace)) + ) + verifyCases(cases, serializeByte, preGeneratedSamples = None) + } + + property("Global.serialize[Short]") { + lazy val serializeShort = mkSerializeFeature[Short] + val expectedCostTrace = TracedCost( + baseTrace ++ Array( + FixedCostItem(Global), + FixedCostItem(MethodCall), + FixedCostItem(ValUse), + FixedCostItem(NamedDesc("SigmaByteWriter.startWriter"), FixedCost(JitCost(10))), + FixedCostItem(NamedDesc("SigmaByteWriter.putNumeric"), FixedCost(JitCost(3))) + ) + ) + val cases = Seq( + (Short.MinValue, Expected(Success(Coll[Byte](0xFF.toByte, 0xFF.toByte, 0x03.toByte)), expectedCostTrace)), + (-1.toShort, Expected(Success(Coll(1.toByte)), expectedCostTrace)), + (0.toShort, Expected(Success(Coll(0.toByte)), expectedCostTrace)), + (1.toShort, Expected(Success(Coll(2.toByte)), expectedCostTrace)), + (Short.MaxValue, Expected(Success(Coll(-2.toByte, -1.toByte, 3.toByte)), expectedCostTrace)) + ) + verifyCases(cases, serializeShort, preGeneratedSamples = None) + } + + // TODO v6.0: implement serialization roundtrip tests after merge with deserializeTo + property("Boolean.toByte") { val toByte = newFeature((x: Boolean) => x.toByte, "{ (x: Boolean) => x.toByte }", sinceVersion = V6SoftForkVersion @@ -1449,4 +1528,1093 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + private def contextData() = { + val input = CBox( + new ErgoBox( + 80946L, + new ErgoTree( + HeaderType @@ 16.toByte, + Vector( + SigmaPropConstant( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb"), + Helpers.decodeECPoint("023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d03"), + Helpers.decodeECPoint("03d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72"), + Helpers.decodeECPoint("037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441") + ) + ) + ) + ), + Right(ConstantPlaceholder(0, SSigmaProp)) + ), + Coll(), + Map( + ErgoBox.R4 -> ByteArrayConstant(Helpers.decodeBytes("34")), + ErgoBox.R5 -> TrueLeaf + ), + ModifierId @@ ("0000bfe96a7c0001e7a5ee00aafb80ff057fbe7f8c6680e33a3dc18001820100"), + 1.toShort, + 5 + ) + ) + + val tx = ErgoLikeTransaction( + IndexedSeq(), + IndexedSeq(input.wrappedValue) + ) + + val tx2 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(true)))))), + IndexedSeq(input.wrappedValue) + ) + + val tx3 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> IntConstant(0)))))), + IndexedSeq(input.wrappedValue) + ) + + val tx4 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(false)))))), + IndexedSeq(input.wrappedValue) + ) + + val ctx = CContext( + _dataInputs = Coll[Box](), + headers = Coll[Header](), + preHeader = CPreHeader( + 0.toByte, + Colls.fromArray(Array.fill(32)(0.toByte)), + -755484979487531112L, + 9223372036854775807L, + 11, + Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + Helpers.decodeBytes("007f00") + ), + inputs = Coll[Box](input), + outputs = Coll[Box](), + height = 11, + selfBox = input.copy(), // in 3.x, 4.x implementation selfBox is never the same instance as input (see toSigmaContext) + selfIndex = 0, + lastBlockUtxoRootHash = CAvlTree( + AvlTreeData( + ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17").toColl, + AvlTreeFlags(true, true, true), + 1211925457, + None + ) + ), + _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + vars = Colls + .replicate[AnyValue](10, null) // reserve 10 vars + .append(Coll[AnyValue]( + CAnyValue(Helpers.decodeBytes("00")), + CAnyValue(true))), + spendingTransaction = tx, + activatedScriptVersion = activatedVersionInTests, + currentErgoTreeVersion = ergoTreeVersionInTests + ) + val ctx2 = ctx.copy(spendingTransaction = tx2) + val ctx3 = ctx.copy(spendingTransaction = tx3, vars = ctx.vars.patch(11, Coll(CAnyValue(0)), 1)) + val ctx4 = ctx.copy(spendingTransaction = tx4, vars = ctx.vars.patch(11, Coll(CAnyValue(false)), 1)) + + (ctx, ctx2, ctx3, ctx4) + } + + property("getVarFromInput") { + + def getVarFromInput = { + newFeature( + { (x: Context) => x.getVarFromInput[Boolean](0, 11) }, + "{ (x: Context) => x.getVarFromInput[Boolean](0, 11) }", + FuncValue( + Array((1, SContext)), + MethodCall.typed[Value[SOption[SBoolean.type]]]( + ValUse(1, SContext), + SContextMethods.getVarFromInputMethod.withConcreteTypes(Map(STypeVar("T") -> SBoolean)), + Array(ShortConstant(0.toShort), ByteConstant(11.toByte)), + Map(STypeVar("T") -> SBoolean) + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + val (ctx, ctx2, ctx3, ctx4) = contextData() + + verifyCases( + Seq( + ctx -> new Expected(ExpectedResult(Success(None), None)), // input with # provided does not exist + ctx2 -> new Expected(ExpectedResult(Success(Some(true)), None)), + ctx3 -> new Expected(ExpectedResult(Success(None), None)), // not expected type in context var + ctx4 -> new Expected(ExpectedResult(Success(Some(false)), None)) + ), + getVarFromInput + ) + } + + property("Context.getVar") { + + def getVar = { + 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 + ) + } + + val (_, ctx2, ctx3, ctx4) = contextData() + + verifyCases( + Seq( + ctx2 -> new Expected(ExpectedResult(Success(Some(true)), None)), + ctx3 -> new Expected(ExpectedResult(Failure(new sigma.exceptions.InvalidType("Cannot getVar[Boolean](11): invalid type of value TestValue(0) at id=11")), None)), // not expected type in context var + ctx4 -> new Expected(ExpectedResult(Success(Some(false)), None)) + ), + getVar + ) + } + + property("Option.getOrElse with lazy default") { + + val trace = TracedCost( + Array( + FixedCostItem(Apply), + FixedCostItem(FuncValue), + FixedCostItem(GetVar), + FixedCostItem(OptionGet), + FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), + FixedCostItem(ValUse), + FixedCostItem(OptionGetOrElse) + ) + ) + + verifyCases( + Seq( + Some(2L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793, + newVersionedResults = { + expectedSuccessForAllTreeVersions(2L, 2015, trace) + } ), + None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793) + ), + changedFeature( + changedInVersion = VersionContext.V6SoftForkVersion, + { (x: Option[Long]) => val default = 1 / 0L; x.getOrElse(default) }, + { (x: Option[Long]) => if (VersionContext.current.isV6SoftForkActivated) {x.getOrElse(1 / 0L)} else {val default = 1 / 0L; x.getOrElse(default)} }, + "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", + FuncValue( + Array((1, SOption(SLong))), + OptionGetOrElse( + ValUse(1, SOption(SLong)), + ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) + ) + ), + allowNewToSucceed = true + ) + ) + } + + property("Coll getOrElse with lazy default") { + + val trace = TracedCost( + Array( + FixedCostItem(Apply), + FixedCostItem(FuncValue), + FixedCostItem(GetVar), + FixedCostItem(OptionGet), + FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), + FixedCostItem(ValUse), + FixedCostItem(Constant), + FixedCostItem(ByIndex) + ) + ) + + def scalaFuncNew(x: Coll[Int]) = { + if (VersionContext.current.isV6SoftForkActivated) { + x.toArray.toIndexedSeq.headOption.getOrElse(1 / 0) + } else scalaFuncOld(x) + } + + def scalaFuncOld(x: Coll[Int]) = { + x.getOrElse(0, 1 / 0) + } + + verifyCases( + Seq( + Coll(1) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793, + newVersionedResults = { + expectedSuccessForAllTreeVersions(1, 2029, trace) + } ), + Coll[Int]() -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793) + ), + changedFeature( + changedInVersion = VersionContext.V6SoftForkVersion, + scalaFuncOld, + scalaFuncNew, + "{ (x: Coll[Int]) => x.getOrElse(0, 1 / 0) }", + FuncValue( + Array((1, SCollectionType(SInt))), + ByIndex( + ValUse(1, SCollectionType(SInt)), + IntConstant(0), + Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) + ) + ), + allowNewToSucceed = true + ) + ) + } + + + 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 + ) + } + + verifyCases( + Seq( + 5.toByte -> new Expected(ExpectedResult(Success(true), None)), + Byte.MaxValue -> new Expected(ExpectedResult(Success(true), None)), + Byte.MinValue -> new Expected(ExpectedResult(Success(true), None)) + ), + byteFromBigEndianBytes + ) + + def shortFromBigEndianBytes: Feature[Short, Boolean] = { + newFeature( + { (x: Short) => CSigmaDslBuilder.fromBigEndianBytes[Short](Colls.fromArray(Shorts.toByteArray(x))) == x}, + "{ (x: Short) => fromBigEndianBytes[Short](x.toBytes) == x }", + 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 + ) + } + + verifyCases( + Seq( + 5.toShort -> new Expected(ExpectedResult(Success(true), None)), + Short.MaxValue -> new Expected(ExpectedResult(Success(true), None)), + Short.MinValue -> new Expected(ExpectedResult(Success(true), None)) + ), + shortFromBigEndianBytes + ) + + def intFromBigEndianBytes: Feature[Int, Boolean] = { + newFeature( + { (x: Int) => CSigmaDslBuilder.fromBigEndianBytes[Int](Colls.fromArray(Ints.toByteArray(x))) == x}, + "{ (x: Int) => fromBigEndianBytes[Int](x.toBytes) == x }", + 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 + ) + } + + verifyCases( + Seq( + 5 -> new Expected(ExpectedResult(Success(true), None)), + Int.MaxValue -> new Expected(ExpectedResult(Success(true), None)) + ), + intFromBigEndianBytes + ) + + def longFromBigEndianBytes: Feature[Long, Boolean] = { + newFeature( + { (x: Long) => CSigmaDslBuilder.fromBigEndianBytes[Long](Colls.fromArray(Longs.toByteArray(x))) == x}, + "{ (x: Long) => fromBigEndianBytes[Long](x.toBytes) == x }", + 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 + ) + } + + verifyCases( + Seq( + 5L -> new Expected(ExpectedResult(Success(true), None)), + Long.MinValue -> new Expected(ExpectedResult(Success(true), None)) + ), + longFromBigEndianBytes + ) + + def bigIntFromBigEndianBytes: Feature[BigInt, Boolean] = { + newFeature( + { (x: BigInt) => CSigmaDslBuilder.fromBigEndianBytes[BigInt](x.toBytes) == x}, + "{ (x: BigInt) => Global.fromBigEndianBytes[BigInt](x.toBytes) == x }", + 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 + ) + } + + verifyCases( + Seq( + CBigInt(BigInteger.valueOf(50)) -> new Expected(ExpectedResult(Success(true), None)), + CBigInt(BigInteger.valueOf(-500000000000L)) -> new Expected(ExpectedResult(Success(true), None)), + CBigInt(sigma.crypto.CryptoConstants.groupOrder.divide(BigInteger.valueOf(2))) -> new Expected(ExpectedResult(Success(true), None)) + ), + bigIntFromBigEndianBytes + ) + + 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 + ) + + verifyCases( + Seq( + Coll(1, 2) -> Expected(ExpectedResult(Success(Coll(2, 1)), None)), + Coll[Int]() -> Expected(ExpectedResult(Success(Coll[Int]()), None)) + ), + f + ) + } + + property("Coll.distinct") { + val f = newFeature[Coll[Int], Coll[Int]]( + { (xs: Coll[Int]) => xs.distinct }, + """{(xs: Coll[Int]) => xs.distinct }""".stripMargin, + 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 + ) + + verifyCases( + Seq( + Coll(1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll(1, 1, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll(1, 2, 2) -> Expected(ExpectedResult(Success(Coll(1, 2)), None)), + Coll(2, 2, 2) -> Expected(ExpectedResult(Success(Coll(2)), None)), + Coll(3, 1, 2, 2, 2, 4, 4, 1) -> Expected(ExpectedResult(Success(Coll(3, 1, 2, 4)), None)), + Coll[Int]() -> Expected(ExpectedResult(Success(Coll[Int]()), None)) + ), + f + ) + } + + property("Coll.startsWith") { + val f = newFeature[(Coll[Int], Coll[Int]), Boolean]( + { (xs: (Coll[Int], Coll[Int])) => xs._1.startsWith(xs._2) }, + """{(xs: (Coll[Int], Coll[Int])) => xs._1.startsWith(xs._2) }""".stripMargin, + 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 + ) + + verifyCases( + Seq( + (Coll(1, 2, 3), Coll(1, 2)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(1, 2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(1, 2, 4)) -> Expected(ExpectedResult(Success(false), None)), + (Coll(1, 2, 3), Coll(1, 2, 3, 4)) -> Expected(ExpectedResult(Success(false), None)), + (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)), + (Coll[Int](1, 2), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) + ), + f + ) + } + + property("Coll.endsWith") { + val f = newFeature[(Coll[Int], Coll[Int]), Boolean]( + { (xs: (Coll[Int], Coll[Int])) => xs._1.endsWith(xs._2) }, + """{(xs: (Coll[Int], Coll[Int])) => xs._1.endsWith(xs._2) }""".stripMargin, + 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( + Seq( + (Coll(1, 2, 3), Coll(1, 2)) -> Expected(ExpectedResult(Success(false), None)), + (Coll(1, 2, 3), Coll(2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll(1, 2, 3), Coll(2, 3, 4)) -> Expected(ExpectedResult(Success(false), None)), + (Coll(1, 2, 3), Coll(1, 2, 3)) -> Expected(ExpectedResult(Success(true), None)), + (Coll[Int](), Coll[Int]()) -> Expected(ExpectedResult(Success(true), None)) + ), + f + ) + } + + property("Coll.get") { + val f = newFeature[(Coll[Int], Int), Option[Int]]( + { (xs: (Coll[Int], Int)) => xs._1.get(xs._2) }, + """{(xs: (Coll[Int], Int)) => xs._1.get(xs._2) }""".stripMargin, + 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( + Seq( + (Coll(1, 2), 0) -> Expected(ExpectedResult(Success(Some(1)), None)), + (Coll(1, 2), 1) -> Expected(ExpectedResult(Success(Some(2)), None)), + (Coll(1, 2), -1) -> Expected(ExpectedResult(Success(None), None)), + (Coll(1, 2), 2) -> Expected(ExpectedResult(Success(None), None)), + (Coll[Int](), 0) -> Expected(ExpectedResult(Success(None), None)) + ), + f + ) + } + + 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 + ) + + } + } diff --git a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala index bcd4b21129..266c5e66e5 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala @@ -34,8 +34,6 @@ class SigmaDslStaginTests extends BaseCtxTests with ErgoScriptTestkit with BaseL val ctx: SContext = newContext(10, boxA1, VersionContext.MaxSupportedScriptVersion, VersionContext.MaxSupportedScriptVersion) .withInputs(boxA2) .withVariables(Map(1 -> toAnyValue(30), 2 -> toAnyValue(40))) - val p1: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(true)) - val p2: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(false)) cake.check(dsl, { env: EnvRep[RSigmaDslBuilder] => for { dsl <- env; arg <- lifted(true) } yield dsl.sigmaProp(arg) }, dsl.sigmaProp(true)) diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 6ab62e39a9..7f3f28b791 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -771,7 +771,7 @@ class SigmaDslTesting extends AnyPropSpec override def checkExpected(input: A, expected: Expected[B]): Unit = { // check the new implementation with Scala semantic function val newRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { - checkEq(scalaFuncNew)(newF)(input) + checkEq(scalaFuncNew)(newF)(input) } if (VersionContext.current.activatedVersion < changedInVersion) { @@ -1016,6 +1016,20 @@ class SigmaDslTesting extends AnyPropSpec } } + /** Used when the old and new value are the same for all versions + * and the expected costs are not specified. + * + * @param value expected result of tested function + * @param expectedDetails expected cost details for all versions + */ + def apply[A](value: Try[A], expectedDetails: CostDetails): Expected[A] = + new Expected(ExpectedResult(value, None)) { + override val newResults = defaultNewResults.map { + case (ExpectedResult(v, _), _) => + (ExpectedResult(v, None), Some(expectedDetails)) + } + } + /** Used when the old and new value and costs are the same for all versions. * * @param value expected result of tested function @@ -1362,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 @@ -1390,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 d62ed6d2bc..477dfec309 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) { + // SBigInt inherit methods from SNumericType.methods + // however they are not resolvable via SBigInt.typeId before v6.0 + import SNumericTypeMethods._ + (SUnsignedBigInt.typeId, Seq( + MInfo(methodId = 1, ToByteMethod, isResolvableFromIds = true), + MInfo(2, ToShortMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(3, ToIntMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(4, ToLongMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(5, ToBigIntMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(6, ToBytesMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(7, ToBitsMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true), + MInfo(14, ModInverseMethod, true), + MInfo(15, PlusModMethod, true), + MInfo(16, SubtractModMethod, true), + MInfo(17, MultiplyModMethod, true), + MInfo(18, ModMethod, true), + MInfo(19, ToSignedMethod, true) + ), true) + } else { + (SUnsignedBigInt.typeId, Seq.empty, false) + } + }, { import SGroupElementMethods._ (SGroupElement.typeId, Seq( MInfo(2, GetEncodedMethod), MInfo(3, ExponentiateMethod), MInfo(4, MultiplyMethod), MInfo(5, NegateMethod) - ), true) + ) ++ { + if(VersionContext.current.isV6SoftForkActivated) { + Seq(MInfo(6, ExponentiateUnsignedMethod)) + } else { + Seq.empty + } + }, true) }, { import SSigmaPropMethods._ (SSigmaProp.typeId, Seq( @@ -457,7 +498,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C MInfo(5, IdMethod), MInfo(6, creationInfoMethod), MInfo(8, tokensMethod) - ) ++ (if (VersionContext.current.isV6SoftForkActivated) { + ) ++ (if (isV6Activated) { Seq(MInfo(7, getRegMethodV6)) } else { Seq(MInfo(7, getRegMethodV5)) @@ -491,8 +532,8 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C MInfo(7, timestampMethod), MInfo(8, nBitsMethod), MInfo(9, heightMethod), MInfo(10, extensionRootMethod), MInfo(11, minerPkMethod), MInfo(12, powOnetimePkMethod), MInfo(13, powNonceMethod), MInfo(14, powDistanceMethod), MInfo(15, votesMethod) - ) ++ (if (VersionContext.current.isV6SoftForkActivated) { - Seq(MInfo(16, checkPowMethod)) + ) ++ (if (isV6Activated) { + Seq(MInfo(16, checkPowMethod)) // methods added in v6.0 } else { Seq.empty[MInfo] }), true) @@ -509,13 +550,21 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C MInfo(1, dataInputsMethod), MInfo(2, headersMethod), MInfo(3, preHeaderMethod), MInfo(4, inputsMethod), MInfo(5, outputsMethod), MInfo(6, heightMethod), MInfo(7, selfMethod), MInfo(8, selfBoxIndexMethod), MInfo(9, lastBlockUtxoRootHashMethod), - MInfo(10, minerPubKeyMethod), MInfo(11, getVarMethod) - ), true) + MInfo(10, minerPubKeyMethod)) ++ (if(VersionContext.current.isV6SoftForkActivated){ + Seq(MInfo(11, getVarV6Method), MInfo(12, getVarFromInputMethod)) + } else { + Seq(MInfo(11, getVarV5Method)) + }), true) }, { import SGlobalMethods._ (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) - ), true) + ) ++ (if (isV6Activated) { + // id = 4 reserved for deserializeTo method + Seq(MInfo(3, serializeMethod), MInfo(5, FromBigEndianBytesMethod)) // methods added in v6.0 + } else { + Seq.empty[MInfo] + }), true) }, { import SCollectionMethods._ (SCollection.typeId, Seq( @@ -557,7 +606,9 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C EndsWithMethod, MapReduceMethod, */ - ), true) + ) ++ (if (isV6Activated) { + Seq(MInfo(30, ReverseMethod), MInfo(31, DistinctMethod), MInfo(32, StartsWithMethod), MInfo(33, EndsWithMethod), MInfo(34, GetMethod)) + } else Seq.empty), true) }, { import SOptionMethods._ (SOption.typeId, Seq( @@ -615,7 +666,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C } property("MethodCall on numerics") { - forAll(Table[STypeCompanion]("type", SByte, SShort, SInt, SLong, SBigInt)) { t => + forAll(Table[STypeCompanion]("type", SByte, SShort, SInt, SLong, SBigInt, SUnsignedBigInt)) { t => // this methods are expected to fail resolution in before v6.0 if (!isV6Activated) { (1 to 7).foreach { methodId => diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala index aa552e9b69..3c3687cb37 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala @@ -216,4 +216,15 @@ class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat e.source shouldBe Some(SourceContext(2, 5, "val x = 10")) } + property("predefined `serialize` should be transformed to MethodCall") { + runWithVersion(VersionContext.V6SoftForkVersion) { + checkBound(env, "serialize(1)", + MethodCall.typed[Value[SCollection[SByte.type]]]( + Global, + SGlobalMethods.getMethodByName("serialize").withConcreteTypes(Map(STypeVar("T") -> SInt)), + Array(IntConstant(1)), + Map() + )) + } + } } diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 3696a7e14e..c86cb11afc 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -683,6 +683,16 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "CONTEXT.dataInputs") shouldBe SCollection(SBox) } + property("SContext.getVar") { + typecheck(env, "CONTEXT.getVar[Int](1.toByte).get") shouldBe SInt + } + + property("SContext.getVarFromInput") { + runWithVersion(VersionContext.V6SoftForkVersion) { + typecheck(env, "CONTEXT.getVarFromInput[Int](1.toShort, 1.toByte).get") shouldBe SInt + } + } + property("SAvlTree.digest") { typecheck(env, "getVar[AvlTree](1).get.digest") shouldBe SByteArray } @@ -714,4 +724,35 @@ class SigmaTyperTest extends AnyPropSpec typecheck(customEnv, "substConstants(scriptBytes, positions, newVals)") shouldBe SByteArray } + property("Global.serialize") { + runWithVersion(VersionContext.V6SoftForkVersion) { + typecheck(env, "Global.serialize(1)", + MethodCall.typed[Value[SCollection[SByte.type]]]( + Global, + SGlobalMethods.getMethodByName("serialize").withConcreteTypes(Map(STypeVar("T") -> SInt)), + Array(IntConstant(1)), + Map() + )) shouldBe SByteArray + } + + runWithVersion((VersionContext.V6SoftForkVersion - 1).toByte) { + assertExceptionThrown( + typecheck(env, "Global.serialize(1)"), + exceptionLike[MethodNotFound]("Cannot find method 'serialize' in in the object Global") + ) + } + } + + property("predefined serialize") { + runWithVersion(VersionContext.V6SoftForkVersion) { + typecheck(env, "serialize((1, 2L))", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + Global, + SGlobalMethods.getMethodByName("serialize").withConcreteTypes(Map(STypeVar("T") -> SPair(SInt, SLong))), + Array(Tuple(Vector(IntConstant(1), LongConstant(2L)))), + Map() + )) shouldBe SByteArray + } + } + } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 6d21dd9edb..2271f6f6e2 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -4,29 +4,41 @@ import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import org.scalatest.Assertion import scorex.util.encode.Base16 -import org.scalatest.Assertion +import scorex.utils.Ints 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, CSigmaDslBuilder} +import sigma.data.{AvlTreeData, CAnyValue, CBigInt, CGroupElement, CSigmaDslBuilder} import sigma.util.StringUtil._ import sigma.ast._ import sigma.ast.syntax._ -import sigma.crypto.CryptoConstants +import sigma.crypto.{CryptoConstants, SecP256K1Group} import sigmastate._ import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigma.interpreter.ContextExtension.VarBinding -import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings +import sigmastate.interpreter.CErgoTreeEvaluator.{DefaultEvalSettings, currentEvaluator} import sigmastate.interpreter.Interpreter._ import sigma.ast.Apply import sigma.eval.EvalSettings import sigma.exceptions.InvalidType -import sigma.serialization.ErgoTreeSerializer +import sigma.serialization.{ErgoTreeSerializer, SerializerException} +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.validation.ValidationException +import sigmastate.utils.Helpers 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 { @@ -88,8 +100,11 @@ class BasicOpsSpecification extends CompilerTestingCommons // is not supported by ErgoScript Compiler) // In such cases we use expected property as the property to test propExp.asSigmaProp - } else - compile(env, script).asBoolValue.toSigmaProp + } else { + withVersions(VersionContext.MaxSupportedScriptVersion, ergoTreeVersionInTests) { + compile(env, script).asBoolValue.toSigmaProp + } + } if (propExp != null) prop shouldBe propExp @@ -106,7 +121,8 @@ class BasicOpsSpecification extends CompilerTestingCommons val newBox1 = testBox(10, tree, creationHeight = 0, boxIndex = 0, additionalRegisters = Map( reg1 -> IntConstant(1), reg2 -> IntConstant(10))) - val tx = createTransaction(newBox1) + val ce = ContextExtension(prover.contextExtenders) + val tx = new ErgoLikeTransaction(IndexedSeq(Input(boxToSpend.id, ProverResult(Array.empty, ce))), ArraySeq.empty, IndexedSeq(newBox1)) val ctx = ErgoLikeContextTesting(currentHeight = 0, lastBlockUtxoRoot = AvlTreeData.dummy, ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(boxToSpend), @@ -140,10 +156,637 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } + property("getVarFromInput") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ sigmaProp(getVarFromInput[Int](0, 1).get == 5) }", + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + getVarTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy getVarTest() + } + } + + property("getVarFromInput - self index") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + """{ + | val idx = CONTEXT.selfBoxIndex + | sigmaProp(CONTEXT.getVarFromInput[Int](idx.toShort, 1.toByte).get == 5) + | }""".stripMargin, + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + getVarTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy getVarTest() + } + } + + property("getVarFromInput - invalid input") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ sigmaProp(CONTEXT.getVarFromInput[Int](1, 1).isDefined == false) }", + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + getVarTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy getVarTest() + } + } + + + 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