diff --git a/.gitignore b/.gitignore index df18ce8ab1..b67ecf6995 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,9 @@ *.fdb_latexmk *.gz +yarn.lock *.log +yarn.lock docs/spec/out/ test-out/ flamegraphs/ diff --git a/README.md b/README.md index 6003daf55a..411c502dd2 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ This library is on Maven repository and can be added to the SBT configuration of Scala project. ```scala -libraryDependencies += "org.scorexfoundation" %% "sigma-state" % "5.0.14" +libraryDependencies += "org.scorexfoundation" %% "sigma-state" % "6.0.0" ``` ## Repository Organization 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/GroupElement.scala b/core/js/src/main/scala/sigma/js/GroupElement.scala index 21b53b265d..82d8d462d6 100644 --- a/core/js/src/main/scala/sigma/js/GroupElement.scala +++ b/core/js/src/main/scala/sigma/js/GroupElement.scala @@ -1,6 +1,6 @@ package sigma.js -import sigma.Extensions.CoreArrayByteOps +import scorex.util.encode.Base16 import sigma.crypto.{CryptoFacade, CryptoFacadeJs, Ecp, Platform} import scala.scalajs.js @@ -13,7 +13,7 @@ class GroupElement(val point: Ecp) extends js.Object { * @see CryptoFacade.getASN1Encoding */ def toPointHex(): String = { - CryptoFacade.getASN1Encoding(point, true).toHex + Base16.encode(CryptoFacade.getASN1Encoding(point, true)) } } diff --git a/core/js/src/main/scala/sigma/js/Isos.scala b/core/js/src/main/scala/sigma/js/Isos.scala index b28a3337b8..bc88c46457 100644 --- a/core/js/src/main/scala/sigma/js/Isos.scala +++ b/core/js/src/main/scala/sigma/js/Isos.scala @@ -1,13 +1,16 @@ 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 import scala.scalajs.js import scala.scalajs.js.JSConverters.JSRichOption +/** Definitions of isomorphisms for sigma-core module. + * @see sigma.data.Iso + */ object Isos { implicit def isoUndefOr[A, B](implicit iso: Iso[A, B]): Iso[js.UndefOr[A], Option[B]] = new Iso[js.UndefOr[A], Option[B]] { @@ -39,6 +42,18 @@ object Isos { } } + implicit val isoUnsignedBigInt: Iso[js.BigInt, sigma.UnsignedBigInt] = new Iso[js.BigInt, sigma.UnsignedBigInt] { + override def to(x: js.BigInt): sigma.UnsignedBigInt = { + CUnsignedBigInt(new BigInteger(x.toString(10))) + } + + override def from(x: sigma.UnsignedBigInt): js.BigInt = { + val bi = x.asInstanceOf[CUnsignedBigInt].wrappedValue + val s = bi.toString(10) + js.BigInt(s) + } + } + implicit val isoBigIntToLong: Iso[js.BigInt, Long] = new Iso[js.BigInt, Long] { override def to(x: js.BigInt): Long = java.lang.Long.parseLong(x.toString(10)) diff --git a/core/js/src/main/scala/sigma/js/Type.scala b/core/js/src/main/scala/sigma/js/Type.scala index b323273a0c..ff391eba66 100644 --- a/core/js/src/main/scala/sigma/js/Type.scala +++ b/core/js/src/main/scala/sigma/js/Type.scala @@ -35,6 +35,9 @@ object Type extends js.Object { /** Descriptor of ErgoScript type BigInt. */ val BigInt = new Type(sigma.BigIntRType) + /** Descriptor of ErgoScript type UnsignedBigInt. */ + val UnsignedBigInt = new Type(sigma.UnsignedBigIntRType) + /** Descriptor of ErgoScript type GroupElement. */ val GroupElement = new Type(sigma.GroupElementRType) diff --git a/core/jvm/src/main/scala/sigma/crypto/Platform.scala b/core/jvm/src/main/scala/sigma/crypto/Platform.scala index b71694e81b..13c8d6515e 100644 --- a/core/jvm/src/main/scala/sigma/crypto/Platform.scala +++ b/core/jvm/src/main/scala/sigma/crypto/Platform.scala @@ -185,6 +185,7 @@ object Platform { case _: Int => tpe == SInt case _: Long => tpe == SLong case _: BigInt => tpe == SBigInt + case _: UnsignedBigInt => tpe == SUnsignedBigInt case _: String => tpe == SString // TODO v6.0: remove this case (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) case _: GroupElement => tpe.isGroupElement case _: SigmaProp => tpe.isSigmaProp diff --git a/core/shared/src/main/scala/sigma/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/Environment.scala b/core/shared/src/main/scala/sigma/Environment.scala index 959e02c1fb..432c40110c 100644 --- a/core/shared/src/main/scala/sigma/Environment.scala +++ b/core/shared/src/main/scala/sigma/Environment.scala @@ -14,7 +14,7 @@ sealed abstract class Environment { object Environment { /** Current runtime environment. */ - implicit val current: Environment = new Environment { + val current: Environment = new Environment { override def isJVM: Boolean = runtimePlatform == RuntimePlatform.JVM override def isJS: Boolean = runtimePlatform == RuntimePlatform.JS override def runtimePlatform: RuntimePlatform = sigma.reflection.Platform.runtimePlatform 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/Extensions.scala b/core/shared/src/main/scala/sigma/Extensions.scala index 81976a3c12..0c79df94ea 100644 --- a/core/shared/src/main/scala/sigma/Extensions.scala +++ b/core/shared/src/main/scala/sigma/Extensions.scala @@ -9,11 +9,6 @@ import sigma.data.RType * See `implicit class ...` wrappers below. */ object Extensions { - /** Extension methods for `Array[Byte]` not available for generic `Array[T]`. */ - implicit class CoreArrayByteOps(val arr: Array[Byte]) extends AnyVal { - /** Encodes array into hex string */ - @inline def toHex: String = Base16.encode(arr) - } /** Extension methods for `Array[T]` where implicit descriptor `RType[T]` is also * required. diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index df2b419273..e2870a3c8b 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -1,14 +1,11 @@ package sigma import java.math.BigInteger - import sigma.data._ /** - * All `modQ` operations assume that Q is a global constant (an order of the only one cryptographically strong group - * which is used for all cryptographic operations). - * So it is globally and implicitly used in all methods. - * */ + * Base class for signed 256-bits integers + */ trait BigInt { /** Convert this BigInt value to Byte. * @throws ArithmeticException if overflow happens. @@ -154,8 +151,204 @@ trait BigInt { */ def or(that: BigInt): BigInt def |(that: BigInt): BigInt = or(that) + + /** + * @return a big integer whose value is `this xor that`. + * This method returns a negative BigInteger if and only if exactly one of this and val are negative. + */ + def xor(that: BigInt): BigInt + + /** + * @return a 256-bit signed integer whose value is (this << n). `n` should be in 0..255 range (inclusive). + */ + def shiftLeft(n: Int): BigInt + + /** + * @return a 256-bit signed integer whose value is (this >> n). `n` should be in 0..255 range (inclusive). + */ + def shiftRight(n: Int): BigInt + + /** + * @return unsigned representation of this BigInt, or exception if its value is negative + */ + def toUnsigned: UnsignedBigInt + + /** + * @return unsigned representation of this BigInt modulo `m`. Cryptographic mod operation is done, so result is + * always non-negative + */ + def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt +} + +/** + * Base class for unsigned 256-bits integers + */ +trait UnsignedBigInt { + /** Convert this BigInt value to Byte. + * @throws ArithmeticException if overflow happens. + */ + def toByte: Byte + + /** Convert this BigInt value to Short. + * @throws ArithmeticException if overflow happens. + */ + def toShort: Short + + /** Convert this BigInt value to Int. + * @throws ArithmeticException if overflow happens. + */ + def toInt: Int + + /** Convert this BigInt value to Int. + * @throws ArithmeticException if overflow happens. + */ + def toLong: Long + + /** Returns a big-endian representation of this BigInt in a collection of bytes. + * For example, the value {@code 0x1213141516171819} would yield the + * byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}}. + */ + def toBytes: Coll[Byte] + + + /** Compares this numeric with that numeric for order. Returns a negative integer, zero, or a positive integer as the + * `this` is less than, equal to, or greater than `that`. + */ + def compareTo(that: UnsignedBigInt): Int + + /** Returns a BigInt whose value is {@code (this + that)}, or exception if result does not fit into 256 bits + * (consider using plusMod to avoid exception) + * + * @param that value to be added to this BigInt. + * @return { @code this + that} + */ + def add(that: UnsignedBigInt): UnsignedBigInt + def +(that: UnsignedBigInt): UnsignedBigInt = add(that) + + /** Returns a BigInt whose value is {@code (this - that)}, or exception if result is negative + * (consider using plusMod to avoid exception) + * + * @param that value to be subtracted from this BigInt. + * @return { @code this - that} + */ + def subtract(that: UnsignedBigInt): UnsignedBigInt + + def -(that: UnsignedBigInt): UnsignedBigInt = subtract(that) + + /** Returns a BigInt whose value is {@code (this * that)} , or exception if result does not fit into 256 bits + * (consider using multiplyMod to avoid exception) + * + * @implNote An implementation may offer better algorithmic + * performance when { @code that == this}. + * @param that value to be multiplied by this BigInt. + * @return { @code this * that} + */ + def multiply(that: UnsignedBigInt): UnsignedBigInt + def *(that: UnsignedBigInt): UnsignedBigInt = multiply(that) + + /** Returns a BigInt whose value is {@code (this / that)}. + * + * @param that value by which this BigInt is to be divided. + * @return { @code this / that} + * @throws ArithmeticException if { @code that} is zero. + */ + def divide(that: UnsignedBigInt): UnsignedBigInt + def /(that: UnsignedBigInt): UnsignedBigInt = divide(that) + + /** + * Returns a BigInt whose value is {@code (this mod m}). This method + * differs from {@code remainder} in that it always returns a + * non-negative BigInteger. + * + * @param m the modulus. + * @return { @code this mod m} + * @throws ArithmeticException { @code m} ≤ 0 + * @see #remainder + */ + def mod(m: UnsignedBigInt): UnsignedBigInt + def %(m: UnsignedBigInt): UnsignedBigInt = mod(m) + + /** + * Returns the minimum of this BigInteger and {@code val}. + * + * @param that value with which the minimum is to be computed. + * @return the BigInteger whose value is the lesser of this BigInteger and + * { @code val}. If they are equal, either may be returned. + */ + def min(that: UnsignedBigInt): UnsignedBigInt + + /** + * Returns the maximum of this BigInteger and {@code val}. + * + * @param that value with which the maximum is to be computed. + * @return the BigInteger whose value is the greater of this and + * { @code val}. If they are equal, either may be returned. + */ + def max(that: UnsignedBigInt): UnsignedBigInt + + /** Returns a BigInteger whose value is `(this & that)`. + * @param that value to be AND'ed with this BigInteger. + * @return `this & that` + */ + def and(that: UnsignedBigInt): UnsignedBigInt + def &(that: UnsignedBigInt): UnsignedBigInt = and(that) + + /** Returns a BigInteger whose value is `(this | that)`. + * + * @param that value to be OR'ed with this BigInteger. + * @return `this | that` + */ + def or(that: UnsignedBigInt): UnsignedBigInt + def |(that: UnsignedBigInt): UnsignedBigInt = or(that) + + def modInverse(m: UnsignedBigInt): UnsignedBigInt + + /** + * @return this + that mod m , where mod is cryptographic mod operation + */ + def plusMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt + + /** + * @return this - that mod m , where mod is cryptographic mod operation, so result is always non-negative + */ + def subtractMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt + + /** + * @return this * that mod m , where mod is cryptographic mod operation + */ + def multiplyMod(that: UnsignedBigInt, m: UnsignedBigInt): UnsignedBigInt + + /** + * @return an unsigned big integer whose value is `this xor that` + */ + def xor(that: UnsignedBigInt): UnsignedBigInt + + /** + * @return a 256-bit unsigned integer whose value is (this << n). The shift distance, n, may be negative, + * in which case this method performs a right shift. (Computes floor(this * 2n).) + */ + def shiftLeft(n: Int): UnsignedBigInt + + /** + * @return a 256-bit unsigned integer whose value is (this >> n). Sign extension is performed. The shift distance, n, + * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).) + */ + def shiftRight(n: Int): UnsignedBigInt + + /** + * @return an unsigned big integer value which is inverse of this (every bit is flipped) + */ + def bitwiseInverse(): UnsignedBigInt + + /** + * @return signed version of the same value, or exception if the value does not fit into signed type (since it is + * also about 256 bits, but one bit is encoding sign) + */ + def toSigned(): BigInt } + + /** Base class for points on elliptic curves. */ trait GroupElement { /** Checks if the provided element is an identity element. */ @@ -168,6 +361,12 @@ trait GroupElement { */ def exp(k: BigInt): GroupElement + /** Exponentiate this GroupElement to the given unsigned 256 bit integer. + * @param k The power. + * @return this to the power of k. + */ + def expUnsigned(k: UnsignedBigInt): GroupElement + /** Group operation. */ def multiply(that: GroupElement): GroupElement @@ -459,6 +658,22 @@ trait Header { /** Miner votes for changing system parameters. */ def votes: Coll[Byte] //3 bytes + + /** Bytes which are coming from future versions of the protocol, so + * their meaning is not known to current version of Sigma, but they + * are stored to get the same id as future version users. + */ + def unparsedBytes: Coll[Byte] + + /** + * @return header bytes without proof of work, a PoW is generated over them + */ + def serializeWithoutPoW: Coll[Byte] + + /** + * @return result of header's proof-of-work validation + */ + def checkPow: Boolean } /** Runtime representation of Context ErgoTree type. @@ -557,6 +772,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. @@ -695,6 +921,20 @@ trait SigmaDslBuilder { */ def groupGenerator: GroupElement + /** + * @return NBits-encoded approximate representation of given big integer, + * see (https://bitcoin.stackexchange.com/questions/57184/what-does-the-nbits-value-represent) + * for NBits format details + */ + def encodeNbits(bi: BigInt): Long + + /** + * @return big integer decoded from NBits value provided, + * see (https://bitcoin.stackexchange.com/questions/57184/what-does-the-nbits-value-represent) + * for format details + */ + def decodeNbits(l: Long): BigInt + /** * Transforms serialized bytes of ErgoTree with segregated constants by replacing constants * at given positions with new values. This operation allow to use serialized scripts as @@ -721,13 +961,32 @@ trait SigmaDslBuilder { /** Create DSL big integer from existing `java.math.BigInteger`*/ def BigInt(n: BigInteger): BigInt + /** Create DSL unsigned big integer from existing `java.math.BigInteger`*/ + def UnsignedBigInt(n: BigInteger): UnsignedBigInt + /** Extract `java.math.BigInteger` from DSL's `BigInt` type*/ def toBigInteger(n: BigInt): BigInteger /** 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] + + /** Calculates value of a custom Autolykos 2 hash function */ + def powHit(k: Int, msg: Coll[Byte], nonce: Coll[Byte], h: Coll[Byte], N: Int): BigInt + + /** Deserializes provided `bytes` into a value of type `T`. **/ + def deserializeTo[T](bytes: Coll[Byte])(implicit cT: RType[T]): T + + /** Returns a number decoded from provided big-endian bytes array. */ + def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T + + def some[T](value: T)(implicit cT: RType[T]): Option[T] + + def none[T]()(implicit cT: RType[T]): Option[T] } diff --git a/core/shared/src/main/scala/sigma/VersionContext.scala b/core/shared/src/main/scala/sigma/VersionContext.scala index 67b7079fea..6ede0a1556 100644 --- a/core/shared/src/main/scala/sigma/VersionContext.scala +++ b/core/shared/src/main/scala/sigma/VersionContext.scala @@ -1,6 +1,6 @@ package sigma -import VersionContext.JitActivationVersion +import VersionContext.{JitActivationVersion, V6SoftForkVersion} import scala.util.DynamicVariable @@ -21,6 +21,10 @@ case class VersionContext(activatedVersion: Byte, ergoTreeVersion: Byte) { /** @return true, if the activated script version of Ergo protocol on the network is * greater than v1. */ def isJitActivated: Boolean = activatedVersion >= JitActivationVersion + + /** @return true, if the activated script version of Ergo protocol on the network is + * including v6.0 update. */ + def isV6SoftForkActivated: Boolean = activatedVersion >= V6SoftForkVersion } object VersionContext { @@ -31,13 +35,21 @@ object VersionContext { * - version 3.x this value must be 0 * - in v4.0 must be 1 * - in v5.x must be 2 + * - in 6.x must be 3 * etc. */ - val MaxSupportedScriptVersion: Byte = 2 // supported versions 0, 1, 2 + val MaxSupportedScriptVersion: Byte = 3 // supported versions 0, 1, 2, 3 - /** The first version of ErgoTree starting from which the JIT costing interpreter is used. */ + /** The first version of ErgoTree starting from which the JIT costing interpreter must be used. + * It must also be used for all subsequent versions (3, 4, etc). + */ val JitActivationVersion: Byte = 2 + /** + * The version of ErgoTree corresponding to "evolution" (6.0) soft-fork + */ + val V6SoftForkVersion: Byte = 3 + private val _defaultContext = VersionContext( activatedVersion = 1 /* v4.x */, ergoTreeVersion = 1 diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index e9ea0d43f0..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} +import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp, UnsignedBigInt, VersionContext} import java.math.BigInteger @@ -94,7 +94,6 @@ object SType { val paramOV = STypeParam(tOV) val paramIVSeq: Seq[STypeParam] = Array(paramIV) - val IndexedSeqOfT1: IndexedSeq[SType] = Array(SType.tT) val IndexedSeqOfT2: IndexedSeq[SType] = Array(SType.tT, SType.tT) /** Immutable empty array, can be used to avoid repeated allocations. */ @@ -103,38 +102,73 @@ 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 * corresponding methods. * - * NOTE: in the current implementation only monomorphic methods are supported (without - * type parameters) + * @note starting from v6.0 methods with type parameters are also supported. * - * NOTE2: in v3.x SNumericType.typeId is silently shadowed by SGlobal.typeId as part of - * `toMap` operation. As a result, the methods collected into SByte.methods cannot be + * @note on versioning: + * In v3.x-5.x SNumericType.typeId is silently shadowed by SGlobal.typeId as part of + * `toMap` operation. As a result, SNumericTypeMethods container cannot be resolved by + * typeId = 106, because SNumericType was being silently removed when `_types` map is + * constructed. See `property("SNumericType.typeId resolves to SGlobal")`. + * In addition, the methods associated with the concrete numeric types cannot be * resolved (using SMethod.fromIds()) for all numeric types (SByte, SShort, SInt, - * SLong, SBigInt). See the corresponding regression `property("MethodCall on numerics")`. + * SLong) because these types are not registered in the `_types` map. + * See the corresponding property("MethodCall on numerics")`. * However, this "shadowing" is not a problem since all casting methods are implemented - * via Downcast, Upcast opcodes and the remaining `toBytes`, `toBits` methods are not - * implemented at all. - * In order to allow MethodCalls on numeric types in future versions the SNumericType.typeId - * should be changed and SGlobal.typeId should be preserved. The regression tests in - * `property("MethodCall Codes")` should pass. + * via lowering to Downcast, Upcast opcodes and the remaining `toBytes`, `toBits` + * methods are not implemented at all. + * + * Starting from v6.0 the SNumericType.typeId is demoted as a receiver object of + * method calls and: + * 1) numeric type SByte, SShort, SInt, SLong are promoted as receivers and added to + * the _types map. + * 2) all methods from SNumericTypeMethods are copied to all the concrete numeric types + * (SByte, SShort, SInt, SLong, SBigInt) and the generic tNum type parameter is + * specialized accordingly. + * + * 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. */ - // TODO v6.0: should contain all numeric types (including also SNumericType) - // to support method calls like 10.toByte which encoded as MethodCall with typeId = 4, methodId = 1 - // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 - lazy val types: Map[Byte, STypeCompanion] = Seq( - SBoolean, SNumericType, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, + private val v5Types = Seq( + SBoolean, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, SAvlTree, SBox, SOption, SCollection, SBigInt - ).map { t => (t.typeId, t) }.toMap + ) + private val v6Types = v5Types ++ Seq(SByte, SShort, SInt, SLong, SUnsignedBigInt) + + private val v5TypesMap = v5Types.map { t => (t.typeId, t) }.toMap + + private val v6TypesMap = v6Types.map { t => (t.typeId, t) }.toMap + + def types: Map[Byte, STypeCompanion] = if (VersionContext.current.isV6SoftForkActivated) { + v6TypesMap + } else { + v5TypesMap + } /** Checks that the type of the value corresponds to the descriptor `tpe`. * If the value has complex structure only root type constructor is checked. @@ -146,7 +180,7 @@ object SType { * 2) `isValueOfType == true` for each tree leaf * 3) `isValueOfType == true` for each sub-expression * - * @param value value to check type + * @param x value to check type * @param tpe type descriptor to check value against * @return true if the given `value` is of type tpe` */ @@ -157,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] @@ -220,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. */ @@ -287,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 @@ -339,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 @@ -376,6 +409,8 @@ case object SByte extends SPrimType with SEmbeddable with SNumericType with SMon case s: Short => s.toByteExact case i: Int => i.toByteExact case l: Long => l.toByteExact + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toByte // toByteExact from int is called under the hood + case 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") } } @@ -397,6 +432,8 @@ case object SShort extends SPrimType with SEmbeddable with SNumericType with SMo case s: Short => s case i: Int => i.toShortExact case l: Long => l.toShortExact + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toShort // toShortExact from int is called under the hood + case 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") } } @@ -420,6 +457,8 @@ case object SInt extends SPrimType with SEmbeddable with SNumericType with SMono case s: Short => s.toInt case i: Int => i case l: Long => l.toIntExact + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toInt + case ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toInt case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -445,17 +484,18 @@ case object SLong extends SPrimType with SEmbeddable with SNumericType with SMon case s: Short => s.toLong case i: Int => i.toLong case l: Long => l + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toLong + case ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toLong case _ => sys.error(s"Cannot downcast value $v to the type $this") } } -/** Type of 256 bit integet values. Implemented using [[java.math.BigInteger]]. */ +/** Type of 256-bit signed integer values. Implemented using [[java.math.BigInteger]]. */ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SMonoType { override type WrappedType = BigInt override val typeCode: TypeCode = 6: Byte override val reprClass: RClass[_] = RClass(classOf[BigInt]) override def typeId = typeCode - implicit def typeBigInt: SBigInt.type = this /** Type of Relation binary op like GE, LE, etc. */ val RelationOpType = SFunc(Array(SBigInt, SBigInt), SBoolean) @@ -465,25 +505,78 @@ 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)) + case x: Short => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Int => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Long => CBigInt(BigInteger.valueOf(x)) + case x: BigInt if VersionContext.current.isV6SoftForkActivated => x + case _ => sys.error(s"Cannot upcast value $v to the type $this") + } + } + + // 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)) + case x: Short => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Int => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Long => CBigInt(BigInteger.valueOf(x)) + case x: BigInt if VersionContext.current.isV6SoftForkActivated => x + case _ => sys.error(s"Cannot downcast value $v to the type $this") + } + } +} + +/** 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") } - CBigInt(bi) + if(bi.compareTo(BigInteger.ZERO) >= 0) { + CUnsignedBigInt(bi) + } else { + sys.error(s"Cannot upcast negative value $v to the type $this") + } } - override def downcast(v: AnyVal): BigInt = { + + // 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") } - CBigInt(bi) + if (bi.compareTo(BigInteger.ZERO) >= 0) { + CUnsignedBigInt(bi) + } else { + sys.error(s"Cannot upcast negative value $v to the type $this") + } } } @@ -584,7 +677,7 @@ case class SFunc(tDom: IndexedSeq[SType], tRange: SType, tpeParams: Seq[STypePa } object SFunc { - final val FuncTypeCode: TypeCode = TypeCodes.FirstFuncType + final val FuncTypeCode: TypeCode = TypeCodes.FuncType def apply(tDom: SType, tRange: SType): SFunc = SFunc(Array(tDom), tRange) // HOTSPOT: val identity = { x: Any => x } } @@ -603,7 +696,8 @@ object STypeApply { /** Type description of optional values. Instances of `Option` * are either constructed by `Some` or by `None` constructors. */ case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct with SGenericType { - override type WrappedType = Option[ElemType#WrappedType] + type ElemWrappedType = ElemType#WrappedType + override type WrappedType = Option[ElemWrappedType] override val typeCode: TypeCode = SOption.OptionTypeCode override def toString = s"Option[$elemType]" override def toTermString: String = s"Option[${elemType.toTermString}]" @@ -624,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) @@ -641,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) @@ -650,7 +746,6 @@ object SOption extends STypeCompanion { def apply[T <: SType](implicit elemType: T, ov: Overloaded1): SOption[T] = SOption(elemType) } - /** Base class for descriptors of `Coll[T]` ErgoTree type for some elemType T. */ trait SCollection[T <: SType] extends SProduct with SGenericType { def elemType: T @@ -702,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/core/shared/src/main/scala/sigma/ast/STypeParam.scala b/core/shared/src/main/scala/sigma/ast/STypeParam.scala index 56d89d01f8..08349ae024 100644 --- a/core/shared/src/main/scala/sigma/ast/STypeParam.scala +++ b/core/shared/src/main/scala/sigma/ast/STypeParam.scala @@ -2,18 +2,10 @@ package sigma.ast /** Represents a type parameter in a type system. * - * @param ident The identifier for this type parameter. - * @param upperBound The upper bound of this type parameter, if exists. - * @param lowerBound The lower bound of this type parameter, if exists. - * @note Type parameters with bounds are currently not supported. + * @param ident The identifier for this type parameter */ -case class STypeParam( - ident: STypeVar, - upperBound: Option[SType] = None, - lowerBound: Option[SType] = None) { - assert(upperBound.isEmpty && lowerBound.isEmpty, s"Type parameters with bounds are not supported, but found $this") - - override def toString = ident.toString + upperBound.fold("")(u => s" <: $u") + lowerBound.fold("")(l => s" >: $l") +case class STypeParam(ident: STypeVar) { + override def toString = ident.toString } object STypeParam { diff --git a/core/shared/src/main/scala/sigma/ast/TypeCodes.scala b/core/shared/src/main/scala/sigma/ast/TypeCodes.scala index 1b7b6121d6..68670449db 100644 --- a/core/shared/src/main/scala/sigma/ast/TypeCodes.scala +++ b/core/shared/src/main/scala/sigma/ast/TypeCodes.scala @@ -14,10 +14,8 @@ object TypeCodes { val LastDataType : TypeCode = TypeCode @@ 111.toByte - /** SFunc types occupy remaining space of byte values [FirstFuncType .. 255] */ - val FirstFuncType: TypeCode = TypeCode @@ (LastDataType + 1).toByte - - val LastFuncType : TypeCode = TypeCode @@ 255.toByte + /** SFunc type */ + val FuncType: TypeCode = TypeCode @@ (LastDataType + 1).toByte /** We use optimized encoding of constant values to save space in serialization. * Since Box registers are stored as Constant nodes we save 1 byte for each register. diff --git a/core/shared/src/main/scala/sigma/ast/package.scala b/core/shared/src/main/scala/sigma/ast/package.scala index 63a2cfbcba..ec51ca6e3a 100644 --- a/core/shared/src/main/scala/sigma/ast/package.scala +++ b/core/shared/src/main/scala/sigma/ast/package.scala @@ -137,6 +137,12 @@ package object ast { def asNumType: SNumericType = tpe.asInstanceOf[SNumericType] + /** Cast this type to numeric type or else throws the given error. */ + def asNumTypeOrElse(error: => Exception): SNumericType = tpe match { + case nt: SNumericType => nt + case _ => throw error + } + def asFunc: SFunc = tpe.asInstanceOf[SFunc] def asProduct: SProduct = tpe.asInstanceOf[SProduct] diff --git a/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 bbf1a85e46..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,9 +45,120 @@ 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)) override def or(that: BigInt): BigInt = CBigInt(wrappedValue.or(that.asInstanceOf[CBigInt].wrappedValue)) + + override def xor(that: BigInt): BigInt = CBigInt(wrappedValue.xor(that.asInstanceOf[CBigInt].wrappedValue)) + + override def shiftLeft(n: Int): BigInt = CBigInt(wrappedValue.shiftLeft(n).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) + } + } 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/Iso.scala b/core/shared/src/main/scala/sigma/data/Iso.scala index 9b90d63bb0..0e463bdd04 100644 --- a/core/shared/src/main/scala/sigma/data/Iso.scala +++ b/core/shared/src/main/scala/sigma/data/Iso.scala @@ -7,14 +7,20 @@ import sigma.{Coll, Colls} /** Type-class of isomorphisms between types. * Isomorphism between two types `A` and `B` essentially say that both types * represents the same information (entity) but in a different way. + * + * Each isomorphism defined by two functions: + * - `to: A => B` - conversion from `A` to `B` + * - `from: B => A` - conversion from `B` to `A` + * *

- * The information is not lost so that both are true: + * such that the information is not lost during conversion, so that both are true: * 1) a == from(to(a)) * 2) b == to(from(b)) *

* It is used to define type-full conversions: * - different conversions between Java and Scala data types. - * - conversion between Ergo representations and generated API representations + * - conversion between internal Ergo representations and API representations + * - conversions between exported JS classes and internal Ergo representations */ abstract class Iso[A, B] { def to(a: A): B 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 028e68bf72..9765f8f58b 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -101,29 +101,85 @@ object ReflectionData { } { val clazz = classOf[sigma.BigInt] - val paramTypes = Array[Class[_]](clazz) + val noParamTypes = Array[Class[_]]() + val oneParamTypes = Array[Class[_]](clazz) registerClassEntry(clazz, methods = Map( - mkMethod(clazz, "add", paramTypes) { (obj, args) => + mkMethod(clazz, "add", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].add(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "max", paramTypes) { (obj, args) => + mkMethod(clazz, "max", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].max(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "min", paramTypes) { (obj, args) => + mkMethod(clazz, "min", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].min(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "subtract", paramTypes) { (obj, args) => + mkMethod(clazz, "subtract", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].subtract(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "multiply", paramTypes) { (obj, args) => + mkMethod(clazz, "multiply", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].multiply(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "mod", paramTypes) { (obj, args) => + mkMethod(clazz, "mod", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].mod(args(0).asInstanceOf[BigInt]) }, - mkMethod(clazz, "divide", paramTypes) { (obj, args) => + mkMethod(clazz, "divide", oneParamTypes) { (obj, args) => obj.asInstanceOf[BigInt].divide(args(0).asInstanceOf[BigInt]) + }, + mkMethod(clazz, "toUnsigned", noParamTypes) { (obj, _) => + obj.asInstanceOf[BigInt].toUnsigned + }, + mkMethod(clazz, "toUnsignedMod", Array[Class[_]](classOf[sigma.UnsignedBigInt])) { (obj, args) => + obj.asInstanceOf[BigInt].toUnsignedMod(args(0).asInstanceOf[UnsignedBigInt]) + } + ) + ) + } + { + val clazz = classOf[sigma.UnsignedBigInt] + val noParamTypes = Array[Class[_]]() + val oneParamTypes = Array[Class[_]](clazz) + val twoParamTypes = Array[Class[_]](clazz, clazz) + registerClassEntry(clazz, + methods = Map( + mkMethod(clazz, "add", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].add(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "max", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].max(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "min", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].min(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "subtract", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].subtract(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "multiply", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].multiply(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "mod", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "divide", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].divide(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "mod", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "modInverse", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].modInverse(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "plusMod", twoParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].plusMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "subtractMod", twoParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].subtractMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "multiplyMod", twoParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].multiplyMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "toSigned", noParamTypes) { (obj, _) => + obj.asInstanceOf[UnsignedBigInt].toSigned() } ) ) @@ -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]) }, @@ -346,6 +423,9 @@ object ReflectionData { }, mkMethod(clazz, "powDistance", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[Header].powDistance + }, + mkMethod(clazz, "checkPow", Array[Class[_]]()) { (obj, _) => + obj.asInstanceOf[Header].checkPow } ) ) @@ -441,8 +521,34 @@ 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, "deserializeTo", Array[Class[_]](cColl, classOf[RType[_]])) { (obj, args) => + obj.asInstanceOf[SigmaDslBuilder].deserializeTo(args(0).asInstanceOf[Coll[Byte]])(args(1).asInstanceOf[RType[_]]) + }, + mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](cColl, classOf[RType[_]])) { (obj, args) => + obj.asInstanceOf[SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[Coll[Byte]])(args(1).asInstanceOf[RType[_]]) + }, + mkMethod(clazz, "powHit", Array[Class[_]](classOf[Int], cColl, cColl, cColl, classOf[Int])) { (obj, args) => + obj.asInstanceOf[SigmaDslBuilder].powHit(args(0).asInstanceOf[Int], args(1).asInstanceOf[Coll[Byte]], + args(2).asInstanceOf[Coll[Byte]], args(3).asInstanceOf[Coll[Byte]], args(4).asInstanceOf[Int]) + }, + mkMethod(clazz, "encodeNbits", Array[Class[_]](classOf[BigInt])) { (obj, args) => + obj.asInstanceOf[SigmaDslBuilder].encodeNbits(args(0).asInstanceOf[BigInt]) + }, + mkMethod(clazz, "decodeNbits", Array[Class[_]](classOf[Long])) { (obj, args) => + obj.asInstanceOf[SigmaDslBuilder].decodeNbits(args(0).asInstanceOf[Long]) + }, + mkMethod(clazz, "some", Array[Class[_]](classOf[Object], classOf[RType[_]])) { (obj, args) => + obj.asInstanceOf[SigmaDslBuilder].some(args(0).asInstanceOf[Any])(args(1).asInstanceOf[RType[Any]]) + }, + mkMethod(clazz, "none", Array[Class[_]](classOf[RType[_]])) { (obj, args) => + obj.asInstanceOf[SigmaDslBuilder].none()(args(0).asInstanceOf[RType[_]]) } ) ) diff --git a/core/shared/src/main/scala/sigma/serialization/CoreByteReader.scala b/core/shared/src/main/scala/sigma/serialization/CoreByteReader.scala index 34ed74ac6a..e238829b6c 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreByteReader.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreByteReader.scala @@ -10,10 +10,6 @@ import sigma.validation.ValidationRules.CheckPositionLimit * methods. * * @param r the underlying reader this reader reads from - * @param constantStore the store of constants which is used to resolve - * [[sigma.ast.ConstantPlaceholder]] - * @param resolvePlaceholdersToConstants if true then resolved constants will be - * substituted in the tree instead of the placeholder. * @param maxTreeDepth limit on the tree depth (recursive invocations) * of the deserializer */ 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 479b199da5..175e91fbcb 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala @@ -2,8 +2,9 @@ package sigma.serialization import debox.cfor import sigma.ast._ +import sigma.crypto.BigIntegers import sigma.data._ -import sigma.util.Extensions.{CoreAvlTreeOps, BigIntOps, GroupElementOps, SigmaPropOps} +import sigma.util.Extensions.{BigIntOps, CoreAvlTreeOps, GroupElementOps, SigmaPropOps} import sigma.validation.ValidationRules.CheckSerializableTypeCode import sigma.{Evaluation, _} @@ -33,6 +34,10 @@ class CoreDataSerializer { val data = v.asInstanceOf[BigInt].toBigInteger.toByteArray w.putUShort(data.length) w.putBytes(data) + case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated => + val data = BigIntegers.asUnsignedByteArray(v.asInstanceOf[CUnsignedBigInt].wrappedValue) + w.putUShort(data.length) + w.putBytes(data) case SGroupElement => GroupElementSerializer.serialize(v.asInstanceOf[GroupElement].toECPoint, w) case SSigmaProp => @@ -68,7 +73,12 @@ class CoreDataSerializer { i += 1 } - // TODO v6.0 (3h): support Option[T] (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659) + case SOption(elemType) if VersionContext.current.isV6SoftForkActivated => + val o = v.asInstanceOf[Option[elemType.WrappedType]] + w.putOption(o){case (w, v) => + serialize(v, elemType, w) + } + case _ => CheckSerializableTypeCode(tpe.typeCode) throw new SerializerException(s"Don't know how to serialize ($v, $tpe)") @@ -103,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 => @@ -118,6 +135,10 @@ class CoreDataSerializer { }.toArray[Any] val coll = Colls.fromArray(arr)(sigma.AnyType) Evaluation.toDslTuple(coll, tuple) + case tOption: SOption[_] if VersionContext.current.isV6SoftForkActivated => + r.getOption[tOption.ElemWrappedType] { + deserialize(tOption.elemType, r).asInstanceOf[tOption.ElemWrappedType] + } case t => CheckSerializableTypeCode(t.typeCode) throw new SerializerException(s"Not defined DataSerializer for type $t") @@ -126,7 +147,7 @@ class CoreDataSerializer { res } - def deserializeColl[T <: SType](len: Int, tpeElem: T, r: CoreByteReader): Coll[T#WrappedType] = + private def deserializeColl[T <: SType](len: Int, tpeElem: T, r: CoreByteReader): Coll[T#WrappedType] = tpeElem match { case SBoolean => Colls.fromArray(r.getBits(len)).asInstanceOf[Coll[T#WrappedType]] diff --git a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala index 9c84de0944..aa5d43e229 100644 --- a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala @@ -1,9 +1,9 @@ package sigma.serialization import debox.cfor +import sigma.VersionContext import sigma.ast.SCollectionType.{CollectionTypeCode, NestedCollectionTypeCode} import sigma.ast._ -import sigma.serialization.{CoreByteReader, CoreByteWriter, InvalidTypePrefix} import sigma.util.safeNewArray import sigma.validation.ValidationRules.{CheckPrimitiveTypeCode, CheckTypeCode} @@ -101,6 +101,17 @@ class TypeSerializer { // `Tuple` type with more than 4 items `(Int, Byte, Box, Boolean, Int)` serializeTuple(tup, w) } + case SFunc(tDom, tRange, tpeParams) => + w.put(SFunc.FuncTypeCode) + w.putUByte(tDom.length) + tDom.foreach { st => + serialize(st, w) + } + serialize(tRange, w) + w.putUByte(tpeParams.length) + tpeParams.foreach { tp => + serialize(tp.ident, w) + } case typeIdent: STypeVar => { w.put(typeIdent.typeCode) val bytes = typeIdent.name.getBytes(StandardCharsets.UTF_8) @@ -189,7 +200,23 @@ class TypeSerializer { case SHeader.typeCode => SHeader case SPreHeader.typeCode => SPreHeader case SGlobal.typeCode => SGlobal + case SFunc.FuncTypeCode if VersionContext.current.isV6SoftForkActivated => + val tdLength = r.getUByte() + + val tDom = (1 to tdLength).map { _ => + deserialize(r) + } + val tRange = deserialize(r) + val tpeParamsLength = r.getUByte() + val tpeParams = (1 to tpeParamsLength).map { _ => + val ident = deserialize(r) + require(ident.isInstanceOf[STypeVar]) + STypeParam(ident.asInstanceOf[STypeVar]) + } + SFunc(tDom, tRange, tpeParams) case _ => + // todo: 6.0: replace 1008 check with identical behavior but other opcode, to activate + // ReplacedRule(1008 -> new opcode) during 6.0 activation CheckTypeCode(c.toByte) NoType } @@ -215,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/main/scala/sigma/util/NBitsUtils.scala b/core/shared/src/main/scala/sigma/util/NBitsUtils.scala new file mode 100644 index 0000000000..36d526d1d5 --- /dev/null +++ b/core/shared/src/main/scala/sigma/util/NBitsUtils.scala @@ -0,0 +1,84 @@ +package sigma.util + +import java.math.BigInteger + +object NBitsUtils { + + /** + *

The "compact" format is a representation of a whole number N using an unsigned 32 bit number similar to a + * floating point format. The most significant 8 bits are the unsigned exponent of base 256. This exponent can + * be thought of as "number of bytes of N". The lower 23 bits are the mantissa. Bit number 24 (0x800000) represents + * the sign of N. Therefore, N = (-1^sign) * mantissa * 256^(exponent-3).

+ * + *

Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). MPI uses the most significant bit of the + * first byte as sign. Thus 0x1234560000 is compact 0x05123456 and 0xc0de000000 is compact 0x0600c0de. Compact + * 0x05c0de00 would be -0x40de000000.

+ * + *

Bitcoin only uses this "compact" format for encoding difficulty targets, which are unsigned 256bit quantities. + * Thus, all the complexities of the sign bit and using base 256 are probably an implementation accident.

+ */ + def decodeCompactBits(compact: Long): BigInt = { + val size: Int = (compact >> 24).toInt & 0xFF + val bytes: Array[Byte] = new Array[Byte](4 + size) + bytes(3) = size.toByte + if (size >= 1) bytes(4) = ((compact >> 16) & 0xFF).toByte + if (size >= 2) bytes(5) = ((compact >> 8) & 0xFF).toByte + if (size >= 3) bytes(6) = (compact & 0xFF).toByte + decodeMPI(bytes) + } + + /** + * @see Utils#decodeCompactBits(long) + */ + def encodeCompactBits(requiredDifficulty: BigInt): Long = { + val value = requiredDifficulty.bigInteger + var result: Long = 0L + var size: Int = value.toByteArray.length + if (size <= 3) { + result = value.longValue << 8 * (3 - size) + } else { + result = value.shiftRight(8 * (size - 3)).longValue + } + // The 0x00800000 bit denotes the sign. + // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. + if ((result & 0x00800000L) != 0) { + result >>= 8 + size += 1 + } + result |= size << 24 + val a: Int = if (value.signum == -1) 0x00800000 else 0 + result |= a + result + } + + + /** Parse 4 bytes from the byte array (starting at the offset) as unsigned 32-bit integer in big endian format. */ + def readUint32BE(bytes: Array[Byte]): Long = ((bytes(0) & 0xffL) << 24) | ((bytes(1) & 0xffL) << 16) | ((bytes(2) & 0xffL) << 8) | (bytes(3) & 0xffL) + + /** + * MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function. They consist of + * a 4 byte big endian length field, followed by the stated number of bytes representing + * the number in big endian format (with a sign bit). + * + */ + private def decodeMPI(mpi: Array[Byte]): BigInteger = { + + val length: Int = readUint32BE(mpi).toInt + val buf = new Array[Byte](length) + System.arraycopy(mpi, 4, buf, 0, length) + + if (buf.length == 0) { + BigInteger.ZERO + } else { + val isNegative: Boolean = (buf(0) & 0x80) == 0x80 + if (isNegative) buf(0) = (buf(0) & 0x7f).toByte + val result: BigInteger = new BigInteger(buf) + if (isNegative) { + result.negate + } else { + result + } + } + } + +} 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 a17fc7a7f9..a73452a838 100644 --- a/core/shared/src/test/scala/sigma/VersionTesting.scala +++ b/core/shared/src/test/scala/sigma/VersionTesting.scala @@ -6,13 +6,11 @@ import scala.util.DynamicVariable trait VersionTesting { - /** In v5.x we run test for only one activated version on the network (== 2). - * In the branch for v6.0 the new version 3 should be added so that the tests run for both. - */ + /** Tests run for both version 2 & version 3 */ protected val activatedVersions: Seq[Byte] = (0 to VersionContext.MaxSupportedScriptVersion).map(_.toByte).toArray[Byte] - private[sigma] val _currActivatedVersion = new DynamicVariable[Byte](2) // v5.x by default + private[sigma] val _currActivatedVersion = new DynamicVariable[Byte](3) // v6.x by default /** Current activated version used in tests. */ def activatedVersionInTests: Byte = _currActivatedVersion.value @@ -43,7 +41,6 @@ trait VersionTesting { // for each tree version up to currently activated, set it up and execute block _currErgoTreeVersion.withValue(treeVersion)(block) } - } } } @@ -75,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..63c2ab5555 100644 --- a/data/js/src/main/scala/sigma/Platform.scala +++ b/data/js/src/main/scala/sigma/Platform.scala @@ -14,30 +14,33 @@ object Platform { private[sigma] def liftToConstant(obj: Any, builder: SigmaBuilder): Nullable[Constant[SType]] = { import builder._ obj match { - case arr: Array[Boolean] => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean)) - case arr: Array[Byte] => Nullable(mkCollectionConstant[SByte.type](arr, SByte)) - case arr: Array[Short] => Nullable(mkCollectionConstant[SShort.type](arr, SShort)) - case arr: Array[Int] => Nullable(mkCollectionConstant[SInt.type](arr, SInt)) - case arr: Array[Long] => Nullable(mkCollectionConstant[SLong.type](arr, SLong)) - case arr: Array[BigInteger] => Nullable(mkCollectionConstant[SBigInt.type](arr.map[BigInt](n => CBigInt(n)), SBigInt)) - case arr: Array[String] => Nullable(mkCollectionConstant[SString.type](arr, SString)) + case arr: Array[Boolean] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean)) + case arr: Array[Byte] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SByte.type](arr, SByte)) + case arr: Array[Short] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SShort.type](arr, SShort)) + case arr: Array[Int] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SInt.type](arr, SInt)) + case arr: Array[Long] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SLong.type](arr, SLong)) + case arr: Array[BigInteger] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SBigInt.type](arr.map[BigInt](n => CBigInt(n)), SBigInt)) + case arr: Array[String] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SString.type](arr, SString)) case v: AnyValue => val tpe = Evaluation.rtypeToSType(v.tVal) Nullable(mkConstant[tpe.type](v.value.asInstanceOf[tpe.WrappedType], tpe)) case v: Int => Nullable(mkConstant[SInt.type](v, SInt)) case v: Long => Nullable(mkConstant[SLong.type](v, SLong)) - case v: BigInteger => Nullable(mkConstant[SBigInt.type](CBigInt(v), SBigInt)) + case v: BigInteger if !VersionContext.current.isV6SoftForkActivated => 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)) + case v: String if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SString.type](v, SString)) + case h: Header if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SHeader.type](h, SHeader)) + case h: PreHeader if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SPreHeader.type](h, SPreHeader)) // The Box lifting was broken in v4.x. `SigmaDsl.Box(b)` was missing which means the // isCorrectType requirement would fail in ConstantNode constructor. // This method is used as part of consensus in SubstConstants operation, however // ErgoBox cannot be passed as argument as it is never valid value during evaluation. // Thus we can use activation-based versioning and fix this code when v5.0 is activated. - case b: ErgoBox => + case b: ErgoBox if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SBox.type](CBox(b), SBox)) // fixed in v5.0 // this case is added in v5.0 and it can be useful when the box value comes from a @@ -47,7 +50,7 @@ object Platform { Nullable(mkConstant[SBox.type](b, SBox)) else Nullable.None // return the same result as in v4.x when there was no this case - case avl: AvlTreeData => Nullable(mkConstant[SAvlTree.type](CAvlTree(avl), SAvlTree)) + case avl: AvlTreeData if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SAvlTree.type](CAvlTree(avl), SAvlTree)) case avl: AvlTree => Nullable(mkConstant[SAvlTree.type](avl, SAvlTree)) case sb: SigmaBoolean => Nullable(mkConstant[SSigmaProp.type](CSigmaProp(sb), SSigmaProp)) case p: SigmaProp => Nullable(mkConstant[SSigmaProp.type](p, SSigmaProp)) diff --git a/data/js/src/main/scala/sigma/data/js/Isos.scala b/data/js/src/main/scala/sigma/data/js/Isos.scala new file mode 100644 index 0000000000..22300b1fd8 --- /dev/null +++ b/data/js/src/main/scala/sigma/data/js/Isos.scala @@ -0,0 +1,152 @@ +package sigma.data.js + +import org.ergoplatform.ErgoBox._ +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate} +import scorex.crypto.authds.ADKey +import scorex.util.encode.Base16 +import sigma.Extensions._ +import sigma.ast.syntax.GroupElementConstant +import sigma.ast.{Constant, GroupElementConstant, SType} +import sigma.data.Iso.isoStringToArray +import sigma.data.{CGroupElement, Digest32Coll, Iso} +import sigma.js.GroupElement +import sigma.serialization.{ErgoTreeSerializer, ValueSerializer} +import sigma.{Coll, Colls} +import sigmastate.fleetSdkCommon.distEsmTypesCommonMod.HexString +import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters +import sigmastate.fleetSdkCommon.{distEsmTypesBoxesMod => boxesMod, distEsmTypesCommonMod => commonMod, distEsmTypesRegistersMod => registersMod, distEsmTypesTokenMod => tokenMod} + +import scala.scalajs.js + +/** Definitions of isomorphisms for sigma-data module. + * @see sigma.data.Iso + */ +object Isos { + + val isoStringToGroupElement: Iso[String, sigma.GroupElement] = new Iso[String, sigma.GroupElement] { + override def to(x: String): sigma.GroupElement = { + val bytes = Base16.decode(x).get + ValueSerializer.deserialize(bytes).asInstanceOf[GroupElementConstant].value + } + override def from(x: sigma.GroupElement): String = { + val bytes = ValueSerializer.serialize(GroupElementConstant(x)) + Base16.encode(bytes) + } + } + + val isoGroupElement: Iso[GroupElement, sigma.GroupElement] = new Iso[GroupElement, sigma.GroupElement] { + override def to(x: GroupElement): sigma.GroupElement = { + CGroupElement(x.point) + } + override def from(x: sigma.GroupElement): GroupElement = { + new GroupElement(x.asInstanceOf[CGroupElement].wrappedValue) + } + } + + implicit val isoBoxId: Iso[boxesMod.BoxId, ErgoBox.BoxId] = new Iso[boxesMod.BoxId, ErgoBox.BoxId] { + override def to(x: boxesMod.BoxId): ErgoBox.BoxId = ADKey @@@ isoStringToArray.to(x) + + override def from(x: ErgoBox.BoxId): boxesMod.BoxId = isoStringToArray.from(x) + } + + implicit val isoHexStringToConstant: Iso[HexString, Constant[SType]] = new Iso[HexString, Constant[SType]] { + override def to(x: HexString): Constant[SType] = { + val bytes = isoStringToArray.to(x) + val value = ValueSerializer.deserialize(bytes) + value.asInstanceOf[Constant[SType]] + } + override def from(x: Constant[SType]): HexString = { + val bytes = ValueSerializer.serialize(x) + isoStringToArray.from(bytes) + } + } + + + implicit val isoAmount: Iso[commonMod.Amount, Long] = new Iso[commonMod.Amount, Long] { + override def to(x: commonMod.Amount): Long = x.asInstanceOf[Any] match { + case s: String => BigInt(s).toLong + case _ => java.lang.Long.parseLong(x.asInstanceOf[js.BigInt].toString(10)) + } + override def from(x: Long): commonMod.Amount = x.toString + } + + implicit val isoToken: Iso[tokenMod.TokenAmount[commonMod.Amount], Token] = + new Iso[tokenMod.TokenAmount[commonMod.Amount], Token] { + override def to(x: tokenMod.TokenAmount[commonMod.Amount]): Token = + (Digest32Coll @@@ Colls.fromArray(Base16.decode(x.tokenId).get), isoAmount.to(x.amount)) + + override def from(x: Token): tokenMod.TokenAmount[commonMod.Amount] = + tokenMod.TokenAmount[commonMod.Amount](isoAmount.from(x._2), x._1.toHex) + } + + val isoTokenArray: Iso[js.Array[tokenMod.TokenAmount[commonMod.Amount]], Coll[Token]] = + new Iso[js.Array[tokenMod.TokenAmount[commonMod.Amount]], Coll[Token]] { + override def to(x: js.Array[tokenMod.TokenAmount[commonMod.Amount]]): Coll[Token] = { + sigma.js.Isos.isoArrayToColl(isoToken).to(x) + } + override def from(x: Coll[Token]): js.Array[tokenMod.TokenAmount[commonMod.Amount]] = { + sigma.js.Isos.isoArrayToColl(isoToken).from(x) + } + } + + val isoNonMandatoryRegisters: Iso[registersMod.NonMandatoryRegisters, AdditionalRegisters] = + new Iso[registersMod.NonMandatoryRegisters, AdditionalRegisters] { + override def to(x: registersMod.NonMandatoryRegisters): AdditionalRegisters = { + val regs = Seq( + x.R4 -> R4, + x.R5 -> R5, + x.R6 -> R6, + x.R7 -> R7, + x.R8 -> R8, + x.R9 -> R9 + ).collect { + case (regOpt, id) if regOpt.isDefined => id -> isoHexStringToConstant.to(regOpt.get) + } + Map(regs:_*) + } + override def from(regs: AdditionalRegisters): registersMod.NonMandatoryRegisters = { + def regHexOpt(t: NonMandatoryRegisterId): Option[HexString] = + regs.get(t).map(v => isoHexStringToConstant.from(v.asInstanceOf[Constant[SType]])) + + val resRegs = NonMandatoryRegisters() + regHexOpt(R4).foreach(resRegs.setR4(_)) + regHexOpt(R5).foreach(resRegs.setR5(_)) + regHexOpt(R6).foreach(resRegs.setR6(_)) + regHexOpt(R7).foreach(resRegs.setR7(_)) + regHexOpt(R8).foreach(resRegs.setR8(_)) + regHexOpt(R9).foreach(resRegs.setR9(_)) + resRegs + } + } + + implicit val isoBoxCandidate: Iso[boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters], ErgoBoxCandidate] = new Iso[boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters], ErgoBoxCandidate] { + override def to(x: boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters]): ErgoBoxCandidate = { + val ergoBoxCandidate = new ErgoBoxCandidate( + value = isoAmount.to(x.value), + ergoTree = { + val bytes = Base16.decode(x.ergoTree).get + ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(bytes) + }, + x.creationHeight.toInt, + additionalTokens = isoTokenArray.to(x.assets), + additionalRegisters = isoNonMandatoryRegisters.to(x.additionalRegisters) + ) + ergoBoxCandidate + } + + override def from(x: ErgoBoxCandidate): boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters] = { + val ergoTree = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(x.ergoTree) + val ergoTreeStr = Base16.encode(ergoTree) + val assets = isoTokenArray.from(x.additionalTokens) + boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters]( + ergoTree = ergoTreeStr, + value = isoAmount.from(x.value), + assets = assets, + creationHeight = x.creationHeight, + additionalRegisters = isoNonMandatoryRegisters.from(x.additionalRegisters) + ) + } + } + + +} diff --git a/data/js/src/main/scala/sigma/js/Box.scala b/data/js/src/main/scala/sigma/js/Box.scala new file mode 100644 index 0000000000..70155633d5 --- /dev/null +++ b/data/js/src/main/scala/sigma/js/Box.scala @@ -0,0 +1,61 @@ +package sigma.js + +import org.ergoplatform.ErgoBox +import scorex.util.ModifierId +import scorex.util.encode.Base16 +import sigma.data.Iso +import sigma.data.js.Isos.{isoAmount, isoNonMandatoryRegisters, isoTokenArray} +import sigma.serialization.ErgoTreeSerializer +import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.{Box => FBox} +import sigmastate.fleetSdkCommon.distEsmTypesCommonMod.Amount +import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters +import sigmastate.fleetSdkCommon.{distEsmTypesCommonMod => commonMod} + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel + +/** Equivalent of [[sigma.Box]] available from JS. */ +@JSExportTopLevel("Box") +class Box(val box: FBox[Amount, NonMandatoryRegisters]) extends js.Object + +object Box extends js.Object { + /** Represents a box in Fleet SDK. */ + type FleetBox = FBox[commonMod.Amount, NonMandatoryRegisters] + + /** Converts Fleet box to ErgoBox and back. */ + val isoBox: Iso[FleetBox, ErgoBox] = new Iso[FleetBox, ErgoBox] { + override def to(x: FleetBox): ErgoBox = { + val ergoBox = new ErgoBox( + value = isoAmount.to(x.value), + ergoTree = { + val bytes = Base16.decode(x.ergoTree).get + ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(bytes) + }, + creationHeight = x.creationHeight.toInt, + additionalTokens = isoTokenArray.to(x.assets), + additionalRegisters = isoNonMandatoryRegisters.to(x.additionalRegisters), + transactionId = ModifierId @@ x.transactionId, + index = x.index.toShort + ) + ergoBox + } + + override def from(x: ErgoBox): FleetBox = { + val ergoTree = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(x.ergoTree) + val ergoTreeStr = Base16.encode(ergoTree) + val assets = isoTokenArray.from(x.additionalTokens) + FBox[commonMod.Amount, NonMandatoryRegisters]( + boxId = Base16.encode(x.id), + ergoTree = ergoTreeStr, + value = isoAmount.from(x.value), + assets = assets, + creationHeight = x.creationHeight, + additionalRegisters = isoNonMandatoryRegisters.from(x.additionalRegisters), + transactionId = x.transactionId, + index = x.index + ) + } + } + + +} \ No newline at end of file diff --git a/core/js/src/main/scala/sigma/js/Value.scala b/data/js/src/main/scala/sigma/js/Value.scala similarity index 89% rename from core/js/src/main/scala/sigma/js/Value.scala rename to data/js/src/main/scala/sigma/js/Value.scala index 13b63c2abc..1fedb30250 100644 --- a/core/js/src/main/scala/sigma/js/Value.scala +++ b/data/js/src/main/scala/sigma/js/Value.scala @@ -7,9 +7,10 @@ import sigma.ast.SType import sigma.crypto.Platform import sigma.data._ import sigma.js.Value.toRuntimeData -import sigma.serialization.{CoreDataSerializer, CoreSerializer} +import sigma.serialization.{DataSerializer, SigmaSerializer} import sigma.util.Extensions.BigIntOps import sigma.{Coll, Colls, Evaluation} +import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.{Box => FBox} import java.math.BigInteger import scala.scalajs.js @@ -55,9 +56,9 @@ class Value(val data: Any, val tpe: Type) extends js.Object { def toHex(): String = { val stype = Evaluation.rtypeToSType(tpe.rtype) val value = runtimeData.asInstanceOf[SType#WrappedType] - val w = CoreSerializer.startWriter() + val w = SigmaSerializer.startWriter() w.putType(stype) - CoreDataSerializer.serialize(value, stype, w) + DataSerializer.serialize(value, stype, w) Base16.encode(w.toBytes) } } @@ -80,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) @@ -89,6 +93,9 @@ object Value extends js.Object { case sigma.AvlTreeRType => val t = data.asInstanceOf[AvlTree] AvlTree.isoAvlTree.to(t) + case sigma.BoxRType => + val t = data.asInstanceOf[Box] + CBox(Box.isoBox.to(t.box)) case ct: CollType[a] => val xs = data.asInstanceOf[js.Array[Any]] implicit val cT = ct.tItem.classTag @@ -117,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) @@ -124,6 +134,9 @@ object Value extends js.Object { new SigmaProp(value.asInstanceOf[CSigmaProp].wrappedValue) case sigma.AvlTreeRType => AvlTree.isoAvlTree.from(value.asInstanceOf[CAvlTree]) + case sigma.BoxRType => + val fleetBox = Box.isoBox.from(value.asInstanceOf[CBox].wrappedValue) + new Box(fleetBox) case ct: CollType[a] => val arr = value.asInstanceOf[Coll[a]].toArray js.Array(arr.map(x => fromRuntimeData(x, ct.tItem)):_*) @@ -151,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 => @@ -220,6 +235,13 @@ object Value extends js.Object { new Value(sp, Type.SigmaProp) } + /** Creates a Value of Box type from a [[FBox]] instance. + * @param fleetBox a Fleet box to be wrapped in a [[Value]] + */ + def ofBox(fleetBox: Box.FleetBox): Value = { + new Value(new Box(fleetBox), Type.Box) + } + /** Create Pair value from two values. */ def pairOf(l: Value, r: Value): Value = { val data = js.Array(l.data, r.data) // the l and r data have been validated @@ -251,9 +273,9 @@ object Value extends js.Object { */ def fromHex(hex: String): Value = { val bytes = Base16.decode(hex).fold(t => throw t, identity) - val r = CoreSerializer.startReader(bytes) + val r = SigmaSerializer.startReader(bytes) val stype = r.getType() - val value = CoreDataSerializer.deserialize(stype, r) + val value = DataSerializer.deserialize(stype, r) val rtype = Evaluation.stypeToRType(stype) val jsvalue = Value.fromRuntimeData(value, rtype) new Value(jsvalue, new Type(rtype)) diff --git a/data/jvm/src/main/scala/sigma/Platform.scala b/data/jvm/src/main/scala/sigma/Platform.scala index bd39d75ab9..8fc5019bf9 100644 --- a/data/jvm/src/main/scala/sigma/Platform.scala +++ b/data/jvm/src/main/scala/sigma/Platform.scala @@ -16,29 +16,33 @@ object Platform { private[sigma] def liftToConstant(obj: Any, builder: SigmaBuilder): Nullable[Constant[SType]] = { import builder._ obj match { - case arr: Array[Boolean] => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean)) - case arr: Array[Byte] => Nullable(mkCollectionConstant[SByte.type](arr, SByte)) - case arr: Array[Short] => Nullable(mkCollectionConstant[SShort.type](arr, SShort)) - case arr: Array[Int] => Nullable(mkCollectionConstant[SInt.type](arr, SInt)) - case arr: Array[Long] => Nullable(mkCollectionConstant[SLong.type](arr, SLong)) - case arr: Array[BigInteger] => Nullable(mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt(_)), SBigInt)) - case arr: Array[String] => Nullable(mkCollectionConstant[SString.type](arr, SString)) + case arr: Array[Boolean] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean)) + case arr: Array[Byte] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SByte.type](arr, SByte)) + case arr: Array[Short] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SShort.type](arr, SShort)) + case arr: Array[Int] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SInt.type](arr, SInt)) + case arr: Array[Long] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SLong.type](arr, SLong)) + case arr: Array[BigInteger] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt(_)), SBigInt)) + case arr: Array[String] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SString.type](arr, SString)) case v: Byte => Nullable(mkConstant[SByte.type](v, SByte)) case v: Short => Nullable(mkConstant[SShort.type](v, SShort)) case v: Int => Nullable(mkConstant[SInt.type](v, SInt)) case v: Long => Nullable(mkConstant[SLong.type](v, SLong)) - case v: BigInteger => Nullable(mkConstant[SBigInt.type](SigmaDsl.BigInt(v), SBigInt)) + case v: BigInteger if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SBigInt.type](SigmaDsl.BigInt(v), SBigInt)) case n: sigma.BigInt => Nullable(mkConstant[SBigInt.type](n, SBigInt)) 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)) + case h: Header if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SHeader.type](h, SHeader)) + case h: PreHeader if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SPreHeader.type](h, SPreHeader)) + case n: sigma.UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SUnsignedBigInt.type](n, SUnsignedBigInt)) + + case v: String if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SString.type](v, SString)) // The Box lifting was broken in v4.x. `SigmaDsl.Box(b)` was missing which means the // isCorrectType requirement would fail in ConstantNode constructor. // This method is used as part of consensus in SubstConstants operation, however // ErgoBox cannot be passed as argument as it is never valid value during evaluation. // Thus we can use activation-based versioning and fix this code when v5.0 is activated. - case b: ErgoBox => + case b: ErgoBox if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SBox.type](SigmaDsl.Box(b), SBox)) // fixed in v5.0 // this case is added in v5.0 and it can be useful when the box value comes from a @@ -48,7 +52,7 @@ object Platform { Nullable(mkConstant[SBox.type](b, SBox)) else Nullable.None // return the same result as in v4.x when there was no this case - case avl: AvlTreeData => Nullable(mkConstant[SAvlTree.type](SigmaDsl.avlTree(avl), SAvlTree)) + case avl: AvlTreeData if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SAvlTree.type](SigmaDsl.avlTree(avl), SAvlTree)) case avl: AvlTree => Nullable(mkConstant[SAvlTree.type](avl, SAvlTree)) case sb: SigmaBoolean => Nullable(mkConstant[SSigmaProp.type](SigmaDsl.SigmaProp(sb), SSigmaProp)) case p: SigmaProp => Nullable(mkConstant[SSigmaProp.type](p, SSigmaProp)) diff --git a/data/shared/src/main/scala/org/ergoplatform/ErgoHeader.scala b/data/shared/src/main/scala/org/ergoplatform/ErgoHeader.scala new file mode 100644 index 0000000000..2c6a40d46d --- /dev/null +++ b/data/shared/src/main/scala/org/ergoplatform/ErgoHeader.scala @@ -0,0 +1,182 @@ +package org.ergoplatform + +import scorex.crypto.authds.ADDigest +import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.util.ModifierId +import sigma.Colls +import sigma.crypto.{BigIntegers, CryptoConstants, EcPointType} +import sigma.serialization.{GroupElementSerializer, SigmaByteReader, SigmaByteWriter, SigmaSerializer} + +import scala.runtime.ScalaRunTime +import scala.util.hashing.MurmurHash3 + + + +/** + * Solution for an Autolykos PoW puzzle. + * + * In Autolykos v.1 all the four fields are used, in Autolykos v.2 only pk and n fields are used. + * + * @param pk - miner public key. Should be used to collect block rewards + * @param w - one-time public key. Prevents revealing of miners secret + * @param n - nonce (8 bytes) + * @param d - distance between pseudo-random number, corresponding to nonce `n` and a secret, + * corresponding to `pk`. The lower `d` is, the harder it was to find this solution. + */ +class AutolykosSolution(val pk: EcPointType, + val w: EcPointType, + val n: Array[Byte], + val d: BigInt) { + + val encodedPk: Array[Byte] = GroupElementSerializer.toBytes(pk) + + override def hashCode(): Int = { + var h = pk.hashCode() + h = h * 31 + w.hashCode() + h = h * 31 + MurmurHash3.arrayHash(n) + h = h * 31 + d.hashCode() + h + } + + override def equals(obj: Any): Boolean = { + obj match { + case other: AutolykosSolution => + this.pk == other.pk && + this.n.sameElements(other.n) && + this.w == other.w && + this.d == other.d + + case _ => false + } + } +} + + +object AutolykosSolution { + // "pk", "w" and "d" values for Autolykos v2 solution, where they not passed from outside + val pkForV2: EcPointType = CryptoConstants.dlogGroup.identity + val wForV2: EcPointType = CryptoConstants.dlogGroup.generator + val dForV2: BigInt = 0 + + object sigmaSerializerV1 extends SigmaSerializer[AutolykosSolution, AutolykosSolution] { + override def serialize(s: AutolykosSolution, w: SigmaByteWriter): Unit = { + GroupElementSerializer.serialize(s.pk, w) + GroupElementSerializer.serialize(s.w, w) + require(s.n.length == 8) // non-consensus check on prover side + w.putBytes(s.n) + val dBytes = BigIntegers.asUnsignedByteArray(s.d.bigInteger) + w.putUByte(dBytes.length) + w.putBytes(dBytes) + } + + override def parse(r: SigmaByteReader): AutolykosSolution = { + val pk = GroupElementSerializer.parse(r) + val w = GroupElementSerializer.parse(r) + val nonce = r.getBytes(8) + val dBytesLength = r.getUByte() + val d = BigInt(BigIntegers.fromUnsignedByteArray(r.getBytes(dBytesLength))) + new AutolykosSolution(pk, w, nonce, d) + } + } + + object sigmaSerializerV2 extends SigmaSerializer[AutolykosSolution, AutolykosSolution] { + override def serialize(s: AutolykosSolution, w: SigmaByteWriter): Unit = { + GroupElementSerializer.serialize(s.pk, w) + require(s.n.length == 8) // non-consensus check on prover side + w.putBytes(s.n) + } + + override def parse(r: SigmaByteReader): AutolykosSolution = { + val pk = GroupElementSerializer.parse(r) + val nonce = r.getBytes(8) + new AutolykosSolution(pk, wForV2, nonce, dForV2) + } + } +} + +/** + * Header of a block. It authenticates link to a previous block, other block sections + * (transactions, UTXO set transformation proofs, extension), UTXO set, votes for parameters + * to be changed and proof-of-work related data. + * + * @param version - protocol version + * @param parentId - id of a parent block header + * @param ADProofsRoot - digest of UTXO set transformation proofs + * @param stateRoot - AVL+ tree digest of UTXO set (after the block) + * @param transactionsRoot - Merkle tree digest of transactions in the block (BlockTransactions section) + * @param timestamp - block generation time reported by a miner + * @param nBits - difficulty encoded + * @param height - height of the block (genesis block height == 1) + * @param extensionRoot - Merkle tree digest of the extension section of the block + * @param powSolution - solution for the proof-of-work puzzle + * @param votes - votes for changing system parameters + * @param unparsedBytes - bytes from future versions of the protocol our version can't parse + * @param _bytes - serialized bytes of the header when not `null` + */ +case class ErgoHeader(override val version: ErgoHeader.Version, + override val parentId: ModifierId, + override val ADProofsRoot: Digest32, + override val stateRoot: ADDigest, //33 bytes! extra byte with tree height here! + override val transactionsRoot: Digest32, + override val timestamp: ErgoHeader.Timestamp, + override val nBits: Long, //actually it is unsigned int + override val height: Int, + override val extensionRoot: Digest32, + powSolution: AutolykosSolution, + override val votes: Array[Byte], //3 bytes + override val unparsedBytes: Array[Byte], + _bytes: Array[Byte]) extends + HeaderWithoutPow(version, parentId, ADProofsRoot, stateRoot, transactionsRoot, timestamp, + nBits, height, extensionRoot, votes, unparsedBytes) { + + lazy val bytes = if(_bytes != null) { + _bytes + } else { + ErgoHeader.sigmaSerializer.toBytes(this) + } + + lazy val serializedId: Array[Byte] = Blake2b256.hash(bytes) + + lazy val id = Colls.fromArray(serializedId) + + override def hashCode(): Int = id.hashCode() + + override def equals(other: Any): Boolean = other match { + case h: ErgoHeader => h.id == this.id + case _ => false + } +} + + +object ErgoHeader { + + type Timestamp = Long + + type Version = Byte + + object sigmaSerializer extends SigmaSerializer[ErgoHeader, ErgoHeader] { + override def serialize(hdr: ErgoHeader, w: SigmaByteWriter): Unit = { + HeaderWithoutPowSerializer.serialize(hdr, w) + if (hdr.version == 1) { + AutolykosSolution.sigmaSerializerV1.serialize(hdr.powSolution, w) + } else { + AutolykosSolution.sigmaSerializerV2.serialize(hdr.powSolution, w) + } + } + + override def parse(r: SigmaByteReader): ErgoHeader = { + val start = r.position + val headerWithoutPow = HeaderWithoutPowSerializer.parse(r) + val powSolution = if (headerWithoutPow.version == 1) { + AutolykosSolution.sigmaSerializerV1.parse(r) + } else { + AutolykosSolution.sigmaSerializerV2.parse(r) + } + val end = r.position + val len = end - start + r.position = start + val headerBytes = r.getBytes(len) // also moves position back to end + headerWithoutPow.toHeader(powSolution, headerBytes) + } + } +} \ No newline at end of file diff --git a/data/shared/src/main/scala/org/ergoplatform/HeaderWithoutPow.scala b/data/shared/src/main/scala/org/ergoplatform/HeaderWithoutPow.scala new file mode 100644 index 0000000000..21a10a5036 --- /dev/null +++ b/data/shared/src/main/scala/org/ergoplatform/HeaderWithoutPow.scala @@ -0,0 +1,145 @@ +package org.ergoplatform + +import scorex.crypto.authds.ADDigest +import scorex.crypto.hash.Digest32 +import scorex.util.{ModifierId, bytesToId, idToBytes} +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} +import scorex.util.Extensions._ + +/** + * Header without proof-of-work puzzle solution, see Header class description for details. + */ +class HeaderWithoutPow(val version: Byte, // 1 byte + val parentId: ModifierId, // 32 bytes + val ADProofsRoot: Digest32, // 32 bytes + val stateRoot: ADDigest, //33 bytes! extra byte with tree height here! + val transactionsRoot: Digest32, // 32 bytes + val timestamp: Long, + val nBits: Long, //actually it is unsigned int + val height: Int, + val extensionRoot: Digest32, + val votes: Array[Byte], //3 bytes + val unparsedBytes: Array[Byte]) { + def toHeader(powSolution: AutolykosSolution, bytes: Array[Byte]): ErgoHeader = + ErgoHeader(version, parentId, ADProofsRoot, stateRoot, transactionsRoot, timestamp, + nBits, height, extensionRoot, powSolution, votes, unparsedBytes, bytes) + + override def toString: String = { + s"HeaderWithoutPow($version, $parentId, ${bytesToId(ADProofsRoot)}, ${bytesToId(stateRoot)}, " + + s"${bytesToId(transactionsRoot)}, $timestamp, $nBits, $height, ${bytesToId(extensionRoot)}, ${bytesToId(votes)}, " + + s"${bytesToId(unparsedBytes)} )" + } +} + +object HeaderWithoutPow { + + def apply(version: Byte, parentId: ModifierId, ADProofsRoot: Digest32, stateRoot: ADDigest, + transactionsRoot: Digest32, timestamp: Long, nBits: Long, height: Int, + extensionRoot: Digest32, votes: Array[Byte], unparsedBytes: Array[Byte]): HeaderWithoutPow = { + new HeaderWithoutPow(version, parentId, ADProofsRoot, stateRoot, transactionsRoot, timestamp, + nBits, height, extensionRoot, votes, unparsedBytes) + } + +} + +object HeaderWithoutPowSerializer extends SigmaSerializer[HeaderWithoutPow, HeaderWithoutPow] { + + override def serialize(h: HeaderWithoutPow, w: SigmaByteWriter): Unit = { + w.put(h.version) + w.putBytes(idToBytes(h.parentId)) + w.putBytes(h.ADProofsRoot) + w.putBytes(h.transactionsRoot) + w.putBytes(h.stateRoot) + w.putULong(h.timestamp) + w.putBytes(h.extensionRoot) + DifficultySerializer.serialize(h.nBits, w) + w.putUInt(h.height.toLong) + w.putBytes(h.votes) + + // For block version >= 2, this new byte encodes length of possible new fields. + // Set to 0 for now, so no new fields. + if (h.version > HeaderVersion.InitialVersion) { + w.putUByte(h.unparsedBytes.length) + w.putBytes(h.unparsedBytes) + } + } + + override def parse(r: SigmaByteReader): HeaderWithoutPow = { + val version = r.getByte() + val parentId = bytesToId(r.getBytes(32)) + val ADProofsRoot = Digest32 @@ r.getBytes(32) + val transactionsRoot = Digest32 @@ r.getBytes(32) + val stateRoot = ADDigest @@ r.getBytes(33) + val timestamp = r.getULong() + val extensionHash = Digest32 @@ r.getBytes(32) + val nBits = DifficultySerializer.parse(r) + val height = r.getUInt().toIntExact + val votes = r.getBytes(3) + + // For block version >= 2, a new byte encodes length of possible new fields. + // If this byte > 0, we read new fields but do nothing, as semantics of the fields is not known. + val unparsedBytes = if (version > HeaderVersion.InitialVersion) { + val newFieldsSize = r.getUByte() + if (newFieldsSize > 0 && version > HeaderVersion.Interpreter60Version) { + // new bytes could be added only for block version >= 5 + r.getBytes(newFieldsSize) + } else { + Array.emptyByteArray + } + } else { + Array.emptyByteArray + } + + HeaderWithoutPow(version, parentId, ADProofsRoot, stateRoot, transactionsRoot, timestamp, + nBits, height, extensionHash, votes, unparsedBytes) + } + +} + + +object DifficultySerializer extends SigmaSerializer[Long, Long] { + + /** Parse 4 bytes from the byte array (starting at the offset) as unsigned 32-bit integer in big endian format. */ + def readUint32BE(bytes: Array[Byte]): Long = ((bytes(0) & 0xffL) << 24) | ((bytes(1) & 0xffL) << 16) | ((bytes(2) & 0xffL) << 8) | (bytes(3) & 0xffL) + + def uint32ToByteArrayBE(value: Long): Array[Byte] = { + Array(0xFF & (value >> 24), 0xFF & (value >> 16), 0xFF & (value >> 8), 0xFF & value).map(_.toByte) + } + + override def serialize(obj: Long, w: SigmaByteWriter): Unit = { + w.putBytes(uint32ToByteArrayBE(obj)) + } + + override def parse(r: SigmaByteReader): Long = { + readUint32BE(r.getBytes(4)) + } + +} + +object HeaderVersion { + type Value = Byte + + /** + * Block version during mainnet launch + */ + val InitialVersion: Value = 1.toByte + + /** + * Block version after the Hardening hard-fork + * Autolykos v2 PoW, witnesses in transactions Merkle tree + */ + val HardeningVersion: Value = 2.toByte + + /** + * Block version after the 5.0 soft-fork + * 5.0 interpreter with JITC, monotonic height rule (EIP-39) + */ + val Interpreter50Version: Value = 3.toByte + + /** + * Block version after the 6.0 soft-fork + * 6.0 interpreter (EIP-50) + */ + val Interpreter60Version: Value = 4.toByte + +} diff --git a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala index 48939b1460..c79473afe1 100644 --- a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala +++ b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala @@ -86,6 +86,22 @@ object SigmaDataReflection { ) ) + registerClassEntry(classOf[ByteArrayToLong], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new ByteArrayToLong(args(0).asInstanceOf[Value[SByteArray]]) + } + ) + ) + + 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 +325,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 +354,26 @@ 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]) + }, + mkMethod(clazz, "deserializeTo_eval", Array[Class[_]](classOf[MethodCall], classOf[SigmaDslBuilder], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SGlobalMethods.type].deserializeTo_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[SigmaDslBuilder], + args(2).asInstanceOf[Coll[Byte]])(args(3).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "powHit_eval", Array[Class[_]](classOf[MethodCall], classOf[SigmaDslBuilder], classOf[Int], classOf[Coll[_]], classOf[Coll[_]], classOf[Coll[_]], classOf[Int], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SGlobalMethods.type].powHit_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[SigmaDslBuilder], + args(2).asInstanceOf[Int], + args(3).asInstanceOf[Coll[Byte]], + args(4).asInstanceOf[Coll[Byte]], + args(5).asInstanceOf[Coll[Byte]], + args(6).asInstanceOf[Int] + )(args(7).asInstanceOf[ErgoTreeEvaluator]) } ) ) diff --git a/data/shared/src/main/scala/sigma/ast/CostKind.scala b/data/shared/src/main/scala/sigma/ast/CostKind.scala index 1fda6018ec..6116da2eed 100644 --- a/data/shared/src/main/scala/sigma/ast/CostKind.scala +++ b/data/shared/src/main/scala/sigma/ast/CostKind.scala @@ -1,5 +1,7 @@ package sigma.ast +import sigma.Coll + import scala.runtime.Statics /** Cost descriptor of a single operation, usually associated with @@ -52,5 +54,37 @@ abstract class TypeBasedCost extends CostKind { * See [[EQ]], [[NEQ]]. */ case object DynamicCost extends CostKind +/** + * Cost of converting numeric value to the numeric value of the given type, i.e. Byte -> Int + */ +object NumericCastCostKind extends TypeBasedCost { + override def costFunc(targetTpe: SType): JitCost = targetTpe match { + case SBigInt => JitCost(30) + case _ => JitCost(10) + } +} + +/** + * Cost of Global.powHit method, which is dependent on few parameters, see cost() function description + */ +object PowHitCostKind extends CostKind { + /** + * @param k - k parameter of Autolykos 2 (number of inputs in k-sum problem)" + * @param msg - message to calculate Autolykos hash 2 for + * @param nonce - used to pad the message to get Proof-of-Work hash function output with desirable properties + * @param h - PoW protocol specific padding for table uniqueness (e.g. block height in Ergo) + * @return cost of custom Autolykos2 hash function invocation + */ + def cost(k: Int, msg: Coll[Byte], nonce: Coll[Byte], h: Coll[Byte]): JitCost = { + val chunkSize = CalcBlake2b256.costKind.chunkSize + val perChunkCost = CalcBlake2b256.costKind.perChunkCost + val baseCost = 300 + + // the heaviest part inside is k + 1 Blake2b256 invocations + val c = baseCost + (k + 1) * ((msg.length + nonce.length + h.length) / chunkSize + 1) * perChunkCost.value + JitCost(c) + } +} + diff --git a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala index d6ed6118dc..8d731e1c67 100644 --- a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala +++ b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala @@ -25,13 +25,13 @@ case class UnparsedErgoTree(bytes: mutable.WrappedArray[Byte], error: Validation * ErgoTreeSerializer defines top-level serialization format of the scripts. * The interpretation of the byte array depend on the first `header` byte, which uses VLQ encoding up to 30 bits. * Currently we define meaning for only first byte, which may be extended in future versions. - * 7 6 5 4 3 2 1 0 + * 7 6 5 4 3 2 1 0 * ------------------------- * | | | | | | | | | * ------------------------- * Bit 7 == 1 if the header contains more than 1 byte (default == 0) * Bit 6 - reserved for GZIP compression (should be 0) - * Bit 5 == 1 - reserved for context dependent costing (should be = 0) + * Bit 5 == 1 - reserved (should be = 0) * Bit 4 == 1 if constant segregation is used for this ErgoTree (default = 0) * (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/264) * Bit 3 == 1 if size of the whole tree is serialized after the header byte (default = 0) @@ -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 2d306d8948..35b452fef0 100644 --- a/data/shared/src/main/scala/sigma/ast/SMethod.scala +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -50,17 +50,20 @@ case class MethodIRInfo( /** Represents method descriptor. * - * @param objType type or type constructor descriptor - * @param name method name - * @param stype method signature type, - * where `stype.tDom`` - argument type and - * `stype.tRange` - method result type. - * @param methodId method code, it should be unique among methods of the same objType. - * @param costKind cost descriptor for this method - * @param irInfo meta information connecting SMethod with ErgoTree (see [[MethodIRInfo]]) - * @param docInfo optional human readable method description data - * @param costFunc optional specification of how the cost should be computed for the - * given method call (See ErgoTreeEvaluator.calcCost method). + * @param objType type or type constructor descriptor + * @param name method name + * @param stype method signature type, + * where `stype.tDom`` - argument type and + * `stype.tRange` - method result type. + * @param methodId method code, it should be unique among methods of the same objType. + * @param costKind cost descriptor for this method + * @param explicitTypeArgs list of type parameters which require explicit + * serialization in [[MethodCall]]s (i.e for deserialize[T], getVar[T], getReg[T]) + * @param irInfo meta information connecting SMethod with ErgoTree (see [[MethodIRInfo]]) + * @param docInfo optional human readable method description data + * @param costFunc optional specification of how the cost should be computed for the + * given method call (See ErgoTreeEvaluator.calcCost method). + * @param userDefinedInvoke optional custom method evaluation function */ case class SMethod( objType: MethodsContainer, @@ -68,13 +71,21 @@ case class SMethod( stype: SFunc, methodId: Byte, costKind: CostKind, + explicitTypeArgs: Seq[STypeVar], irInfo: MethodIRInfo, docInfo: Option[OperationInfo], - costFunc: Option[MethodCostFunc]) { + costFunc: Option[MethodCostFunc], + userDefinedInvoke: Option[SMethod.InvokeHandler] +) { /** Operation descriptor of this method. */ lazy val opDesc = MethodDesc(this) + /** 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. * The lazy value is forced only if irInfo.javaMethod == None */ @@ -105,8 +116,13 @@ case class SMethod( /** Invoke this method on the given object with the arguments. * This is used for methods with FixedCost costKind. */ - def invokeFixed(obj: Any, args: Array[Any])(implicit E: ErgoTreeEvaluator): Any = { - javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) + def invokeFixed(obj: Any, args: Array[Any]): Any = { + userDefinedInvoke match { + case Some(h) => + h(this, obj, args) + case None => + javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) + } } // TODO optimize: avoid lookup when this SMethod is created via `specializeFor` @@ -139,13 +155,17 @@ case class SMethod( val methodName = name + "_eval" val m = try { objType.thisRClass.getMethod(methodName, paramTypes:_*) - } - catch { case e: NoSuchMethodException => + } catch { case e: NoSuchMethodException => throw new RuntimeException(s"Cannot find eval method def $methodName(${Seq(paramTypes:_*)})", e) } m } + /** Create a new instance with the given user-defined invoke handler. */ + def withUserDefinedInvoke(handler: SMethod.InvokeHandler): SMethod = { + copy(userDefinedInvoke = Some(handler)) + } + /** Create a new instance with the given stype. */ def withSType(newSType: SFunc): SMethod = copy(stype = newSType) @@ -255,6 +275,12 @@ object SMethod { */ type InvokeDescBuilder = SFunc => Seq[SType] + /** Type of user-defined function which is called to handle method invocation. + * Instances of this type can be attached to [[SMethod]] instances. + * @see SNumericTypeMethods.ToBytesMethod + */ + type InvokeHandler = (SMethod, Any, Array[Any]) => Any + /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. * @param methodName the name of the method to lookup * @param cT the class where to search the methodName @@ -275,6 +301,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) => @@ -284,10 +322,12 @@ object SMethod { /** Convenience factory method. */ def apply(objType: MethodsContainer, name: String, stype: SFunc, methodId: Byte, - costKind: CostKind): SMethod = { + costKind: CostKind, + explicitTypeArgs: Seq[STypeVar] = Nil + ): SMethod = { SMethod( - objType, name, stype, methodId, costKind, - MethodIRInfo(None, None, None), None, None) + objType, name, stype, methodId, costKind, explicitTypeArgs, + MethodIRInfo(None, None, None), None, None, None) } @@ -298,7 +338,7 @@ object SMethod { * @return an instance of [[SMethod]] which may contain generic type variables in the * signature (see SMethod.stype). As a result `specializeFor` is called by * deserializer to obtain monomorphic method descriptor. - * @consensus this is method is used in [[sigmastate.serialization.MethodCallSerializer]] + * @consensus this is method is used in [[sigma.serialization.MethodCallSerializer]] * `parse` method and hence it is part of consensus protocol */ def fromIds(typeId: Byte, methodId: Byte): SMethod = { diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 5cdbdedaa8..c88366d82b 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -5,13 +5,14 @@ import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} import scorex.util.encode.{Base16, Base58, Base64} import sigma.ast.SCollection.{SByteArray, SIntArray} import sigma.ast.SOption.SIntOption -import sigma.ast.SigmaPropConstant import sigma.ast.syntax._ import sigma.data.Nullable import sigma.exceptions.InvalidArguments import sigma.serialization.CoreByteWriter.ArgInfo import sigma.serialization.ValueSerializer +import java.math.BigInteger + object SigmaPredef { type IrBuilderFunc = PartialFunction[(SValue, Seq[SValue]), SValue] @@ -145,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( @@ -179,6 +192,33 @@ object SigmaPredef { Seq(ArgInfo("", ""))) ) + val BigIntFromStringFunc = PredefinedFunc("bigInt", + Lambda(Array("input" -> SString), SBigInt, None), + PredefFuncInfo( + { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => + BigIntConstant(new BigInteger(arg.value)) + }), + OperationInfo(Constant, + """Parsing string literal argument as a 256-bit signed big integer.""".stripMargin, + 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( @@ -390,6 +430,62 @@ 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 DeserializeToFunc = PredefinedFunc("deserializeTo", + 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.deserializeToMethod.withConcreteTypes(Map(tT -> resType)), + args.toIndexedSeq, + Map(tT -> resType) + ) + }), + docInfo = OperationInfo(MethodCall, + """Deserializes provided bytes into a value of given type using the default serialization format. + """.stripMargin, + Seq(ArgInfo("bytes", "bytes to deserialize")) + ) + ) + + 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, @@ -402,6 +498,8 @@ object SigmaPredef { SigmaPropFunc, GetVarFunc, DeserializeFunc, + BigIntFromStringFunc, + UBigIntFromStringFunc, FromBase16Func, FromBase64Func, FromBase58Func, @@ -416,7 +514,11 @@ object SigmaPredef { AvlTreeFunc, SubstConstantsFunc, ExecuteFromVarFunc, - ExecuteFromSelfRegFunc + ExecuteFromSelfRegFunc, + SerializeFunc, + DeserializeToFunc, + GetVarFromInputFunc, + FromBigEndianBytesFunc ).map(f => f.name -> f).toMap def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { @@ -530,7 +632,7 @@ object SigmaPredef { val funcs: Map[String, PredefinedFunc] = globalFuncs ++ infixFuncs ++ unaryFuncs - /** WARNING: This operations are not used in frontend, and should be be used. + /** WARNING: This operations are not used in frontend, and should not be used. * They are used in SpecGen only the source of metadata for the corresponding ErgoTree nodes. */ val specialFuncs: Map[String, PredefinedFunc] = Seq( @@ -591,7 +693,7 @@ object SigmaPredef { ).map(f => f.name -> f).toMap private val funcNameToIrBuilderMap: Map[String, PredefinedFunc] = - funcs.filter { case (n, f) => f.irInfo.irBuilder != undefined } + funcs.filter { case (_, f) => f.irInfo.irBuilder != undefined } def irBuilderForFunc(name: String): Option[IrBuilderFunc] = funcNameToIrBuilderMap.get(name).map(_.irInfo.irBuilder) } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index b637acf792..a69654de7e 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,20 +2,31 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ +import sigma.{UnsignedBigInt, _} +import sigma.{Coll, VersionContext, _} +import sigma.Evaluation.stypeToRType import sigma._ +import sigma.{VersionContext, _} import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} +import sigma.ast.SGlobalMethods.{decodeNBitsMethod, encodeNBitsMethod} 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.data.{CBigInt, DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} +import sigma.pow.Autolykos2PowValidation import sigma.reflection.RClass import sigma.serialization.CoreByteWriter.ArgInfo +import sigma.serialization.{DataSerializer, SigmaByteWriter, SigmaSerializer} +import sigma.util.NBitsUtils import sigma.utils.SparseArrayContainer import scala.annotation.unused -import scala.language.implicitConversions /** Base type for all companions of AST nodes of sigma lang. */ trait SigmaNodeCompanion @@ -53,7 +64,7 @@ sealed trait MethodsContainer { protected def getMethods(): Seq[SMethod] = Nil /** Returns all the methods of this type. */ - lazy val methods: Seq[SMethod] = { + def methods: Seq[SMethod] = { //todo: consider versioned caching val ms = getMethods().toArray assert(ms.map(_.name).distinct.length == ms.length, s"Duplicate method names in $this") ms.groupBy(_.objType).foreach { case (comp, ms) => @@ -61,7 +72,7 @@ sealed trait MethodsContainer { } ms } - private lazy val _methodsMap: Map[Byte, Map[Byte, SMethod]] = methods + private def _methodsMap: Map[Byte, Map[Byte, SMethod]] = methods //todo: consider versioned caching .groupBy(_.objType.typeId) .map { case (typeId, ms) => (typeId -> ms.map(m => m.methodId -> m).toMap) } @@ -91,7 +102,7 @@ sealed trait MethodsContainer { } object MethodsContainer { - private val containers = new SparseArrayContainer[MethodsContainer](Array( + private val methodsV5 = Array( SByteMethods, SShortMethods, SIntMethods, @@ -112,11 +123,29 @@ object MethodsContainer { STupleMethods, SUnitMethods, SAnyMethods - ).map(m => (m.typeId, m))) + ) + + private val methodsV6 = methodsV5 ++ Seq(SUnsignedBigIntMethods) - def contains(typeId: TypeCode): Boolean = containers.contains(typeId) + private val containersV5 = new SparseArrayContainer[MethodsContainer](methodsV5.map(m => (m.typeId, m))) - def apply(typeId: TypeCode): MethodsContainer = containers(typeId) + private val containersV6 = new SparseArrayContainer[MethodsContainer](methodsV6.map(m => (m.typeId, m))) + + 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. * @@ -128,7 +157,11 @@ object MethodsContainer { case tup: STuple => STupleMethods.getTupleMethod(tup, methodName) case _ => - containers.get(tpe.typeCode).flatMap(_.method(methodName)) + if (VersionContext.current.isV6SoftForkActivated) { + containersV6.get(tpe.typeCode).flatMap(_.method(methodName)) + } else { + containersV5.get(tpe.typeCode).flatMap(_.method(methodName)) + } } } @@ -157,14 +190,34 @@ trait MonoTypeMethods extends MethodsContainer { trait SNumericTypeMethods extends MonoTypeMethods { import SNumericTypeMethods.tNum + + private val subst = Map(tNum -> this.ownerType) + + val v5Methods = { + SNumericTypeMethods.v5Methods.map { m => + m.copy(stype = applySubst(m.stype, subst).asFunc) + } + } + + val v6Methods = { + SNumericTypeMethods.v6Methods.map { m => + m.copy( + objType = this, // associate the method with the concrete numeric type + stype = applySubst(m.stype, subst).asFunc + )} + } + protected override def getMethods(): Seq[SMethod] = { - super.getMethods() ++ SNumericTypeMethods.methods.map { - m => m.copy(stype = applySubst(m.stype, Map(tNum -> this.ownerType)).asFunc) + if (VersionContext.current.isV6SoftForkActivated) { + super.getMethods() ++ v6Methods + } else { + super.getMethods() ++ v5Methods } } } object SNumericTypeMethods extends MethodsContainer { + /** Type for which this container defines methods. */ override def ownerType: STypeCompanion = SNumericType @@ -217,6 +270,16 @@ object SNumericTypeMethods extends MethodsContainer { val ToBytesMethod: SMethod = SMethod( this, "toBytes", SFunc(tNum, SByteArray), 6, ToBytes_CostKind) .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.toBigEndianBytes(obj.asInstanceOf[Long]) + case SBigIntMethods => obj.asInstanceOf[BigInt].toBytes + case SUnsignedBigIntMethods => obj.asInstanceOf[UnsignedBigInt].toBytes + } + }) .withInfo(PropertyCall, """ Returns a big-endian representation of this numeric value in a collection of bytes. | For example, the \lst{Int} value \lst{0x12131415} would yield the @@ -230,12 +293,131 @@ object SNumericTypeMethods extends MethodsContainer { val ToBitsMethod: SMethod = SMethod( this, "toBits", SFunc(tNum, SBooleanArray), 7, ToBits_CostKind) .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.toBits(obj.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.toBits(obj.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.toBits(obj.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.toBits(obj.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.toBits(obj.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.toBits(obj.asInstanceOf[UnsignedBigInt]) + } + }) .withInfo(PropertyCall, """ Returns a big-endian representation of this numeric in a collection of Booleans. | Each boolean corresponds to one bit. """.stripMargin) - protected override def getMethods: Seq[SMethod] = Array( + /** Cost of inverting bits of a number. */ + val BitwiseOp_CostKind = FixedCost(JitCost(5)) + + val BitwiseInverseMethod: SMethod = SMethod( + this, "bitwiseInverse", SFunc(tNum, tNum), 8, BitwiseOp_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, _: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseInverse(obj.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.bitwiseInverse(obj.asInstanceOf[UnsignedBigInt]) + } + }) + .withInfo(PropertyCall, desc = "Returns bitwise inverse of this numeric. ") + + val BitwiseOrMethod: SMethod = SMethod( + this, "bitwiseOr", SFunc(Array(tNum, tNum), tNum), 9, BitwiseOp_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.bitwiseOr(obj.asInstanceOf[Byte], other.head.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseOr(obj.asInstanceOf[Short], other.head.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseOr(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseOr(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseOr(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.bitwiseOr(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[UnsignedBigInt]) + } + }) + .withInfo(MethodCall, + """ Returns bitwise or of this numeric and provided one. """.stripMargin, + ArgInfo("that", "A numeric value to calculate or with.")) + + val BitwiseAndMethod: SMethod = SMethod( + this, "bitwiseAnd", SFunc(Array(tNum, tNum), tNum), 10, BitwiseOp_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Byte], other.head.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Short], other.head.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseAnd(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseAnd(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.bitwiseAnd(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[UnsignedBigInt]) + } + }) + .withInfo(MethodCall, + """ Returns bitwise and of this numeric and provided one. """.stripMargin, + ArgInfo("that", "A numeric value to calculate and with.")) + + val BitwiseXorMethod: SMethod = SMethod( + this, "bitwiseXor", SFunc(Array(tNum, tNum), tNum), 11, BitwiseOp_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.bitwiseXor(obj.asInstanceOf[Byte], other.head.asInstanceOf[Byte]) + case SShortMethods => ShortIsExactIntegral.bitwiseXor(obj.asInstanceOf[Short], other.head.asInstanceOf[Short]) + case SIntMethods => IntIsExactIntegral.bitwiseXor(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.bitwiseXor(obj.asInstanceOf[Long], other.head.asInstanceOf[Long]) + case SBigIntMethods => BigIntIsExactIntegral.bitwiseXor(obj.asInstanceOf[BigInt], other.head.asInstanceOf[BigInt]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.bitwiseXor(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[UnsignedBigInt]) + } + }) + .withInfo(MethodCall, + """ Returns bitwise xor of this numeric and provided one. """.stripMargin, + ArgInfo("that", "A numeric value to calculate xor with.")) + + val ShiftLeftMethod: SMethod = SMethod( + this, "shiftLeft", SFunc(Array(tNum, SInt), tNum), 12, BitwiseOp_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.shiftLeft(obj.asInstanceOf[Byte], other.head.asInstanceOf[Int]) + case SShortMethods => ShortIsExactIntegral.shiftLeft(obj.asInstanceOf[Short], other.head.asInstanceOf[Int]) + case SIntMethods => IntIsExactIntegral.shiftLeft(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.shiftLeft(obj.asInstanceOf[Long], other.head.asInstanceOf[Int]) + case SBigIntMethods => BigIntIsExactIntegral.shiftLeft(obj.asInstanceOf[BigInt], other.head.asInstanceOf[Int]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.shiftLeft(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[Int]) + } + }) + .withInfo(MethodCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.stripMargin, + ArgInfo("bits", "Number of bit to shift to the left. Note, that bits value must be non-negative and less than " + + "the size of the number in bits (e.g. 64 for Long, 256 for BigInt)")) + + val ShiftRightMethod: SMethod = SMethod( + this, "shiftRight", SFunc(Array(tNum, SInt), tNum), 13, BitwiseOp_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withUserDefinedInvoke({ (m: SMethod, obj: Any, other: Array[Any]) => + m.objType match { + case SByteMethods => ByteIsExactIntegral.shiftRight(obj.asInstanceOf[Byte], other.head.asInstanceOf[Int]) + case SShortMethods => ShortIsExactIntegral.shiftRight(obj.asInstanceOf[Short], other.head.asInstanceOf[Int]) + case SIntMethods => IntIsExactIntegral.shiftRight(obj.asInstanceOf[Int], other.head.asInstanceOf[Int]) + case SLongMethods => LongIsExactIntegral.shiftRight(obj.asInstanceOf[Long], other.head.asInstanceOf[Int]) + case SBigIntMethods => BigIntIsExactIntegral.shiftRight(obj.asInstanceOf[BigInt], other.head.asInstanceOf[Int]) + case SUnsignedBigIntMethods => UnsignedBigIntIsExactIntegral.shiftRight(obj.asInstanceOf[UnsignedBigInt], other.head.asInstanceOf[Int]) + } + }) + .withInfo(MethodCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.stripMargin, + ArgInfo("bits", "Number of bit to shift to the right. Note, that bits value must be non-negative and less than " + + "the size of the number in bits (e.g. 64 for Long, 256 for BigInt)")) + + lazy val v5Methods = Array( ToByteMethod, // see Downcast ToShortMethod, // see Downcast ToIntMethod, // see Downcast @@ -245,7 +427,21 @@ object SNumericTypeMethods extends MethodsContainer { ToBitsMethod ) + lazy val v6Methods = v5Methods ++ Array( + BitwiseInverseMethod, + BitwiseOrMethod, + BitwiseAndMethod, + BitwiseXorMethod, + ShiftLeftMethod, + ShiftRightMethod + ) + + protected override def getMethods(): Seq[SMethod] = { + throw new Exception("SNumericTypeMethods.getMethods shouldn't ever be called") + } + /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ + // todo: add unsigned big int val castMethods: Array[String] = Array(ToByteMethod, ToShortMethod, ToIntMethod, ToLongMethod, ToBigIntMethod) .map(_.name) @@ -310,28 +506,88 @@ case object SBigIntMethods extends SNumericTypeMethods { /** Type for which this container defines methods. */ override def ownerType: SMonoType = SBigInt - /** The following `modQ` methods are not fully implemented in v4.x and this descriptors. - * This descritors are remain here in the code and are waiting for full implementation - * is upcoming soft-forks at which point the cost parameters should be calculated and - * changed. - */ - val ModQMethod = SMethod(this, "modQ", SFunc(this.ownerType, SBigInt), 1, FixedCost(JitCost(1))) - .withInfo(ModQ, "Returns this \\lst{mod} Q, i.e. remainder of division by Q, where Q is an order of the cryprographic group.") - val PlusModQMethod = SMethod(this, "plusModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 2, FixedCost(JitCost(1))) - .withInfo(ModQArithOp.PlusModQ, "Adds this number with \\lst{other} by module Q.", ArgInfo("other", "Number to add to this.")) - val MinusModQMethod = SMethod(this, "minusModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 3, FixedCost(JitCost(1))) - .withInfo(ModQArithOp.MinusModQ, "Subtracts \\lst{other} number from this by module Q.", ArgInfo("other", "Number to subtract from this.")) - val MultModQMethod = SMethod(this, "multModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 4, FixedCost(JitCost(1))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) + 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() ++ 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 + ) + } - protected override def getMethods() = super.getMethods() ++ Seq( -// ModQMethod, -// PlusModQMethod, -// MinusModQMethod, - // TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - // MultModQMethod, - ) } /** Methods of type `String`. */ @@ -363,6 +619,12 @@ case object SGroupElementMethods extends MonoTypeMethods { "Exponentiate this \\lst{GroupElement} to the given number. Returns this to the power of k", ArgInfo("k", "The power")) + lazy val ExponentiateUnsignedMethod: SMethod = SMethod( + this, "expUnsigned", SFunc(Array(this.ownerType, SUnsignedBigInt), this.ownerType), 6, Exponentiate.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo("Exponentiate this \\lst{GroupElement} to the given number. Returns this to the power of k", + ArgInfo("k", "The power")) + lazy val MultiplyMethod: SMethod = SMethod( this, "multiply", SFunc(Array(this.ownerType, SGroupElement), this.ownerType), 4, MultiplyGroup.costKind) .withIRInfo({ case (builder, obj, _, Seq(arg), _) => @@ -378,16 +640,24 @@ case object SGroupElementMethods extends MonoTypeMethods { .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, "Inverse element of the group.") - protected override def getMethods(): Seq[SMethod] = super.getMethods() ++ Seq( + protected override def getMethods(): Seq[SMethod] = { /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 SMethod(this, "isIdentity", SFunc(this, SBoolean), 1) .withInfo(PropertyCall, "Checks if this value is identity element of the eliptic curve group."), */ - GetEncodedMethod, - ExponentiateMethod, - MultiplyMethod, - NegateMethod - ) + val v5Methods = Seq( + GetEncodedMethod, + ExponentiateMethod, + MultiplyMethod, + NegateMethod) + + super.getMethods() ++ (if (VersionContext.current.isV6SoftForkActivated) { + v5Methods ++ Seq(ExponentiateUnsignedMethod) + } else { + v5Methods + }) + } + } /** Methods of type `SigmaProp` which represent sigma-protocol propositions. */ @@ -710,7 +980,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { | \lst{f} to each element of this collection and concatenating the results. """.stripMargin, ArgInfo("f", "the function to apply to each element.")) - /** We assume all flatMap body patterns have similar executon cost. */ + /** We assume all flatMap body patterns have similar execution cost. */ final val CheckFlatmapBody_Info = OperationCostInfo( PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(20), chunkSize = 1), NamedDesc("CheckFlatmapBody")) @@ -720,7 +990,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { * of flatMap. Other bodies are rejected with throwing exception. */ val flatMap_BodyPatterns = Array[PartialFunction[SValue, Int]]( - { case MethodCall(ValUse(id, tpe), m, args, _) if args.isEmpty => id }, + { case MethodCall(ValUse(id, _), _, args, _) if args.isEmpty => id }, { case ExtractScriptBytes(ValUse(id, _)) => id }, { case ExtractId(ValUse(id, _)) => id }, { case SigmaPropBytes(ValUse(id, _)) => id }, @@ -766,7 +1036,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { var res: Nullable[(Int, SValue)] = Nullable.None E.addFixedCost(MatchSingleArgMethodCall_Info) { res = mc match { - case MethodCall(_, m, Seq(FuncValue(args, body)), _) if args.length == 1 => + case MethodCall(_, _, Seq(FuncValue(args, body)), _) if args.length == 1 => val id = args(0)._1 Nullable((id, body)) case _ => @@ -871,7 +1141,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. @@ -903,8 +1173,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, "") @@ -920,29 +1189,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 { @@ -1044,7 +1417,7 @@ case object SBoxMethods extends MonoTypeMethods { | identifier followed by box index in the transaction outputs. """.stripMargin ) // see ExtractCreationInfo - lazy val getRegMethod = SMethod(this, "getReg", + lazy val getRegMethodV5 = SMethod(this, "getReg", SFunc(Array(SBox, SInt), SOption(tT), Array(paramT)), 7, ExtractRegisterAs.costKind) .withInfo(ExtractRegisterAs, """ Extracts register by id and type. @@ -1053,23 +1426,49 @@ case object SBoxMethods extends MonoTypeMethods { """.stripMargin, ArgInfo("regId", "zero-based identifier of the register.")) + lazy val getRegMethodV6 = SMethod(this, "getReg", + SFunc(Array(SBox, SInt), SOption(tT), Array(paramT)), 7, ExtractRegisterAs.costKind, Seq(tT)) + .withIRInfo(MethodCallIrBuilder, + javaMethodOf[Box, Int, RType[_]]("getReg"), + { mtype => Array(mtype.tRange.asOption[SType].elemType) }) + .withInfo(MethodCall, """ Extracts register by id and type. + | Type param \lst{T} expected type of the register. + | Returns \lst{Some(value)} if the register is defined and has given type and \lst{None} otherwise + """.stripMargin, + ArgInfo("regId", "zero-based identifier of the register.")) + lazy val tokensMethod = SMethod( this, "tokens", SFunc(SBox, ErgoBox.STokensRegType), 8, FixedCost(JitCost(15))) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, "Secondary tokens") - - // should be lazy to solve recursive initialization - protected override def getMethods() = super.getMethods() ++ Array( + lazy val commonBoxMethods = super.getMethods() ++ Array( ValueMethod, // see ExtractAmount PropositionBytesMethod, // see ExtractScriptBytes BytesMethod, // see ExtractBytes BytesWithoutRefMethod, // see ExtractBytesWithNoRef IdMethod, // see ExtractId creationInfoMethod, - getRegMethod, tokensMethod ) ++ registers(8) + + lazy val v5Methods = commonBoxMethods ++ Array( + getRegMethodV5 + ) + + lazy val v6Methods = commonBoxMethods ++ Array( + getRegMethodV6 + ) + + // should be lazy to solve recursive initialization + protected override def getMethods() = { + if (VersionContext.current.isV6SoftForkActivated) { + v6Methods + } else { + v5Methods + } + } + } /** Type descriptor of `AvlTree` type of ErgoTree. */ @@ -1399,16 +1798,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( @@ -1437,11 +1872,28 @@ case object SHeaderMethods extends MonoTypeMethods { lazy val powDistanceMethod = propertyCall("powDistance", SBigInt, 14, FixedCost(JitCost(10))) lazy val votesMethod = propertyCall("votes", SByteArray, 15, FixedCost(JitCost(10))) - protected override def getMethods() = super.getMethods() ++ Seq( + // methods added in 6.0 below + // cost of checkPoW is 700 as about 2*32 hashes required, and 1 hash (id) over short data costs 10 + lazy val checkPowMethod = SMethod( + this, "checkPow", SFunc(Array(SHeader), SBoolean), 16, FixedCost(JitCost(700))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "Validate header's proof-of-work") + + private lazy val v5Methods = super.getMethods() ++ Seq( idMethod, versionMethod, parentIdMethod, ADProofsRootMethod, stateRootMethod, transactionsRootMethod, timestampMethod, nBitsMethod, heightMethod, extensionRootMethod, minerPkMethod, powOnetimePkMethod, - powNonceMethod, powDistanceMethod, votesMethod - ) + powNonceMethod, powDistanceMethod, votesMethod) + + // 6.0 : checkPow method added + private lazy val v6Methods = v5Methods ++ Seq(checkPowMethod) + + protected override def getMethods() = { + if (VersionContext.current.isV6SoftForkActivated) { + v6Methods + } else { + v5Methods + } + } } /** Type descriptor of `PreHeader` type of ErgoTree. */ @@ -1480,7 +1932,7 @@ case object SGlobalMethods extends MonoTypeMethods { lazy val groupGeneratorMethod = SMethod( this, "groupGenerator", SFunc(SGlobal, SGroupElement), 1, GroupGenerator.costKind) - .withIRInfo({ case (builder, obj, method, args, tparamSubst) => GroupGenerator }) + .withIRInfo({ case (_, _, _, _, _) => GroupGenerator }) .withInfo(GroupGenerator, "") lazy val xorMethod = SMethod( @@ -1491,6 +1943,35 @@ case object SGlobalMethods extends MonoTypeMethods { .withInfo(Xor, "Byte-wise XOR of two collections of bytes", ArgInfo("left", "left operand"), ArgInfo("right", "right operand")) + lazy val powHitMethod = SMethod( + this, "powHit", SFunc(Array(SGlobal, SInt, SByteArray, SByteArray, SByteArray, SInt), SBigInt), methodId = 8, + PowHitCostKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + "Calculating Proof-of-Work hit (Autolykos 2 hash value) for custom Autolykos 2 function", + ArgInfo("k", "k parameter of Autolykos 2 (number of inputs in k-sum problem)"), + ArgInfo("msg", "Message to calculate Autolykos hash 2 for"), + ArgInfo("nonce", "Nonce used to pad the message to get Proof-of-Work hash function output with desirable properties"), + ArgInfo("h", "PoW protocol specific padding for table uniqueness (e.g. block height in Ergo)"), + ArgInfo("N", "Size of table filled with pseudo-random data to find k elements in") + ) + + def powHit_eval(mc: MethodCall, G: SigmaDslBuilder, k: Int, msg: Coll[Byte], nonce: Coll[Byte], h: Coll[Byte], N: Int) + (implicit E: ErgoTreeEvaluator): BigInt = { + val cost = PowHitCostKind.cost(k, msg, nonce, h) + E.addCost(FixedCost(cost), powHitMethod.opDesc) + CBigInt(Autolykos2PowValidation.hitForVersion2ForMessageWithChecks(k, msg.toArray, nonce.toArray, h.toArray, N).bigInteger) + } + + private val deserializeCostKind = PerItemCost(baseCost = JitCost(30), perChunkCost = JitCost(20), chunkSize = 32) + + lazy val deserializeToMethod = SMethod( + this, "deserializeTo", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 4, deserializeCostKind, Seq(tT)) + .withIRInfo(MethodCallIrBuilder, + javaMethodOf[SigmaDslBuilder, Coll[Byte], RType[_]]("deserializeTo")) + .withInfo(MethodCall, "Deserialize provided bytes into an object of requested type", + ArgInfo("first", "Bytes to deserialize")) + /** Implements evaluation of Global.xor method call ErgoTree node. * Called via reflection based on naming convention. * @see SMethod.evalMethod, Xor.eval, Xor.xorWithCosting @@ -1500,9 +1981,105 @@ 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.")) + + private lazy val EncodeNBitsCost = FixedCost(JitCost(25)) // cost for nbits encoding + + private lazy val DecodeNBitsCost = FixedCost(JitCost(50)) // cost for nbits decoding + + lazy val encodeNBitsMethod: SMethod = SMethod( + this, "encodeNbits", SFunc(Array(SGlobal, SBigInt), SLong), 6, EncodeNBitsCost) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "Encode big integer number as nbits", ArgInfo("bigInt", "Big integer")) + + lazy val decodeNBitsMethod: SMethod = SMethod( + this, "decodeNbits", SFunc(Array(SGlobal, SLong), SBigInt), 7, DecodeNBitsCost) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "Decode nbits-encoded big integer number", ArgInfo("nbits", "NBits-encoded argument")) + + def deserializeTo_eval(mc: MethodCall, G: SigmaDslBuilder, bytes: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Any = { + val tpe = mc.tpe + val cT = stypeToRType(tpe) + E.addSeqCost(deserializeCostKind, bytes.length, deserializeToMethod.opDesc) { () => + G.deserializeTo(bytes)(cT) + } + } + + 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) + } + + lazy val someMethod = SMethod(this, "some", + SFunc(Array(SGlobal, tT), SOption(tT), Array(paramT)), 9, FixedCost(JitCost(5)), Seq(tT)) + .withIRInfo(MethodCallIrBuilder, + javaMethodOf[SigmaDslBuilder, Any, RType[_]]("some"), + { mtype => Array(mtype.tRange) }) + .withInfo(MethodCall, "Wrap given input into optional value (Option()).", + ArgInfo("value", "Value to wrap into Option.")) + + lazy val noneMethod = SMethod(this, "none", + SFunc(Array(SGlobal), SOption(tT), Array(paramT)), 10, FixedCost(JitCost(5)), Seq(tT)) + .withIRInfo(MethodCallIrBuilder, + javaMethodOf[SigmaDslBuilder, RType[_]]("none"), + { mtype => Array(mtype.tRange) }) + .withInfo(MethodCall, "Returns empty Option[T] of given type T.") + + protected override def getMethods() = super.getMethods() ++ { + if (VersionContext.current.isV6SoftForkActivated) { + Seq( + groupGeneratorMethod, + xorMethod, + serializeMethod, + powHitMethod, + deserializeToMethod, + encodeNBitsMethod, + decodeNBitsMethod, + FromBigEndianBytesMethod, + someMethod, + noneMethod + ) + } else { + Seq( + groupGeneratorMethod, + xorMethod + ) + } + } } diff --git a/data/shared/src/main/scala/sigma/ast/syntax.scala b/data/shared/src/main/scala/sigma/ast/syntax.scala index 5a257481cb..a20c7dd274 100644 --- a/data/shared/src/main/scala/sigma/ast/syntax.scala +++ b/data/shared/src/main/scala/sigma/ast/syntax.scala @@ -40,6 +40,7 @@ object syntax { type LongConstant = Constant[SLong.type] type StringConstant = Constant[SString.type] type BigIntConstant = Constant[SBigInt.type] + type UnsignedBigIntConstant = Constant[SUnsignedBigInt.type] type BoxConstant = Constant[SBox.type] type GroupElementConstant = Constant[SGroupElement.type] type SigmaPropConstant = Constant[SSigmaProp.type] diff --git a/data/shared/src/main/scala/sigma/ast/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 38d4565f30..4195ca1b2d 100644 --- a/data/shared/src/main/scala/sigma/ast/trees.scala +++ b/data/shared/src/main/scala/sigma/ast/trees.scala @@ -15,6 +15,7 @@ import sigma.serialization.CoreByteWriter.ArgInfo import sigma.validation.SigmaValidationSettings import sigma.{Coll, Colls, GroupElement, SigmaProp, VersionContext} import NumericOps.{BigIntIsExactIntegral, BigIntIsExactOrdering} +import sigma.data.UnsignedBigIntNumericOps.{UnsignedBigIntIsExactIntegral, UnsignedBigIntIsExactOrdering} import sigma.eval.ErgoTreeEvaluator.DataEnv import sigma.eval.Extensions.EvalCollOps import sigma.eval.{ErgoTreeEvaluator, SigmaDsl} @@ -295,7 +296,6 @@ object AND extends LogicalTransformerCompanion { def apply(children: Seq[Value[SBoolean.type]]): AND = AND(ConcreteCollection.fromSeq(children)) -// def apply(head: Value[SBoolean.type], tail: Value[SBoolean.type]*): AND = apply(head +: tail) def apply(items: Value[SBoolean.type]*)(implicit o1: Overloaded1): AND = apply(items) } @@ -415,18 +415,6 @@ trait NumericCastCompanion extends ValueCompanion { def costKind: TypeBasedCost = NumericCastCostKind } -/** Cost of: - * 1) converting numeric value to the numeric value of the given type, i.e. Byte -> Int - * NOTE: the cost of BigInt casting is the same in JITC (comparing to AOTC) to simplify - * implementation. - */ -object NumericCastCostKind extends TypeBasedCost { - override def costFunc(targetTpe: SType): JitCost = targetTpe match { - case SBigInt => JitCost(30) - case _ => JitCost(10) - } -} - object Upcast extends NumericCastCompanion { override def opCode: OpCode = OpCodes.UpcastCode override def argInfos: Seq[ArgInfo] = UpcastInfo.argInfos @@ -653,7 +641,7 @@ case class SubstConstants[T <: SType](scriptBytes: Value[SByteArray], positions: val (newBytes, nConstants) = SubstConstants.eval( scriptBytes = scriptBytesV.toArray, positions = positionsV.toArray, - newVals = typedNewVals)(SigmaDsl.validationSettings) + newVals = typedNewVals) res = Colls.fromArray(newBytes) nConstants @@ -684,7 +672,7 @@ object SubstConstants extends ValueCompanion { */ def eval(scriptBytes: Array[Byte], positions: Array[Int], - newVals: Array[Constant[SType]])(implicit vs: SigmaValidationSettings): (Array[Byte], Int) = + newVals: Array[Constant[SType]]): (Array[Byte], Int) = ErgoTreeSerializer.DefaultSerializer.substituteConstants(scriptBytes, positions, newVals) } @@ -876,7 +864,8 @@ object ArithOp { SShort -> new OperationImpl(ShortIsExactIntegral, ShortIsExactOrdering, SShort), SInt -> new OperationImpl(IntIsExactIntegral, IntIsExactOrdering, SInt), SLong -> new OperationImpl(LongIsExactIntegral, LongIsExactOrdering, SLong), - SBigInt -> new OperationImpl(BigIntIsExactIntegral, BigIntIsExactOrdering, SBigInt) + SBigInt -> new OperationImpl(BigIntIsExactIntegral, BigIntIsExactOrdering, SBigInt), + SUnsignedBigInt -> new OperationImpl(UnsignedBigIntIsExactIntegral, UnsignedBigIntIsExactOrdering, SUnsignedBigInt) ).map { case (t, n) => (t.typeCode, n) }) /** Returns operation name for the given opCode. */ diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala index 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 168b2f8266..d849479a17 100644 --- a/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -2,6 +2,7 @@ package sigma.data import sigma._ import sigma.eval.Extensions.IntExt +import sigma.util.Extensions.BigIntOps import scala.math.{Integral, Ordering} @@ -12,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 { @@ -89,6 +95,32 @@ object NumericOps { * NOTE: This method should not be used in v4.x */ override def divisionRemainder(x: BigInt, y: BigInt): BigInt = x.mod(y) + + override def toBigEndianBytes(x: BigInt): Coll[Byte] = Colls.fromArray(x.toBigInteger.toByteArray) + + override def bitwiseInverse(x: BigInt): BigInt = CBigInt(x.toBigInteger.not()) + + override def bitwiseOr(x: BigInt, y: BigInt): BigInt = x.or(y) + + override def bitwiseAnd(x: BigInt, y: BigInt): BigInt = x.and(y) + + override def bitwiseXor(x: BigInt, y: BigInt): BigInt = x.xor(y) + + override def shiftLeft(x: BigInt, bits: Int): BigInt = { + if (bits < 0 || bits >= 256) { + throw new IllegalArgumentException(s"Wrong argument in BigInt.shiftRight: bits < 0 || bits >= 256 ($bits)") + } else { + x.shiftLeft(bits) + } + } + + override def shiftRight(x: BigInt, bits: Int): BigInt = { + if (bits < 0 || bits >= 256) { + throw new IllegalArgumentException(s"Wrong argument in BigInt.shiftRight: bits < 0 || bits >= 256 ($bits)") + } else { + x.shiftRight(bits) + } + } } /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ diff --git a/data/shared/src/main/scala/sigma/data/CHeader.scala b/data/shared/src/main/scala/sigma/data/CHeader.scala new file mode 100644 index 0000000000..3acb295835 --- /dev/null +++ b/data/shared/src/main/scala/sigma/data/CHeader.scala @@ -0,0 +1,146 @@ +package sigma.data + +import org.ergoplatform.{AutolykosSolution, ErgoHeader, HeaderWithoutPow, HeaderWithoutPowSerializer} +import scorex.crypto.authds.ADDigest +import scorex.crypto.hash.Digest32 +import scorex.util.{bytesToId, idToBytes} +import sigma.pow.Autolykos2PowValidation +import sigma.{AvlTree, BigInt, Coll, Colls, GroupElement, Header} + +/** A default implementation of [[Header]] interface. + * + * @see [[Header]] for detailed descriptions + */ +class CHeader(val ergoHeader: ErgoHeader) extends Header with WrapperOf[ErgoHeader] { + + /** Bytes representation of ModifierId of this Header */ + override lazy val id: Coll[Byte] = ergoHeader.id + + /** Block version, to be increased on every soft and hardfork. */ + override def version: Byte = ergoHeader.version + + /** Bytes representation of ModifierId of the parent block */ + override def parentId: Coll[Byte] = Colls.fromArray(idToBytes(ergoHeader.parentId)) + + /** Hash of ADProofs for transactions in a block */ + override def ADProofsRoot: Coll[Byte] = Colls.fromArray(ergoHeader.ADProofsRoot) + + /** AvlTree of a state after block application */ + override def stateRoot: AvlTree = CAvlTree(AvlTreeData.avlTreeFromDigest(Colls.fromArray(ergoHeader.stateRoot))) + + /** Root hash (for a Merkle tree) of transactions in a block. */ + override def transactionsRoot: Coll[Byte] = Colls.fromArray(ergoHeader.transactionsRoot) + + /** Block timestamp (in milliseconds since beginning of Unix Epoch) */ + override def timestamp: Long = ergoHeader.timestamp + + /** Current difficulty in a compressed view. + * NOTE: actually it is unsigned Int */ + override def nBits: Long = ergoHeader.nBits + + /** Block height */ + override def height: Int = ergoHeader.height + + /** Root hash of extension section */ + override def extensionRoot: Coll[Byte] = Colls.fromArray(ergoHeader.extensionRoot) + + /** Miner public key. Should be used to collect block rewards. + * Part of Autolykos solution. */ + override def minerPk: GroupElement = CGroupElement(ergoHeader.powSolution.pk) + + /** One-time public key. Prevents revealing of miners secret. */ + override def powOnetimePk: GroupElement = CGroupElement(ergoHeader.powSolution.w) + + /** nonce */ + override def powNonce: Coll[Byte] = Colls.fromArray(ergoHeader.powSolution.n) + + /** Distance between pseudo-random number, corresponding to nonce `powNonce` and a secret, + * corresponding to `minerPk`. The lower `powDistance` is, the harder it was to find this solution. */ + override def powDistance: BigInt = CBigInt(ergoHeader.powSolution.d.bigInteger) + + /** Miner votes for changing system parameters. */ + override def votes: Coll[Byte] = Colls.fromArray(ergoHeader.votes) + + override def unparsedBytes: Coll[Byte] = Colls.fromArray(ergoHeader.unparsedBytes) + + /** The data value wrapped by this wrapper. */ + override def wrappedValue: ErgoHeader = ergoHeader + + override def serializeWithoutPoW: Coll[Byte] = { + Colls.fromArray(HeaderWithoutPowSerializer.toBytes(ergoHeader)) + } + + override def checkPow: Boolean = { + if (version == 1) { + throw new Exception("Autolykos v1 is not supported") //todo: more specific exception? + } else { + Autolykos2PowValidation.checkPoWForVersion2(this) + } + } + + override def toString: String = + s"""CHeader( + | id: ${id}, + | version: ${version}, + | tx proofs hash: ${ADProofsRoot}, + | state root: ${stateRoot.digest}, + | transactions root: ${transactionsRoot}, + | time: $timestamp, + | nbits: $nBits, + | extension root: $extensionRoot, + | miner pubkey: $minerPk, + | pow one time pubkey(from AL 1): $powOnetimePk, + | pow nonce: $powNonce, + | pow distance (from AL 1): $powDistance, + | votes: $votes, + | unparsed bytes: $unparsedBytes + |)""".stripMargin + + override def hashCode(): Int = id.hashCode() + + override def equals(other: Any): Boolean = other match { + case ch: CHeader => ch.id == this.id + case _ => false + } + + def copy(): CHeader = new CHeader(ergoHeader.copy()) // used in tests only +} + +object CHeader { + + def apply( version: Byte, + parentId: Coll[Byte], + ADProofsRoot: Coll[Byte], + stateRootDigest: Coll[Byte], + transactionsRoot: Coll[Byte], + timestamp: Long, + nBits: Long, + height: Int, + extensionRoot: Coll[Byte], + minerPk: GroupElement, + powOnetimePk: GroupElement, + powNonce: Coll[Byte], + powDistance: BigInt, + votes: Coll[Byte], + unparsedBytes: Coll[Byte]): CHeader = { + + val solution = new AutolykosSolution( + minerPk.asInstanceOf[CGroupElement].wrappedValue, + powOnetimePk.asInstanceOf[CGroupElement].wrappedValue, + powNonce.toArray, + powDistance.asInstanceOf[CBigInt].wrappedValue) + + val h = ErgoHeader(version, bytesToId(parentId.toArray), Digest32 @@ ADProofsRoot.toArray, + ADDigest @@ stateRootDigest.toArray, Digest32 @@ transactionsRoot.toArray, timestamp, nBits, height, + Digest32 @@ extensionRoot.toArray, solution, votes.toArray, unparsedBytes.toArray, null) + + new CHeader(h) + } + + /** Size of of Header.votes array. */ + val VotesSize: Int = SigmaConstants.VotesArraySize.value + + /** Size of nonce array from Autolykos POW solution in Header.powNonce array. */ + val NonceSize: Int = SigmaConstants.AutolykosPowSolutionNonceArraySize.value + +} diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 3938feacd3..6e599a3605 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -1,31 +1,43 @@ package sigma.data import debox.cfor -import org.ergoplatform.ErgoBox +import org.ergoplatform.{ErgoBox, ErgoHeader} import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} +import scorex.util.serialization.VLQByteBufferReader +import scorex.utils.{Ints, Longs} +import sigma.ast.{AtLeast, SBigInt, SType, SUnsignedBigInt, SubstConstants} import scorex.utils.Longs -import sigma.ast.{AtLeast, SubstConstants} +import sigma.Evaluation.rtypeToSType +import sigma.ast.{AtLeast, SType, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} +import sigma.crypto.{BigIntegers, CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps -import sigma.serialization.{GroupElementSerializer, SigmaSerializer} +import sigma.serialization.{ConstantStore, DataSerializer, GroupElementSerializer, SigmaByteReader, SigmaSerializer} +import sigma.serialization.{DataSerializer, GroupElementSerializer, SigmaSerializer} +import sigma.serialization.SerializerException +import sigma.pow.Autolykos2PowValidation import sigma.util.Extensions.BigIntegerOps +import sigma.util.NBitsUtils 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 +import java.nio.ByteBuffer /** A default implementation of [[SigmaDslBuilder]] interface. * * @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 +161,7 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => } override def byteArrayToBigInt(bytes: Coll[Byte]): BigInt = { - val bi = new BigInteger(bytes.toArray).to256BitValueExact + val bi = new BigInteger(bytes.toArray).toSignedBigIntValueExact this.BigInt(bi) } @@ -175,6 +187,14 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => override def groupGenerator: GroupElement = _generatorElement + override def encodeNbits(bi: BigInt): Long = { + NBitsUtils.encodeCompactBits(bi.asInstanceOf[CBigInt].wrappedValue) + } + + override def decodeNbits(l: Long): BigInt = { + CBigInt(NBitsUtils.decodeCompactBits(l).bigInteger) + } + /** * @return the identity of the Dlog group used in ErgoTree */ @@ -191,7 +211,7 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => case e: Throwable => throw new RuntimeException(s"Cannot evaluate substConstants($scriptBytes, $positions, $newValues)", e) } - val (res, _) = SubstConstants.eval(scriptBytes.toArray, positions.toArray, constants)(validationSettings) + val (res, _) = SubstConstants.eval(scriptBytes.toArray, positions.toArray, constants) Colls.fromArray(res) } @@ -200,6 +220,73 @@ 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) + } + + override def powHit(k: Int, msg: Coll[Byte], nonce: Coll[Byte], h: Coll[Byte], N: Int): BigInt = { + val bi = Autolykos2PowValidation.hitForVersion2ForMessageWithChecks(k, msg.toArray, nonce.toArray, h.toArray, N) + this.BigInt(bi.bigInteger) + } + + + def deserializeTo[T](bytes: Coll[Byte])(implicit cT: RType[T]): T = { + val tpe = rtypeToSType(cT) + val reader = new SigmaByteReader(new VLQByteBufferReader(ByteBuffer.wrap(bytes.toArray)), new ConstantStore(), false) + val res = DataSerializer.deserialize(tpe, reader) + res.asInstanceOf[T] + } + + override def some[T](value: T)(implicit cT: RType[T]): Option[T] = { + Some(value) + } + + override def none[T]()(implicit cT: RType[T]): Option[T] = { + None + } } /** 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 34e2f47f63..8adebc36d6 100644 --- a/data/shared/src/main/scala/sigma/data/ExactIntegral.scala +++ b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala @@ -1,8 +1,9 @@ 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. @@ -37,6 +38,25 @@ object ExactIntegral { override def plus(x: Byte, y: Byte): Byte = x.addExact(y) override def minus(x: Byte, y: Byte): Byte = x.subtractExact(y) override def times(x: Byte, y: Byte): Byte = x.multiplyExact(y) + override def toBigEndianBytes(x: Byte): Coll[Byte] = Colls.fromItems(x) + override def bitwiseInverse(x: Byte): Byte = (~x).toByte + override def bitwiseOr(x: Byte, y: Byte): Byte = (x | y).toByte + override def bitwiseAnd(x: Byte, y: Byte): Byte = (x & y).toByte + override def bitwiseXor(x: Byte, y: Byte): Byte = (x ^ y).toByte + override def shiftLeft(x: Byte, bits: Int): Byte = { + if (bits < 0 || bits >= 8) { + throw new IllegalArgumentException(s"Wrong argument in Byte.shiftRight: bits < 0 || bits >= 8 ($bits)") + } else { + (x << bits).toByte + } + } + override def shiftRight(x: Byte, bits: Int): Byte = { + if (bits < 0 || bits >= 8) { + throw new IllegalArgumentException(s"Wrong argument in Byte.shiftRight: bits < 0 || bits >= 8 ($bits)") + } else { + (x >> bits).toByte + } + } } implicit object ShortIsExactIntegral extends ExactIntegral[Short] { @@ -44,6 +64,25 @@ object ExactIntegral { override def plus(x: Short, y: Short): Short = x.addExact(y) override def minus(x: Short, y: Short): Short = x.subtractExact(y) override def times(x: Short, y: Short): Short = x.multiplyExact(y) + override def toBigEndianBytes(x: Short): Coll[Byte] = Colls.fromItems((x >> 8).toByte, x.toByte) + override def bitwiseInverse(x: Short): Short = (~x).toShort + override def bitwiseOr(x: Short, y: Short): Short = (x | y).toShort + override def bitwiseAnd(x: Short, y: Short): Short = (x & y).toShort + override def bitwiseXor(x: Short, y: Short): Short = (x ^ y).toShort + override def shiftLeft(x: Short, bits: Int): Short = { + if (bits < 0 || bits >= 16) { + throw new IllegalArgumentException(s"Wrong argument in Short.shiftRight: bits < 0 || bits >= 16 ($bits)") + } else { + (x << bits).toShort + } + } + override def shiftRight(x: Short, bits: Int): Short = { + if (bits < 0 || bits >= 16){ + throw new IllegalArgumentException(s"Wrong argument in Short.shiftRight: bits < 0 || bits >= 16 ($bits)") + } else { + (x >> bits).toShort + } + } } implicit object IntIsExactIntegral extends ExactIntegral[Int] { @@ -51,6 +90,28 @@ object ExactIntegral { override def plus(x: Int, y: Int): Int = java7.compat.Math.addExact(x, y) override def minus(x: Int, y: Int): Int = java7.compat.Math.subtractExact(x, y) override def times(x: Int, y: Int): Int = java7.compat.Math.multiplyExact(x, y) + override def toBigEndianBytes(x: Int): Coll[Byte] = + Colls.fromItems((x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) + override def bitwiseInverse(x: Int): Int = ~x + override def bitwiseOr(x: Int, y: Int): Int = x | y + override def bitwiseAnd(x: Int, y: Int): Int = x & y + override def bitwiseXor(x: Int, y: Int): Int = x ^ y + + override def shiftLeft(x: Int, bits: Int): Int = { + if (bits < 0 || bits >= 32) { + throw new IllegalArgumentException(s"Wrong argument in Byte.shiftRight: bits < 0 || bits >= 32 ($bits)") + } else { + x << bits + } + } + + override def shiftRight(x: Int, bits: Int): Int = { + if (bits < 0 || bits >= 32) { + throw new IllegalArgumentException(s"Wrong argument in Int.shiftRight: bits < 0 || bits >= 32 ($bits)") + } else { + x >> bits + } + } } implicit object LongIsExactIntegral extends ExactIntegral[Long] { @@ -58,5 +119,27 @@ object ExactIntegral { override def plus(x: Long, y: Long): Long = java7.compat.Math.addExact(x, y) override def minus(x: Long, y: Long): Long = java7.compat.Math.subtractExact(x, y) override def times(x: Long, y: Long): Long = java7.compat.Math.multiplyExact(x, y) + override def toBigEndianBytes(x: Long): Coll[Byte] = + Colls.fromItems((x >> 56).toByte, (x >> 48).toByte, (x >> 40).toByte, (x >> 32).toByte, (x >> 24).toByte, (x >> 16).toByte, (x >> 8).toByte, x.toByte) + override def bitwiseInverse(x: Long): Long = ~x + override def bitwiseOr(x: Long, y: Long): Long = x | y + override def bitwiseAnd(x: Long, y: Long): Long = x & y + override def bitwiseXor(x: Long, y: Long): Long = x ^ y + + override def shiftLeft(x: Long, bits: Int): Long = { + if (bits < 0 || bits >= 64) { + throw new IllegalArgumentException(s"Wrong argument in Long.shiftRight: bits < 0 || bits >= 64 ($bits)") + } else { + x << bits + } + } + + override def shiftRight(x: Long, bits: Int): Long = { + if (bits < 0 || bits >= 64) { + throw new IllegalArgumentException(s"Wrong argument in Long.shiftRight: bits < 0 || bits >= 64 ($bits)") + } else { + x >> bits + } + } } } diff --git a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala index 2e9b799a61..244cc6b7b7 100644 --- a/data/shared/src/main/scala/sigma/data/ExactNumeric.scala +++ b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala @@ -1,5 +1,7 @@ package sigma.data +import debox.cfor +import sigma.{Coll, Colls} import sigma.data.ExactIntegral._ /** Numeric operations with overflow checks. @@ -30,6 +32,63 @@ trait ExactNumeric[T] { def toInt(x: T): Int = n.toInt(x) def toLong(x: T): Long = n.toLong(x) + /** Returns a big-endian representation of this value in a collection of bytes. + * For example, the `Int` value `0x12131415` would yield the + * collection of bytes [0x12, 0x13, 0x14, 0x15] + */ + def toBigEndianBytes(x: T): Coll[Byte] + + /** + * Returns a big-endian binary representation of this value as boolean array. + */ + def toBits(x: T): Coll[Boolean] = { + + def isBitSet(byte: Byte)(bit: Int): Boolean = ((byte >> bit) & 1) == 1 + + val bytes = toBigEndianBytes(x) + val l = bytes.length + val res = new Array[Boolean](l * 8) + cfor(0)(_ < l, _ + 1) { i => + val b = bytes(i) + cfor(0)(_ < 8, _ + 1) { bitIdx => + res(i * 8 + (7 - bitIdx)) = isBitSet(b)(bitIdx) + } + } + Colls.fromArray(res) + } + + /** + * @return a numeric value which is inverse of `x` (every bit, including sign, is flipped) + */ + def bitwiseInverse(x: T): T + + /** + * @return a numeric value which is `this | that` + */ + def bitwiseOr(x: T, y: T): T + + /** + * @return a numeric value which is `this && that` + */ + def bitwiseAnd(x: T, y: T): T + + /** + * @return a numeric value which is `this xor that` + */ + def bitwiseXor(x: T, y: T): T + + /** + * @return a value which is (this << n). The shift distance, n, may be negative, + * in which case this method performs a right shift. (Computes floor(this * 2n).) + */ + def shiftLeft(x: T, bits: Int): T + + /** + * @return a value which is (this >> n). Sign extension is performed. The shift distance, n, + * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).) + */ + def shiftRight(x: T, bits: Int): T + /** A value of type T which corresponds to integer 0. */ lazy val zero: T = fromInt(0) diff --git a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala new file mode 100644 index 0000000000..e628703deb --- /dev/null +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -0,0 +1,156 @@ +package sigma.data + +import debox.cfor +import scorex.util.encode.Base16 +import sigma._ +import sigma.crypto.BigIntegers +import sigma.data.UnsignedBigIntOrderingOps.UnsignedBigIntOrdering +import sigma.eval.Extensions.IntExt + +import scala.math.{Integral, Ordering} + +object UnsignedBigIntOrderingOps { + def apply[T](implicit ord: Ordering[T]) = ord + + trait UnsignedBigIntOrdering extends Ordering[UnsignedBigInt] { + def compare(x: UnsignedBigInt, y: UnsignedBigInt) = x.compareTo(y) + } + implicit object UnsignedBigIntOrdering extends UnsignedBigIntOrdering +} + +object UnsignedBigIntNumericOps { + + /** Base implementation of Integral methods for UnsignedBigInt. */ + trait UnsignedBigIntIsIntegral extends Integral[UnsignedBigInt] { + /** This method should not be used in v4.x */ + def quot(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.divide(y) + + /** This method is used in ErgoTreeEvaluator based interpreter, to implement + * '%' operation of ErgoTree (i.e. `%: (T, T) => T` operation) for all + * numeric types T including BigInt. + * + * In the v4.x interpreter, however, the `%` operation is implemented using + * [[CBigInt]].mod method , which delegates to [[java.math.BigInteger]].mod method. + * + * Even though this method is called `rem`, the semantics of ErgoTree + * language requires it to correspond to [[java.math.BigInteger]].mod + * method. + * + * For this reason we define implementation of this `rem` method using + * [[BigInt]].mod. + * + * NOTE: This method should not be used in v4.x + */ + def rem(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.mod(y) + + def plus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.add(y) + def minus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.subtract(y) + def times(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.multiply(y) + def negate(x: UnsignedBigInt): UnsignedBigInt = ??? + def fromInt(x: Int): UnsignedBigInt = x.toUnsignedBigInt + def toInt(x: UnsignedBigInt): Int = x.toInt + def toLong(x: UnsignedBigInt): Long = x.toLong + def toFloat(x: UnsignedBigInt): Float = x.toFloat + def toDouble(x: UnsignedBigInt): Double = x.toDouble + } + + /** + * The instance of Integral for UnsignedBigInt. + * Done similarly to BigIntIsIntegral. + */ + object UnsignedBigIntIsIntegral extends UnsignedBigIntIsIntegral with UnsignedBigIntOrdering { + def parseString(str: String): Option[UnsignedBigInt] = ??? + } + + /** The instance of [[ExactIntegral]] typeclass for [[BigInt]]. */ + implicit object UnsignedBigIntIsExactIntegral extends ExactIntegral[UnsignedBigInt] { + val n = UnsignedBigIntIsIntegral + override def plus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = n.plus(x, y) + override def minus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = n.minus(x, y) + override def times(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = n.times(x, y) + + override def quot(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.divide(y) + + /** This method is used in ErgoTreeEvaluator based interpreter, to implement + * '%' operation of ErgoTree (i.e. `%: (T, T) => T` operation) for all + * numeric types T including BigInt. + * + * In the v4.x interpreter, however, the `%` operation is implemented using + * [[CBigInt]].mod method, which delegates to [[java.math.BigInteger]].mod method. + * + * Even though this method is called `divisionRemainder`, the semantics of ErgoTree + * language requires it to correspond to [[java.math.BigInteger]].mod method. + * + * For this reason we define implementation of this method using [[BigInt]].mod. + * + * NOTE: This method should not be used in v4.x + */ + override def divisionRemainder(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.mod(y) + + /** Returns a big-endian representation of this value in a collection of bytes. + * For example, the `Int` value `0x12131415` would yield the + * collection of bytes [0x12, 0x13, 0x14, 0x15] + */ + override def toBigEndianBytes(x: UnsignedBigInt): Coll[Byte] = x.toBytes + + /** + * @return a numeric value which is inverse of `x` (every bit is flipped) + */ + override def bitwiseInverse(x: UnsignedBigInt): UnsignedBigInt = x.bitwiseInverse() + + /** + * @return a numeric value which is `this | that` + */ + override def bitwiseOr(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = { + val vx = x.asInstanceOf[CUnsignedBigInt].wrappedValue + val vy = y.asInstanceOf[CUnsignedBigInt].wrappedValue + CUnsignedBigInt(vx.or(vy)) + } + + /** + * @return a numeric value which is `this && that` + */ + override def bitwiseAnd(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = { + val vx = x.asInstanceOf[CUnsignedBigInt].wrappedValue + val vy = y.asInstanceOf[CUnsignedBigInt].wrappedValue + CUnsignedBigInt(vx.and(vy)) + } + + /** + * @return a numeric value which is `this xor that` + */ + override def bitwiseXor(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = { + val vx = x.asInstanceOf[CUnsignedBigInt].wrappedValue + val vy = y.asInstanceOf[CUnsignedBigInt].wrappedValue + CUnsignedBigInt(vx.xor(vy)) + } + + /** + * @return a value which is (this << n). The shift distance, n, may be negative, + * in which case this method performs a right shift. (Computes floor(this * 2n).) + */ + override def shiftLeft(x: UnsignedBigInt, bits: Int): UnsignedBigInt = { + if (bits < 0 || bits >= 256) { + throw new IllegalArgumentException(s"Wrong argument in UnsignedBigInt.shiftLeft: bits < 0 || bits >= 256 ($bits)") + } else { + x.shiftLeft(bits) + } + } + + /** + * @return a value which is (this >> n). Sign extension is performed. The shift distance, n, + * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).) + */ + override def shiftRight(x: UnsignedBigInt, bits: Int): UnsignedBigInt = { + if (bits < 0 || bits >= 256) { + throw new IllegalArgumentException(s"Wrong argument in UnsignedBigInt.shiftLeft: bits < 0 || bits >= 256 ($bits)") + } else { + x.shiftRight(bits) + } + } + } + + /** The instance of [[scalan.ExactOrdering]] typeclass for [[BigInt]]. */ + implicit object UnsignedBigIntIsExactOrdering extends ExactOrderingImpl[UnsignedBigInt](UnsignedBigIntIsIntegral) +} + diff --git a/data/shared/src/main/scala/sigma/eval/ErgoTreeEvaluator.scala b/data/shared/src/main/scala/sigma/eval/ErgoTreeEvaluator.scala index 52f839354c..b986528979 100644 --- a/data/shared/src/main/scala/sigma/eval/ErgoTreeEvaluator.scala +++ b/data/shared/src/main/scala/sigma/eval/ErgoTreeEvaluator.scala @@ -1,6 +1,6 @@ package sigma.eval -import sigma.{AvlTree, Coll, Context} +import sigma.{AvlTree, Coll, Context, Header} import sigma.ast.{Constant, FixedCost, MethodCall, OperationCostInfo, OperationDesc, PerItemCost, SType, TypeBasedCost} import sigma.data.KeyValueColl @@ -59,9 +59,9 @@ abstract class ErgoTreeEvaluator { * @param opDesc the operation descriptor to associate the cost with (when costTracingEnabled) * @param block operation executed under the given cost */ - def addFixedCost(costKind: FixedCost, opDesc: OperationDesc)(block: => Unit): Unit + def addFixedCost[R](costKind: FixedCost, opDesc: OperationDesc)(block: => R): R - def addFixedCost(costInfo: OperationCostInfo[FixedCost])(block: => Unit): Unit + def addFixedCost[R](costInfo: OperationCostInfo[FixedCost])(block: => R): R /** Adds the given cost to the `coster`. If tracing is enabled, creates a new cost item * with the given operation. 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/pow/Autolykos2PowValidation.scala b/data/shared/src/main/scala/sigma/pow/Autolykos2PowValidation.scala new file mode 100644 index 0000000000..f51a625825 --- /dev/null +++ b/data/shared/src/main/scala/sigma/pow/Autolykos2PowValidation.scala @@ -0,0 +1,182 @@ +package sigma.pow + + +import scorex.crypto.hash.Blake2b256 +import scorex.utils.{Bytes, Ints, Longs} +import sigma.Header +import sigma.crypto.{BcDlogGroup, BigIntegers, CryptoConstants} +import sigma.util.NBitsUtils + +/** + * Functions used to validate Autolykos2 Proof-of-Work. + */ +object Autolykos2PowValidation { + + type Height = Int + + /** + * k value for k-sum problem Autolykos is based on (find k numbers in table on N size) + */ + private val k = 32 + + /** + * Initial size of N value for k-sum problem Autolykos is based on (find k numbers in table on N size). + * It grows from it since predefined block height in Autolykos 2. + */ + private val NStart = 26 + + /** + * Group order, used in Autolykos V.1 for non-outsourceability, + * and also to obtain target in both Autolykos v1 and v2 + */ + private val q: BigInt = CryptoConstants.dlogGroup.order + + /** + * Number of elements in a table to find k-sum problem solution on top of + */ + val NBase: Int = Math.pow(2, NStart.toDouble).toInt + + /** + * Initial height since which table (`N` value) starting to increase by 5% per `IncreasePeriodForN` blocks + */ + val IncreaseStart: Height = 600 * 1024 + + /** + * Table size (`N`) increased every 50 * 1024 blocks + */ + val IncreasePeriodForN: Height = 50 * 1024 + + /** + * On this height, the table (`N` value) will stop to grow. + * Max N on and after this height would be 2,143,944,600 which is still less than 2^^31. + */ + val NIncreasementHeightMax: Height = 4198400 + + /** + * Blake2b256 hash function invocation + * @param in - input bit-string + * @return - 256 bits (32 bytes) array + */ + def hash(in: Array[Byte]): Array[Byte] = Blake2b256.hash(in) + + /** + * Convert byte array to unsigned integer + * @param in - byte array + * @return - unsigned integer + */ + def toBigInt(in: Array[Byte]): BigInt = BigInt(BigIntegers.fromUnsignedByteArray(in)) + + /** + * Constant data to be added to hash function to increase its calculation time + */ + val M: Array[Byte] = (0 until 1024).toArray.flatMap(i => Longs.toByteArray(i.toLong)) + + /** + * Calculates table size (N value) for a given height (moment of time) + * + * @see papers/yellow/pow/ErgoPow.tex for full description and test vectors + * @param headerHeight - height of a header to mine + * @return - N value + */ + def calcN(headerHeight: Height): Int = { + val height = Math.min(NIncreasementHeightMax, headerHeight) + if (height < IncreaseStart) { + NBase + } else { + val itersNumber = (height - IncreaseStart) / IncreasePeriodForN + 1 + (1 to itersNumber).foldLeft(NBase) { case (step, _) => + step / 100 * 105 + } + } + } + + def calcN(header: Header): Int = calcN(header.height) + + /** + * Hash function that takes `m` and `nonceBytes` and returns a list of size `k` with numbers in + * [0,`N`) + */ + private def genIndexes(k: Int, seed: Array[Byte], N: Int): Seq[Int] = { + val hash = Blake2b256(seed) + val extendedHash = Bytes.concat(hash, hash.take(3)) + (0 until k).map { i => + BigInt(1, extendedHash.slice(i, i + 4)).mod(N).toInt + } + }.ensuring(_.length == k) + + /** + * Generate element of Autolykos equation. + */ + private def genElementV2(indexBytes: Array[Byte], heightBytes: => Array[Byte]): BigInt = { + // Autolykos v. 2: H(j|h|M) (line 5 from the Algo 2 of the spec) + toBigInt(hash(Bytes.concat(indexBytes, heightBytes, M)).drop(1)) + } + + def hitForVersion2ForMessageWithChecks(k: Int, msg: Array[Byte], nonce: Array[Byte], h: Array[Byte], N: Int): BigInt = { + require(k >= 2) // at least 2 elements needed for sum + require(k <= 32) // genIndexes function of Autolykos2 not supporting k > 32 + require(N >= 16) // min table size + hitForVersion2ForMessage(k, msg, nonce, h, N) + } + + private def hitForVersion2ForMessage(k: Int, msg: Array[Byte], nonce: Array[Byte], h: Array[Byte], N: Int): BigInt = { + val prei8 = BigIntegers.fromUnsignedByteArray(hash(Bytes.concat(msg, nonce)).takeRight(8)) + val i = BigIntegers.asUnsignedByteArray(4, prei8.mod(BigInt(N).underlying())) + val f = Blake2b256(Bytes.concat(i, h, M)).drop(1) // .drop(1) is the same as takeRight(31) + val seed = Bytes.concat(f, msg, nonce) // Autolykos v1, Alg. 2, line4: + + val indexes = genIndexes(k, seed, N) + //pk and w not used in v2 + val elems = indexes.map(idx => genElementV2(Ints.toByteArray(idx), h)) + val f2 = elems.sum + + // sum as byte array is always about 32 bytes + val array: Array[Byte] = BigIntegers.asUnsignedByteArray(32, f2.underlying()) + val ha = hash(array) + toBigInt(ha) + } + + /** + * Header digest ("message" for default GPU miners) a miner is working on + */ + def msgByHeader(h: Header): Array[Byte] = Blake2b256(h.serializeWithoutPoW.toArray) + + /** + * Get hit for Autolykos v2 header (to test it then against PoW target) + * + * @param header - header to check PoW for + * @return PoW hit + */ + def hitForVersion2(header: Header): BigInt = { + + val msg = msgByHeader(header) + val nonce = header.powNonce + + val h = Ints.toByteArray(header.height) // used in AL v.2 only + + val N = calcN(header) + + hitForVersion2ForMessage(k, msg, nonce.toArray, h, N) + } + + /** + * Get target `b` from encoded difficulty `nBits` + */ + def getB(nBits: Long): BigInt = { + q / NBitsUtils.decodeCompactBits(nBits) + } + + /** + * Check PoW for Autolykos v2 header + * + * @param header - header to check PoW for + * @return whether PoW is valid or not + */ + def checkPoWForVersion2(header: Header): Boolean = { + val b = getB(header.nBits) + // for version 2, we're calculating hit and compare it with target + val hit = hitForVersion2(header) + hit < b + } + +} diff --git a/data/shared/src/main/scala/sigma/serialization/DataSerializer.scala b/data/shared/src/main/scala/sigma/serialization/DataSerializer.scala index 5f554e96a1..92a54f9aa4 100644 --- a/data/shared/src/main/scala/sigma/serialization/DataSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/DataSerializer.scala @@ -1,8 +1,9 @@ package sigma.serialization -import org.ergoplatform.ErgoBox +import org.ergoplatform.{ErgoBox, ErgoHeader} +import sigma.VersionContext import sigma.ast._ -import sigma.data.CBox +import sigma.data.{CBox, CHeader} /** This works in tandem with ConstantSerializer, if you change one make sure to check the other.*/ object DataSerializer extends CoreDataSerializer { @@ -15,6 +16,9 @@ object DataSerializer extends CoreDataSerializer { case SBox => val b = v.asInstanceOf[CBox] ErgoBox.sigmaSerializer.serialize(b.ebox, w.asInstanceOf[SigmaByteWriter]) + case SHeader if VersionContext.current.isV6SoftForkActivated => + val h = v.asInstanceOf[CHeader] + ErgoHeader.sigmaSerializer.serialize(h.ergoHeader, w.asInstanceOf[SigmaByteWriter]) case _ => super.serialize(v, tpe, w) } @@ -32,6 +36,12 @@ object DataSerializer extends CoreDataSerializer { val res = CBox(ErgoBox.sigmaSerializer.parse(r.asInstanceOf[SigmaByteReader])) r.level = r.level - 1 res + case SHeader if VersionContext.current.isV6SoftForkActivated => + val depth = r.level + r.level = depth + 1 + val res = new CHeader(ErgoHeader.sigmaSerializer.parse(r.asInstanceOf[SigmaByteReader])) + r.level = r.level - 1 + res case t => super.deserialize(t, r) }).asInstanceOf[T#WrappedType] diff --git a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala index 3d1061d774..5122ee940c 100644 --- a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala @@ -1,13 +1,13 @@ package sigma.serialization import org.ergoplatform.validation.ValidationRules.{CheckDeserializedScriptIsSigmaProp, CheckHeaderSizeBit} -import sigma.ast.{Constant, DeserializationSigmaBuilder, ErgoTree, SType, SubstConstants, UnparsedErgoTree} +import sigma.ast.{Constant, DeserializationSigmaBuilder, ErgoTree, SType, UnparsedErgoTree} import sigma.ast.syntax.ValueOps import sigma.ast.ErgoTree.{EmptyConstants, HeaderType} import sigma.util.safeNewArray import debox.cfor import sigma.VersionContext -import sigma.validation.{SigmaValidationSettings, ValidationException} +import sigma.validation.ValidationException import sigma.validation.ValidationRules.CheckPositionLimit /** @@ -287,6 +287,9 @@ class ErgoTreeSerializer { * allow to use serialized scripts as pre-defined templates. * See [[SubstConstants]] for details. * + * Note, this operation doesn't require (de)serialization of ErgoTree expression, + * thus it is more efficient than serialization roundtrip. + * * @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1. * @param positions zero based indexes in ErgoTree.constants array which * should be replaced with new values @@ -299,26 +302,28 @@ class ErgoTreeSerializer { */ def substituteConstants(scriptBytes: Array[Byte], positions: Array[Int], - newVals: Array[Constant[SType]])(implicit vs: SigmaValidationSettings): (Array[Byte], Int) = { + newVals: Array[Constant[SType]]): (Array[Byte], Int) = { require(positions.length == newVals.length, s"expected positions and newVals to have the same length, got: positions: ${positions.toSeq},\n newVals: ${newVals.toSeq}") val r = SigmaSerializer.startReader(scriptBytes) val (header, _, constants, treeBytes) = deserializeHeaderWithTreeBytes(r) - val w = SigmaSerializer.startWriter() - w.put(header) + val nConstants = constants.length + + val resBytes = if (VersionContext.current.isJitActivated) { + // need to measure the serialized size of the new constants + // by serializing them into a separate writer + val constW = SigmaSerializer.startWriter() - if (VersionContext.current.isJitActivated) { // The following `constants.length` should not be serialized when segregation is off // in the `header`, because in this case there is no `constants` section in the // ErgoTree serialization format. Thus, applying this `substituteConstants` for // non-segregated trees will return non-parsable ErgoTree bytes (when // `constants.length` is put in `w`). if (ErgoTree.isConstantSegregation(header)) { - w.putUInt(constants.length) + constW.putUInt(constants.length) } // The following is optimized O(nConstants + position.length) implementation - val nConstants = constants.length if (nConstants > 0) { val backrefs = getPositionsBackref(positions, nConstants) cfor(0)(_ < nConstants, _ + 1) { i => @@ -326,17 +331,38 @@ class ErgoTreeSerializer { val iPos = backrefs(i) // index to `positions` if (iPos == -1) { // no position => no substitution, serialize original constant - constantSerializer.serialize(c, w) + constantSerializer.serialize(c, constW) } else { - assert(positions(iPos) == i) // INV: backrefs and positions are mutually inverse + require(positions(iPos) == i) // INV: backrefs and positions are mutually inverse val newConst = newVals(iPos) require(c.tpe == newConst.tpe, s"expected new constant to have the same ${c.tpe} tpe, got ${newConst.tpe}") - constantSerializer.serialize(newConst, w) + constantSerializer.serialize(newConst, constW) } } } + + val constBytes = constW.toBytes // nConstants + serialized new constants + + // start composing the resulting tree bytes + val w = SigmaSerializer.startWriter() + w.put(header) // header byte + + if (VersionContext.current.isV6SoftForkActivated) { + // fix in v6.0 to save tree size to respect size bit of the original tree + if (ErgoTree.hasSize(header)) { + val size = constBytes.length + treeBytes.length + w.putUInt(size) // tree size + } + } + + w.putBytes(constBytes) // constants section + w.putBytes(treeBytes) // tree section + w.toBytes } else { + val w = SigmaSerializer.startWriter() + w.put(header) + // for v4.x compatibility we save constants.length here (see the above comment to // understand the consequences) w.putUInt(constants.length) @@ -347,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) @@ -357,10 +383,12 @@ class ErgoTreeSerializer { case (c, _) => constantSerializer.serialize(c, w) } + + w.putBytes(treeBytes) + w.toBytes } - w.putBytes(treeBytes) - (w.toBytes, constants.length) + (resBytes, nConstants) } } diff --git a/data/shared/src/main/scala/sigma/serialization/MethodCallSerializer.scala b/data/shared/src/main/scala/sigma/serialization/MethodCallSerializer.scala index 319a4284e2..abc12c9c9e 100644 --- a/data/shared/src/main/scala/sigma/serialization/MethodCallSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/MethodCallSerializer.scala @@ -1,13 +1,15 @@ package sigma.serialization import sigma.ast.syntax._ -import sigma.ast.{MethodCall, SContextMethods, SMethod, SType, STypeSubst, Value, ValueCompanion} +import sigma.ast.{MethodCall, SContextMethods, SMethod, SType, STypeSubst, STypeVar, Value, ValueCompanion} import sigma.util.safeNewArray import SigmaByteWriter._ import debox.cfor import sigma.ast.SContextMethods.BlockchainContextMethodNames import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo} +import scala.collection.compat.immutable.ArraySeq + case class MethodCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[SType]], STypeSubst) => Value[SType]) extends ValueSerializer[MethodCall] { override def opDesc: ValueCompanion = MethodCall @@ -23,6 +25,10 @@ case class MethodCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[S w.putValue(mc.obj, objInfo) assert(mc.args.nonEmpty) w.putValues(mc.args, argsInfo, argsItemInfo) + mc.method.explicitTypeArgs.foreach { a => + val tpe = mc.typeSubst(a) // existence is checked in MethodCall constructor + w.putType(tpe) + } } /** The SMethod instances in STypeCompanions may have type STypeIdent in methods types, @@ -43,9 +49,36 @@ case class MethodCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[S val obj = r.getValue() val args = r.getValues() val method = SMethod.fromIds(typeId, methodId) - val nArgs = args.length - val types: Seq[SType] = + val explicitTypes = if (method.hasExplicitTypeArgs) { + val nTypes = method.explicitTypeArgs.length + val res = safeNewArray[SType](nTypes) + cfor(0)(_ < nTypes, _ + 1) { i => + res(i) = r.getType() + } + ArraySeq.unsafeWrapArray(res) + } else SType.EmptySeq + + val explicitTypeSubst = method.explicitTypeArgs.zip(explicitTypes).toMap + val specMethod = getSpecializedMethodFor(method, explicitTypeSubst, obj, args) + + var isUsingBlockchainContext = specMethod.objType == SContextMethods && + BlockchainContextMethodNames.contains(method.name) + r.wasUsingBlockchainContext ||= isUsingBlockchainContext + + cons(obj, specMethod, args, explicitTypeSubst) + } + + def getSpecializedMethodFor( + methodTemplate: SMethod, + explicitTypeSubst: STypeSubst, + obj: SValue, + args: Seq[SValue] + ): SMethod = { + // TODO optimize: avoid repeated transformation of method type + val method = methodTemplate.withConcreteTypes(explicitTypeSubst) + val nArgs = args.length + val argTypes: Seq[SType] = if (nArgs == 0) SType.EmptySeq else { val types = safeNewArray[SType](nArgs) @@ -55,12 +88,6 @@ case class MethodCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[S types } - val specMethod = method.specializeFor(obj.tpe, types) - - var isUsingBlockchainContext = specMethod.objType == SContextMethods && - BlockchainContextMethodNames.contains(method.name) - r.wasUsingBlockchainContext ||= isUsingBlockchainContext - - cons(obj, specMethod, args, Map.empty) + method.specializeFor(obj.tpe, argTypes) } } 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/PropertyCallSerializer.scala b/data/shared/src/main/scala/sigma/serialization/PropertyCallSerializer.scala index 10411e21ce..072e7a7ed5 100644 --- a/data/shared/src/main/scala/sigma/serialization/PropertyCallSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/PropertyCallSerializer.scala @@ -5,6 +5,10 @@ import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo} import sigma.ast._ import sigma.ast.syntax.SValue import SigmaByteWriter._ +import debox.cfor +import sigma.util.safeNewArray + +import scala.collection.compat.immutable.ArraySeq case class PropertyCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[SType]], STypeSubst) => Value[SType]) extends ValueSerializer[MethodCall] { @@ -17,6 +21,10 @@ case class PropertyCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value w.put(mc.method.objType.typeId, typeCodeInfo) w.put(mc.method.methodId, methodCodeInfo) w.putValue(mc.obj, objInfo) + mc.method.explicitTypeArgs.foreach { a => + val tpe = mc.typeSubst(a) // existence is checked in MethodCall constructor + w.putType(tpe) + } } override def parse(r: SigmaByteReader): Value[SType] = { @@ -24,7 +32,22 @@ case class PropertyCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value val methodId = r.getByte() val obj = r.getValue() val method = SMethod.fromIds(typeId, methodId) - val specMethod = method.specializeFor(obj.tpe, SType.EmptySeq) - cons(obj, specMethod, Value.EmptySeq, EmptySubst) + + val (explicitTypeSubst: Map[STypeVar, SType], specMethod: SMethod) = if (method.hasExplicitTypeArgs) { + val nTypes = method.explicitTypeArgs.length + val res = safeNewArray[SType](nTypes) + cfor(0)(_ < nTypes, _ + 1) { i => + res(i) = r.getType() + } + val explicitTypes = ArraySeq.unsafeWrapArray(res) + val explicitTypeSubst = method.explicitTypeArgs.zip(explicitTypes).toMap + val specMethod = method.withConcreteTypes(explicitTypeSubst) + (explicitTypeSubst, specMethod) + } else { + val specMethod = method.specializeFor(obj.tpe, SType.EmptySeq) + (EmptySubst, specMethod) + } + + cons(obj, specMethod, Value.EmptySeq, explicitTypeSubst) } } 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 cdb28d724b..7da7ec1606 100644 --- a/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala @@ -4,7 +4,7 @@ import java.nio.ByteBuffer import scorex.util.ByteArrayBuilder import scorex.util.serialization._ import sigma.data.SigmaConstants -import sigma.validation.SigmaValidationSettings +import sigma.serialization.SigmaByteWriter.{FixedCostCallback, PerItemCostCallback} import sigma.serialization.ValueCodes.OpCode object SigmaSerializer { @@ -35,8 +35,7 @@ object SigmaSerializer { /** Helper function to be use in serializers. */ def startReader(bytes: Array[Byte], constantStore: ConstantStore, - resolvePlaceholdersToConstants: Boolean) - (implicit vs: SigmaValidationSettings): SigmaByteReader = { + resolvePlaceholdersToConstants: Boolean): SigmaByteReader = { val buf = ByteBuffer.wrap(bytes) val r = new SigmaByteReader(new VLQByteBufferReader(buf), constantStore, @@ -53,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 ddbb7bd680..3f5453072f 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -4,18 +4,18 @@ ErgoScript is a language to write contracts for [Ergo blockchain](https://ergoplatform.org). ErgoScript contracts can be compiled to -[ErgoTrees](https://ergoplatform.org/docs/ErgoTree.pdf), serialized and stored +[ErgoTrees](https://ergoplatform.org/docs/ErgoTree.pdf), typed abstract serialized and stored in UTXOs. A good starting point to writing contracts is to use [ErgoScript by -Example](https://github.com/ergoplatform/ergoscript-by-example) with [Ergo +Example](https://github.com/ergoplatform/ergoscript-by-example), with [Ergo Playgrounds](https://github.com/ergoplatform/ergo-playgrounds) or -[Appkit](https://github.com/ergoplatform/ergo-appkit). +[Appkit](https://github.com/ergoplatform/ergo-appkit) or [Fleet](https://github.com/fleet-sdk) . ErgoScript compiler is [published](https://mvnrepository.com/artifact/org.scorexfoundation/sigma-state) as a library which is cross compiled for Java 7 and Java 8+ and thus can be used -from any JVM lanugage and also on Android and JavaFX platforms. +from any JVM langugage and also on Android and JavaFX platforms. It is also cross-compiled to JavaScript using Scala.js. The following example shows how source code of ErgoScript contract can be used to create new transaction using @@ -56,7 +56,7 @@ The following sections describe ErgoScript and its operations. #### ErgoScript language features overview -- syntax borrowed from Scala +- syntax borrowed from Scala but simplified - standard syntax and semantics for well known constructs (operations, code blocks, if branches etc.) - high-order language with first-class lambdas which are used in collection operations - call-by-value (eager evaluation) @@ -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: `deserializeTo`, `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)` @@ -97,6 +97,7 @@ Type Name | Description `Int` | 32 bit signed integer `Long` | 64 bit signed integer `BigInt` | 256 bit signed integer +`UnsignedBigInt` | 256 bit unsigned integer `SigmaProp` | a type representing a _sigma proposition_ which can be verified by executing a Sigma protocol with zero-knowledge proof of knowledge. Every contract should return a value of this type. `AvlTree` | represents a digest of authenticated dynamic dictionary and can be used to verify proofs of operations performed on the dictionary `GroupElement` | elliptic curve points @@ -119,11 +120,12 @@ types as in the following example. Literals are used to introduce values of some types directly in program text like in the following example: ``` - val unit: Unit = () // unit constant - val long: Int = 10 // interger value literal - val bool: Boolean = true // logical literal - val arr = Coll(1, 2, 3) // constructs a collection with given items - val str = "abc" // string of characters + val unit: Unit = () // unit constant + val long: Int = 10 // interger value literal + val bool: Boolean = true // logical literal + val arr = Coll(1, 2, 3) // constructs a collection with given items + val str = "abc" // string of characters + val ubi = unsignedBigInt("508473958676860") // unsigned big integer ``` Note that many types don't have literal syntax and their values are introduced by applying operations, for example `deserialize` function can be used to introduce @@ -167,9 +169,61 @@ class Numeric { /** Convert this Numeric value to BigInt. */ def toBigInt: BigInt + + /** Convert this Numeric value to UnsignedBigInt. */ + def toUnsignedBigInt: UnsignedBigInt + + /** Returns a big-endian representation of this value in a collection of bytes. + * For example, the `Int` value `0x12131415` would yield the + * collection of bytes [0x12, 0x13, 0x14, 0x15] + */ + def toBytes(x: T): Coll[Byte] + + /** + * Returns a big-endian binary representation of this value as boolean array. + */ + def toBits(x: T): Coll[Boolean] + + /** + * @return a numeric value which is inverse of `x` (every bit, including sign, is flipped) + */ + def bitwiseInverse(x: T): T + + /** + * @return a numeric value which is `this | that` + */ + def bitwiseOr(x: T, y: T): T + + /** + * @return a numeric value which is `this && that` + */ + def bitwiseAnd(x: T, y: T): T + + /** + * @return a numeric value which is `this xor that` + */ + def bitwiseXor(x: T, y: T): T + + /** + * @return a value which is (this << n). The shift distance, n, may be negative, + * in which case this method performs a right shift. (Computes floor(this * 2n).) + */ + def shiftLeft(x: T, bits: Int): T + + /** + * @return a value which is (this >> n). Sign extension is performed. The shift distance, n, + * may be negative, in which case this method performs a left shift. (Computes floor(this / 2n).) + */ + def shiftRight(x: T, bits: Int): T + } ``` +The only exception for conversions is about BigInt to and from UnsignedBigInt. To convert from signed big int to unsigned, use +`.toUnsigned` method to convert signed big integer to unsigned, or `.toUnsignedMod(m)` to convert modulo `m` (and modulo + operation is cryptographic, ie always returns positive number modulo `m`). To convert from unsigned big int to signed, + use `.toSigned`. + All the predefined numeric types inherit Numeric class and its methods. They can be thought as being pre-defined like the following. @@ -179,8 +233,51 @@ class Short extends Numeric class Int extends Numeric class Long extends Numeric class BigInt extends Numeric +class UnsignedBigInt extends Numeric +``` + +examples: + +``` +val b = (126 + 1).toByte // max byte value +b.bitwiseInverse == (-128).toByte // min byte value +``` + +``` +val x = 4.toByte +val y = 2 +x.shiftLeft(y) == 16.toByte +``` + +``` +val l = getVar[Long](1).get +val ba = l.toBytes +Global.fromBigEndianBytes[Long](ba) == l +``` + +In addition to all the methods from above, UnsignedBigInt type has support for modular arithmetics, namely, `modInverse`, +`plusMod`, `subtractMod`, `multiplyMod`, `mod`. Examples: + +``` +val bi1 = unsignedBigInt("2") +val bi2 = unsignedBigInt("4") +val m = unsignedBigInt("575879797") +bi1.subtractMod(bi2, m) > 0 +``` + +``` +val bi = unsignedBigInt("248486720836984554860790790898080606") +val m = unsignedBigInt("575879797") +bi.mod(m) < bi ``` +``` +val bi = unsignedBigInt("248486720836984554860790790898080606") +val m = unsignedBigInt("575879797") +bi.modInverse(m) > 0 // returns multiplicative inverse of bi mod m +``` + + #### Context Data Every script is executed in a context, which is a collection of data available @@ -245,10 +342,35 @@ class Context { */ def minerPubKey: Coll[Byte] + /** Extracts Context variable 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. + */ + def getVar[T](id: Byte): Option[T] + + /** + * A variant of `getVar` to extract a context variable by id and type from any input + */ + def getVarFromInput[T](inputIndex: Short, id: Byte): Option[T] + + /** Returns new $coll with elements in reversed order. + * + * @return A new $coll with all elements of this $coll in reversed order. + */ + def reverse: Coll[T] + + /** Builds a new $coll from this $coll without any duplicate elements. + * + * @return A new $coll which contains the first occurrence of every element of this $coll. + */ + def distinct: Coll[T] + } /** Represents data of the block headers available in scripts. */ class Header { + /** Bytes representation of ModifierId of this Header */ def id: Coll[Byte] @@ -272,7 +394,7 @@ class Header { /** Current difficulty in a compressed view. * NOTE: actually it is unsigned Int*/ - def nBits: Long // actually it is unsigned Int + def nBits: Long /** Block height */ def height: Int @@ -284,24 +406,16 @@ class Header { * Part of Autolykos solution (pk). */ def minerPk: GroupElement - - /** One-time public key. Prevents revealing of miners secret. - * Part of Autolykos solution (w). - */ - def powOnetimePk: GroupElement - + /** Nonce value found by the miner. Part of Autolykos solution (n). */ def powNonce: Coll[Byte] - - /** Distance between pseudo-random number, corresponding to nonce `powNonce` - * and a secret, corresponding to `minerPk`. The lower `powDistance` is, the - * harder it was to find this solution. - * Part of Autolykos solution (d). - */ - def powDistance: BigInt - + /** Miner votes for changing system parameters. */ def votes: Coll[Byte] + + /** Validate header's proof-of-work */ + def checkPow: Boolean + } /** Only header fields that can be predicted by a miner. */ @@ -454,6 +568,12 @@ class GroupElement { * @return this to the power of k. */ 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 @@ -665,8 +785,7 @@ class Option[A] { /** Returns the option's value if the option is nonempty, otherwise * return the result of evaluating `default`. - * NOTE: the `default` is evaluated even if the option contains the value - * i.e. not lazily. + * NOTE: the `default` is evaluated lazily. * * @param default the default expression. */ @@ -841,6 +960,27 @@ class Coll[A] { * to `elem`, or `-1`, if none exists. */ def indexOf(elem: A, from: Int): Int + + /** 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] + + /** + * @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 + + } ``` @@ -850,6 +990,14 @@ val myOutput = OUTPUTS(0) val myInput = INPUTS(0) ``` +or, if you want to get None instead of exception: +``` +val elemOpt = coll.get(0) +if (elemOpt.isDefined) { + val elem = elemOpt.get +} +``` + Any collection have the `size` property which returns the number of elements in the collection. @@ -864,6 +1012,73 @@ satisfying some predicate (condition) val ok = OUTPUTS.exists { (box: Box) => box.value > 1000 } ``` +### Predefined global functions + +There are some functions which do not belong to other types, thus they put under `Global` type. Those functions are: + + + +``` +{ + /** The generator g of the group is an element of the group such that, when written + * multiplicative form, every element of the group is a power of g. + * @return the generator of this Dlog group + */ + def groupGenerator: GroupElement + + /** + * @return NBits-encoded approximate representation of given big integer, + * see (https://bitcoin.stackexchange.com/questions/57184/what-does-the-nbits-value-represent) + * for NBits format details + */ + def encodeNbits(bi: BigInt): Long + + /** + * @return big integer decoded from NBits value provided, + * see (https://bitcoin.stackexchange.com/questions/57184/what-does-the-nbits-value-represent) + * for format details + */ + def decodeNbits(l: Long): BigInt + + + /** 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] + + /** Calculates value of a custom Autolykos 2 hash function + * @param k - k parameter of Autolykos 2 (number of inputs in k-sum problem) + * @param msg - message to calculate Autolykos hash 2 for + * @param nonce - used to pad the message to get Proof-of-Work hash function output with desirable properties + * @param h - PoW protocol specific padding for table uniqueness (e.g. block height in Ergo) + */ + def powHit(k: Int, msg: Coll[Byte], nonce: Coll[Byte], h: Coll[Byte], N: Int): BigInt + + /** Deserializes provided `bytes` into a value of type `T`. **/ + def deserializeTo[T](bytes: Coll[Byte])(implicit cT: RType[T]): T + + /** Returns a number decoded from provided big-endian bytes array. */ + def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T +} +``` + +examples: + +``` +val h = getVar[Header](21).get +val n = h.nBits +val target = Global.decodeNbits(n) +``` + +``` +val src = getVar[Coll[Short]](21).get +val ba = Global.serialize(src) +val restored = Global.deserializeTo[Coll[Short]](ba) +src == restored +``` + + ### Predefined global functions @@ -899,6 +1114,10 @@ def blake2b256(input: Coll[Byte]): Coll[Byte] /** Cryptographic hash function Sha256 (See scorex.crypto.hash.Sha256) */ def sha256(input: Coll[Byte]): Coll[Byte] +/** Create an instance of type T from bytes of its wrapped type. +See https://github.com/ScorexFoundation/sigmastate-interpreter/pull/979 for more details */ +def deserializeTo[T](input: Coll[Byte]): T + /** Create BigInt from a collection of bytes. */ def byteArrayToBigInt(input: Coll[Byte]): BigInt @@ -915,7 +1134,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. @@ -972,6 +1191,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. @@ -985,6 +1216,18 @@ def proveDHTuple(g: GroupElement, h: GroupElement, */ def proveDlog(value: GroupElement): SigmaProp +/** Transforms Base16 encoded string literal into constant of type BigInt. + * It is a compile-time operation and only string literal (constant) can be its + * argument. + */ +def bigInt(input: String): BigInt + +/** Transforms Base16 encoded string literal into constant of type UnsignedBigInt. + * It is a compile-time operation and only string literal (constant) can be its + * argument. + */ +def unsignedBigInt(input: String): UnsignedBigInt + /** Transforms Base16 encoded string literal into constant of type Coll[Byte]. * It is a compile-time operation and only string literal (constant) can be its * argument. diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala index a04d32fa54..414a9d1220 100644 --- a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala @@ -65,7 +65,6 @@ class SigmaPropProver(override val wrappedValue: org.ergoplatform.SigmaPropProve val realsToExtract = toSigmaBooleanSeq(realSecretsToExtract) val simsToExtract = toSigmaBooleanSeq(simulatedSecretsToExtract) val hints = wrappedValue.bagForMultisig( - context = null, sigmaTree = sigmaTree.sigmaBoolean, proof.toArray, realSecretsToExtract = realsToExtract, simulatedSecretsToExtract = simsToExtract) diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala index 8468175631..4b1366d9fd 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala @@ -6,7 +6,6 @@ import sigma.ast.SType.{AnyOps, TypeCode} import sigma.ast._ import sigma.data.{AvlTreeData, CAvlTree, CSigmaDslBuilder, SigmaConstants} import sigma.eval.Extensions.toAnyValue -import sigma.exceptions.InterpreterException import sigma.interpreter.ContextExtension import sigma.validation.SigmaValidationSettings import sigma.{AnyValue, Coll, Header, PreHeader} @@ -169,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/crypto/GF2_192.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192.scala index bb0ad60a84..e63526e854 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192.scala +++ b/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192.scala @@ -30,8 +30,6 @@ package sigmastate.crypto import debox.cfor -import java.util - class GF2_192 extends AnyRef { private[crypto] val word: Array[Long] = new Array[Long](3) diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192_Poly.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192_Poly.scala index a9212fe565..d2ddc4db48 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192_Poly.scala +++ b/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192_Poly.scala @@ -31,8 +31,6 @@ package sigmastate.crypto import debox.cfor -import java.util - class GF2_192_Poly { final private var c: Array[GF2_192] = null // must be not null and of length at least 1 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/main/scala/sigmastate/eval/CHeader.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CHeader.scala deleted file mode 100644 index 7062fa0f0e..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CHeader.scala +++ /dev/null @@ -1,34 +0,0 @@ -package sigmastate.eval - -import sigma.data.SigmaConstants -import sigma.{AvlTree, BigInt, Coll, GroupElement, Header} - -/** A default implementation of [[Header]] interface. - * - * @see [[Header]] for detailed descriptions - */ -case class CHeader( - id: Coll[Byte], - version: Byte, - parentId: Coll[Byte], - ADProofsRoot: Coll[Byte], - stateRoot: AvlTree, - transactionsRoot: Coll[Byte], - timestamp: Long, - nBits: Long, - height: Int, - extensionRoot: Coll[Byte], - minerPk: GroupElement, - powOnetimePk: GroupElement, - powNonce: Coll[Byte], - powDistance: BigInt, - votes: Coll[Byte] -) extends Header - -object CHeader { - /** Size of of Header.votes array. */ - val VotesSize: Int = SigmaConstants.VotesArraySize.value - - /** Size of nonce array from Autolykos POW solution in Header.powNonce array. */ - val NonceSize: Int = SigmaConstants.AutolykosPowSolutionNonceArraySize.value -} \ No newline at end of file diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CProfiler.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CProfiler.scala index ced8a357b8..d07153bd51 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CProfiler.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CProfiler.scala @@ -280,7 +280,7 @@ class CProfiler extends Profiler { case ci: TypeBasedCostItem => val comment = s"count: $count, suggested: $suggestedCost, actCost: ${ci.cost}$warn" (ci.opName, time, time, comment) - case ci @ SeqCostItem(_, costKind, nItems) => + case ci @ SeqCostItem(_, costKind, _) => val nChunks = ci.chunks val timePerChunk = if (nChunks > 0) time / nChunks else time val name = s"${ci.opName}(nChunks: $nChunks)" @@ -289,7 +289,7 @@ class CProfiler extends Profiler { } } (name, timePerItem, time, comment) - }.sortBy({ case (name, tpi, t, c) => (name, tpi)})(Ordering[(String, Long)]) + }.sortBy({ case (name, tpi, _, _) => (name, tpi)})(Ordering[(String, Long)]) val estLines = estimationCostStat.mapToArray { case (script, stat) => val (cost, count) = stat.mean @@ -302,7 +302,7 @@ class CProfiler extends Profiler { val rows = opCodeLines - .map { case (opName, opCode, time, comment) => + .map { case (opName, _, time, comment) => val key = s"$opName".padTo(26, ' ') s"$key -> time: $time ns, $comment " } diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala index de8aa6b620..9916a6c565 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala @@ -5,15 +5,17 @@ import sigma.ast._ import sigma.ast.syntax._ import sigmastate.eval.{CAvlTreeVerifier, CProfiler} import sigmastate.interpreter.Interpreter.ReductionResult -import sigma.{AvlTree, Coll, Colls, Context, VersionContext} +import sigma.{AvlTree, Coll, Colls, Context, Header, VersionContext} import sigma.util.Extensions._ import debox.{cfor, Buffer => DBuffer} import scorex.crypto.authds.ADKey import sigma.ast.SAvlTreeMethods._ +import sigma.ast.SHeaderMethods.checkPowMethod import sigma.ast.SType import sigma.data.{CSigmaProp, KeyValueColl, SigmaBoolean} import sigma.eval.{AvlTreeVerifier, ErgoTreeEvaluator, EvalSettings, Profiler} import sigma.eval.ErgoTreeEvaluator.DataEnv +import sigmastate.interpreter.CErgoTreeEvaluator.fixedCostOp import scala.collection.compat.immutable.ArraySeq import scala.util.{DynamicVariable, Failure, Success} @@ -293,7 +295,7 @@ class CErgoTreeEvaluator( } /** @hotspot don't beautify the code */ - override def addFixedCost(costKind: FixedCost, opDesc: OperationDesc)(block: => Unit): Unit = { + override def addFixedCost[R](costKind: FixedCost, opDesc: OperationDesc)(block: => R): R = { var costItem: FixedCostItem = null if (settings.costTracingEnabled) { costItem = FixedCostItem(opDesc, costKind) @@ -305,16 +307,17 @@ class CErgoTreeEvaluator( } val start = System.nanoTime() coster.add(costKind.cost) - val _ = block + val res = block val end = System.nanoTime() profiler.addCostItem(costItem, end - start) + res } else { coster.add(costKind.cost) block } } - override def addFixedCost(costInfo: OperationCostInfo[FixedCost])(block: => Unit): Unit = { + override def addFixedCost[R](costInfo: OperationCostInfo[FixedCost])(block: => R): R = { addFixedCost(costInfo.costKind, costInfo.opDesc)(block) } @@ -449,7 +452,7 @@ object CErgoTreeEvaluator { * HOTSPOT: don't beautify the code * Note, `null` is used instead of Option to avoid allocations. */ - def fixedCostOp[R <: AnyRef](costInfo: OperationCostInfo[FixedCost]) + def fixedCostOp[R](costInfo: OperationCostInfo[FixedCost]) (block: => R)(implicit E: ErgoTreeEvaluator): R = { if (E != null) { var res: R = null.asInstanceOf[R] diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala index 6b673038c7..88d7be4324 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala @@ -23,7 +23,7 @@ import sigmastate.interpreter.CErgoTreeEvaluator.fixedCostOp import sigmastate.interpreter.Interpreter._ import sigma.ast.syntax.ValueOps import sigma.eval.{EvalSettings, SigmaDsl} -import sigma.exceptions.{CostLimitException, InterpreterException} +import sigma.exceptions.InterpreterException import sigma.interpreter.ProverResult import sigma.util.CollectionUtil import sigmastate.utils.Helpers._ @@ -126,7 +126,7 @@ trait Interpreter { case _ => None } - /** Extracts proposition for ErgoTree handing soft-fork condition. + /** Extracts proposition for ErgoTree handling soft-fork condition. * @note soft-fork handler */ protected def propositionFromErgoTree(ergoTree: ErgoTree, context: CTX): SigmaPropValue = { val validationSettings = context.validationSettings @@ -164,12 +164,11 @@ trait Interpreter { * else `exp` is computed in the given context and the resulting SigmaBoolean returned. * * @param context the context in which `exp` should be executed - * @param env environment of system variables used by the interpreter internally * @param exp expression to be executed in the given `context` * @return result of script reduction * @see `ReductionResult` */ - protected def reduceToCryptoJITC(context: CTX, env: ScriptEnv, exp: SigmaPropValue): Try[ReductionResult] = Try { + protected def reduceToCryptoJITC(context: CTX, exp: SigmaPropValue): Try[ReductionResult] = Try { implicit val vs = context.validationSettings trySoftForkable[ReductionResult](whenSoftFork = WhenSoftForkReductionResult(context.initCost)) { @@ -248,12 +247,17 @@ trait Interpreter { val currCost = addCostChecked(context.initCost, deserializeSubstitutionCost, context.costLimit) val context1 = context.withInitCost(currCost).asInstanceOf[CTX] val (propTree, context2) = trySoftForkable[(SigmaPropValue, CTX)](whenSoftFork = (TrueSigmaProp, context1)) { - applyDeserializeContextJITC(context, prop) + // Before ErgoTree V3 the deserialization cost was not added to the total cost + applyDeserializeContextJITC(if (VersionContext.current.activatedVersion >= VersionContext.V6SoftForkVersion) { + context1 + } else { + context + }, prop) } // here we assume that when `propTree` is TrueProp then `reduceToCrypto` always succeeds // and the rest of the verification is also trivial - reduceToCryptoJITC(context2, env, propTree).getOrThrow + reduceToCryptoJITC(context2, propTree).getOrThrow } res @@ -594,4 +598,4 @@ object Interpreter { case x => throw new Error(s"Context-dependent pre-processing should produce tree of type Boolean or SigmaProp but was $x") } -} \ No newline at end of file +} diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala index 15daaec382..dc2259b0b7 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala @@ -81,7 +81,7 @@ trait ProverUtils extends Interpreter { realSecretsToExtract: Seq[SigmaBoolean], simulatedSecretsToExtract: Seq[SigmaBoolean] = Seq.empty): HintsBag = { val reduced = fullReduction(ergoTree, context, Interpreter.emptyEnv) - bagForMultisig(context, reduced.value, proof, realSecretsToExtract, simulatedSecretsToExtract) + bagForMultisig(reduced.value, proof, realSecretsToExtract, simulatedSecretsToExtract) } /** @@ -90,15 +90,13 @@ trait ProverUtils extends Interpreter { * * See DistributedSigSpecification for examples of usage. * - * @param context - context used to reduce the proposition * @param sigmaTree - public key (in form of a sigma-tree) * @param proof - signature for the key * @param realSecretsToExtract - public keys of secrets with real proofs * @param simulatedSecretsToExtract - public keys of secrets with simulated proofs * @return - bag of OtherSecretProven and OtherCommitment hints */ - def bagForMultisig(context: CTX, - sigmaTree: SigmaBoolean, + def bagForMultisig(sigmaTree: SigmaBoolean, proof: Array[Byte], realSecretsToExtract: Seq[SigmaBoolean], simulatedSecretsToExtract: Seq[SigmaBoolean]): HintsBag = { diff --git a/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala b/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala index 54abc40f4e..2e66eb6b19 100644 --- a/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala +++ b/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala @@ -13,7 +13,7 @@ object Extensions { * For example, the Byte value {@code 0x12} would yield the * byte array {@code {0x12}}. */ - def toBytes: Coll[Byte] = SigmaDsl.Colls.fromItems(b) + def toBigEndianBytes: Coll[Byte] = SigmaDsl.Colls.fromItems(b) } @@ -22,7 +22,7 @@ object Extensions { * For example, the Short value {@code 0x1213} would yield the * byte array {@code {0x12, 0x13}}. */ - def toBytes: Coll[Byte] = Colls.fromArray(Shorts.toByteArray(x)) + def toBigEndianBytes: Coll[Byte] = Colls.fromArray(Shorts.toByteArray(x)) } implicit class IntOpsForSigma(val x: Int) extends AnyVal { @@ -30,7 +30,7 @@ object Extensions { * For example, the Int value {@code 0x12131415} would yield the * byte array {@code {0x12, 0x13, 0x14, 0x15}}. */ - def toBytes: Coll[Byte] = Colls.fromArray(Ints.toByteArray(x)) + def toBigEndianBytes: Coll[Byte] = Colls.fromArray(Ints.toByteArray(x)) } implicit class LongOpsForSigma(val x: Long) extends AnyVal { @@ -38,7 +38,7 @@ object Extensions { * For example, the Long value {@code 0x1213141516171819} would yield the * byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}}. */ - def toBytes: Coll[Byte] = Colls.fromArray(Longs.toByteArray(x)) + def toBigEndianBytes: Coll[Byte] = Colls.fromArray(Longs.toByteArray(x)) } /** Provides extension methods for `ModifierId` instances. diff --git a/interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala b/interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala index acf8e74bd2..ea3fa5d5ec 100644 --- a/interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala +++ b/interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala @@ -117,14 +117,18 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma def testArray[T <: SType] (v: T#WrappedType, c: Constant[T])(implicit t: RType[T#WrappedType]) = { - // for any Byte and Short value `v`, lifting of array should succeed val arr = Array.fill[T#WrappedType](10)(v)(t.classTag) testSuccess(arr, TransformingSigmaBuilder.mkCollectionConstant(arr, c.tpe)) } + def testArrayFailure[T <: SType] + (v: T#WrappedType, c: Constant[T])(implicit t: RType[T#WrappedType]) = { + val arr = Array.fill[T#WrappedType](10)(v)(t.classTag) + testFailure(arr) + } + def testColl[T <: SType] (v: T#WrappedType, c: Constant[T])(implicit t: RType[T#WrappedType]) = { - // for any Byte and Short value `v`, lifting of Coll should succeed val arr = Array.fill[T#WrappedType](10)(v)(t.classTag) val coll = arr.toColl testSuccess(coll, TransformingSigmaBuilder.mkCollectionConstant(coll, c.tpe)) @@ -134,7 +138,11 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma val v = true val c = BooleanConstant(v) test[SBoolean.type](v, c) - testArray[SBoolean.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) + if (!VersionContext.current.isV6SoftForkActivated) { + testArray[SBoolean.type](v, c) + } else { + testArrayFailure[SBoolean.type](v, c) + } testColl[SBoolean.type](v, c) } @@ -143,7 +151,11 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma val c = ByteConstant(v) testNumeric[SByte.type](v, c) testLiftingOfCAnyValue[SByte.type](v, c) - testArray[SByte.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) + if (!VersionContext.current.isV6SoftForkActivated) { + testArray[SByte.type](v, c) + } else { + testArrayFailure[SByte.type](v, c) + } testColl[SByte.type](v, c) } @@ -152,7 +164,11 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma val c = ShortConstant(v) testNumeric[SShort.type](v, c) testLiftingOfCAnyValue[SShort.type](v, c) - testArray[SShort.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) + if (!VersionContext.current.isV6SoftForkActivated) { + testArray[SShort.type](v, c) + } else { + testArrayFailure[SShort.type](v, c) + } testColl[SShort.type](v, c) } @@ -160,7 +176,11 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma val v = 1 val c = IntConstant(v) test[SInt.type](v, c) - testArray[SInt.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) + if (!VersionContext.current.isV6SoftForkActivated) { + testArray[SInt.type](v, c) + } else { + testArrayFailure[SInt.type](v, c) + } testColl[SInt.type](v, c) } @@ -168,24 +188,41 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma val v = 1L val c = LongConstant(v) test[SLong.type](v, c) - testArray[SLong.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) + if (!VersionContext.current.isV6SoftForkActivated) { + testArray[SLong.type](v, c) + } else { + testArrayFailure[SLong.type](v, c) + } testColl[SLong.type](v, c) } property("liftToConstant String") { val v = "abc" val c = StringConstant(v) - test[SString.type](v, c) - testArray[SString.type](v, c) // TODO v6.0: String should be liftable at all (not supported in ErgoTree) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) - testColl[SString.type](v, c) + if (!VersionContext.current.isV6SoftForkActivated) { + // v6.0: String should not be liftable at all (not supported in ErgoTree) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) + test[SString.type](v, c) + testArray[SString.type](v, c) + testColl[SString.type](v, c) + } else { + testFailure(v) + } } property("liftToConstant BigInteger") { val v = BigInteger.valueOf(1L) val c = BigIntConstant(v) - testSuccess(v, c) // TODO v6.0: both BigInteger and arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) + if (!VersionContext.current.isV6SoftForkActivated) { + testSuccess(v, c) + } else { + testFailure(v) + } val arr = Array.fill(10)(v) - testSuccess(arr, TransformingSigmaBuilder.mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt), c.tpe)) + if (!VersionContext.current.isV6SoftForkActivated) { + testSuccess(arr, TransformingSigmaBuilder.mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt), c.tpe)) + } else { + testFailure(arr) + } } property("liftToConstant BigInt") { @@ -204,10 +241,26 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma testColl[SGroupElement.type](v, c) } + property("liftToConstant Header") { + val h = TestData.h1 + val c = HeaderConstant(h) + if (VersionContext.current.isV6SoftForkActivated) { + testSuccess(h, c) + } else { + testFailure(h) + } + testFailure(Array.fill(10)(h)) + testColl[SHeader.type](h, c) + } + property("liftToConstant ErgoBox") { val v = TestData.b2.asInstanceOf[CBox].wrappedValue val c = BoxConstant(TestData.b2) - testSuccess(v, c) // TODO v6.0: ErgoBox should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) + if (!VersionContext.current.isV6SoftForkActivated) { + testSuccess(v, c) + } else { + testFailure(v) + } testFailure(Array.fill(10)(v)) } @@ -234,7 +287,11 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma property("liftToConstant AvlTreeData") { val v = TestData.t1.asInstanceOf[CAvlTree].wrappedValue val c = AvlTreeConstant(SigmaDsl.avlTree(v)) - testSuccess(v, c) // TODO v6.0: AvlTreeData should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) + if (!VersionContext.current.isV6SoftForkActivated) { + testSuccess(v, c) + } else { + testFailure(v) + } testFailure(Array.fill(10)(v)) } diff --git a/interpreter/shared/src/test/scala/sigma/serialization/ConstantSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/ConstantSerializerSpecification.scala index 7bc73643f2..c9478c8356 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/ConstantSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/ConstantSerializerSpecification.scala @@ -9,7 +9,7 @@ import sigma.ast.{BigIntConstant, ByteArrayConstant, Constant, DeserializationSi import sigmastate.eval._ import sigma.Extensions.ArrayOps import sigma.ast._ -import sigma.{AvlTree, Colls, Evaluation} +import sigma.{AvlTree, Colls, Evaluation, Header, VersionContext} import sigma.ast.SType.AnyOps import scorex.util.encode.Base16 import sigma.ast.BoolArrayConstant.BoolArrayTypeCode @@ -17,6 +17,7 @@ import sigma.ast.ByteArrayConstant.ByteArrayTypeCode import sigma.ast.syntax.{BoolValue, SValue} import sigma.crypto.EcPointType import sigma.util.Extensions.{BigIntegerOps, EcpOps, SigmaBooleanOps} + import scala.annotation.nowarn class ConstantSerializerSpecification extends TableSerializationSpecification { @@ -25,22 +26,29 @@ class ConstantSerializerSpecification extends TableSerializationSpecification { implicit val wWrapped = wrappedTypeGen(tpe) implicit val tT = Evaluation.stypeToRType(tpe) implicit val tag = tT.classTag + + val withVersion = if (tpe == SHeader) { + Some(VersionContext.V6SoftForkVersion) + } else { + None + } + forAll { xs: Array[T#WrappedType] => implicit val tAny = sigma.AnyType - roundTripTest(Constant[SCollection[T]](xs.toColl, SCollection(tpe))) - roundTripTest(Constant[SType](xs.toColl.map(x => (x, x)).asWrappedType, SCollection(STuple(tpe, tpe)))) // pairs are special case + roundTripTest(Constant[SCollection[T]](xs.toColl, SCollection(tpe)), withVersion) + roundTripTest(Constant[SType](xs.toColl.map(x => (x, x)).asWrappedType, SCollection(STuple(tpe, tpe))), withVersion) // pairs are special case val triples = xs.toColl.map(x => TupleColl(x, x, x)).asWrappedType - roundTripTest(Constant[SType](triples, SCollection(STuple(tpe, tpe, tpe)))) + roundTripTest(Constant[SType](triples, SCollection(STuple(tpe, tpe, tpe))), withVersion) val quartets = xs.toColl.map(x => TupleColl(x, x, x, x)).asWrappedType - roundTripTest(Constant[SType](quartets, SCollection(STuple(tpe, tpe, tpe, tpe)))) - roundTripTest(Constant[SCollection[SCollection[T]]](xs.toColl.map(x => Colls.fromItems(x, x)), SCollection(SCollection(tpe)))) + roundTripTest(Constant[SType](quartets, SCollection(STuple(tpe, tpe, tpe, tpe))), withVersion) + roundTripTest(Constant[SCollection[SCollection[T]]](xs.toColl.map(x => Colls.fromItems(x, x)), SCollection(SCollection(tpe))), withVersion) roundTripTest(Constant[SType]( xs.toColl.map { x => val arr = Colls.fromItems(x, x) (arr, arr) }.asWrappedType, SCollection(STuple(SCollection(tpe), SCollection(tpe))) - )) + ), withVersion) } } @@ -49,19 +57,24 @@ class ConstantSerializerSpecification extends TableSerializationSpecification { implicit val tT = Evaluation.stypeToRType(tpe) @nowarn implicit val tag = tT.classTag implicit val tAny: RType[Any] = sigma.AnyType + val withVersion = if (tpe == SHeader) { + Some(VersionContext.V6SoftForkVersion) + } else { + None + } forAll { in: (T#WrappedType, T#WrappedType) => val (x,y) = (in._1, in._2) - roundTripTest(Constant[SType]((x, y).asWrappedType, STuple(tpe, tpe))) - roundTripTest(Constant[SType](TupleColl(x, y, x).asWrappedType, STuple(tpe, tpe, tpe))) - roundTripTest(Constant[SType](TupleColl(x, y, x, y).asWrappedType, STuple(tpe, tpe, tpe, tpe))) - roundTripTest(Constant[STuple](Colls.fromItems[Any](x, y, (x, y)), STuple(tpe, tpe, STuple(tpe, tpe)))) - roundTripTest(Constant[STuple](Colls.fromItems[Any](x, y, TupleColl(x, y, x)), STuple(tpe, tpe, STuple(tpe, tpe, tpe)))) - roundTripTest(Constant[STuple](Colls.fromItems[Any](x, y, TupleColl(x, y, (x, y))), STuple(tpe, tpe, STuple(tpe, tpe, STuple(tpe, tpe))))) + roundTripTest(Constant[SType]((x, y).asWrappedType, STuple(tpe, tpe)), withVersion) + roundTripTest(Constant[SType](TupleColl(x, y, x).asWrappedType, STuple(tpe, tpe, tpe)), withVersion) + roundTripTest(Constant[SType](TupleColl(x, y, x, y).asWrappedType, STuple(tpe, tpe, tpe, tpe)), withVersion) + roundTripTest(Constant[STuple](Colls.fromItems[Any](x, y, (x, y)), STuple(tpe, tpe, STuple(tpe, tpe))), withVersion) + roundTripTest(Constant[STuple](Colls.fromItems[Any](x, y, TupleColl(x, y, x)), STuple(tpe, tpe, STuple(tpe, tpe, tpe))), withVersion) + roundTripTest(Constant[STuple](Colls.fromItems[Any](x, y, TupleColl(x, y, (x, y))), STuple(tpe, tpe, STuple(tpe, tpe, STuple(tpe, tpe)))), withVersion) } } property("Constant serialization round trip") { - forAll { x: Unit => roundTripTest(UnitConstant()) } + forAll { _: Unit => roundTripTest(UnitConstant()) } forAll { x: Byte => roundTripTest(Constant[SByte.type](x, SByte)) } forAll { x: Boolean => roundTripTest(BooleanConstant.fromBoolean(x)) } forAll { x: Long => roundTripTest(Constant[SLong.type](x, SLong)) } @@ -71,6 +84,7 @@ class ConstantSerializerSpecification extends TableSerializationSpecification { forAll { x: SigmaBoolean => roundTripTest(Constant[SSigmaProp.type](x.toSigmaProp, SSigmaProp)) } forAll { x: ErgoBox => roundTripTest(Constant[SBox.type](x, SBox)) } forAll { x: AvlTree => roundTripTest(Constant[SAvlTree.type](x, SAvlTree)) } + forAll { x: Header => roundTripTest(Constant[SHeader.type](x, SHeader), Some(VersionContext.V6SoftForkVersion)) } forAll { x: Array[Byte] => roundTripTest(Constant[SByteArray](x.toColl, SByteArray)) } forAll { t: SPredefType => testCollection(t) } forAll { t: SPredefType => testTuples(t) } @@ -88,6 +102,7 @@ class ConstantSerializerSpecification extends TableSerializationSpecification { testCollection(SUnit) testCollection(SBox) testCollection(SAvlTree) + testCollection(SHeader) } private def caseObjectValue(v: SValue) = (v, Array[Byte](v.opCode)) diff --git a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala index ecb2d2ef70..9201214e4f 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala @@ -3,11 +3,10 @@ package sigma.serialization import java.math.BigInteger import org.ergoplatform.ErgoBox import org.scalacheck.Arbitrary._ -import sigma.data.{DataValueComparer, RType, SigmaBoolean, TupleColl} +import sigma.data.{CBigInt, CHeader, DataValueComparer, OptionType, RType, SigmaBoolean, TupleColl} import sigma.ast.SCollection.SByteArray -import sigmastate._ import sigmastate.eval._ -import sigma.{AvlTree, Colls, Evaluation} +import sigma.{AvlTree, Colls, Evaluation, Header, VersionContext} import sigma.ast.SType.AnyOps import sigma.ast._ import org.scalacheck.Gen @@ -15,7 +14,7 @@ import sigma.Extensions.ArrayOps import sigma.crypto.EcPointType import sigma.eval.SigmaDsl import sigma.util.Extensions.{BigIntegerOps, EcpOps, SigmaBooleanOps} -import sigmastate.interpreter.{CostAccumulator, CErgoTreeEvaluator} +import sigmastate.interpreter.{CErgoTreeEvaluator, CostAccumulator} import sigmastate.interpreter.CErgoTreeEvaluator.DefaultProfiler import sigmastate.utils.Helpers @@ -24,29 +23,41 @@ import scala.reflect.ClassTag class DataSerializerSpecification extends SerializationSpecification { - def roundtrip[T <: SType](obj: T#WrappedType, tpe: T) = { - val w = SigmaSerializer.startWriter() - DataSerializer.serialize(obj, tpe, w) - val bytes = w.toBytes - val r = SigmaSerializer.startReader(bytes) - val res = DataSerializer.deserialize(tpe, r) - res shouldBe obj - - val es = CErgoTreeEvaluator.DefaultEvalSettings - val accumulator = new CostAccumulator( - initialCost = JitCost(0), - costLimit = Some(JitCost.fromBlockCost(es.scriptCostLimitInEvaluator))) - val evaluator = new CErgoTreeEvaluator( - context = null, - constants = ErgoTree.EmptyConstants, - coster = accumulator, DefaultProfiler, es) - val ok = DataValueComparer.equalDataValues(res, obj)(evaluator) - ok shouldBe true - - val randomPrefix = arrayGen[Byte].sample.get - val r2 = SigmaSerializer.startReader(randomPrefix ++ bytes, randomPrefix.length) - val res2 = DataSerializer.deserialize(tpe, r2) - res2 shouldBe obj + def roundtrip[T <: SType](obj: T#WrappedType, tpe: T, withVersion: Option[Byte] = None) = { + + def test() = { + val w = SigmaSerializer.startWriter() + DataSerializer.serialize(obj, tpe, w) + val bytes = w.toBytes + val r = SigmaSerializer.startReader(bytes) + val res = DataSerializer.deserialize(tpe, r) + res shouldBe obj + + val es = CErgoTreeEvaluator.DefaultEvalSettings + val accumulator = new CostAccumulator( + initialCost = JitCost(0), + costLimit = Some(JitCost.fromBlockCost(es.scriptCostLimitInEvaluator))) + val evaluator = new CErgoTreeEvaluator( + context = null, + constants = ErgoTree.EmptyConstants, + coster = accumulator, DefaultProfiler, es) + val ok = DataValueComparer.equalDataValues(res, obj)(evaluator) + ok shouldBe true + + val randomPrefix = arrayGen[Byte].sample.get + val r2 = SigmaSerializer.startReader(randomPrefix ++ bytes, randomPrefix.length) + val res2 = DataSerializer.deserialize(tpe, r2) + res2 shouldBe obj + } + + withVersion match { + case Some(ver) => + VersionContext.withVersions(ver, 1) { + test() + } + case None => + test() + } } def testCollection[T <: SType](tpe: T) = { @@ -54,25 +65,33 @@ class DataSerializerSpecification extends SerializationSpecification { implicit val tT = Evaluation.stypeToRType(tpe) implicit val tagT = tT.classTag implicit val tAny = sigma.AnyType + + 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)) - roundtrip[SType](xs.toColl.map(x => (x, x)).asWrappedType, SCollection(STuple(tpe, tpe))) + roundtrip[SCollection[T]](xs.toColl, SCollection(tpe), withVersion) + roundtrip[SType](xs.toColl.map(x => (x, x)).asWrappedType, SCollection(STuple(tpe, tpe)), withVersion) val triples = xs.toColl.map(x => TupleColl(x, x, x)).asWrappedType - roundtrip(triples, SCollection(STuple(tpe, tpe, tpe))) + roundtrip(triples, SCollection(STuple(tpe, tpe, tpe)), withVersion) val quartets = xs.toColl.map(x => TupleColl(x, x, x, x)).asWrappedType - roundtrip(quartets, SCollection(STuple(tpe, tpe, tpe, tpe))) + roundtrip(quartets, SCollection(STuple(tpe, tpe, tpe, tpe)), withVersion) val nested = xs.toColl.map(x => Colls.fromItems[T#WrappedType](x, x)) - roundtrip[SCollection[SCollection[T]]](nested, SCollection(SCollection(tpe))) + roundtrip[SCollection[SCollection[T]]](nested, SCollection(SCollection(tpe)), withVersion) roundtrip[SType]( xs.toColl.map { x => val arr = Colls.fromItems[T#WrappedType](x, x) (arr, arr) }.asWrappedType, - SCollection(STuple(SCollection(tpe), SCollection(tpe))) + SCollection(STuple(SCollection(tpe), SCollection(tpe))), + withVersion ) } } @@ -82,14 +101,45 @@ class DataSerializerSpecification extends SerializationSpecification { val tT = Evaluation.stypeToRType(tpe) @nowarn implicit val tag: ClassTag[T#WrappedType] = tT.classTag implicit val tAny : RType[Any] = sigma.AnyType + val withVersion = if (tpe == SHeader) { + Some(VersionContext.V6SoftForkVersion) + } else { + None + } forAll { in: (T#WrappedType, T#WrappedType) => val (x,y) = (in._1, in._2) - roundtrip[SType]((x, y).asWrappedType, STuple(tpe, tpe)) - roundtrip[SType](TupleColl(x, y, x).asWrappedType, STuple(tpe, tpe, tpe)) - roundtrip[SType](TupleColl(x, y, x, y).asWrappedType, STuple(tpe, tpe, tpe, tpe)) - roundtrip[STuple](Colls.fromItems[Any](x, y, (x, y)), STuple(tpe, tpe, STuple(tpe, tpe))) - roundtrip[STuple](Colls.fromItems[Any](x, y, TupleColl(x, y, x)), STuple(tpe, tpe, STuple(tpe, tpe, tpe))) - roundtrip[STuple](Colls.fromItems[Any](x, y, TupleColl(x, y, (x, y))), STuple(tpe, tpe, STuple(tpe, tpe, STuple(tpe, tpe)))) + roundtrip[SType]((x, y).asWrappedType, STuple(tpe, tpe), withVersion) + roundtrip[SType](TupleColl(x, y, x).asWrappedType, STuple(tpe, tpe, tpe), withVersion) + roundtrip[SType](TupleColl(x, y, x, y).asWrappedType, STuple(tpe, tpe, tpe, tpe), withVersion) + roundtrip[STuple](Colls.fromItems[Any](x, y, (x, y)), STuple(tpe, tpe, STuple(tpe, tpe)), withVersion) + roundtrip[STuple](Colls.fromItems[Any](x, y, TupleColl(x, y, x)), STuple(tpe, tpe, STuple(tpe, tpe, tpe)), withVersion) + roundtrip[STuple](Colls.fromItems[Any](x, y, TupleColl(x, y, (x, y))), STuple(tpe, tpe, STuple(tpe, tpe, STuple(tpe, tpe))), withVersion) + } + } + + def testOption[T <: SType](tpe: T) = { + implicit val wWrapped: Gen[T#WrappedType] = wrappedTypeGen(tpe) + val tT = Evaluation.stypeToRType(tpe) + + an[Exception] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + forAll { in: T#WrappedType => + roundtrip[SType](Some(in).asWrappedType, SOption(tpe)) + roundtrip[SOption[SCollection[T]]](Some(Colls.fromItems(in)(tT)), SOption(SCollectionType(tpe))) + } + }) + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + forAll { in: T#WrappedType => + roundtrip[SType](Some(in).asWrappedType, SOption(tpe)) + roundtrip[SOption[T]](None, SOption(tpe)) + roundtrip[SOption[T]](Some(in), SOption(tpe)) + roundtrip[SOption[SCollection[T]]](Some(Colls.fromItems(in)(tT)), SOption(SCollectionType(tpe))) + roundtrip[SCollection[SOption[T]]](Colls.fromItems(Option(in), None.asInstanceOf[Option[T#WrappedType]])(OptionType(tT)), SCollectionType(SOption(tpe))) + roundtrip[SOption[SOption[T]]](None, SOption(SOption(tpe))) + roundtrip[SOption[SOption[T]]](Some(Some(in)), SOption(SOption(tpe))) + roundtrip[SOption[SOption[T]]](Some(None), SOption(SOption(tpe))) + } } } @@ -99,13 +149,16 @@ 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) } forAll { x: AvlTree => roundtrip[SAvlTree.type](x, SAvlTree) } forAll { x: Array[Byte] => roundtrip[SByteArray](x.toColl, SByteArray) } + forAll { x: Header => roundtrip[SHeader.type](x, SHeader, Some(VersionContext.V6SoftForkVersion)) } forAll { t: SPredefType => testCollection(t) } forAll { t: SPredefType => testTuples(t) } + forAll { t: SPredefType => testOption(t) } } property("Should check limits and fail") { @@ -133,6 +186,42 @@ class DataSerializerSpecification extends SerializationSpecification { t.isInstanceOf[SerializerException] && t.getMessage.contains(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes") }) + } + + property("nuanced versioned test for header roundtrip") { + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + forAll { x: Header => roundtrip[SHeader.type](x, SHeader) } + } + + an[SerializerException] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + val h = headerGen.sample.get + roundtrip[SHeader.type](h, SHeader) + }) + } + property("header vector") { + val header = CHeader( + 0.toByte, + Helpers.decodeBytes("7a7fe5347f09017818010062000001807f86808000ff7f66ffb07f7ad27f3362"), + Helpers.decodeBytes("c1d70ad9b1ffc1fb9a715fff19807f2401017fcd8b73db017f1cff77727fff08"), + Helpers.decodeBytes("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17"), + Helpers.decodeBytes("5e7f1164ccd0990080c501fc0e0181cb387fc17f00ff00c7d5ff767f91ff5e68"), + -7421721754642387858L, + -4826493284887861030L, + 10, + Helpers.decodeBytes("e580c88001ff6fc89c5501017f80e001ff0101fe48c153ff7f00666b80d780ab"), + Helpers.decodeGroupElement("03e7f2875298fddd933c2e0a38968fe85bdeeb70dd8b389559a1d36e2ff1b58fc5"), + Helpers.decodeGroupElement("034e2d3b5f9e409e3ae8a2e768340760362ca33764eda5855f7a43487f14883300"), + Helpers.decodeBytes("974651c9efff7f00"), + CBigInt(new BigInteger("478e827dfa1e4b57", 16)), + Helpers.decodeBytes("01ff13"), + Colls.emptyColl + ) + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + roundtrip[SHeader.type](header, SHeader) + } } + } diff --git a/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala index ac9c997d98..4bfe3c6a25 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala @@ -1,6 +1,11 @@ package sigma.serialization +import scorex.utils.Ints +import sigma.VersionContext +import sigma.ast.SCollection.SByteArray +import sigma.ast.SType.tT import sigma.ast._ +import sigma.validation.ValidationException class MethodCallSerializerSpecification extends SerializationSpecification { @@ -21,4 +26,142 @@ class MethodCallSerializerSpecification extends SerializationSpecification { ) roundTripTest(expr) } + + property("MethodCall deserialization round trip for Header.checkPow") { + def code = { + val bi = HeaderConstant(headerGen.sample.get) + val expr = MethodCall(bi, + SHeaderMethods.checkPowMethod, + Vector(), + Map() + ) + roundTripTest(expr) + } + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + code + } + + a[SerializerException] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + code + } + ) + } + + property("MethodCall deserialization round trip for Global.powHit") { + val k = IntConstant(32) + val msg = ByteArrayConstant(Array.fill(5)(1.toByte)) + val nonce = ByteArrayConstant(Array.fill(8)(2.toByte)) + val h = ByteArrayConstant(Ints.toByteArray(5)) + val N = IntConstant(1024 * 1024) + + def code = { + val expr = MethodCall(Global, + SGlobalMethods.powHitMethod, + Vector(k, msg, nonce, h, N), + Map() + ) + roundTripTest(expr) + } + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + code + } + + an[ValidationException] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + code + } + ) + } + + 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(tT -> SByteArray)), + Vector(b), + Map() + ) + roundTripTest(expr) + } + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + code + } + + an[ValidationException] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + code + } + ) + } + + property("MethodCall deserialization round trip for Global.deserializeTo[]") { + def code = { + val h = HeaderConstant(headerGen.sample.get) + val expr = MethodCall(h, + SGlobalMethods.deserializeToMethod.withConcreteTypes(Map(tT -> SHeader)), + Array(ByteArrayConstant(Array(1.toByte, 2.toByte, 3.toByte))), // wrong header bytes but ok for test + Map(tT -> SHeader) + ) + roundTripTest(expr) + } + + println(SGlobalMethods.deserializeToMethod.hasExplicitTypeArgs) + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + code + } + + an[Exception] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + code + }) + } + + property("MethodCall deserialization round trip for Global.encodeNBits") { + def code = { + val bi = BigIntConstant(5) + val expr = MethodCall(Global, + SGlobalMethods.encodeNBitsMethod, + Vector(bi), + Map() + ) + roundTripTest(expr) + } + + // should be ok + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + code + } + + an[ValidationException] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + code + }) + } + + property("MethodCall deserialization round trip for Global.decodeNBits") { + def code = { + val l = LongConstant(5) + val expr = MethodCall(Global, + SGlobalMethods.decodeNBitsMethod, + Vector(l), + Map() + ) + roundTripTest(expr) + } + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + code + } + + an[ValidationException] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + code + } + ) + } } diff --git a/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala index fb3bbcd326..1c67806dbc 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala @@ -4,8 +4,9 @@ import org.scalacheck.Gen import sigma.ast.syntax.CollectionOps import sigma.ast.{FalseLeaf, IntConstant, SelectField, Tuple} import sigma.serialization.OpCodes.{SelectFieldCode, TupleCode} +import sigmastate.CrossVersionProps -class SelectFieldSerializerSpecification extends TableSerializationSpecification { +class SelectFieldSerializerSpecification extends TableSerializationSpecification with CrossVersionProps { property("SelectField: Serializer round trip ") { forAll(tupleGen(2, 10)) { tuple: Tuple => diff --git a/interpreter/shared/src/test/scala/sigma/serialization/SerializationSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/SerializationSpecification.scala index 30ae6af19b..aa7a8722ba 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/SerializationSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/SerializationSpecification.scala @@ -8,6 +8,7 @@ import org.scalacheck.Arbitrary._ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.{ScalaCheckDrivenPropertyChecks, ScalaCheckPropertyChecks} +import sigma.VersionContext import sigma.ast.SType import sigma.ast._ import sigmastate.helpers.NegativeTesting @@ -26,10 +27,20 @@ trait SerializationSpecification extends AnyPropSpec with ValidationSpecification with NegativeTesting { - protected def roundTripTest[V <: Value[_ <: SType]](v: V): Assertion = { - val bytes = ValueSerializer.serialize(v) - predefinedBytesTest(v, bytes) - predefinedBytesTestNotFomZeroElement(bytes, v) + protected def roundTripTest[V <: Value[_ <: SType]](v: V, withVersion: Option[Byte] = None): Assertion = { + def test() = { + val bytes = ValueSerializer.serialize(v) + predefinedBytesTest(v, bytes) + predefinedBytesTestNotFomZeroElement(bytes, v) + } + withVersion match { + case Some(ver) => + VersionContext.withVersions(ver, 0) { + test() + } + case None => + test() + } } protected def predefinedBytesTest[V <: Value[_ <: SType]](v: V, bytes: Array[Byte]): Assertion = { @@ -41,7 +52,7 @@ trait SerializationSpecification extends AnyPropSpec r.positionLimit shouldBe positionLimitBefore } - //check that pos and consumed are being implented correctly + //check that pos and consumed are being implemented correctly protected def predefinedBytesTestNotFomZeroElement[V <: Value[_ <: SType]](bytes: Array[Byte], v: V): Assertion = { val randomInt = Gen.chooseNum(1, 20).sample.get val randomBytes = Gen.listOfN(randomInt, arbByte.arbitrary).sample.get.toArray diff --git a/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala index e72890cbb7..04b4cffdc9 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala @@ -1,8 +1,9 @@ package sigma.serialization import sigma.ast.{FalseLeaf, IntConstant, Tuple} +import sigmastate.CrossVersionProps -class TupleSerializerSpecification extends TableSerializationSpecification { +class TupleSerializerSpecification extends TableSerializationSpecification with CrossVersionProps { property("Tuple: Serializer round trip ") { forAll(tupleGen(1, 10)) { tuple: Tuple => diff --git a/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala index 6419faf364..ce28a712fd 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala @@ -3,8 +3,9 @@ package sigma.serialization import org.scalacheck.Arbitrary._ import org.scalatest.Assertion import sigma.ast._ +import sigmastate.CrossVersionProps -class TypeSerializerSpecification extends SerializationSpecification { +class TypeSerializerSpecification extends SerializationSpecification with CrossVersionProps { private def roundtrip[T <: SType](tpe: T, expected: Array[Byte]): Assertion = { val w = SigmaSerializer.startWriter() diff --git a/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala index d4971f88c2..1af51d4eee 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala @@ -8,7 +8,6 @@ import org.scalacheck.Arbitrary._ import org.scalacheck.Gen.{choose, frequency} import org.scalacheck.util.Buildable import org.scalacheck.{Arbitrary, Gen} -import sigma.data._ import scorex.crypto.authds.{ADDigest, ADKey} import scorex.util.encode.{Base58, Base64} import scorex.util.{ModifierId, bytesToId} @@ -27,6 +26,7 @@ import sigma.util.Extensions.EcpOps import sigma.validation.{ChangedRule, DisabledRule, EnabledRule, ReplacedRule, RuleStatus} import sigma.validation.ValidationRules.FirstRuleId import ErgoTree.ZeroHeader +import sigma.data.{AvlTreeData, AvlTreeFlags, CAND, CBox, CHeader, COR, CTHRESHOLD, Digest32Coll, ProveDHTuple, ProveDlog, RType, SigmaBoolean} import sigma.eval.Extensions.{EvalIterableOps, SigmaBooleanOps} import sigma.eval.SigmaDsl import sigma.interpreter.{ContextExtension, ProverResult} @@ -83,6 +83,7 @@ trait ObjectGenerators extends TypeGenerators implicit lazy val arbRegisterIdentifier: Arbitrary[RegisterId] = Arbitrary(registerIdentifierGen) implicit lazy val arbBigInteger: Arbitrary[BigInteger] = Arbitrary(Arbitrary.arbBigInt.arbitrary.map(_.bigInteger)) implicit lazy val arbBigInt: Arbitrary[BigInt] = Arbitrary(arbBigInteger.arbitrary.map(SigmaDsl.BigInt(_))) + implicit lazy val arbUnsignedBigInt: Arbitrary[UnsignedBigInt] = Arbitrary(arbBigInteger.arbitrary.map(_.abs()).map(SigmaDsl.UnsignedBigInt(_))) implicit lazy val arbEcPointType: Arbitrary[dlogGroup.ElemType] = Arbitrary(Gen.const(()).flatMap(_ => CryptoConstants.dlogGroup.createRandomGenerator())) implicit lazy val arbGroupElement: Arbitrary[GroupElement] = Arbitrary(arbEcPointType.arbitrary.map(SigmaDsl.GroupElement(_))) implicit lazy val arbSigmaBoolean: Arbitrary[SigmaBoolean] = Arbitrary(Gen.oneOf(proveDHTGen, proveDHTGen)) @@ -142,6 +143,8 @@ trait ObjectGenerators extends TypeGenerators arbString.arbitrary.map { v => mkConstant[SString.type](v, SString) } lazy val bigIntConstGen: Gen[BigIntConstant] = arbBigInt.arbitrary.map { v => mkConstant[SBigInt.type](v, SBigInt) } + lazy val unsignedBigIntConstGen: Gen[UnsignedBigIntConstant] = + arbUnsignedBigInt.arbitrary.map { v => mkConstant[SUnsignedBigInt.type](v, SUnsignedBigInt) } lazy val byteArrayConstGen: Gen[CollectionConstant[SByte.type]] = for { bytes <- arrayOfRange(1, 100, arbByte.arbitrary) @@ -305,12 +308,14 @@ 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 case SAvlTree => arbAvlTree case SAny => arbAnyVal case SUnit => arbUnit + case SHeader => arbHeader case opt: SOption[a] => Arbitrary(frequency((5, None), (5, for (x <- wrappedTypeGen(opt.elemType)) yield Some(x)))) }).asInstanceOf[Arbitrary[T#WrappedType]].arbitrary @@ -324,6 +329,11 @@ trait ObjectGenerators extends TypeGenerators longConstGen, booleanConstGen, bigIntConstGen, + if(VersionContext.current.isV6SoftForkActivated) { + unsignedBigIntConstGen + } else { + bigIntConstGen + }, groupElementConstGen, getVar[SInt.type], getVar[SLong.type], @@ -694,7 +704,6 @@ trait ObjectGenerators extends TypeGenerators } yield ErgoTree.withSegregation(ZeroHeader, prop) def headerGen(stateRoot: AvlTree, parentId: Coll[Byte]): Gen[Header] = for { - id <- modifierIdBytesGen version <- arbByte.arbitrary adProofsRoot <- digest32Gen transactionRoot <- digest32Gen @@ -707,8 +716,10 @@ trait ObjectGenerators extends TypeGenerators powNonce <- nonceBytesGen powDistance <- arbBigInt.arbitrary votes <- minerVotesGen - } yield CHeader(id, version, parentId, adProofsRoot, stateRoot, transactionRoot, timestamp, nBits, - height, extensionRoot, minerPk.toGroupElement, powOnetimePk.toGroupElement, powNonce, powDistance, votes) + unparsedBytes <- collOfRange(0, 32, arbByte.arbitrary) + } yield CHeader(version, parentId, adProofsRoot, stateRoot.digest, transactionRoot, timestamp, nBits, + height, extensionRoot, minerPk.toGroupElement, powOnetimePk.toGroupElement, powNonce, powDistance, votes, + if(version > HeaderVersion.Interpreter60Version){ unparsedBytes } else {Colls.emptyColl[Byte]}) lazy val headerGen: Gen[Header] = for { stateRoot <- avlTreeGen 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 81073c4849..c27053ebf2 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala @@ -2,6 +2,7 @@ package sigma.serialization.generators import org.scalacheck.{Arbitrary, Gen} import org.scalacheck.Arbitrary.arbString +import sigma.VersionContext import sigma.ast._ trait TypeGenerators { @@ -11,17 +12,24 @@ 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) implicit val avlTreeTypeGen: Gen[SAvlTree.type] = Gen.const(SAvlTree) implicit val optionSigmaPropTypeGen: Gen[SOption[SSigmaProp.type]] = Gen.const(SOption(SSigmaProp)) + 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) + 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]]) @@ -33,7 +41,12 @@ trait TypeGenerators { shortTypeGen, intTypeGen, longTypeGen, - bigIntTypeGen + bigIntTypeGen, + if(VersionContext.current.isV6SoftForkActivated) { + unsignedBigIntTypeGen + } else { + bigIntTypeGen + } )) } yield STuple(values.toIndexedSeq) diff --git a/interpreter/shared/src/test/scala/sigmastate/crypto/BigIntSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/crypto/BigIntSpecification.scala new file mode 100644 index 0000000000..2662ff0a7a --- /dev/null +++ b/interpreter/shared/src/test/scala/sigmastate/crypto/BigIntSpecification.scala @@ -0,0 +1,9 @@ +package sigmastate.crypto + +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigmastate.TestsBase + +class BigIntSpecification extends AnyPropSpec with ScalaCheckPropertyChecks with TestsBase { + +} diff --git a/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala b/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala index ba996b0246..52cc6af66c 100644 --- a/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala +++ b/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala @@ -2,17 +2,25 @@ package sigmastate.eval import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import sigma.ast.{BigIntConstant, ErgoTree, Global, JitCost, MethodCall, SGlobalMethods} +import scorex.util.encode.Base16 +import sigma.Extensions.ArrayOps +import sigma.ast.{ByteArrayConstant, IntConstant} import sigma.crypto.SecP256K1Group -import sigma.data.{CSigmaDslBuilder, TrivialProp} +import sigma.data.{CBigInt, TrivialProp} +import sigma.eval.SigmaDsl import sigma.util.Extensions.SigmaBooleanOps +import sigma.util.NBitsUtils import java.math.BigInteger -import sigma.{ContractsTestkit, SigmaDslBuilder, SigmaProp} +import sigma.{ContractsTestkit, SigmaProp} +import sigmastate.interpreter.{CErgoTreeEvaluator, CostAccumulator} +import sigmastate.interpreter.CErgoTreeEvaluator.DefaultProfiler +import sigma.{Box, VersionContext} import scala.language.implicitConversions class BasicOpsTests extends AnyFunSuite with ContractsTestkit with Matchers { - override val SigmaDsl: SigmaDslBuilder = CSigmaDslBuilder implicit def boolToSigma(b: Boolean): SigmaProp = TrivialProp(b).toSigmaProp @@ -60,8 +68,93 @@ class BasicOpsTests extends AnyFunSuite with ContractsTestkit with Matchers { } test("box.creationInfo._1 is Int") { - val box = newAliceBox(1, 100) + val box = newAliceBox(100) box.creationInfo._1 shouldBe a [Integer] } + test("xor evaluation") { + val es = CErgoTreeEvaluator.DefaultEvalSettings + val accumulator = new CostAccumulator( + initialCost = JitCost(0), + costLimit = Some(JitCost.fromBlockCost(es.scriptCostLimitInEvaluator))) + + val context = new CContext( + noInputs.toColl, noHeaders, dummyPreHeader, + Array[Box]().toColl, Array[Box]().toColl, 0, null, 0, null, + dummyPubkey.toColl, Colls.emptyColl, null, VersionContext.V6SoftForkVersion, VersionContext.V6SoftForkVersion) + + val evaluator = new CErgoTreeEvaluator( + context = context, + constants = ErgoTree.EmptyConstants, + coster = accumulator, DefaultProfiler, es) + + val msg = Colls.fromArray(Base16.decode("0a101b8c6a4f2e").get) + VersionContext.withVersions(VersionContext.V6SoftForkVersion, VersionContext.V6SoftForkVersion) { + val res = MethodCall(Global, SGlobalMethods.xorMethod, + IndexedSeq(ByteArrayConstant(msg), ByteArrayConstant(msg)), Map.empty) + .evalTo[sigma.Coll[Byte]](Map.empty)(evaluator) + + res should be(Colls.fromArray(Base16.decode("00000000000000").get)) + } + } + + /** + * Checks BigInt.nbits evaluation for SigmaDSL as well as AST interpreter (MethodCall) layers + */ + test("powHit evaluation") { + val k = 32 + val msg = Colls.fromArray(Base16.decode("0a101b8c6a4f2e").get) + val nonce = Colls.fromArray(Base16.decode("000000000000002c").get) + val hbs = Colls.fromArray(Base16.decode("00000000").get) + val N = 1024 * 1024 + + SigmaDsl.powHit(k, msg, nonce, hbs, N) shouldBe CBigInt(new BigInteger("326674862673836209462483453386286740270338859283019276168539876024851191344")) + + val es = CErgoTreeEvaluator.DefaultEvalSettings + val accumulator = new CostAccumulator( + initialCost = JitCost(0), + costLimit = Some(JitCost.fromBlockCost(es.scriptCostLimitInEvaluator))) + + val context = new CContext( + noInputs.toColl, noHeaders, dummyPreHeader, + Array[Box]().toColl, Array[Box]().toColl, 0, null, 0, null, + dummyPubkey.toColl, Colls.emptyColl, null, VersionContext.V6SoftForkVersion, VersionContext.V6SoftForkVersion) + + val evaluator = new CErgoTreeEvaluator( + context = context, + constants = ErgoTree.EmptyConstants, + coster = accumulator, DefaultProfiler, es) + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, VersionContext.V6SoftForkVersion) { + val res = MethodCall(Global, SGlobalMethods.powHitMethod, + IndexedSeq(IntConstant(k), ByteArrayConstant(msg), ByteArrayConstant(nonce), + ByteArrayConstant(hbs), IntConstant(N)), Map.empty) + .evalTo[sigma.BigInt](Map.empty)(evaluator) + + res should be(CBigInt(new BigInteger("326674862673836209462483453386286740270338859283019276168539876024851191344"))) + } + } + + /** + * Checks BigInt.nbits evaluation for SigmaDSL as well as AST interpreter (MethodCall) layers + */ + test("nbits evaluation") { + SigmaDsl.encodeNbits(CBigInt(BigInteger.valueOf(0))) should be + (NBitsUtils.encodeCompactBits(0)) + + val es = CErgoTreeEvaluator.DefaultEvalSettings + val accumulator = new CostAccumulator( + initialCost = JitCost(0), + costLimit = Some(JitCost.fromBlockCost(es.scriptCostLimitInEvaluator))) + val evaluator = new CErgoTreeEvaluator( + context = null, + constants = ErgoTree.EmptyConstants, + coster = accumulator, DefaultProfiler, es) + + val res = MethodCall(Global, SGlobalMethods.encodeNBitsMethod, IndexedSeq(BigIntConstant(BigInteger.valueOf(0))), Map.empty) + .evalTo[Long](Map.empty)(evaluator) + + res should be (NBitsUtils.encodeCompactBits(0)) + } + } diff --git a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala index 720223ee3a..ba04df1347 100644 --- a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala +++ b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala @@ -8,43 +8,18 @@ import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ import sigma.data._ -import scala.annotation.nowarn // imports implicit ClassTag - trait ContractsTestkit { - val R0 = 0.toByte; - val R1 = 1.toByte; - val R2 = 2.toByte; - val R3 = 3.toByte; - val R4 = 4.toByte; - val R5 = 5.toByte; - val R6 = 6.toByte; - val R7 = 7.toByte; - val R8 = 8.toByte; - val R9 = 9.toByte; + val Colls = new CollOverArrayBuilder - val SigmaDsl: SigmaDslBuilder = CSigmaDslBuilder - val noRegisters = collection[AnyValue]() - val noBytes = collection[Byte]() val noInputs = Array[Box]() val noOutputs = Array[Box]() val dummyPubkey: Array[Byte] = Array.fill(32)(0: Byte) - val dummyADDigest: Coll[Byte] = Colls.fromArray(Array.fill(33)(0: Byte)) val emptyAvlTree = new CAvlTree(AvlTreeData.dummy) val noHeaders = CSigmaDslBuilder.Colls.emptyColl[Header] val dummyPreHeader: PreHeader = null /** Create collection from array of items */ - def collection[T: RType](items: T*) = Colls.fromArray(items.toArray) - - /** Converts a map of registers to collection of registers. */ - def regs(m: Map[Byte, AnyValue]): Coll[AnyValue] = { - val res = new Array[AnyValue](10) - for ( (id, v) <- m ) { - assert(res(id) == null, s"register $id is defined more then once") - res(id) = v - } - Colls.fromArray(res) - } + def collection[T: RType](items: T*): Coll[T] = Colls.fromArray(items.toArray) /** Converts a map of context vars to collection of context vars. */ def contextVars(m: Map[Byte, AnyValue]): Coll[AnyValue] = { @@ -58,9 +33,7 @@ trait ContractsTestkit { Colls.fromArray(res) } - val AliceId = Array[Byte](1) // 0x0001 - - def newAliceBox(@nowarn id: Byte, value: Long): Box = { + def newAliceBox(value: Long): Box = { val ergoBox = testBox(value, ErgoTree.fromProposition(TrueSigmaProp), creationHeight = 0, additionalTokens = Seq(), additionalRegisters = Map()) @@ -74,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, @@ -90,8 +63,6 @@ trait ContractsTestkit { implicit class TestContextOps(ctx: CContext) { def withInputs(inputs: Box*) = ctx.copy(inputs = inputs.toArray.toColl) - def withOutputs(outputs: Box*) = ctx.copy(outputs = outputs.toArray.toColl) - def withVariables(vars: Map[Int, AnyValue]) = ctx.copy(vars = contextVars(vars.map { case (k, v) => (k.toByte, v) })) } diff --git a/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala b/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala index d33f09dd80..77d87a31f8 100644 --- a/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala +++ b/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala @@ -1,6 +1,6 @@ package sigma -import org.ergoplatform.ErgoBox +import org.ergoplatform.{ErgoBox, ErgoHeader} import org.ergoplatform.settings.ErgoAlgos import org.scalacheck.Arbitrary.arbitrary import org.scalacheck.Gen.containerOfN @@ -12,7 +12,7 @@ import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId import sigma.ast._ import sigma.Extensions.ArrayOps -import sigmastate.eval.{CHeader, CPreHeader} +import sigmastate.eval.CPreHeader import sigmastate.helpers.TestingCommons import sigma.serialization.ErgoTreeSerializer import sigma.serialization.generators.ObjectGenerators @@ -30,13 +30,6 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators { def Coll[T](items: T*)(implicit cT: RType[T]): Coll[T] = CSigmaDslBuilder.Colls.fromItems(items: _*) - /** Generator of random collection with `n` elements. */ - def collOfN[T: RType : Arbitrary](n: Int) - (implicit b: Buildable[T, Array[T]]): Gen[Coll[T]] = { - implicit val g: Gen[T] = Arbitrary.arbitrary[T] - containerOfN[Array, T](n, g).map(Colls.fromArray(_)) - } - val bytesGen: Gen[Array[Byte]] = for { len <- Gen.choose(0, 100) arr <- containerOfN[Array, Byte](len, Arbitrary.arbByte.arbitrary) @@ -54,63 +47,9 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators { res } - protected def sampleAvlProver = { - val keys = arrayOfN(100, keyCollGen).sample.get - val values = arrayOfN(100, bytesCollGen).sample.get - val (tree, prover) = createAvlTreeAndProver(keys.zip(values): _*) - (keys, values, tree, prover) - } - - protected def sampleAvlTree: AvlTree = { - val (_, _, _, avlProver) = sampleAvlProver - val digest = avlProver.digest.toColl - val tree = SigmaDsl.avlTree(AvlTreeFlags.ReadOnly.serializeToByte, digest, 32, None) - tree - } - val tokenId1: Digest32 = Blake2b256("id1") val tokenId2: Digest32 = Blake2b256("id2") - val header1: Header = CHeader(Blake2b256("Header.id").toColl, - 0, - Blake2b256("Header.parentId").toColl, - Blake2b256("ADProofsRoot").toColl, - sampleAvlTree, - Blake2b256("transactionsRoot").toColl, - timestamp = 0, - nBits = 0, - height = 0, - extensionRoot = Blake2b256("transactionsRoot").toColl, - minerPk = SigmaDsl.groupGenerator, - powOnetimePk = SigmaDsl.groupGenerator, - powNonce = Colls.fromArray(Array[Byte](0, 1, 2, 3, 4, 5, 6, 7)), - powDistance = SigmaDsl.BigInt(BigInt("1405498250268750867257727119510201256371618473728619086008183115260323").bigInteger), - votes = Colls.fromArray(Array[Byte](0, 1, 2)) - ) - val header2: Header = CHeader(Blake2b256("Header2.id").toColl, - 0, - header1.id, - Blake2b256("ADProofsRoot2").toColl, - sampleAvlTree, - Blake2b256("transactionsRoot2").toColl, - timestamp = 2, - nBits = 0, - height = 1, - extensionRoot = Blake2b256("transactionsRoot2").toColl, - minerPk = SigmaDsl.groupGenerator, - powOnetimePk = SigmaDsl.groupGenerator, - powNonce = Colls.fromArray(Array.fill(0.toByte)(8)), - powDistance = SigmaDsl.BigInt(BigInt("19306206489815517413186395405558417825367537880571815686937307203793939").bigInteger), - votes = Colls.fromArray(Array[Byte](0, 1, 0)) - ) - val headers = Colls.fromItems(header2, header1) - val preHeader: PreHeader = CPreHeader(0, - header2.id, - timestamp = 3, - nBits = 0, - height = 2, - minerPk = SigmaDsl.groupGenerator, - votes = Colls.emptyColl[Byte] - ) + object TestData { val BigIntZero: BigInt = CBigInt(new BigInteger("0", 16)) @@ -301,17 +240,16 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators { def createAvlTreeData() = AvlTreeData( ErgoAlgos.decodeUnsafe("010180017f7f7b7f720c00007f7f7f0f01e857a626f37f1483d06af8077a008080").toColl, - AvlTreeFlags(false, true, false), - 728138553, - Some(2147483647) + AvlTreeFlags(true, true, true), + 32, + None ) val h1_instances = new CloneSet(1000, CHeader( - Helpers.decodeBytes("957f008001808080ffe4ffffc8f3802401df40006aa05e017fa8d3f6004c804a"), 0.toByte, Helpers.decodeBytes("0180dd805b0000ff5400b997fd7f0b9b00de00fb03c47e37806a8186b94f07ff"), Helpers.decodeBytes("01f07f60d100ffb970c3007f60ff7f24d4070bb8fffa7fca7f34c10001ffe39d"), - CAvlTree(createAvlTreeData()), + CAvlTree(createAvlTreeData()).digest, Helpers.decodeBytes("804101ff01000080a3ffbd006ac080098df132a7017f00649311ec0e00000100"), 1L, -1L, @@ -321,14 +259,18 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators { Helpers.decodeGroupElement("0361299207fa392231e23666f6945ae3e867b978e021d8d702872bde454e9abe9c"), Helpers.decodeBytes("7f4f09012a807f01"), CBigInt(new BigInteger("-e24990c47e15ed4d0178c44f1790cc72155d516c43c3e8684e75db3800a288", 16)), - Helpers.decodeBytes("7f0180") + Helpers.decodeBytes("7f0180"), + Colls.emptyColl[Byte] )) def create_h1(): Header = h1_instances.getNext val h1: Header = create_h1() - val h2: Header = create_h1().asInstanceOf[CHeader].copy(height = 2) + val eh1 = h1.asInstanceOf[CHeader].ergoHeader + val h2: Header = new CHeader(new ErgoHeader(eh1.version, eh1.parentId, eh1.ADProofsRoot, eh1.stateRoot, + eh1.transactionsRoot, eh1.timestamp, eh1.nBits, 2, eh1.extensionRoot, + eh1.powSolution, eh1.votes, eh1.unparsedBytes, null)) val dlog_instances = new CloneSet(1000, ProveDlog( SigmaDsl.toECPoint(create_ge1()).asInstanceOf[EcPointType] diff --git a/parsers/shared/src/main/scala/sigmastate/lang/ContractParser.scala b/parsers/shared/src/main/scala/sigmastate/lang/ContractParser.scala index 87dd20ab0c..a192a789b9 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/ContractParser.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/ContractParser.scala @@ -3,7 +3,7 @@ package sigmastate.lang import fastparse._ import fastparse.NoWhitespace._ import SigmaParser._ -import sigma.ast.SType +import sigma.ast.{Constant, SType} import sigma.ast.syntax.SValue import sigmastate.lang.parsers.Basic @@ -187,9 +187,9 @@ object ContractParser { def annotation[_: P] = P("@contract") - def paramDefault[_: P] = P(WL.? ~ `=` ~ WL.? ~ ExprLiteral).map(s => s.asWrappedType) + def paramDefault[_: P] = P(WL.? ~ `=` ~ WL.? ~ ExprLiteral) - def param[_: P] = P(WL.? ~ Id.! ~ ":" ~ Type ~ paramDefault.?).map(s => ContractParam(s._1, s._2, s._3)) + def param[_: P] = P(WL.? ~ Id.! ~ ":" ~ Type ~ paramDefault.?).map(s => ContractParam(s._1, s._2, s._3.map(_.value))) def params[_: P] = P("(" ~ param.rep(1, ",").? ~ ")") } diff --git a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala index 06683f6e96..4402eb949a 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala @@ -34,6 +34,7 @@ trait Types extends Core { "Int" -> SInt, "Long" -> SLong, "BigInt" -> SBigInt, + "UnsignedBigInt" -> SUnsignedBigInt, // added in 6.0, but put in this map "AvlTree" -> SAvlTree, "Context" -> SContext, "GroupElement" -> SGroupElement, diff --git a/sc/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala b/parsers/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala similarity index 98% rename from sc/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala rename to parsers/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala index 1b0f7b112d..11381e1ac3 100644 --- a/sc/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala +++ b/parsers/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala @@ -5,15 +5,13 @@ import org.ergoplatform.ErgoBox.RegisterId import org.ergoplatform.settings.ErgoAlgos import pprint.{PPrinter, Tree} import sigma.ast.SCollection.{SBooleanArray, SByteArray, SByteArray2} -import sigma.ast._ +import sigma.ast.{ConstantNode, FuncValue, MethodCall, ValueCompanion, _} import sigma.crypto.EcPointType import sigma.data.{AvlTreeData, AvlTreeFlags, CollType, PrimitiveType, TrivialProp} import sigma.serialization.GroupElementSerializer import sigma.{Coll, GroupElement} -import sigma.ast.{ConstantNode, FuncValue, ValueCompanion} -import sigmastate._ import sigmastate.crypto.GF2_192_Poly -import sigma.ast.MethodCall + import java.math.BigInteger import scala.collection.compat.immutable.ArraySeq import scala.collection.mutable @@ -54,6 +52,8 @@ object SigmaPPrint extends PPrinter { s"SOption[${typeName(ot.elemType)}]" case _: STuple => "STuple" + case _: SFunc => + s"SFunc" case _ => sys.error(s"Cannot get typeName($tpe)") } diff --git a/parsers/shared/src/test/scala/sigmastate/lang/ContractParserSpec.scala b/parsers/shared/src/test/scala/sigmastate/lang/ContractParserSpec.scala index 9a412b7100..bce252d866 100644 --- a/parsers/shared/src/test/scala/sigmastate/lang/ContractParserSpec.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/ContractParserSpec.scala @@ -3,6 +3,7 @@ package sigmastate.lang import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigma.ast.SType.AnyOps import sigma.ast._ class ContractParserSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers { @@ -34,8 +35,8 @@ class ContractParserSpec extends AnyPropSpec with ScalaCheckPropertyChecks with parsed.name shouldBe "contractName" parsed.params should contain theSameElementsInOrderAs Seq( - ContractParam("p1", SInt, Some(IntConstant(5).asWrappedType)), - ContractParam("p2", SString, Some(StringConstant("default string").asWrappedType)), + ContractParam("p1", SInt, Some(5.asWrappedType)), + ContractParam("p2", SString, Some("default string".asWrappedType)), ContractParam("param3", SLong, None) ) } diff --git a/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala b/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala index 498c3934bf..de83070ac3 100644 --- a/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala @@ -1,16 +1,15 @@ package sigmastate.lang import org.scalatest.matchers.should.Matchers -import sigma.{Coll, _} import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.{SValue, ValueOps} import sigma.ast._ import sigma.crypto.CryptoConstants import sigma.data.{CAnyValue, CSigmaDslBuilder, ProveDHTuple, ProveDlog, SigmaBoolean} import sigma.util.Extensions.BigIntegerOps +import sigma._ import sigmastate.helpers.NegativeTesting import sigmastate.interpreter.Interpreter.ScriptEnv -import sigma.ast.{Ident, MethodCallLike} import java.math.BigInteger @@ -80,4 +79,10 @@ trait LangTests extends Matchers with NegativeTesting { node }))(tree) } + + /** Execute the given `block` having `version` as both activated and ErgoTree version. */ + def runWithVersion[T](version: Byte)(block: => T): T = { + VersionContext.withVersions(version, version)(block) + } + } diff --git a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala index 7213088a05..5a0f2b3465 100644 --- a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala @@ -14,6 +14,7 @@ import SigmaPredef.PredefinedFuncRegistry import sigma.ast.syntax._ import sigmastate.lang.parsers.ParserException import sigma.serialization.OpCodes +import sigmastate.helpers.SigmaPPrint class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with LangTests { import StdSigmaBuilder._ @@ -34,6 +35,17 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat } } + /** Checks parsing result, printing the actual value as a test vector if expected value + * is not equal to actual. + */ + def checkParsed(x: String, expected: SValue) = { + val parsed = parse(x) + if (expected != parsed) { + SigmaPPrint.pprintln(parsed, width = 100) + } + parsed shouldBe expected + } + def parseWithException(x: String): SValue = { SigmaParser(x) match { case Parsed.Success(v, _) => v @@ -615,6 +627,11 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat MethodCallLike(StringConstant("hello"), "+", IndexedSeq(StringConstant("hello"))) } + property("bigInt string decoding") { + parse("""bigInt("32667486267383620946248345338628674027033885928301927616853987602485119134400")""") shouldBe + Apply(BigIntFromStringFunc.symNoType, IndexedSeq(StringConstant("32667486267383620946248345338628674027033885928301927616853987602485119134400"))) + } + property("fromBaseX string decoding") { parse("""fromBase16("1111")""") shouldBe Apply(FromBase16Func.symNoType, IndexedSeq(StringConstant("1111"))) parse("""fromBase58("111")""") shouldBe Apply(FromBase58Func.symNoType, IndexedSeq(StringConstant("111"))) @@ -889,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/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala b/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala index 8fe26a30b0..2fc7556875 100644 --- a/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala +++ b/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala @@ -9,13 +9,13 @@ import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel import sigma.js.Value import sigma.ast.ErgoTree.HeaderType -import sigmastate.eval.CompiletimeIRContext +import sigma.compiler.ir.CompiletimeIRContext import sigma.ast.syntax.ValueOps /** Wrapper exported to JS. */ @JSExportTopLevel("SigmaCompiler") -class SigmaCompiler(_compiler: sigmastate.lang.SigmaCompiler) extends js.Object { +class SigmaCompiler(_compiler: sigma.compiler.SigmaCompiler) extends js.Object { /** Compiles ErgoScript code to ErgoTree. * @@ -59,7 +59,7 @@ object SigmaCompiler extends js.Object { * @return SigmaCompiler instance */ private def create(networkPrefix: Byte): SigmaCompiler = { - val compiler = sigmastate.lang.SigmaCompiler(networkPrefix) + val compiler = sigma.compiler.SigmaCompiler(networkPrefix) new SigmaCompiler(compiler) } } diff --git a/sc/js/src/test/scala/scalan/Platform.scala b/sc/js/src/test/scala/scalan/Platform.scala index 5f938111d2..697453a5d2 100644 --- a/sc/js/src/test/scala/scalan/Platform.scala +++ b/sc/js/src/test/scala/scalan/Platform.scala @@ -1,14 +1,16 @@ package scalan +import sigma.compiler.ir.IRContext + import scala.annotation.unused object Platform { /** In JS tests do nothing. The corresponding JVM method outputs graphs into files. */ - def stage[Ctx <: Scalan](scalan: Ctx)( + def stage[Ctx <: IRContext](ctx: Ctx)( @unused prefix: String, @unused testName: String, @unused name: String, - @unused sfs: Seq[() => scalan.Sym]): Unit = { + @unused sfs: Seq[() => ctx.Sym]): Unit = { } /** On JS it is no-operation. */ diff --git a/sc/jvm/src/test/scala/scalan/Platform.scala b/sc/jvm/src/test/scala/scalan/Platform.scala index 43041a92c6..7c9d48b135 100644 --- a/sc/jvm/src/test/scala/scalan/Platform.scala +++ b/sc/jvm/src/test/scala/scalan/Platform.scala @@ -3,22 +3,23 @@ package scalan import scalan.compilation.GraphVizExport import sigma.util.FileUtil import org.scalatest.Assertions +import sigma.compiler.ir.IRContext object Platform { /** Output graph given by symbols in `sfs` to files. - * @param scalan The Scalan context + * @param ctx The Scalan context * @param prefix The prefix of the directory where the graphs will be stored. * @param testName The name of the test. * @param name The name of the graph. * @param sfs A sequence of functions that return symbols of the graph roots. */ - def stage[Ctx <: Scalan](scalan: Ctx) - (prefix: String, testName: String, name: String, sfs: Seq[() => scalan.Sym]): Unit = { + def stage[Ctx <: IRContext](ctx: Ctx) + (prefix: String, testName: String, name: String, sfs: Seq[() => ctx.Sym]): Unit = { val directory = FileUtil.file(prefix, testName) - val gv = new GraphVizExport(scalan) + val gv = new GraphVizExport(ctx) implicit val graphVizConfig = gv.defaultGraphVizConfig try { - val ss = sfs.map(_.apply()).asInstanceOf[Seq[gv.scalan.Sym]] + val ss = sfs.map(_.apply()).asInstanceOf[Seq[gv.ctx.Sym]] gv.emitDepGraph(ss, directory, name)(graphVizConfig) } catch { case e: Exception => diff --git a/sc/jvm/src/test/scala/scalan/compilation/GraphVizExport.scala b/sc/jvm/src/test/scala/scalan/compilation/GraphVizExport.scala index 8568e6c1db..020e066080 100644 --- a/sc/jvm/src/test/scala/scalan/compilation/GraphVizExport.scala +++ b/sc/jvm/src/test/scala/scalan/compilation/GraphVizExport.scala @@ -2,8 +2,8 @@ package scalan.compilation import java.awt.Desktop import java.io.{File, PrintWriter} -import scalan.Scalan import scalan.core.ScalaNameUtil +import sigma.compiler.ir.IRContext import sigma.util.{FileUtil, ProcessUtil, StringUtil} import scala.annotation.unused @@ -12,8 +12,8 @@ import scala.collection.immutable.StringOps // TODO implement this outside of the cake /** Implementation of Graphviz's dot file generator. */ -class GraphVizExport[Ctx <: Scalan](val scalan: Ctx) { - import scalan._ +class GraphVizExport[Ctx <: IRContext](val ctx: Ctx) { + import ctx._ case class GraphFile(file: File, fileType: String) { def open() = { Desktop.getDesktop.open(file) diff --git a/sc/jvm/src/test/scala/sigmastate/InterpreterReflectionGeneratorTests.scala b/sc/jvm/src/test/scala/sigmastate/InterpreterReflectionGeneratorTests.scala index aaa1d6c424..c10c0123b0 100644 --- a/sc/jvm/src/test/scala/sigmastate/InterpreterReflectionGeneratorTests.scala +++ b/sc/jvm/src/test/scala/sigmastate/InterpreterReflectionGeneratorTests.scala @@ -2,7 +2,7 @@ package sigmastate import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec -import scalan.TypeDescs +import sigma.compiler.ir.{IRContext, TypeDescs} import sigma.reflection.ReflectionData.registerClassEntry import sigma.reflection.SRConstructor @@ -37,7 +37,7 @@ class InterpreterReflectionGeneratorTests extends AnyPropSpec with Matchers { } property("inner class") { - val ctx = null.asInstanceOf[scalan.Library] // ok! type level only + val ctx = null.asInstanceOf[IRContext] // ok! type level only val clazz = classOf[ctx.Coll.CollElem[_, _]] registerClassEntry(clazz, constructors = Array( diff --git a/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala b/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala index 1375b48d99..dd5262e700 100644 --- a/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala +++ b/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala @@ -1,10 +1,21 @@ package sigmastate -import scalan.{Base, TypeDescs} +import sigma.compiler.ir.{Base, TypeDescs} import sigma.reflection._ import scala.annotation.unused import scala.collection.mutable +/** Generates code for registering classes in the ReflectionData. + * It is not used in the runtime. + * + * The generated invocations of `registerClassEntry`, `mkMethod`, `mkConstructor` may + * require manual adjustments. + * + * It uses [[sigma.reflection.Platform.unknownClasses]] to collect classes which were + * accessed during runtime + * + * @see [[ReflectionData]] + */ object ReflectionGenerator { def normalizeName(name: String): String = { @@ -30,17 +41,17 @@ object ReflectionGenerator { } val knownPackages = Array( - "scalan.primitives.", + "sigma.compiler.ir.primitives.", "sigma.", "sigma.", - "special.wrappers.", + "sigma.compiler.ir.wrappers.", "sigma.ast.", "sigmastate.lang.Terms.", "sigmastate.interpreter.", "sigmastate.utxo.", "sigmastate.", - "wrappers.scala.", - "scalan.", + "sigma.compiler.ir.wrappers.scala.", + "sigma.compiler.ir.", "scala.collection.", "scala." ) diff --git a/sc/jvm/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala b/sc/jvm/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala index 54c0f652dc..ffd591b0df 100644 --- a/sc/jvm/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala +++ b/sc/jvm/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala @@ -8,7 +8,6 @@ import sigma.SigmaDslTesting import sigma.ast._ import sigma.data.{AvlTreeData, AvlTreeFlags, CBox, CollType, Digest32Coll} import ErgoTree.HeaderType -import sigmastate.eval._ import sigma.ast.MethodCall import sigma.serialization.OpCodes import sigmastate.utils.Helpers diff --git a/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala b/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala index faded6e28c..7bcf10d593 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala @@ -1,12 +1,13 @@ package org.ergoplatform -import sigmastate.lang.SigmaCompiler -import sigmastate.eval.IRContext import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix +import sigma.Colls import sigma.ast.SType import sigma.ast.syntax.SigmaPropValue import sigma.ast.Value import sigma.ast.syntax.ValueOps +import sigma.compiler.SigmaCompiler +import sigma.compiler.ir.IRContext object ErgoScriptPredef { import sigmastate.interpreter.Interpreter._ @@ -32,7 +33,7 @@ object ErgoScriptPredef { networkPrefix: NetworkPrefix) (implicit IR: IRContext): SigmaPropValue = { val env = emptyEnv + - ("tokenId" -> tokenId, "thresholdAmount" -> thresholdAmount) + ("tokenId" -> Colls.fromArray(tokenId), "thresholdAmount" -> thresholdAmount) val res = compileWithCosting(env, """{ | val sumValues = { (xs: Coll[Long]) => xs.fold(0L, { (acc: Long, amt: Long) => acc + amt }) } diff --git a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSpec.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSpec.scala index 484b9e2728..ffe7239c62 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSpec.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSpec.scala @@ -5,11 +5,11 @@ import sigma.interpreter.{CostedProverResult, ProverResult} import sigma.data.{CSigmaDslBuilder, RType} import org.ergoplatform.{ErgoBox, ErgoLikeContext} import sigma.{Coll, SigmaDslBuilder, SigmaProp} -import sigmastate.eval.IRContext import scala.util.Try import org.ergoplatform.dsl.ContractSyntax.{ErgoScript, Proposition, Token} import sigma.ast.{ErgoTree, EvaluatedValue, SType} +import sigma.compiler.ir.IRContext import scala.language.implicitConversions diff --git a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala index 11cbaff739..34598627db 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala @@ -55,6 +55,7 @@ trait ContractSyntax { contract: SigmaContract => case _: String => StringType case _: Unit => UnitType case _: sigma.BigInt => BigIntRType + case _: sigma.UnsignedBigInt => UnsignedBigIntRType case _: GroupElement => GroupElementRType case _: ErgoBox => syntax.ErgoBoxRType // TODO remove this RType case _: Box => BoxRType diff --git a/sc/shared/src/main/scala/org/ergoplatform/dsl/ErgoContractSpec.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ErgoContractSpec.scala index 2c31304b70..2511bef439 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ErgoContractSpec.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ErgoContractSpec.scala @@ -1,9 +1,9 @@ package org.ergoplatform.dsl import sigma.Coll -import sigmastate.eval.IRContext import org.ergoplatform.dsl.ContractSyntax.{ErgoScript, Proposition, Token} import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, TokenId} +import sigma.compiler.ir.IRContext import sigma.interpreter.CostedProverResult class ErgoContractSpec(implicit val IR: IRContext) extends ContractSpec { diff --git a/sc/shared/src/main/scala/scalan/Scalan.scala b/sc/shared/src/main/scala/scalan/Scalan.scala deleted file mode 100644 index c2e9359261..0000000000 --- a/sc/shared/src/main/scala/scalan/Scalan.scala +++ /dev/null @@ -1,36 +0,0 @@ -package scalan - -import scalan.primitives._ -import scalan.staged.Transforming - -/** Aggregate cake with all inter-dependent modules assembled together. - * Each instance of this class contains independent IR context, thus many - * instances can be created simultaneously. - * However, the inner types declared in the traits are path-dependant. - * This in particular means that ctx1.Ref[_] and ctx2.Ref[_] are different types. - * The typical usage is to create `val ctx = new Scalan` and then import inner - * declarations using `import ctx._`. - * This way the declaration will be directly available as if they were global - * declarations. - * At the same time cake design pattern allow to `override` many methods and values - * in classed derived from `Scalan`, this is significant benefit over - * *everything is global* design. - */ -class Scalan - extends TypeDescs - with MethodCalls - with Tuples - with NumericOps - with UnBinOps - with LogicalOps - with OrderingOps - with Equal - with UniversalOps - with Functions - with IfThenElse - with Transforming - with Thunks - with Entities - with Modules - with DefRewriting - diff --git a/sc/shared/src/main/scala/scalan/meta/SSymName.scala b/sc/shared/src/main/scala/scalan/meta/SSymName.scala deleted file mode 100644 index ea9b1546ba..0000000000 --- a/sc/shared/src/main/scala/scalan/meta/SSymName.scala +++ /dev/null @@ -1,22 +0,0 @@ -package scalan.meta - -import sigma.util.StringUtil.StringUtilExtensions - -case class ImportItem(packageName: String, importedNames: List[String]) - -case class SSymName(packageName: String, name: String) { - import SSymName._ - def this(name: String) = this("", name) - def mkFullName = fullNameString(packageName, name) - def isImportedBy(item: ImportItem): Boolean = { - if (packageName != item.packageName) return false - item.importedNames.contains(SSymName.ImportAllWildcard) || item.importedNames.contains(name) - } -} - -object SSymName { - /** Wildcard character used to signify imporing all names from namespace */ - val ImportAllWildcard = "*" - def fullNameString(packageName: String, name: String): String = - if (packageName.isNullOrEmpty) name else s"$packageName.$name" -} \ No newline at end of file diff --git a/sc/shared/src/main/scala/scalan/primitives/UniversalOps.scala b/sc/shared/src/main/scala/scalan/primitives/UniversalOps.scala deleted file mode 100644 index eaca5821ce..0000000000 --- a/sc/shared/src/main/scala/scalan/primitives/UniversalOps.scala +++ /dev/null @@ -1,92 +0,0 @@ -package scalan.primitives - -import scalan.{Base, Scalan} - -trait UniversalOps extends Base { scalan: Scalan => - case class HashCode[A]() extends UnOp[A, Int]("hashCode") { - override def applySeq(x: A): Int = x.hashCode - } - - case class ToString[A]() extends UnOp[A, String]("toString") { - override def applySeq(x: A): String = x.toString - } - - /** Represents calculation of size in bytes of the given value. - * The descriptor value.elem can be used to decompose value into components. - */ - case class SizeOf[T](value: Ref[T]) extends BaseDef[Long] { - override def transform(t: Transformer) = SizeOf(t(value)) - } - - def sizeOf[T](value: Ref[T]): Ref[Long] = SizeOf(value) - - /** Special graph node to represent accumulation of the operation costs. - * In general, due to node sharing it is incorrect to just sum up all the `args` costs - * and add `resCost` to that value. - * Example:
- * - * val x = .. - * val y = op1(x) - * val z = op2(x) - * val res = op3(y, z) - * - * The naive summation will lead to the cost of x` is accumulated both into `cost of y` - * and into `cost of z`, so in the `cost of res` it is accumulated twice. - * To avoid this problem OpCost nodes require special handling in during evaluation. - * - * @param lambdaVar the variable of the lambda in which scope this node is created. - * This makes this node belong to the lambda body, even if it doesn't - * otherwise depend on lambda argument. - * @param costedValueId The id of the node for which this node represents cost - * @param args costs of the arguments, which are here represent dependency information. - * @param opCost operation cost, which should be added to the current scope accumulated cost - */ - case class OpCost(lambdaVar: Sym, costedValueId: Int, args: Seq[Ref[Int]], opCost: Ref[Int]) extends BaseDef[Int] { - /** When this node is mirrored (as part of mirrorLambda) we apply transformer t for all arguments - * with standard data flow semantics. - * However this is not correct to do for lambdaVar. - * Instead we use current lambda from the top of the stack, which is always points to the most nested - * lambda. - */ - override def transform(t: Transformer) = OpCost(lambdaStack.head.x, costedValueId, t(args), t(opCost)) - } - - def opCost(costedValue: Sym, args: Seq[Ref[Int]], opCost: Ref[Int]): Ref[Int] = { - val id = costedValue.node.nodeId - val lamVar = lambdaStack.head.x - OpCost(lamVar, id, args, opCost) - } - - def assertValueIdForOpCost[A,B](value: Ref[A], cost: Ref[B]): Unit = { - assert(if (cost.node.isInstanceOf[OpCost]) value.node.nodeId == cost.node.asInstanceOf[OpCost].costedValueId else true, - s"${value.node} value node id (${value.node.nodeId}) is not equal to OpCost.costedValueId (${cost.node.asInstanceOf[OpCost].costedValueId})") - } - - case class Downcast[From, To](input: Ref[From], eTo: Elem[To]) extends BaseDef[To]()(eTo) { - override def transform(t: Transformer) = Downcast(t(input), eTo) - } - case class Upcast[From, To](input: Ref[From], eTo: Elem[To]) extends BaseDef[To]()(eTo) { - override def transform(t: Transformer) = Upcast(t(input), eTo) - } - - def downcast[To:Elem](value: Ref[_]): Ref[To] = Downcast(value, element[To]) - def upcast[To:Elem](value: Ref[_]): Ref[To] = Upcast(value, element[To]) - - implicit class RepUniversalOps[A](x: Ref[A]) { - def hashCodeRep: Ref[Int] = HashCode[A]().apply(x) - def toStringRep = ToString[A]().apply(x) - } - - case class Convert[From,To](eFrom: Elem[From], eTo: Elem[To], x: Ref[Def[_]], conv: Ref[From => To]) - extends BaseDef[To]()(eTo) { - override def transform(t: Transformer) = Convert(eFrom, eTo, t(x), t(conv)) - } - - def tryConvert[From, To](eFrom: Elem[From], eTo: Elem[To], x: Ref[Def[_]], conv: Ref[From => To]): Ref[To] = { - if (x.elem <:< eFrom) - conv(asRep[From](x)) - else - Convert(eFrom, eTo, x, conv) - } - -} diff --git a/sc/shared/src/main/scala/scalan/Exceptions.scala b/sc/shared/src/main/scala/sigma/compiler/Exceptions.scala similarity index 93% rename from sc/shared/src/main/scala/scalan/Exceptions.scala rename to sc/shared/src/main/scala/sigma/compiler/Exceptions.scala index 8ab128a06e..75b1ecb6a3 100644 --- a/sc/shared/src/main/scala/scalan/Exceptions.scala +++ b/sc/shared/src/main/scala/sigma/compiler/Exceptions.scala @@ -1,4 +1,4 @@ -package scalan +package sigma.compiler /** * Can be thrown in staged method body to prevent body unfolding. diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaCompiler.scala b/sc/shared/src/main/scala/sigma/compiler/SigmaCompiler.scala similarity index 92% rename from sc/shared/src/main/scala/sigmastate/lang/SigmaCompiler.scala rename to sc/shared/src/main/scala/sigma/compiler/SigmaCompiler.scala index 849167e159..942154409b 100644 --- a/sc/shared/src/main/scala/sigmastate/lang/SigmaCompiler.scala +++ b/sc/shared/src/main/scala/sigma/compiler/SigmaCompiler.scala @@ -1,12 +1,10 @@ -package sigmastate.lang +package sigma.compiler import fastparse.Parsed import fastparse.Parsed.Success import sigma.kiama.rewriting.Rewriter.{everywherebu, rewrite, rule} import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix -import scalan.GraphIRReflection import sigma.ast.{Exponentiate, MultiplyGroup, SCollectionMethods, SGlobalMethods, SGroupElementMethods, Value, Xor} -import sigmastate.eval.IRContext import sigmastate.interpreter.Interpreter.ScriptEnv import sigma.ast.SigmaPredef.PredefinedFuncRegistry import sigma.ast.MethodCall @@ -14,7 +12,10 @@ import sigmastate.lang.parsers.ParserException import sigma.ast._ import sigma.ast.syntax.SValue import SCollectionMethods.{ExistsMethod, ForallMethod, MapMethod} +import sigma.compiler.ir.{GraphIRReflection, IRContext} +import sigma.compiler.phases.{SigmaBinder, SigmaTyper} import sigmastate.InterpreterReflection +import sigmastate.lang.SigmaParser /** * @param networkPrefix network prefix to decode an ergo address from string (PK op) @@ -72,7 +73,8 @@ class SigmaCompiler private(settings: CompilerSettings) { val predefinedFuncRegistry = new PredefinedFuncRegistry(builder) val binder = new SigmaBinder(env, builder, networkPrefix, predefinedFuncRegistry) val bound = binder.bind(parsed) - val typer = new SigmaTyper(builder, predefinedFuncRegistry, settings.lowerMethodCalls) + val typeEnv = env.collect { case (k, v: SType) => k -> v } + val typer = new SigmaTyper(builder, predefinedFuncRegistry, typeEnv, settings.lowerMethodCalls) val typed = typer.typecheck(bound) typed } @@ -91,7 +93,12 @@ class SigmaCompiler private(settings: CompilerSettings) { /** Compiles the given typed expression. */ def compileTyped(env: ScriptEnv, typedExpr: SValue)(implicit IR: IRContext): CompilerResult[IR.type] = { - val compiledGraph = IR.buildGraph(env, typedExpr) + val placeholdersEnv = env + .collect { case (name, t: SType) => name -> t } + .zipWithIndex + .map { case ((name, t), index) => name -> ConstantPlaceholder(index, t) } + .toMap + val compiledGraph = IR.buildGraph(env ++ placeholdersEnv, typedExpr) val compiledTree = IR.buildTree(compiledGraph) CompilerResult(env, "", compiledGraph, compiledTree) } diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaTemplateCompiler.scala b/sc/shared/src/main/scala/sigma/compiler/SigmaTemplateCompiler.scala similarity index 78% rename from sc/shared/src/main/scala/sigmastate/lang/SigmaTemplateCompiler.scala rename to sc/shared/src/main/scala/sigma/compiler/SigmaTemplateCompiler.scala index ec94bdfd3a..0b30f9830a 100644 --- a/sc/shared/src/main/scala/sigmastate/lang/SigmaTemplateCompiler.scala +++ b/sc/shared/src/main/scala/sigma/compiler/SigmaTemplateCompiler.scala @@ -1,12 +1,11 @@ -package sigmastate.lang +package sigma.compiler import fastparse.Parsed -import org.ergoplatform.sdk.ContractTemplate -import sigmastate.eval.CompiletimeIRContext -import org.ergoplatform.sdk.Parameter +import org.ergoplatform.sdk.{ContractTemplate, Parameter} import sigma.ast.SourceContext import sigma.ast.syntax.SValue -import sigmastate.interpreter.Interpreter.ScriptEnv +import sigma.compiler.ir.CompiletimeIRContext +import sigmastate.lang.{ContractParser, ParsedContractTemplate} import sigmastate.lang.parsers.ParserException /** Compiler which compiles Ergo contract templates into a [[ContractTemplate]]. */ @@ -19,13 +18,14 @@ class SigmaTemplateCompiler(networkPrefix: Byte) { * @param source The ErgoScript contract source code. * @return The contract template. */ - def compile(env: ScriptEnv, source: String): ContractTemplate = { + def compile(source: String): ContractTemplate = { ContractParser.parse(source) match { - case Parsed.Success(template, _) => { + case Parsed.Success(parsedTemplate, _) => implicit val ir = new CompiletimeIRContext - val result = sigmaCompiler.compileParsed(env, template.body) - assemble(template, result.buildTree) - } + val parEnv = parsedTemplate.signature.params.map { p => p.name -> p.tpe }.toMap + val result = sigmaCompiler.compileParsed(parEnv, parsedTemplate.body) + assemble(parsedTemplate, result.buildTree) + case f: Parsed.Failure => throw new ParserException(s"Contract template syntax error: $f", Some(SourceContext.fromParserFailure(f))) } diff --git a/sc/shared/src/main/scala/scalan/staged/AstGraphs.scala b/sc/shared/src/main/scala/sigma/compiler/ir/AstGraphs.scala similarity index 96% rename from sc/shared/src/main/scala/scalan/staged/AstGraphs.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/AstGraphs.scala index 7ad5c24f17..79a078ad19 100644 --- a/sc/shared/src/main/scala/scalan/staged/AstGraphs.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/AstGraphs.scala @@ -1,13 +1,15 @@ -package scalan.staged +package sigma.compiler.ir -import scala.collection._ -import scalan.Scalan -import debox.cfor -import debox.{Buffer => DBuffer, Map => DMap, Set => DSet} +import debox.{cfor, Buffer => DBuffer, Map => DMap, Set => DSet} import sigma.data.emptyDBufferOfInt +import scala.collection._ + -trait AstGraphs extends Transforming { self: Scalan => +/** Defines reusable abstraction of AstGraph, the base class for [[ProgramGraph]], + * [[Lambda]] and [[ThunkDef]]. + */ +trait AstGraphs extends Transforming { self: IRContext => /** GraphNode is created for each symbol of the AstGraph and represents graph linking structure */ case class GraphNode( diff --git a/sc/shared/src/main/scala/scalan/Base.scala b/sc/shared/src/main/scala/sigma/compiler/ir/Base.scala similarity index 97% rename from sc/shared/src/main/scala/scalan/Base.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/Base.scala index cc29af45b9..d6f682abd5 100644 --- a/sc/shared/src/main/scala/scalan/Base.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/Base.scala @@ -1,14 +1,15 @@ -package scalan +package sigma.compiler.ir import debox.{cfor, Buffer => DBuffer} -import sigma.data.{AVHashMap, Nullable, RType} +import sigma.compiler.ir.core.MutableLazy import sigma.data.OverloadHack.Overloaded1 -import sigma.util.StringUtil +import sigma.data.{AVHashMap, Nullable, RType} import sigma.reflection.RConstructor +import sigma.util.StringUtil import java.util.Arrays -import scala.annotation.{implicitNotFound, unused} import scala.annotation.unchecked.uncheckedVariance +import scala.annotation.{implicitNotFound, unused} import scala.collection.compat.immutable.ArraySeq import scala.collection.mutable import scala.language.implicitConversions @@ -17,7 +18,7 @@ import scala.language.implicitConversions * The Base trait houses common AST nodes. It also manages a list of encountered definitions which * allows for common sub-expression elimination (CSE). */ -abstract class Base { scalan: Scalan => +abstract class Base { thisIR: IRContext => type |[+A, +B] = Either[A, B] type RFunc[-A,+B] = Ref[Function1[A,B]] type RPair[+A, +B] = Ref[(A,B)] @@ -51,7 +52,7 @@ abstract class Base { scalan: Scalan => /** Base class for all IR nodes/operations/definitions. */ abstract class Node extends Product { - private[scalan] var _nodeId: Int = freshId + private[compiler] var _nodeId: Int = freshId /** Unique id of the graph node assigned for each new instance using * `freshId` generator. @@ -168,7 +169,7 @@ abstract class Base { scalan: Scalan => /** Create a copy of this definition applying the given transformer to all `syms`. */ def transform(t: Transformer): Def[T] = - !!!(s"Cannot transfrom definition using transform($this)", self) + !!!(s"Cannot transform definition using transform($this)", self) /** Clone this definition transforming all symbols using `t`. * If new Def[A] is created, it is added to the graph with collapsing and rewriting. @@ -365,8 +366,8 @@ abstract class Base { scalan: Scalan => /** Most of the references are initialized when created. * These methods are used in core to assign new value for the reference.*/ - private[scalan] def assignDef[B >: T](d: Def[B]): Unit - private[scalan] def assignDefFrom[B >: T](ref: Ref[B]): Unit + private[compiler] def assignDef[B >: T](d: Def[B]): Unit + private[compiler] def assignDefFrom[B >: T](ref: Ref[B]): Unit /** Whether the underlying node is Placeholder. */ @inline final def isPlaceholder: Boolean = node.isInstanceOf[Placeholder[_]] @@ -550,10 +551,10 @@ abstract class Base { scalan: Scalan => } /** Prepend owner parameter depending on its kind. */ - private[scalan] def addOwnerParameter(ownerType: OwnerKind, params: Seq[Any]): Seq[AnyRef] = { + private[compiler] def addOwnerParameter(ownerType: OwnerKind, params: Seq[Any]): Seq[AnyRef] = { val finalParams = (ownerType match { case EntityObjectOwner(obj) => obj +: params - case ScalanOwner => scalan +: params + case ScalanOwner => thisIR +: params case NoOwner => params }) finalParams.asInstanceOf[Seq[AnyRef]] @@ -601,7 +602,7 @@ abstract class Base { scalan: Scalan => override def elem: Elem[T @uncheckedVariance] = _node.resultType override def node: Def[T] = _node - private[scalan] def assignDefInternal[B >: T](d: Def[B]): Unit = { + private[compiler] def assignDefInternal[B >: T](d: Def[B]): Unit = { assert(_node.isInstanceOf[Placeholder[_]]) assert(_node.nodeId > 0) val tab = _symbolTable @@ -612,12 +613,12 @@ abstract class Base { scalan: Scalan => _node = d.asInstanceOf[Def[T]] } - private[scalan] def assignDef[B >: T](d: Def[B]): Unit = { + private[compiler] def assignDef[B >: T](d: Def[B]): Unit = { assignDefInternal(d) updateSymbolTable(this, d) } - private[scalan] def assignDefFrom[B >: T](sym: Ref[B]): Unit = { + private[compiler] def assignDefFrom[B >: T](sym: Ref[B]): Unit = { assignDefInternal(sym.node) } @@ -670,7 +671,7 @@ abstract class Base { scalan: Scalan => } /** Should be invoked to reset IR global node counter. */ - @inline final private[scalan] def resetIdCounter() = { currId = 0 } + @inline final private[compiler] def resetIdCounter() = { currId = 0 } /** Create or find symbol (node Ref) which refers to the given node in the table of all created symbols. * The d.nodeId is the index in the _symbolTable which is DBuffer (backed by Array) @@ -825,7 +826,7 @@ abstract class Base { scalan: Scalan => * @tparam T * @return The symbol of the graph which is semantically(up to rewrites) equivalent to d */ - protected[scalan] def toExp[T](d: Def[T], newSym: => Ref[T]): Ref[T] = { + protected[compiler] def toExp[T](d: Def[T], newSym: => Ref[T]): Ref[T] = { var res = findOrCreateDefinition(d, newSym) var currSym = res var currDef = d diff --git a/sc/shared/src/main/scala/scalan/DefRewriting.scala b/sc/shared/src/main/scala/sigma/compiler/ir/DefRewriting.scala similarity index 94% rename from sc/shared/src/main/scala/scalan/DefRewriting.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/DefRewriting.scala index 114fb1b0e6..62deffad31 100644 --- a/sc/shared/src/main/scala/scalan/DefRewriting.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/DefRewriting.scala @@ -1,8 +1,14 @@ -package scalan +package sigma.compiler.ir +import sigma.compiler.DelayInvokeException import sigma.data.ExactNumeric -trait DefRewriting { scalan: Scalan => +/** Defines methods to implement graph rewriting optimization rules. + * The rules are defines as a series of pattern matching with stackable overriding. + * Can be mixed in into traits to add new rules (pattern cases) and call `super` + * implementation as a fallback case. + */ +trait DefRewriting { scalan: IRContext => /** Rewrites given node to another equivalent node and returns its reference. * @param d node to be matched against rewrite patterns diff --git a/sc/shared/src/main/scala/scalan/Entities.scala b/sc/shared/src/main/scala/sigma/compiler/ir/Entities.scala similarity index 96% rename from sc/shared/src/main/scala/scalan/Entities.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/Entities.scala index 8a32dcde10..dfaeff207c 100644 --- a/sc/shared/src/main/scala/scalan/Entities.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/Entities.scala @@ -1,7 +1,7 @@ -package scalan +package sigma.compiler.ir /** A slice in the Scalan cake with base classes for various descriptors. */ -trait Entities extends TypeDescs { self: Scalan => +trait Entities extends TypeDescs { self: IRContext => /** Base class for all descriptors of staged traits. * See derived classes in `impl` packages. diff --git a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala similarity index 81% rename from sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 9c9fa5ffe1..f5235aba5e 100644 --- a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1,24 +1,33 @@ -package sigmastate.eval +package sigma.compiler.ir import org.ergoplatform._ -import scalan.MutableLazy -import sigma.{SigmaException, ast} +import sigma.ast.SType.tT +import sigma.{SigmaException, VersionContext, ast} +import sigma.Evaluation.stypeToRType +import sigma.ast.SType.tT +import sigma.{SigmaException, VersionContext, ast} import sigma.ast.TypeCodes.LastConstantCode import sigma.ast.Value.Typed -import sigma.ast._ import sigma.ast.syntax.{SValue, ValueOps} +import sigma.ast._ +import sigma.compiler.ir.core.MutableLazy import sigma.crypto.EcPointType +import sigma.VersionContext import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} import sigma.data.ExactOrdering.{ByteIsExactOrdering, IntIsExactOrdering, LongIsExactOrdering, ShortIsExactOrdering} import sigma.data.{CSigmaDslBuilder, ExactIntegral, ExactNumeric, ExactOrdering, Lazy, Nullable} import sigma.util.Extensions.ByteOps import sigmastate.interpreter.Interpreter.ScriptEnv import sigma.ast.{Ident, Select, Val} +import sigma.data.UnsignedBigIntNumericOps.{UnsignedBigIntIsExactIntegral, UnsignedBigIntIsExactOrdering} import sigma.exceptions.GraphBuildingException import sigma.serialization.OpCodes +import sigma.{SigmaException, ast} +import sigma.VersionContext import scala.collection.mutable.ArrayBuffer + /** Perform translation of typed expression given by [[Value]] to a graph in IRContext. * Which be than be translated to [[ErgoTree]] by using [[TreeBuilding]]. * @@ -27,9 +36,10 @@ import scala.collection.mutable.ArrayBuffer * CSE however means the original structure of source code may not be preserved in the * resulting ErgoTree. * */ -trait GraphBuilding extends SigmaLibrary { IR: IRContext => +trait GraphBuilding extends Base with DefRewriting { IR: IRContext => import AvlTree._ import BigInt._ + import UnsignedBigInt._ import Box._ import Coll._ import CollBuilder._ @@ -42,8 +52,8 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => import SigmaProp._ import WOption._ - /** Should be specified in the final cake */ - val builder: SigmaBuilder + /** Builder used to create ErgoTree nodes. */ + val builder = TransformingSigmaBuilder import builder._ val okMeasureOperationTime: Boolean = false @@ -52,10 +62,6 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => this.keepOriginalFunc = false // original lambda of Lambda node contains invocations of evalNode and we don't want that this.useAlphaEquality = false - /** Whether to create CostOf nodes or substutute costs from CostTable as constants in the graph. - * true - substitute; false - create CostOf nodes */ - var substFromCostTable: Boolean = true - /** Whether to save calcF and costF graphs in the file given by ScriptNameProp environment variable */ var saveGraphsInFile: Boolean = false @@ -259,6 +265,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case SString => StringElement case SAny => AnyElement case SBigInt => bigIntElement + case SUnsignedBigInt => unsignedBigIntElement case SBox => boxElement case SContext => contextElement case SGlobal => sigmaDslBuilderElement @@ -285,6 +292,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case StringElement => SString case AnyElement => SAny case _: BigIntElem[_] => SBigInt + case _: UnsignedBigIntElem[_] => SUnsignedBigInt case _: GroupElementElem[_] => SGroupElement case _: AvlTreeElem[_] => SAvlTree case oe: WOptionElem[_, _] => SOption(elemToSType(oe.eItem)) @@ -312,6 +320,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case StringElement => StringIsLiftable case UnitElement => UnitIsLiftable case _: BigIntElem[_] => LiftableBigInt + case _: UnsignedBigIntElem[_] => LiftableUnsignedBigInt case _: GroupElementElem[_] => LiftableGroupElement case ce: CollElem[t,_] => implicit val lt = liftableFromElem[t](ce.eItem) @@ -332,20 +341,24 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => (ShortElement, ShortIsExactIntegral), (IntElement, IntIsExactIntegral), (LongElement, LongIsExactIntegral), - (bigIntElement, BigIntIsExactIntegral) + (bigIntElement, BigIntIsExactIntegral), + (unsignedBigIntElement, UnsignedBigIntIsExactIntegral) ) private lazy val elemToExactIntegralMap = Map[Elem[_], ExactIntegral[_]]( (ByteElement, ByteIsExactIntegral), (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 */ @@ -380,13 +393,23 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case _ => error(s"Cannot find BinOp for opcode newOpCode(${opCode.toUByte - LastConstantCode}) and type $eA") } - import sigmastate._ - protected implicit def groupElementToECPoint(g: sigma.GroupElement): EcPointType = CSigmaDslBuilder.toECPoint(g).asInstanceOf[EcPointType] def error(msg: String) = throw new GraphBuildingException(msg, None) def error(msg: String, srcCtx: Option[SourceContext]) = throw new GraphBuildingException(msg, srcCtx) + /** Graph node to represent a placeholder of a constant in ErgoTree. + * @param id Zero based index in ErgoTree.constants array. + * @param resultType type descriptor of the constant value. + */ + case class ConstantPlaceholder[T](id: Int, resultType: Elem[T]) extends Def[T] + + /** Smart constructor method for [[ConstantPlaceholder]], should be used instead of the + * class constructor. + */ + @inline def constantPlaceholder[T](id: Int, eT: Elem[T]): Ref[T] = ConstantPlaceholder(id, eT) + + /** Translates the given typed expression to IR graph representing a function from * Context to some type T. * @param env contains values for each named constant used @@ -432,11 +455,11 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => } Nullable(res) }} - def throwError = - error(s"Don't know how to buildNode($node)", node.sourceContext.toOption) + def throwError(clue: String = "") = + error((if (clue.nonEmpty) clue + ": " else "") + s"Don't know how to buildNode($node)", node.sourceContext.toOption) val res: Ref[Any] = node match { - case c @ Constant(v, tpe) => v match { + case Constant(v, tpe) => v match { case p: SSigmaProp => assert(tpe == SSigmaProp) val resV = liftConst(p) @@ -445,6 +468,10 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => assert(tpe == SBigInt) val resV = liftConst(bi) resV + case ubi: SUnsignedBigInt => + assert(tpe == SUnsignedBigInt) + val resV = liftConst(ubi) + resV case p: SGroupElement => assert(tpe == SGroupElement) val resV = liftConst(p) @@ -471,6 +498,8 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val resV = toRep(v)(e) resV } + case sigma.ast.ConstantPlaceholder(id, tpe) => + constantPlaceholder(id, stypeToElem(tpe)) case sigma.ast.Context => ctx case Global => sigmaDslBuilder case Height => ctx.HEIGHT @@ -887,7 +916,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => f } - case l @ FuncValue(Seq((n, argTpe)), body) => + case FuncValue(Seq((n, argTpe)), body) => val eArg = stypeToElem(argTpe).asInstanceOf[Elem[Any]] val f = fun { x: Ref[Any] => buildNode(ctx, env + (n -> x), body) @@ -921,7 +950,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => sigmaDslBuilder.decodePoint(bytes) // fallback rule for MethodCall, should be the last case in the list - case sigma.ast.MethodCall(obj, method, args, _) => + case sigma.ast.MethodCall(obj, method, args, typeSubst) => val objV = eval(obj) val argsV = args.map(eval) (objV, method.objType) match { @@ -978,7 +1007,20 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val i = asRep[Int](argsV(0)) val d = asRep[t](argsV(1)) xs.getOrElse(i, d) - case _ => throwError + 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 { case SOptionMethods.GetMethod.name => @@ -992,7 +1034,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => opt.map(asRep[t => Any](argsV(0))) case SOptionMethods.FilterMethod.name => opt.filter(asRep[t => Boolean](argsV(0))) - case _ => throwError + case _ => throwError() } case (ge: Ref[GroupElement]@unchecked, SGroupElementMethods) => method.name match { case SGroupElementMethods.GetEncodedMethod.name => @@ -1005,12 +1047,19 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case SGroupElementMethods.ExponentiateMethod.name => val k = asRep[BigInt](argsV(0)) ge.exp(k) - case _ => throwError + case SGroupElementMethods.ExponentiateUnsignedMethod.name => + val k = asRep[UnsignedBigInt](argsV(0)) + ge.expUnsigned(k) + case _ => throwError() } case (box: Ref[Box]@unchecked, SBoxMethods) => method.name match { case SBoxMethods.tokensMethod.name => box.tokens - case _ => throwError + case SBoxMethods.getRegMethodV6.name if VersionContext.current.isV6SoftForkActivated => + val c1 = asRep[Int](argsV(0)) + val c2 = stypeToElem(typeSubst.apply(tT)) + box.getReg(c1)(c2) + case _ => throwError() } case (ctx: Ref[Context]@unchecked, SContextMethods) => method.name match { case SContextMethods.dataInputsMethod.name => @@ -1033,7 +1082,16 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => ctx.LastBlockUtxoRootHash case SContextMethods.minerPubKeyMethod.name => ctx.minerPubKey - case _ => throwError + 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 { case SAvlTreeMethods.digestMethod.name => @@ -1080,7 +1138,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val operations = asRep[Coll[(Coll[Byte], Coll[Byte])]](argsV(0)) val proof = asRep[Coll[Byte]](argsV(1)) tree.update(operations, proof) - case _ => throwError + case _ => throwError() } case (ph: Ref[PreHeader]@unchecked, SPreHeaderMethods) => method.name match { case SPreHeaderMethods.versionMethod.name => @@ -1097,7 +1155,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => ph.minerPk case SPreHeaderMethods.votesMethod.name => ph.votes - case _ => throwError + case _ => throwError() } case (h: Ref[Header]@unchecked, SHeaderMethods) => method.name match { case SHeaderMethods.idMethod.name => @@ -1130,7 +1188,9 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => h.powDistance case SHeaderMethods.votesMethod.name => h.votes - case _ => throwError + case SHeaderMethods.checkPowMethod.name if VersionContext.current.isV6SoftForkActivated => + h.checkPow + case _ => throwError() } case (g: Ref[SigmaDslBuilder]@unchecked, SGlobalMethods) => method.name match { case SGlobalMethods.groupGeneratorMethod.name => @@ -1139,13 +1199,116 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val c1 = asRep[Coll[Byte]](argsV(0)) val c2 = asRep[Coll[Byte]](argsV(1)) g.xor(c1, c2) - case _ => throwError + case SGlobalMethods.encodeNBitsMethod.name if VersionContext.current.isV6SoftForkActivated => + val c1 = asRep[BigInt](argsV(0)) + g.encodeNbits(c1) + case SGlobalMethods.decodeNBitsMethod.name if VersionContext.current.isV6SoftForkActivated => + val c1 = asRep[Long](argsV(0)) + g.decodeNbits(c1) + case SGlobalMethods.powHitMethod.name if VersionContext.current.isV6SoftForkActivated => + val k = asRep[Int](argsV(0)) + val msg = asRep[Coll[Byte]](argsV(1)) + val nonce = asRep[Coll[Byte]](argsV(2)) + val h = asRep[Coll[Byte]](argsV(3)) + val N = asRep[Int](argsV(4)) + g.powHit(k, msg, nonce, h, N) + case SGlobalMethods.encodeNBitsMethod.name if VersionContext.current.isV6SoftForkActivated => + val c1 = asRep[BigInt](argsV(0)) + g.encodeNbits(c1) + case SGlobalMethods.decodeNBitsMethod.name if VersionContext.current.isV6SoftForkActivated => + val c1 = asRep[Long](argsV(0)) + g.decodeNbits(c1) + case SGlobalMethods.deserializeToMethod.name if VersionContext.current.isV6SoftForkActivated => + val c1 = asRep[Coll[Byte]](argsV(0)) + val c2 = stypeToElem(method.stype.tRange.withSubstTypes(typeSubst)) + g.deserializeTo(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 SGlobalMethods.someMethod.name => + val value = asRep[tT.WrappedType](argsV(0)) + val cT = stypeToElem(typeSubst.apply(tT)).asInstanceOf[Elem[tT.WrappedType]] + g.some(value)(cT) + case SGlobalMethods.noneMethod.name => + val cT = stypeToElem(typeSubst.apply(tT)).asInstanceOf[Elem[tT.WrappedType]] + g.none()(cT) + case _ => throwError() + } + case (x: Ref[tNum], ms: SNumericTypeMethods) => method.name match { + case SNumericTypeMethods.ToBytesMethod.name => + val op = NumericToBigEndianBytes(elemToExactNumeric(x.elem)) + ApplyUnOp(op, x) + case SNumericTypeMethods.ToBitsMethod.name => + val op = NumericToBits(elemToExactNumeric(x.elem)) + ApplyUnOp(op, x) + case SNumericTypeMethods.BitwiseInverseMethod.name => + val op = NumericBitwiseInverse(elemToExactNumeric(x.elem))(x.elem) + ApplyUnOp(op, x) + case SNumericTypeMethods.BitwiseOrMethod.name => + val y = asRep[tNum](argsV(0)) + val op = NumericBitwiseOr(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOp(op, x, y) + case SNumericTypeMethods.BitwiseAndMethod.name => + val y = asRep[tNum](argsV(0)) + val op = NumericBitwiseAnd(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOp(op, x, y) + case SNumericTypeMethods.BitwiseXorMethod.name => + val y = asRep[tNum](argsV(0)) + val op = NumericBitwiseXor(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOp(op, x, y) + case SNumericTypeMethods.ShiftLeftMethod.name => + val y = asRep[Int](argsV(0)) + val op = NumericShiftLeft(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOpDiffArgs(op, x, y) + case SNumericTypeMethods.ShiftRightMethod.name => + val y = asRep[Int](argsV(0)) + val op = NumericShiftRight(elemToExactNumeric(x.elem))(x.elem) + ApplyBinOpDiffArgs(op, x, y) + case 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 + case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") } case _ => - throwError + throwError() } val resC = asRep[T#WrappedType](res) resC diff --git a/sc/shared/src/main/scala/scalan/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala similarity index 75% rename from sc/shared/src/main/scala/scalan/GraphIRReflection.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala index 0eaba9d8a3..a49dbe53a6 100644 --- a/sc/shared/src/main/scala/scalan/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -1,14 +1,14 @@ -package scalan +package sigma.compiler.ir -import scalan.primitives.Thunks +import sigma.Coll +import sigma.{BigInt, SigmaDslBuilder} +import sigma.ast.SType +import sigma.compiler.ir.primitives.Thunks +import sigma.data.RType import sigma.reflection.ReflectionData.registerClassEntry import sigma.reflection.{ReflectionData, mkConstructor, mkMethod} -import sigmastate.eval.SigmaLibrary -import sigma.Colls -import sigma.SigmaDsl -import sigma.data.RType -import special.wrappers.{OptionWrapSpec, RTypeWrapSpec} -import wrappers.scalan.WRTypes +import sigma.compiler.ir.wrappers.{OptionWrapSpec, RTypeWrapSpec} +import sigma.compiler.ir.wrappers.sigma.{SigmaDsl, WRTypes} /** Registrations of reflection metadata for graph-ir module (see README.md). * Such metadata is only used on JS platform to support reflection-like interfaces of @@ -23,8 +23,8 @@ object GraphIRReflection { /** Forces initialization of reflection data. */ val reflection = ReflectionData - { val clazz = classOf[wrappers.scala.WOptions#WOption[_]] - val ctx = null.asInstanceOf[scalan.Library] // ok! type level only + { val clazz = classOf[sigma.compiler.ir.wrappers.scala.WOptions#WOption[_]] + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "filter", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => @@ -48,8 +48,8 @@ object GraphIRReflection { registerClassEntry(classOf[TypeDescs#FuncElem[_,_]], constructors = Array( - mkConstructor(Array(classOf[Scalan], classOf[TypeDescs#Elem[_]], classOf[TypeDescs#Elem[_]])) { args => - val ctx = args(0).asInstanceOf[Scalan] + mkConstructor(Array(classOf[IRContext], classOf[TypeDescs#Elem[_]], classOf[TypeDescs#Elem[_]])) { args => + val ctx = args(0).asInstanceOf[IRContext] new ctx.FuncElem(args(1).asInstanceOf[ctx.Elem[_]], args(2).asInstanceOf[ctx.Elem[_]]) } ) @@ -57,8 +57,8 @@ object GraphIRReflection { registerClassEntry(classOf[TypeDescs#PairElem[_,_]], constructors = Array( - mkConstructor(Array(classOf[Scalan], classOf[TypeDescs#Elem[_]], classOf[TypeDescs#Elem[_]])) { args => - val ctx = args(0).asInstanceOf[Scalan] + mkConstructor(Array(classOf[IRContext], classOf[TypeDescs#Elem[_]], classOf[TypeDescs#Elem[_]])) { args => + val ctx = args(0).asInstanceOf[IRContext] new ctx.PairElem(args(1).asInstanceOf[ctx.Elem[_]], args(2).asInstanceOf[ctx.Elem[_]]) } ) @@ -66,14 +66,14 @@ object GraphIRReflection { registerClassEntry(classOf[Thunks#ThunkElem[_]], constructors = Array( - mkConstructor(Array(classOf[Scalan], classOf[TypeDescs#Elem[_]])) { args => - val ctx = args(0).asInstanceOf[Scalan] + mkConstructor(Array(classOf[IRContext], classOf[TypeDescs#Elem[_]])) { args => + val ctx = args(0).asInstanceOf[IRContext] new ctx.ThunkElem(args(1).asInstanceOf[ctx.Elem[_]]) } ) ) - { val clazz = classOf[sigma.SigmaDsl#SigmaProp] + { val clazz = classOf[sigma.compiler.ir.wrappers.sigma.SigmaDsl#SigmaProp] val ctx = null.asInstanceOf[SigmaDsl] // ok! type level only registerClassEntry(clazz, methods = Map( @@ -117,13 +117,66 @@ 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[Colls#CollBuilder] - val ctx = null.asInstanceOf[Library] // ok! type level only + { 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 + } + ) + ) + } + + { val clazz = classOf[sigma.compiler.ir.wrappers.sigma.Colls#CollBuilder] + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "xor", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) => @@ -144,8 +197,8 @@ object GraphIRReflection { } { - val clazz = classOf[Colls#Coll[_]] - val ctx = null.asInstanceOf[Library] // ok! type level only + val clazz = classOf[sigma.compiler.ir.wrappers.sigma.Colls#Coll[_]] + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "append", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => @@ -205,13 +258,29 @@ 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]]) } ) ) } { val clazz = classOf[SigmaDsl#AvlTree] - val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "updateOperations", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => @@ -269,7 +338,7 @@ object GraphIRReflection { } { val clazz = classOf[SigmaDsl#Box] - val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "value", Array[Class[_]]()) { (obj, _) => @@ -302,7 +371,7 @@ object GraphIRReflection { { val clazz = classOf[SigmaDsl#Context] - val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "LastBlockUtxoRootHash", Array[Class[_]]()) { (obj, args) => @@ -335,6 +404,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 } @@ -343,12 +415,15 @@ object GraphIRReflection { } { val clazz = classOf[SigmaDsl#GroupElement] - val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( 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]]) }, @@ -363,7 +438,7 @@ object GraphIRReflection { } { val clazz = classOf[SigmaDsl#Header] - val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "minerPk", Array[Class[_]]()) { (obj, args) => @@ -410,13 +485,16 @@ object GraphIRReflection { }, mkMethod(clazz, "powDistance", Array[Class[_]]()) { (obj, args) => obj.asInstanceOf[ctx.Header].powDistance + }, + mkMethod(clazz, "checkPow", Array[Class[_]]()) { (obj, args) => + obj.asInstanceOf[ctx.Header].checkPow } ) ) } { val clazz = classOf[SigmaDsl#PreHeader] - val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "minerPk", Array[Class[_]]()) { (obj, _) => @@ -445,7 +523,7 @@ object GraphIRReflection { } { val clazz = classOf[SigmaDsl#SigmaDslBuilder] - val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "byteArrayToLong", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => @@ -507,12 +585,39 @@ 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, "powHit", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]], + classOf[Base#Ref[_]], classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].powHit(args(0).asInstanceOf[ctx.Ref[Int]], + args(1).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]], args(2).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]], + args(3).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]], args(4).asInstanceOf[ctx.Ref[Int]]) + }, + mkMethod(clazz, "encodeNbits", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].encodeNbits(args(0).asInstanceOf[ctx.Ref[ctx.BigInt]]) + }, + mkMethod(clazz, "decodeNbits", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].decodeNbits(args(0).asInstanceOf[ctx.Ref[Long]]) + }, + 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]]) + }, + mkMethod(clazz, "deserializeTo", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].deserializeTo(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])(args(1).asInstanceOf[ctx.Elem[SType]]) + }, + mkMethod(clazz, "some", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].some(args(0).asInstanceOf[ctx.Ref[Any]])(args(1).asInstanceOf[ctx.Elem[Any]]) + }, + mkMethod(clazz, "none", Array[Class[_]](classOf[TypeDescs#Elem[_]])) { (obj, args) => + obj.asInstanceOf[ctx.SigmaDslBuilder].none()(args(0).asInstanceOf[ctx.Elem[SType]]) } ) ) } - { val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + { val ctx = null.asInstanceOf[IRContext] // ok! type level only val clazz = classOf[ctx.WOption.WOptionElem[_, _]] registerClassEntry(clazz, constructors = Array( @@ -525,7 +630,7 @@ object GraphIRReflection { } { val clazz = classOf[WRTypes#WRType[_]] - val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + val ctx = null.asInstanceOf[IRContext] // ok! type level only registerClassEntry(clazz, methods = Map( mkMethod(clazz, "name", Array[Class[_]]()) { (obj, _) => @@ -535,7 +640,7 @@ object GraphIRReflection { ) } - { val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + { val ctx = null.asInstanceOf[IRContext] // ok! type level only val clazz = classOf[ctx.WRType.WRTypeElem[_, _]] registerClassEntry(clazz, constructors = Array( @@ -547,7 +652,7 @@ object GraphIRReflection { ) } - { val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only + { val ctx = null.asInstanceOf[IRContext] // ok! type level only val clazz = classOf[ctx.Coll.CollElem[_, _]] registerClassEntry(clazz, constructors = Array( @@ -559,16 +664,6 @@ object GraphIRReflection { ) } - { val clazz = classOf[wrappers.special.WSpecialPredefs#WSpecialPredefCompanion] - val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only - registerClassEntry(clazz, - methods = Map( - mkMethod(clazz, "some", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => - obj.asInstanceOf[ctx.WSpecialPredefCompanion].some(args(0).asInstanceOf[ctx.Ref[Any]]) - } - ) - ) - } { val clazz = classOf[OptionWrapSpec] registerClassEntry(clazz, diff --git a/sc/shared/src/main/scala/scalan/Library.scala b/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala similarity index 57% rename from sc/shared/src/main/scala/scalan/Library.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala index ee1ff80ad3..a22962f987 100644 --- a/sc/shared/src/main/scala/scalan/Library.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala @@ -1,19 +1,65 @@ -package scalan +package sigma.compiler.ir -import scala.language.implicitConversions -import sigma._ +import sigma.compiler.ir.core.MutableLazy +import sigma.compiler.ir.primitives._ import sigma.data.{Nullable, RType} -import special.wrappers.WrappersModule import sigma.util.MemoizedFunc - -trait Library extends Scalan - with WrappersModule +import sigma.compiler.ir.wrappers.scala.WOptionsModule +import sigma.compiler.ir.wrappers.sigma.{CollsModule, SigmaDslModule, WRTypesModule} + +/** Aggregate cake with all inter-dependent modules assembled together. + * Each instance of this class contains independent IR context, thus many + * instances can be created simultaneously. + * However, the inner types declared in the traits are path-dependant. + * This in particular means that ctx1.Ref[_] and ctx2.Ref[_] are different types. + * The typical usage is to create `val ctx = new Scalan` and then import inner + * declarations using `import ctx._`. + * This way the declaration will be directly available as if they were global + * declarations. + * At the same time cake design pattern allow to `override` many methods and values + * in classed derived from `Scalan`, this is significant benefit over + * *everything is global* design. + * + * It is not used in v5.0 interpreter and thus not part of consensus. + * Used in ErgoScript compiler only. + * + * @see CompiletimeIRContext + */ +trait IRContext + extends TypeDescs + with MethodCalls + with Tuples + with NumericOps + with UnBinOps + with LogicalOps + with OrderingOps + with Equal + with MiscOps + with Functions + with IfThenElse + with Transforming + with Thunks + with Entities + with Modules + with DefRewriting with CollsModule -{ + with SigmaDslModule + with TreeBuilding + with GraphBuilding + with WOptionsModule + with WRTypesModule { + + import Coll._ + import CollBuilder._ import WOption._ import WRType._ - import Coll._; import CollBuilder._; - import WSpecialPredef._ + + /** Pass configuration which is used to turn-off constant propagation. + * USED IN TESTS ONLY. + * @see `beginPass(noCostPropagationPass)` */ + lazy val noConstPropagationPass = new DefaultPass( + "noCostPropagationPass", + Pass.defaultPassConfig.copy(constantPropagation = false)) type LazyRep[T] = MutableLazy[Ref[T]] @@ -26,11 +72,7 @@ trait Library extends Scalan _liftElemMemo(eT).asInstanceOf[Ref[WRType[T]]] // asRep cannot be used for AnyRef } - private val _specialPredef: LazyRep[WSpecialPredefCompanionCtor] = MutableLazy(RWSpecialPredef.value) - def specialPredef: Ref[WSpecialPredefCompanionCtor] = _specialPredef.value - override protected def onReset(): Unit = { - _specialPredef.reset() _liftElemMemo.reset() super.onReset() } @@ -38,10 +80,15 @@ trait Library extends Scalan val CM = CollMethods private val CBM = CollBuilderMethods private val WOptionM = WOptionMethods - private val SPCM = WSpecialPredefCompanionMethods def colBuilder: Ref[CollBuilder] + /** Type descriptor for [[WRType]] */ + implicit lazy val wRTypeAnyElement: Elem[WRType[Any]] = wRTypeElement(AnyElement) + + /** During compilation represent a global value Global, see also SGlobal type. */ + def sigmaDslBuilder: Ref[SigmaDslBuilder] + object IsNumericToInt { def unapply(d: Def[_]): Nullable[Ref[A] forSome {type A}] = d match { case ApplyUnOp(_: NumericToInt[_], x) => Nullable(x.asInstanceOf[Ref[A] forSome {type A}]) @@ -96,8 +143,6 @@ trait Library extends Scalan } case WOptionM.getOrElse(opt, _) => opt.node match { - // Rule: Some(x).getOrElse(_) ==> x - case SPCM.some(x) => x case WOptionConst(Some(x), lA) => lA.lift(x) case _ => super.rewriteDef(d) } @@ -107,8 +152,8 @@ trait Library extends Scalan override def invokeUnlifted(e: Elem[_], mc: MethodCall, dataEnv: DataEnv): Any = e match { case _: CollElem[_,_] => mc match { - case CollMethods.map(xs, f) => - val newMC = mc.copy(args = mc.args :+ f.elem.eRange)(mc.resultType, mc.isAdapterCall) + case CollMethods.map(_, f) => + val newMC = mc.copy(args = mc.args :+ f.elem.eRange)(mc.resultType, mc.isAdapterCall, mc.typeSubst) super.invokeUnlifted(e, newMC, dataEnv) case _ => super.invokeUnlifted(e, mc, dataEnv) @@ -118,3 +163,6 @@ trait Library extends Scalan } } + +/** IR context to be used by script development tools to compile ErgoScript into ErgoTree bytecode. */ +class CompiletimeIRContext extends IRContext diff --git a/sc/shared/src/main/scala/scalan/MethodCalls.scala b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala similarity index 82% rename from sc/shared/src/main/scala/scalan/MethodCalls.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala index af3c3201ef..089b76cae4 100644 --- a/sc/shared/src/main/scala/scalan/MethodCalls.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala @@ -1,13 +1,17 @@ -package scalan +package sigma.compiler.ir -import scala.annotation.{tailrec, unused} -import scala.reflect.ClassTag -import debox.{Buffer => DBuffer} -import debox.cfor +import debox.{cfor, Buffer => DBuffer} +import sigma.ast.{SType, STypeVar} +import sigma.compiler.DelayInvokeException import sigma.reflection.RMethod import sigma.util.CollectionUtil.TraversableOps -trait MethodCalls extends Base { self: Scalan => +import scala.annotation.{tailrec, unused} + +/** Defines graph-ir representation of method calls, new object creation as well as the + * related utility methods. + */ +trait MethodCalls extends Base { self: IRContext => def delayInvoke = throw new DelayInvokeException @@ -23,7 +27,9 @@ trait MethodCalls extends Base { self: Scalan => * given `method`. */ case class MethodCall private[MethodCalls](receiver: Sym, method: RMethod, args: Seq[AnyRef], neverInvoke: Boolean) - (val resultType: Elem[Any], val isAdapterCall: Boolean = false) extends Def[Any] { + (val resultType: Elem[Any], + val isAdapterCall: Boolean = false, + val typeSubst: Map[STypeVar, SType]) extends Def[Any] { override def mirror(t: Transformer): Ref[Any] = { val len = args.length @@ -96,14 +102,14 @@ trait MethodCalls extends Base { self: Scalan => } /** Creates new MethodCall node and returns its node ref. */ - def mkMethodCall(receiver: Sym, method: RMethod, args: Seq[AnyRef], - neverInvoke: Boolean, isAdapterCall: Boolean, resultElem: Elem[_]): Sym = { - reifyObject(MethodCall(receiver, method, args, neverInvoke)(asElem[Any](resultElem), isAdapterCall)) - } - - /** Creates new NewObject node and returns its node ref. */ - def newObjEx[A](args: Any*)(implicit eA: Elem[A]): Ref[A] = { - reifyObject(NewObject[A](eA, args)) + def mkMethodCall(receiver: Sym, + method: RMethod, + args: Seq[AnyRef], + neverInvoke: Boolean, + isAdapterCall: Boolean, + resultElem: Elem[_], + typeSubst: Map[STypeVar, SType] = Map.empty): Sym = { + reifyObject(MethodCall(receiver, method, args, neverInvoke)(asElem[Any](resultElem), isAdapterCall, typeSubst)) } @tailrec @@ -120,23 +126,8 @@ trait MethodCalls extends Base { self: Scalan => * point we know that the first RW set didn't triggered any rewrite. */ def rewriteNonInvokableMethodCall(@unused mc: MethodCall): Ref[_] = null - /** Create delegate instance suitable for method invocation. - * It is used when T is a class or a trait and the node referred by x doesn't conform to T. - * This method returns dynamically constructed instance, which conforms to T. - * Whenever a method of T is called on that instance, the call is intercepted and - * `DelegatedInterceptionHandler.invoke` method is called, then a new MethodCall can - * be constructed (which is befavior by default). - */ - protected def unrefDelegate[T <: AnyRef](x: Ref[T])(implicit ct: ClassTag[T]): T = { - val d = x.node - if (d.isInstanceOf[Const[_]]) - d.asInstanceOf[Const[T]@unchecked].x - else - !!!(s"Cannot do undefDelegate($x -> ${x.node})") - } - /** Generic helper to call the given method on the given receiver node. */ - private[scalan] def invokeMethod[A](receiver: Sym, m: RMethod, args: Array[AnyRef], + private[compiler] def invokeMethod[A](receiver: Sym, m: RMethod, args: Array[AnyRef], onInvokeSuccess: Any => A, onInvokeException: Throwable => A, onInvokeImpossible: => A): A = { @@ -148,9 +139,9 @@ trait MethodCalls extends Base { self: Scalan => } catch { case e: Exception => onInvokeException(baseCause(e)) } - } - else + } else { onInvokeImpossible + } } /** Method invocation enabler. diff --git a/sc/shared/src/main/scala/scalan/Modules.scala b/sc/shared/src/main/scala/sigma/compiler/ir/Modules.scala similarity index 70% rename from sc/shared/src/main/scala/scalan/Modules.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/Modules.scala index 989f2c524c..b58bacb5fb 100644 --- a/sc/shared/src/main/scala/scalan/Modules.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/Modules.scala @@ -1,6 +1,11 @@ -package scalan +package sigma.compiler.ir -trait Modules extends Base { self: Scalan => +import sigma.compiler.ir.meta.ModuleInfo + +/** Defines registration of IR modules. + * @see ModuleInfo + */ +trait Modules extends Base { self: IRContext => /** Whether staged modules should be registered when cake is constructed and initialized. */ def okRegisterModules: Boolean = false diff --git a/sc/shared/src/main/scala/scalan/staged/ProgramGraphs.scala b/sc/shared/src/main/scala/sigma/compiler/ir/ProgramGraphs.scala similarity index 92% rename from sc/shared/src/main/scala/scalan/staged/ProgramGraphs.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/ProgramGraphs.scala index ccb351e7a2..6f447d97f9 100644 --- a/sc/shared/src/main/scala/scalan/staged/ProgramGraphs.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/ProgramGraphs.scala @@ -1,13 +1,13 @@ -package scalan.staged +package sigma.compiler.ir -import scalan.Scalan -import debox.{Buffer => DBuffer} -import sigma.util.GraphUtil -import debox.cfor +import debox.{cfor, Buffer => DBuffer} import sigma.data.{DFunc, Nullable} +import sigma.util.GraphUtil + import scala.collection.compat.immutable.ArraySeq -trait ProgramGraphs extends AstGraphs { self: Scalan => +/** Defines implementation of [[ProgramGraph]] and related utilities. */ +trait ProgramGraphs extends AstGraphs { self: IRContext => type PGraph = ProgramGraph diff --git a/sc/shared/src/main/scala/scalan/staged/Transforming.scala b/sc/shared/src/main/scala/sigma/compiler/ir/Transforming.scala similarity index 97% rename from sc/shared/src/main/scala/scalan/staged/Transforming.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/Transforming.scala index cd1fac9fd0..876f2b5db1 100644 --- a/sc/shared/src/main/scala/scalan/staged/Transforming.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/Transforming.scala @@ -1,14 +1,18 @@ -package scalan.staged +package sigma.compiler.ir -import java.util -import scala.language.existentials -import scalan.Scalan -import debox.{Buffer => DBuffer} -import debox.cfor +import debox.{cfor, Buffer => DBuffer} import sigma.data.{Lazy, Nullable} import sigma.reflection.RMethod -trait Transforming { self: Scalan => +import java.util +import scala.language.existentials + +/** Implements utilites for graph transformation and rewriting. + * @see Pass + * @see MapTransformer + * @see Rewriter + */ +trait Transforming { self: IRContext => /** Descriptor of a current compiler pass. * Compiler can be configured to perform one pass after another. @@ -47,9 +51,6 @@ trait Transforming { self: Scalan => constantPropagation: Boolean = true, /** Used in SlicingPass */ shouldSlice: Boolean = false) - { - def withConstantPropagation(value: Boolean) = this.copy(constantPropagation = value) - } /** Default pass to be used when IR is used without special compiler configuration. */ class DefaultPass(val name: String, override val config: PassConfig = PassConfig()) extends Pass diff --git a/sc/shared/src/main/scala/sigmastate/eval/TreeBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala similarity index 79% rename from sc/shared/src/main/scala/sigmastate/eval/TreeBuilding.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala index 26bc33956f..652aeb4fe0 100644 --- a/sc/shared/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -1,16 +1,17 @@ -package sigmastate.eval +package sigma.compiler.ir - -import sigma.ast._ import org.ergoplatform._ -import sigma.ast.syntax.ValueOps +import sigma.VersionContext +import sigma.Evaluation.{rtypeToSType, stypeToRType} +import sigma.ast.SType.tT +import sigma.ast._ +import sigma.ast.syntax.{ValueOps, _} import sigma.serialization.OpCodes._ import sigma.serialization.ConstantStore -import sigma.ast.syntax._ import sigma.data.{ProveDHTuple, ProveDlog} +import sigma.serialization.ValueCodes.OpCode import scala.collection.mutable.ArrayBuffer -import sigma.serialization.ValueCodes.OpCode /** Implementation of IR-graph to ErgoTree expression translation. * This, in a sense, is inverse to [[GraphBuilding]], however roundtrip identity is not @@ -24,17 +25,17 @@ import sigma.serialization.ValueCodes.OpCode * * @see buildTree method * */ -trait TreeBuilding extends SigmaLibrary { IR: IRContext => - import Liftables._ - import Context._ - import SigmaProp._ - import Coll._ +trait TreeBuilding extends Base { IR: IRContext => + import BigInt._ import Box._ + import Coll._ import CollBuilder._ + import Context._ + import GroupElement._ + import Liftables._ import SigmaDslBuilder._ - import BigInt._ + import SigmaProp._ import WOption._ - import GroupElement._ /** Convenience synonyms for easier pattern matching. */ private val ContextM = ContextMethods @@ -108,6 +109,27 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => object IsNumericUnOp { def unapply(op: UnOp[_,_]): Option[SValue => SValue] = op match { case NumericNegate(_) => Some({ v: SValue => builder.mkNegation(v.asNumValue) }) + case _: NumericToBigEndianBytes[_] => + val mkNode = { v: SValue => + val receiverType = v.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${v.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.ToBytesMethod.methodId) + builder.mkMethodCall(v.asNumValue, m, IndexedSeq.empty) + } + Some(mkNode) + case _: NumericToBits[_] => + val mkNode = { v: SValue => + val receiverType = v.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${v.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.ToBitsMethod.methodId) + builder.mkMethodCall(v.asNumValue, m, IndexedSeq.empty) + } + Some(mkNode) + case _: NumericBitwiseInverse[_] => + val mkNode = { v: SValue => + val receiverType = v.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${v.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseInverseMethod.methodId) + builder.mkMethodCall(v.asNumValue, m, IndexedSeq.empty) + } + Some(mkNode) case _ => None } } @@ -130,7 +152,7 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => */ object IsInternalDef { def unapply(d: Def[_]): Option[Def[_]] = d match { - case _: SigmaDslBuilder | _: CollBuilder | _: WSpecialPredefCompanion => Some(d) + case _: SigmaDslBuilder | _: CollBuilder => Some(d) case _ => None } } @@ -161,7 +183,7 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => case _ if env.contains(s) => val (id, tpe) = env(s) ValUse(id, tpe) // recursion base - case Def(Lambda(lam, _, x, y)) => + case Def(Lambda(lam, _, x, _)) => val varId = defId + 1 // arguments are treated as ValDefs and occupy id space val env1 = env + (x -> (varId, elemToSType(x.elem))) val block = processAstGraph(ctx, mainG, env1, lam, varId + 1, constantsProcessing) @@ -170,7 +192,7 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => case Def(Apply(fSym, xSym, _)) => val Seq(f, x) = Seq(fSym, xSym).map(recurse) builder.mkApply(f, Array(x)) - case Def(th @ ThunkDef(root, _)) => + case Def(th @ ThunkDef(_, _)) => val block = processAstGraph(ctx, mainG, env, th, defId, constantsProcessing) block case Def(Const(x)) => @@ -181,8 +203,16 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => .asInstanceOf[ConstantNode[SType]] s.put(constant)(builder) case None => - mkConstant[tpe.type](x.asInstanceOf[tpe.WrappedType], tpe) + if(x.isInstanceOf[CollConst[_, _]]) { // hack used to process NumericToBigEndianBytes only + mkConstant[tpe.type](x.asInstanceOf[CollConst[_, _]].constValue.asInstanceOf[tpe.WrappedType], tpe) + } else { + mkConstant[tpe.type](x.asInstanceOf[tpe.WrappedType], tpe) + } } + case Def(IR.ConstantPlaceholder(id, elem)) => + val tpe = elemToSType(elem) + mkConstantPlaceholder[tpe.type](id, tpe) + case Def(wc: LiftedConst[a,_]) => val tpe = elemToSType(s.elem) mkConstant[tpe.type](wc.constValue.asInstanceOf[tpe.WrappedType], tpe) @@ -190,6 +220,37 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => case Def(IsContextProperty(v)) => v case s if s == sigmaDslBuilder => Global + case Def(ApplyBinOp(op, xSym, ySym)) if op.isInstanceOf[NumericBitwiseOr[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseOrMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + + case Def(ApplyBinOp(op, xSym, ySym)) if op.isInstanceOf[NumericBitwiseAnd[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseAndMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + + case Def(ApplyBinOp(op, xSym, ySym)) if op.isInstanceOf[NumericBitwiseXor[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.BitwiseXorMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + + case Def(ApplyBinOpDiffArgs(op, xSym, ySym)) if op.isInstanceOf[NumericShiftLeft[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.ShiftLeftMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + + case Def(ApplyBinOpDiffArgs(op, xSym, ySym)) if op.isInstanceOf[NumericShiftRight[_]] => + val Seq(x, y) = Seq(xSym, ySym).map(recurse) + val receiverType = x.asNumValue.tpe.asNumTypeOrElse(error(s"Expected numeric type, got: ${x.tpe}")) + val m = SMethod.fromIds(receiverType.typeId, SNumericTypeMethods.ShiftRightMethod.methodId) + builder.mkMethodCall(x.asNumValue, m, IndexedSeq(y)) + + case Def(ApplyBinOp(IsArithOp(opCode), xSym, ySym)) => val Seq(x, y) = Seq(xSym, ySym).map(recurse) mkArith(x.asNumValue, y.asNumValue, opCode) @@ -218,6 +279,13 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => val tpe = elemToSType(eVar) mkGetVar(id, tpe) + case SDBM.deserializeTo(g, bytes, eVar) => + val tpe = elemToSType(eVar) + val typeSubst = Map(tT -> tpe): STypeSubst + // method specialization done to avoid serialization roundtrip issues + val method = SGlobalMethods.deserializeToMethod.withConcreteTypes(typeSubst) + builder.mkMethodCall(recurse(g), method, IndexedSeq(recurse(bytes)), typeSubst) + case BIM.subtract(In(x), In(y)) => mkArith(x.asNumValue, y.asNumValue, MinusCode) case BIM.add(In(x), In(y)) => @@ -288,13 +356,10 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => mkExtractAmount(box.asBox) case BoxM.propositionBytes(In(box)) => mkExtractScriptBytes(box.asBox) - case BoxM.getReg(In(box), regId, _) => + case BoxM.getReg(In(box), regId, _) if regId.isConst => val tpe = elemToSType(s.elem).asOption - if (regId.isConst) - mkExtractRegisterAs(box.asBox, ErgoBox.allRegisters(valueFromRep(regId)), tpe) - else - error(s"Non constant expressions (${regId.node}) are not supported in getReg") - case BoxM.creationInfo(In(box)) => + mkExtractRegisterAs(box.asBox, ErgoBox.allRegisters(valueFromRep(regId)), tpe) + case BoxM.creationInfo(In(box)) => mkExtractCreationInfo(box.asBox) case BoxM.id(In(box)) => mkExtractId(box.asBox) @@ -397,13 +462,14 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement) // Fallback MethodCall rule: should be the last in this list of cases - case Def(MethodCall(objSym, m, argSyms, _)) => + case Def(mc @ MethodCall(objSym, m, argSyms, _)) => val obj = recurse[SType](objSym) val args = argSyms.collect { case argSym: Sym => recurse[SType](argSym) } MethodsContainer.getMethod(obj.tpe, m.getName) match { case Some(method) => - val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)) - builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, Map()) + val typeSubst = mc.typeSubst + val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)).withConcreteTypes(typeSubst) + builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, typeSubst) case None => error(s"Cannot find method ${m.getName} in object $obj") } @@ -449,7 +515,7 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => (valdefs.toArray[BlockItem], rhs) match { // simple optimization to avoid producing block sub-expressions like: // `{ val idNew = id; idNew }` which this rules rewrites to just `id` - case (Array(ValDef(idNew, _, source @ ValUse(id, tpe))), ValUse(idUse, tpeUse)) + case (Array(ValDef(idNew, _, source @ ValUse(_, tpe))), ValUse(idUse, tpeUse)) if idUse == idNew && tpeUse == tpe => source case (items, _) => BlockValue(items, rhs) diff --git a/sc/shared/src/main/scala/scalan/TypeDescs.scala b/sc/shared/src/main/scala/sigma/compiler/ir/TypeDescs.scala similarity index 97% rename from sc/shared/src/main/scala/scalan/TypeDescs.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/TypeDescs.scala index fdef77a108..366107edca 100644 --- a/sc/shared/src/main/scala/scalan/TypeDescs.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TypeDescs.scala @@ -1,18 +1,22 @@ -package scalan +package sigma.compiler.ir -import scala.language.implicitConversions -import scala.annotation.implicitNotFound -import scala.collection.immutable.ListMap - -import scala.collection.mutable import debox.cfor import scalan.core.{Contravariant, Covariant, Variance} import sigma.data.{AVHashMap, Lazy, Nullable, RType} import sigma.reflection.{RClass, RConstructor, RMethod} import sigma.util.CollectionUtil -import special.wrappers.WrapSpec +import sigma.compiler.ir.wrappers.WrapSpec + +import scala.annotation.implicitNotFound +import scala.collection.immutable.ListMap +import scala.collection.mutable +import scala.language.implicitConversions -abstract class TypeDescs extends Base { self: Scalan => +/** Defines [[Elem]] descriptor of types in IRContext together with related utilities. + * @see MethodDesc + * @see TypeDesc + */ +abstract class TypeDescs extends Base { self: IRContext => /** Helper type case method. */ @inline final def asElem[T](d: TypeDesc): Elem[T] = d.asInstanceOf[Elem[T]] @@ -267,7 +271,7 @@ abstract class TypeDescs extends Base { self: Scalan => e.invokeUnlifted(mc, dataEnv) /** Get first (and the only) constructor of the `clazz`. */ - private[scalan] final def getConstructor(clazz: RClass[_]): RConstructor[_] = { + private[compiler] final def getConstructor(clazz: RClass[_]): RConstructor[_] = { val constructors = clazz.getConstructors() if (constructors.length != 1) !!!(s"Element class $clazz has ${constructors.length} constructors, 1 expected") @@ -297,7 +301,7 @@ abstract class TypeDescs extends Base { self: Scalan => protected val elemCache = AVHashMap[RClass[_], ElemCacheEntry](1000) - private[scalan] final def cachedElem0(clazz: RClass[_], optConstructor: Nullable[RConstructor[_]], args: Seq[AnyRef]): Elem[_] = { + private[compiler] final def cachedElem0(clazz: RClass[_], optConstructor: Nullable[RConstructor[_]], args: Seq[AnyRef]): Elem[_] = { val entry = elemCache.get(clazz) match { case Nullable(entry) => entry case _ => diff --git a/sc/shared/src/main/scala/scalan/MutableLazy.scala b/sc/shared/src/main/scala/sigma/compiler/ir/core/MutableLazy.scala similarity index 96% rename from sc/shared/src/main/scala/scalan/MutableLazy.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/core/MutableLazy.scala index 5b6a579476..efaab623a3 100644 --- a/sc/shared/src/main/scala/scalan/MutableLazy.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/core/MutableLazy.scala @@ -1,4 +1,4 @@ -package scalan +package sigma.compiler.ir.core import scala.language.implicitConversions diff --git a/sc/shared/src/main/scala/scalan/core/Variance.scala b/sc/shared/src/main/scala/sigma/compiler/ir/core/Variance.scala similarity index 100% rename from sc/shared/src/main/scala/scalan/core/Variance.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/core/Variance.scala diff --git a/sc/shared/src/main/scala/scalan/ModuleInfo.scala b/sc/shared/src/main/scala/sigma/compiler/ir/meta/ModuleInfo.scala similarity index 90% rename from sc/shared/src/main/scala/scalan/ModuleInfo.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/meta/ModuleInfo.scala index 86fce48ce6..3ccdca18ac 100644 --- a/sc/shared/src/main/scala/scalan/ModuleInfo.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/meta/ModuleInfo.scala @@ -1,6 +1,4 @@ -package scalan - -import scalan.meta.SSymName +package sigma.compiler.ir.meta /** Information about generated Special library module. * Instances are created in generated code. diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/meta/SSymName.scala b/sc/shared/src/main/scala/sigma/compiler/ir/meta/SSymName.scala new file mode 100644 index 0000000000..944b505250 --- /dev/null +++ b/sc/shared/src/main/scala/sigma/compiler/ir/meta/SSymName.scala @@ -0,0 +1,13 @@ +package sigma.compiler.ir.meta + +import sigma.util.StringUtil.StringUtilExtensions + +case class SSymName(packageName: String, name: String) { + import SSymName._ + def mkFullName = fullNameString(packageName, name) +} + +object SSymName { + def fullNameString(packageName: String, name: String): String = + if (packageName.isNullOrEmpty) name else s"$packageName.$name" +} \ No newline at end of file diff --git a/sc/shared/src/main/scala/scalan/primitives/Equal.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/Equal.scala similarity index 84% rename from sc/shared/src/main/scala/scalan/primitives/Equal.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/primitives/Equal.scala index 4d64e94383..ec27fff12b 100644 --- a/sc/shared/src/main/scala/scalan/primitives/Equal.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/Equal.scala @@ -1,10 +1,11 @@ -package scalan.primitives +package sigma.compiler.ir.primitives -import scalan.{Base, Scalan} +import sigma.compiler.ir.{Base, IRContext} import scala.annotation.unused -trait Equal extends Base { self: Scalan => +/** Defines IR representation of equality operations. */ +trait Equal extends Base { self: IRContext => /** Binary operation representing structural equality between arguments. */ case class Equals[A: Elem]() extends BinOp[A, Boolean]("==") { override def applySeq(x: A, y: A): Boolean = equalValues[A](x, y) diff --git a/sc/shared/src/main/scala/scalan/primitives/Functions.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/Functions.scala similarity index 96% rename from sc/shared/src/main/scala/scalan/primitives/Functions.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/primitives/Functions.scala index 31a6ca8d81..cd028de9b6 100644 --- a/sc/shared/src/main/scala/scalan/primitives/Functions.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/Functions.scala @@ -1,27 +1,20 @@ -package scalan.primitives +package sigma.compiler.ir.primitives -import java.util -import scalan.staged.ProgramGraphs -import scalan.{Base, Scalan} -import debox.{Buffer => DBuffer} - -import scala.language.implicitConversions -import debox.cfor +import debox.{cfor, Buffer => DBuffer} +import sigma.compiler.ir.{Base, IRContext, ProgramGraphs} import sigma.data.{Lazy, Nullable, emptyDBufferOfInt} import sigma.util.GraphUtil -trait Functions extends Base with ProgramGraphs { self: Scalan => +import java.util +import scala.language.implicitConversions + +/** Defines IR representation of functions (Lambdas) and function application. */ +trait Functions extends Base with ProgramGraphs { self: IRContext => implicit class LambdaOps[A,B](f: Ref[A => B]) { /** Apply given function symbol to the given argument symbol. * @return symbol representing result of function application */ final def apply(x: Ref[A]): Ref[B] = mkApply(f, x) - - /** Build new function which applies `f` and then `g`*/ - final def >>[C](g: Ref[B => C]): Ref[A => C] = compose(g, f) - - /** Build new function which applies `g` and then `f`*/ - final def <<[C](g: Ref[C => A]): Ref[C => B] = compose(f, g) } /** Global lambda equality mode used by default. It is used in `fun` and `fun2` lambda builders. @@ -151,10 +144,6 @@ trait Functions extends Base with ProgramGraphs { self: Scalan => // emitDepGraph(roots, FileUtil.file(cwd, dir), "nested_lambda")(defaultGraphVizConfig) assert(false, s"Invalid nested lambda $l inside $this") } - case op @ OpCost(_, _, args, opCost) => - if (args.contains(opCost)) { - !!!(s"Invalid OpCost($op)") - } case _ => } } diff --git a/sc/shared/src/main/scala/scalan/primitives/IfThenElse.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/IfThenElse.scala similarity index 90% rename from sc/shared/src/main/scala/scalan/primitives/IfThenElse.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/primitives/IfThenElse.scala index 22fd2095d7..0ce94a83c1 100644 --- a/sc/shared/src/main/scala/scalan/primitives/IfThenElse.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/IfThenElse.scala @@ -1,8 +1,9 @@ -package scalan.primitives +package sigma.compiler.ir.primitives -import scalan.{Base, Scalan} +import sigma.compiler.ir.{Base, IRContext} -trait IfThenElse extends Base { self: Scalan => +/** Defines IR representation of conditional expressions `if (c) t else e`. */ +trait IfThenElse extends Base { self: IRContext => /** If c then t else e construction with standard lazy evaluation of branches. * The representation uses Thunk for each branch */ diff --git a/sc/shared/src/main/scala/scalan/primitives/LogicalOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/LogicalOps.scala similarity index 91% rename from sc/shared/src/main/scala/scalan/primitives/LogicalOps.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/primitives/LogicalOps.scala index e81b546139..183b4a8e84 100644 --- a/sc/shared/src/main/scala/scalan/primitives/LogicalOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/LogicalOps.scala @@ -1,9 +1,9 @@ -package scalan.primitives +package sigma.compiler.ir.primitives -import scalan.{Base, Scalan} +import sigma.compiler.ir.{Base, IRContext} -/** Slice in Scala cake with definitions of logical operations. */ -trait LogicalOps extends Base { self: Scalan => +/** Slice in IRContext cake with definitions of logical operations. */ +trait LogicalOps extends Base { self: IRContext => /** Logical AND binary operation. */ val And = new EndoBinOp[Boolean]("&&") { override def applySeq(x: Boolean, y: Boolean): Boolean = x && y diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/MiscOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/MiscOps.scala new file mode 100644 index 0000000000..58382a279c --- /dev/null +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/MiscOps.scala @@ -0,0 +1,44 @@ +package sigma.compiler.ir.primitives + +import sigma.compiler.ir.{Base, IRContext} + +/** Defines IR representation of miscellaneous operations that doesn't fit into any + * specific category. + */ +trait MiscOps extends Base { self: IRContext => + case class HashCode[A]() extends UnOp[A, Int]("hashCode") { + override def applySeq(x: A): Int = x.hashCode + } + + case class ToString[A]() extends UnOp[A, String]("toString") { + override def applySeq(x: A): String = x.toString + } + + case class Downcast[From, To](input: Ref[From], eTo: Elem[To]) extends BaseDef[To]()(eTo) { + override def transform(t: Transformer) = Downcast(t(input), eTo) + } + case class Upcast[From, To](input: Ref[From], eTo: Elem[To]) extends BaseDef[To]()(eTo) { + override def transform(t: Transformer) = Upcast(t(input), eTo) + } + + def downcast[To:Elem](value: Ref[_]): Ref[To] = Downcast(value, element[To]) + def upcast[To:Elem](value: Ref[_]): Ref[To] = Upcast(value, element[To]) + + implicit class RepUniversalOps[A](x: Ref[A]) { + def hashCodeRep: Ref[Int] = HashCode[A]().apply(x) + def toStringRep = ToString[A]().apply(x) + } + + case class Convert[From,To](eFrom: Elem[From], eTo: Elem[To], x: Ref[Def[_]], conv: Ref[From => To]) + extends BaseDef[To]()(eTo) { + override def transform(t: Transformer) = Convert(eFrom, eTo, t(x), t(conv)) + } + + def tryConvert[From, To](eFrom: Elem[From], eTo: Elem[To], x: Ref[Def[_]], conv: Ref[From => To]): Ref[To] = { + if (x.elem <:< eFrom) + conv(asRep[From](x)) + else + Convert(eFrom, eTo, x, conv) + } + +} diff --git a/sc/shared/src/main/scala/scalan/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala similarity index 63% rename from sc/shared/src/main/scala/scalan/primitives/NumericOps.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala index 3c3d80e94d..5b858d8586 100644 --- a/sc/shared/src/main/scala/scalan/primitives/NumericOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala @@ -1,10 +1,10 @@ -package scalan.primitives +package sigma.compiler.ir.primitives -import scalan.{Base, Scalan} +import sigma.compiler.ir.{Base, IRContext} import sigma.data.{ExactIntegral, ExactNumeric} -/** Slice in Scala cake with definitions of numeric operations. */ -trait NumericOps extends Base { self: Scalan => +/** Slice in IRContext cake with definitions of numeric operations. */ +trait NumericOps extends Base { self: IRContext => /** Extension methods over `Ref[T]` where T is instance of ExactNumeric type-class. */ implicit class NumericOpsCls[T](x: Ref[T])(implicit val n: ExactNumeric[T]) { @@ -14,6 +14,7 @@ trait NumericOps extends Base { self: Scalan => def unary_- : Ref[T] = NumericNegate(n)(x.elem).apply(x) def toInt: Ref[Int] = NumericToInt(n).apply(x) def toLong: Ref[Long] = NumericToLong(n).apply(x) + def toBigEndianBytes: Ref[Coll[Byte]] = NumericToBigEndianBytes(n).apply(x) } /** Extension methods over `Ref[T]` where T is instance of ExactIntegral type-class. */ @@ -46,6 +47,27 @@ trait NumericOps extends Base { self: Scalan => override def applySeq(x: T, y: T): T = n.times(x, y) } + /** Descriptor of unary `ToBits` conversion operation. */ + case class NumericBitwiseOr[T: Elem](n: ExactNumeric[T]) extends EndoBinOp[T]("|") { + override def applySeq(x: T, y: T): T = n.bitwiseOr(x, y) + } + + case class NumericBitwiseAnd[T: Elem](n: ExactNumeric[T]) extends EndoBinOp[T]("&") { + override def applySeq(x: T, y: T): T = n.bitwiseAnd(x, y) + } + + case class NumericBitwiseXor[T: Elem](n: ExactNumeric[T]) extends EndoBinOp[T]("^") { + override def applySeq(x: T, y: T): T = n.bitwiseXor(x, y) + } + + case class NumericShiftLeft[T: Elem](n: ExactNumeric[T]) extends BinDiffArgsOp[T, Int]("<<") { + override def applySeq(x: T, y: Int): T = n.shiftLeft(x, y) + } + + case class NumericShiftRight[T: Elem](n: ExactNumeric[T]) extends BinDiffArgsOp[T, Int](">>") { + override def applySeq(x: T, y: Int): T = n.shiftRight(x, y) + } + /** Base class for descriptors of binary division operations. */ abstract class DivOp[T: Elem](opName: String, n: ExactIntegral[T]) extends EndoBinOp[T](opName) { override def shouldPropagate(lhs: T, rhs: T) = rhs != n.zero @@ -66,6 +88,28 @@ trait NumericOps extends Base { self: Scalan => override def applySeq(x: T): Long = n.toLong(x) } + import Coll._ + /** Descriptor of unary `ToBigEndianBytes` conversion operation. */ + case class NumericToBigEndianBytes[T](n: ExactNumeric[T]) + extends UnOp[T, Coll[Byte]]("ToBigEndianBytes")(element[Coll[Byte]]) { + override def applySeq(x: T): Coll[Byte] = { + liftableColl(Liftables.ByteIsLiftable).lift(n.toBigEndianBytes(x)) + } + } + + /** Descriptor of unary `ToBits` conversion operation. */ + case class NumericToBits[T](n: ExactNumeric[T]) + extends UnOp[T, Coll[Boolean]]("ToBits")(element[Coll[Boolean]]) { + override def applySeq(x: T): Coll[Boolean] = { + liftableColl(Liftables.BooleanIsLiftable).lift(n.toBits(x)) + } + } + + /** Descriptor of unary `BitwiseInverse` conversion operation. */ + case class NumericBitwiseInverse[T: Elem](n: ExactNumeric[T]) extends UnOp[T, T]("~") { + override def applySeq(x: T): T = n.bitwiseInverse(x) + } + /** Descriptor of binary `/` operation (integral division). */ case class IntegralDivide[T](i: ExactIntegral[T])(implicit elem: Elem[T]) extends DivOp[T]("/", i) { override def applySeq(x: T, y: T): T = i.quot(x, y) diff --git a/sc/shared/src/main/scala/scalan/primitives/OrderingOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/OrderingOps.scala similarity index 91% rename from sc/shared/src/main/scala/scalan/primitives/OrderingOps.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/primitives/OrderingOps.scala index 8e6f5e0134..be8f934706 100644 --- a/sc/shared/src/main/scala/scalan/primitives/OrderingOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/OrderingOps.scala @@ -1,12 +1,12 @@ -package scalan.primitives +package sigma.compiler.ir.primitives -import scalan.{Base, Scalan} +import sigma.compiler.ir.{Base, IRContext} import sigma.data.ExactOrdering import scala.language.implicitConversions -/** Slice in Scala cake with definitions of comparison operations. */ -trait OrderingOps extends Base { self: Scalan => +/** Slice in IRContext cake with definitions of comparison operations. */ +trait OrderingOps extends Base { self: IRContext => implicit def repOrderingToOrderingOps[T](x: Ref[T])(implicit n: ExactOrdering[T]) = new OrderingOpsCls(x) implicit def OrderingToOrderingOps[T](x: T)(implicit n: ExactOrdering[T], et: Elem[T]) = new OrderingOpsCls(toRep(x)) diff --git a/sc/shared/src/main/scala/scalan/primitives/Thunks.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/Thunks.scala similarity index 98% rename from sc/shared/src/main/scala/scalan/primitives/Thunks.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/primitives/Thunks.scala index f9f843664d..8674918907 100644 --- a/sc/shared/src/main/scala/scalan/primitives/Thunks.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/Thunks.scala @@ -1,22 +1,22 @@ -package scalan.primitives +package sigma.compiler.ir.primitives -import scala.language.{existentials, implicitConversions} -import scalan._ -import debox.{Buffer => DBuffer, Set => DSet} -import debox.cfor +import debox.{cfor, Buffer => DBuffer, Set => DSet} import scalan.core.Covariant +import sigma.compiler.ir.IRContext import sigma.data.{AVHashMap, DFunc, Lazy, Nullable, RType} import sigma.reflection.RClass import sigma.util.GraphUtil import scala.collection.Seq +import scala.language.{existentials, implicitConversions} -/** Slice in the [[Scalan]] cake with definitions of Thunk operations. +/** Slice in the [[IRContext]] cake with definitions of Thunk operations. * See https://en.wikipedia.org/wiki/Thunk. * Thunks are used to represent lazy operations in the graph IR. + * * @see ApplyBinOpLazy, IfThenElseLazy */ -trait Thunks extends Functions { self: Scalan => +trait Thunks extends Functions { self: IRContext => type Th[+T] = Ref[Thunk[T]] diff --git a/sc/shared/src/main/scala/scalan/primitives/Tuples.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/Tuples.scala similarity index 92% rename from sc/shared/src/main/scala/scalan/primitives/Tuples.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/primitives/Tuples.scala index fd201b9574..59a5d810b9 100644 --- a/sc/shared/src/main/scala/scalan/primitives/Tuples.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/Tuples.scala @@ -2,13 +2,15 @@ * Author: Alexander Slesarenko * Date: 7/25/12 */ -package scalan.primitives +package sigma.compiler.ir.primitives + +import sigma.compiler.ir.{Base, IRContext} +import sigma.data.AVHashMap -import scalan.{Base, Scalan} import scala.language.implicitConversions -import sigma.data.{AVHashMap} -trait Tuples extends Base { self: Scalan => +/** Slice in [[IRContext]] cake with definitions of pairs and tuples. */ +trait Tuples extends Base { self: IRContext => object Pair { def apply[A, B](a: Ref[A], b: Ref[B]) = zipPair[A, B]((a, b)) def unapply[A, B](p: Ref[(A, B)]) = Some(unzipPair[A, B](p)) diff --git a/sc/shared/src/main/scala/scalan/primitives/UnBinOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala similarity index 61% rename from sc/shared/src/main/scala/scalan/primitives/UnBinOps.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala index 749556bf0e..b55596052e 100644 --- a/sc/shared/src/main/scala/scalan/primitives/UnBinOps.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala @@ -1,8 +1,9 @@ -package scalan.primitives +package sigma.compiler.ir.primitives -import scalan.{Scalan, Base} +import sigma.compiler.ir.{Base, IRContext} -trait UnBinOps extends Base { self: Scalan => +/** Defines IR representation of unary and binary operations. */ +trait UnBinOps extends Base { self: IRContext => /** Base class for descriptors of unary operations. */ abstract class UnOp[A, R](val opName: String)(implicit val eResult: Elem[R]) { @@ -44,6 +45,30 @@ trait UnBinOps extends Base { self: Scalan => def shouldPropagate(lhs: A, rhs: A) = true } + /** Base class for descriptors of binary operations where arguments are of different types. */ + abstract class BinDiffArgsOp[A, B](val opName: String)(implicit val eResult: Elem[A]) { + override def toString = opName + + /** Called as part of graph interpretation to execute the given binary operation. + * @param x operation argument + * @param y operation argument + * @return result of applying this operation to (x, y) + */ + def applySeq(x: A, y: B): A + + /** Builds a new graph node by applying this operation to the given arguments. */ + def apply(lhs: Ref[A], rhs: Ref[B]) = ApplyBinOpDiffArgs(this, lhs, rhs) + + /** Builds a new graph node by applying this operation to the given arguments. + * This is a short-cuting (aka lazy) version of the operation, where the lazyness is + * represented by Thunk. + */ + def applyLazy(lhs: Ref[A], rhs: Ref[Thunk[B]]) = ApplyBinOpDiffArgsLazy(this, lhs, rhs) + + /** Whether the constants should be propagated through this operations by rewriting. */ + def shouldPropagate(lhs: A, rhs: B) = true + } + type EndoUnOp[A] = UnOp[A, A] type EndoBinOp[A] = BinOp[A, A] @@ -67,6 +92,19 @@ trait UnBinOps extends Base { self: Scalan => override def transform(t: Transformer): Def[R] = ApplyBinOpLazy[A,R](op, t(lhs), t(rhs)) } + /** Graph node which represents application of the given binary operation to the given arguments of different types + * where the second argument is lazy. */ + case class ApplyBinOpDiffArgsLazy[A, B](op: BinDiffArgsOp[A, B], lhs: Ref[A], rhs: Ref[Thunk[B]]) extends BaseDef[A]()(op.eResult) { + override def toString = s"$lhs $op { $rhs }" + override def transform(t: Transformer): Def[A] = ApplyBinOpDiffArgsLazy[A, B](op, t(lhs), t(rhs)) + } + + /** Graph node which represents application of the given binary operation to the given arguments of different types. */ + case class ApplyBinOpDiffArgs[A, B](op: BinDiffArgsOp[A, B], lhs: Ref[A], rhs: Ref[B]) extends BaseDef[A]()(op.eResult) { + override def toString = s"$op($lhs, $rhs)" + override def transform(t: Transformer): Def[A] = ApplyBinOpDiffArgs[A, B](op, t(lhs), t(rhs)) + } + /** Overridable constructor of an unary operation node. */ def applyUnOp[A, R](op: UnOp[A, R], arg: Ref[A]): Ref[R] = ApplyUnOp(op, arg) diff --git a/sc/shared/src/main/scala/special/wrappers/WrapSpec.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/WrapSpec.scala similarity index 76% rename from sc/shared/src/main/scala/special/wrappers/WrapSpec.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/wrappers/WrapSpec.scala index dd07f1144c..b9c21227b2 100644 --- a/sc/shared/src/main/scala/special/wrappers/WrapSpec.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/WrapSpec.scala @@ -1,4 +1,4 @@ -package special.wrappers +package sigma.compiler.ir.wrappers /** Base type for all wrapper specification classes. * @see OptionWrapSpec as an example */ diff --git a/sc/shared/src/main/scala/special/wrappers/WrappersSpec.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/WrappersSpec.scala similarity index 91% rename from sc/shared/src/main/scala/special/wrappers/WrappersSpec.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/wrappers/WrappersSpec.scala index d520ce9006..4c0c5f05eb 100644 --- a/sc/shared/src/main/scala/special/wrappers/WrappersSpec.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/WrappersSpec.scala @@ -1,7 +1,5 @@ -package special.wrappers - -import sigma.data.RType - +package sigma.compiler.ir.wrappers +import _root_.sigma.data.RType /** Base class for wrappers of such types as WOption, WRType etc. * Used in graph IR to implement method invocation. */ diff --git a/sc/shared/src/main/scala/wrappers/scala/WOptions.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/scala/WOptions.scala similarity index 64% rename from sc/shared/src/main/scala/wrappers/scala/WOptions.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/wrappers/scala/WOptions.scala index 4bd314c1de..2ed1237e6b 100644 --- a/sc/shared/src/main/scala/wrappers/scala/WOptions.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/scala/WOptions.scala @@ -1,9 +1,8 @@ -package wrappers.scala { - import scalan._ +package sigma.compiler.ir.wrappers.scala { + import sigma.compiler.ir.{Base, IRContext} - import special.wrappers.WrappersModule - - trait WOptions extends Base { self: WrappersModule => + /** Defines IR representation of Option type. */ + trait WOptions extends Base { self: IRContext => trait WOption[A] extends Def[WOption[A]] { implicit def eA: Elem[A]; def isDefined: Ref[Boolean]; diff --git a/sc/shared/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/scala/impl/WOptionsImpl.scala similarity index 93% rename from sc/shared/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/wrappers/scala/impl/WOptionsImpl.scala index 45d0a91ad6..514415d2bd 100644 --- a/sc/shared/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/scala/impl/WOptionsImpl.scala @@ -1,18 +1,21 @@ -package wrappers.scala +package sigma.compiler.ir.wrappers.scala + +import scala.language.{existentials, implicitConversions} +import sigma.compiler.ir.IRContext +import sigma.compiler.ir.wrappers.OptionWrapSpec -import scala.language.{existentials,implicitConversions} -import scalan._ -import special.wrappers.WrappersModule -import special.wrappers.OptionWrapSpec import scala.collection.compat.immutable.ArraySeq package impl { + import sigma.compiler.ir.meta.ModuleInfo + import sigma.compiler.ir.{Base, GraphIRReflection, IRContext} import sigma.data.{Nullable, RType} import sigma.reflection.{RClass, RMethod} // Abs ----------------------------------- -trait WOptionsDefs extends scalan.Scalan with WOptions { - self: WrappersModule => +/** IR representation of Option type and methods. */ +trait WOptionsDefs extends Base with WOptions { + self: IRContext => class WOptionCls extends EntityObject("WOption") { // entityConst: single const for each entity @@ -235,9 +238,9 @@ class WOptionCls extends EntityObject("WOption") { registerEntityObject("WOption", WOption) } -object WOptionsModule extends scalan.ModuleInfo("wrappers.scala", "WOptions") { +object WOptionsModule extends ModuleInfo("sigma.compiler.ir.wrappers.scala", "WOptions") { val reflection = GraphIRReflection } } -trait WOptionsModule extends wrappers.scala.impl.WOptionsDefs {self: WrappersModule =>} +trait WOptionsModule extends sigma.compiler.ir.wrappers.scala.impl.WOptionsDefs {self: IRContext =>} diff --git a/sc/shared/src/main/scala/special/collection/CollsUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala similarity index 83% rename from sc/shared/src/main/scala/special/collection/CollsUnit.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala index 19ae1f5e48..fdc9fadcba 100644 --- a/sc/shared/src/main/scala/special/collection/CollsUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala @@ -1,5 +1,6 @@ -package sigma { - import scalan._ +package sigma.compiler.ir.wrappers.sigma + +import sigma.compiler.ir.{Base, IRContext} /** Staged version of collection interfaces which is used in graph-based IR to represent * methods of Coll and CollBuilder. @@ -8,11 +9,12 @@ package sigma { * The semantics of each method is the same as in the original class, please look there * for details. */ - trait Colls extends Base { self: Library => + trait Colls extends Base { self: IRContext => trait Coll[A] extends Def[Coll[A]] { 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]]]; @@ -28,13 +30,14 @@ package sigma { 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]]; def xor(left: Ref[Coll[Byte]], right: Ref[Coll[Byte]]): Ref[Coll[Byte]]; def replicate[T](n: Ref[Int], v: Ref[T]): Ref[Coll[T]]; }; - trait CollCompanion; - trait CollBuilderCompanion - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/sc/shared/src/main/scala/special/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala similarity index 73% rename from sc/shared/src/main/scala/special/sigma/SigmaDslUnit.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index 48548226a5..84c43f14dd 100644 --- a/sc/shared/src/main/scala/special/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -1,8 +1,10 @@ -package sigma { - import scalan._ - import sigmastate.eval.SigmaLibrary +package sigma.compiler.ir.wrappers.sigma - trait SigmaDsl extends Base { self: SigmaLibrary => +import scalan._ + import sigma.compiler.ir.{Base, IRContext} + + /** IR representation of ErgoScript (Sigma) language types and methods. */ + trait SigmaDsl extends Base { self: IRContext => trait BigInt extends Def[BigInt] { def add(that: Ref[BigInt]): Ref[BigInt]; def subtract(that: Ref[BigInt]): Ref[BigInt]; @@ -11,9 +13,26 @@ package sigma { 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]] @@ -76,6 +95,7 @@ package sigma { def powNonce: Ref[Coll[Byte]]; def powDistance: Ref[BigInt]; def votes: Ref[Coll[Byte]] + def checkPow: Ref[Boolean] }; trait Context extends Def[Context] { def OUTPUTS: Ref[Coll[Box]]; @@ -89,6 +109,7 @@ package sigma { 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]; @@ -112,6 +133,14 @@ package sigma { /** 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 encodeNbits(bi: Ref[BigInt]): Ref[Long] + def decodeNbits(l: Ref[Long]): Ref[BigInt] + def powHit(k: Ref[Int], msg: Ref[Coll[Byte]], nonce: Ref[Coll[Byte]], h: Ref[Coll[Byte]], N: Ref[Int]): Ref[BigInt]; + def serialize[T](value: Ref[T]): Ref[Coll[Byte]] + def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] + def deserializeTo[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] + def some[T](value: Ref[T])(implicit cT: Elem[T]): Ref[WOption[T]] + def none[T]()(implicit cT: Elem[T]): Ref[WOption[T]] }; trait CostModelCompanion; trait BigIntCompanion; @@ -124,5 +153,4 @@ package sigma { trait ContextCompanion; trait SigmaContractCompanion; trait SigmaDslBuilderCompanion - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/WRTypes.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/WRTypes.scala new file mode 100644 index 0000000000..0bd2b47e5b --- /dev/null +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/WRTypes.scala @@ -0,0 +1,13 @@ +package sigma.compiler.ir.wrappers.sigma + +import sigma.compiler.ir.{Base, IRContext} + + + /** IR representation of RType. */ + trait WRTypes extends Base { self: IRContext => + trait WRType[A] extends Def[WRType[A]] { + implicit def eA: Elem[A]; + def name: Ref[String] + }; + trait WRTypeCompanion + } \ No newline at end of file diff --git a/sc/shared/src/main/scala/special/collection/impl/CollsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala similarity index 90% rename from sc/shared/src/main/scala/special/collection/impl/CollsImpl.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala index 239bfff4e5..04a7070acf 100644 --- a/sc/shared/src/main/scala/special/collection/impl/CollsImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala @@ -1,16 +1,21 @@ -package sigma +package sigma.compiler.ir.wrappers.sigma + +import scala.language.{existentials, implicitConversions} +import sigma.compiler.ir.IRContext +import sigma.compiler.ir.wrappers.sigma.impl.CollsDefs -import scala.language.{existentials,implicitConversions} -import scalan._ import scala.collection.compat.immutable.ArraySeq package impl { + import sigma.compiler.ir.meta.ModuleInfo + import sigma.compiler.ir.{Base, GraphIRReflection, IRContext} import sigma.data.{Nullable, RType} import sigma.reflection.{RClass, RMethod} - // Abs ----------------------------------- -trait CollsDefs extends scalan.Scalan with Colls { - self: Library => +// Abs ----------------------------------- +/** Implementation part of IR represenation related to Coll type and methods. */ +trait CollsDefs extends Base with Colls { + self: IRContext => registerModule(CollsModule) @@ -58,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, @@ -159,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]) @@ -205,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, @@ -306,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 @@ -634,9 +710,9 @@ object CollBuilder extends EntityObject("CollBuilder") { } -object CollsModule extends scalan.ModuleInfo("sigma", "Colls") { +object CollsModule extends ModuleInfo("sigma", "Colls") { val reflection = GraphIRReflection } } -trait CollsModule extends sigma.impl.CollsDefs {self: Library =>} +trait CollsModule extends CollsDefs {self: IRContext =>} diff --git a/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala similarity index 81% rename from sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index 037505b531..695f503246 100644 --- a/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -1,19 +1,25 @@ -package sigma +package sigma.compiler.ir.wrappers.sigma import scala.language.{existentials, implicitConversions} import scalan._ -import sigmastate.eval.SigmaLibrary +import sigma.compiler.ir.IRContext +import sigma.compiler.ir.wrappers.sigma.impl.SigmaDslDefs import scala.collection.compat.immutable.ArraySeq package impl { + import sigma.Evaluation + import sigma.ast.SType.tT + import sigma.compiler.ir.meta.ModuleInfo + import sigma.compiler.ir.wrappers.sigma.SigmaDsl + import sigma.compiler.ir.{Base, GraphIRReflection, IRContext} import sigma.data.{Nullable, RType} import sigma.reflection.{RClass, RMethod} - import sigmastate.eval.SigmaLibrary +/** Implementation part of IR represenation related to Sigma types and methods. */ // Abs ----------------------------------- -trait SigmaDslDefs extends scalan.Scalan with SigmaDsl { - self: SigmaLibrary => +trait SigmaDslDefs extends Base with SigmaDsl { + self: IRContext => registerModule(SigmaDslModule) @@ -93,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 @@ -161,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 @@ -178,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" )) } } @@ -263,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._ @@ -287,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]), @@ -334,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]), @@ -616,10 +898,11 @@ object Box extends EntityObject("Box") { } override def getReg[T](i: Ref[Int])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) asRep[WOption[T]](mkMethodCall(self, BoxClass.getMethod("getReg", classOf[Sym], classOf[Elem[_]]), Array[AnyRef](i, cT), - true, false, element[WOption[T]])) + true, false, element[WOption[T]], Map(tT -> st) )) } override def tokens: Ref[Coll[(Coll[Byte], Long)]] = { @@ -691,10 +974,11 @@ object Box extends EntityObject("Box") { } def getReg[T](i: Ref[Int])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) asRep[WOption[T]](mkMethodCall(source, BoxClass.getMethod("getReg", classOf[Sym], classOf[Elem[_]]), Array[AnyRef](i, cT), - true, true, element[WOption[T]])) + true, true, element[WOption[T]], Map(tT -> st))) } def tokens: Ref[Coll[(Coll[Byte], Long)]] = { @@ -1364,6 +1648,14 @@ object Header extends EntityObject("Header") { ArraySeq.empty, true, false, element[Coll[Byte]])) } + + override def checkPow: Ref[Boolean] = { + asRep[Boolean](mkMethodCall(self, + HeaderClass.getMethod("checkPow"), + ArraySeq.empty, + true, false, element[Boolean])) + } + } implicit object LiftableHeader @@ -1488,6 +1780,13 @@ object Header extends EntityObject("Header") { ArraySeq.empty, true, true, element[Coll[Byte]])) } + + def checkPow: Ref[Boolean] = { + asRep[Boolean](mkMethodCall(source, + HeaderClass.getMethod("checkPow"), + ArraySeq.empty, + true, true, element[Boolean])) + } } // entityUnref: single unref method for each type family @@ -1505,7 +1804,7 @@ object Header extends EntityObject("Header") { override protected def collectMethods: Map[RMethod, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[Header]), RClass(classOf[SHeader]), Set( - "id", "version", "parentId", "ADProofsRoot", "stateRoot", "transactionsRoot", "timestamp", "nBits", "height", "extensionRoot", "minerPk", "powOnetimePk", "powNonce", "powDistance", "votes" + "id", "version", "parentId", "ADProofsRoot", "stateRoot", "transactionsRoot", "timestamp", "nBits", "height", "extensionRoot", "minerPk", "powOnetimePk", "powNonce", "powDistance", "votes", "checkPow" )) } } @@ -1604,10 +1903,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))) } } @@ -1701,10 +2009,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))) } } @@ -1723,7 +2040,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" )) } } @@ -1915,7 +2232,6 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { } override def substConstants[T](scriptBytes: Ref[Coll[Byte]], positions: Ref[Coll[Int]], newValues: Ref[Coll[T]]): Ref[Coll[Byte]] = { - implicit val eT = newValues.eA asRep[Coll[Byte]](mkMethodCall(self, SigmaDslBuilderClass.getMethod("substConstants", classOf[Sym], classOf[Sym], classOf[Sym]), Array[AnyRef](scriptBytes, positions, newValues), @@ -1942,6 +2258,62 @@ 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 deserializeTo[T](l: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = { + asRep[T](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("deserializeTo", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](l, cT), + true, false, element[T](cT), Map(tT -> Evaluation.rtypeToSType(cT.sourceType)))) + } + override def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = { + asRep[T](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](bytes, cT), + true, false, cT, Map(tT -> Evaluation.rtypeToSType(cT.sourceType)))) + } + + override def some[T](value: Ref[T])(implicit cT: Elem[T]): Ref[WOption[T]] = { + asRep[WOption[T]](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("some", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](value, cT), + true, false, element[WOption[T]], Map(tT -> Evaluation.rtypeToSType(cT.sourceType)))) + } + + override def none[T]()(implicit cT: Elem[T]): Ref[WOption[T]] = { + asRep[WOption[T]](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("none", classOf[Elem[T]]), + Array[AnyRef](cT), + true, false, element[WOption[T]], Map(tT -> Evaluation.rtypeToSType(cT.sourceType)))) + } + + + override def powHit(k: Ref[Int], msg: Ref[Coll[Byte]], nonce: Ref[Coll[Byte]], h: Ref[Coll[Byte]], N: Ref[Int]): Ref[BigInt] = { + asRep[BigInt](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("powHit", classOf[Sym], classOf[Sym], classOf[Sym], classOf[Sym], classOf[Sym]), + Array[AnyRef](k, msg, nonce, h, N), + true, false, element[BigInt])) + } + + override def encodeNbits(bi: Ref[BigInt]): Ref[Long] = { + asRep[Long](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("encodeNbits", classOf[Sym]), + Array[AnyRef](bi), + true, false, element[Long])) + } + + override def decodeNbits(l: Ref[Long]): Ref[BigInt] = { + asRep[BigInt](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("decodeNbits", classOf[Sym]), + Array[AnyRef](l), + true, false, element[BigInt])) + } } implicit object LiftableSigmaDslBuilder @@ -2075,7 +2447,6 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { } def substConstants[T](scriptBytes: Ref[Coll[Byte]], positions: Ref[Coll[Int]], newValues: Ref[Coll[T]]): Ref[Coll[Byte]] = { - implicit val eT = newValues.eA asRep[Coll[Byte]](mkMethodCall(source, SigmaDslBuilderClass.getMethod("substConstants", classOf[Sym], classOf[Sym], classOf[Sym]), Array[AnyRef](scriptBytes, positions, newValues), @@ -2102,6 +2473,63 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { Array[AnyRef](l, r), true, true, element[Coll[Byte]])) } + + def powHit(k: Ref[Int], msg: Ref[Coll[Byte]], nonce: Ref[Coll[Byte]], h: Ref[Coll[Byte]], N: Ref[Int]): Ref[BigInt] = { + asRep[BigInt](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("powHit", classOf[Sym], classOf[Sym], classOf[Sym], classOf[Sym], classOf[Sym]), + Array[AnyRef](k, msg, nonce, h, N), + true, true, element[BigInt])) + } + + 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 deserializeTo[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = { + asRep[T](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("deserializeTo", classOf[Sym], classOf[Elem[_]]), + Array[AnyRef](bytes, cT), + true, true, element[T](cT), Map(tT -> Evaluation.rtypeToSType(cT.sourceType)))) + } + + 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)))) + } + + def some[T](value: Ref[T])(implicit cT: Elem[T]): Ref[WOption[T]] = { + asRep[WOption[T]](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("some", classOf[Sym], classOf[Elem[T]]), + Array[AnyRef](value, cT), + true, true, element[WOption[T]], Map(tT -> Evaluation.rtypeToSType(cT.sourceType)))) + } + + def none[T]()(implicit cT: Elem[T]): Ref[WOption[T]] = { + asRep[WOption[T]](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("none", classOf[Elem[T]]), + Array[AnyRef](cT), + true, true, element[WOption[T]], Map(tT -> Evaluation.rtypeToSType(cT.sourceType)))) + } + + + override def encodeNbits(bi: Ref[BigInt]): Ref[Long] = { + asRep[Long](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("encodeNbits", classOf[Sym]), + Array[AnyRef](bi), + true, true, element[Long])) + } + + override def decodeNbits(l: Ref[Long]): Ref[BigInt] = { + asRep[BigInt](mkMethodCall(source, + SigmaDslBuilderClass.getMethod("decodeNbits", classOf[Sym]), + Array[AnyRef](l), + true, true, element[BigInt])) + } } // entityUnref: single unref method for each type family @@ -2119,7 +2547,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", "encodeNBits", "decodeNBits", "serialize", "fromBigEndianBytes", "powHit", "deserializeTo" )) } } @@ -2278,16 +2708,6 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { def unapply(exp: Sym): Nullable[(Ref[SigmaDslBuilder], Ref[GroupElement], Ref[GroupElement], Ref[GroupElement], Ref[GroupElement])] = unapply(exp.node) } - object groupGenerator { - def unapply(d: Def[_]): Nullable[Ref[SigmaDslBuilder]] = d match { - case MethodCall(receiver, method, _, _) if method.getName == "groupGenerator" && receiver.elem.isInstanceOf[SigmaDslBuilderElem[_]] => - val res = receiver - Nullable(res).asInstanceOf[Nullable[Ref[SigmaDslBuilder]]] - case _ => Nullable.None - } - def unapply(exp: Sym): Nullable[Ref[SigmaDslBuilder]] = unapply(exp.node) - } - object substConstants { def unapply(d: Def[_]): Nullable[(Ref[SigmaDslBuilder], Ref[Coll[Byte]], Ref[Coll[Int]], Ref[Coll[T]]) forSome {type T}] = d match { case MethodCall(receiver, method, args, _) if method.getName == "substConstants" && receiver.elem.isInstanceOf[SigmaDslBuilderElem[_]] => @@ -2308,6 +2728,16 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { def unapply(exp: Sym): Nullable[(Ref[SigmaDslBuilder], Ref[Coll[Byte]])] = unapply(exp.node) } + object deserializeTo { + def unapply(d: Def[_]): Nullable[(Ref[SigmaDslBuilder], Ref[Coll[Byte]], Elem[T]) forSome {type T}] = d match { + case MethodCall(receiver, method, args, _) if method.getName == "deserializeTo" && receiver.elem.isInstanceOf[SigmaDslBuilderElem[_]] => + val res = (receiver, args(0), args(1)) + Nullable(res).asInstanceOf[Nullable[(Ref[SigmaDslBuilder], Ref[Coll[Byte]], Elem[T]) forSome {type T}]] + case _ => Nullable.None + } + def unapply(exp: Sym): Nullable[(Ref[SigmaDslBuilder], Ref[Coll[Byte]], Elem[T]) forSome {type T}] = unapply(exp.node) + } + /** This is necessary to handle CreateAvlTree in GraphBuilding (v6.0) */ object avlTree { def unapply(d: Def[_]): Nullable[(Ref[SigmaDslBuilder], Ref[Byte], Ref[Coll[Byte]], Ref[Int], Ref[WOption[Int]])] = d match { @@ -2333,9 +2763,9 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { registerEntityObject("SigmaDslBuilder", SigmaDslBuilder) } -object SigmaDslModule extends scalan.ModuleInfo("sigma", "SigmaDsl") { +object SigmaDslModule extends ModuleInfo("sigma", "SigmaDsl") { val reflection = GraphIRReflection } } -trait SigmaDslModule extends sigma.impl.SigmaDslDefs {self: SigmaLibrary =>} +trait SigmaDslModule extends SigmaDslDefs {self: IRContext =>} diff --git a/sc/shared/src/main/scala/wrappers/scalan/impl/WRTypesImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/WRTypesImpl.scala similarity index 84% rename from sc/shared/src/main/scala/wrappers/scalan/impl/WRTypesImpl.scala rename to sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/WRTypesImpl.scala index 9b2676f70d..beddba3085 100644 --- a/sc/shared/src/main/scala/wrappers/scalan/impl/WRTypesImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/WRTypesImpl.scala @@ -1,18 +1,22 @@ -package wrappers.scalan +package sigma.compiler.ir.wrappers.sigma -import scala.language.{existentials,implicitConversions} +import sigma.compiler.ir.IRContext +import scala.language.{existentials, implicitConversions} import sigma.data.RType -import special.wrappers.WrappersModule -import special.wrappers.RTypeWrapSpec +import sigma.compiler.ir.wrappers.RTypeWrapSpec + import scala.collection.compat.immutable.ArraySeq package impl { - import scalan.GraphIRReflection + import sigma.compiler.ir.meta.ModuleInfo + import sigma.compiler.ir.wrappers.sigma.WRTypes + import sigma.compiler.ir.{Base, GraphIRReflection, IRContext} import sigma.reflection.{RClass, RMethod} +/** Implementation of IR nodes related to [[RType]]. */ // Abs ----------------------------------- -trait WRTypesDefs extends scalan.Scalan with WRTypes { - self: WrappersModule => +trait WRTypesDefs extends Base with WRTypes { + self: IRContext => registerModule(WRTypesModule) @@ -111,9 +115,9 @@ registerEntityObject("WRType", WRType) } -object WRTypesModule extends scalan.ModuleInfo("wrappers.scalan", "WRTypes") { +object WRTypesModule extends ModuleInfo("wrappers.scalan", "WRTypes") { val reflection = GraphIRReflection } } -trait WRTypesModule extends wrappers.scalan.impl.WRTypesDefs {self: WrappersModule =>} +trait WRTypesModule extends sigma.compiler.ir.wrappers.sigma.impl.WRTypesDefs {self: IRContext =>} diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala similarity index 95% rename from sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala rename to sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala index f93e31703d..d4943ef892 100644 --- a/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala @@ -1,7 +1,6 @@ -package sigmastate.lang +package sigma.compiler.phases import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix -import org.ergoplatform._ import sigma.ast.NoType import sigma.data.Nullable import sigma.kiama.rewriting.CallbackRewriter @@ -106,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/sigmastate/lang/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala similarity index 89% rename from sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala rename to sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index 7fd6dadd45..7d31b91c1e 100644 --- a/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -1,24 +1,26 @@ -package sigmastate.lang +package sigma.compiler.phases -import org.ergoplatform._ import sigma.ast.SCollection.{SBooleanArray, SByteArray} +import sigma.ast.SigmaPredef._ import sigma.ast._ -import sigma.ast.syntax.SValue +import sigma.ast.syntax.{SValue, _} import sigma.data.{Nullable, SigmaBoolean} -import sigma.util.Extensions.Ensuring -import sigmastate.exceptions._ -import SigmaPredef._ -import sigma.ast.syntax._ import sigma.exceptions.TyperException import sigma.serialization.OpCodes +import sigma.util.Extensions.Ensuring +import sigmastate.exceptions._ import scala.collection.mutable.ArrayBuffer -/** - * Type inference and analysis for Sigma expressions. +/** Type inference and analysis for Sigma expressions. + * @param builder SigmaBuilder instance to create new nodes + * @param predefFuncRegistry predefined functions registry used to resolve names + * @param typeEnv environment with types of variables/names + * @param lowerMethodCalls if true, then MethodCall nodes are lowered to the corresponding ErgoTree nodes */ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRegistry, + typeEnv: Map[String, SType], lowerMethodCalls: Boolean) { import SigmaTyper._ import builder._ @@ -28,18 +30,21 @@ class SigmaTyper(val builder: SigmaBuilder, import SType.tT - private val predefinedEnv: Map[String, SType] = - predefFuncRegistry.funcs.map { case (k, f) => k -> f.declaration.tpe }.toMap + private val predefinedEnv: Map[String, SType] = { + val predefFuncs = predefFuncRegistry.funcs.map { case (k, f) => k -> f.declaration.tpe }.toMap + predefFuncs ++ typeEnv + } private def processGlobalMethod(srcCtx: Nullable[SourceContext], method: SMethod, - args: IndexedSeq[SValue]) = { + args: IndexedSeq[SValue], + subst: Map[STypeVar, SType] = EmptySubst): SValue = { val global = Global.withPropagatedSrcCtx(srcCtx) val node = for { pf <- method.irInfo.irBuilder if lowerMethodCalls - res <- pf.lift((builder, global, method, args, EmptySubst)) + res <- pf.lift((builder, global, method, args, subst)) } yield res - node.getOrElse(mkMethodCall(global, method, args, EmptySubst).withPropagatedSrcCtx(srcCtx)) + node.getOrElse(mkMethodCall(global, method, args, subst).withPropagatedSrcCtx(srcCtx)) } /** * Rewrite tree to typed tree. Checks constituent names and types. Uses @@ -130,12 +135,24 @@ 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, _)) - obj.tpe match { + val newArgs = nArgs.map(assignType(env, _)) + newObj.tpe match { case p: SProduct => MethodsContainer.getMethod(p, n) match { - case Some(method @ SMethod(_, _, genFunTpe @ SFunc(_, _, _), _, _, _, _, _)) => + case Some(method: SMethod) => + val genFunTpe = method.stype val subst = Map(genFunTpe.tpeParams.head.ident -> rangeTpe) val concrFunTpe = applySubst(genFunTpe, subst) val expectedArgs = concrFunTpe.asFunc.tDom.tail @@ -150,20 +167,31 @@ 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) case None => - throw new MethodNotFound(s"Cannot find method '$n' in in the object $obj of Product type $p", obj.sourceContext.toOption) + throw new MethodNotFound(s"Cannot find method '$n' in in the object $newObj of Product type $p", newObj.sourceContext.toOption) } case _ => - error(s"Cannot get field '$n' in in the object $obj of non-product type ${obj.tpe}", sel.sourceContext) + 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. @@ -216,6 +244,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) @@ -404,11 +437,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) @@ -507,6 +535,7 @@ class SigmaTyper(val builder: SigmaBuilder, case v: SigmaBoolean => v case v: Upcast[_, _] => v case v @ Select(_, _, Some(_)) => v + case v @ MethodCall(_, _, _, _) => v case v => error(s"Don't know how to assignType($v)", v.sourceContext) }).ensuring(v => v.tpe != NoType, diff --git a/sc/shared/src/main/scala/sigmastate/eval/IRContext.scala b/sc/shared/src/main/scala/sigmastate/eval/IRContext.scala deleted file mode 100644 index b84098f2e7..0000000000 --- a/sc/shared/src/main/scala/sigmastate/eval/IRContext.scala +++ /dev/null @@ -1,65 +0,0 @@ -package sigmastate.eval - -import sigma.ast.TransformingSigmaBuilder -import sigma.data.CSigmaDslBuilder - -import java.util.concurrent.locks.ReentrantLock -import scala.util.Try - -/** Main interface of graph IR context which contain both GraphBuilding and TreeBuilding - * methods. - * It is not used in v5.0 interpreter and thus not part of consensus. - * - * @see RuntimeIRContext, CompiletimeIRContext - */ -trait IRContext extends TreeBuilding with GraphBuilding { - import SigmaProp._ - - private val SigmaM = SigmaPropMethods - - override val builder = TransformingSigmaBuilder - - /** Can be used to synchronize access to this IR object from multiple threads. */ - val lock = new ReentrantLock() - - /** Pass configuration which is used to turn-off constant propagation. - * @see `beginPass(noCostPropagationPass)` */ - lazy val noConstPropagationPass = new DefaultPass( - "noCostPropagationPass", - Pass.defaultPassConfig.copy(constantPropagation = false)) - - /** The value of Global ErgoTree operation */ - val sigmaDslBuilderValue = CSigmaDslBuilder - - /** Finds SigmaProp.isProven method calls in the given Lambda `f` */ - def findIsProven[T](f: Ref[Context => T]): Option[Sym] = { - val Def(Lambda(lam,_,_,_)) = f - val s = lam.flatSchedule.find(sym => sym.node match { - case SigmaM.isValid(_) => true - case _ => false - }) - s - } - - /** Checks that if SigmaProp.isProven method calls exists in the given Lambda's schedule, - * then it is the last operation. */ - def verifyIsProven[T](f: Ref[Context => T]): Try[Unit] = { - val isProvenOpt = findIsProven(f) - Try { - isProvenOpt match { - case Some(s) => - if (f.getLambda.y != s) !!!(s"Sigma.isProven found in none-root position", s) - case None => - } - } - } -} - -/** IR context to be used by blockchain nodes to validate transactions. */ -class RuntimeIRContext extends IRContext { -} - -/** IR context to be used by script development tools to compile ErgoScript into ErgoTree bytecode. */ -class CompiletimeIRContext extends IRContext { -} - diff --git a/sc/shared/src/main/scala/sigmastate/eval/SigmaLibrary.scala b/sc/shared/src/main/scala/sigmastate/eval/SigmaLibrary.scala deleted file mode 100644 index be17179cbf..0000000000 --- a/sc/shared/src/main/scala/sigmastate/eval/SigmaLibrary.scala +++ /dev/null @@ -1,17 +0,0 @@ -package sigmastate.eval - -import scalan.Library -import sigma.SigmaDslModule - -/** Main trait which represents sigma operations in graph IR cake. */ -trait SigmaLibrary extends Library - with sigma.wrappers.WrappersModule - with SigmaDslModule -{ - import WRType._ - - implicit lazy val wRTypeAnyElement = wRTypeElement(AnyElement) - - /** During compilation represent a global value Global, see also SGlobal type. */ - def sigmaDslBuilder: Ref[SigmaDslBuilder] -} diff --git a/sc/shared/src/main/scala/special/sigma/wrappers/WrappersModule.scala b/sc/shared/src/main/scala/special/sigma/wrappers/WrappersModule.scala deleted file mode 100644 index 6696788621..0000000000 --- a/sc/shared/src/main/scala/special/sigma/wrappers/WrappersModule.scala +++ /dev/null @@ -1,4 +0,0 @@ -package sigma.wrappers - -trait WrappersModule - extends special.wrappers.WrappersModule diff --git a/sc/shared/src/main/scala/special/wrappers/WrappersModule.scala b/sc/shared/src/main/scala/special/wrappers/WrappersModule.scala deleted file mode 100644 index b74bec314e..0000000000 --- a/sc/shared/src/main/scala/special/wrappers/WrappersModule.scala +++ /dev/null @@ -1,10 +0,0 @@ -package special.wrappers - -import wrappers.scala.WOptionsModule -import wrappers.scalan.WRTypesModule -import wrappers.special.WSpecialPredefsModule - -trait WrappersModule - extends WSpecialPredefsModule - with WOptionsModule - with WRTypesModule \ No newline at end of file diff --git a/sc/shared/src/main/scala/wrappers/scalan/WRTypes.scala b/sc/shared/src/main/scala/wrappers/scalan/WRTypes.scala deleted file mode 100644 index 8c6bc48f8e..0000000000 --- a/sc/shared/src/main/scala/wrappers/scalan/WRTypes.scala +++ /dev/null @@ -1,13 +0,0 @@ -package wrappers.scalan { - import scalan._ - import special.wrappers.WrappersModule - - - trait WRTypes extends Base { self: WrappersModule => - trait WRType[A] extends Def[WRType[A]] { - implicit def eA: Elem[A]; - def name: Ref[String] - }; - trait WRTypeCompanion - } -} \ No newline at end of file diff --git a/sc/shared/src/main/scala/wrappers/special/WSpecialPredefs.scala b/sc/shared/src/main/scala/wrappers/special/WSpecialPredefs.scala deleted file mode 100644 index 5abbab95e2..0000000000 --- a/sc/shared/src/main/scala/wrappers/special/WSpecialPredefs.scala +++ /dev/null @@ -1,12 +0,0 @@ -package wrappers.special { - import scalan._ - - import special.wrappers.WrappersModule - - trait WSpecialPredefs extends Base { self: WrappersModule => - trait WSpecialPredef extends Def[WSpecialPredef]; - trait WSpecialPredefCompanion { - def some[A](x: Ref[A]): Ref[WOption[A]]; - } - } -} \ No newline at end of file diff --git a/sc/shared/src/main/scala/wrappers/special/impl/WSpecialPredefsImpl.scala b/sc/shared/src/main/scala/wrappers/special/impl/WSpecialPredefsImpl.scala deleted file mode 100644 index 1f80e71676..0000000000 --- a/sc/shared/src/main/scala/wrappers/special/impl/WSpecialPredefsImpl.scala +++ /dev/null @@ -1,69 +0,0 @@ -package wrappers.special - -import scala.language.{existentials,implicitConversions} -import scalan._ -import special.wrappers.WrappersModule - -package impl { - import sigma.data.Nullable - import sigma.reflection.RClass - - // Abs ----------------------------------- -trait WSpecialPredefsDefs extends scalan.Scalan with WSpecialPredefs { - self: WrappersModule => - - registerModule(WSpecialPredefsModule) - -import WOption._ -import WSpecialPredef._ - -object WSpecialPredef extends EntityObject("WSpecialPredef") { - - implicit case object WSpecialPredefCompanionElem extends CompanionElem[WSpecialPredefCompanionCtor] - - abstract class WSpecialPredefCompanionCtor extends CompanionDef[WSpecialPredefCompanionCtor] with WSpecialPredefCompanion { - def resultType = WSpecialPredefCompanionElem - override def toString = "WSpecialPredef" - } - implicit final def unrefWSpecialPredefCompanionCtor(p: Ref[WSpecialPredefCompanionCtor]): WSpecialPredefCompanionCtor = - p.node.asInstanceOf[WSpecialPredefCompanionCtor] - - lazy val RWSpecialPredef: MutableLazy[WSpecialPredefCompanionCtor] = MutableLazy(new WSpecialPredefCompanionCtor { - private val thisClass = RClass(classOf[WSpecialPredefCompanion]) - - def some[A](x: Ref[A]): Ref[WOption[A]] = { - implicit val eA = x.elem - asRep[WOption[A]](mkMethodCall(self, - thisClass.getMethod("some", classOf[Sym]), - Array[AnyRef](x), - true, false, element[WOption[A]])) - } - }) - - object WSpecialPredefCompanionMethods { - object some { - def unapply(d: Def[_]): Nullable[Ref[A] forSome {type A}] = d match { - case MethodCall(receiver, method, args, _) if method.getName == "some" && receiver.elem == WSpecialPredefCompanionElem => - val res = args(0) - Nullable(res).asInstanceOf[Nullable[Ref[A] forSome {type A}]] - case _ => Nullable.None - } - def unapply(exp: Sym): Nullable[Ref[A] forSome {type A}] = unapply(exp.node) - } - } -} // of object WSpecialPredef - registerEntityObject("WSpecialPredef", WSpecialPredef) - - override def resetContext(): Unit = { - super.resetContext() - RWSpecialPredef.reset() - } - -} - -object WSpecialPredefsModule extends scalan.ModuleInfo("wrappers.special", "WSpecialPredefs") { - val reflection = GraphIRReflection -} -} - -trait WSpecialPredefsModule extends wrappers.special.impl.WSpecialPredefsDefs {self: WrappersModule =>} 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/org/ergoplatform/ErgoTreePredefSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala index eb086e88c8..40012c2806 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala @@ -58,8 +58,8 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio boxesToSpend = inputBoxes, spendingTransaction, self = inputBox, activatedVersionInTests) - val pr = prover.prove(emptyEnv + (ScriptNameProp -> "boxCreationHeight_prove"), propTree, ctx, fakeMessage).get - verifier.verify(emptyEnv + (ScriptNameProp -> "boxCreationHeight_verify"), propTree, ctx, pr, fakeMessage).get._1 shouldBe true + val pr = prover.prove(emptyEnv, propTree, ctx, fakeMessage).get + verifier.verify(emptyEnv, propTree, ctx, pr, fakeMessage).get._1 shouldBe true } property("collect coins from the founders' box") { @@ -118,8 +118,8 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio boxesToSpend = inputBoxes, spendingTransaction, self = inputBoxes.head, activatedVersionInTests) - val pr = prover.prove(emptyEnv + (ScriptNameProp -> "checkSpending_prove"), prop, ctx, fakeMessage).get - verifier.verify(emptyEnv + (ScriptNameProp -> "checkSpending_verify"), prop, ctx, pr, fakeMessage).get._1 shouldBe true + val pr = prover.prove(emptyEnv, prop, ctx, fakeMessage).get + verifier.verify(emptyEnv, prop, ctx, pr, fakeMessage).get._1 shouldBe true } } @@ -148,13 +148,13 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio self = inputBoxes.head, activatedVersionInTests) // should not be able to collect before minerRewardDelay - val prove = prover.prove(emptyEnv + (ScriptNameProp -> "rewardOutputScript_prove"), prop, ctx, fakeMessage).get - verifier.verify(emptyEnv + (ScriptNameProp -> "rewardOutputScript_verify"), prop, prevBlockCtx, prove, fakeMessage) + val prove = prover.prove(emptyEnv, prop, ctx, fakeMessage).get + verifier.verify(emptyEnv, prop, prevBlockCtx, prove, fakeMessage) .getOrThrow should matchPattern { case (false,_) => } // should be able to collect after minerRewardDelay - val pr = prover.prove(emptyEnv + (ScriptNameProp -> "prove"), prop, ctx, fakeMessage).getOrThrow - verifier.verify(emptyEnv + (ScriptNameProp -> "verify"), prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true + val pr = prover.prove(emptyEnv, prop, ctx, fakeMessage).getOrThrow + verifier.verify(emptyEnv, prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true } property("create transaction collecting the emission box") { @@ -232,8 +232,8 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio self = inputBoxes.head, activatedVersionInTests).withCostLimit(scriptCostLimitInTests * 10) - val pr = prover.prove(emptyEnv + (ScriptNameProp -> "tokenThresholdScript_prove"), prop, ctx, fakeMessage).getOrThrow - verifier.verify(emptyEnv + (ScriptNameProp -> "tokenThresholdScript_verify"), prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true + val pr = prover.prove(emptyEnv, prop, ctx, fakeMessage).getOrThrow + verifier.verify(emptyEnv, prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true } @@ -308,8 +308,8 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio boxesToSpend = inputBoxes, spendingTransaction, self = inputBoxes.head, activatedVersionInTests) - val pr = prover.prove(emptyEnv + (ScriptNameProp -> "checkRewardTx_prove"), prop, ctx, fakeMessage).getOrThrow - verifier.verify(emptyEnv + (ScriptNameProp -> "checkRewardTx_verify"), prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true + val pr = prover.prove(emptyEnv, prop, ctx, fakeMessage).getOrThrow + verifier.verify(emptyEnv, prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true spendingTransaction } diff --git a/sc/shared/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala index 26e8b08b14..0216f71f21 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala @@ -4,6 +4,7 @@ import sigmastate.interpreter.Interpreter.ScriptNameProp import scala.collection.mutable import sigma.interpreter.{CostedProverResult, ProverResult} + import scala.collection.mutable.ArrayBuffer import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} import sigma.data.{AvlTreeData, CAnyValue, CSigmaProp, Nullable} @@ -14,12 +15,12 @@ import org.ergoplatform.dsl.ContractSyntax.{ErgoScript, Proposition, Token} import sigma.ast.{ErgoTree, SType} import sigma.ast.EvaluatedValue import sigma.interpreter.ContextExtension -import sigmastate.eval.IRContext import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigma.ast.syntax.ValueOps import sigma.{AnyValue, Evaluation, SigmaProp} import ErgoTree.ZeroHeader +import sigma.compiler.ir.IRContext case class TestContractSpec(testSuite: CompilerTestingCommons)(implicit val IR: IRContext) extends ContractSpec { diff --git a/sc/shared/src/test/scala/scalan/BaseLiftableTests.scala b/sc/shared/src/test/scala/scalan/BaseLiftableTests.scala index 3d437a2f1d..a9da1792ec 100644 --- a/sc/shared/src/test/scala/scalan/BaseLiftableTests.scala +++ b/sc/shared/src/test/scala/scalan/BaseLiftableTests.scala @@ -1,8 +1,10 @@ package scalan +import sigma.compiler.ir.IRContext + trait BaseLiftableTests { self: BaseCtxTests => - trait LiftableTestKit { scalan: Scalan => + trait LiftableTestKit { scalan: IRContext => import Liftables._ /** Check the MethodCall reified in f can be mapped to unlifted method which can be invoked.*/ diff --git a/sc/shared/src/test/scala/scalan/LibraryTests.scala b/sc/shared/src/test/scala/scalan/LibraryTests.scala index e082e9154d..b21bd2daf1 100644 --- a/sc/shared/src/test/scala/scalan/LibraryTests.scala +++ b/sc/shared/src/test/scala/scalan/LibraryTests.scala @@ -1,14 +1,15 @@ package scalan +import sigma.compiler.ir.IRContext import sigma.util.BenchmarkUtil.{measure, measureTime} -class Benchmark[T <: Scalan](createContext: => T) { +class Benchmark[T <: IRContext](createContext: => T) { val printDebugInfo: Boolean = false def run() = { val (ctx, total) = measureTime { var ctx = createContext - measure(10000, okShowIterTime = printDebugInfo, okShowTotalTime = printDebugInfo) { i => + measure(10000, okShowIterTime = printDebugInfo, okShowTotalTime = printDebugInfo) { _ => ctx = createContext } ctx diff --git a/sc/shared/src/test/scala/scalan/TestContexts.scala b/sc/shared/src/test/scala/scalan/TestContexts.scala index a86bf42276..82a6d6b21c 100644 --- a/sc/shared/src/test/scala/scalan/TestContexts.scala +++ b/sc/shared/src/test/scala/scalan/TestContexts.scala @@ -1,11 +1,12 @@ package scalan +import sigma.compiler.ir.{GraphIRReflection, IRContext} import sigma.reflection.RMethod import sigma.{BaseNestedTests, BaseShouldTests, BaseTests, TestUtils} trait TestContexts extends TestUtils { - trait TestContextApi { scalan: Scalan => + trait TestContextApi { ctx: IRContext => def invokeAll: Boolean def isInvokeEnabled(d: Def[_], m: RMethod): Boolean def shouldUnpack(e: Elem[_]): Boolean @@ -19,7 +20,7 @@ trait TestContexts extends TestUtils { emitF(testName, Seq(() => s1) ++ s2.map((s: Ref[_]) => () => s): _*) } } - abstract class TestContext(val testName: String) extends Scalan with TestContextApi { + abstract class TestContext(val testName: String) extends IRContext with TestContextApi { def this() = this(currentTestNameAsFileName) override val invokeAll = true diff --git a/sc/shared/src/test/scala/scalan/TestLibrary.scala b/sc/shared/src/test/scala/scalan/TestLibrary.scala index efbce25e68..cb3608d4c3 100644 --- a/sc/shared/src/test/scala/scalan/TestLibrary.scala +++ b/sc/shared/src/test/scala/scalan/TestLibrary.scala @@ -1,7 +1,9 @@ package scalan -trait TestLibrary extends Library { +import sigma.compiler.ir.{GraphIRReflection, IRContext} + +trait TestLibrary extends IRContext { import CollBuilder._ + import SigmaDslBuilder._ val reflection = (GraphIRReflection) - lazy val colBuilder: Ref[CollBuilder] = variable[CollBuilder] } diff --git a/sc/shared/src/test/scala/sigma/CollsStagingTests.scala b/sc/shared/src/test/scala/sigma/CollsStagingTests.scala index 3b56920c8d..b26c7d4119 100644 --- a/sc/shared/src/test/scala/sigma/CollsStagingTests.scala +++ b/sc/shared/src/test/scala/sigma/CollsStagingTests.scala @@ -3,6 +3,7 @@ package sigma import scala.language.reflectiveCalls import special.wrappers.WrappersTests import scalan._ +import sigma.compiler.ir.IRContext import sigma.data.CollOverArrayBuilder class CollsStagingTests extends WrappersTests { @@ -41,7 +42,7 @@ class CollsStagingTests extends WrappersTests { } test("invokeUnlifted for Col") { - val ctx = new WrappersCtx with Library + val ctx = new WrappersCtx with IRContext import ctx._ import Coll._ import CollBuilder._ @@ -59,7 +60,7 @@ class CollsStagingTests extends WrappersTests { } test("invokeUnlifted for method of Ctor") { - val ctx = new WrappersCtx with Library + val ctx = new WrappersCtx with IRContext import ctx._ import Coll._ import CollBuilder._ diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationBase.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationBase.scala new file mode 100644 index 0000000000..7be79546e7 --- /dev/null +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationBase.scala @@ -0,0 +1,146 @@ +package sigma + +import org.scalatest.BeforeAndAfterAll +import sigma.ast.{Apply, FixedCostItem, FuncValue, GetVar, JitCost, OptionGet, ValUse} +import sigma.eval.{CostDetails, EvalSettings, Profiler} +import sigmastate.CompilerCrossVersionProps +import sigmastate.interpreter.CErgoTreeEvaluator + +import scala.util.Success + +/** Base class for language test suites (one suite for each language version: 5.0, 6.0, etc.) + * Each suite tests every method of every SigmaDsl type to be equivalent to + * the evaluation of the corresponding ErgoScript operation. + * + * The properties of this suite exercise two interpreters: the current (aka `old` + * interpreter) and the new interpreter for a next soft-fork. After the soft-fork is + * released, the new interpreter becomes current at which point the `old` and `new` + * interpreters in this suite should be equivalent. This change is reflected in this + * suite by commiting changes in expected values. + * The `old` and `new` interpreters are compared like the following: + * 1) for existingFeature the interpreters should be equivalent + * 2) for changedFeature the test cases contain different expected values + * 3) for newFeature the old interpreter should throw and the new interpreter is checked + * against expected values. + * + * This suite can be used for Cost profiling, i.e. measurements of operations times and + * comparing them with cost parameters of the operations. + * + * The following settings should be specified for profiling: + * isMeasureOperationTime = true + * isMeasureScriptTime = true + * isLogEnabled = false + * printTestVectors = false + * costTracingEnabled = false + * isTestRun = true + * perTestWarmUpIters = 1 + * nBenchmarkIters = 1 + */ +abstract class LanguageSpecificationBase extends SigmaDslTesting + with CompilerCrossVersionProps + with BeforeAndAfterAll { suite => + + /** Version of the language (ErgoScript/ErgoTree) which is specified by this suite. */ + def languageVersion: Byte + + /** Use VersionContext so that each property in this suite runs under correct + * parameters. + */ + protected override def testFun_Run(testName: String, testFun: => Any): Unit = { + VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { + super.testFun_Run(testName, testFun) + } + } + + implicit override val generatorDrivenConfig: PropertyCheckConfiguration = PropertyCheckConfiguration(minSuccessful = 30) + + val evalSettingsInTests = CErgoTreeEvaluator.DefaultEvalSettings.copy( + isMeasureOperationTime = true, + isMeasureScriptTime = true, + isLogEnabled = false, // don't commit the `true` value (travis log is too high) + printTestVectors = false, // don't commit the `true` value (travis log is too high) + + /** Should always be enabled in tests (and false by default) + * Should be disabled for cost profiling, which case the new costs are not checked. + */ + costTracingEnabled = true, + profilerOpt = Some(CErgoTreeEvaluator.DefaultProfiler), + isTestRun = true + ) + + def warmupSettings(p: Profiler) = evalSettingsInTests.copy( + isLogEnabled = false, + printTestVectors = false, + profilerOpt = Some(p) + ) + + implicit override def evalSettings: EvalSettings = { + warmupProfiler match { + case Some(p) => warmupSettings(p) + case _ => evalSettingsInTests + } + } + + override val perTestWarmUpIters = 0 + + override val nBenchmarkIters = 0 + + override val okRunTestsWithoutMCLowering: Boolean = true + + implicit def IR = createIR() + + def testCases[A, B](cases: Seq[(A, Expected[B])], f: Feature[A, B]) = { + val table = Table(("x", "y"), cases: _*) + forAll(table) { (x, expectedRes) => + val res = f.checkEquality(x) + val resValue = res.map(_._1) + val (expected, expDetailsOpt) = expectedRes.newResults(ergoTreeVersionInTests) + checkResult(resValue, expected.value, failOnTestVectors = true, + "SigmaDslSpecifiction#testCases: compare expected new result with res = f.checkEquality(x)") + res match { + case Success((value, details)) => + details.cost shouldBe JitCost(expected.verificationCost.get) + expDetailsOpt.foreach(expDetails => + if (details.trace != expDetails.trace) { + printCostDetails(f.script, details) + details.trace shouldBe expDetails.trace + } + ) + } + } + } + + override protected def beforeAll(): Unit = { + prepareSamples[BigInt] + prepareSamples[GroupElement] + prepareSamples[AvlTree] + prepareSamples[Box] + prepareSamples[PreHeader] + prepareSamples[Header] + prepareSamples[(BigInt, BigInt)] + prepareSamples[(GroupElement, GroupElement)] + prepareSamples[(AvlTree, AvlTree)] + prepareSamples[(Box, Box)] + prepareSamples[(PreHeader, PreHeader)] + prepareSamples[(Header, Header)] + } + + ///===================================================== + /// CostDetails shared among test cases + ///----------------------------------------------------- + val traceBase = Array( + FixedCostItem(Apply), + FixedCostItem(FuncValue), + FixedCostItem(GetVar), + FixedCostItem(OptionGet), + FixedCostItem(FuncValue.AddToEnvironmentDesc, FuncValue.AddToEnvironmentDesc_CostKind), + FixedCostItem(ValUse) + ) + + /** Helper method to create the given expected results for all tree versions. */ + def expectedSuccessForAllTreeVersions[A](value: A, cost: Int, costDetails: CostDetails) = { + val res = ExpectedResult(Success(value), Some(cost)) -> Some(costDetails) + Seq(0, 1, 2, 3).map(version => version -> res) + } + +} diff --git a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala similarity index 87% rename from sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala rename to sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index 4dd576f03a..0bb92f8249 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -5,31 +5,30 @@ import org.ergoplatform._ import org.ergoplatform.settings.ErgoAlgos import org.scalacheck.Arbitrary._ import org.scalacheck.{Arbitrary, Gen} -import org.scalatest.BeforeAndAfterAll import scorex.crypto.authds.avltree.batch._ import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId import sigma.Extensions.{ArrayOps, CollOps} +import sigma.VersionContext.V6SoftForkVersion +import sigma.VersionContext.JitActivationVersion +import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} import sigma.ast.SCollection._ -import sigma.ast._ import sigma.ast.syntax._ +import sigma.ast.{Apply, MethodCall, PropertyCall, _} +import sigma.data.OrderingOps._ import sigma.data.RType._ import sigma.data._ +import sigma.eval.Extensions.{ByteExt, IntExt, LongExt, ShortExt} +import sigma.eval.{CostDetails, EvalSettings, SigmaDsl, TracedCost} +import sigma.exceptions.InvalidType +import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, IntOps, LongOps} import sigma.{VersionContext, ast, data, _} -import ErgoTree.{HeaderType, ZeroHeader} -import sigma.eval.{CostDetails, EvalSettings, Profiler, SigmaDsl, TracedCost} -import sigmastate._ import sigmastate.eval.Extensions.AvlTreeOps -import sigma.eval.Extensions.{ByteExt, IntExt, LongExt, ShortExt} -import OrderingOps._ import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter._ -import sigma.ast.{Apply, MethodCall, PropertyCall} -import sigma.exceptions.InvalidType -import sigma.serialization.ValueCodes.OpCode import sigmastate.utils.Extensions._ import sigmastate.utils.Helpers import sigmastate.utils.Helpers._ @@ -38,133 +37,18 @@ import java.math.BigInteger import scala.collection.compat.immutable.ArraySeq import scala.util.{Failure, Success} -/** This suite tests every method of every SigmaDsl type to be equivalent to - * the evaluation of the corresponding ErgoScript operation. - * - * The properties of this suite excercise two interpreters: the current (aka `old` - * interpreter) and the new interpreter for a next soft-fork. After the soft-fork is - * released, the new interpreter becomes current at which point the `old` and `new` - * interpreters in this suite should be equivalent. This change is reflected in this - * suite by commiting changes in expected values. - * The `old` and `new` interpreters are compared like the following: - * 1) for existingFeature the interpreters should be equivalent - * 2) for changedFeature the test cases contain different expected values - * 3) for newFeature the old interpreter should throw and the new interpreter is checked - * against expected values. - * - * This suite can be used for Cost profiling, i.e. measurements of operations times and - * comparing them with cost parameteres of the operations. + +/** This suite tests all operations for v5.0 version of the language. + * The base classes establish the infrastructure for the tests. * - * The following settings should be specified for profiling: - * isMeasureOperationTime = true - * isMeasureScriptTime = true - * isLogEnabled = false - * printTestVectors = false - * costTracingEnabled = false - * isTestRun = true - * perTestWarmUpIters = 1 - * nBenchmarkIters = 1 + * @see SigmaDslSpecificationBase */ -class SigmaDslSpecification extends SigmaDslTesting - with CompilerCrossVersionProps - with BeforeAndAfterAll { suite => - - /** Use VersionContext so that each property in this suite runs under correct - * parameters. - */ - protected override def testFun_Run(testName: String, testFun: => Any): Unit = { - VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { - super.testFun_Run(testName, testFun) - } - } - - implicit override val generatorDrivenConfig = PropertyCheckConfiguration(minSuccessful = 30) - - val evalSettingsInTests = CErgoTreeEvaluator.DefaultEvalSettings.copy( - isMeasureOperationTime = true, - isMeasureScriptTime = true, - isLogEnabled = false, // don't commit the `true` value (travis log is too high) - printTestVectors = false, // don't commit the `true` value (travis log is too high) - - /** Should always be enabled in tests (and false by default) - * Should be disabled for cost profiling, which case the new costs are not checked. - */ - costTracingEnabled = true, +class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => - profilerOpt = Some(CErgoTreeEvaluator.DefaultProfiler), - isTestRun = true - ) - - def warmupSettings(p: Profiler) = evalSettingsInTests.copy( - isLogEnabled = false, - printTestVectors = false, - profilerOpt = Some(p) - ) - - implicit override def evalSettings: EvalSettings = { - warmupProfiler match { - case Some(p) => warmupSettings(p) - case _ => evalSettingsInTests - } - } - - override val perTestWarmUpIters = 0 - - override val nBenchmarkIters = 0 - - override val okRunTestsWithoutMCLowering: Boolean = true - - implicit def IR = createIR() - - def testCases[A, B](cases: Seq[(A, Expected[B])], f: Feature[A, B]) = { - val table = Table(("x", "y"), cases:_*) - forAll(table) { (x, expectedRes) => - val res = f.checkEquality(x) - val resValue = res.map(_._1) - val (expected, expDetailsOpt) = expectedRes.newResults(ergoTreeVersionInTests) - checkResult(resValue, expected.value, failOnTestVectors = true, - "SigmaDslSpecifiction#testCases: compare expected new result with res = f.checkEquality(x)") - res match { - case Success((value, details)) => - details.cost shouldBe JitCost(expected.verificationCost.get) - expDetailsOpt.foreach(expDetails => - if (details.trace != expDetails.trace) { - printCostDetails(f.script, details) - details.trace shouldBe expDetails.trace - } - ) - } - } - } + override def languageVersion: Byte = VersionContext.JitActivationVersion import TestData._ - override protected def beforeAll(): Unit = { - prepareSamples[BigInt] - prepareSamples[GroupElement] - prepareSamples[AvlTree] - prepareSamples[Box] - prepareSamples[PreHeader] - prepareSamples[Header] - prepareSamples[(BigInt, BigInt)] - prepareSamples[(GroupElement, GroupElement)] - prepareSamples[(AvlTree, AvlTree)] - prepareSamples[(Box, Box)] - prepareSamples[(PreHeader, PreHeader)] - prepareSamples[(Header, Header)] - } - - ///===================================================== - /// CostDetails shared among test cases - ///----------------------------------------------------- - val traceBase = Array( - FixedCostItem(Apply), - FixedCostItem(FuncValue), - FixedCostItem(GetVar), - FixedCostItem(OptionGet), - FixedCostItem(FuncValue.AddToEnvironmentDesc, FuncValue.AddToEnvironmentDesc_CostKind), - FixedCostItem(ValUse) - ) def upcastCostDetails(tpe: SType) = TracedCost(traceBase :+ TypeBasedCostItem(Upcast, tpe)) def downcastCostDetails(tpe: SType) = TracedCost(traceBase :+ TypeBasedCostItem(Downcast, tpe)) def arithOpsCostDetails(tpe: SType) = CostDetails( @@ -232,17 +116,6 @@ class SigmaDslSpecification extends SigmaDslTesting /// Boolean type operations ///----------------------------------------------------- - property("Boolean methods equivalence") { - val toByte = newFeature((x: Boolean) => x.toByte, "{ (x: Boolean) => x.toByte }") - - val cases = Seq( - (true, Success(1.toByte)), - (false, Success(0.toByte)) - ) - - testCases(cases, toByte) - } - property("BinXor(logical XOR) equivalence") { val binXor = existingFeature((x: (Boolean, Boolean)) => x._1 ^ x._2, "{ (x: (Boolean, Boolean)) => x._1 ^ x._2 }", @@ -262,7 +135,9 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) val newCost = 1768 - def success(b: Boolean) = Expected(Success(b), 1768, newDetails, newCost) + + def success(b: Boolean) = Expected(Success(b), 1768, newDetails, newCost, Seq.fill(4)(2012)) + val cases = Seq( (true, true) -> success(false), (true, false) -> success(true), @@ -291,14 +166,14 @@ class SigmaDslSpecification extends SigmaDslTesting val expectedCost = 1768 val newCost = 1768 val cases = Seq( - (true, true) -> Expected(Success(false), expectedCost, newDetails, newCost) + (true, true) -> Expected(Success(false), expectedCost, newDetails, newCost, Seq.fill(4)(2012)) ) verifyCases(cases, feature) val initCost = 100 initialCostInTests.withValue(initCost) { val cases = Seq( - (true, true) -> Expected(Success(false), expectedCost + initCost, newDetails, newCost + initCost) + (true, true) -> Expected(Success(false), expectedCost + initCost, newDetails, newCost + initCost, Seq.fill(4)(2012 + initCost)) ) verifyCases(cases, feature) } @@ -327,7 +202,9 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(BinXor) ) ) - def success(b: Boolean) = Expected(Success(b), 1769, newDetails, 1769) + + def success(b: Boolean) = Expected(Success(b), 1769, newDetails, 1769, Seq.fill(4)(2027)) + val cases = Seq( (1095564593, true) -> success(true), (-901834021, true) -> success(true), @@ -364,10 +241,10 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) val cases = Seq( - (false, true) -> Expected(Success(false), 1766, newDetails1, 1766), - (false, false) -> Expected(Success(false), 1766, newDetails1, 1766), - (true, true) -> Expected(Success(true), 1768, newDetails2, 1768), - (true, false) -> Expected(Success(false), 1768, newDetails2, 1768) + (false, true) -> Expected(Success(false), 1766, newDetails1, 1766, Seq.fill(4)(2010)), + (false, false) -> Expected(Success(false), 1766, newDetails1, 1766, Seq.fill(4)(2010)), + (true, true) -> Expected(Success(true), 1768, newDetails2, 1768, Seq.fill(4)(2012)), + (true, false) -> Expected(Success(false), 1768, newDetails2, 1768, Seq.fill(4)(2012)) ) verifyCases(cases, eq) } @@ -397,10 +274,10 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) val cases = Seq( - (true, false) -> Expected(Success(true), 1766, newDetails1, 1766), - (true, true) -> Expected(Success(true), 1766, newDetails1, 1766), - (false, false) -> Expected(Success(false), 1768, newDetails2, 1768), - (false, true) -> Expected(Success(true), 1768, newDetails2, 1768) + (true, false) -> Expected(Success(true), 1766, newDetails1, 1766, Seq.fill(4)(2010)), + (true, true) -> Expected(Success(true), 1766, newDetails1, 1766, Seq.fill(4)(2010)), + (false, false) -> Expected(Success(false), 1768, newDetails2, 1768, Seq.fill(4)(2012)), + (false, true) -> Expected(Success(true), 1768, newDetails2, 1768, Seq.fill(4)(2012)) ) verifyCases(cases, eq) } @@ -445,7 +322,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( Seq( - (true, Expected(Success(true), 1765, newDetails1, 1765)), + (true, Expected(Success(true), 1765, newDetails1, 1765, Seq.fill(4)(2027))), (false, Expected(new ArithmeticException("/ by zero"))) ), existingFeature((x: Boolean) => x || (1 / 0 == 1), @@ -461,7 +338,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( (true, Expected(new ArithmeticException("/ by zero"))), - (false, Expected(Success(false), 1765, newDetails2, 1765)) + (false, Expected(Success(false), 1765, newDetails2, 1765, Seq.fill(4)(2027))) ), existingFeature((x: Boolean) => x && (1 / 0 == 1), "{ (x: Boolean) => x && (1 / 0 == 1) }", @@ -475,8 +352,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( - (false, Expected(Success(false), 1765, newDetails2, 1765)), - (true, Expected(Success(true), 1768, newDetails3, 1768)) + (false, Expected(Success(false), 1765, newDetails2, 1765, Seq.fill(4)(2033))), + (true, Expected(Success(true), 1768, newDetails3, 1768, Seq.fill(4)(2036))) ), existingFeature((x: Boolean) => x && (x || (1 / 0 == 1)), "{ (x: Boolean) => x && (x || (1 / 0 == 1)) }", @@ -493,8 +370,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( - (false, Expected(Success(false), 1765, newDetails2, 1765)), - (true, Expected(Success(true), 1770, newDetails4, 1770)) + (false, Expected(Success(false), 1765, newDetails2, 1765, Seq.fill(4)(2039))), + (true, Expected(Success(true), 1770, newDetails4, 1770, Seq.fill(4)(2044))) ), existingFeature((x: Boolean) => x && (x && (x || (1 / 0 == 1))), "{ (x: Boolean) => x && (x && (x || (1 / 0 == 1))) }", @@ -514,8 +391,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( - (false, Expected(Success(false), 1765, newDetails2, 1765)), - (true, Expected(Success(true), 1773, newDetails5, 1773)) + (false, Expected(Success(false), 1765, newDetails2, 1765, Seq.fill(4)(2045))), + (true, Expected(Success(true), 1773, newDetails5, 1773, Seq.fill(4)(2053))) ), existingFeature((x: Boolean) => x && (x && (x && (x || (1 / 0 == 1)))), "{ (x: Boolean) => x && (x && (x && (x || (1 / 0 == 1)))) }", @@ -549,7 +426,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( (false, Expected(new ArithmeticException("/ by zero"))), - (true, Expected(Success(true), 1773, newDetails6, 1773)) + (true, Expected(Success(true), 1773, newDetails6, 1773, Seq.fill(4)(2075))) ), existingFeature((x: Boolean) => !(!x && (1 / 0 == 1)) && (x || (1 / 0 == 1)), "{ (x: Boolean) => !(!x && (1 / 0 == 1)) && (x || (1 / 0 == 1)) }", @@ -578,7 +455,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( Seq( - (true, Expected(Success(true), 1768, newDetails7, 1768)), + (true, Expected(Success(true), 1768, newDetails7, 1768, Seq.fill(4)(2036))), (false, Expected(new ArithmeticException("/ by zero"))) ), existingFeature((x: Boolean) => (x || (1 / 0 == 1)) && x, @@ -604,7 +481,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( Seq( - (true, Expected(Success(true), 1770, newDetails8, 1770)), + (true, Expected(Success(true), 1770, newDetails8, 1770, Seq.fill(4)(2068))), (false, Expected(new ArithmeticException("/ by zero"))) ), existingFeature((x: Boolean) => (x || (1 / 0 == 1)) && (x || (1 / 0 == 1)), @@ -636,7 +513,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( Seq( - (true, Expected(Success(true), 1775, newDetails9, 1775)), + (true, Expected(Success(true), 1775, newDetails9, 1775, Seq.fill(4)(2107))), (false, Expected(new ArithmeticException("/ by zero"))) ), existingFeature( @@ -686,7 +563,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( (false, Expected(new ArithmeticException("/ by zero"))), - (true, Expected(Success(true), 1780, newDetails10, 1780)) + (true, Expected(Success(true), 1780, newDetails10, 1780, Seq.fill(4)(2156))) ), existingFeature( (x: Boolean) => (!(!x && (1 / 0 == 1)) || (1 / 0 == 0)) && (!(!x && (1 / 0 == 1)) || (1 / 0 == 1)), @@ -729,7 +606,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def expect(v: Byte) = Expected(Success(v), 1763, TracedCost(traceBase), 1763) + def expect(v: Byte) = Expected(Success(v), 1763, TracedCost(traceBase), 1763, Seq.fill(4)(1993)) + Seq( (0.toByte, expect(0.toByte)), (1.toByte, expect(1.toByte)), @@ -746,7 +624,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def expected(v: Short) = Expected(Success(v), 1764, upcastCostDetails(SShort), 1764) + def expected(v: Short) = Expected(Success(v), 1764, upcastCostDetails(SShort), 1764, Seq.fill(4)(1998)) + Seq( (0.toByte, expected(0.toShort)), (1.toByte, expected(1.toShort)), @@ -763,7 +642,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def expected(v: Int) = Expected(Success(v), 1764, upcastCostDetails(SInt), 1764) + def expected(v: Int) = Expected(Success(v), 1764, upcastCostDetails(SInt), 1764, Seq.fill(4)(1998)) + Seq( (0.toByte, expected(0)), (1.toByte, expected(1)), @@ -780,7 +660,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def expected(v: Long) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764) + def expected(v: Long) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764, Seq.fill(4)(1998)) + Seq( (0.toByte, expected(0L)), (1.toByte, expected(1L)), @@ -797,7 +678,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def expected(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767) + def expected(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767, Seq.fill(4)(2001)) + Seq( (0.toByte, expected(CBigInt(new BigInteger("0", 16)))), (1.toByte, expected(CBigInt(new BigInteger("1", 16)))), @@ -815,7 +697,8 @@ class SigmaDslSpecification extends SigmaDslTesting val n = ExactIntegral.ByteIsExactIntegral verifyCases( { - def success[T](v: (T, (T, (T, (T, T))))) = Expected(Success(v), 1788, arithOpsCostDetails(SByte), 1788) + def success[T](v: (T, (T, (T, (T, T))))) = Expected(Success(v), 1788, arithOpsCostDetails(SByte), 1788, Seq.fill(4)(2116)) + Seq( ((-128.toByte, -128.toByte), Expected(new ArithmeticException("Byte overflow"))), ((-128.toByte, 0.toByte), Expected(new ArithmeticException("/ by zero"))), @@ -914,6 +797,12 @@ class SigmaDslSpecification extends SigmaDslTesting ((y, x), Expected(res.value, cost, newCostDetails, cost)) } + def swapArgs[A](cases: Seq[((A, A), Expected[Boolean])], cost: Int, newCostDetails: CostDetails, + expectedV3Costs: Seq[Int]) = + cases.map { case ((x, y), res) => + ((y, x), Expected(res.value, cost, newCostDetails, cost, expectedV3Costs)) + } + def newCasesFrom[A, R]( cases: Seq[(A, A)] )( @@ -932,12 +821,19 @@ class SigmaDslSpecification extends SigmaDslTesting ((x, y), Expected(Success(getExpectedRes(x, y)), cost = cost, expectedDetails = newCostDetails, expectedNewCost = cost)) } - def verifyOp[A: Ordering: Arbitrary] - (cases: Seq[((A, A), Expected[Boolean])], - opName: String, - op: (SValue, SValue) => SValue) - (expectedFunc: (A, A) => Boolean, generateCases: Boolean = true) - (implicit tA: RType[A], sampled: Sampled[(A, A)], evalSettings: EvalSettings) = { + def newCasesFrom3[A, R](cases: Seq[(A, A)]) + (getExpectedRes: (A, A) => R, cost: Int, newCostDetails: CostDetails, + expectedV3Costs: Seq[Int]) = + cases.map { case (x, y) => + ((x, y), Expected(Success(getExpectedRes(x, y)), cost = cost, expectedDetails = newCostDetails, expectedNewCost = cost, expectedV3Costs)) + } + + def verifyOp[A: Ordering : Arbitrary] + (cases: Seq[((A, A), Expected[Boolean])], + opName: String, + op: (SValue, SValue) => SValue) + (expectedFunc: (A, A) => Boolean, generateCases: Boolean = true) + (implicit tA: RType[A], sampled: Sampled[(A, A)], evalSettings: EvalSettings) = { val nameA = RType[A].name val tpeA = Evaluation.rtypeToSType(tA) verifyCases(cases, @@ -962,7 +858,9 @@ class SigmaDslSpecification extends SigmaDslTesting property("Byte LT, GT, NEQ") { val o = ExactOrdering.ByteIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SByte), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SByte), 1768, Seq.fill(4)(2012)) + val LT_cases: Seq[((Byte, Byte), Expected[Boolean])] = Seq( (-128.toByte, -128.toByte) -> expect(false), (-128.toByte, -127.toByte) -> expect(true), @@ -1003,16 +901,18 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LT_cases, "<", LT.apply)(_ < _) verifyOp( - swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SByte)), + swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SByte), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(_ > _) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost), expectedV3Costs = Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } property("Byte LE, GE") { val o = ExactOrdering.ByteIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SByte), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SByte), 1768, Seq.fill(4)(2012)) + val LE_cases: Seq[((Byte, Byte), Expected[Boolean])] = Seq( (-128.toByte, -128.toByte) -> expect(true), (-128.toByte, -127.toByte) -> expect(true), @@ -1054,41 +954,18 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LE_cases, "<=", LE.apply)(_ <= _) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SByte)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SByte), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(_ >= _) } - property("Byte methods equivalence (new features)") { - lazy val toBytes = newFeature((x: Byte) => x.toBytes, "{ (x: Byte) => x.toBytes }") - lazy val toAbs = newFeature((x: Byte) => x.toAbs, "{ (x: Byte) => x.toAbs }") - lazy val compareTo = newFeature( - (x: (Byte, Byte)) => x._1.compareTo(x._2), - "{ (x: (Byte, Byte)) => x._1.compareTo(x._2) }") - - lazy val bitOr = newFeature( - { (x: (Byte, Byte)) => (x._1 | x._2).toByteExact }, - "{ (x: (Byte, Byte)) => (x._1 | x._2).toByteExact }") - - lazy val bitAnd = newFeature( - { (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }, - "{ (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }") - - forAll { x: Byte => - Seq(toBytes, toAbs).foreach(f => f.checkEquality(x)) - } - - forAll { x: (Byte, Byte) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } - property("Short methods equivalence") { SShort.upcast(0.toShort) shouldBe 0.toShort // boundary test case SShort.downcast(0.toShort) shouldBe 0.toShort // boundary test case verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764, Seq.fill(4)(1998)) + Seq( (Short.MinValue, Expected(new ArithmeticException("Byte overflow"))), (-21626.toShort, Expected(new ArithmeticException("Byte overflow"))), @@ -1107,7 +984,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763) + def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763, Seq.fill(4)(1993)) + Seq( (-32768.toShort, success(-32768.toShort)), (-27798.toShort, success(-27798.toShort)), @@ -1124,7 +1002,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SInt), 1764) + def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SInt), 1764, Seq.fill(4)(1998)) + Seq( (-32768.toShort, success(-32768)), (-21064.toShort, success(-21064)), @@ -1141,7 +1020,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764) + def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764, Seq.fill(4)(1998)) + Seq( (-32768.toShort, success(-32768L)), (-23408.toShort, success(-23408L)), @@ -1158,7 +1038,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767) + def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767, Seq.fill(4)(2001)) + Seq( (-32768.toShort, success(CBigInt(new BigInteger("-8000", 16)))), (-26248.toShort, success(CBigInt(new BigInteger("-6688", 16)))), @@ -1176,7 +1057,8 @@ class SigmaDslSpecification extends SigmaDslTesting val n = ExactIntegral.ShortIsExactIntegral verifyCases( { - def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SShort), 1788) + def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SShort), 1788, Seq.fill(4)(2116)) + Seq( ((-32768.toShort, 1.toShort), Expected(new ArithmeticException("Short overflow"))), ((-32768.toShort, 4006.toShort), Expected(new ArithmeticException("Short overflow"))), @@ -1268,7 +1150,9 @@ class SigmaDslSpecification extends SigmaDslTesting property("Short LT, GT, NEQ") { val o = ExactOrdering.ShortIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SShort), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SShort), 1768, Seq.fill(4)(2012)) + val LT_cases: Seq[((Short, Short), Expected[Boolean])] = Seq( (Short.MinValue, Short.MinValue) -> expect(false), (Short.MinValue, (Short.MinValue + 1).toShort) -> expect(true), @@ -1308,15 +1192,17 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LT_cases, "<", LT.apply)(_ < _) - verifyOp(swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SShort)), ">", GT.apply)(_ > _) + verifyOp(swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SShort), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(_ > _) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost), expectedV3Costs = Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } property("Short LE, GE") { val o = ExactOrdering.ShortIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SShort), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SShort), 1768, Seq.fill(4)(2012)) + val LE_cases: Seq[((Short, Short), Expected[Boolean])] = Seq( (Short.MinValue, Short.MinValue) -> expect(true), (Short.MinValue, (Short.MinValue + 1).toShort) -> expect(true), @@ -1358,40 +1244,18 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LE_cases, "<=", LE.apply)(_ <= _) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SShort)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SShort), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(_ >= _) } - property("Short methods equivalence (new features)") { - lazy val toBytes = newFeature((x: Short) => x.toBytes, "{ (x: Short) => x.toBytes }") - lazy val toAbs = newFeature((x: Short) => x.toAbs, "{ (x: Short) => x.toAbs }") - - lazy val compareTo = newFeature((x: (Short, Short)) => x._1.compareTo(x._2), - "{ (x: (Short, Short)) => x._1.compareTo(x._2) }") - - lazy val bitOr = newFeature( - { (x: (Short, Short)) => (x._1 | x._2).toShortExact }, - "{ (x: (Short, Short)) => x._1 | x._2 }") - - lazy val bitAnd = newFeature( - { (x: (Short, Short)) => (x._1 & x._2).toShortExact }, - "{ (x: (Short, Short)) => x._1 & x._2 }") - - forAll { x: Short => - Seq(toBytes, toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (Short, Short) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } - property("Int methods equivalence") { SInt.upcast(0) shouldBe 0 // boundary test case SInt.downcast(0) shouldBe 0 // boundary test case verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764, Seq.fill(4)(1998)) + Seq( (Int.MinValue, Expected(new ArithmeticException("Byte overflow"))), (-2014394379, Expected(new ArithmeticException("Byte overflow"))), @@ -1410,7 +1274,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SShort), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SShort), 1764, Seq.fill(4)(1998)) + Seq( (Int.MinValue, Expected(new ArithmeticException("Short overflow"))), (Short.MinValue - 1, Expected(new ArithmeticException("Short overflow"))), @@ -1429,7 +1294,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763) + def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763, Seq.fill(4)(1993)) + Seq( (Int.MinValue, success(Int.MinValue)), (-1, success(-1)), @@ -1444,7 +1310,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764) + def success[T](v: T) = Expected(Success(v), 1764, upcastCostDetails(SLong), 1764, Seq.fill(4)(1998)) + Seq( (Int.MinValue, success(Int.MinValue.toLong)), (-1, success(-1L)), @@ -1459,7 +1326,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767) + def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767, Seq.fill(4)(2001)) + Seq( (Int.MinValue, success(CBigInt(new BigInteger("-80000000", 16)))), (-1937187314, success(CBigInt(new BigInteger("-737721f2", 16)))), @@ -1476,57 +1344,59 @@ class SigmaDslSpecification extends SigmaDslTesting val n = ExactNumeric.IntIsExactNumeric verifyCases( - { - def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SInt), 1788) - Seq( - ((Int.MinValue, 449583993), Expected(new ArithmeticException("integer overflow"))), - ((-1589633733, 2147483647), Expected(new ArithmeticException("integer overflow"))), - ((-1585471506, -1), success((-1585471507, (-1585471505, (1585471506, (1585471506, 0)))))), - ((-1569005179, 1230236634), Expected(new ArithmeticException("integer overflow"))), - ((-1493733356, -1319619597), Expected(new ArithmeticException("integer overflow"))), - ((-1100263120, -880052091), Expected(new ArithmeticException("integer overflow"))), - ((-1055955857, 309147303), Expected(new ArithmeticException("integer overflow"))), - ((-569807371, 0), Expected(new ArithmeticException("/ by zero"))), - ((-522264843, 2147483647), Expected(new ArithmeticException("integer overflow"))), - ((-109552389, 0), Expected(new ArithmeticException("/ by zero"))), - ((-1, -2147483648), Expected(new ArithmeticException("integer overflow"))), - ((-1, -1), success((-2, (0, (1, (1, 0)))))), - ((-1, 0), Expected(new ArithmeticException("/ by zero"))), - ((0, -2147483648), Expected(new ArithmeticException("integer overflow"))), - ((1, -1525049432), success((-1525049431, (1525049433, (-1525049432, (0, 1)))))), - ((1, 0), Expected(new ArithmeticException("/ by zero"))), - ((1, 805353746), success((805353747, (-805353745, (805353746, (0, 1)))))), - ((1, 2147483647), Expected(new ArithmeticException("integer overflow"))), - ((475797978, 0), Expected(new ArithmeticException("/ by zero"))), - ((782343922, -1448560539), Expected(new ArithmeticException("integer overflow"))), - ((928769361, 542647292), Expected(new ArithmeticException("integer overflow"))), - ((1568062151, 0), Expected(new ArithmeticException("/ by zero"))), - ((1698252401, -1), success((1698252400, (1698252402, (-1698252401, (-1698252401, 0)))))), - ((1949795740, -1575667037), Expected(new ArithmeticException("integer overflow"))), - ((Int.MaxValue, -1), Expected(new ArithmeticException("integer overflow"))), - ((Int.MaxValue, 1), Expected(new ArithmeticException("integer overflow"))), - ((Int.MaxValue, 1738276576), Expected(new ArithmeticException("integer overflow"))) - ) - }, - existingFeature( - { (x: (Int, Int)) => - val a = x._1; val b = x._2 - val plus = n.plus(a, b) - val minus = n.minus(a, b) - val mul = n.times(a, b) - val div = a / b - val mod = a % b - (plus, (minus, (mul, (div, mod)))) + { + def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SInt), 1788, Seq.fill(4)(2116)) + + Seq( + ((Int.MinValue, 449583993), Expected(new ArithmeticException("integer overflow"))), + ((-1589633733, 2147483647), Expected(new ArithmeticException("integer overflow"))), + ((-1585471506, -1), success((-1585471507, (-1585471505, (1585471506, (1585471506, 0)))))), + ((-1569005179, 1230236634), Expected(new ArithmeticException("integer overflow"))), + ((-1493733356, -1319619597), Expected(new ArithmeticException("integer overflow"))), + ((-1100263120, -880052091), Expected(new ArithmeticException("integer overflow"))), + ((-1055955857, 309147303), Expected(new ArithmeticException("integer overflow"))), + ((-569807371, 0), Expected(new ArithmeticException("/ by zero"))), + ((-522264843, 2147483647), Expected(new ArithmeticException("integer overflow"))), + ((-109552389, 0), Expected(new ArithmeticException("/ by zero"))), + ((-1, -2147483648), Expected(new ArithmeticException("integer overflow"))), + ((-1, -1), success((-2, (0, (1, (1, 0)))))), + ((-1, 0), Expected(new ArithmeticException("/ by zero"))), + ((0, -2147483648), Expected(new ArithmeticException("integer overflow"))), + ((1, -1525049432), success((-1525049431, (1525049433, (-1525049432, (0, 1)))))), + ((1, 0), Expected(new ArithmeticException("/ by zero"))), + ((1, 805353746), success((805353747, (-805353745, (805353746, (0, 1)))))), + ((1, 2147483647), Expected(new ArithmeticException("integer overflow"))), + ((475797978, 0), Expected(new ArithmeticException("/ by zero"))), + ((782343922, -1448560539), Expected(new ArithmeticException("integer overflow"))), + ((928769361, 542647292), Expected(new ArithmeticException("integer overflow"))), + ((1568062151, 0), Expected(new ArithmeticException("/ by zero"))), + ((1698252401, -1), success((1698252400, (1698252402, (-1698252401, (-1698252401, 0)))))), + ((1949795740, -1575667037), Expected(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, -1), Expected(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, 1), Expected(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, 1738276576), Expected(new ArithmeticException("integer overflow"))) + ) }, - """{ (x: (Int, Int)) => - | val a = x._1; val b = x._2 - | val plus = a + b - | val minus = a - b - | val mul = a * b - | val div = a / b - | val mod = a % b - | (plus, (minus, (mul, (div, mod)))) - |}""".stripMargin, + existingFeature( + { (x: (Int, Int)) => + val a = x._1; + val b = x._2 + val plus = n.plus(a, b) + val minus = n.minus(a, b) + val mul = n.times(a, b) + val div = a / b + val mod = a % b + (plus, (minus, (mul, (div, mod)))) + }, + """{ (x: (Int, Int)) => + | val a = x._1; val b = x._2 + | val plus = a + b + | val minus = a - b + | val mul = a * b + | val div = a / b + | val mod = a % b + | (plus, (minus, (mul, (div, mod)))) + |}""".stripMargin, FuncValue( Vector((1, STuple(Vector(SInt, SInt)))), BlockValue( @@ -1569,7 +1439,9 @@ class SigmaDslSpecification extends SigmaDslTesting property("Int LT, GT, NEQ") { val o = ExactOrdering.IntIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SInt), 1768) + + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SInt), 1768, Seq.fill(4)(2012)) + val LT_cases: Seq[((Int, Int), Expected[Boolean])] = Seq( (Int.MinValue, Int.MinValue) -> expect(false), (Int.MinValue, (Int.MinValue + 1).toInt) -> expect(true), @@ -1610,16 +1482,16 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LT_cases, "<", LT.apply)(_ < _) verifyOp( - swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SInt)), + swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SInt), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(_ > _) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost), expectedV3Costs = Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } property("Int LE, GE") { val o = ExactOrdering.IntIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SInt), 1768) + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SInt), 1768, Seq.fill(4)(2012)) val LE_cases: Seq[((Int, Int), Expected[Boolean])] = Seq( (Int.MinValue, Int.MinValue) -> expect(true), (Int.MinValue, (Int.MinValue + 1).toInt) -> expect(true), @@ -1661,32 +1533,10 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LE_cases, "<=", LE.apply)(_ <= _) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SInt)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SInt), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(_ >= _) } - property("Int methods equivalence (new features)") { - lazy val toBytes = newFeature((x: Int) => x.toBytes, "{ (x: Int) => x.toBytes }") - lazy val toAbs = newFeature((x: Int) => x.toAbs, "{ (x: Int) => x.toAbs }") - lazy val compareTo = newFeature((x: (Int, Int)) => x._1.compareTo(x._2), - "{ (x: (Int, Int)) => x._1.compareTo(x._2) }") - - lazy val bitOr = newFeature( - { (x: (Int, Int)) => x._1 | x._2 }, - "{ (x: (Int, Int)) => x._1 | x._2 }") - - lazy val bitAnd = newFeature( - { (x: (Int, Int)) => x._1 & x._2 }, - "{ (x: (Int, Int)) => x._1 & x._2 }") - - forAll { x: Int => - Seq(toBytes, toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (Int, Int) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } - property("Long downcast and upcast identity") { forAll { x: Long => SLong.upcast(x) shouldBe x // boundary test case @@ -1697,7 +1547,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("Long.toByte method") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SByte), 1764, Seq.fill(4)(1998)) Seq( (Long.MinValue, Expected(new ArithmeticException("Byte overflow"))), (Byte.MinValue.toLong - 1, Expected(new ArithmeticException("Byte overflow"))), @@ -1718,7 +1568,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("Long.toShort method") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SShort), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SShort), 1764, Seq.fill(4)(1998)) Seq( (Long.MinValue, Expected(new ArithmeticException("Short overflow"))), (Short.MinValue.toLong - 1, Expected(new ArithmeticException("Short overflow"))), @@ -1739,7 +1589,8 @@ class SigmaDslSpecification extends SigmaDslTesting property("Long.toInt method") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SInt), 1764) + def success[T](v: T) = Expected(Success(v), 1764, downcastCostDetails(SInt), 1764, Seq.fill(4)(1998)) + Seq( (Long.MinValue, Expected(new ArithmeticException("Int overflow"))), (Int.MinValue.toLong - 1, Expected(new ArithmeticException("Int overflow"))), @@ -1760,7 +1611,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("Long.toLong method") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763) + def success[T](v: T) = Expected(Success(v), 1763, TracedCost(traceBase), 1763, Seq.fill(4)(1993)) Seq( (Long.MinValue, success(Long.MinValue)), (-1L, success(-1L)), @@ -1777,7 +1628,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("Long.toBigInt method") { verifyCases( { - def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767) + def success(v: BigInt) = Expected(Success(v), 1767, upcastCostDetails(SBigInt), 1767, Seq.fill(4)(2001)) Seq( (Long.MinValue, success(CBigInt(new BigInteger("-8000000000000000", 16)))), (-1074651039980347209L, success(CBigInt(new BigInteger("-ee9ed6d57885f49", 16)))), @@ -1797,83 +1648,85 @@ class SigmaDslSpecification extends SigmaDslTesting val n = ExactNumeric.LongIsExactNumeric verifyCases( - { - def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SLong), 1788) - Seq( - ((Long.MinValue, -4677100190307931395L), Expected(new ArithmeticException("long overflow"))), - ((Long.MinValue, -1L), Expected(new ArithmeticException("long overflow"))), - ((Long.MinValue, 1L), Expected(new ArithmeticException("long overflow"))), - ((-9223372036854775808L, 0L), Expected(new ArithmeticException("/ by zero"))), - ((-5828066432064138816L, 9105034716270510411L), Expected(new ArithmeticException("long overflow"))), - ((-4564956247298949325L, -1L), success( - (-4564956247298949326L, (-4564956247298949324L, (4564956247298949325L, (4564956247298949325L, 0L)))) - )), - ((-1499553565058783253L, -3237683216870282569L), Expected(new ArithmeticException("long overflow"))), - ((-1368457031689886112L, 9223372036854775807L), Expected(new ArithmeticException("long overflow"))), - ((-1L, -4354407074688367443L), success((-4354407074688367444L, (4354407074688367442L, (4354407074688367443L, (0L, -1L)))))), - ((-1L, -1L), success((-2L, (0L, (1L, (1L, 0L)))))), - ((-1L, 5665019549505434695L), success((5665019549505434694L, (-5665019549505434696L, (-5665019549505434695L, (0L, -1L)))))), - ((0L, -1L), success((-1L, (1L, (0L, (0L, 0L)))))), - ((0L, 0L), Expected(new ArithmeticException("/ by zero"))), - ((0L, 2112386634269044172L), success((2112386634269044172L, (-2112386634269044172L, (0L, (0L, 0L)))))), - ((2254604056782701370L, -5878231674026236574L), Expected(new ArithmeticException("long overflow"))), - ((2903872550238813643L, 1L), success( - (2903872550238813644L, (2903872550238813642L, (2903872550238813643L, (2903872550238813643L, 0L)))) - )), - ((5091129735284641762L, -427673944382373638L), Expected(new ArithmeticException("long overflow"))), - ((6029085020194630780L, 2261786144956037939L), Expected(new ArithmeticException("long overflow"))), - ((8126382074515995418L, -4746652047588907829L), Expected(new ArithmeticException("long overflow"))), - ((Long.MaxValue, 1L), Expected(new ArithmeticException("long overflow"))), - ((Long.MaxValue, -1L), Expected(new ArithmeticException("long overflow"))) - ) - }, - existingFeature( - { (x: (Long, Long)) => - val a = x._1; val b = x._2 - val plus = n.plus(a, b) - val minus = n.minus(a, b) - val mul = n.times(a, b) - val div = a / b - val mod = a % b - (plus, (minus, (mul, (div, mod)))) + { + def success[T](v: T) = Expected(Success(v), 1788, arithOpsCostDetails(SLong), 1788, Seq.fill(4)(2116)) + Seq( + ((Long.MinValue, -4677100190307931395L), Expected(new ArithmeticException("long overflow"))), + ((Long.MinValue, -1L), Expected(new ArithmeticException("long overflow"))), + ((Long.MinValue, 1L), Expected(new ArithmeticException("long overflow"))), + ((-9223372036854775808L, 0L), Expected(new ArithmeticException("/ by zero"))), + ((-5828066432064138816L, 9105034716270510411L), Expected(new ArithmeticException("long overflow"))), + ((-4564956247298949325L, -1L), success( + (-4564956247298949326L, (-4564956247298949324L, (4564956247298949325L, (4564956247298949325L, 0L)))) + )), + ((-1499553565058783253L, -3237683216870282569L), Expected(new ArithmeticException("long overflow"))), + ((-1368457031689886112L, 9223372036854775807L), Expected(new ArithmeticException("long overflow"))), + ((-1L, -4354407074688367443L), success((-4354407074688367444L, (4354407074688367442L, (4354407074688367443L, (0L, -1L)))))), + ((-1L, -1L), success((-2L, (0L, (1L, (1L, 0L)))))), + ((-1L, 5665019549505434695L), success((5665019549505434694L, (-5665019549505434696L, (-5665019549505434695L, (0L, -1L)))))), + ((0L, -1L), success((-1L, (1L, (0L, (0L, 0L)))))), + ((0L, 0L), Expected(new ArithmeticException("/ by zero"))), + ((0L, 2112386634269044172L), success((2112386634269044172L, (-2112386634269044172L, (0L, (0L, 0L)))))), + ((2254604056782701370L, -5878231674026236574L), Expected(new ArithmeticException("long overflow"))), + ((2903872550238813643L, 1L), success( + (2903872550238813644L, (2903872550238813642L, (2903872550238813643L, (2903872550238813643L, 0L)))) + )), + ((5091129735284641762L, -427673944382373638L), Expected(new ArithmeticException("long overflow"))), + ((6029085020194630780L, 2261786144956037939L), Expected(new ArithmeticException("long overflow"))), + ((8126382074515995418L, -4746652047588907829L), Expected(new ArithmeticException("long overflow"))), + ((Long.MaxValue, 1L), Expected(new ArithmeticException("long overflow"))), + ((Long.MaxValue, -1L), Expected(new ArithmeticException("long overflow"))) + ) }, - """{ (x: (Long, Long)) => - | val a = x._1; val b = x._2 - | val plus = a + b - | val minus = a - b - | val mul = a * b - | val div = a / b - | val mod = a % b - | (plus, (minus, (mul, (div, mod)))) - |}""".stripMargin, - FuncValue( - Vector((1, STuple(Vector(SLong, SLong)))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 1.toByte) - ), - ValDef( - 4, - List(), - SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 2.toByte) - ) - ), - Tuple( + existingFeature( + { (x: (Long, Long)) => + val a = x._1; + val b = x._2 + val plus = n.plus(a, b) + val minus = n.minus(a, b) + val mul = n.times(a, b) + val div = a / b + val mod = a % b + (plus, (minus, (mul, (div, mod)))) + }, + """{ (x: (Long, Long)) => + | val a = x._1; val b = x._2 + | val plus = a + b + | val minus = a - b + | val mul = a * b + | val div = a / b + | val mod = a % b + | (plus, (minus, (mul, (div, mod)))) + |}""".stripMargin, + FuncValue( + Vector((1, STuple(Vector(SLong, SLong)))), + BlockValue( Vector( - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-102.toByte)), - Tuple( - Vector( - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-103.toByte)), - Tuple( - Vector( - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-100.toByte)), - Tuple( - Vector( - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-99.toByte)), - ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-98.toByte)) + ValDef( + 3, + List(), + SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 1.toByte) + ), + ValDef( + 4, + List(), + SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 2.toByte) + ) + ), + Tuple( + Vector( + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-102.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-103.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-100.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-99.toByte)), + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-98.toByte)) + ) ) ) ) @@ -1882,13 +1735,12 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) ) - ) - ))) + ))) } property("Long LT, GT, NEQ") { val o = ExactOrdering.LongIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SLong), 1768) + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SLong), 1768, Seq.fill(4)(2012)) val LT_cases: Seq[((Long, Long), Expected[Boolean])] = Seq( (Long.MinValue, Long.MinValue) -> expect(false), (Long.MinValue, (Long.MinValue + 1).toLong) -> expect(true), @@ -1929,16 +1781,16 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LT_cases, "<", LT.apply)(_ < _) verifyOp( - swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SLong)), + swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SLong), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(_ > _) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constNeqCost), Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } property("Long LE, GE") { val o = ExactOrdering.LongIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SLong), 1768) + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SLong), 1768, Seq.fill(4)(2012)) val LE_cases: Seq[((Long, Long), Expected[Boolean])] = Seq( (Long.MinValue, Long.MinValue) -> expect(true), (Long.MinValue, (Long.MinValue + 1).toLong) -> expect(true), @@ -1980,36 +1832,14 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LE_cases, "<=", LE.apply)(_ <= _) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SLong)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SLong), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(_ >= _) } - property("Long methods equivalence (new features)") { - lazy val toBytes = newFeature((x: Long) => x.toBytes, "{ (x: Long) => x.toBytes }") - lazy val toAbs = newFeature((x: Long) => x.toAbs, "{ (x: Long) => x.toAbs }") - lazy val compareTo = newFeature((x: (Long, Long)) => x._1.compareTo(x._2), - "{ (x: (Long, Long)) => x._1.compareTo(x._2) }") - - lazy val bitOr = newFeature( - { (x: (Long, Long)) => x._1 | x._2 }, - "{ (x: (Long, Long)) => x._1 | x._2 }") - - lazy val bitAnd = newFeature( - { (x: (Long, Long)) => x._1 & x._2 }, - "{ (x: (Long, Long)) => x._1 & x._2 }") - - forAll { x: Long => - Seq(toBytes, toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (Long, Long) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } - property("BigInt methods equivalence") { verifyCases( { - def success(v: BigInt) = Expected(Success(v), 1764, TracedCost(traceBase), 1764) + def success(v: BigInt) = Expected(Success(v), 1764, TracedCost(traceBase), 1764, Seq.fill(4)(1994)) Seq( (CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)), success( CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)) @@ -2030,11 +1860,11 @@ class SigmaDslSpecification extends SigmaDslTesting val n = NumericOps.BigIntIsExactIntegral verifyCases( - { - def success(v: (BigInt, (BigInt, (BigInt, (BigInt, BigInt))))) = - Expected(Success(v), 1793, arithOpsCostDetails(SBigInt), 1793) - Seq( - ((CBigInt(new BigInteger("-8683d1cd99d5fcf0e6eff6295c285c36526190e13dbde008c49e5ae6fddc1c", 16)), + { + def success(v: (BigInt, (BigInt, (BigInt, (BigInt, BigInt))))) = + Expected(Success(v), 1793, arithOpsCostDetails(SBigInt), 1793, Seq.fill(4)(2121)) + Seq( + ((CBigInt(new BigInteger("-8683d1cd99d5fcf0e6eff6295c285c36526190e13dbde008c49e5ae6fddc1c", 16)), CBigInt(new BigInteger("-2ef55db3f245feddacf0182e299dd", 16))), Expected(new ArithmeticException("BigInteger out of 256 bit range"))), @@ -2156,8 +1986,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("BigInt LT, GT, NEQ") { val o = NumericOps.BigIntIsExactOrdering - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SBigInt), 1768) - + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LT, SBigInt), 1768, Seq.fill(4)(2012)) val LT_cases: Seq[((BigInt, BigInt), Expected[Boolean])] = Seq( (BigIntMinValue, BigIntMinValue) -> expect(false), (BigIntMinValue, BigIntMinValue.add(1.toBigInt)) -> expect(true), @@ -2200,11 +2029,11 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LT_cases, "<", LT.apply)(o.lt(_, _)) verifyOp( - swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SBigInt)), + swapArgs(LT_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GT, SBigInt), expectedV3Costs = Seq.fill(4)(2012)), ">", GT.apply)(o.gt(_, _)) val constBigIntCost = Array[CostItem](FixedCostItem(NamedDesc("EQ_BigInt"), FixedCost(JitCost(5)))) - val neqCases = newCasesFrom2(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constBigIntCost)) + val neqCases = newCasesFrom3(LT_cases.map(_._1))(_ != _, cost = 1766, newCostDetails = costNEQ(constBigIntCost), expectedV3Costs = Seq.fill(4)(2010)) verifyOp(neqCases, "!=", NEQ.apply)(_ != _) } @@ -2215,8 +2044,7 @@ class SigmaDslSpecification extends SigmaDslTesting val BigIntMaxValue = CBigInt(new BigInteger("7F" + "ff" * 31, 16)) val BigIntOverlimit = CBigInt(new BigInteger("7F" + "ff" * 33, 16)) - def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SBigInt), 1768) - + def expect(v: Boolean) = Expected(Success(v), 1768, binaryRelationCostDetails(LE, SBigInt), 1768, Seq.fill(4)(2012)) val LE_cases: Seq[((BigInt, BigInt), Expected[Boolean])] = Seq( (BigIntMinValue, BigIntMinValue) -> expect(true), (BigIntMinValue, BigIntMinValue.add(1.toBigInt)) -> expect(true), @@ -2260,74 +2088,21 @@ class SigmaDslSpecification extends SigmaDslTesting verifyOp(LE_cases, "<=", LE.apply)(o.lteq(_, _)) verifyOp( - swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SBigInt)), + swapArgs(LE_cases, cost = 1768, newCostDetails = binaryRelationCostDetails(GE, SBigInt), expectedV3Costs = Seq.fill(4)(2012)), ">=", GE.apply)(o.gteq(_, _)) } - property("BigInt methods equivalence (new features)") { - // TODO v6.0: the behavior of `upcast` for BigInt is different from all other Numeric types (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/877) - // The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. - // It makes sense to fix this inconsistency as part of upcoming forks - assertExceptionThrown( - SBigInt.upcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), - _.getMessage.contains("Cannot upcast value") - ) - - // TODO v6.0: the behavior of `downcast` for BigInt is different from all other Numeric types (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/877) - // The `Downcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. - // It makes sense to fix this inconsistency as part of HF - assertExceptionThrown( - SBigInt.downcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), - _.getMessage.contains("Cannot downcast value") - ) - - val toByte = newFeature((x: BigInt) => x.toByte, - "{ (x: BigInt) => x.toByte }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SByte))) - - val toShort = newFeature((x: BigInt) => x.toShort, - "{ (x: BigInt) => x.toShort }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SShort))) - - val toInt = newFeature((x: BigInt) => x.toInt, - "{ (x: BigInt) => x.toInt }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SInt))) - - val toLong = newFeature((x: BigInt) => x.toLong, - "{ (x: BigInt) => x.toLong }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SLong))) - - lazy val toBytes = newFeature((x: BigInt) => x.toBytes, "{ (x: BigInt) => x.toBytes }") - lazy val toAbs = newFeature((x: BigInt) => x.toAbs, "{ (x: BigInt) => x.toAbs }") - - lazy val compareTo = newFeature((x: (BigInt, BigInt)) => x._1.compareTo(x._2), - "{ (x: (BigInt, BigInt)) => x._1.compareTo(x._2) }") - - lazy val bitOr = newFeature({ (x: (BigInt, BigInt)) => x._1 | x._2 }, - "{ (x: (BigInt, BigInt)) => x._1 | x._2 }") - - lazy val bitAnd = newFeature({ (x: (BigInt, BigInt)) => x._1 & x._2 }, - "{ (x: (BigInt, BigInt)) => x._1 & x._2 }") - - forAll { x: BigInt => - Seq(toByte, toShort, toInt, toLong, toBytes, toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (BigInt, BigInt) => - Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } - /** Executed a series of test cases of NEQ operation verify using two _different_ * data instances `x` and `y`. * @param cost the expected cost of `verify` (the same for all cases) */ def verifyNeq[A: Ordering: Arbitrary: RType] - (x: A, y: A, cost: Int, neqCost: Seq[CostItem] = ArraySeq.empty, newCost: Int) + (x: A, y: A, cost: Int, neqCost: Seq[CostItem] = ArraySeq.empty, newCost: Int, expectedV3Costs: Seq[Int]) (copy: A => A, generateCases: Boolean = true) (implicit sampled: Sampled[(A, A)], evalSettings: EvalSettings) = { val copied_x = copy(x) val newCostDetails = if (neqCost.isEmpty) CostDetails.ZeroCost else costNEQ(neqCost) - def expected(v: Boolean) = Expected(Success(v), cost, newCostDetails, newCost) + def expected(v: Boolean) = Expected(Success(v), cost, newCostDetails, newCost, expectedV3Costs) def expectedNoCost(v: Boolean) = new Expected(ExpectedResult(Success(v), None)) verifyOp(Seq( (x, y) -> expected(true), // check cost only for this test case, because the trace depends in x and y @@ -2340,11 +2115,11 @@ class SigmaDslSpecification extends SigmaDslTesting } property("NEQ of pre-defined types") { - verifyNeq(ge1, ge2, 1783, Array[CostItem](FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172)))), 1783)(_.asInstanceOf[CGroupElement].copy()) - verifyNeq(t1, t2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_AvlTree"), FixedCost(JitCost(6)))), 1767)(_.asInstanceOf[CAvlTree].copy()) - verifyNeq(b1, b2, 1767, Array[CostItem](), 1767)(_.asInstanceOf[CBox].copy()) - verifyNeq(preH1, preH2, 1766, Array[CostItem](FixedCostItem(NamedDesc("EQ_PreHeader"), FixedCost(JitCost(4)))), 1766)(_.asInstanceOf[CPreHeader].copy()) - verifyNeq(h1, h2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6)))), 1767)(_.asInstanceOf[CHeader].copy()) + verifyNeq(ge1, ge2, 1783, Array[CostItem](FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172)))), 1783, Seq.fill(4)(2027))(_.asInstanceOf[CGroupElement].copy()) + verifyNeq(t1, t2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_AvlTree"), FixedCost(JitCost(6)))), 1767, Seq.fill(4)(2019))(_.asInstanceOf[CAvlTree].copy()) + verifyNeq(b1, b2, 1767, Array[CostItem](), 1767, Seq.fill(4)(2019))(_.asInstanceOf[CBox].copy()) + verifyNeq(preH1, preH2, 1766, Array[CostItem](FixedCostItem(NamedDesc("EQ_PreHeader"), FixedCost(JitCost(4)))), 1766, Seq.fill(4)(2018))(_.asInstanceOf[CPreHeader].copy()) + verifyNeq(h1, h2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6)))), 1767, Seq.fill(4)(2019))(_.asInstanceOf[CHeader].copy()) } property("NEQ of tuples of numerics") { @@ -2352,14 +2127,14 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_Prim"), FixedCost(JitCost(3))) ) - verifyNeq((0.toByte, 1.toByte), (1.toByte, 1.toByte), 1767, tuplesNeqCost, 1767)(_.copy()) - verifyNeq((0.toShort, 1.toByte), (1.toShort, 1.toByte), 1767, tuplesNeqCost, 1767)(_.copy()) - verifyNeq((0, 1.toByte), (1, 1.toByte), 1767, tuplesNeqCost, 1767)(_.copy()) - verifyNeq((0.toLong, 1.toByte), (1.toLong, 1.toByte), 1767, tuplesNeqCost, 1767)(_.copy()) + verifyNeq((0.toByte, 1.toByte), (1.toByte, 1.toByte), 1767, tuplesNeqCost, 1767, Seq.fill(4)(2019))(_.copy()) + verifyNeq((0.toShort, 1.toByte), (1.toShort, 1.toByte), 1767, tuplesNeqCost, 1767, Seq.fill(4)(2029))(_.copy()) + verifyNeq((0, 1.toByte), (1, 1.toByte), 1767, tuplesNeqCost, 1767, Seq.fill(4)(2029))(_.copy()) + verifyNeq((0.toLong, 1.toByte), (1.toLong, 1.toByte), 1767, tuplesNeqCost, 1767, Seq.fill(4)(2029))(_.copy()) verifyNeq((0.toBigInt, 1.toByte), (1.toBigInt, 1.toByte), 1767, Array( FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_BigInt"), FixedCost(JitCost(5))) - ), 1767)(_.copy()) + ), 1767, Seq.fill(4)(2029))(_.copy()) } property("NEQ of tuples of pre-defined types") { @@ -2368,30 +2143,29 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172))), FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172))) ) - verifyNeq((ge1, ge1), (ge1, ge2), 1801, groupNeqCost, 1801)(_.copy()) + verifyNeq((ge1, ge1), (ge1, ge2), 1801, groupNeqCost, 1801, Seq.fill(4)(2053))(_.copy()) val treeNeqCost = Array( FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_AvlTree"), FixedCost(JitCost(6))), FixedCostItem(NamedDesc("EQ_AvlTree"), FixedCost(JitCost(6))) ) - verifyNeq((t1, t1), (t1, t2), 1768, treeNeqCost, 1768)(_.copy()) - - verifyNeq((b1, b1), (b1, b2), 1768, Array[CostItem](), 1768)(_.copy()) + verifyNeq((t1, t1), (t1, t2), 1768, treeNeqCost, 1768, Seq.fill(4)(2038))(_.copy()) + verifyNeq((b1, b1), (b1, b2), 1768, Array[CostItem](), 1768, Seq.fill(4)(2038))(_.copy()) val preHeaderNeqCost = Array( FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_PreHeader"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_PreHeader"), FixedCost(JitCost(4))) ) - verifyNeq((preH1, preH1), (preH1, preH2), 1767, preHeaderNeqCost, 1767)(_.copy()) + verifyNeq((preH1, preH1), (preH1, preH2), 1767, preHeaderNeqCost, 1767, Seq.fill(4)(2037))(_.copy()) val headerNeqCost = Array( FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6))), FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6))) ) - verifyNeq((h1, h1), (h1, h2), 1768, headerNeqCost, 1768)(_.copy()) + verifyNeq((h1, h1), (h1, h2), 1768, headerNeqCost, 1768, Seq.fill(4)(2038))(_.copy()) } property("NEQ of nested tuples") { @@ -2475,15 +2249,15 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6))), FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6))) ) - verifyNeq((ge1, (t1, t1)), (ge1, (t1, t2)), 1785, nestedTuplesNeqCost1, 1785)(_.copy()) - verifyNeq((ge1, (t1, (b1, b1))), (ge1, (t1, (b1, b2))), 1786, nestedTuplesNeqCost2, 1786)(_.copy()) - verifyNeq((ge1, (t1, (b1, (preH1, preH1)))), (ge1, (t1, (b1, (preH1, preH2)))), 1787, nestedTuplesNeqCost3, 1787)(_.copy()) - verifyNeq((ge1, (t1, (b1, (preH1, (h1, h1))))), (ge1, (t1, (b1, (preH1, (h1, h2))))), 1788, nestedTuplesNeqCost4, 1788)(_.copy()) + verifyNeq((ge1, (t1, t1)), (ge1, (t1, t2)), 1785, nestedTuplesNeqCost1, 1785, Seq.fill(4)(2063))(_.copy()) + verifyNeq((ge1, (t1, (b1, b1))), (ge1, (t1, (b1, b2))), 1786, nestedTuplesNeqCost2, 1786, Seq.fill(4)(2080))(_.copy()) + verifyNeq((ge1, (t1, (b1, (preH1, preH1)))), (ge1, (t1, (b1, (preH1, preH2)))), 1787, nestedTuplesNeqCost3, 1787, Seq.fill(4)(2097))(_.copy()) + verifyNeq((ge1, (t1, (b1, (preH1, (h1, h1))))), (ge1, (t1, (b1, (preH1, (h1, h2))))), 1788, nestedTuplesNeqCost4, 1788, Seq.fill(4)(2114))(_.copy()) - verifyNeq(((ge1, t1), t1), ((ge1, t1), t2), 1785, nestedTuplesNeqCost5, 1785)(_.copy()) - verifyNeq((((ge1, t1), b1), b1), (((ge1, t1), b1), b2), 1786, nestedTuplesNeqCost6, 1786)(_.copy()) - verifyNeq((((ge1, t1), b1), (preH1, preH1)), (((ge1, t1), b1), (preH1, preH2)), 1787, nestedTuplesNeqCost7, 1787)(_.copy()) - verifyNeq((((ge1, t1), b1), (preH1, (h1, h1))), (((ge1, t1), b1), (preH1, (h1, h2))), 1788, nestedTuplesNeqCost8, 1788)(_.copy()) + verifyNeq(((ge1, t1), t1), ((ge1, t1), t2), 1785, nestedTuplesNeqCost5, 1785, Seq.fill(4)(2063))(_.copy()) + verifyNeq((((ge1, t1), b1), b1), (((ge1, t1), b1), b2), 1786, nestedTuplesNeqCost6, 1786, Seq.fill(4)(2080))(_.copy()) + verifyNeq((((ge1, t1), b1), (preH1, preH1)), (((ge1, t1), b1), (preH1, preH2)), 1787, nestedTuplesNeqCost7, 1787, Seq.fill(4)(2097))(_.copy()) + verifyNeq((((ge1, t1), b1), (preH1, (h1, h1))), (((ge1, t1), b1), (preH1, (h1, h2))), 1788, nestedTuplesNeqCost8, 1788, Seq.fill(4)(2114))(_.copy()) } property("NEQ of collections of pre-defined types") { @@ -2495,63 +2269,71 @@ class SigmaDslSpecification extends SigmaDslTesting ast.SeqCostItem(NamedDesc("EQ_COA_Box"), PerItemCost(JitCost(15), JitCost(5), 1), 0) ) implicit val evalSettings = suite.evalSettings.copy(isMeasureOperationTime = false) - verifyNeq(Coll[Byte](), Coll(1.toByte), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Byte](), Coll(1.toByte), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[Byte](0, 1), Coll(1.toByte, 1.toByte), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Byte"), PerItemCost(JitCost(15), JitCost(2), 128), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) - verifyNeq(Coll[Short](), Coll(1.toShort), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Short](), Coll(1.toShort), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[Short](0), Coll(1.toShort), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Short"), PerItemCost(JitCost(15), JitCost(2), 96), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) - verifyNeq(Coll[Int](), Coll(1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Int](), Coll(1), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[Int](0), Coll(1), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Int"), PerItemCost(JitCost(15), JitCost(2), 64), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) - verifyNeq(Coll[Long](), Coll(1.toLong), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Long](), Coll(1.toLong), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[Long](0), Coll(1.toLong), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Long"), PerItemCost(JitCost(15), JitCost(2), 48), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) prepareSamples[Coll[BigInt]] - verifyNeq(Coll[BigInt](), Coll(1.toBigInt), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[BigInt](), Coll(1.toBigInt), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[BigInt](0.toBigInt), Coll(1.toBigInt), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_BigInt"), PerItemCost(JitCost(15), JitCost(7), 5), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) prepareSamples[Coll[GroupElement]] - verifyNeq(Coll[GroupElement](), Coll(ge1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[GroupElement](), Coll(ge1), 1766, collNeqCost1, 1766, Seq.fill(4)(2018))(cloneColl(_)) verifyNeq(Coll[GroupElement](ge1), Coll(ge2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_GroupElement"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), - 1768 + 1768, + Seq.fill(4)(2020) )(cloneColl(_)) prepareSamples[Coll[AvlTree]] - verifyNeq(Coll[AvlTree](), Coll(t1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[AvlTree](), Coll(t1), 1766, collNeqCost1, 1766, Seq.fill(4)(2028))(cloneColl(_)) + verifyNeq(Coll[AvlTree](t1), Coll(t2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_AvlTree"), PerItemCost(JitCost(15), JitCost(5), 2), 1)), - 1768 + 1768, + Seq.fill(4)(2030) )(cloneColl(_)) { // since SBox.isConstantSize = false, the cost is different among cases @@ -2560,38 +2342,40 @@ class SigmaDslSpecification extends SigmaDslTesting val y = Coll(b1) val copied_x = cloneColl(x) verifyOp(Seq( - (x, x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768), - (x, copied_x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768), - (copied_x, x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768), - (x, y) -> Expected(Success(true), 1766, costNEQ(collNeqCost1), 1766), - (y, x) -> Expected(Success(true), 1766, costNEQ(collNeqCost1), 1766) - ), + (x, x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768, Seq.fill(4)(2030)), + (x, copied_x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768, Seq.fill(4)(2030)), + (copied_x, x) -> Expected(Success(false), 1768, costNEQ(collNeqCost2), 1768, Seq.fill(4)(2030)), + (x, y) -> Expected(Success(true), 1766, costNEQ(collNeqCost1), 1766, Seq.fill(4)(2028)), + (y, x) -> Expected(Success(true), 1766, costNEQ(collNeqCost1), 1766, Seq.fill(4)(2028)) + ), "!=", NEQ.apply)(_ != _, generateCases = false) verifyNeq(Coll[Box](b1), Coll(b2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Box"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), - 1768 + 1768, + Seq.fill(4)(2030) )(cloneColl(_), generateCases = false) } prepareSamples[Coll[PreHeader]] - verifyNeq(Coll[PreHeader](), Coll(preH1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[PreHeader](), Coll(preH1), 1766, collNeqCost1, 1766, Seq.fill(4)(2028))(cloneColl(_)) verifyNeq(Coll[PreHeader](preH1), Coll(preH2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_PreHeader"), PerItemCost(JitCost(15), JitCost(3), 1), 1)), - 1768 + 1768, + Seq.fill(4)(2030) )(cloneColl(_)) prepareSamples[Coll[Header]] - verifyNeq(Coll[Header](), Coll(h1), 1766, collNeqCost1, 1766)(cloneColl(_)) + verifyNeq(Coll[Header](), Coll(h1), 1766, collNeqCost1, 1766, Seq.fill(4)(2028))(cloneColl(_)) verifyNeq(Coll[Header](h1), Coll(h2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), ast.SeqCostItem(NamedDesc("EQ_COA_Header"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), - 1768 + 1768, Seq.fill(4)(2030) )(cloneColl(_)) } @@ -2615,10 +2399,10 @@ class SigmaDslSpecification extends SigmaDslTesting ast.SeqCostItem(NamedDesc("EQ_COA_Int"), PerItemCost(JitCost(15), JitCost(2), 64), 1), ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) ) - verifyNeq(Coll[Coll[Int]](), Coll(Coll[Int]()), 1766, nestedNeq1, 1766)(cloneColl(_)) - verifyNeq(Coll(Coll[Int]()), Coll(Coll[Int](1)), 1767, nestedNeq2, 1767)(cloneColl(_)) - verifyNeq(Coll(Coll[Int](1)), Coll(Coll[Int](2)), 1769, nestedNeq3, 1769)(cloneColl(_)) - verifyNeq(Coll(Coll[Int](1)), Coll(Coll[Int](1, 2)), 1767, nestedNeq2, 1767)(cloneColl(_)) + verifyNeq(Coll[Coll[Int]](), Coll(Coll[Int]()), 1766, nestedNeq1, 1766, Seq.fill(4)(2018))(cloneColl(_)) + verifyNeq(Coll(Coll[Int]()), Coll(Coll[Int](1)), 1767, nestedNeq2, 1767, Seq.fill(4)(2019))(cloneColl(_)) + verifyNeq(Coll(Coll[Int](1)), Coll(Coll[Int](2)), 1769, nestedNeq3, 1769, Seq.fill(4)(2021))(cloneColl(_)) + verifyNeq(Coll(Coll[Int](1)), Coll(Coll[Int](1, 2)), 1767, nestedNeq2, 1767, Seq.fill(4)(2019))(cloneColl(_)) prepareSamples[Coll[(Int, BigInt)]] prepareSamples[Coll[Coll[(Int, BigInt)]]] @@ -2661,8 +2445,8 @@ class SigmaDslSpecification extends SigmaDslTesting ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) ) - verifyNeq(Coll(Coll((1, 10.toBigInt))), Coll(Coll((1, 11.toBigInt))), 1770, nestedNeq4, 1770)(cloneColl(_)) - verifyNeq(Coll(Coll(Coll((1, 10.toBigInt)))), Coll(Coll(Coll((1, 11.toBigInt)))), 1771, nestedNeq5, 1771)(cloneColl(_)) + verifyNeq(Coll(Coll((1, 10.toBigInt))), Coll(Coll((1, 11.toBigInt))), 1770, nestedNeq4, 1770, Seq.fill(4)(2048))(cloneColl(_)) + verifyNeq(Coll(Coll(Coll((1, 10.toBigInt)))), Coll(Coll(Coll((1, 11.toBigInt)))), 1771, nestedNeq5, 1771, Seq.fill(4)(2057))(cloneColl(_)) verifyNeq( (Coll( (Coll( @@ -2676,14 +2460,15 @@ class SigmaDslSpecification extends SigmaDslTesting ), preH1), 1774, nestedNeq6, - 1774 + 1774, + Seq.fill(4)(2100) )(x => (cloneColl(x._1), x._2)) } property("GroupElement.getEncoded equivalence") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1790, methodCostDetails(SGroupElementMethods.GetEncodedMethod, 250), 1790) + def success[T](v: T) = Expected(Success(v), 1790, methodCostDetails(SGroupElementMethods.GetEncodedMethod, 250), 1790, Seq.fill(4)(2026)) Seq( (ge1, success(Helpers.decodeBytes(ge1str))), (ge2, success(Helpers.decodeBytes(ge2str))), @@ -2714,7 +2499,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172))) ) ) - def success[T](v: T) = Expected(Success(v), 1837, costDetails, 1837) + def success[T](v: T) = Expected(Success(v), 1837, costDetails, 1837, Seq.fill(4)(2081)) Seq( (ge1, success(true)), (ge2, success(true)), @@ -2744,7 +2529,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("GroupElement.negate equivalence") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1785, methodCostDetails(SGroupElementMethods.NegateMethod, 45), 1785) + def success[T](v: T) = Expected(Success(v), 1785, methodCostDetails(SGroupElementMethods.NegateMethod, 45), 1785, Seq.fill(4)(2021)) Seq( (ge1, success(Helpers.decodeGroupElement("02358d53f01276211f92d0aefbd278805121d4ff6eb534b777af1ee8abae5b2056"))), (ge2, success(Helpers.decodeGroupElement("03dba7b94b111f3894e2f9120b577da595ec7d58d488485adf73bf4e153af63575"))), @@ -2762,8 +2547,9 @@ class SigmaDslSpecification extends SigmaDslTesting } property("GroupElement.exp equivalence") { - def cases(cost: Int, details: CostDetails) = { - def success[T](v: T) = Expected(Success(v), cost, details, cost) + def cases(cost: Int, details: CostDetails, expectedV3Costs: Seq[Int]) = { + def success[T](v: T) = Expected(Success(v), cost, details, cost, expectedV3Costs) + Seq( ((ge1, CBigInt(new BigInteger("-25c80b560dd7844e2efd10f80f7ee57d", 16))), success(Helpers.decodeGroupElement("023a850181b7b73f92a5bbfa0bfc78f5bbb6ff00645ddde501037017e1a2251e2e"))), @@ -2786,7 +2572,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(Exponentiate) ) ) - verifyCases(cases(1873, costDetails), + verifyCases(cases(1873, costDetails, Seq.fill(4)(2121)), existingFeature( scalaFunc, script, @@ -2813,7 +2599,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SGroupElementMethods.ExponentiateMethod, FixedCost(JitCost(900))) ) ) - verifyCases(cases(1873, costDetails), + verifyCases(cases(1873, costDetails, Seq.fill(4)(2121)), existingFeature( scalaFunc, script, @@ -2860,7 +2646,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) ) - def success[T](v: T) = Expected(Success(v), 1787, costDetails, 1787) + def success[T](v: T) = Expected(Success(v), 1787, costDetails, 1787, Seq.fill(4)(2031)) Seq( ((ge1, Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), success(Helpers.decodeGroupElement("02bc48937b4a66f249a32dfb4d2efd0743dc88d46d770b8c5d39fd03325ba211df"))), @@ -2923,7 +2709,7 @@ class SigmaDslSpecification extends SigmaDslTesting } verifyCases( { - def success[T](v: T) = Expected(Success(v), 1767, methodCostDetails(SAvlTreeMethods.digestMethod, 15), 1767) + def success[T](v: T) = Expected(Success(v), 1767, methodCostDetails(SAvlTreeMethods.digestMethod, 15), 1767, Seq.fill(4)(2003)) Seq( (t1, success(Helpers.decodeBytes("000183807f66b301530120ff7fc6bd6601ff01ff7f7d2bedbbffff00187fe89094"))), (t2, success(Helpers.decodeBytes("ff000d937f80ffd731ed802d24358001ff8080ff71007f00ad37e0a7ae43fff95b"))), @@ -2936,7 +2722,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.enabledOperationsMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.enabledOperationsMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(6.toByte)), (t2, success(0.toByte)), @@ -2949,7 +2735,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.keyLengthMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.keyLengthMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(1)), (t2, success(32)), @@ -2962,11 +2748,11 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, methodCostDetails(SAvlTreeMethods.valueLengthOptMethod, 15), newCost) + def success[T](v: T, newCost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), newCost, methodCostDetails(SAvlTreeMethods.valueLengthOptMethod, 15), newCost, expectedV3Costs) Seq( - (t1, success(Some(1), 1766)), - (t2, success(Some(64), 1766)), - (t3, success(None, 1765)) + (t1, success(Some(1), 1766, Seq.fill(4)(2002))), + (t2, success(Some(64), 1766, Seq.fill(4)(2002))), + (t3, success(None, 1765, Seq.fill(4)(2001))) ) }, existingFeature((t: AvlTree) => t.valueLengthOpt, @@ -2975,7 +2761,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isInsertAllowedMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isInsertAllowedMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(false)), (t2, success(false)), @@ -2988,7 +2774,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isUpdateAllowedMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isUpdateAllowedMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(true)), (t2, success(false)), @@ -3001,7 +2787,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isRemoveAllowedMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isRemoveAllowedMethod, 15), 1765, Seq.fill(4)(2001)) Seq( (t1, success(true)), (t2, success(false)), @@ -3256,7 +3042,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) - getMany.checkExpected(input, Expected(Success(expRes), 1845, costDetails, 1845)) + getMany.checkExpected(input, Expected(Success(expRes), 1845, costDetails, 1845, Seq.fill(4)(2139))) } val key = Colls.fromArray(Array[Byte](-16,-128,99,86,1,-128,-36,-83,109,72,-124,-114,1,-32,15,127,-30,125,127,1,-102,-53,-53,-128,-107,0,64,8,1,127,22,1)) @@ -3334,8 +3120,9 @@ class SigmaDslSpecification extends SigmaDslTesting // positive test { val input = (tree, (key, proof)) - contains.checkExpected(input, Expected(Success(okContains), 1790, costDetails(105 + additionalDetails), 1790)) - get.checkExpected(input, Expected(Success(valueOpt), 1790 + additionalCost, costDetails(105 + additionalDetails), 1790 + additionalCost)) + val expectedV3Costs: Seq[Int] = Seq.fill(4)(2082) + contains.checkExpected(input, Expected(Success(okContains), 1790, costDetails(105 + additionalDetails), 1790, expectedV3Costs)) + get.checkExpected(input, Expected(Success(valueOpt), 1790 + additionalCost, costDetails(105 + additionalDetails), 1790 + additionalCost, expectedV3Costs.map(additionalCost + _))) } val keys = Colls.fromItems(key) @@ -3343,14 +3130,15 @@ class SigmaDslSpecification extends SigmaDslTesting { val input = (tree, (keys, proof)) - getMany.checkExpected(input, Expected(Success(expRes), 1791 + additionalCost, costDetails(105 + additionalDetails), 1791 + additionalCost)) + val expectedV3Costs: Seq[Int] = Seq.fill(4)(2085 + additionalCost) + getMany.checkExpected(input, Expected(Success(expRes), 1791 + additionalCost, costDetails(105 + additionalDetails), 1791 + additionalCost, expectedV3Costs)) } { val input = (tree, digest) val (res, _) = updateDigest.checkEquality(input).getOrThrow res.digest shouldBe digest - updateDigest.checkExpected(input, Expected(Success(res), 1771, updateDigestCostDetails, 1771)) + updateDigest.checkExpected(input, Expected(Success(res), 1771, updateDigestCostDetails, 1771, Seq.fill(4)(2029))) } val newOps = 1.toByte @@ -3359,7 +3147,7 @@ class SigmaDslSpecification extends SigmaDslTesting val input = (tree, newOps) val (res,_) = updateOperations.checkEquality(input).getOrThrow res.enabledOperations shouldBe newOps - updateOperations.checkExpected(input, Expected(Success(res), 1771, updateOperationsCostDetails, 1771)) + updateOperations.checkExpected(input, Expected(Success(res), 1771, updateOperationsCostDetails, 1771, Seq.fill(4)(2025))) } // negative tests: invalid proof @@ -3369,7 +3157,7 @@ class SigmaDslSpecification extends SigmaDslTesting val input = (tree, (key, invalidProof)) val (res, _) = contains.checkEquality(input).getOrThrow res shouldBe false - contains.checkExpected(input, Expected(Success(res), 1790, costDetails(105 + additionalDetails), 1790)) + contains.checkExpected(input, Expected(Success(res), 1790, costDetails(105 + additionalDetails), 1790, Seq.fill(4)(2082))) } { @@ -3522,7 +3310,7 @@ class SigmaDslSpecification extends SigmaDslTesting val input = (preInsertTree, (kvs, insertProof)) val (res, _) = insert.checkEquality(input).getOrThrow res.isDefined shouldBe true - insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796)) + insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796, Seq.fill(4)(2102))) } { // negative: readonly tree @@ -3530,7 +3318,7 @@ class SigmaDslSpecification extends SigmaDslTesting val input = (readonlyTree, (kvs, insertProof)) val (res, _) = insert.checkEquality(input).getOrThrow res.isDefined shouldBe false - insert.checkExpected(input, Expected(Success(res), 1772, costDetails1, 1772)) + insert.checkExpected(input, Expected(Success(res), 1772, costDetails1, 1772, Seq.fill(4)(2078))) } { // negative: invalid key @@ -3540,7 +3328,7 @@ class SigmaDslSpecification extends SigmaDslTesting val input = (tree, (invalidKvs, insertProof)) val (res, _) = insert.checkEquality(input).getOrThrow res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908) - insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796)) + insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796, Seq.fill(4)(2102))) } { // negative: invalid proof @@ -3670,7 +3458,7 @@ class SigmaDslSpecification extends SigmaDslTesting val endTree = preUpdateTree.updateDigest(endDigest) val input = (preUpdateTree, (kvs, updateProof)) val res = Some(endTree) - update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805)) + update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805, Seq.fill(4)(2111))) } { // positive: update to the same value (identity operation) @@ -3678,13 +3466,13 @@ class SigmaDslSpecification extends SigmaDslTesting val keys = Colls.fromItems((key -> value)) val input = (tree, (keys, updateProof)) val res = Some(tree) - update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805)) + update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805, Seq.fill(4)(2111))) } { // negative: readonly tree val readonlyTree = createTree(preUpdateDigest) val input = (readonlyTree, (kvs, updateProof)) - update.checkExpected(input, Expected(Success(None), 1772, costDetails1, 1772)) + update.checkExpected(input, Expected(Success(None), 1772, costDetails1, 1772, Seq.fill(4)(2078))) } { // negative: invalid key @@ -3692,7 +3480,7 @@ class SigmaDslSpecification extends SigmaDslTesting val invalidKey = key.map(x => (-x).toByte) // any other different from key val invalidKvs = Colls.fromItems((invalidKey -> newValue)) val input = (tree, (invalidKvs, updateProof)) - update.checkExpected(input, Expected(Success(None), 1801, costDetails3, 1801)) + update.checkExpected(input, Expected(Success(None), 1801, costDetails3, 1801, Seq.fill(4)(2107))) } { // negative: invalid value (different from the value in the proof) @@ -3701,15 +3489,15 @@ class SigmaDslSpecification extends SigmaDslTesting val invalidKvs = Colls.fromItems((key -> invalidValue)) val input = (tree, (invalidKvs, updateProof)) val (res, _) = update.checkEquality(input).getOrThrow - res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908) - update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805)) + res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908) + update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805, Seq.fill(4)(2111))) } { // negative: invalid proof val tree = createTree(preUpdateDigest, updateAllowed = true) val invalidProof = updateProof.map(x => (-x).toByte) // any other different from proof val input = (tree, (kvs, invalidProof)) - update.checkExpected(input, Expected(Success(None), 1801, costDetails3, 1801)) + update.checkExpected(input, Expected(Success(None), 1801, costDetails3, 1801, Seq.fill(4)(2107))) } } @@ -3820,7 +3608,7 @@ class SigmaDslSpecification extends SigmaDslTesting val endTree = preRemoveTree.updateDigest(endDigest) val input = (preRemoveTree, (Colls.fromArray(keysToRemove), removeProof)) val res = Some(endTree) - remove.checkExpected(input, Expected(Success(res), 1832, costDetails1, 1832)) + remove.checkExpected(input, Expected(Success(res), 1832, costDetails1, 1832, Seq.fill(4)(2126))) } { @@ -3837,13 +3625,13 @@ class SigmaDslSpecification extends SigmaDslTesting val endTree = preRemoveTree.updateDigest(endDigest) val input = (preRemoveTree, (keys, removeProof)) val res = Some(endTree) - remove.checkExpected(input, Expected(Success(res), 1806, costDetails2, 1806)) + remove.checkExpected(input, Expected(Success(res), 1806, costDetails2, 1806, Seq.fill(4)(2100))) } { // negative: readonly tree val readonlyTree = createTree(preRemoveDigest) val input = (readonlyTree, (keys, removeProof)) - remove.checkExpected(input, Expected(Success(None), 1772, costDetails3, 1772)) + remove.checkExpected(input, Expected(Success(None), 1772, costDetails3, 1772, Seq.fill(4)(2066))) } { // negative: invalid key @@ -3851,14 +3639,14 @@ class SigmaDslSpecification extends SigmaDslTesting val invalidKey = key.map(x => (-x).toByte) // any other different from `key` val invalidKeys = Colls.fromItems(invalidKey) val input = (tree, (invalidKeys, removeProof)) - remove.checkExpected(input, Expected(Success(None), 1802, costDetails4, 1802)) + remove.checkExpected(input, Expected(Success(None), 1802, costDetails4, 1802, Seq.fill(4)(2096))) } { // negative: invalid proof val tree = createTree(preRemoveDigest, removeAllowed = true) val invalidProof = removeProof.map(x => (-x).toByte) // any other different from `removeProof` val input = (tree, (keys, invalidProof)) - remove.checkExpected(input, Expected(Success(None), 1802, costDetails4, 1802)) + remove.checkExpected(input, Expected(Success(None), 1802, costDetails4, 1802, Seq.fill(4)(2096))) } } } @@ -3867,7 +3655,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(LongToByteArray), FixedCost(JitCost(17)))) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767) + def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767, Seq.fill(4)(1999)) Seq( (-9223372036854775808L, success(Helpers.decodeBytes("8000000000000000"))), (-1148502660425090565L, success(Helpers.decodeBytes("f00fb2ea55c579fb"))), @@ -3887,20 +3675,20 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ByteArrayToBigInt), FixedCost(JitCost(30)))) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767) + def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767, Seq.fill(4)(1999)) Seq( (Helpers.decodeBytes(""), Expected(new NumberFormatException("Zero length BigInteger"))), (Helpers.decodeBytes("00"), - success(CBigInt(new BigInteger("0", 16)))), + success(CBigInt(new BigInteger("0", 16)))), (Helpers.decodeBytes("01"), - success(CBigInt(new BigInteger("1", 16)))), + success(CBigInt(new BigInteger("1", 16)))), (Helpers.decodeBytes("ff"), - success(CBigInt(new BigInteger("-1", 16)))), + success(CBigInt(new BigInteger("-1", 16)))), (Helpers.decodeBytes("80d6c201"), - Expected(Success(CBigInt(new BigInteger("-7f293dff", 16))), 1767, costDetails, 1767)), + Expected(Success(CBigInt(new BigInteger("-7f293dff", 16))), 1767, costDetails, 1767, Seq.fill(4)(1999))), (Helpers.decodeBytes("70d6c20170d6c201"), - Expected(Success(CBigInt(new BigInteger("70d6c20170d6c201", 16))), 1767, costDetails, 1767)), + Expected(Success(CBigInt(new BigInteger("70d6c20170d6c201", 16))), 1767, costDetails, 1767, Seq.fill(4)(1999))), (Helpers.decodeBytes( "80e0ff7f02807fff72807f0a00ff7fb7c57f75c11ba2802970fd250052807fc37f6480ffff007fff18eeba44" ), Expected(new ArithmeticException("BigInteger out of 256 bit range"))) @@ -3915,7 +3703,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ByteArrayToLong), FixedCost(JitCost(16)))) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, costDetails, 1765) + def success[T](v: T) = Expected(Success(v), 1765, costDetails, 1765, Seq.fill(4)(1997)) Seq( (Helpers.decodeBytes(""), Expected(new IllegalArgumentException("array too small: 0 < 8"))), (Helpers.decodeBytes("81"), Expected(new IllegalArgumentException("array too small: 1 < 8"))), @@ -3937,7 +3725,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractId), FixedCost(JitCost(12)))) - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (b1, success(Helpers.decodeBytes("5ee78f30ae4e770e44900a46854e9fecb6b12e8112556ef1cd19aef633b4421e"))), (b2, success(Helpers.decodeBytes("3a0089be265460e29ca47d26e5b55a6f3e3ffaf5b4aed941410a2437913848ad"))) @@ -3950,7 +3738,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractAmount), FixedCost(JitCost(8)))) - def success[T](v: T) = Expected(Success(v), 1764, costDetails, 1764) + def success[T](v: T) = Expected(Success(v), 1764, costDetails, 1764, Seq.fill(4)(1996)) Seq( (b1, success(9223372036854775807L)), (b2, success(12345L)) @@ -3963,7 +3751,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractScriptBytes), FixedCost(JitCost(10)))) - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (b1, success(Helpers.decodeBytes( "100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e7300" @@ -3978,7 +3766,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractBytes), FixedCost(JitCost(12)))) - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (b1, success(Helpers.decodeBytes( "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080bcb001" @@ -3995,7 +3783,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractBytesWithNoRef), FixedCost(JitCost(12)))) - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (b1, success(Helpers.decodeBytes( "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff" @@ -4012,7 +3800,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { val costDetails = CostDetails(traceBase :+ FixedCostItem(CompanionDesc(ExtractCreationInfo), FixedCost(JitCost(16)))) - def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767) + def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767, Seq.fill(4)(2001)) Seq( (b1, success(( 677407, @@ -4035,8 +3823,8 @@ class SigmaDslSpecification extends SigmaDslTesting b1 -> Expected(Success(Coll[(Coll[Byte], Long)]( (Helpers.decodeBytes("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001"), 10000000L), (Helpers.decodeBytes("a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600"), 500L) - ).map(identity)), 1772, methodCostDetails(SBoxMethods.tokensMethod, 15), 1772), - b2 -> Expected(Success(Coll[(Coll[Byte], Long)]().map(identity)), 1766, methodCostDetails(SBoxMethods.tokensMethod, 15), 1766) + ).map(identity)), 1772, methodCostDetails(SBoxMethods.tokensMethod, 15), 1772, Seq.fill(4)(2012)), + b2 -> Expected(Success(Coll[(Coll[Byte], Long)]().map(identity)), 1766, methodCostDetails(SBoxMethods.tokensMethod, 15), 1766, Seq.fill(4)(2006)) ), existingFeature({ (x: Box) => x.tokens }, "{ (x: Box) => x.tokens }", @@ -4051,16 +3839,6 @@ class SigmaDslSpecification extends SigmaDslTesting ))) } - property("Box properties equivalence (new features)") { - // TODO v6.0: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416 - val getReg = newFeature((x: Box) => x.getReg[Int](1).get, - "{ (x: Box) => x.getReg[Int](1).get }") - - forAll { box: Box => - Seq(getReg).foreach(_.checkEquality(box)) - } - } - property("Conditional access to registers") { def boxWithRegisters(regs: AdditionalRegisters): Box = { SigmaDsl.Box(testBox(20, TrueTree, 0, Seq(), regs)) @@ -4115,11 +3893,11 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( - (box1, Expected(Success(1024.toShort), 1774, expCostDetails1, 1774)), + (box1, Expected(Success(1024.toShort), 1774, expCostDetails1, 1774, Seq.fill(4)(2042))), (box2, Expected( new InvalidType("Cannot getReg[Short](5): invalid type of value TestValue(1048576) at id=5") )), - (box3, Expected(Success(0.toShort), 1772, expCostDetails2, 1772)) + (box3, Expected(Success(0.toShort), 1772, expCostDetails2, 1772, Seq.fill(4)(2040))) ), existingFeature( { (x: Box) => @@ -4244,10 +4022,10 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( - (box1, Expected(Success(1024), cost = 1785, expCostDetails3, 1785)), - (box2, Expected(Success(1024 * 1024), cost = 1786, expCostDetails4, 1786)), - (box3, Expected(Success(0), cost = 1779, expCostDetails5, 1779)), - (box4, Expected(Success(-1), cost = 1772, expCostDetails2, 1772)) + (box1, Expected(Success(1024), cost = 1785, expCostDetails3, 1785, Seq.fill(4)(2129))), + (box2, Expected(Success(1024 * 1024), cost = 1786, expCostDetails4, 1786, Seq.fill(4)(2130))), + (box3, Expected(Success(0), cost = 1779, expCostDetails5, 1779, Seq.fill(4)(2123))), + (box4, Expected(Success(-1), cost = 1772, expCostDetails2, 1772, Seq.fill(4)(2116))) ), existingFeature( { (x: Box) => @@ -4341,7 +4119,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( Seq( - (box1, Expected(Success(1.toByte), cost = 1770, expCostDetails, 1770)), + (box1, Expected(Success(1.toByte), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))), (box2, Expected(new InvalidType("Cannot getReg[Byte](4): invalid type of value Value(Coll(1)) at id=4"))) ), existingFeature((x: Box) => x.R4[Byte].get, @@ -4353,7 +4131,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( - (box1, Expected(Success(1024.toShort), cost = 1770, expCostDetails, 1770)), + (box1, Expected(Success(1024.toShort), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))), (box2, Expected(new NoSuchElementException("None.get"))) ), existingFeature((x: Box) => x.R5[Short].get, @@ -4365,7 +4143,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( - (box1, Expected(Success(1024 * 1024), cost = 1770, expCostDetails, 1770)) + (box1, Expected(Success(1024 * 1024), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))) ), existingFeature((x: Box) => x.R6[Int].get, "{ (x: Box) => x.R6[Int].get }", @@ -4376,7 +4154,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( - (box1, Expected(Success(1024.toLong), cost = 1770, expCostDetails, 1770)) + (box1, Expected(Success(1024.toLong), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))) ), existingFeature((x: Box) => x.R7[Long].get, "{ (x: Box) => x.R7[Long].get }", @@ -4387,7 +4165,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( - (box1, Expected(Success(CBigInt(BigInteger.valueOf(222L))), cost = 1770, expCostDetails, 1770)) + (box1, Expected(Success(CBigInt(BigInteger.valueOf(222L))), cost = 1770, expCostDetails, 1770, Seq.fill(4)(2008))) ), existingFeature((x: Box) => x.R8[BigInt].get, "{ (x: Box) => x.R8[BigInt].get }", @@ -4406,10 +4184,10 @@ class SigmaDslSpecification extends SigmaDslTesting None ) )), - cost = 1770, - expCostDetails, - 1770) - )), + cost = 1770, + expCostDetails, + 1770, Seq.fill(4)(2008)) + )), existingFeature((x: Box) => x.R9[AvlTree].get, "{ (x: Box) => x.R9[AvlTree].get }", FuncValue( @@ -4481,117 +4259,117 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( - Seq((preH1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SPreHeaderMethods.versionMethod, 10), 1765))), + Seq((preH1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SPreHeaderMethods.versionMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("version", { (x: PreHeader) => x.version })) verifyCases( Seq((preH1, Expected(Success( Helpers.decodeBytes("7fff7fdd6f62018bae0001006d9ca888ff7f56ff8006573700a167f17f2c9f40")), - cost = 1766, methodCostDetails(SPreHeaderMethods.parentIdMethod, 10), 1766))), + cost = 1766, methodCostDetails(SPreHeaderMethods.parentIdMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("parentId", { (x: PreHeader) => x.parentId })) verifyCases( - Seq((preH1, Expected(Success(6306290372572472443L), cost = 1765, methodCostDetails(SPreHeaderMethods.timestampMethod, 10), 1765))), + Seq((preH1, Expected(Success(6306290372572472443L), cost = 1765, methodCostDetails(SPreHeaderMethods.timestampMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("timestamp", { (x: PreHeader) => x.timestamp })) verifyCases( - Seq((preH1, Expected(Success(-3683306095029417063L), cost = 1765, methodCostDetails(SPreHeaderMethods.nBitsMethod, 10), 1765))), + Seq((preH1, Expected(Success(-3683306095029417063L), cost = 1765, methodCostDetails(SPreHeaderMethods.nBitsMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("nBits", { (x: PreHeader) => x.nBits })) verifyCases( - Seq((preH1, Expected(Success(1), cost = 1765, methodCostDetails(SPreHeaderMethods.heightMethod, 10), 1765))), + Seq((preH1, Expected(Success(1), cost = 1765, methodCostDetails(SPreHeaderMethods.heightMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("height", { (x: PreHeader) => x.height })) verifyCases( Seq((preH1, Expected(Success( Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b")), - cost = 1782, methodCostDetails(SPreHeaderMethods.minerPkMethod, 10), 1782))), + cost = 1782, methodCostDetails(SPreHeaderMethods.minerPkMethod, 10), 1782, Seq.fill(4)(2018)))), existingPropTest("minerPk", { (x: PreHeader) => x.minerPk })) verifyCases( - Seq((preH1, Expected(Success(Helpers.decodeBytes("ff8087")), cost = 1766, methodCostDetails(SPreHeaderMethods.votesMethod,10), 1766))), + Seq((preH1, Expected(Success(Helpers.decodeBytes("ff8087")), cost = 1766, methodCostDetails(SPreHeaderMethods.votesMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("votes", { (x: PreHeader) => x.votes })) } property("Header properties equivalence") { verifyCases( Seq((h1, Expected(Success( - Helpers.decodeBytes("957f008001808080ffe4ffffc8f3802401df40006aa05e017fa8d3f6004c804a")), - cost = 1766, methodCostDetails(SHeaderMethods.idMethod, 10), 1766))), + Helpers.decodeBytes("cea31f0e0a794b103f65f8296a22ac8ff214e1bc75442186b90df4844c978e81")), + cost = 1766, methodCostDetails(SHeaderMethods.idMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("id", { (x: Header) => x.id })) verifyCases( - Seq((h1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SHeaderMethods.versionMethod, 10), 1765))), + Seq((h1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SHeaderMethods.versionMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("version", { (x: Header) => x.version })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("0180dd805b0000ff5400b997fd7f0b9b00de00fb03c47e37806a8186b94f07ff")), - cost = 1766, methodCostDetails(SHeaderMethods.parentIdMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.parentIdMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("parentId", { (x: Header) => x.parentId })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("01f07f60d100ffb970c3007f60ff7f24d4070bb8fffa7fca7f34c10001ffe39d")), - cost = 1766, methodCostDetails(SHeaderMethods.ADProofsRootMethod, 10), 1766))), - existingPropTest("ADProofsRoot", { (x: Header) => x.ADProofsRoot})) + cost = 1766, methodCostDetails(SHeaderMethods.ADProofsRootMethod, 10), 1766, Seq.fill(4)(2002)))), + existingPropTest("ADProofsRoot", { (x: Header) => x.ADProofsRoot })) verifyCases( - Seq((h1, Expected(Success(CAvlTree(createAvlTreeData())), cost = 1765, methodCostDetails(SHeaderMethods.stateRootMethod, 10), 1765))), + Seq((h1, Expected(Success(CAvlTree(createAvlTreeData())), cost = 1765, methodCostDetails(SHeaderMethods.stateRootMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("stateRoot", { (x: Header) => x.stateRoot })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("804101ff01000080a3ffbd006ac080098df132a7017f00649311ec0e00000100")), - cost = 1766, methodCostDetails(SHeaderMethods.transactionsRootMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.transactionsRootMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("transactionsRoot", { (x: Header) => x.transactionsRoot })) verifyCases( - Seq((h1, Expected(Success(1L), cost = 1765, methodCostDetails(SHeaderMethods.timestampMethod, 10), 1765))), + Seq((h1, Expected(Success(1L), cost = 1765, methodCostDetails(SHeaderMethods.timestampMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("timestamp", { (x: Header) => x.timestamp })) verifyCases( - Seq((h1, Expected(Success(-1L), cost = 1765, methodCostDetails(SHeaderMethods.nBitsMethod, 10), 1765))), + Seq((h1, Expected(Success(-1L), cost = 1765, methodCostDetails(SHeaderMethods.nBitsMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("nBits", { (x: Header) => x.nBits })) verifyCases( - Seq((h1, Expected(Success(1), cost = 1765, methodCostDetails(SHeaderMethods.heightMethod, 10), 1765))), + Seq((h1, Expected(Success(1), cost = 1765, methodCostDetails(SHeaderMethods.heightMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("height", { (x: Header) => x.height })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("e57f80885601b8ff348e01808000bcfc767f2dd37f0d01015030ec018080bc62")), - cost = 1766, methodCostDetails(SHeaderMethods.extensionRootMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.extensionRootMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("extensionRoot", { (x: Header) => x.extensionRoot })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeGroupElement("039bdbfa0b49cc6bef58297a85feff45f7bbeb500a9d2283004c74fcedd4bd2904")), - cost = 1782, methodCostDetails(SHeaderMethods.minerPkMethod, 10), 1782))), + cost = 1782, methodCostDetails(SHeaderMethods.minerPkMethod, 10), 1782, Seq.fill(4)(2018)))), existingPropTest("minerPk", { (x: Header) => x.minerPk })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeGroupElement("0361299207fa392231e23666f6945ae3e867b978e021d8d702872bde454e9abe9c")), - cost = 1782, methodCostDetails(SHeaderMethods.powOnetimePkMethod, 10), 1782))), + cost = 1782, methodCostDetails(SHeaderMethods.powOnetimePkMethod, 10), 1782, Seq.fill(4)(2018)))), existingPropTest("powOnetimePk", { (x: Header) => x.powOnetimePk })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("7f4f09012a807f01")), - cost = 1766, methodCostDetails(SHeaderMethods.powNonceMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.powNonceMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("powNonce", { (x: Header) => x.powNonce })) verifyCases( Seq((h1, Expected(Success( CBigInt(new BigInteger("-e24990c47e15ed4d0178c44f1790cc72155d516c43c3e8684e75db3800a288", 16))), - cost = 1765, methodCostDetails(SHeaderMethods.powDistanceMethod, 10), 1765))), + cost = 1765, methodCostDetails(SHeaderMethods.powDistanceMethod, 10), 1765, Seq.fill(4)(2001)))), existingPropTest("powDistance", { (x: Header) => x.powDistance })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("7f0180")), - cost = 1766, methodCostDetails(SHeaderMethods.votesMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.votesMethod, 10), 1766, Seq.fill(4)(2002)))), existingPropTest("votes", { (x: Header) => x.votes })) } @@ -4654,18 +4432,10 @@ class SigmaDslSpecification extends SigmaDslTesting ) val header = CHeader( - Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), 0.toByte, Helpers.decodeBytes("7a7fe5347f09017818010062000001807f86808000ff7f66ffb07f7ad27f3362"), Helpers.decodeBytes("c1d70ad9b1ffc1fb9a715fff19807f2401017fcd8b73db017f1cff77727fff08"), - CAvlTree( - AvlTreeData( - ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17").toColl, - AvlTreeFlags(true, true, false), - 2147483647, - None - ) - ), + Helpers.decodeBytes("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17"), Helpers.decodeBytes("5e7f1164ccd0990080c501fc0e0181cb387fc17f00ff00c7d5ff767f91ff5e68"), -7421721754642387858L, -4826493284887861030L, @@ -4675,7 +4445,8 @@ class SigmaDslSpecification extends SigmaDslTesting Helpers.decodeGroupElement("034e2d3b5f9e409e3ae8a2e768340760362ca33764eda5855f7a43487f14883300"), Helpers.decodeBytes("974651c9efff7f00"), CBigInt(new BigInteger("478e827dfa1e4b57", 16)), - Helpers.decodeBytes("01ff13") + Helpers.decodeBytes("01ff13"), + Colls.emptyColl ) val ctx = CContext( @@ -4683,7 +4454,7 @@ class SigmaDslSpecification extends SigmaDslTesting headers = Coll[Header](header), preHeader = CPreHeader( 0.toByte, - Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), + header.id, -755484979487531112L, 9223372036854775807L, 11, @@ -4762,6 +4533,7 @@ class SigmaDslSpecification extends SigmaDslTesting .append(Coll[AnyValue]( CAnyValue(Helpers.decodeBytes("00")), CAnyValue(true))), + spendingTransaction = null, activatedScriptVersion = activatedVersionInTests, currentErgoTreeVersion = ergoTreeVersionInTests ) @@ -4805,7 +4577,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails = TracedCost(testTraceBase) verifyCases( Seq( - (ctx, Expected(Success(dataBox), cost = 1769, costDetails, 1769)), + (ctx, Expected(Success(dataBox), cost = 1769, costDetails, 1769, Seq.fill(4)(2017))), (ctx.copy(_dataInputs = Coll()), Expected(new ArrayIndexOutOfBoundsException("0"))) ), existingFeature({ (x: Context) => x.dataInputs(0) }, @@ -4830,7 +4602,7 @@ class SigmaDslSpecification extends SigmaDslTesting Seq( (ctx, Expected(Success( Helpers.decodeBytes("7da4b55971f19a78d007638464580f91a020ab468c0dbe608deb1f619e245bc3")), - cost = 1772, idCostDetails, 1772)) + cost = 1772, idCostDetails, 1772, Seq.fill(4)(2022))) ), existingFeature({ (x: Context) => x.dataInputs(0).id }, "{ (x: Context) => x.dataInputs(0).id }", @@ -4891,7 +4663,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) verifyCases( - Seq(ctx -> Expected(Success(ctx.HEIGHT), cost = 1766, heightCostDetails, 1766)), + Seq(ctx -> Expected(Success(ctx.HEIGHT), cost = 1766, heightCostDetails, 1766, Seq.fill(4)(1994))), existingFeature( { (x: Context) => x.HEIGHT }, "{ (x: Context) => x.HEIGHT }", @@ -4928,7 +4700,7 @@ class SigmaDslSpecification extends SigmaDslTesting )) verifyCases( - Seq((ctx, Expected(Success(Coll[Long](80946L)), cost = 1770, inputsCostDetails1, 1770))), + Seq((ctx, Expected(Success(Coll[Long](80946L)), cost = 1770, inputsCostDetails1, 1770, Seq.fill(4)(2014)))), existingFeature( { (x: Context) => x.INPUTS.map { (b: Box) => b.value } }, "{ (x: Context) => x.INPUTS.map { (b: Box) => b.value } }", @@ -4988,7 +4760,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) verifyCases( - Seq((ctx, Expected(Success(Coll((80946L, 80946L))), cost = 1774, inputsCostDetails2, 1774))), + Seq((ctx, Expected(Success(Coll((80946L, 80946L))), cost = 1774, inputsCostDetails2, 1774, Seq.fill(4)(2042)))), existingFeature( { (x: Context) => x.INPUTS.map { (b: Box) => (b.value, b.value) } }, """{ (x: Context) => @@ -5080,11 +4852,20 @@ class SigmaDslSpecification extends SigmaDslTesting expectedDetails = CostDetails.ZeroCost, newCost = 1766, newVersionedResults = { + val expectedV3Costs = Seq.fill(4)(2002) + // V3 activation will have different costs due to deserialization cost + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + expectedV3Costs + } else { + Seq.fill(4)(1766) + } val res = (ExpectedResult(Success(0), Some(1766)) -> Some(selfCostDetails)) - Seq(0, 1, 2).map(version => version -> res) + Seq(0, 1, 2, 3).map(version => version -> (ExpectedResult(Success(0), Some(costs(version))) -> Some(selfCostDetails))) })) ), - changedFeature({ (x: Context) => x.selfBoxIndex }, + changedFeature( + changedInVersion = VersionContext.JitActivationVersion, + { (x: Context) => x.selfBoxIndex }, { (x: Context) => x.selfBoxIndex }, // see versioning in selfBoxIndex implementation "{ (x: Context) => x.selfBoxIndex }", FuncValue( @@ -5110,7 +4891,7 @@ class SigmaDslSpecification extends SigmaDslTesting } verifyCases( - Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash), cost = 1766, methodCostDetails(SContextMethods.lastBlockUtxoRootHashMethod, 15), 1766)), + Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash), cost = 1766, methodCostDetails(SContextMethods.lastBlockUtxoRootHashMethod, 15), 1766, Seq.fill(4)(2002))), existingPropTest("LastBlockUtxoRootHash", { (x: Context) => x.LastBlockUtxoRootHash }), preGeneratedSamples = Some(samples)) @@ -5123,7 +4904,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) verifyCases( - Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash.isUpdateAllowed), cost = 1767, isUpdateAllowedCostDetails, 1767)), + Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash.isUpdateAllowed), cost = 1767, isUpdateAllowedCostDetails, 1767, Seq.fill(4)(2009))), existingFeature( { (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }, "{ (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }", @@ -5144,7 +4925,7 @@ class SigmaDslSpecification extends SigmaDslTesting preGeneratedSamples = Some(samples)) verifyCases( - Seq(ctx -> Expected(Success(ctx.minerPubKey), cost = 1767, methodCostDetails(SContextMethods.minerPubKeyMethod, 20), 1767)), + Seq(ctx -> Expected(Success(ctx.minerPubKey), cost = 1767, methodCostDetails(SContextMethods.minerPubKeyMethod, 20), 1767, Seq.fill(4)(2003))), existingPropTest("minerPubKey", { (x: Context) => x.minerPubKey }), preGeneratedSamples = Some(samples)) @@ -5184,7 +4965,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet))) verifyCases( - Seq((ctx, Expected(Success(true), cost = 1765, getVarCostDetails, 1765))), + Seq((ctx, Expected(Success(true), cost = 1765, getVarCostDetails, 1765, Seq.fill(4)(1999)))), existingFeature((x: Context) => x.getVar[Boolean](11).get, "{ (x: Context) => getVar[Boolean](11).get }", FuncValue(Vector((1, SContext)), OptionGet(GetVar(11.toByte, SOption(SBoolean))))), @@ -5218,7 +4999,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( Seq( - ctx -> Expected(Success(-135729055492651903L), 1779, registerIsDefinedCostDetails, 1779) + ctx -> Expected(Success(-135729055492651903L), 1779, registerIsDefinedCostDetails, 1779, Seq.fill(4)(2065)) ), existingFeature( { (x: Context) => @@ -5280,11 +5061,19 @@ class SigmaDslSpecification extends SigmaDslTesting Seq( ctx -> Expected(Failure(expectedError), 0, CostDetails.ZeroCost, 1793, newVersionedResults = { - Seq.tabulate(3)(v => v -> (ExpectedResult(Success(true), Some(1793)) -> None)) + val expectedV3Costs = Seq.fill(4)(2121) + // V3 activation will have different costs due to deserialization cost + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + expectedV3Costs + } else { + Seq.fill(4)(1793) + } + Seq.tabulate(4)(v => v -> (ExpectedResult(Success(true), Some(costs(v))) -> None)) } ) ), changedFeature( + changedInVersion = VersionContext.JitActivationVersion, scalaFunc = { (x: Context) => // this error is expected in v3.x, v4.x throw expectedError @@ -5446,12 +5235,12 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( Seq( - ctx -> Expected(Success(5008366408131208436L), 1791, registerTagCostDetails1, 1791), + ctx -> Expected(Success(5008366408131208436L), 1791, registerTagCostDetails1, 1791, Seq.fill(4)(2153)), ctxWithRegsInOutput(ctx, Map( ErgoBox.R5 -> LongConstant(0L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(10L), 1790, registerTagCostDetails2, 1790), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(10L), 1790, registerTagCostDetails2, 1790, Seq.fill(4)(2152)), ctxWithRegsInOutput(ctx, Map( - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1777, registerTagCostDetails3, 1777) + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1777, registerTagCostDetails3, 1777, Seq.fill(4)(2139)) ), existingFeature( { (x: Context) => @@ -5654,22 +5443,22 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( // case 1L - ctx -> Expected(Success(5008366408131289382L), 1794, tagRegisterCostDetails1, 1794), + ctx -> Expected(Success(5008366408131289382L), 1794, tagRegisterCostDetails1, 1794, Seq.fill(4)(2168)), // case 0L ctxWithRegsInOutput(ctx, Map( ErgoBox.R5 -> LongConstant(0L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(80956L), 1793, tagRegisterCostDetails2, 1793), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(80956L), 1793, tagRegisterCostDetails2, 1793, Seq.fill(4)(2167)), // case returning 0L ctxWithRegsInOutput(ctx, Map( ErgoBox.R5 -> LongConstant(2L), // note R4 is required to avoid // "RuntimeException: Set of non-mandatory indexes is not densely packed" - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1784, tagRegisterCostDetails3, 1784), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1784, tagRegisterCostDetails3, 1784, Seq.fill(4)(2158)), // case returning -1L ctxWithRegsInOutput(ctx, Map( - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1777, tagRegisterCostDetails4, 1777) + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1777, tagRegisterCostDetails4, 1777, Seq.fill(4)(2151)) ), existingFeature( { (x: Context) => @@ -5887,15 +5676,15 @@ class SigmaDslSpecification extends SigmaDslTesting Seq( ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(1L), - ErgoBox.R4 -> LongConstant(10))) -> Expected(Success(10L), 1792, tagRegisterCostDetails1, 1792), + ErgoBox.R4 -> LongConstant(10))) -> Expected(Success(10L), 1792, tagRegisterCostDetails1, 1792, Seq.fill(4)(2162)), ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(0L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(10L), 1791, tagRegisterCostDetails2, 1791), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(10L), 1791, tagRegisterCostDetails2, 1791, Seq.fill(4)(2161)), ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(2L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1786, tagRegisterCostDetails3, 1786), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1786, tagRegisterCostDetails3, 1786, Seq.fill(4)(2156)), ctxWithRegsInDataInput(ctx, Map( - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1779, tagRegisterCostDetails4, 1779) + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1779, tagRegisterCostDetails4, 1779, Seq.fill(4)(2149)) ), existingFeature( { (x: Context) => @@ -6120,15 +5909,15 @@ class SigmaDslSpecification extends SigmaDslTesting Seq( ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(1L), - ErgoBox.R4 -> LongConstant(10))) -> Expected(Success(80956L), 1796, costDetails1, 1796), + ErgoBox.R4 -> LongConstant(10))) -> Expected(Success(80956L), 1796, costDetails1, 1796, Seq.fill(4)(2178)), ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(0L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(80956L), 1794, costDetails2, 1794), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(80956L), 1794, costDetails2, 1794, Seq.fill(4)(2176)), ctxWithRegsInDataInput(ctx, Map( ErgoBox.R5 -> LongConstant(2L), - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1786, costDetails3, 1786), + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(0L), 1786, costDetails3, 1786, Seq.fill(4)(2168)), ctxWithRegsInDataInput(ctx, Map( - ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1779, costDetails4, 1779) + ErgoBox.R4 -> ShortConstant(10))) -> Expected(Success(-1L), 1779, costDetails4, 1779, Seq.fill(4)(2161)) ), existingFeature( { (x: Context) => @@ -6233,7 +6022,15 @@ class SigmaDslSpecification extends SigmaDslTesting cost = c, expectedDetails = CostDetails.ZeroCost, newCost = 1766, - newVersionedResults = Seq(0, 1, 2).map(i => i -> (ExpectedResult(Success(newV), Some(1766)) -> Some(cd))) + newVersionedResults = { + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(1998) + } + else { + Seq.fill(4)(1766) + } + Seq.tabulate(4)(i => i -> (ExpectedResult(Success(newV), Some(costs(i))) -> Some(cd))) + } ) Seq( (Coll[Boolean](), successNew(false, 1766, newV = false, costDetails(0))), @@ -6258,6 +6055,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) }, changedFeature( + changedInVersion = VersionContext.JitActivationVersion, (x: Coll[Boolean]) => SigmaDsl.xorOf(x), (x: Coll[Boolean]) => SigmaDsl.xorOf(x), "{ (x: Coll[Boolean]) => xorOf(x) }", @@ -6268,8 +6066,8 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails = TracedCost(traceBase :+ FixedCostItem(LogicalNot)) verifyCases( Seq( - (true, Expected(Success(false), 1765, costDetails, 1765)), - (false, Expected(Success(true), 1765, costDetails, 1765))), + (true, Expected(Success(false), 1765, costDetails, 1765, Seq.fill(4)(1997))), + (false, Expected(Success(true), 1765, costDetails, 1765, Seq.fill(4)(1997)))), existingFeature((x: Boolean) => !x, "{ (x: Boolean) => !x }", FuncValue(Vector((1, SBoolean)), LogicalNot(ValUse(1, SBoolean))))) @@ -6279,7 +6077,8 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails = TracedCost(traceBase :+ FixedCostItem(Negation)) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) + Seq( (Byte.MinValue, success(Byte.MinValue)), // !!! ((Byte.MinValue + 1).toByte, success(Byte.MaxValue)), @@ -6298,7 +6097,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) Seq( (Short.MinValue, success(Short.MinValue)), // special case! ((Short.MinValue + 1).toShort, success(Short.MaxValue)), @@ -6316,7 +6115,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) + Seq( (Int.MinValue, success(Int.MinValue)), // special case! (Int.MinValue + 1, success(Int.MaxValue)), @@ -6333,7 +6133,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766) + def success[T](v: T) = Expected(Success(v), 1766, costDetails, 1766, Seq.fill(4)(1998)) + Seq( (Long.MinValue, success(Long.MinValue)), // special case! (Long.MinValue + 1, success(Long.MaxValue)), @@ -6350,7 +6151,8 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767) + def success[T](v: T) = Expected(Success(v), 1767, costDetails, 1767, Seq.fill(4)(1999)) + Seq( (CBigInt(new BigInteger("-1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)), success(CBigInt(new BigInteger("1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)))), (CBigInt(new BigInteger("-1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)), success(CBigInt(new BigInteger("1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)))), @@ -6388,7 +6190,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1782, costDetails, 1782) + def success[T](v: T) = Expected(Success(v), 1782, costDetails, 1782, Seq.fill(4)(2016)) Seq( (-1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), (1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))) @@ -6407,7 +6209,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1782, costDetails, 1782) + def success[T](v: T) = Expected(Success(v), 1782, costDetails, 1782, Seq.fill(4)(2016)) Seq( (-1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), (1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))) @@ -6432,37 +6234,38 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) verifyCases( - { - def success[T](v: T) = Expected(Success(v), 1872, expCostDetails, 1872) - Seq( - (CBigInt(new BigInteger("-e5c1a54694c85d644fa30a6fc5f3aa209ed304d57f72683a0ebf21038b6a9d", 16)), success(Helpers.decodeGroupElement("023395bcba3d7cf21d73c50f8af79d09a8c404c15ce9d04f067d672823bae91a54"))), - (CBigInt(new BigInteger("-bc2d08f935259e0eebf272c66c6e1dbd484c6706390215", 16)), success(Helpers.decodeGroupElement("02ddcf4c48105faf3c16f7399b5dbedd82ab0bb50ae292d8f88f49a3f86e78974e"))), - (CBigInt(new BigInteger("-35cbe9a7a652e5fe85f735ee9909fdd8", 16)), success(Helpers.decodeGroupElement("03b110ec9c7a8c20ed873818e976a0e96e5a17be979d3422d59b362de2a3ae043e"))), - (CBigInt(new BigInteger("-3f05ffca6bd4b15c", 16)), success(Helpers.decodeGroupElement("02acf2657d0714cef8d65ae15c362faa09c0934c0bce872a23398e564c090b85c8"))), - (CBigInt(new BigInteger("-80000001", 16)), success(Helpers.decodeGroupElement("0391b418fd1778356ce947a5cbb46539fd29842aea168486fae91fc5317177a575"))), - (CBigInt(new BigInteger("-80000000", 16)), success(Helpers.decodeGroupElement("025318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), - (CBigInt(new BigInteger("-1", 16)), success(Helpers.decodeGroupElement("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (CBigInt(new BigInteger("0", 16)), success(Helpers.decodeGroupElement("000000000000000000000000000000000000000000000000000000000000000000"))), - (CBigInt(new BigInteger("1", 16)), success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (CBigInt(new BigInteger("80000000", 16)), success(Helpers.decodeGroupElement("035318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), - (CBigInt(new BigInteger("1251b7fcd8a01e95", 16)), success(Helpers.decodeGroupElement("030fde7238b8dddfafab8f5481dc17b880505d6bacbe3cdf2ce975afdcadf66354"))), - (CBigInt(new BigInteger("12f6bd76d8fe1d035bdb14bf2f696e52", 16)), success(Helpers.decodeGroupElement("028f2ccf13669461cb3cfbea281e2db08fbb67b38493a1628855203d3f69b82763"))), - (CBigInt(new BigInteger("102bb404f5e36bdba004fdefa34df8cfa02e7912f3caf79", 16)), success(Helpers.decodeGroupElement("03ce82f431d115d45ad555084f8b2861ce5c4561d154e931e9f778594896e46a25")))) - }, - existingFeature({ (n: BigInt) => SigmaDsl.groupGenerator.exp(n) }, - "{ (n: BigInt) => groupGenerator.exp(n) }", - FuncValue( - Vector((1, SBigInt)), - Exponentiate( - MethodCall.typed[Value[SGroupElement.type]]( - Global, - SGlobalMethods.getMethodByName("groupGenerator"), - Vector(), - Map() - ), - ValUse(1, SBigInt) - ) - ))) + { + def success[T](v: T) = Expected(Success(v), 1872, expCostDetails, 1872, Seq.fill(4)(2112)) + + Seq( + (CBigInt(new BigInteger("-e5c1a54694c85d644fa30a6fc5f3aa209ed304d57f72683a0ebf21038b6a9d", 16)), success(Helpers.decodeGroupElement("023395bcba3d7cf21d73c50f8af79d09a8c404c15ce9d04f067d672823bae91a54"))), + (CBigInt(new BigInteger("-bc2d08f935259e0eebf272c66c6e1dbd484c6706390215", 16)), success(Helpers.decodeGroupElement("02ddcf4c48105faf3c16f7399b5dbedd82ab0bb50ae292d8f88f49a3f86e78974e"))), + (CBigInt(new BigInteger("-35cbe9a7a652e5fe85f735ee9909fdd8", 16)), success(Helpers.decodeGroupElement("03b110ec9c7a8c20ed873818e976a0e96e5a17be979d3422d59b362de2a3ae043e"))), + (CBigInt(new BigInteger("-3f05ffca6bd4b15c", 16)), success(Helpers.decodeGroupElement("02acf2657d0714cef8d65ae15c362faa09c0934c0bce872a23398e564c090b85c8"))), + (CBigInt(new BigInteger("-80000001", 16)), success(Helpers.decodeGroupElement("0391b418fd1778356ce947a5cbb46539fd29842aea168486fae91fc5317177a575"))), + (CBigInt(new BigInteger("-80000000", 16)), success(Helpers.decodeGroupElement("025318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), + (CBigInt(new BigInteger("-1", 16)), success(Helpers.decodeGroupElement("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (CBigInt(new BigInteger("0", 16)), success(Helpers.decodeGroupElement("000000000000000000000000000000000000000000000000000000000000000000"))), + (CBigInt(new BigInteger("1", 16)), success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (CBigInt(new BigInteger("80000000", 16)), success(Helpers.decodeGroupElement("035318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), + (CBigInt(new BigInteger("1251b7fcd8a01e95", 16)), success(Helpers.decodeGroupElement("030fde7238b8dddfafab8f5481dc17b880505d6bacbe3cdf2ce975afdcadf66354"))), + (CBigInt(new BigInteger("12f6bd76d8fe1d035bdb14bf2f696e52", 16)), success(Helpers.decodeGroupElement("028f2ccf13669461cb3cfbea281e2db08fbb67b38493a1628855203d3f69b82763"))), + (CBigInt(new BigInteger("102bb404f5e36bdba004fdefa34df8cfa02e7912f3caf79", 16)), success(Helpers.decodeGroupElement("03ce82f431d115d45ad555084f8b2861ce5c4561d154e931e9f778594896e46a25")))) + }, + existingFeature({ (n: BigInt) => SigmaDsl.groupGenerator.exp(n) }, + "{ (n: BigInt) => groupGenerator.exp(n) }", + FuncValue( + Vector((1, SBigInt)), + Exponentiate( + MethodCall.typed[Value[SGroupElement.type]]( + Global, + SGlobalMethods.getMethodByName("groupGenerator"), + Vector(), + Map() + ), + ValUse(1, SBigInt) + ) + ))) } } @@ -6498,7 +6301,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1769, cd, 1769) + def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1769, cd, 1769, Seq.fill(4)(2021)) Seq( ((Helpers.decodeBytes(""), Helpers.decodeBytes("")), success(Helpers.decodeBytes(""), costDetails(0))), ((Helpers.decodeBytes("01"), Helpers.decodeBytes("01")), success(Helpers.decodeBytes("00"), costDetails(1))), @@ -6509,9 +6312,16 @@ class SigmaDslSpecification extends SigmaDslTesting cost = 0, expectedDetails = CostDetails.ZeroCost, newCost = 1769, - newVersionedResults = { - val res = (ExpectedResult(Success(Helpers.decodeBytes("00")), Some(1769)), Some(costDetails(1))) - Seq(0, 1, 2).map(version => version -> res) + newVersionedResults = { + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2021) + } else { + Seq.fill(4)(1769) + } + Seq.tabulate(4) { version => + val res = (ExpectedResult(Success(Helpers.decodeBytes("00")), Some(costs(version))), Some(costDetails(1))) + version -> res + } } )), ((Helpers.decodeBytes("800136fe89afff802acea67128a0ff007fffe3498c8001806080012b"), @@ -6520,6 +6330,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) }, changedFeature( + changedInVersion = VersionContext.JitActivationVersion, (x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), (x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), "{ (x: (Coll[Byte], Coll[Byte])) => xor(x._1, x._2) }", @@ -6653,11 +6464,11 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( { - def success[T](v: T, c: Int, costDetails: CostDetails, newCost: Int) = Expected(Success(v), c, costDetails, newCost) + def success[T](v: T, c: Int, costDetails: CostDetails, newCost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), c, costDetails, newCost, expectedV3Costs) Seq( - (Coll[Box](), success(Coll[Box](), 1767, costDetails, 1767)), - (Coll[Box](b1), success(Coll[Box](), 1772, costDetails2, 1772)), - (Coll[Box](b1, b2), success(Coll[Box](b2), 1776, costDetails3, 1776)) + (Coll[Box](), success(Coll[Box](), 1767, costDetails, 1767, Seq.fill(4)(2031))), + (Coll[Box](b1), success(Coll[Box](), 1772, costDetails2, 1772, Seq.fill(4)(2036))), + (Coll[Box](b1, b2), success(Coll[Box](b2), 1776, costDetails3, 1776, Seq.fill(4)(2040))) ) }, existingFeature({ (x: Coll[Box]) => x.filter({ (b: Box) => b.value > 1 }) }, @@ -6710,13 +6521,13 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { Seq( - (Coll[Box](), Expected(Success(Coll[Byte]()), 1773, costDetails1, 1773)), + (Coll[Box](), Expected(Success(Coll[Byte]()), 1773, costDetails1, 1773, Seq.fill(4)(2029))), (Coll[Box](b1), Expected(Success(Helpers.decodeBytes( "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd5" - )), 1791, costDetails2, 1791)), + )), 1791, costDetails2, 1791, Seq.fill(4)(2047))), (Coll[Box](b1, b2), Expected(Success(Helpers.decodeBytes( "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd500d197830201010096850200" - )), 1795, costDetails3, 1795)) + )), 1795, costDetails3, 1795, Seq.fill(4)(2051))) ) }, existingFeature({ (x: Coll[Box]) => x.flatMap({ (b: Box) => b.propositionBytes }) }, @@ -6749,11 +6560,12 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T, c: Int, cd: CostDetails, nc: Int) = Expected(Success(v), c, cd, nc) + def success[T](v: T, c: Int, cd: CostDetails, nc: Int, v3Costs: Seq[Int]) = Expected(Success(v), c, cd, nc, v3Costs) + Seq( - (Coll[Box](), success(Coll[(Box, Box)](), 1766, costDetails(0), 1766)), - (Coll[Box](b1), success(Coll[(Box, Box)]((b1, b1)), 1768, costDetails(1), 1768)), - (Coll[Box](b1, b2), success(Coll[(Box, Box)]((b1, b1), (b2, b2)), 1770, costDetails(2), 1770)) + (Coll[Box](), success(Coll[(Box, Box)](), 1766, costDetails(0), 1766, Seq.fill(4)(2018))), + (Coll[Box](b1), success(Coll[(Box, Box)]((b1, b1)), 1768, costDetails(1), 1768, Seq.fill(4)(2020))), + (Coll[Box](b1, b2), success(Coll[(Box, Box)]((b1, b1), (b2, b2)), 1770, costDetails(2), 1770, Seq.fill(4)(2022))) ) }, existingFeature({ (x: Coll[Box]) => x.zip(x) }, @@ -6779,7 +6591,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails = TracedCost(traceBase :+ FixedCostItem(SizeOf)) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, costDetails, 1765) + def success[T](v: T) = Expected(Success(v), 1765, costDetails, 1765, Seq.fill(4)(2001)) Seq( (Coll[Box](), success(0)), (Coll[Box](b1), success(1)), @@ -6804,7 +6616,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( { - def success[T](v: T, i: Int) = Expected(Success(v), 1768, costDetails(i), 1768) + def success[T](v: T, i: Int) = Expected(Success(v), 1768, costDetails(i), 1768, Seq.fill(4)(2008)) Seq( (Coll[Box](), success(Coll[Int](), 0)), (Coll[Box](b1), success(Coll[Int](0), 1)), @@ -6860,9 +6672,9 @@ class SigmaDslSpecification extends SigmaDslTesting ) def cases = { Seq( - (Coll[Box](), Expected(Success(true), 1764, costDetails1, 1764)), - (Coll[Box](b1), Expected(Success(false), 1769, costDetails2, 1769)), - (Coll[Box](b1, b2), Expected(Success(false), 1769, costDetails3, 1769)) + (Coll[Box](), Expected(Success(true), 1764, costDetails1, 1764, Seq.fill(4)(2026))), + (Coll[Box](b1), Expected(Success(false), 1769, costDetails2, 1769, Seq.fill(4)(2031))), + (Coll[Box](b1, b2), Expected(Success(false), 1769, costDetails3, 1769, Seq.fill(4)(2031))) ) } if (lowerMethodCallsInTests) { @@ -6939,9 +6751,9 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { Seq( - (Coll[Box](), Expected(Success(false), 1764, costDetails1, 1764)), - (Coll[Box](b1), Expected(Success(false), 1769, costDetails2, 1769)), - (Coll[Box](b1, b2), Expected(Success(true), 1773, costDetails3, 1773)) + (Coll[Box](), Expected(Success(false), 1764, costDetails1, 1764, Seq.fill(4)(2026))), + (Coll[Box](b1), Expected(Success(false), 1769, costDetails2, 1769, Seq.fill(4)(2031))), + (Coll[Box](b1, b2), Expected(Success(true), 1773, costDetails3, 1773, Seq.fill(4)(2035))) ) }, existingFeature({ (x: Coll[Box]) => x.exists({ (b: Box) => b.value > 1 }) }, @@ -7032,11 +6844,11 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { Seq( - (Coll[BigInt](), Expected(Success(false), 1764, costDetails1, 1764)), - (Coll[BigInt](BigIntZero), Expected(Success(false), 1769, costDetails2, 1769)), - (Coll[BigInt](BigIntOne), Expected(Success(true), 1772, costDetails3, 1772)), - (Coll[BigInt](BigIntZero, BigIntOne), Expected(Success(true), 1777, costDetails4, 1777)), - (Coll[BigInt](BigIntZero, BigInt10), Expected(Success(false), 1777, costDetails4, 1777)) + (Coll[BigInt](), Expected(Success(false), 1764, costDetails1, 1764, Seq.fill(4)(2048))), + (Coll[BigInt](BigIntZero), Expected(Success(false), 1769, costDetails2, 1769, Seq.fill(4)(2053))), + (Coll[BigInt](BigIntOne), Expected(Success(true), 1772, costDetails3, 1772, Seq.fill(4)(2056))), + (Coll[BigInt](BigIntZero, BigIntOne), Expected(Success(true), 1777, costDetails4, 1777, Seq.fill(4)(2061))), + (Coll[BigInt](BigIntZero, BigInt10), Expected(Success(false), 1777, costDetails4, 1777, Seq.fill(4)(2061))) ) }, existingFeature( @@ -7147,11 +6959,11 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { Seq( - (Coll[BigInt](), Expected(Success(true), 1764, costDetails1, 1764)), - (Coll[BigInt](BigIntMinusOne), Expected(Success(false), 1769, costDetails2, 1769)), - (Coll[BigInt](BigIntOne), Expected(Success(true), 1772, costDetails3, 1772)), - (Coll[BigInt](BigIntZero, BigIntOne), Expected(Success(true), 1779, costDetails4, 1779)), - (Coll[BigInt](BigIntZero, BigInt11), Expected(Success(false), 1779, costDetails4, 1779)) + (Coll[BigInt](), Expected(Success(true), 1764, costDetails1, 1764, Seq.fill(4)(2048))), + (Coll[BigInt](BigIntMinusOne), Expected(Success(false), 1769, costDetails2, 1769, Seq.fill(4)(2053))), + (Coll[BigInt](BigIntOne), Expected(Success(true), 1772, costDetails3, 1772, Seq.fill(4)(2056))), + (Coll[BigInt](BigIntZero, BigIntOne), Expected(Success(true), 1779, costDetails4, 1779, Seq.fill(4)(2063))), + (Coll[BigInt](BigIntZero, BigInt11), Expected(Success(false), 1779, costDetails4, 1779, Seq.fill(4)(2063))) ) }, existingFeature( @@ -7281,28 +7093,34 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T, c: Int, cd: CostDetails, newCost: Int) = Expected(Success(v), c, cd, newCost) + def success[T](v: T, c: Int, cd: CostDetails, newCost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), c, cd, newCost, expectedV3Costs) + Seq( - Coll[GroupElement]() -> Expected(Success(Coll[Byte]()), 1773, CostDetails.ZeroCost, 1773, - newVersionedResults = { - val res = ExpectedResult(Success(Coll[Byte]()), Some(1773)) - Seq.tabulate(3)(v => - v -> (res -> Some(costDetails0)) - ) - }), + Coll[GroupElement]() -> Expected(Success(Coll[Byte]()), 1773, CostDetails.ZeroCost, 1773, + newVersionedResults = { + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2029) + } + else { + Seq.fill(4)(1773) + } + Seq.tabulate(4)(v => + v -> (ExpectedResult(Success(Coll[Byte]()), Some(costs(v))) -> Some(costDetails0)) + ) + }), Coll[GroupElement]( Helpers.decodeGroupElement("02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee587"), Helpers.decodeGroupElement("0390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa")) -> - success(Helpers.decodeBytes( - "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa" - ), 1834, costDetails2, 1834), + success(Helpers.decodeBytes( + "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa" + ), 1834, costDetails2, 1834, Seq.fill(4)(2090)), Coll[GroupElement]( Helpers.decodeGroupElement("02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee587"), Helpers.decodeGroupElement("0390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa"), Helpers.decodeGroupElement("03bd839b969b02d218fd1192f2c80cbda9c6ce9c7ddb765f31b748f4666203df85")) -> - success(Helpers.decodeBytes( - "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa03bd839b969b02d218fd1192f2c80cbda9c6ce9c7ddb765f31b748f4666203df85" - ), 1864, costDetails3, 1864) + success(Helpers.decodeBytes( + "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa03bd839b969b02d218fd1192f2c80cbda9c6ce9c7ddb765f31b748f4666203df85" + ), 1864, costDetails3, 1864, Seq.fill(4)(2120)) ) }, existingFeature( @@ -7340,11 +7158,17 @@ class SigmaDslSpecification extends SigmaDslTesting ) -> Expected(res, 1840, expectedDetails = CostDetails.ZeroCost, newCost = 1840, - newVersionedResults = (0 to 2).map(version => + newVersionedResults = (0 to 3).map({ version => + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2104) + } + else { + Seq.fill(4)(1840) + } // successful result for each version version -> (ExpectedResult(res, - verificationCost = Some(1840)) -> Some(costDetails4)) - )) + verificationCost = Some(costs(version))) -> Some(costDetails4)) + })) ) } val f = existingFeature( @@ -7410,7 +7234,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1776, cd, 1776) + def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1776, cd, 1776, Seq.fill(4)(2072)) Seq( ((Coll[Int](), (0, 0)), success(Coll[Int](), costDetails(0))), ((Coll[Int](1), (0, 0)), success(Coll[Int](1, 1), costDetails(2))), @@ -7499,7 +7323,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( // (coll, (index, elem)) { - def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1774, cd, 1774) + def success[T](v: T, cd: CostDetails) = Expected(Success(v), 1774, cd, 1774, Seq.fill(4)(2058)) Seq( ((Coll[Int](), (0, 0)), Expected(new IndexOutOfBoundsException("0"))), ((Coll[Int](1), (0, 0)), success(Coll[Int](0), costDetails(1))), @@ -7576,7 +7400,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( // (coll, (indexes, values)) { - def success[T](v: T, i: Int) = Expected(Success(v), 1774, costDetails(i), 1774) + def success[T](v: T, i: Int) = Expected(Success(v), 1774, costDetails(i), 1774, Seq.fill(4)(2066)) Seq( ((Coll[Int](), (Coll(0), Coll(0))), Expected(new IndexOutOfBoundsException("0"))), ((Coll[Int](), (Coll(0, 1), Coll(0, 0))), Expected(new IndexOutOfBoundsException("0"))), @@ -7638,36 +7462,6 @@ class SigmaDslSpecification extends SigmaDslTesting preGeneratedSamples = Some(samples)) } - // TODO v6.0 (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - property("Coll find method equivalence") { - val find = newFeature((x: Coll[Int]) => x.find({ (v: Int) => v > 0 }), - "{ (x: Coll[Int]) => x.find({ (v: Int) => v > 0} ) }") - forAll { x: Coll[Int] => - find.checkEquality(x) - } - } - - // TODO v6.0 (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/418 - property("Coll bitwise methods equivalence") { - val shiftRight = newFeature( - { (x: Coll[Boolean]) => - if (x.size > 2) x.slice(0, x.size - 2) else Colls.emptyColl[Boolean] - }, - "{ (x: Coll[Boolean]) => x >> 2 }") - forAll { x: Array[Boolean] => - shiftRight.checkEquality(Colls.fromArray(x)) - } - } - - // TODO v6.0 (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - property("Coll diff methods equivalence") { - val diff = newFeature((x: (Coll[Int], Coll[Int])) => x._1.diff(x._2), - "{ (x: (Coll[Int], Coll[Int])) => x._1.diff(x._2) }") - forAll { (x: Coll[Int], y: Coll[Int]) => - diff.checkEquality((x, y)) - } - } - property("Coll fold method equivalence") { val n = ExactNumeric.IntIsExactNumeric val costDetails1 = TracedCost( @@ -7753,15 +7547,15 @@ class SigmaDslSpecification extends SigmaDslTesting // (coll, initState) { Seq( - ((Coll[Byte](), 0), Expected(Success(0), 1767, costDetails1, 1767)), - ((Coll[Byte](), Int.MaxValue), Expected(Success(Int.MaxValue), 1767, costDetails1, 1767)), - ((Coll[Byte](1), Int.MaxValue - 1), Expected(Success(Int.MaxValue), 1773, costDetails2, 1773)), - ((Coll[Byte](1), Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), - ((Coll[Byte](-1), Int.MinValue + 1), Expected(Success(Int.MinValue), 1773, costDetails2, 1773)), - ((Coll[Byte](-1), Int.MinValue), Expected(new ArithmeticException("integer overflow"))), - ((Coll[Byte](1, 2), 0), Expected(Success(3), 1779, costDetails3, 1779)), - ((Coll[Byte](1, -1), 0), Expected(Success(0), 1779, costDetails3, 1779)), - ((Coll[Byte](1, -1, 1), 0), Expected(Success(1), 1785, costDetails4, 1785)) + ((Coll[Byte](), 0), Expected(Success(0), 1767, costDetails1, 1767, Seq.fill(4)(2049))), + ((Coll[Byte](), Int.MaxValue), Expected(Success(Int.MaxValue), 1767, costDetails1, 1767, Seq.fill(4)(2049))), + ((Coll[Byte](1), Int.MaxValue - 1), Expected(Success(Int.MaxValue), 1773, costDetails2, 1773, Seq.fill(4)(2055))), + ((Coll[Byte](1), Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), + ((Coll[Byte](-1), Int.MinValue + 1), Expected(Success(Int.MinValue), 1773, costDetails2, 1773, Seq.fill(4)(2055))), + ((Coll[Byte](-1), Int.MinValue), Expected(new ArithmeticException("integer overflow"))), + ((Coll[Byte](1, 2), 0), Expected(Success(3), 1779, costDetails3, 1779, Seq.fill(4)(2061))), + ((Coll[Byte](1, -1), 0), Expected(Success(0), 1779, costDetails3, 1779, Seq.fill(4)(2061))), + ((Coll[Byte](1, -1, 1), 0), Expected(Success(1), 1785, costDetails4, 1785, Seq.fill(4)(2067))) ) }, existingFeature( @@ -8014,15 +7808,15 @@ class SigmaDslSpecification extends SigmaDslTesting // (coll, initState) { Seq( - ((Coll[Byte](), 0), Expected(Success(0), 1767, costDetails1, 1767)), - ((Coll[Byte](), Int.MaxValue), Expected(Success(Int.MaxValue), 1767, costDetails1, 1767)), - ((Coll[Byte](1), Int.MaxValue - 1), Expected(Success(Int.MaxValue), 1779, costDetails2, 1779)), - ((Coll[Byte](1), Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), - ((Coll[Byte](-1), Int.MinValue + 1), Expected(Success(Int.MinValue + 1), 1777, costDetails3, 1777)), - ((Coll[Byte](-1), Int.MinValue), Expected(Success(Int.MinValue), 1777, costDetails3, 1777)), - ((Coll[Byte](1, 2), 0), Expected(Success(3), 1791, costDetails4, 1791)), - ((Coll[Byte](1, -1), 0), Expected(Success(1), 1789, costDetails5, 1789)), - ((Coll[Byte](1, -1, 1), 0), Expected(Success(2), 1801, costDetails6, 1801)) + ((Coll[Byte](), 0), Expected(Success(0), 1767, costDetails1, 1767, Seq.fill(4)(2089))), + ((Coll[Byte](), Int.MaxValue), Expected(Success(Int.MaxValue), 1767, costDetails1, 1767, Seq.fill(4)(2089))), + ((Coll[Byte](1), Int.MaxValue - 1), Expected(Success(Int.MaxValue), 1779, costDetails2, 1779, Seq.fill(4)(2101))), + ((Coll[Byte](1), Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), + ((Coll[Byte](-1), Int.MinValue + 1), Expected(Success(Int.MinValue + 1), 1777, costDetails3, 1777, Seq.fill(4)(2099))), + ((Coll[Byte](-1), Int.MinValue), Expected(Success(Int.MinValue), 1777, costDetails3, 1777, Seq.fill(4)(2099))), + ((Coll[Byte](1, 2), 0), Expected(Success(3), 1791, costDetails4, 1791, Seq.fill(4)(2113))), + ((Coll[Byte](1, -1), 0), Expected(Success(1), 1789, costDetails5, 1789, Seq.fill(4)(2111))), + ((Coll[Byte](1, -1, 1), 0), Expected(Success(2), 1801, costDetails6, 1801, Seq.fill(4)(2123))) ) }, existingFeature( @@ -8133,11 +7927,16 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( // (coll, (elem: Byte, from: Int)) { - def success0[T](v: T) = Expected(Success(v), 1773, costDetails(0), 1773) - def success1[T](v: T) = Expected(Success(v), 1773, costDetails(1), 1773) - def success2[T](v: T) = Expected(Success(v), 1774, costDetails(2), 1774) - def success3[T](v: T) = Expected(Success(v), 1775, costDetails(3), 1775) - def success12[T](v: T) = Expected(Success(v), 1782, costDetails(12), 1782) + def success0[T](v: T) = Expected(Success(v), 1773, costDetails(0), 1773, Seq.fill(4)(2061)) + + def success1[T](v: T) = Expected(Success(v), 1773, costDetails(1), 1773, Seq.fill(4)(2061)) + + def success2[T](v: T) = Expected(Success(v), 1774, costDetails(2), 1774, Seq.fill(4)(2062)) + + def success3[T](v: T) = Expected(Success(v), 1775, costDetails(3), 1775, Seq.fill(4)(2063)) + + def success12[T](v: T) = Expected(Success(v), 1782, costDetails(12), 1782, Seq.fill(4)(2070)) + Seq( ((Coll[Byte](), (0.toByte, 0)), success0(-1)), ((Coll[Byte](), (0.toByte, -1)), success0(-1)), @@ -8199,7 +7998,8 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1769, costDetails, 1769) + def success[T](v: T) = Expected(Success(v), 1769, costDetails, 1769, Seq.fill(4)(2019)) + Seq( ((Coll[Int](), 0), Expected(new ArrayIndexOutOfBoundsException("0"))), ((Coll[Int](), -1), Expected(new ArrayIndexOutOfBoundsException("-1"))), @@ -8260,10 +8060,11 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) ) + if(!VersionContext.current.isV6SoftForkActivated) { verifyCases( // (coll, (index, default)) { - def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773) + def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773, Seq.fill(4)(2053)) Seq( ((Coll[Int](), (0, default)), success(default)), ((Coll[Int](), (-1, default)), success(default)), @@ -8330,7 +8131,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) ) - )) + ))} } property("Tuple size method equivalence") { @@ -8346,7 +8147,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( { - def success[T](v: T) = Expected(Success(v), 1763, costDetails, 1763) + def success[T](v: T) = Expected(Success(v), 1763, costDetails, 1763, Seq.fill(4)(1997)) Seq( ((0, 0), success(2)), ((1, 2), success(2)) @@ -8361,7 +8162,7 @@ class SigmaDslSpecification extends SigmaDslTesting val samples = genSamples[(Int, Int)](DefaultMinSuccessful) val costDetails = TracedCost(traceBase :+ FixedCostItem(SelectField)) verifyCases( - Seq(((1, 2), Expected(Success(1), cost = 1764, costDetails, 1764))), + Seq(((1, 2), Expected(Success(1), cost = 1764, costDetails, 1764, Seq.fill(4)(1998)))), existingFeature((x: (Int, Int)) => x._1, "{ (x: (Int, Int)) => x(0) }", FuncValue( @@ -8370,7 +8171,7 @@ class SigmaDslSpecification extends SigmaDslTesting )), preGeneratedSamples = Some(samples)) verifyCases( - Seq(((1, 2), Expected(Success(2), cost = 1764, costDetails, 1764))), + Seq(((1, 2), Expected(Success(2), cost = 1764, costDetails, 1764, Seq.fill(4)(1998)))), existingFeature((x: (Int, Int)) => x._2, "{ (x: (Int, Int)) => x(1) }", FuncValue( @@ -8412,9 +8213,9 @@ class SigmaDslSpecification extends SigmaDslTesting { def success[T](v: T, c: Int) = Expected(Success(v), c) Seq( - (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768)), - (Coll[Int](1), Expected(Success(Coll[Int](2)), 1771, costDetails(1), 1771)), - (Coll[Int](1, 2), Expected(Success(Coll[Int](2, 3)), 1774, costDetails(2), 1774)), + (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768, Seq.fill(4)(2022))), + (Coll[Int](1), Expected(Success(Coll[Int](2)), 1771, costDetails(1), 1771, Seq.fill(4)(2025))), + (Coll[Int](1, 2), Expected(Success(Coll[Int](2, 3)), 1774, costDetails(2), 1774, Seq.fill(4)(2028))), (Coll[Int](1, 2, Int.MaxValue), Expected(new ArithmeticException("integer overflow"))) ) }, @@ -8508,10 +8309,10 @@ class SigmaDslSpecification extends SigmaDslTesting { def success[T](v: T, c: Int) = Expected(Success(v), c) Seq( - (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails1, 1768)), - (Coll[Int](1), Expected(Success(Coll[Int](2)), 1775, costDetails2, 1775)), - (Coll[Int](-1), Expected(Success(Coll[Int](1)), 1775, costDetails3, 1775)), - (Coll[Int](1, -2), Expected(Success(Coll[Int](2, 2)), 1782, costDetails4, 1782)), + (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails1, 1768, Seq.fill(4)(2054))), + (Coll[Int](1), Expected(Success(Coll[Int](2)), 1775, costDetails2, 1775, Seq.fill(4)(2061))), + (Coll[Int](-1), Expected(Success(Coll[Int](1)), 1775, costDetails3, 1775, Seq.fill(4)(2061))), + (Coll[Int](1, -2), Expected(Success(Coll[Int](2, 2)), 1782, costDetails4, 1782, Seq.fill(4)(2068))), (Coll[Int](1, 2, Int.MaxValue), Expected(new ArithmeticException("integer overflow"))), (Coll[Int](1, 2, Int.MinValue), Expected(new ArithmeticException("integer overflow"))) ) @@ -8559,24 +8360,24 @@ class SigmaDslSpecification extends SigmaDslTesting val o = ExactOrdering.IntIsExactOrdering verifyCases( - { - Seq( - (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768)), - (Coll[Int](1), Expected(Success(Coll[Int](1)), 1771, costDetails(1), 1771)), - (Coll[Int](1, 2), Expected(Success(Coll[Int](1, 2)), 1775, costDetails(2), 1775)), - (Coll[Int](1, 2, -1), Expected(Success(Coll[Int](1, 2)), 1778, costDetails(3), 1778)), - (Coll[Int](1, -1, 2, -2), Expected(Success(Coll[Int](1, 2)), 1782, costDetails(4), 1782)) - ) - }, - existingFeature((x: Coll[Int]) => x.filter({ (v: Int) => o.gt(v, 0) }), - "{ (x: Coll[Int]) => x.filter({ (v: Int) => v > 0 }) }", - FuncValue( - Array((1, SCollectionType(SInt))), - Filter( - ValUse(1, SCollectionType(SInt)), - FuncValue(Array((3, SInt)), GT(ValUse(3, SInt), IntConstant(0))) + { + Seq( + (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768, Seq.fill(4)(2022))), + (Coll[Int](1), Expected(Success(Coll[Int](1)), 1771, costDetails(1), 1771, Seq.fill(4)(2025))), + (Coll[Int](1, 2), Expected(Success(Coll[Int](1, 2)), 1775, costDetails(2), 1775, Seq.fill(4)(2029))), + (Coll[Int](1, 2, -1), Expected(Success(Coll[Int](1, 2)), 1778, costDetails(3), 1778, Seq.fill(4)(2032))), + (Coll[Int](1, -1, 2, -2), Expected(Success(Coll[Int](1, 2)), 1782, costDetails(4), 1782, Seq.fill(4)(2036))) ) - ))) + }, + existingFeature((x: Coll[Int]) => x.filter({ (v: Int) => o.gt(v, 0) }), + "{ (x: Coll[Int]) => x.filter({ (v: Int) => v > 0 }) }", + FuncValue( + Array((1, SCollectionType(SInt))), + Filter( + ValUse(1, SCollectionType(SInt)), + FuncValue(Array((3, SInt)), GT(ValUse(3, SInt), IntConstant(0))) + ) + ))) } property("Coll filter with nested If") { @@ -8634,29 +8435,30 @@ class SigmaDslSpecification extends SigmaDslTesting val o = ExactOrdering.IntIsExactOrdering verifyCases( - { - def success[T](v: T, c: Int) = Expected(Success(v), c) - Seq( - (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768)), - (Coll[Int](1), Expected(Success(Coll[Int](1)), 1775, costDetails(1), 1775)), - (Coll[Int](10), Expected(Success(Coll[Int]()), 1775, costDetails(1), 1775)), - (Coll[Int](1, 2), Expected(Success(Coll[Int](1, 2)), 1783, costDetails(2), 1783)), - (Coll[Int](1, 2, 0), Expected(Success(Coll[Int](1, 2)), 1788, costDetails3, 1788)), - (Coll[Int](1, -1, 2, -2, 11), Expected(Success(Coll[Int](1, 2)), 1800, costDetails5, 1800)) - ) - }, - existingFeature((x: Coll[Int]) => x.filter({ (v: Int) => if (o.gt(v, 0)) v < 10 else false }), - "{ (x: Coll[Int]) => x.filter({ (v: Int) => if (v > 0) v < 10 else false }) }", - FuncValue( - Array((1, SCollectionType(SInt))), - Filter( - ValUse(1, SCollectionType(SInt)), - FuncValue( - Array((3, SInt)), - If(GT(ValUse(3, SInt), IntConstant(0)), LT(ValUse(3, SInt), IntConstant(10)), FalseLeaf) - ) + { + def success[T](v: T, c: Int) = Expected(Success(v), c) + + Seq( + (Coll[Int](), Expected(Success(Coll[Int]()), 1768, costDetails(0), 1768, Seq.fill(4)(2048))), + (Coll[Int](1), Expected(Success(Coll[Int](1)), 1775, costDetails(1), 1775, Seq.fill(4)(2055))), + (Coll[Int](10), Expected(Success(Coll[Int]()), 1775, costDetails(1), 1775, Seq.fill(4)(2055))), + (Coll[Int](1, 2), Expected(Success(Coll[Int](1, 2)), 1783, costDetails(2), 1783, Seq.fill(4)(2063))), + (Coll[Int](1, 2, 0), Expected(Success(Coll[Int](1, 2)), 1788, costDetails3, 1788, Seq.fill(4)(2068))), + (Coll[Int](1, -1, 2, -2, 11), Expected(Success(Coll[Int](1, 2)), 1800, costDetails5, 1800, Seq.fill(4)(2080))) ) - ))) + }, + existingFeature((x: Coll[Int]) => x.filter({ (v: Int) => if (o.gt(v, 0)) v < 10 else false }), + "{ (x: Coll[Int]) => x.filter({ (v: Int) => if (v > 0) v < 10 else false }) }", + FuncValue( + Array((1, SCollectionType(SInt))), + Filter( + ValUse(1, SCollectionType(SInt)), + FuncValue( + Array((3, SInt)), + If(GT(ValUse(3, SInt), IntConstant(0)), LT(ValUse(3, SInt), IntConstant(10)), FalseLeaf) + ) + ) + ))) } property("Coll slice method equivalence") { @@ -8683,48 +8485,49 @@ class SigmaDslSpecification extends SigmaDslTesting val samples = genSamples(collWithRangeGen, DefaultMinSuccessful) if (lowerMethodCallsInTests) { verifyCases( - { - val cost = 1772 - val newCost = 1772 - Seq( - // (coll, (from, until)) - ((Coll[Int](), (-1, 0)), Expected(Success(Coll[Int]()), cost, costDetails(1), newCost)), - ((Coll[Int](), (0, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1), (0, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1), (0, -1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1), (1, 1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1), (-1, 1)), Expected(Success(Coll[Int](1)), cost, costDetails(2), newCost)), - ((Coll[Int](1, 2), (1, 1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1, 2), (1, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost)), - ((Coll[Int](1, 2), (1, 2)), Expected(Success(Coll[Int](2)), cost, costDetails(1), newCost)), - ((Coll[Int](1, 2, 3, 4), (1, 3)), Expected(Success(Coll[Int](2, 3)), cost, costDetails(2), newCost)) - ) - }, - existingFeature((x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2), - "{ (x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2) }", - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[Value[STuple]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 2.toByte + { + val cost = 1772 + val newCost = 1772 + val v3Costs = Seq.fill(4)(2050) + Seq( + // (coll, (from, until)) + ((Coll[Int](), (-1, 0)), Expected(Success(Coll[Int]()), cost, costDetails(1), newCost, v3Costs)), + ((Coll[Int](), (0, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1), (0, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1), (0, -1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1), (1, 1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1), (-1, 1)), Expected(Success(Coll[Int](1)), cost, costDetails(2), newCost, v3Costs)), + ((Coll[Int](1, 2), (1, 1)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1, 2), (1, 0)), Expected(Success(Coll[Int]()), cost, costDetails(0), newCost, v3Costs)), + ((Coll[Int](1, 2), (1, 2)), Expected(Success(Coll[Int](2)), cost, costDetails(1), newCost, v3Costs)), + ((Coll[Int](1, 2, 3, 4), (1, 3)), Expected(Success(Coll[Int](2, 3)), cost, costDetails(2), newCost, v3Costs)) + ) + }, + existingFeature((x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2), + "{ (x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2) }", + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), + BlockValue( + Vector( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 2.toByte + ) ) - ) - ), - Slice( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 1.toByte ), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) + Slice( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 1.toByte + ), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) + ) ) - ) - )), + )), preGeneratedSamples = Some(samples)) } else { def error = new java.lang.NoSuchMethodException("sigmastate.SCollection$.slice_eval(sigmastate.lang.Terms$MethodCall,sigma.Coll,int,int,sigmastate.interpreter.ErgoTreeEvaluator))") @@ -8774,37 +8577,38 @@ class SigmaDslSpecification extends SigmaDslTesting ) if (lowerMethodCallsInTests) { verifyCases( - { - def success[T](v: T, size: Int) = Expected(Success(v), 1770, costDetails(size), 1770) - val arr1 = Gen.listOfN(100, arbitrary[Int]).map(_.toArray).sample.get - val arr2 = Gen.listOfN(200, arbitrary[Int]).map(_.toArray).sample.get - Seq( - (Coll[Int](), Coll[Int]()) -> success(Coll[Int](), 0), - (Coll[Int](), Coll[Int](1)) -> success(Coll[Int](1), 1), - (Coll[Int](1), Coll[Int]()) -> success(Coll[Int](1), 1), - (Coll[Int](1), Coll[Int](2)) -> success(Coll[Int](1, 2), 2), - (Coll[Int](1), Coll[Int](2, 3)) -> success(Coll[Int](1, 2, 3), 3), - (Coll[Int](1, 2), Coll[Int](3)) -> success(Coll[Int](1, 2, 3), 3), - (Coll[Int](1, 2), Coll[Int](3, 4)) -> success(Coll[Int](1, 2, 3, 4), 4), - (Coll[Int](arr1:_*), Coll[Int](arr2:_*)) -> Expected(Success(Coll[Int](arr1 ++ arr2:_*)), 1771, costDetails(300), 1771) - ) - }, - existingFeature( - { (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }, - "{ (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }", - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SCollectionType(SInt)))), - Append( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), - 1.toByte - ), - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), - 2.toByte - ) + { + def success[T](v: T, size: Int) = Expected(Success(v), 1770, costDetails(size), 1770, Seq.fill(4)(2022)) + + val arr1 = Gen.listOfN(100, arbitrary[Int]).map(_.toArray).sample.get + val arr2 = Gen.listOfN(200, arbitrary[Int]).map(_.toArray).sample.get + Seq( + (Coll[Int](), Coll[Int]()) -> success(Coll[Int](), 0), + (Coll[Int](), Coll[Int](1)) -> success(Coll[Int](1), 1), + (Coll[Int](1), Coll[Int]()) -> success(Coll[Int](1), 1), + (Coll[Int](1), Coll[Int](2)) -> success(Coll[Int](1, 2), 2), + (Coll[Int](1), Coll[Int](2, 3)) -> success(Coll[Int](1, 2, 3), 3), + (Coll[Int](1, 2), Coll[Int](3)) -> success(Coll[Int](1, 2, 3), 3), + (Coll[Int](1, 2), Coll[Int](3, 4)) -> success(Coll[Int](1, 2, 3, 4), 4), + (Coll[Int](arr1: _*), Coll[Int](arr2: _*)) -> Expected(Success(Coll[Int](arr1 ++ arr2: _*)), 1771, costDetails(300), 1771, Seq.fill(4)(2023)) ) - ))) + }, + existingFeature( + { (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }, + "{ (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }", + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SCollectionType(SInt)))), + Append( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 1.toByte + ), + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 2.toByte + ) + ) + ))) } else { def error = new java.lang.NoSuchMethodException("sigmastate.SCollection$.append_eval(sigmastate.lang.Terms$MethodCall,sigma.Coll,sigma.Coll,sigmastate.interpreter.ErgoTreeEvaluator))") verifyCases( @@ -8881,33 +8685,35 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( (None -> Expected(new NoSuchElementException("None.get"))), - (Some(10L) -> Expected(Success(10L), 1765, costDetails1, 1765))), + (Some(10L) -> Expected(Success(10L), 1765, costDetails1, 1765, Seq.fill(4)(1997)))), existingFeature({ (x: Option[Long]) => x.get }, "{ (x: Option[Long]) => x.get }", FuncValue(Vector((1, SOption(SLong))), OptionGet(ValUse(1, SOption(SLong)))))) verifyCases( Seq( - (None -> Expected(Success(false), 1764, costDetails2, 1764)), - (Some(10L) -> Expected(Success(true), 1764, costDetails2, 1764))), + (None -> Expected(Success(false), 1764, costDetails2, 1764, Seq.fill(4)(1996))), + (Some(10L) -> Expected(Success(true), 1764, costDetails2, 1764, Seq.fill(4)(1996)))), existingFeature({ (x: Option[Long]) => x.isDefined }, "{ (x: Option[Long]) => x.isDefined }", FuncValue(Vector((1, SOption(SLong))), OptionIsDefined(ValUse(1, SOption(SLong)))))) - verifyCases( - Seq( - (None -> Expected(Success(1L), 1766, costDetails3, 1766)), - (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766))), - 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( - (None -> Expected(Success(None), 1766, costDetails4, 1766)), - (Some(10L) -> Expected(Success(None), 1768, costDetails5, 1768)), - (Some(1L) -> Expected(Success(Some(1L)), 1769, costDetails5, 1769))), - existingFeature({ (x: Option[Long]) => x.filter({ (v: Long) => v == 1} ) }, + (None -> Expected(Success(None), 1766, costDetails4, 1766, Seq.fill(4)(2028))), + (Some(10L) -> Expected(Success(None), 1768, costDetails5, 1768, Seq.fill(4)(2030))), + (Some(1L) -> Expected(Success(Some(1L)), 1769, costDetails5, 1769, Seq.fill(4)(2031)))), + existingFeature({ (x: Option[Long]) => x.filter({ (v: Long) => v == 1 }) }, "{ (x: Option[Long]) => x.filter({ (v: Long) => v == 1 }) }", FuncValue( Vector((1, SOption(SLong))), @@ -8922,8 +8728,8 @@ class SigmaDslSpecification extends SigmaDslTesting val n = ExactNumeric.LongIsExactNumeric verifyCases( Seq( - (None -> Expected(Success(None), 1766, costDetails6, 1766)), - (Some(10L) -> Expected(Success(Some(11L)), 1770, costDetails7, 1770)), + (None -> Expected(Success(None), 1766, costDetails6, 1766, Seq.fill(4)(2028))), + (Some(10L) -> Expected(Success(Some(11L)), 1770, costDetails7, 1770, Seq.fill(4)(2032))), (Some(Long.MaxValue) -> Expected(new ArithmeticException("long overflow")))), existingFeature({ (x: Option[Long]) => x.map( (v: Long) => n.plus(v, 1) ) }, "{ (x: Option[Long]) => x.map({ (v: Long) => v + 1 }) }", @@ -8985,10 +8791,10 @@ class SigmaDslSpecification extends SigmaDslTesting val o = ExactOrdering.LongIsExactOrdering verifyCases( Seq( - (None -> Expected(Success(None), 1766, costDetails1, 1766)), - (Some(0L) -> Expected(Success(None), 1771, costDetails2, 1771)), - (Some(10L) -> Expected(Success(Some(10L)), 1774, costDetails3, 1774)), - (Some(11L) -> Expected(Success(None), 1774, costDetails3, 1774))), + (None -> Expected(Success(None), 1766, costDetails1, 1766, Seq.fill(4)(2052))), + (Some(0L) -> Expected(Success(None), 1771, costDetails2, 1771, Seq.fill(4)(2057))), + (Some(10L) -> Expected(Success(Some(10L)), 1774, costDetails3, 1774, Seq.fill(4)(2060))), + (Some(11L) -> Expected(Success(None), 1774, costDetails3, 1774, Seq.fill(4)(2060)))), existingFeature( { (x: Option[Long]) => x.filter({ (v: Long) => if (o.gt(v, 0L)) v <= 10 else false } ) }, "{ (x: Option[Long]) => x.filter({ (v: Long) => if (v > 0) v <= 10 else false }) }", @@ -9049,10 +8855,10 @@ class SigmaDslSpecification extends SigmaDslTesting val n = ExactNumeric.LongIsExactNumeric verifyCases( Seq( - (None -> Expected(Success(None), 1766, costDetails4, 1766)), - (Some(0L) -> Expected(Success(Some(0L)), 1772, costDetails5, 1772)), - (Some(10L) -> Expected(Success(Some(10L)), 1772, costDetails5, 1772)), - (Some(-1L) -> Expected(Success(Some(-2L)), 1774, costDetails6, 1774)), + (None -> Expected(Success(None), 1766, costDetails4, 1766, Seq.fill(4)(2048))), + (Some(0L) -> Expected(Success(Some(0L)), 1772, costDetails5, 1772, Seq.fill(4)(2054))), + (Some(10L) -> Expected(Success(Some(10L)), 1772, costDetails5, 1772, Seq.fill(4)(2054))), + (Some(-1L) -> Expected(Success(Some(-2L)), 1774, costDetails6, 1774, Seq.fill(4)(2056))), (Some(Long.MinValue) -> Expected(new ArithmeticException("long overflow")))), existingFeature( { (x: Option[Long]) => x.map( (v: Long) => if (o.lt(v, 0)) n.minus(v, 1) else v ) }, @@ -9079,17 +8885,6 @@ class SigmaDslSpecification extends SigmaDslTesting ) )) } - // TODO v6.0: implement Option.fold (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479) - property("Option new methods") { - val n = ExactNumeric.LongIsExactNumeric - val fold = newFeature({ (x: Option[Long]) => x.fold(5.toLong)( (v: Long) => n.plus(v, 1) ) }, - "{ (x: Option[Long]) => x.fold(5, { (v: Long) => v + 1 }) }") - - forAll { x: Option[Long] => - Seq(fold).map(_.checkEquality(x)) - } - } - property("Option fold workaround method") { val costDetails1 = TracedCost( traceBase ++ Array( @@ -9116,20 +8911,30 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq( (None -> Expected( - value = Failure(new NoSuchElementException("None.get")), - cost = 0, - expectedDetails = CostDetails.ZeroCost, - newCost = 1766, - newVersionedResults = Seq.tabulate(3)(v => v -> (ExpectedResult(Success(5L), Some(1766)) -> Some(costDetails1))) - )), + value = Failure(new NoSuchElementException("None.get")), + cost = 0, + expectedDetails = CostDetails.ZeroCost, + newCost = 1766, + newVersionedResults = Seq.tabulate(4)({ v => + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2042) + } + else { + Seq.fill(4)(1766) + } + v -> (ExpectedResult(Success(5L), Some(costs(v))) -> Some(costDetails1)) + }) + )), (Some(0L) -> Expected( Success(1L), cost = 1774, expectedDetails = costDetails2, - expectedNewCost = 1774)), + expectedNewCost = 1774, + expectedV3Costs = Seq.fill(4)(2050))), (Some(Long.MaxValue) -> Expected(new ArithmeticException("long overflow"))) ), changedFeature( + changedInVersion = VersionContext.JitActivationVersion, scalaFunc = { (x: Option[Long]) => def f(opt: Long): Long = n.plus(opt, 1) if (x.isDefined) f(x.get) @@ -9194,19 +8999,22 @@ class SigmaDslSpecification extends SigmaDslTesting Success(Helpers.decodeBytes("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8")), 1768, costDetailsBlake(0), - 1768 + 1768, + Seq.fill(4)(2000) ), Helpers.decodeBytes("e0ff0105ffffac31010017ff33") -> Expected( Success(Helpers.decodeBytes("33707eed9aab64874ff2daa6d6a378f61e7da36398fb36c194c7562c9ff846b5")), 1768, costDetailsBlake(13), - 1768 + 1768, + Seq.fill(4)(2000) ), Colls.replicate(1024, 1.toByte) -> Expected( Success(Helpers.decodeBytes("45d8456fc5d41d1ec1124cb92e41192c1c3ec88f0bf7ae2dc6e9cf75bec22045")), 1773, costDetailsBlake(1024), - 1773 + 1773, + Seq.fill(4)(2005) ) ), existingFeature((x: Coll[Byte]) => SigmaDsl.blake2b256(x), @@ -9219,19 +9027,22 @@ class SigmaDslSpecification extends SigmaDslTesting Success(Helpers.decodeBytes("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")), 1774, costDetailsSha(0), - 1774 + 1774, + Seq.fill(4)(2006) ), Helpers.decodeBytes("e0ff0105ffffac31010017ff33") -> Expected( Success(Helpers.decodeBytes("367d0ec2cdc14aac29d5beb60c2bfc86d5a44a246308659af61c1b85fa2ca2cc")), 1774, costDetailsSha(13), - 1774 + 1774, + Seq.fill(4)(2006) ), Colls.replicate(1024, 1.toByte) -> Expected( Success(Helpers.decodeBytes("5a648d8015900d89664e00e125df179636301a2d8fa191c1aa2bd9358ea53a69")), 1786, costDetailsSha(1024), - 1786 + 1786, + Seq.fill(4)(2018) ) ), existingFeature((x: Coll[Byte]) => SigmaDsl.sha256(x), @@ -9241,10 +9052,11 @@ class SigmaDslSpecification extends SigmaDslTesting property("sigmaProp equivalence") { val costDetails = TracedCost(traceBase :+ FixedCostItem(BoolToSigmaProp)) + val v3Costs = Seq.fill(4)(1997) verifyCases( Seq( - (false, Expected(Success(CSigmaProp(TrivialProp.FalseProp)), 1765, costDetails, 1765)), - (true, Expected(Success(CSigmaProp(TrivialProp.TrueProp)), 1765, costDetails, 1765))), + (false, Expected(Success(CSigmaProp(TrivialProp.FalseProp)), 1765, costDetails, 1765, v3Costs)), + (true, Expected(Success(CSigmaProp(TrivialProp.TrueProp)), 1765, costDetails, 1765, v3Costs))), existingFeature((x: Boolean) => sigmaProp(x), "{ (x: Boolean) => sigmaProp(x) }", FuncValue(Vector((1, SBoolean)), BoolToSigmaProp(ValUse(1, SBoolean))))) @@ -9271,7 +9083,7 @@ class SigmaDslSpecification extends SigmaDslTesting Helpers.decodeECPoint("02614b14a8c6c6b4b7ce017d72fbca7f9218b72c16bdd88f170ffb300b106b9014"), Helpers.decodeECPoint("034cc5572276adfa3e283a3f1b0f0028afaadeaa362618c5ec43262d8cefe7f004") ) - )) -> Expected(Success(CSigmaProp(TrivialProp.TrueProp)), 1770, costDetails(1), 1770), + )) -> Expected(Success(CSigmaProp(TrivialProp.TrueProp)), 1770, costDetails(1), 1770, Seq.fill(4)(2018)), Coll[SigmaProp]( CSigmaProp( ProveDHTuple( @@ -9299,7 +9111,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) ) - ), 1873, costDetails(3), 1873), + ), 1873, costDetails(3), 1873, Seq.fill(4)(2121)), Colls.replicate[SigmaProp](AtLeast.MaxChildrenCount + 1, CSigmaProp(TrivialProp.TrueProp)) -> Expected(new IllegalArgumentException("Expected input elements count should not exceed 255, actual: 256")) ), @@ -9331,31 +9143,32 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, costDetails1, newCost) + def success[T](v: T, newCost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), newCost, costDetails1, newCost, expectedV3Costs) + Seq( (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success( - CSigmaProp( - CAND( - Seq( - ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), - ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) - ) + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success( + CSigmaProp( + CAND( + Seq( + ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), + ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) ) - ), 1802), + ) + ), 1802, Seq.fill(4)(2048)), (CSigmaProp(TrivialProp.TrueProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784, Seq.fill(4)(2030)), (CSigmaProp(TrivialProp.FalseProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success(CSigmaProp(TrivialProp.FalseProp), 1767), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(TrivialProp.FalseProp), 1767, Seq.fill(4)(2013)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.TrueProp)) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784), + CSigmaProp(TrivialProp.TrueProp)) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784, Seq.fill(4)(2030)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.FalseProp)) -> - success(CSigmaProp(TrivialProp.FalseProp), 1767) + CSigmaProp(TrivialProp.FalseProp)) -> + success(CSigmaProp(TrivialProp.FalseProp), 1767, Seq.fill(4)(2013)) ) }, existingFeature( @@ -9375,9 +9188,9 @@ class SigmaDslSpecification extends SigmaDslTesting { Seq( (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> - Expected(Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), 1786, costDetails2, 1786), + Expected(Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), 1786, costDetails2, 1786, Seq.fill(4)(2038)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> - Expected(Success(CSigmaProp(TrivialProp.FalseProp)), 1769, costDetails2, 1769) + Expected(Success(CSigmaProp(TrivialProp.FalseProp)), 1769, costDetails2, 1769, Seq.fill(4)(2021)) ) }, existingFeature( @@ -9406,32 +9219,33 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails1 = TracedCost(testTraceBase :+ ast.SeqCostItem(CompanionDesc(SigmaOr), PerItemCost(JitCost(10), JitCost(2), 1), 2)) verifyCases( { - def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, costDetails1, newCost) + def success[T](v: T, newCost: Int, v3Costs: Seq[Int]) = Expected(Success(v), newCost, costDetails1, newCost, v3Costs) + Seq( (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success( - CSigmaProp( - COR( - Seq( - ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), - ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) - ) + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success( + CSigmaProp( + COR( + Seq( + ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), + ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) ) - ), - 1802), + ) + ), + 1802, Seq.fill(4)(2048)), (CSigmaProp(TrivialProp.FalseProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784, Seq.fill(4)(2030)), (CSigmaProp(TrivialProp.TrueProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - success(CSigmaProp(TrivialProp.TrueProp), 1767), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(TrivialProp.TrueProp), 1767, Seq.fill(4)(2013)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.FalseProp)) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784), + CSigmaProp(TrivialProp.FalseProp)) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1784, Seq.fill(4)(2030)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.TrueProp)) -> - success(CSigmaProp(TrivialProp.TrueProp), 1767) + CSigmaProp(TrivialProp.TrueProp)) -> + success(CSigmaProp(TrivialProp.TrueProp), 1767, Seq.fill(4)(2013)) ) }, existingFeature( @@ -9455,12 +9269,13 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( { - def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, costDetails2, newCost) + def success[T](v: T, newCost: Int, v3Costs: Seq[Int]) = Expected(Success(v), newCost, costDetails2, newCost, v3Costs) + Seq( (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> - success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1786), + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), 1786, Seq.fill(4)(2038)), (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> - success(CSigmaProp(TrivialProp.TrueProp), 1769) + success(CSigmaProp(TrivialProp.TrueProp), 1769, Seq.fill(4)(2021)) ) }, existingFeature( @@ -9498,25 +9313,25 @@ class SigmaDslSpecification extends SigmaDslTesting Helpers.decodeBytes( "0008ce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) - ), cost = 1771, newDetails(4), expectedNewCost = 1771), + ), cost = 1771, newDetails(4), expectedNewCost = 1771, Seq.fill(4)(2003)), CSigmaProp(pk) -> Expected(Success( Helpers.decodeBytes("0008cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6f")), - cost = 1769, newDetails(1), expectedNewCost = 1769), + cost = 1769, newDetails(1), expectedNewCost = 1769, Seq.fill(4)(2001)), CSigmaProp(and) -> Expected(Success( Helpers.decodeBytes( "00089602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) - ), cost = 1772, newDetails(6), expectedNewCost = 1772), + ), cost = 1772, newDetails(6), expectedNewCost = 1772, Seq.fill(4)(2004)), CSigmaProp(threshold) -> Expected(Success( Helpers.decodeBytes( "0008980204cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419702cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) - ), cost = 1780, newDetails(18), expectedNewCost = 1780), + ), cost = 1780, newDetails(18), expectedNewCost = 1780, Seq.fill(4)(2012)), CSigmaProp(data.COR(Array(pk, dht, and, or, threshold))) -> Expected(Success( Helpers.decodeBytes( "00089705cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419702cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441980204cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419702cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) - ), cost = 1791, newDetails(36), expectedNewCost = 1791) + ), cost = 1791, newDetails(36), expectedNewCost = 1791, Seq.fill(4)(2023)) ) }, existingFeature((x: SigmaProp) => x.propBytes, @@ -9525,39 +9340,22 @@ class SigmaDslSpecification extends SigmaDslTesting preGeneratedSamples = Some(Seq())) } - // TODO v6.0 (3h): implement allZK func https://github.com/ScorexFoundation/sigmastate-interpreter/issues/543 - property("allZK equivalence") { - lazy val allZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.allZK(x), - "{ (x: Coll[SigmaProp]) => allZK(x) }") - forAll { x: Coll[SigmaProp] => - allZK.checkEquality(x) - } - } - - // TODO v6.0 (3h): implement anyZK func https://github.com/ScorexFoundation/sigmastate-interpreter/issues/543 - property("anyZK equivalence") { - lazy val anyZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.anyZK(x), - "{ (x: Coll[SigmaProp]) => anyZK(x) }") - forAll { x: Coll[SigmaProp] => - anyZK.checkEquality(x) - } - } - property("allOf equivalence") { def costDetails(i: Int) = TracedCost(traceBase :+ ast.SeqCostItem(CompanionDesc(AND), PerItemCost(JitCost(10), JitCost(5), 32), i)) + val v3Costs = Seq.fill(4)(1997) verifyCases( Seq( - (Coll[Boolean]() -> Expected(Success(true), 1765, costDetails(0), 1765)), - (Coll[Boolean](true) -> Expected(Success(true), 1765, costDetails(1), 1765)), - (Coll[Boolean](false) -> Expected(Success(false), 1765, costDetails(1), 1765)), - (Coll[Boolean](false, false) -> Expected(Success(false), 1765, costDetails(1), 1765)), - (Coll[Boolean](false, true) -> Expected(Success(false), 1765, costDetails(1), 1765)), - (Coll[Boolean](true, false) -> Expected(Success(false), 1765, costDetails(2), 1765)), - (Coll[Boolean](true, true) -> Expected(Success(true), 1765, costDetails(2), 1765)), - (Coll[Boolean](true, false, false) -> Expected(Success(false), 1765, costDetails(2), 1765)), - (Coll[Boolean](true, false, true) -> Expected(Success(false), 1765, costDetails(2), 1765)), - (Coll[Boolean](true, true, false) -> Expected(Success(false), 1765, costDetails(3), 1765)), - (Coll[Boolean](true, true, true) -> Expected(Success(true), 1765, costDetails(3), 1765)) + (Coll[Boolean]() -> Expected(Success(true), 1765, costDetails(0), 1765, v3Costs)), + (Coll[Boolean](true) -> Expected(Success(true), 1765, costDetails(1), 1765, v3Costs)), + (Coll[Boolean](false) -> Expected(Success(false), 1765, costDetails(1), 1765, v3Costs)), + (Coll[Boolean](false, false) -> Expected(Success(false), 1765, costDetails(1), 1765, v3Costs)), + (Coll[Boolean](false, true) -> Expected(Success(false), 1765, costDetails(1), 1765, v3Costs)), + (Coll[Boolean](true, false) -> Expected(Success(false), 1765, costDetails(2), 1765, v3Costs)), + (Coll[Boolean](true, true) -> Expected(Success(true), 1765, costDetails(2), 1765, v3Costs)), + (Coll[Boolean](true, false, false) -> Expected(Success(false), 1765, costDetails(2), 1765, v3Costs)), + (Coll[Boolean](true, false, true) -> Expected(Success(false), 1765, costDetails(2), 1765, v3Costs)), + (Coll[Boolean](true, true, false) -> Expected(Success(false), 1765, costDetails(3), 1765, v3Costs)), + (Coll[Boolean](true, true, true) -> Expected(Success(true), 1765, costDetails(3), 1765, v3Costs)) ), existingFeature((x: Coll[Boolean]) => SigmaDsl.allOf(x), "{ (x: Coll[Boolean]) => allOf(x) }", @@ -9566,19 +9364,20 @@ class SigmaDslSpecification extends SigmaDslTesting property("anyOf equivalence") { def costDetails(i: Int) = TracedCost(traceBase :+ ast.SeqCostItem(CompanionDesc(OR), PerItemCost(JitCost(5), JitCost(5), 64), i)) + val v3Costs = Seq.fill(4)(1996) verifyCases( Seq( - (Coll[Boolean]() -> Expected(Success(false), 1764, costDetails(0), 1764)), - (Coll[Boolean](true) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](false) -> Expected(Success(false), 1764, costDetails(1), 1764)), - (Coll[Boolean](false, false) -> Expected(Success(false), 1764, costDetails(2), 1764)), - (Coll[Boolean](false, true) -> Expected(Success(true), 1764, costDetails(2), 1764)), - (Coll[Boolean](true, false) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, true) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, false, false) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, false, true) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, true, false) -> Expected(Success(true), 1764, costDetails(1), 1764)), - (Coll[Boolean](true, true, true) -> Expected(Success(true), 1764, costDetails(1), 1764)) + (Coll[Boolean]() -> Expected(Success(false), 1764, costDetails(0), 1764, v3Costs)), + (Coll[Boolean](true) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](false) -> Expected(Success(false), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](false, false) -> Expected(Success(false), 1764, costDetails(2), 1764, v3Costs)), + (Coll[Boolean](false, true) -> Expected(Success(true), 1764, costDetails(2), 1764, v3Costs)), + (Coll[Boolean](true, false) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, true) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, false, false) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, false, true) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, true, false) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)), + (Coll[Boolean](true, true, true) -> Expected(Success(true), 1764, costDetails(1), 1764, v3Costs)) ), existingFeature((x: Coll[Boolean]) => SigmaDsl.anyOf(x), "{ (x: Coll[Boolean]) => anyOf(x) }", @@ -9591,10 +9390,11 @@ class SigmaDslSpecification extends SigmaDslTesting Seq( (Helpers.decodeGroupElement("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69") -> Expected(Success( - CSigmaProp(ProveDlog(Helpers.decodeECPoint("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69")))), - cost = 1782, - costDetails, - 1782)) + CSigmaProp(ProveDlog(Helpers.decodeECPoint("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69")))), + cost = 1782, + costDetails, + 1782, + Seq.fill(4)(2014))) ), existingFeature({ (x: GroupElement) => SigmaDsl.proveDlog(x) }, "{ (x: GroupElement) => proveDlog(x) }", @@ -9614,18 +9414,19 @@ class SigmaDslSpecification extends SigmaDslTesting Seq( (Helpers.decodeGroupElement("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") -> Expected(Success( - CSigmaProp( - ProveDHTuple( - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") - ) - )), - cost = 1836, - costDetails, - 1836 - )) + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") + ) + )), + cost = 1836, + costDetails, + 1836, + Seq.fill(4)(2080) + )) ), existingFeature({ (x: GroupElement) => SigmaDsl.proveDHTuple(x, x, x, x) }, "{ (x: GroupElement) => proveDHTuple(x, x, x, x) }", @@ -9666,7 +9467,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) verifyCases( { - def success[T](v: T, cd: CostDetails, cost: Int) = Expected(Success(v), cost, cd, cost) + def success[T](v: T, cd: CostDetails, cost: Int, expectedV3Costs: Seq[Int]) = Expected(Success(v), cost, cd, cost, expectedV3Costs) Seq( (Helpers.decodeBytes(""), 0) -> Expected(new java.nio.BufferUnderflowException()), @@ -9678,8 +9479,16 @@ class SigmaDslSpecification extends SigmaDslTesting expectedDetails = CostDetails.ZeroCost, newCost = 1783, newVersionedResults = { - val res = (ExpectedResult(Success(Helpers.decodeBytes("0008d3")), Some(1783)) -> Some(costDetails(0))) - Seq(0, 1, 2).map(version => version -> res) + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2055) + } + else { + Seq.fill(4)(1783) + } + Seq(0, 1, 2, 3).map({ version => + val res = (ExpectedResult(Success(Helpers.decodeBytes("0008d3")), Some(costs(version))) -> Some(costDetails(0))) + version -> res + }) }), (Helpers.decodeBytes("000008d3"), 0) -> Expected( @@ -9688,21 +9497,30 @@ class SigmaDslSpecification extends SigmaDslTesting expectedDetails = CostDetails.ZeroCost, newCost = 1783, newVersionedResults = { - // since the tree without constant segregation, substitution has no effect - val res = (ExpectedResult(Success(Helpers.decodeBytes("000008d3")), Some(1783)) -> Some(costDetails(0))) - Seq(0, 1, 2).map(version => version -> res) + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2055) + } + else { + Seq.fill(4)(1783) + } + Seq(0, 1, 2, 3).map({ version => + // since the tree without constant segregation, substitution has no effect + val res = (ExpectedResult(Success(Helpers.decodeBytes("000008d3")), Some(costs(version))) -> Some(costDetails(0))) + version -> res + }) }), // tree with segregation flag, empty constants array - (Coll(t2.bytes:_*), 0) -> success(Helpers.decodeBytes("100008d3"), costDetails(0), 1783), - (Helpers.decodeBytes("100008d3"), 0) -> success(Helpers.decodeBytes("100008d3"), costDetails(0), 1783), + (Coll(t2.bytes: _*), 0) -> success(Helpers.decodeBytes("100008d3"), costDetails(0), 1783, Seq.fill(4)(2055)), + (Helpers.decodeBytes("100008d3"), 0) -> success(Helpers.decodeBytes("100008d3"), costDetails(0), 1783, Seq.fill(4)(2055)), // tree with one segregated constant - (Coll(t3.bytes:_*), 0) -> success(Helpers.decodeBytes("100108d27300"), costDetails(1), 1793), - (Helpers.decodeBytes("100108d37300"), 0) -> success(Helpers.decodeBytes("100108d27300"), costDetails(1), 1793), - (Coll(t3.bytes:_*), 1) -> success(Helpers.decodeBytes("100108d37300"), costDetails(1), 1793), - (Coll(t4.bytes:_*), 0) -> Expected(new IllegalArgumentException("requirement failed: expected new constant to have the same SInt$ tpe, got SSigmaProp")) + (Coll(t3.bytes: _*), 0) -> success(Helpers.decodeBytes("100108d27300"), costDetails(1), 1793, Seq.fill(4)(2065)), + (Helpers.decodeBytes("100108d37300"), 0) -> success(Helpers.decodeBytes("100108d27300"), costDetails(1), 1793, Seq.fill(4)(2065)), + (Coll(t3.bytes: _*), 1) -> success(Helpers.decodeBytes("100108d37300"), costDetails(1), 1793, Seq.fill(4)(2065)), + (Coll(t4.bytes: _*), 0) -> Expected(new IllegalArgumentException("requirement failed: expected new constant to have the same SInt$ tpe, got SSigmaProp")) ) }, changedFeature( + changedInVersion = VersionContext.JitActivationVersion, { (x: (Coll[Byte], Int)) => SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) }, @@ -9754,6 +9572,12 @@ class SigmaDslSpecification extends SigmaDslTesting if (lowerMethodCallsInTests) { val error = new RuntimeException("any exception") + val costs = if (activatedVersionInTests >= V6SoftForkVersion) { + Seq.fill(4)(2144) + } + else { + Seq.fill(4)(1776) + } verifyCases( Seq( ctx -> Expected( @@ -9761,107 +9585,110 @@ class SigmaDslSpecification extends SigmaDslTesting cost = 1776, expectedDetails = CostDetails.ZeroCost, newCost = 1776, - newVersionedResults = (0 to 2).map(i => i -> (ExpectedResult(Success(true), Some(1776)) -> Some(costDetails))) + newVersionedResults = (0 to 3).map({ i => + i -> (ExpectedResult(Success(true), Some(costs(i))) -> Some(costDetails)) + }) ) ), changedFeature( - { (x: Context) => - throw error - true - }, - { (x: Context) => - val headers = x.headers - val ids = headers.map({ (h: Header) => h.id }) - val parentIds = headers.map({ (h: Header) => h.parentId }) - headers.indices.slice(0, headers.size - 1).forall({ (i: Int) => - val parentId = parentIds(i) - val id = ids(i + 1) - parentId == id - }) - }, - """{ - |(x: Context) => - | val headers = x.headers - | val ids = headers.map({(h: Header) => h.id }) - | val parentIds = headers.map({(h: Header) => h.parentId }) - | headers.indices.slice(0, headers.size - 1).forall({ (i: Int) => - | val parentId = parentIds(i) - | val id = ids(i + 1) - | parentId == id - | }) - |}""".stripMargin, - FuncValue( - Array((1, SContext)), - BlockValue( - Array( - ValDef( - 3, - List(), - MethodCall.typed[Value[SCollection[SHeader.type]]]( - ValUse(1, SContext), - SContextMethods.getMethodByName("headers"), - Vector(), - Map() - ) - ) - ), - ForAll( - Slice( - MethodCall.typed[Value[SCollection[SInt.type]]]( - ValUse(3, SCollectionType(SHeader)), - SCollectionMethods.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SHeader)), - Vector(), - Map() - ), - IntConstant(0), - ArithOp( - SizeOf(ValUse(3, SCollectionType(SHeader))), - IntConstant(1), - OpCode @@ (-103.toByte) + changedInVersion = VersionContext.JitActivationVersion, + { (x: Context) => + throw error + true + }, + { (x: Context) => + val headers = x.headers + val ids = headers.map({ (h: Header) => h.id }) + val parentIds = headers.map({ (h: Header) => h.parentId }) + headers.indices.slice(0, headers.size - 1).forall({ (i: Int) => + val parentId = parentIds(i) + val id = ids(i + 1) + parentId == id + }) + }, + """{ + |(x: Context) => + | val headers = x.headers + | val ids = headers.map({(h: Header) => h.id }) + | val parentIds = headers.map({(h: Header) => h.parentId }) + | headers.indices.slice(0, headers.size - 1).forall({ (i: Int) => + | val parentId = parentIds(i) + | val id = ids(i + 1) + | parentId == id + | }) + |}""".stripMargin, + FuncValue( + Array((1, SContext)), + BlockValue( + Array( + ValDef( + 3, + List(), + MethodCall.typed[Value[SCollection[SHeader.type]]]( + ValUse(1, SContext), + SContextMethods.getMethodByName("headers"), + Vector(), + Map() + ) ) ), - FuncValue( - Array((4, SInt)), - EQ( - ByIndex( - MapCollection( - ValUse(3, SCollectionType(SHeader)), - FuncValue( - Array((6, SHeader)), - MethodCall.typed[Value[SCollection[SByte.type]]]( - ValUse(6, SHeader), - SHeaderMethods.getMethodByName("parentId"), - Vector(), - Map() - ) - ) - ), - ValUse(4, SInt), - None + ForAll( + Slice( + MethodCall.typed[Value[SCollection[SInt.type]]]( + ValUse(3, SCollectionType(SHeader)), + SCollectionMethods.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SHeader)), + Vector(), + Map() ), - ByIndex( - MapCollection( - ValUse(3, SCollectionType(SHeader)), - FuncValue( - Array((6, SHeader)), - MethodCall.typed[Value[SCollection[SByte.type]]]( - ValUse(6, SHeader), - SHeaderMethods.getMethodByName("id"), - Vector(), - Map() + IntConstant(0), + ArithOp( + SizeOf(ValUse(3, SCollectionType(SHeader))), + IntConstant(1), + OpCode @@ (-103.toByte) + ) + ), + FuncValue( + Array((4, SInt)), + EQ( + ByIndex( + MapCollection( + ValUse(3, SCollectionType(SHeader)), + FuncValue( + Array((6, SHeader)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(6, SHeader), + SHeaderMethods.getMethodByName("parentId"), + Vector(), + Map() + ) ) - ) + ), + ValUse(4, SInt), + None ), - ArithOp(ValUse(4, SInt), IntConstant(1), OpCode @@ (-102.toByte)), - None + ByIndex( + MapCollection( + ValUse(3, SCollectionType(SHeader)), + FuncValue( + Array((6, SHeader)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(6, SHeader), + SHeaderMethods.getMethodByName("id"), + Vector(), + Map() + ) + ) + ), + ArithOp(ValUse(4, SInt), IntConstant(1), OpCode @@ (-102.toByte)), + None + ) ) ) ) ) - ) - ), - allowDifferentErrors = true, - allowNewToSucceed = true + ), + allowDifferentErrors = true, + allowNewToSucceed = true ), preGeneratedSamples = Some(ArraySeq.empty) ) @@ -9872,8 +9699,8 @@ class SigmaDslSpecification extends SigmaDslTesting property("nested loops: map inside fold") { val keys = Colls.fromArray(Array(Coll[Byte](1, 2, 3, 4, 5))) val initial = Coll[Byte](0, 0, 0, 0, 0) - val cases = Seq( - (keys, initial) -> Expected(Success(Coll[Byte](1, 2, 3, 4, 5)), cost = 1801, expectedDetails = CostDetails.ZeroCost, 1801) + val cases = Seq( + (keys, initial) -> Expected(Success(Coll[Byte](1, 2, 3, 4, 5)), cost = 1801, expectedDetails = CostDetails.ZeroCost, 1801, Seq.fill(4)(2119)) ) val scalaFunc = { (x: (Coll[Coll[Byte]], Coll[Byte])) => x._1.foldLeft(x._2, { (a: (Coll[Byte], Coll[Byte])) => @@ -9999,30 +9826,6 @@ class SigmaDslSpecification extends SigmaDslTesting } } - property("higher order lambdas") { - val f = existingFeature( - { (xs: Coll[Int]) => - val inc = { (x: Int) => x + 1 } - - def apply(in: (Int => Int, Int)) = in._1(in._2) - - xs.map { (x: Int) => apply((inc, x)) } - }, - """{(xs: Coll[Int]) => - | val inc = { (x: Int) => x + 1 } - | def apply(in: (Int => Int, Int)) = in._1(in._2) - | xs.map { (x: Int) => apply((inc, x)) } - | } - |""".stripMargin - ) - - // TODO v6.0: Add support of SFunc in TypeSerializer (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/847) - assertExceptionThrown( - f.verifyCase(Coll[Int](), Expected(Success(Coll[Int]()), 0)), - exceptionLike[MatchError]("(SInt$) => SInt$ (of class sigma.ast.SFunc)") - ) - } - override protected def afterAll(): Unit = { printDebug(CErgoTreeEvaluator.DefaultProfiler.generateReport) printDebug("==========================================================") diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala new file mode 100644 index 0000000000..29b78d0e97 --- /dev/null +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -0,0 +1,2861 @@ +package sigma + +import org.ergoplatform.{ErgoBox, ErgoHeader, ErgoLikeTransaction, Input} +import scorex.util.encode.Base16 +import sigma.VersionContext.V6SoftForkVersion +import org.ergoplatform.ErgoBox.Token +import org.ergoplatform.settings.ErgoAlgos +import scorex.util.ModifierId +import scorex.utils.{Ints, Longs, Shorts} +import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} +import sigma.ast.SCollection.SByteArray +import sigma.ast.SType.tT +import sigma.ast.syntax.TrueSigmaProp +import sigma.ast.{SInt, _} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CHeader, CSigmaProp, ExactNumeric, ProveDHTuple, RType} +import sigma.data.CSigmaDslBuilder +import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CGroupElement, CHeader, CSigmaDslBuilder, CSigmaProp, CUnsignedBigInt, ExactNumeric, PairOfCols, ProveDHTuple, RType} +import sigma.crypto.SecP256K1Group +import sigma.data.{CBigInt, CBox, CGroupElement, CHeader, CSigmaDslBuilder, ExactNumeric, RType} +import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, PairOfCols, RType} +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 sigma.util.Extensions.{BooleanOps, IntOps} +import sigma.data.RType +import sigma.serialization.ValueCodes.OpCode +import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} +import sigma.pow.Autolykos2PowValidation +import sigmastate.exceptions.MethodNotFound +import sigmastate.utils.Extensions.ByteOpsForSigma +import sigmastate.utils.Helpers +import sigma.Extensions.ArrayOps +import sigma.Extensions.{ArrayOps, CollOps} +import sigma.crypto.CryptoConstants +import sigma.interpreter.{ContextExtension, ProverResult} + +import java.math.BigInteger +import scala.util.{Failure, Success} + +/** This suite tests all operations for v6.0 version of the language. + * The base classes establish the infrastructure for the tests. + * + * @see SigmaDslSpecificationBase + */ +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) + } + + property("Boolean.toByte") { + val toByte = newFeature((x: Boolean) => x.toByte, "{ (x: Boolean) => x.toByte }", + sinceVersion = V6SoftForkVersion + ) + + val cases = Seq( + (true, Success(1.toByte)), + (false, Success(0.toByte)) + ) + + if (toByte.isSupportedIn(VersionContext.current)) { + // TODO v6.0: implement as part of https://github.com/ScorexFoundation/sigmastate-interpreter/pull/932 + assertExceptionThrown( + testCases(cases, toByte), + rootCauseLike[MethodNotFound]("Cannot find method") + ) + } + else + testCases(cases, toByte) + } + + property("Byte methods - 6.0 features") { + + lazy val bitOr = newFeature( + { (x: (Byte, Byte)) => (x._1 | x._2).toByteExact }, + "{ (x: (Byte, Byte)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SByte, SByte))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (1.toByte, 2.toByte) -> new Expected(ExpectedResult(Success(3.toByte), None)) + ), + bitOr + ) + + lazy val bitNot = newFeature( + { (x: Byte) => (~x).toByteExact }, + "{ (x: Byte) => x.bitwiseInverse }", + FuncValue( + Array((1, SByte)), + MethodCall.typed[Value[SByte.type]]( + ValUse(1, SByte), + SByteMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 1.toByte -> new Expected(ExpectedResult(Success((-2).toByte), None)) + ), + bitNot + ) + + lazy val bitAnd = newFeature( + { (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }, + "{ (x: (Byte, Byte)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SByte, SByte))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toByte, 5.toByte) -> new Expected(ExpectedResult(Success(1.toByte), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (Byte, Byte)) => (x._1 ^ x._2).toByteExact }, + "{ (x: (Byte, Byte)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SByte, SByte))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toByte, 5.toByte) -> new Expected(ExpectedResult(Success(6.toByte), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature( + { x: Byte => Coll(x) }, + "{ (x: Byte) => x.toBytes }", + FuncValue( + Array((1, SByte)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SByte), + SByteMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 127.toByte -> new Expected(ExpectedResult(Success(Coll(127.toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[Byte, Coll[Boolean]]( + { x: Byte => Colls.fromArray(byte2Bools(x).toArray) }, + "{ (x: Byte) => x.toBits }", + FuncValue( + Array((1, SByte)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SByte), + SByteMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 83.toByte -> new Expected(ExpectedResult(Success(Coll(false, true, false, true, false, false, true, true)), None)), + -55.toByte -> new Expected(ExpectedResult(Success(Coll(true, true, false, false, true, false, false, true)), None)), + -1.toByte -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true)), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (Byte, Int)) => if(x._2 < 0 || x._2 >= 8) throw new IllegalArgumentException() else (x._1 << x._2).toByte }, + "{ (x: (Byte, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SByte, SInt))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SInt)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByte, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toByte, 3) -> new Expected(ExpectedResult(Success(24.toByte), None)), + (3.toByte, 0) -> new Expected(ExpectedResult(Success(3.toByte), None)), + (3.toByte, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (3.toByte, 8) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftLeft, + preGeneratedSamples = Some(Seq()) + ) + + lazy val shiftRight = newFeature( + { (x: (Byte, Int)) => if(x._2 < 0 || x._2 >= 8) throw new IllegalArgumentException() else (x._1 >> x._2).toByte }, + "{ (x: (Byte, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SByte, SInt))), + MethodCall.typed[Value[SByte.type]]( + SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SInt)), 1.toByte), + SByteMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByte, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (24.toByte, 3) -> new Expected(ExpectedResult(Success(3.toByte), None)), + (24.toByte, 0) -> new Expected(ExpectedResult(Success(24.toByte), None)), + (24.toByte, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (24.toByte, 8) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) + } + + property("Short - 6.0 methods") { + + lazy val bitOr = newFeature( + { (x: (Short, Short)) => (x._1 | x._2).toShortExact }, + "{ (x: (Short, Short)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SShort, SShort))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1,SPair(SShort, SShort)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (1.toShort, 2.toShort) -> new Expected(ExpectedResult(Success(3.toShort), None)), + (1001.toShort, 2002.toShort) -> new Expected(ExpectedResult(Success(2043.toShort), None)) + ), + bitOr + ) + + lazy val bitNot = newFeature( + { (x: Short) => (~x).toShortExact }, + "{ (x: Short) => x.bitwiseInverse }", + FuncValue( + Array((1, SShort)), + MethodCall.typed[Value[SShort.type]]( + ValUse(1, SShort), + SShortMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 1.toShort -> new Expected(ExpectedResult(Success((-2).toShort), None)), + 10001.toShort -> new Expected(ExpectedResult(Success((-10002).toShort), None)) + ), + bitNot + ) + + lazy val bitAnd = newFeature( + { (x: (Short, Short)) => (x._1 & x._2).toShortExact }, + "{ (x: (Short, Short)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SShort, SShort))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toShort, 5.toShort) -> new Expected(ExpectedResult(Success(1.toShort), None)), + (10001.toShort, 2202.toShort) -> new Expected(ExpectedResult(Success(16.toShort), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (Short, Short)) => (x._1 ^ x._2).toShortExact }, + "{ (x: (Short, Short)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SShort, SShort))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SShort)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toShort, 5.toShort) -> new Expected(ExpectedResult(Success(6.toShort), None)), + (10001.toShort, 2202.toShort) -> new Expected(ExpectedResult(Success(12171.toShort), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature[Short, Coll[Byte]]( + { x: Short => Colls.fromArray(Shorts.toByteArray(x)) }, + "{ (x: Short) => x.toBytes }", + FuncValue( + Array((1, SShort)), + MethodCall.typed[Value[SCollection[SShort.type]]]( + ValUse(1, SShort), + SShortMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 127.toShort -> new Expected(ExpectedResult(Success(Coll(0.toByte, 127.toByte)), None)), + Short.MaxValue -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte)), None)), + Short.MinValue -> new Expected(ExpectedResult(Success(Coll((-128).toByte, 0.toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[Short, Coll[Boolean]]( + { x: Short => Colls.fromArray(Shorts.toByteArray(x)).flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, + "{ (x: Short) => x.toBits }", + FuncValue( + Array((1, SShort)), + MethodCall.typed[Value[SCollection[SShort.type]]]( + ValUse(1, SShort), + SShortMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 83.toShort -> new Expected(ExpectedResult(Success(Coll(false, false, false, false, false, false, false, false, false, true, false, true, false, false, true, true)), None)), + -55.toShort -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, false, false, true, false, false, true)), None)), + -1.toShort-> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true)), None)), + -10001.toShort-> new Expected(ExpectedResult(Success(Coll(true, true, false, true, true, false, false, false, true, true, true, false, true, true, true, true)), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (Short, Int)) => if(x._2 < 0 || x._2 >= 16) throw new IllegalArgumentException() else (x._1 << x._2).toShort }, + "{ (x: (Short, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SShort, SInt))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SInt)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SShort, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3.toShort, 3) -> new Expected(ExpectedResult(Success(24.toShort), None)), + (3.toShort, 8) -> new Expected(ExpectedResult(Success(768.toShort), None)), + ((-2).toShort, 10) -> new Expected(ExpectedResult(Success((-2048).toShort), None)), + ((-2).toShort, 20) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (3.toShort, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftLeft, + preGeneratedSamples = Some(Seq()) + ) + + lazy val shiftRight = newFeature( + { (x: (Short, Int)) => if(x._2 < 0 || x._2 >= 16) throw new IllegalArgumentException() else (x._1 >> x._2).toShort }, + "{ (x: (Short, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SShort, SInt))), + MethodCall.typed[Value[SShort.type]]( + SelectField.typed[Value[SShort.type]](ValUse(1, SPair(SShort, SInt)), 1.toByte), + SShortMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SShort, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (24.toShort, 3) -> new Expected(ExpectedResult(Success(3.toShort), None)), + (1600.toShort, 8) -> new Expected(ExpectedResult(Success(6.toShort), None)), + ((-3200).toShort, 8) -> new Expected(ExpectedResult(Success((-13).toShort), None)), + (3.toShort, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (3.toShort, 16) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) + } + + property("Int - 6.0 methods") { + + lazy val bitOr = newFeature( + { (x: (Int, Int)) => (x._1 | x._2)}, + "{ (x: (Int, Int)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1,SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (1, 2) -> new Expected(ExpectedResult(Success(3), None)), + (1001, 2002) -> new Expected(ExpectedResult(Success(2043), None)), + (100001, 20002) -> new Expected(ExpectedResult(Success(118435), None)) + ), + bitOr + ) + + lazy val bitNot = newFeature( + { (x: Int) => ~x }, + "{ (x: Int) => x.bitwiseInverse }", + FuncValue( + Array((1, SInt)), + MethodCall.typed[Value[SInt.type]]( + ValUse(1, SInt), + SIntMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 1 -> new Expected(ExpectedResult(Success(-2), None)), + 10001 -> new Expected(ExpectedResult(Success(-10002), None)), + Int.MinValue -> new Expected(ExpectedResult(Success(Int.MaxValue), None)) + ), + bitNot + ) + + lazy val bitAnd = newFeature( + { (x: (Int, Int)) => x._1 & x._2 }, + "{ (x: (Int, Int)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3, 5) -> new Expected(ExpectedResult(Success(1), None)), + (10001, 2202) -> new Expected(ExpectedResult(Success(16), None)), + (-10001, 200202) -> new Expected(ExpectedResult(Success(198666), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (Int, Int)) => (x._1 ^ x._2) }, + "{ (x: (Int, Int)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3, 5) -> new Expected(ExpectedResult(Success(6), None)), + (10001, 2202) -> new Expected(ExpectedResult(Success(12171), None)), + (-10001, 200202) -> new Expected(ExpectedResult(Success(-207131), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature[Int, Coll[Byte]]( + { x: Int => Colls.fromArray(Ints.toByteArray(x)) }, + "{ (x: Int) => x.toBytes }", + FuncValue( + Array((1, SInt)), + MethodCall.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SInt), + SIntMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 127 -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 0.toByte, 127.toByte)), None)), + Short.MaxValue.toInt -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 127.toByte, (-1).toByte)), None)), + Short.MinValue.toInt -> new Expected(ExpectedResult(Success(Coll((-1).toByte, (-1).toByte, (-128).toByte, 0.toByte)), None)), + Int.MaxValue.toInt -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte, (-1).toByte, (-1).toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[Int, Coll[Boolean]]( + { x: Int => Colls.fromArray(Ints.toByteArray(x)).flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, + "{ (x: Int) => x.toBits }", + FuncValue( + Array((1, SInt)), + MethodCall.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SInt), + SIntMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 83 -> new Expected(ExpectedResult(Success(Coll(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, true, true)), None)), + -55 -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, true, false, false, true)), None)), + -1 -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true)), None)), + -10001 -> new Expected(ExpectedResult(Success(Coll(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, true, false, false, false, true, true, true, false, true, true, true, true)), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (Int, Int)) => if(x._2 < 0 || x._2 >= 32) throw new IllegalArgumentException() else (x._1 << x._2) }, + "{ (x: (Int, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3, 3) -> new Expected(ExpectedResult(Success(24), None)), + (3, 8) -> new Expected(ExpectedResult(Success(768), None)), + (-2, 10) -> new Expected(ExpectedResult(Success(-2048), None)), + (-222, 10) -> new Expected(ExpectedResult(Success(-227328), None)), + (-222, 32) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (-222, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftLeft, + preGeneratedSamples = Some(Seq()) + ) + + lazy val shiftRight = newFeature( + { (x: (Int, Int)) => if(x._2 < 0 || x._2 >= 32) throw new IllegalArgumentException() else (x._1 >> x._2) }, + "{ (x: (Int, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SInt, SInt))), + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte), + SIntMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (24, 3) -> new Expected(ExpectedResult(Success(3), None)), + (1600, 8) -> new Expected(ExpectedResult(Success(6), None)), + (-3200, 8) -> new Expected(ExpectedResult(Success(-13), None)), + (-320019, 18) -> new Expected(ExpectedResult(Success(-2), None)), + (-320019, 32) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (-320019, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) + } + + property("Long - 6.0 methods") { + + lazy val bitOr = newFeature( + { (x: (Long, Long)) => (x._1 | x._2)}, + "{ (x: (Long, Long)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SLong, SLong))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1,SPair(SLong, SLong)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (1L, 2L) -> new Expected(ExpectedResult(Success(3L), None)), + (1001L, 2002L) -> new Expected(ExpectedResult(Success(2043L), None)), + (100001L, 20002L) -> new Expected(ExpectedResult(Success(118435L), None)), + (1000010111L, -22L) -> new Expected(ExpectedResult(Success(-1L), None)) + ), + bitOr + ) + + lazy val bitNot = newFeature( + { (x: Long) => ~x }, + "{ (x: Long) => x.bitwiseInverse }", + FuncValue( + Array((1, SLong)), + MethodCall.typed[Value[SLong.type]]( + ValUse(1, SLong), + SLongMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 1L -> new Expected(ExpectedResult(Success(-2L), None)), + 10001L -> new Expected(ExpectedResult(Success(-10002L), None)), + Int.MinValue.toLong -> new Expected(ExpectedResult(Success(Int.MaxValue.toLong), None)), + Long.MinValue -> new Expected(ExpectedResult(Success(Long.MaxValue), None)), + Long.MaxValue -> new Expected(ExpectedResult(Success(Long.MinValue), None)) + ), + bitNot + ) + + lazy val bitAnd = newFeature( + { (x: (Long, Long)) => x._1 & x._2 }, + "{ (x: (Long, Long)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SLong, SLong))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3L, 5L) -> new Expected(ExpectedResult(Success(1L), None)), + (10001L, 2202L) -> new Expected(ExpectedResult(Success(16L), None)), + (-10001L, 200202L) -> new Expected(ExpectedResult(Success(198666L), None)), + (1000010111L, -22L) -> new Expected(ExpectedResult(Success(1000010090L), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (Long, Long)) => (x._1 ^ x._2) }, + "{ (x: (Long, Long)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SLong, SLong))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SLong)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3L, 5L) -> new Expected(ExpectedResult(Success(6L), None)), + (10001L, 2202L) -> new Expected(ExpectedResult(Success(12171L), None)), + (-10001L, 200202L) -> new Expected(ExpectedResult(Success(-207131L), None)), + (1000010111L, -22L) -> new Expected(ExpectedResult(Success(-1000010091L), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature[Long, Coll[Byte]]( + { x: Long => Colls.fromArray(Longs.toByteArray(x)) }, + "{ (x: Long) => x.toBytes }", + FuncValue( + Array((1, SLong)), + MethodCall.typed[Value[SCollection[SLong.type]]]( + ValUse(1, SLong), + SLongMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 127L -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 127.toByte)), None)), + Short.MaxValue.toLong -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 0.toByte, 127.toByte, (-1).toByte)), None)), + Short.MinValue.toLong -> new Expected(ExpectedResult(Success(Coll((-1).toByte, (-1).toByte, (-1).toByte, (-1).toByte, (-1).toByte, (-1).toByte, (-128).toByte, 0.toByte)), None)), + Int.MaxValue.toLong -> new Expected(ExpectedResult(Success(Coll(0.toByte, 0.toByte, 0.toByte, 0.toByte, 127.toByte, (-1).toByte, (-1).toByte, (-1).toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[Long, Coll[Boolean]]( + { x: Long => Colls.fromArray(Longs.toByteArray(x)).flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, + "{ (x: Long) => x.toBits }", + FuncValue( + Array((1, SLong)), + MethodCall.typed[Value[SCollection[SLong.type]]]( + ValUse(1, SLong), + SLongMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + 83L -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(57)(false)).append(Coll(true, false, true, false, false, true, true))), None)), + -55L -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(58)(true)).append(Coll(false, false, true, false, false, true))), None)), + -1L -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(64)(true))), None)), + -10001L -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(50)(true)).append(Coll( false, true, true, false, false, false, true, true, true, false, true, true, true, true))), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (Long, Int)) => if(x._2 < 0 || x._2 >= 32) throw new IllegalArgumentException() else (x._1 << x._2) }, + "{ (x: (Long, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SLong, SInt))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SInt)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SLong, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (3L, 3) -> new Expected(ExpectedResult(Success(24L), None)), + (3L, 8) -> new Expected(ExpectedResult(Success(768L), None)), + (-2L, 10) -> new Expected(ExpectedResult(Success(-2048L), None)), + (-222L, 10) -> new Expected(ExpectedResult(Success(-227328L), None)), + (-222L, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (-222L, 64) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftLeft, + preGeneratedSamples = Some(Seq()) + ) + + lazy val shiftRight = newFeature( + { (x: (Long, Int)) => if(x._2 < 0 || x._2 >= 64) throw new IllegalArgumentException() else (x._1 >> x._2) }, + "{ (x: (Long, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SLong, SInt))), + MethodCall.typed[Value[SLong.type]]( + SelectField.typed[Value[SLong.type]](ValUse(1, SPair(SLong, SInt)), 1.toByte), + SLongMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SLong, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (24L, 3) -> new Expected(ExpectedResult(Success(3L), None)), + (1600L, 8) -> new Expected(ExpectedResult(Success(6L), None)), + (-3200L, 8) -> new Expected(ExpectedResult(Success(-13L), None)), + (-320019L, 18) -> new Expected(ExpectedResult(Success(-2L), None)), + (-320019L, 63) -> new Expected(ExpectedResult(Success(-1L), None)), + (24L, -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) + } + + property("BigInt - 6.0 features") { + import sigma.data.OrderingOps.BigIntOrdering + + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + // The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. + // Fixed in 6.0 + assertExceptionThrown( + SBigInt.upcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot upcast value") + ) + + // The `Downcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. + // Fixed in 6.0 + assertExceptionThrown( + SBigInt.downcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + + forAll { x: Long => + assertExceptionThrown( + SLong.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + forAll { x: Int => + assertExceptionThrown( + SInt.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + forAll { x: Byte => + assertExceptionThrown( + SByte.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + forAll { x: Short => + assertExceptionThrown( + SShort.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + } else { + forAll { x: BigInteger => + SBigInt.upcast(CBigInt(x).asInstanceOf[AnyVal]) shouldBe CBigInt(x) + SBigInt.downcast(CBigInt(x).asInstanceOf[AnyVal]) shouldBe CBigInt(x) + } + forAll { x: Long => + SLong.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + forAll { x: Int => + SInt.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + forAll { x: Byte => + SByte.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + forAll { x: Short => + SShort.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + } + + lazy val bitOr = newFeature[(BigInt, BigInt), BigInt]( + { (x: (BigInt, BigInt)) => (x._1 | x._2)}, + "{ (x: (BigInt, BigInt)) => x._1.bitwiseOr(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SBigInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1,SPair(SBigInt, SBigInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "bitwiseOr").get, + Vector(SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(1)), CBigInt(BigInteger.valueOf(2))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(3))), None)), + (CBigInt(BigInteger.valueOf(1001)), CBigInt(BigInteger.valueOf(2002))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(2043))), None)), + (CBigInt(BigInteger.valueOf(100001)), CBigInt(BigInteger.valueOf(20002))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(118435))), None)), + (CBigInt(BigInteger.valueOf(1000010111)), CBigInt(BigInteger.valueOf(-22))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-1))), None)) + ), + bitOr + ) + + lazy val bitNot = newFeature[BigInt, BigInt]( + { (x: BigInt) => CBigInt(x.asInstanceOf[CBigInt].wrappedValue.not()) }, + "{ (x: BigInt) => x.bitwiseInverse }", + FuncValue( + Array((1, SBigInt)), + MethodCall.typed[Value[SBigInt.type]]( + ValUse(1, SBigInt), + SBigIntMethods.v6Methods.find(_.name == "bitwiseInverse").get, + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + CBigInt(BigInteger.valueOf(1)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-2))), None)), + CBigInt(BigInteger.valueOf(10001)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-10002))), None)), + CBigInt(BigInteger.valueOf(Int.MinValue)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(Int.MaxValue))), None)), + CBigInt(BigInteger.valueOf(Long.MinValue)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(Long.MaxValue))), None)), + CBigInt(BigInteger.valueOf(Long.MaxValue)) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(Long.MinValue))), None)) + ), + bitNot + ) + + lazy val bitAnd = newFeature( + { (x: (BigInt, BigInt)) => x._1.asInstanceOf[CBigInt].and(x._2.asInstanceOf[CBigInt]) }, + "{ (x: (BigInt, BigInt)) => x._1.bitwiseAnd(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SBigInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "bitwiseAnd").get, + Vector(SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(3)), CBigInt(BigInteger.valueOf(5))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(1))), None)), + (CBigInt(BigInteger.valueOf(10001)), CBigInt(BigInteger.valueOf(2202))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(16))), None)), + (CBigInt(BigInteger.valueOf(-10001)), CBigInt(BigInteger.valueOf(200202))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(198666))), None)), + (CBigInt(BigInteger.valueOf(1000010111)), CBigInt(BigInteger.valueOf(-22))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(1000010090))), None)) + ), + bitAnd + ) + + lazy val bitXor = newFeature( + { (x: (BigInt, BigInt)) => x._1.asInstanceOf[CBigInt].xor(x._2.asInstanceOf[CBigInt]) }, + "{ (x: (BigInt, BigInt)) => x._1.bitwiseXor(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SBigInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "bitwiseXor").get, + Vector(SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SBigInt)),2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(3)), CBigInt(BigInteger.valueOf(5))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(6))), None)), + (CBigInt(BigInteger.valueOf(10001)), CBigInt(BigInteger.valueOf(2202))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(12171))), None)), + (CBigInt(BigInteger.valueOf(-10001)), CBigInt(BigInteger.valueOf(200202))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-207131))), None)), + (CBigInt(BigInteger.valueOf(1000010111)), CBigInt(BigInteger.valueOf(-22))) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-1000010091))), None)) + ), + bitXor + ) + + lazy val toBigEndianBytes = newFeature[BigInt, Coll[Byte]]( + { x: BigInt => x.toBytes }, + "{ (x: BigInt) => x.toBytes }", + FuncValue( + Array((1, SBigInt)), + MethodCall.typed[Value[SCollection[SBigInt.type]]]( + ValUse(1, SBigInt), + SBigIntMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + CBigInt(BigInteger.valueOf(127)) -> new Expected(ExpectedResult(Success(Coll(127.toByte)), None)), + CBigInt(BigInteger.valueOf(Short.MaxValue)) -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte)), None)), + CBigInt(BigInteger.valueOf(Short.MinValue)) -> new Expected(ExpectedResult(Success(Coll((-128).toByte, 0.toByte)), None)), + CBigInt(BigInteger.valueOf(Int.MaxValue)) -> new Expected(ExpectedResult(Success(Coll(127.toByte, (-1).toByte, (-1).toByte, (-1).toByte)), None)) + ), + toBigEndianBytes + ) + + def byte2Bools(b: Byte): Seq[Boolean] = + (0 to 7 map isBitSet(b)).reverse + + def isBitSet(byte: Byte)(bit: Int): Boolean = + ((byte >> bit) & 1) == 1 + + lazy val toBits = newFeature[BigInt, Coll[Boolean]]( + { x: BigInt => x.toBytes.flatMap(b => Colls.fromArray(byte2Bools(b).toArray)) }, + "{ (x: BigInt) => x.toBits }", + FuncValue( + Array((1, SBigInt)), + MethodCall.typed[Value[SCollection[SBigInt.type]]]( + ValUse(1, SBigInt), + SBigIntMethods.getMethodByName("toBits"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + CBigInt(BigInteger.valueOf(83)) -> new Expected(ExpectedResult(Success(Coll(false, true, false, true, false, false, true, true)), None)), + CBigInt(BigInteger.valueOf(-55)) -> new Expected(ExpectedResult(Success(Coll(true, true, false, false, true, false, false, true)), None)), + CBigInt(BigInteger.valueOf(-1L)) -> new Expected(ExpectedResult(Success(Colls.fromArray(Array.fill(8)(true))), None)), + CBigInt(BigInteger.valueOf(-10001L)) -> new Expected(ExpectedResult(Success(Coll(true,true,false,true,true,false,false,false,true,true,true,false,true,true,true,true)), None)) + ), + toBits + ) + + lazy val shiftLeft = newFeature( + { (x: (BigInt, Int)) => if(x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[BigInt].shiftLeft(x._2)) }, + "{ (x: (BigInt, Int)) => x._1.shiftLeft(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "shiftLeft").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SBigInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(3)), 3) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(24))), None)), + (CBigInt(BigInteger.valueOf(3)), 8) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(768))), None)), + (CBigInt(BigInteger.valueOf(-2)), 10) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-2048))), None)), + (CBigInt(BigInteger.valueOf(-222)), 10) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-227328L))), None)), + (CBigInt(BigInteger.valueOf(-222)), -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (CBigInt(BigInteger.valueOf(-222)), 256) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftLeft, + preGeneratedSamples = Some(Seq()) + ) + + lazy val shiftRight = newFeature( + { (x: (BigInt, Int)) => if(x._2 < 0 || x._2 >= 256) throw new IllegalArgumentException() else (x._1.asInstanceOf[BigInt].shiftRight(x._2)) }, + "{ (x: (BigInt, Int)) => x._1.shiftRight(x._2) }", + FuncValue( + Array((1, SPair(SBigInt, SInt))), + MethodCall.typed[Value[SBigInt.type]]( + SelectField.typed[Value[SBigInt.type]](ValUse(1, SPair(SBigInt, SInt)), 1.toByte), + SBigIntMethods.v6Methods.find(_.name == "shiftRight").get, + Vector(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SBigInt, SInt)), 2.toByte)), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + + verifyCases( + Seq( + (CBigInt(BigInteger.valueOf(24)), 3) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(3))), None)), + (CBigInt(BigInteger.valueOf(1600)), 8) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(6))), None)), + (CBigInt(BigInteger.valueOf(-3200)), 8) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-13))), None)), + (CBigInt(BigInteger.valueOf(-320019)), 18) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-2))), None)), + (CBigInt(BigInteger.valueOf(-320019)), 63) -> new Expected(ExpectedResult(Success(CBigInt(BigInteger.valueOf(-1))), None)), + (CBigInt(BigInteger.valueOf(24)), -1) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)), + (CBigInt(BigInteger.valueOf(24)), 256) -> new Expected(ExpectedResult(Failure(new IllegalArgumentException()), None)) + ), + shiftRight, + preGeneratedSamples = Some(Seq()) + ) + } + + property("Box properties equivalence (new features)") { + // related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416 + def getReg = newFeature((x: Box) => x.getReg[Long](0).get, + "{ (x: Box) => x.getReg[Long](0).get }", + FuncValue( + Array((1, SBox)), + OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R0, SOption(SLong))) + ), + sinceVersion = V6SoftForkVersion) + + if (activatedVersionInTests < V6SoftForkVersion) { + // NOTE, for such versions getReg is not supported + // which is checked below + + forAll { box: Box => + Seq(getReg).foreach(_.checkEquality(box)) + } + } else { + val value = 10L + val box = CBox(new ErgoBox(value, TrueTree, Colls.emptyColl[Token], Map.empty, + ModifierId @@ Base16.encode(Array.fill(32)(0)), 0, 0)) + verifyCases( + Seq( + box -> new Expected(ExpectedResult(Success(value), None)) + ), + getReg + ) + } + } + + // TODO v6.0 (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + property("Coll find method equivalence") { + val find = newFeature((x: Coll[Int]) => x.find({ (v: Int) => v > 0 }), + "{ (x: Coll[Int]) => x.find({ (v: Int) => v > 0} ) }", + sinceVersion = V6SoftForkVersion) + + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + // NOTE, for such versions getReg is not supported + // which is checked below + + forAll { x: Coll[Int] => + find.checkEquality(x) + } + } + } + + // TODO v6.0 (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/418 + property("Coll bitwise methods equivalence") { + val shiftRight = newFeature( + { (x: Coll[Boolean]) => + if (x.size > 2) x.slice(0, x.size - 2) else Colls.emptyColl[Boolean] + }, + "{ (x: Coll[Boolean]) => x >> 2 }", + sinceVersion = V6SoftForkVersion) + + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + // NOTE, for such versions getReg is not supported + // which is checked below + + forAll { x: Array[Boolean] => + shiftRight.checkEquality(Colls.fromArray(x)) + } + } + } + + // TODO v6.0 (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + property("Coll diff methods equivalence") { + val diff = newFeature((x: (Coll[Int], Coll[Int])) => x._1.diff(x._2), + "{ (x: (Coll[Int], Coll[Int])) => x._1.diff(x._2) }", + sinceVersion = V6SoftForkVersion) + + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + // NOTE, for such versions getReg is not supported + // which is checked below + + forAll { (x: Coll[Int], y: Coll[Int]) => + diff.checkEquality((x, y)) + } + } + } + + // TODO v6.0: implement Option.fold (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479) + property("Option new methods") { + val n = ExactNumeric.LongIsExactNumeric + val fold = newFeature({ (x: Option[Long]) => x.fold(5.toLong)( (v: Long) => n.plus(v, 1) ) }, + "{ (x: Option[Long]) => x.fold(5, { (v: Long) => v + 1 }) }", + sinceVersion = V6SoftForkVersion) + + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + // NOTE, for such versions getReg is not supported + // which is checked below + + forAll { x: Option[Long] => + Seq(fold).map(_.checkEquality(x)) + } + } + } + + // TODO v6.0 (3h): implement allZK func https://github.com/ScorexFoundation/sigmastate-interpreter/issues/543 + property("allZK equivalence") { + lazy val allZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.allZK(x), + "{ (x: Coll[SigmaProp]) => allZK(x) }", + sinceVersion = V6SoftForkVersion) + + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + // NOTE, for such versions getReg is not supported + // which is checked below + + forAll { x: Coll[SigmaProp] => + allZK.checkEquality(x) + } + } + } + + // TODO v6.0 (3h): implement anyZK func https://github.com/ScorexFoundation/sigmastate-interpreter/issues/543 + property("anyZK equivalence") { + lazy val anyZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.anyZK(x), + "{ (x: Coll[SigmaProp]) => anyZK(x) }", + sinceVersion = V6SoftForkVersion) + + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + // NOTE, for such versions getReg is not supported + // which is checked below + + forAll { x: Coll[SigmaProp] => + anyZK.checkEquality(x) + } + } + } + + property("Numeric.toBytes methods equivalence") { + lazy val toBytes = newFeature( + { (x: Byte) => x.toBigEndianBytes }, + "{ (x: Byte) => x.toBytes }", + FuncValue( + Array((1, SByte)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SByte), + SByteMethods.getMethodByName("toBytes"), + Vector(), + Map() + ) + ), + sinceVersion = V6SoftForkVersion) + val cases = Seq( + (0.toByte, Success(Coll(0.toByte))), + (1.toByte, Success(Coll(1.toByte))) + ) + + testCases(cases, toBytes) + } + + property("Fix substConstants in v6.0 for ErgoTree version > 0") { + // tree with one segregated constant and v0 + val t1 = ErgoTree( + header = ErgoTree.setConstantSegregation(ZeroHeader), + constants = Vector(TrueSigmaProp), + ConstantPlaceholder(0, SSigmaProp)) + + // tree with one segregated constant and max supported version + val t2 = ErgoTree( + header = ErgoTree.setConstantSegregation( + ErgoTree.headerWithVersion(ZeroHeader, VersionContext.MaxSupportedScriptVersion) + ), + Vector(TrueSigmaProp), + ConstantPlaceholder(0, SSigmaProp)) + + def costDetails(nItems: Int) = TracedCost( + traceBase ++ Array( + FixedCostItem(SelectField), + FixedCostItem(ConcreteCollection), + FixedCostItem(ValUse), + FixedCostItem(SelectField), + FixedCostItem(ConcreteCollection), + FixedCostItem(Constant), + FixedCostItem(BoolToSigmaProp), + ast.SeqCostItem(CompanionDesc(SubstConstants), PerItemCost(JitCost(100), JitCost(100), 1), nItems) + ) + ) + val expectedTreeBytes_beforeV6 = Helpers.decodeBytes("1b0108d27300") + val expectedTreeBytes_V6 = Helpers.decodeBytes("1b050108d27300") + + verifyCases( + Seq( + // for tree v0, the result is the same for all versions + (Coll(t1.bytes: _*), 0) -> Expected( + Success(Helpers.decodeBytes("100108d27300")), + cost = 1793, + expectedDetails = CostDetails.ZeroCost, + newCost = 2065, + newVersionedResults = expectedSuccessForAllTreeVersions(Helpers.decodeBytes("100108d27300"), 2065, costDetails(1)) + ), + // for tree version > 0, the result depend on activated version + (Coll(t2.bytes: _*), 0) -> Expected( + Success(expectedTreeBytes_beforeV6), + cost = 1793, + expectedDetails = CostDetails.ZeroCost, + newCost = 2065, + newVersionedResults = expectedSuccessForAllTreeVersions(expectedTreeBytes_V6, 2065, costDetails(1))) + ), + changedFeature( + changedInVersion = VersionContext.V6SoftForkVersion, + { (x: (Coll[Byte], Int)) => + SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) + }, + { (x: (Coll[Byte], Int)) => + SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(sigma.AnyType)) + }, + "{ (x: (Coll[Byte], Int)) => substConstants[Any](x._1, Coll[Int](x._2), Coll[Any](sigmaProp(false))) }", + FuncValue( + Vector((1, SPair(SByteArray, SInt))), + SubstConstants( + SelectField.typed[Value[SCollection[SByte.type]]](ValUse(1, SPair(SByteArray, SInt)), 1.toByte), + ConcreteCollection( + Array(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByteArray, SInt)), 2.toByte)), + SInt + ), + ConcreteCollection(Array(BoolToSigmaProp(FalseLeaf)), SSigmaProp) + ) + ) + ) + ) + + // before v6.0 the expected tree is not parsable + ErgoTree.fromBytes(expectedTreeBytes_beforeV6.toArray).isRightParsed shouldBe false + + // in v6.0 the expected tree should be parsable and similar to the original tree + val tree = ErgoTree.fromBytes(expectedTreeBytes_V6.toArray) + tree.isRightParsed shouldBe true + tree.header shouldBe t2.header + tree.constants.length shouldBe t2.constants.length + tree.root shouldBe t2.root + } + + property("Header new methods") { + + def checkPoW = { + newFeature( + { (x: Header) => x.checkPow}, + "{ (x: Header) => x.checkPow }", + FuncValue( + Array((1, SHeader)), + MethodCall.typed[Value[SBoolean.type]]( + ValUse(1, SHeader), + SHeaderMethods.checkPowMethod, + IndexedSeq(), + Map() + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + // bytes of real mainnet block header at height 614,440 + val headerBytes = "02ac2101807f0000ca01ff0119db227f202201007f62000177a080005d440896d05d3f80dcff7f5e7f59007294c180808d0158d1ff6ba10000f901c7f0ef87dcfff17fffacb6ff7f7f1180d2ff7f1e24ffffe1ff937f807f0797b9ff6ebdae007e5c8c00b8403d3701557181c8df800001b6d5009e2201c6ff807d71808c00019780f087adb3fcdbc0b3441480887f80007f4b01cf7f013ff1ffff564a0000b9a54f00770e807f41ff88c00240000080c0250000000003bedaee069ff4829500b3c07c4d5fe6b3ea3d3bf76c5c28c1d4dcdb1bed0ade0c0000000000003105" + val header1 = new CHeader(ErgoHeader.sigmaSerializer.fromBytes(Base16.decode(headerBytes).get)) + + verifyCases( + Seq( + header1 -> new Expected(ExpectedResult(Success(true), None)) + ), + checkPoW + ) + } + + property("Global.powHit") { + def powHit: Feature[Coll[Byte], sigma.BigInt] = newFeature( + { (x: Coll[Byte]) => + val msg = x.slice(0, 7).toArray + val nonce = x.slice(7, 15).toArray + val h = x.slice(15, 19).toArray + CBigInt(Autolykos2PowValidation.hitForVersion2ForMessageWithChecks(32, msg, nonce, h, 1024 * 1024).bigInteger) + }, + "{ (x: Coll[Byte]) => val msg = x.slice(0,7); val nonce = x.slice(7,15); val h = x.slice(15,19); " + + "Global.powHit(32, msg, nonce, h, 1024 * 1024) }", + FuncValue( + Array((1, SByteArray)), + MethodCall.typed[Value[SBigInt.type]]( + Global, + SGlobalMethods.powHitMethod, + Array( + IntConstant(32), + Slice(ValUse(1, SByteArray), IntConstant(0), IntConstant(7)), + Slice(ValUse(1, SByteArray), IntConstant(7), IntConstant(15)), + Slice(ValUse(1, SByteArray), IntConstant(15), IntConstant(19)), + IntConstant(1048576) + ), + Map() + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion) + + // bytes of real mainnet block header at height 614,440 + val msg = Base16.decode("0a101b8c6a4f2e").get + val nonce = Base16.decode("000000000000002c").get + val h = Base16.decode("00000000").get + val x = Colls.fromArray(msg ++ nonce ++ h) + val hit = CBigInt(new BigInteger("326674862673836209462483453386286740270338859283019276168539876024851191344")) + + verifyCases( + Seq( + x -> new Expected(ExpectedResult(Success(hit), None)) + ), + powHit + ) + } + + property("higher order lambdas") { + val f = newFeature[Coll[Int], Coll[Int]]( + { (xs: Coll[Int]) => + val inc = { (x: Int) => x + 1 } + + def apply(in: (Int => Int, Int)) = in._1(in._2) + + xs.map { (x: Int) => apply((inc, x)) } + }, + """{(xs: Coll[Int]) => + | val inc = { (x: Int) => x + 1 } + | def apply(in: (Int => Int, Int)) = in._1(in._2) + | xs.map { (x: Int) => apply((inc, x)) } + | } + |""".stripMargin, + FuncValue( + Array((1, SCollectionType(SInt))), + MapCollection( + ValUse(1, SCollectionType(SInt)), + FuncValue( + Array((3, SInt)), + Apply( + FuncValue( + Array((5, SPair(SFunc(Array(SInt), SInt, List()), SInt))), + Apply( + SelectField.typed[Value[SFunc]]( + ValUse(5, SPair(SFunc(Array(SInt), SInt, List()), SInt)), + 1.toByte + ), + Array( + SelectField.typed[Value[SInt.type]]( + ValUse(5, SPair(SFunc(Array(SInt), SInt, List()), SInt)), + 2.toByte + ) + ) + ) + ), + Array( + Tuple( + Vector( + FuncValue( + Array((5, SInt)), + ArithOp(ValUse(5, SInt), IntConstant(1), OpCode @@ (-102.toByte)) + ), + ValUse(3, SInt) + ) + ) + ) + ) + ) + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + Coll(1, 2) -> Expected( + Success(Coll(2, 3)), + cost = 1793, + expectedDetails = CostDetails.ZeroCost + ) + ), + f, + preGeneratedSamples = Some(Seq( + Coll(Int.MinValue, Int.MaxValue - 1), + Coll(0, 1, 2, 3, 100, 1000) + )) + ) + } + + property("Global.deserializeTo - group element") { + def deserializeTo: Feature[GroupElement, Boolean] = { + newFeature( + { (x: GroupElement) => CSigmaDslBuilder.deserializeTo[GroupElement](x.getEncoded) == x}, + "{ (x: GroupElement) => Global.deserializeTo[GroupElement](x.getEncoded) == x }", + FuncValue( + Array((1, SGroupElement)), + EQ( + MethodCall.typed[Value[SGroupElement.type]]( + Global, + SGlobalMethods.deserializeToMethod.withConcreteTypes(Map(tT -> SGroupElement)), + Vector( + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SGroupElement), + SGroupElementMethods.getMethodByName("getEncoded"), + IndexedSeq(), + Map() + ) + ), + Map(STypeVar("T") -> SGroupElement) + ), + ValUse(1, SGroupElement) + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + CGroupElement(SecP256K1Group.generator) -> new Expected(ExpectedResult(Success(true), None)) + ), + deserializeTo + ) + } + + property("Global.deserializeTo - header") { + val headerBytes = "02ac2101807f0000ca01ff0119db227f202201007f62000177a080005d440896d05d3f80dcff7f5e7f59007294c180808d0158d1ff6ba10000f901c7f0ef87dcfff17fffacb6ff7f7f1180d2ff7f1e24ffffe1ff937f807f0797b9ff6ebdae007e5c8c00b8403d3701557181c8df800001b6d5009e2201c6ff807d71808c00019780f087adb3fcdbc0b3441480887f80007f4b01cf7f013ff1ffff564a0000b9a54f00770e807f41ff88c00240000080c0250000000003bedaee069ff4829500b3c07c4d5fe6b3ea3d3bf76c5c28c1d4dcdb1bed0ade0c0000000000003105" + val header1 = new CHeader(ErgoHeader.sigmaSerializer.fromBytes(Base16.decode(headerBytes).get)) + + // v1 header below + val header2Bytes = "010000000000000000000000000000000000000000000000000000000000000000766ab7a313cd2fb66d135b0be6662aa02dfa8e5b17342c05a04396268df0bfbb93fb06aa44413ff57ac878fda9377207d5db0e78833556b331b4d9727b3153ba18b7a08878f2a7ee4389c5a1cece1e2724abe8b8adc8916240dd1bcac069177303f1f6cee9ba2d0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8060117650100000003be7ad70c74f691345cbedba19f4844e7fc514e1188a7929f5ae261d5bb00bb6602da9385ac99014ddcffe88d2ac5f28ce817cd615f270a0a5eae58acfb9fd9f6a0000000030151dc631b7207d4420062aeb54e82b0cfb160ff6ace90ab7754f942c4c3266b" + val header2 = new CHeader(ErgoHeader.sigmaSerializer.fromBytes(Base16.decode(header2Bytes).get)) + + def deserializeTo: Feature[Header, Boolean] = { + newFeature( + { (x: Header) => CSigmaDslBuilder.deserializeTo[Header](CSigmaDslBuilder.serialize(x)) == x}, + "{ (x: Header) => Global.deserializeTo[Header](serialize(x)) == x }", + FuncValue( + Array((1, SHeader)), + EQ( + MethodCall.typed[Value[SHeader.type]]( + Global, + SGlobalMethods.deserializeToMethod.withConcreteTypes(Map(tT -> SHeader)), + Vector( + MethodCall.typed[Value[SCollection[SByte.type]]]( + Global, + SGlobalMethods.serializeMethod.withConcreteTypes( + Map(STypeVar("T") -> SHeader) + ), + Array(ValUse(1, SHeader)), + Map() + ) + ), + Map(STypeVar("T") -> SHeader) + ), + ValUse(1, SHeader) + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + header1 -> new Expected(ExpectedResult(Success(true), None)), + header2 -> new Expected(ExpectedResult(Success(true), None)) + ), + deserializeTo + ) + } + + property("Global.serialize & deserialize roundtrip - BigInt") { + import sigma.data.OrderingOps.BigIntOrdering + + def deserializeTo: Feature[BigInt, Boolean] = { + newFeature( + { (x: BigInt) => CSigmaDslBuilder.deserializeTo[BigInt](CSigmaDslBuilder.serialize(x)) == x}, + "{ (x: BigInt) => Global.deserializeTo[BigInt](serialize(x)) == x }", + FuncValue( + Array((1, SBigInt)), + EQ( + MethodCall.typed[Value[SBigInt.type]]( + Global, + SGlobalMethods.deserializeToMethod.withConcreteTypes(Map(tT -> SBigInt)), + Vector( + MethodCall.typed[Value[SCollection[SByte.type]]]( + Global, + SGlobalMethods.serializeMethod.withConcreteTypes( + Map(STypeVar("T") -> SBigInt) + ), + Array(ValUse(1, SBigInt)), + Map() + ) + ), + Map(STypeVar("T") -> SBigInt) + ), + ValUse(1, SBigInt) + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + val cases = Seq( + (CBigInt(BigInteger.ONE), new Expected(ExpectedResult(Success(true), None))), + (CBigInt(sigma.crypto.SecP256K1Group.q.divide(new BigInteger("2"))), new Expected(ExpectedResult(Success(true), None))), + (CBigInt(sigma.crypto.SecP256K1Group.p.divide(new BigInteger("2"))), new Expected(ExpectedResult(Success(true), None))) + ) + verifyCases(cases, deserializeTo) + } + + 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("Global.encodeNbits") { + import sigma.data.OrderingOps.BigIntOrdering + + val f = newFeature[BigInt, Long]( + { (bi: BigInt) => SigmaDsl.encodeNbits(bi) }, + """{(bi: BigInt) => Global.encodeNbits(bi) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + //cases taken from Ergo blockchain and BitcoinJ / Ergo node tests + verifyCases( + Seq( + (CBigInt(new BigInteger("1146584469340160"))) -> Expected(ExpectedResult(Success(117707472L), None)), + (CBigInt(new BigInteger("130e0000000000000000000000000000000000000000000", 16))) -> Expected(ExpectedResult(Success(0x180130e0L), None)), + (CBigInt(new BigInteger("7fffff0000000000000000000000000000000000000000000000000000000000", 16))) -> Expected(ExpectedResult(Success(0x207fffffL), None)) + ), + f + ) + } + + property("Global.decodeNbits") { + import sigma.data.OrderingOps.BigIntOrdering + + val f = newFeature[Long, BigInt]( + { (l: Long) => SigmaDsl.decodeNbits(l) }, + """{(l: Long) => Global.decodeNbits(l) }""".stripMargin, + sinceVersion = VersionContext.V6SoftForkVersion + ) + + //cases taken from Ergo blockchain and BitcoinJ / Ergo node tests + verifyCases( + Seq( + (0x207fffffL) -> Expected(ExpectedResult(Success(CBigInt(new BigInteger("7fffff0000000000000000000000000000000000000000000000000000000000", 16))), None)), + (0x04923456L) -> Expected(ExpectedResult(Success(CBigInt(new BigInteger("-12345600", 16))), None)), + (0x04123456L) -> Expected(ExpectedResult(Success(CBigInt(new BigInteger("12345600", 16))), None)), + (0x01003456L) -> Expected(ExpectedResult(Success(CBigInt(new BigInteger("0", 16))), 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 + ) + + } + + property("Global.some") { + lazy val some = newFeature( + { (x: Byte) => CSigmaDslBuilder.some[Byte](x) }, + "{ (x: Byte) => Global.some[Byte](x) }", + sinceVersion = V6SoftForkVersion) + val cases = Seq( + (0.toByte, Success(Some(0.toByte))), + (1.toByte, Success(Some(1.toByte))) + ) + + testCases(cases, some) + } + + property("Global.none") { + lazy val some = newFeature( + { (x: Byte) => CSigmaDslBuilder.none[Byte]() }, + "{ (x: Byte) => Global.none[Byte]() }", + sinceVersion = V6SoftForkVersion) + val cases = Seq( + (0.toByte, Success(None)), + (1.toByte, Success(None)) + ) + + testCases(cases, some) + } + +} diff --git a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala index 5ac9b80889..266c5e66e5 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala @@ -2,6 +2,7 @@ package sigma import org.scalatest.BeforeAndAfterAll import scalan.{BaseCtxTests, BaseLiftableTests} +import sigma.compiler.ir.IRContext import sigma.data.TrivialProp import sigma.eval.Extensions.toAnyValue import sigmastate.eval._ @@ -28,13 +29,11 @@ class SigmaDslStaginTests extends BaseCtxTests with ErgoScriptTestkit with BaseL type RContext = cake.Context type RBox = cake.Box type RSigmaProp = cake.SigmaProp - val boxA1 = newAliceBox(1, 100) - val boxA2 = newAliceBox(2, 200) + val boxA1 = newAliceBox(100) + val boxA2 = newAliceBox(200) 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 46222d9fb1..7f3f28b791 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -14,6 +14,7 @@ import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scalan.Platform.threadSleepOrNoOp import sigma.Extensions.ArrayOps import sigma.data.{CBox, CollType, OptionType, PairType, ProveDlog, RType, SigmaLeaf} +import sigma.VersionContext.V6SoftForkVersion import sigma.util.BenchmarkUtil import sigma.util.CollectionUtil._ import sigma.util.Extensions._ @@ -25,12 +26,13 @@ import sigma.ast._ import sigma.eval.{CostDetails, EvalSettings, SigmaDsl} import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.crypto.SigmaProtocolPrivateInput -import sigmastate.eval.{CContext, CompiletimeIRContext, IRContext} +import sigmastate.eval.CContext import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{CompilerTestingCommons, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaPPrint} import sigmastate.interpreter.Interpreter.{ScriptEnv, VerificationResult} import sigmastate.interpreter._ import sigma.ast.Apply +import sigma.compiler.ir.{CompiletimeIRContext, IRContext} import sigma.eval.Extensions.SigmaBooleanOps import sigma.interpreter.{ContextExtension, ProverResult} import sigma.serialization.ValueSerializer @@ -122,6 +124,12 @@ class SigmaDslTesting extends AnyPropSpec /** Type descriptor for type B. */ def tB: RType[B] + /** Checks if this feature is supported in the given version context. */ + def isSupportedIn(vc: VersionContext): Boolean + + /** Version in which the feature is first implemented of changed. */ + def sinceVersion: Byte + /** Script containing this feature. */ def script: String @@ -176,13 +184,13 @@ class SigmaDslTesting extends AnyPropSpec } /** v3 and v4 implementation*/ - private var _oldF: CompiledFunc[A, B] = _ + private var _oldF: Try[CompiledFunc[A, B]] = _ def oldF: CompiledFunc[A, B] = { if (_oldF == null) { - _oldF = oldImpl() - checkExpectedExprIn(_oldF) + _oldF = Try(oldImpl()) + _oldF.foreach(cf => checkExpectedExprIn(cf)) } - _oldF + _oldF.getOrThrow } /** v5 implementation*/ @@ -253,6 +261,7 @@ class SigmaDslTesting extends AnyPropSpec s"""Should succeed with the same value or fail with the same exception, but was: |First result: $b1 |Second result: $b2 + |Input: $x |Root cause: $cause |""".stripMargin) } @@ -322,14 +331,16 @@ class SigmaDslTesting extends AnyPropSpec // Compile script the same way it is performed by applications (i.e. via Ergo Appkit) val prop = compile(env, code)(IR).asSigmaProp - // Add additional oparations which are not yet implemented in ErgoScript compiler + // Add additional operations which are not yet implemented in ErgoScript compiler val multisig = AtLeast( IntConstant(2), Array( pkAlice, DeserializeRegister(ErgoBox.R5, SSigmaProp), // deserialize pkBob DeserializeContext(2, SSigmaProp))) // deserialize pkCarol - val header = ErgoTree.headerWithVersion(ZeroHeader, ergoTreeVersionInTests) + // We set size for trees v0 as well, to have the same size and so the same cost in V6 interpreter + // (where tree size is accounted in cost) + val header = ErgoTree.setSizeBit(ErgoTree.headerWithVersion(ZeroHeader, ergoTreeVersionInTests)) ErgoTree.withSegregation(header, SigmaOr(prop, multisig)) } @@ -393,7 +404,7 @@ class SigmaDslTesting extends AnyPropSpec ctx } - val (expectedResult, expectedCost) = if (activatedVersionInTests < VersionContext.JitActivationVersion) + val (expectedResult, expectedCost) = if (activatedVersionInTests < sinceVersion) (expected.oldResult, expected.verificationCostOpt) else { val res = expected.newResults(ergoTreeVersionInTests) @@ -452,7 +463,7 @@ class SigmaDslTesting extends AnyPropSpec val verificationCost = cost.toIntExact if (expectedCost.isDefined) { assertResult(expectedCost.get, - s"Actual verify() cost $cost != expected ${expectedCost.get}")(verificationCost) + s"Actual verify() cost $cost != expected ${expectedCost.get} (version: ${VersionContext.current.activatedVersion})")(verificationCost) } case Failure(t) => throw t @@ -500,6 +511,10 @@ class SigmaDslTesting extends AnyPropSpec implicit val cs = compilerSettingsInTests + override def sinceVersion: Byte = 0 + + override def isSupportedIn(vc: VersionContext): Boolean = true + /** in v5.x the old and the new interpreters are the same */ override val oldImpl = () => funcJit[A, B](script) override val newImpl = () => funcJit[A, B](script) @@ -634,10 +649,11 @@ class SigmaDslTesting extends AnyPropSpec } } - /** Descriptor of a language feature which is changed in v5.0. + /** Descriptor of a language feature which is changed in the specified version. * * @tparam A type of an input test data * @tparam B type of an output of the feature function + * @param changedInVersion version in which the feature behaviour is changed * @param script script of the feature function (see Feature trait) * @param scalaFunc feature function written in Scala and used to simulate the behavior * of the script @@ -657,6 +673,7 @@ class SigmaDslTesting extends AnyPropSpec * @param allowDifferentErrors if true, allow v4.x and v5.0 to fail with different error */ case class ChangedFeature[A, B]( + changedInVersion: Byte, script: String, scalaFunc: A => B, override val scalaFuncNew: A => B, @@ -670,6 +687,10 @@ class SigmaDslTesting extends AnyPropSpec implicit val cs = compilerSettingsInTests + override def sinceVersion: Byte = changedInVersion + + override def isSupportedIn(vc: VersionContext): Boolean = true + /** Apply given function to the context variable 1 */ private def getApplyExpr(funcValue: SValue) = { val sType = Evaluation.rtypeToSType(RType[A]) @@ -695,11 +716,17 @@ class SigmaDslTesting extends AnyPropSpec override def checkEquality(input: A, logInputOutput: Boolean = false): Try[(B, CostDetails)] = { // check the old implementation against Scala semantic function var oldRes: Try[(B, CostDetails)] = null - if (ergoTreeVersionInTests < VersionContext.JitActivationVersion) oldRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { try checkEq(scalaFunc)(oldF)(input) catch { - case e: TestFailedException => throw e + case e: TestFailedException => + if(activatedVersionInTests < changedInVersion) { + throw e + } else { + // old ergoscript may succeed in new version while old scalafunc may fail, + // see e.g. "Option.getOrElse with lazy default" test + Failure(e) + } case t: Throwable => Failure(t) } @@ -744,10 +771,10 @@ 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.isJitActivated) { + if (VersionContext.current.activatedVersion < changedInVersion) { // check the old implementation with Scala semantic val expectedOldRes = expected.value @@ -832,8 +859,17 @@ class SigmaDslTesting extends AnyPropSpec * This in not yet implemented and will be finished in v6.0. * In v5.0 is only checks that some features are NOT implemented, i.e. work for * negative tests. + * + * @param sinceVersion language version (protocol) when the feature is introduced, see + * [[VersionContext]] + * @param script the script to be tested against semantic function + * @param scalaFuncNew semantic function which defines expected behavior of the given script + * @param expectedExpr expected ErgoTree expression which corresponds to the given script + * @param printExpectedExpr if true, print the test vector for expectedExpr when it is None + * @param logScript if true, log scripts to console */ case class NewFeature[A, B]( + sinceVersion: Byte, script: String, override val scalaFuncNew: A => B, expectedExpr: Option[SValue], @@ -841,25 +877,34 @@ class SigmaDslTesting extends AnyPropSpec logScript: Boolean = LogScriptDefault )(implicit IR: IRContext, override val evalSettings: EvalSettings, val tA: RType[A], val tB: RType[B]) extends Feature[A, B] { + + override def isSupportedIn(vc: VersionContext): Boolean = + vc.activatedVersion >= sinceVersion + override def scalaFunc: A => B = { x => - sys.error(s"Semantic Scala function is not defined for old implementation: $this") + if (isSupportedIn(VersionContext.current)) { + scalaFuncNew(x) + } else { + sys.error(s"Semantic Scala function is not defined for old implementation: $this") + } } implicit val cs = compilerSettingsInTests - /** in v5.x the old and the new interpreters are the same */ + /** Starting from v5.x the old and the new interpreters are the same */ val oldImpl = () => funcJit[A, B](script) - val newImpl = oldImpl // funcJit[A, B](script) // TODO v6.0: use actual new implementation here (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/910) + val newImpl = oldImpl - /** In v5.x this method just checks the old implementations fails on the new feature. */ + /** Check the new implementation works equal to the semantic function. + * This method also checks the old implementations fails on the new feature. + */ override def checkEquality(input: A, logInputOutput: Boolean = false): Try[(B, CostDetails)] = { - val oldRes = Try(oldF(input)) - oldRes.isFailure shouldBe true - if (!(newImpl eq oldImpl)) { - val newRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { - checkEq(scalaFuncNew)(newF)(input) - } + if (this.isSupportedIn(VersionContext.current)) { + checkEq(scalaFuncNew)(newF)(input) + } else { + val oldRes = Try(oldF(input)) + oldRes.isFailure shouldBe true + oldRes } - oldRes } override def checkExpected(input: A, expected: Expected[B]): Unit = { @@ -879,7 +924,10 @@ class SigmaDslTesting extends AnyPropSpec printTestCases: Boolean, failOnTestVectors: Boolean): Unit = { val res = checkEquality(input, printTestCases).map(_._1) - res.isFailure shouldBe true + if (this.isSupportedIn(VersionContext.current)) { + res shouldBe expectedResult + } else + res.isFailure shouldBe true Try(scalaFuncNew(input)) shouldBe expectedResult } @@ -888,8 +936,19 @@ class SigmaDslTesting extends AnyPropSpec printTestCases: Boolean, failOnTestVectors: Boolean): Unit = { val funcRes = checkEquality(input, printTestCases) - funcRes.isFailure shouldBe true - Try(scalaFunc(input)) shouldBe expected.value + if(!isSupportedIn(VersionContext.current)) { + funcRes.isFailure shouldBe true + } + if(isSupportedIn(VersionContext.current)) { + val res = Try(scalaFunc(input)) + if(expected.value.isSuccess) { + res shouldBe expected.value + } else { + res.isFailure shouldBe true + } + } else { + Try(scalaFunc(input)).isFailure shouldBe true + } } } @@ -957,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 @@ -975,6 +1048,30 @@ class SigmaDslTesting extends AnyPropSpec } } + /** Used when the old and new value and costs are the same for all versions, but Version 3 (Ergo 6.0) will have a different cost due to deserialization cost being added. + * Different versions of ErgoTree can have different deserialization costs as well + * + * @param value expected result of tested function + * @param cost expected verification cost + * @param expectedDetails expected cost details for all versions <= V3 + * @param expectedNewCost expected new verification cost for all versions <= V3 + * @param expectedV3Cost expected cost for >=V3 + */ + def apply[A](value: Try[A], + cost: Int, + expectedDetails: CostDetails, + expectedNewCost: Int, + expectedV3Costs: Seq[Int] + )(implicit dummy: DummyImplicit): Expected[A] = + new Expected(ExpectedResult(value, Some(cost))) { + override val newResults = defaultNewResults.zipWithIndex.map { + case ((ExpectedResult(v, _), _), version) => { + var cost = if (activatedVersionInTests >= V6SoftForkVersion) expectedV3Costs(version) else expectedNewCost + (ExpectedResult(v, Some(cost)), Some(expectedDetails)) + } + } + } + /** Used when operation semantics changes in new versions. For those versions expected * test vectors can be specified. * @@ -985,8 +1082,10 @@ class SigmaDslTesting extends AnyPropSpec * @param newVersionedResults new results returned by each changed feature function in * v5.+ for each ErgoTree version. */ - def apply[A](value: Try[A], cost: Int, - expectedDetails: CostDetails, newCost: Int, + def apply[A](value: Try[A], + cost: Int, + expectedDetails: CostDetails, + newCost: Int, newVersionedResults: Seq[(Int, (ExpectedResult[A], Option[CostDetails]))]): Expected[A] = new Expected[A](ExpectedResult(value, Some(cost))) { override val newResults = { @@ -1029,14 +1128,16 @@ class SigmaDslTesting extends AnyPropSpec * various ways */ def changedFeature[A: RType, B: RType] - (scalaFunc: A => B, + (changedInVersion: Byte, + scalaFunc: A => B, scalaFuncNew: A => B, script: String, expectedExpr: SValue = null, allowNewToSucceed: Boolean = false, - allowDifferentErrors: Boolean = false) + allowDifferentErrors: Boolean = false + ) (implicit IR: IRContext, evalSettings: EvalSettings): Feature[A, B] = { - ChangedFeature(script, scalaFunc, scalaFuncNew, Option(expectedExpr), + ChangedFeature(changedInVersion, script, scalaFunc, scalaFuncNew, Option(expectedExpr), allowNewToSucceed = allowNewToSucceed, allowDifferentErrors = allowDifferentErrors) } @@ -1044,6 +1145,7 @@ class SigmaDslTesting extends AnyPropSpec /** Describes a NEW language feature which must NOT be supported in v4 and * must BE supported in v5 of the language. * + * @param sinceVersion language version (protocol) when the feature is introduced, see [[VersionContext]] * @param scalaFunc semantic function which defines expected behavior of the given script * @param script the script to be tested against semantic function * @param expectedExpr expected ErgoTree expression which corresponds to the given script @@ -1051,9 +1153,9 @@ class SigmaDslTesting extends AnyPropSpec * various ways */ def newFeature[A: RType, B: RType] - (scalaFunc: A => B, script: String, expectedExpr: SValue = null) + (scalaFunc: A => B, script: String, expectedExpr: SValue = null, sinceVersion: Byte = VersionContext.JitActivationVersion) (implicit IR: IRContext, es: EvalSettings): Feature[A, B] = { - NewFeature(script, scalaFunc, Option(expectedExpr)) + NewFeature(sinceVersion, script, scalaFunc, Option(expectedExpr)) } val contextGen: Gen[Context] = ergoLikeContextGen.map(c => c.toSigmaContext()) @@ -1274,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 @@ -1302,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/CompilerCrossVersionProps.scala b/sc/shared/src/test/scala/sigmastate/CompilerCrossVersionProps.scala index 89d15dd4df..4062f13686 100644 --- a/sc/shared/src/test/scala/sigmastate/CompilerCrossVersionProps.scala +++ b/sc/shared/src/test/scala/sigmastate/CompilerCrossVersionProps.scala @@ -12,12 +12,11 @@ trait CompilerCrossVersionProps extends CrossVersionProps with CompilerTestsBase (implicit pos: Position): Unit = { super.property(testName, testTags:_*)(testFun) - val testName2 = s"${testName}_MCLowering" - super.property2(testName2, testTags:_*) { - if (okRunTestsWithoutMCLowering) { - _lowerMethodCalls.withValue(false) { - testFun_Run(testName2, testFun) - } + if (okRunTestsWithoutMCLowering) { + val testName2 = s"${testName}_MCLowering" + _lowerMethodCalls.withValue(false) { + // run testFun for all versions again, but now with this flag + super.property(testName2, testTags:_*)(testFun) } } } diff --git a/sc/shared/src/test/scala/sigmastate/CompilerTestsBase.scala b/sc/shared/src/test/scala/sigmastate/CompilerTestsBase.scala index 28f907c199..f1d5c6aa89 100644 --- a/sc/shared/src/test/scala/sigmastate/CompilerTestsBase.scala +++ b/sc/shared/src/test/scala/sigmastate/CompilerTestsBase.scala @@ -1,16 +1,17 @@ package sigmastate import scala.util.DynamicVariable -import sigmastate.lang.{CompilerResult, CompilerSettings, SigmaCompiler} import sigmastate.interpreter.Interpreter.ScriptEnv import sigma.ast.{ErgoTree, SType, TransformingSigmaBuilder, Value} import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix import sigma.ast.syntax.{SValue, SigmaPropValue} import sigma.serialization.ValueSerializer -import sigmastate.eval.IRContext +import sigma.compiler.ir.IRContext import sigma.ast.syntax.ValueOps +import sigma.compiler.{CompilerResult, CompilerSettings, SigmaCompiler} +import sigmastate.helpers.{NegativeTesting, SigmaPPrint} -trait CompilerTestsBase extends TestsBase { +trait CompilerTestsBase extends TestsBase with NegativeTesting { protected val _lowerMethodCalls = new DynamicVariable[Boolean](true) /** Returns true if MethodCall nodes should be lowered by TypeChecker to the @@ -63,4 +64,12 @@ trait CompilerTestsBase extends TestsBase { val tree = mkTestErgoTree(prop) (tree, prop) } + + /** Checks expectation pretty printing the actual value if there is a difference. */ + def checkEquals[T](actual: T, expected: T): Unit = { + if (expected != actual) { + SigmaPPrint.pprintln(actual, width = 100) + } + actual shouldBe expected + } } diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index b0b6273fad..0504a79c65 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -7,28 +7,30 @@ 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 import sigmastate.interpreter.CErgoTreeEvaluator import sigma.ast.syntax._ +import sigma.compiler.CompilerSettings import sigma.eval.EvalSettings import sigma.exceptions.{CostLimitException, InterpreterException} -import sigmastate.lang.CompilerSettings import sigma.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.Plus +import sigmastate.{CrossVersionProps, Plus} import sigmastate.utils.Helpers.TryOps /** Regression tests with ErgoTree related test vectors. * This test vectors verify various constants which are consensus critical and should not change. */ -class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { +class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with CrossVersionProps { property("Value.sourceContext") { val srcCtx = SourceContext.fromParserIndex(0, "") @@ -264,7 +266,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { val typeCodes = Table( ("constant", "expectedValue"), - (SPrimType.LastPrimTypeCode, 8), + (SPrimType.LastPrimTypeCode, 9), (SPrimType.MaxPrimTypeCode, 11) ) @@ -285,6 +287,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { (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), @@ -313,45 +316,172 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { */ case class MInfo(methodId: Byte, method: SMethod, isResolvableFromIds: Boolean = true) + def isV6Activated = VersionContext.current.isV6SoftForkActivated + // NOTE, the type code constants are checked above // The methodId codes as checked here, they MUST be PRESERVED. - // The following table should be made dependent on HF activation - val methods = Table( + // Note, the following table is dependent on SF activation. + def methods = { + import SNumericTypeMethods._ + Table( ("typeId", "methods", "CanHaveMethods"), (SBoolean.typeId, Seq.empty[MInfo], true), - (SByte.typeId, Seq.empty[MInfo], false), - (SShort.typeId, Seq.empty[MInfo], false), - (SInt.typeId, Seq.empty[MInfo], false), - (SLong.typeId, Seq.empty[MInfo], false), - - { // SNumericType.typeId is erroneously shadowed by SGlobal.typeId - // this should be preserved in v3.x and fixed in v4.0 - (SNumericType.typeId, Seq( - MInfo(methodId = 1, SGlobalMethods.groupGeneratorMethod), - MInfo(2, SGlobalMethods.xorMethod) - ), true) + { + if (isV6Activated) + (SByte.typeId, Seq( + MInfo(methodId = 1, ToByteMethod), + MInfo(2, ToShortMethod), + MInfo(3, ToIntMethod), + MInfo(4, ToLongMethod), + MInfo(5, ToBigIntMethod), + MInfo(6, ToBytesMethod), + MInfo(7, ToBitsMethod), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true) + ), true) + else + (SByte.typeId, Seq.empty[MInfo], false) }, + { + if (isV6Activated) + (SShort.typeId, Seq( + MInfo(methodId = 1, ToByteMethod), + MInfo(2, ToShortMethod), + MInfo(3, ToIntMethod), + MInfo(4, ToLongMethod), + MInfo(5, ToBigIntMethod), + MInfo(6, ToBytesMethod), + MInfo(7, ToBitsMethod), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true) + ), true) + else + (SShort.typeId, Seq.empty[MInfo], false) + }, + { + if (isV6Activated) + (SInt.typeId, Seq( + MInfo(methodId = 1, ToByteMethod), + MInfo(2, ToShortMethod), + MInfo(3, ToIntMethod), + MInfo(4, ToLongMethod), + MInfo(5, ToBigIntMethod), + MInfo(6, ToBytesMethod), + MInfo(7, ToBitsMethod), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true) + ), true) + else + (SInt.typeId, Seq.empty[MInfo], false) + }, + { + if (isV6Activated) + (SLong.typeId, Seq( + MInfo(methodId = 1, ToByteMethod), + MInfo(2, ToShortMethod), + MInfo(3, ToIntMethod), + MInfo(4, ToLongMethod), + MInfo(5, ToBigIntMethod), + MInfo(6, ToBytesMethod), + MInfo(7, ToBitsMethod), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true) + ), true) + else + (SLong.typeId, Seq.empty[MInfo], false) + }, + +// { // SNumericType.typeId is erroneously shadowed by SGlobal.typeId +// // this should be preserved in v3.x and fixed in v4.0 +// (SNumericType.typeId, Seq( +// MInfo(methodId = 1, SGlobalMethods.groupGeneratorMethod), +// MInfo(2, SGlobalMethods.xorMethod) +// ), true) +// }, { // SBigInt inherit methods from SNumericType.methods - // however they are not resolvable via SBigInt.typeId + // however they are not resolvable via SBigInt.typeId before v6.0 import SNumericTypeMethods._ (SBigInt.typeId, Seq( - MInfo(methodId = 1, ToByteMethod, isResolvableFromIds = false), - MInfo(2, ToShortMethod, isResolvableFromIds = false), - MInfo(3, ToIntMethod, isResolvableFromIds = false), - MInfo(4, ToLongMethod, isResolvableFromIds = false), - MInfo(5, ToBigIntMethod, isResolvableFromIds = false), - MInfo(6, ToBytesMethod, isResolvableFromIds = false), - MInfo(7, ToBitsMethod, isResolvableFromIds = false) - ), true) + MInfo(methodId = 1, ToByteMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(2, ToShortMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(3, ToIntMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(4, ToLongMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(5, ToBigIntMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(6, ToBytesMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(7, ToBitsMethod, isResolvableFromIds = if (isV6Activated) true else false)) ++ + (if (isV6Activated) Seq( + // methods added in v6.0 + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true), + MInfo(14, ToUnsigned, isResolvableFromIds = true), + MInfo(15, ToUnsignedMod, isResolvableFromIds = true) + ) else Seq.empty) + , true) }, + { + if (isV6Activated) { + // SUnsignedBigInt inherit methods from SNumericType.methods + // however they are not resolvable via SBigInt.typeId before v6.0 + import SNumericTypeMethods._ + (SUnsignedBigInt.typeId, Seq( + MInfo(methodId = 1, ToByteMethod, isResolvableFromIds = true), + MInfo(2, ToShortMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(3, ToIntMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(4, ToLongMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(5, ToBigIntMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(6, ToBytesMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(7, ToBitsMethod, isResolvableFromIds = if (isV6Activated) true else false), + MInfo(8, BitwiseInverseMethod, isResolvableFromIds = true), + MInfo(9, BitwiseOrMethod, isResolvableFromIds = true), + MInfo(10, BitwiseAndMethod, isResolvableFromIds = true), + MInfo(11, BitwiseXorMethod, isResolvableFromIds = true), + MInfo(12, ShiftLeftMethod, isResolvableFromIds = true), + MInfo(13, ShiftRightMethod, isResolvableFromIds = true), + MInfo(14, ModInverseMethod, true), + MInfo(15, PlusModMethod, true), + MInfo(16, SubtractModMethod, true), + MInfo(17, MultiplyModMethod, true), + MInfo(18, ModMethod, true), + MInfo(19, ToSignedMethod, true) + ), true) + } else { + (SUnsignedBigInt.typeId, Seq.empty, false) + } + }, { import SGroupElementMethods._ (SGroupElement.typeId, Seq( MInfo(2, GetEncodedMethod), MInfo(3, ExponentiateMethod), MInfo(4, MultiplyMethod), MInfo(5, NegateMethod) - ), true) + ) ++ { + if(VersionContext.current.isV6SoftForkActivated) { + Seq(MInfo(6, ExponentiateUnsignedMethod)) + } else { + Seq.empty + } + }, true) }, { import SSigmaPropMethods._ (SSigmaProp.typeId, Seq( @@ -367,9 +497,12 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(4, BytesWithoutRefMethod), MInfo(5, IdMethod), MInfo(6, creationInfoMethod), - MInfo(7, getRegMethod), MInfo(8, tokensMethod) - ) ++ registers(idOfs = 8) + ) ++ (if (isV6Activated) { + Seq(MInfo(7, getRegMethodV6)) + } else { + Seq(MInfo(7, getRegMethodV5)) + }) ++ registers(idOfs = 8) .zipWithIndex .map { case (m,i) => MInfo((8 + i + 1).toByte, m) }, true) }, @@ -399,7 +532,11 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { 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) - ), true) + ) ++ (if (isV6Activated) { + Seq(MInfo(16, checkPowMethod)) // methods added in v6.0 + } else { + Seq.empty[MInfo] + }), true) }, { import SPreHeaderMethods._ (SPreHeader.typeId, Seq( @@ -413,13 +550,20 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(1, dataInputsMethod), MInfo(2, headersMethod), MInfo(3, preHeaderMethod), MInfo(4, inputsMethod), MInfo(5, outputsMethod), MInfo(6, heightMethod), MInfo(7, selfMethod), MInfo(8, selfBoxIndexMethod), MInfo(9, lastBlockUtxoRootHashMethod), - MInfo(10, minerPubKeyMethod), MInfo(11, getVarMethod) - ), 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) { + Seq(MInfo(3, serializeMethod), MInfo(4, deserializeToMethod), MInfo(5, FromBigEndianBytesMethod), MInfo(6, encodeNBitsMethod), MInfo(7, decodeNBitsMethod), MInfo(8, powHitMethod), MInfo(9, someMethod), MInfo(10, noneMethod)) // methods added in v6.0 + } else { + Seq.empty[MInfo] + }), true) }, { import SCollectionMethods._ (SCollection.typeId, Seq( @@ -461,7 +605,9 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { 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( @@ -475,7 +621,13 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(8, FilterMethod) ), true) } - ) + ) + } + + property("SNumericType.typeId resolves to SGlobal") { + SNumericType.typeId shouldBe SGlobal.typeId + SMethod.fromIds(SNumericType.typeId, 1) shouldBe SGlobalMethods.groupGeneratorMethod + } property("MethodCall Codes") { forAll(methods) { (typeId, methods, canHaveMethods) => @@ -484,7 +636,9 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { assert(canHaveMethods, s"Type $tyDesc should NOT have methods") val mc = MethodsContainer(tyDesc.typeId) + mc.methods.length shouldBe methods.length + for (expectedMethod <- methods) { if (expectedMethod.isResolvableFromIds) { @@ -508,21 +662,28 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { assert(!canHaveMethods, s"Type with code $typeId can have methods") } } - } property("MethodCall on numerics") { - forAll(Table[STypeCompanion]("type", SByte, SShort, SInt, SLong, SBigInt)) { t => - // this methods are expected to fail resolution in v3.x (but may change in future) - (1 to 7).foreach { methodId => - assertExceptionThrown( - SMethod.fromIds(t.typeId, methodId.toByte), - { - case _: ValidationException => true - case _ => false - }, - s"SMethod mustn't resolve for typeId = ${t.typeId} and methodId = $methodId" - ) + 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 => + assertExceptionThrown( + SMethod.fromIds(t.typeId, methodId.toByte), + { + case _: ValidationException => true + case _ => false + }, + s"SMethod mustn't resolve for typeId = ${t.typeId} and methodId = $methodId" + ) + } + } else { + // in v6.0 these codes should resolve to the methods of the concrete numeric type + (1 to 7).foreach { methodId => + val m = SMethod.fromIds(t.typeId, methodId.toByte) + m.objType.ownerType shouldBe t + } } } } diff --git a/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala b/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala index b0206fa464..153aae28d5 100644 --- a/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala @@ -14,6 +14,7 @@ import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings import sigma.eval.EvalSettings.EvaluationMode import sigmastate.interpreter._ import sigma.ast.syntax.ValueOps +import sigma.compiler.ir.IRContext import sigma.data.CBox import sigma.eval.EvalSettings import sigma.exceptions.InterpreterException @@ -272,17 +273,18 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { /** Rule#| BlockVer | Block Type| Script Version | Release | Validation Action * -----|----------|-----------|----------------|---------|-------- - * 19 | 4 | candidate | Script v3 | v5.0 | skip-accept (rely on majority) - * 20 | 4 | mined | Script v3 | v5.0 | skip-accept (rely on majority) + * 19 | 5 | candidate | Script v4 | v6.0 | skip-accept (rely on majority) + * 20 | 5 | mined | Script v4 | v6.0 | skip-accept (rely on majority) */ - property("Rules 19,20 | Block v4 | candidate or mined block | Script v3") { - forEachActivatedScriptVersion(activatedVers = Array[Byte](3)) // version for Block v4 + property("Rules 19,20 | Block v5 | candidate or mined block | Script v4") { + forEachActivatedScriptVersion(activatedVers = Array[Byte](4)) // activated version is greater than MaxSupported { - forEachErgoTreeVersion(ergoTreeVers = Array[Byte](3, 4)) { // scripts >= v3 + forEachErgoTreeVersion(ergoTreeVers = Array[Byte](4, 5)) { // tree version >= activated val headerFlags = ErgoTree.defaultHeaderWithVersion(ergoTreeVersionInTests) val ergoTree = createErgoTree(headerFlags) - // prover is rejecting, because such context parameters doesn't make sense + // prover is rejecting, because it cannot generate proofs for ErgoTrees with version + // higher than max supported by the interpreter assertExceptionThrown( testProve(ergoTree, activatedScriptVersion = activatedVersionInTests), exceptionLike[InterpreterException](s"Both ErgoTree version ${ergoTree.version} and activated version $activatedVersionInTests is greater than MaxSupportedScriptVersion $MaxSupportedScriptVersion") @@ -296,20 +298,20 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { } } - /** Rule#| BlockVer | Block Type| Script Version | Release | Validation Action - * -----|----------|-----------|----------------|---------|-------- - * 21 | 4 | candidate | Script v0/v1 | v5.0 | R5.0-JIT-verify - * 22 | 4 | candidate | Script v2 | v5.0 | R5.0-JIT-verify - * 23 | 4 | mined | Script v0/v1 | v5.0 | R5.0-JIT-verify - * 24 | 4 | mined | Script v2 | v5.0 | R5.0-JIT-verify + /** Rule#| BlockVer | Block Type| Script Version | Release | Validation Action + * -----|----------|-----------|-----------------|---------|-------- + * 21 | 5 | candidate | Script v0/v1/v2 | v6.0 | R6.0-JIT-verify + * 22 | 5 | candidate | Script v3 | v6.0 | R6.0-JIT-verify + * 23 | 5 | mined | Script v0/v1/v2 | v6.0 | R6.0-JIT-verify + * 24 | 5 | mined | Script v3 | v6.0 | R6.0-JIT-verify */ - property("Rules 21,22,23,24 | Block v4 | candidate or mined block | Script v0/v1/v2") { - // this test verifies the normal validation action R5.0-JIT-verify of v5.x releases - // when Block v4 already activated, but the script is v0, v1 or v2. + property("Rules 21,22,23,24 | Block v5 | candidate or mined block | Script v0/v1/v2/v3") { + // this test verifies the normal validation action R6.0-JIT-verify of v6.x releases + // when Block v5 already activated, but the script is v0, v1, v2, or v3. - forEachActivatedScriptVersion(Array[Byte](3)) // version for Block v4 + forEachActivatedScriptVersion(Array[Byte](4)) // version for Block v5 { - forEachErgoTreeVersion(Array[Byte](0, 1, 2)) { // tree versions supported by v5.x + forEachErgoTreeVersion(Array[Byte](0, 1, 2, 3)) { // tree versions supported by v6.x // SF inactive: check cost vectors of v4.x interpreter val headerFlags = ErgoTree.defaultHeaderWithVersion(ergoTreeVersionInTests) val ergoTree = createErgoTree(headerFlags) diff --git a/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala b/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala index bca2d0e638..c8b9f06399 100644 --- a/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala @@ -29,7 +29,7 @@ class SoftForkabilitySpecification extends SigmaTestingData with CompilerTestingCommons with BeforeAndAfterAll { - implicit lazy val IR: TestingIRContext = new TestingIRContext + val IR: TestingIRContext = new TestingIRContext lazy val prover = new ErgoLikeTestProvingInterpreter() lazy val verifier = new ErgoLikeTestInterpreter val deadline = 100 @@ -38,7 +38,7 @@ class SoftForkabilitySpecification extends SigmaTestingData lazy val booleanPropV1 = compile(emptyEnv + ("deadline" -> deadline), """{ | HEIGHT > deadline && OUTPUTS.size == 1 - |}""".stripMargin).asBoolValue + |}""".stripMargin)(IR).asBoolValue // cast Boolean typed prop to SigmaProp (which is invalid) // ErgoTree v0 lazy val invalidPropV1: ErgoTree = diff --git a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala index fe5a678679..4075ec5d77 100644 --- a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala @@ -6,38 +6,62 @@ import sigma.ast._ import sigma.ast.syntax._ import sigmastate.interpreter._ import Interpreter._ -import sigma.ast.syntax._ import org.ergoplatform._ import org.scalatest.BeforeAndAfterAll -import scorex.util.encode.Base58 -import sigma.crypto.CryptoConstants +import scorex.util.encode.{Base16, Base58} +import sigma.Colls +import sigma.VersionContext.V6SoftForkVersion +import sigma.VersionContext.V6SoftForkVersion +import sigma.VersionContext +import sigma.data.{CAND, CAvlTree, CBox, CHeader, ProveDlog, SigmaBoolean, TrivialProp} +import sigma.interpreter.ContextExtension import sigma.data.{AvlTreeData, CAND, ProveDlog, SigmaBoolean, TrivialProp} +import sigma.VersionContext.V6SoftForkVersion +import sigma.VersionContext import sigma.util.Extensions.IntOps import sigmastate.helpers.{CompilerTestingCommons, ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigma.serialization.ValueSerializer +import sigma.serialization.{GroupElementSerializer, SigmaSerializer, ValueSerializer} +import sigmastate.eval.CPreHeader +import sigmastate.helpers.ErgoLikeContextTesting.noBoxes +import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings import sigmastate.utils.Helpers._ +import sigma.util.Extensions._ import scala.util.Random class TestingInterpreterSpecification extends CompilerTestingCommons with CompilerCrossVersionProps with BeforeAndAfterAll { - implicit lazy val IR: TestingIRContext = new TestingIRContext - lazy val prover = new ErgoLikeTestProvingInterpreter() { - } + val IR: TestingIRContext = new TestingIRContext - lazy val verifier = new ErgoLikeTestInterpreter { - } + lazy val prover = new ErgoLikeTestProvingInterpreter() - implicit val soundness: Int = CryptoConstants.soundnessBits + lazy val verifier = new ErgoLikeTestInterpreter - def testingContext(h: Int) = - ErgoLikeContextTesting(h, - AvlTreeData.dummy, ErgoLikeContextTesting.dummyPubkey, IndexedSeq(fakeSelf), - ErgoLikeTransaction(IndexedSeq.empty, IndexedSeq.empty), - fakeSelf, activatedVersionInTests) - .withErgoTreeVersion(ergoTreeVersionInTests) + def testingContext(h: Int = 614401) = { + // bytes of real mainnet block header at height 614,440 + val headerBytes = "02ac2101807f0000ca01ff0119db227f202201007f62000177a080005d440896d05d3f80dcff7f5e7f59007294c180808d0158d1ff6ba10000f901c7f0ef87dcfff17fffacb6ff7f7f1180d2ff7f1e24ffffe1ff937f807f0797b9ff6ebdae007e5c8c00b8403d3701557181c8df800001b6d5009e2201c6ff807d71808c00019780f087adb3fcdbc0b3441480887f80007f4b01cf7f013ff1ffff564a0000b9a54f00770e807f41ff88c00240000080c0250000000003bedaee069ff4829500b3c07c4d5fe6b3ea3d3bf76c5c28c1d4dcdb1bed0ade0c0000000000003105" + val header1 = new CHeader(ErgoHeader.sigmaSerializer.fromBytes(Base16.decode(headerBytes).get)) + + val boxesToSpend = IndexedSeq(fakeSelf) + + val preHeader = CPreHeader(activatedVersionInTests, + parentId = header1.id, + timestamp = 3, + nBits = 0, + height = h, + minerPk = GroupElementSerializer.parse(SigmaSerializer.startReader(ErgoLikeContextTesting.dummyPubkey)).toGroupElement, + votes = Colls.emptyColl[Byte] + ) + + new ErgoLikeContext( + header1.stateRoot.asInstanceOf[CAvlTree].treeData, Colls.fromArray(Array(header1)), + preHeader, noBoxes, + boxesToSpend, ErgoLikeTransaction(IndexedSeq.empty, IndexedSeq.empty), + boxesToSpend.indexOf(fakeSelf), ContextExtension.empty, vs, DefaultEvalSettings.scriptCostLimitInEvaluator, + initCost = 0L, activatedVersionInTests).withErgoTreeVersion(ergoTreeVersionInTests) + } property("Reduction to crypto #1") { forAll() { i: Int => @@ -119,16 +143,24 @@ class TestingInterpreterSpecification extends CompilerTestingCommons val dk1 = prover.dlogSecrets(0).publicImage val dk2 = prover.dlogSecrets(1).publicImage - val ctx = testingContext(99) + val ctx = testingContext() val env = Map( "dk1" -> dk1, "dk2" -> dk2, - "bytes1" -> Array[Byte](1, 2, 3), - "bytes2" -> Array[Byte](4, 5, 6), - "box1" -> testBox(10, TrueTree, 0, Seq(), Map( + "bytes1" -> Colls.fromArray(Array[Byte](1, 2, 3)), + "bytes2" -> Colls.fromArray(Array[Byte](4, 5, 6)), + "box1" -> (if(VersionContext.current.isJitActivated) { + CBox(testBox(10, TrueTree, 0, Seq(), Map( + reg1 -> IntArrayConstant(Array[Int](1, 2, 3)), + reg2 -> BoolArrayConstant(Array[Boolean](true, false, true)) + )))} else { + testBox(10, TrueTree, 0, Seq(), Map( reg1 -> IntArrayConstant(Array[Int](1, 2, 3)), - reg2 -> BoolArrayConstant(Array[Boolean](true, false, true))))) - val prop = mkTestErgoTree(compile(env, code).asBoolValue.toSigmaProp) + reg2 -> BoolArrayConstant(Array[Boolean](true, false, true)) + )) + }) + ) + val prop = mkTestErgoTree(compile(env, code)(IR).asBoolValue.toSigmaProp) val challenge = Array.fill(32)(Random.nextInt(100).toByte) val proof1 = prover.prove(prop, ctx, challenge).get.proof verifier.verify(Interpreter.emptyEnv, prop, ctx, proof1, challenge) @@ -204,6 +236,21 @@ class TestingInterpreterSpecification extends CompilerTestingCommons |}""".stripMargin) } + property("Evaluate BigInt to nbits conversion") { + val source = + """ + |{ + | val b: BigInt = 11999.toBigInt + | Global.encodeNbits(b) == 36626176 + |} + |""".stripMargin + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigmastate.exceptions.MethodNotFound] should be thrownBy testEval(source) + } else { + testEval(source) + } + } + property("Evaluate numeric casting ops") { def testWithCasting(castSuffix: String): Unit = { testEval(s"OUTPUTS.size.toByte.$castSuffix == 0.$castSuffix") @@ -218,6 +265,46 @@ class TestingInterpreterSpecification extends CompilerTestingCommons testWithCasting("toBigInt") } + property("BigInt downcasting to byte") { + def test() = testEval("{ sigmaProp(0L.toBigInt.toByte <= CONTEXT.preHeader.version) }") + if(VersionContext.current.isV6SoftForkActivated) { + test() + } else { + an[Exception] shouldBe thrownBy(test()) + } + } + + property("BigInt downcasting to short") { + def test() = testEval("{ sigmaProp(0L.toBigInt.toShort <= CONTEXT.preHeader.version.toShort) }") + if(VersionContext.current.isV6SoftForkActivated) { + test() + } else { + an[Exception] shouldBe thrownBy(test()) + } + } + + property("BigInt downcasting to int") { + def test() = testEval("{ sigmaProp(1L.toBigInt.toInt < CONTEXT.preHeader.timestamp.toInt) }") + if(VersionContext.current.isV6SoftForkActivated) { + test() + } else { + an[Exception] shouldBe thrownBy(test()) + } + } + + property("BigInt downcasting to long") { + def test() = testEval("{ sigmaProp(1L.toBigInt.toLong < CONTEXT.preHeader.timestamp) }") + if(VersionContext.current.isV6SoftForkActivated) { + test() + } else { + an[Exception] shouldBe thrownBy(test()) + } + } + + property("upcasting to bigint") { + testEval("{ sigmaProp(1L.toBigInt < bigInt(\"2\")) }") + } + property("Evaluate arithmetic ops") { def testWithCasting(castSuffix: String): Unit = { testEval(s"1.$castSuffix + 2.$castSuffix == 3.$castSuffix") @@ -254,6 +341,27 @@ class TestingInterpreterSpecification extends CompilerTestingCommons testEval("Coll(1, 1).getOrElse(3, 1 + 1) == 2") } + property("Evaluate powHit") { + val source = + """ + |{ + | val b: BigInt = bigInt("1157920892373161954235709850086879078528375642790749043826051631415181614943") + | val k = 32 + | val N = 1024 * 1024 + | val msg = fromBase16("0a101b8c6a4f2e") + | val nonce = fromBase16("000000000000002c") + | val h = fromBase16("00000000") + | + | Global.powHit(k, msg, nonce, h, N) <= b // hit == b in this example + |} + |""".stripMargin + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigmastate.exceptions.MethodNotFound] should be thrownBy testEval(source) + } else { + testEval(source) + } + } + property("Evaluation example #1") { val dk1 = prover.dlogSecrets(0).publicImage val dk2 = prover.dlogSecrets(1).publicImage @@ -340,6 +448,24 @@ class TestingInterpreterSpecification extends CompilerTestingCommons verifier.verify(prop3, env, proof, challenge).map(_._1).getOrElse(false) shouldBe false } + property("blake2b - test vector") { + testEval( + """ { + | val input = fromBase16("68656c6c6f20776f726c64") + | val output = fromBase16("256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610") + | blake2b256(input) == output + | }""".stripMargin) + } + + property("blake2b - test vector #2") { + testEval( + """ { + | val input = fromBase16("02ac2101807f0000ca01ff0119db227f202201007f62000177a080005d440896d05d3f80dcff7f5e7f59007294c180808d0158d1ff6ba10000f901c7f0ef87dcfff17fffacb6ff7f7f1180d2ff7f1e24ffffe1ff937f807f0797b9ff6ebdae007e5c8c00b8403d3701557181c8df800001b6d5009e2201c6ff807d71808c00019780d085adb3fcdbc0b3441480887f80007f4b01cf7f013ff1ffff564a0000b9a54f00770e807f41ff88c00240000080c02500000000") + | val output = fromBase16("bdb84cda5b105c3eb522857b50a0882f88ed5bb3cc8cf3325a1edf7eeb6a0954") + | blake2b256(input) == output + | }""".stripMargin) + } + property("passing a lambda argument") { // single expression testEval( @@ -374,6 +500,29 @@ class TestingInterpreterSpecification extends CompilerTestingCommons testEval(s"""deserialize[Coll[Byte]]("$str")(0) == 2""") } + property("header.id") { + testEval( + """ { + | val h = CONTEXT.headers(0) + | val id = h.id + | id.size == 32 + | }""".stripMargin) + } + + property("checkPow") { + val source = """ { + | val h = CONTEXT.headers(0) + | h.checkPow + | } + | """.stripMargin + + if (activatedVersionInTests < V6SoftForkVersion) { + an [Exception] should be thrownBy testEval(source) + } else { + testEval(source) + } + } + override protected def afterAll(): Unit = { } diff --git a/sc/shared/src/test/scala/sigmastate/TypesSpecification.scala b/sc/shared/src/test/scala/sigmastate/TypesSpecification.scala index 9a4c5ce1b8..6d13863f26 100644 --- a/sc/shared/src/test/scala/sigmastate/TypesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/TypesSpecification.scala @@ -13,8 +13,6 @@ class TypesSpecification extends SigmaTestingData { implicit val tWrapped = wrappedTypeGen(t) forAll { x: SPredefType#WrappedType => isValueOfType(x, t) shouldBe true - // since forall t. SHeader != t - isValueOfType(x, SHeader) shouldBe false } } } diff --git a/sc/shared/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala b/sc/shared/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala index abbed09992..da9ed70efd 100644 --- a/sc/shared/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala +++ b/sc/shared/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala @@ -14,14 +14,16 @@ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeConte import sigmastate.interpreter.Interpreter.ScriptEnv import sigmastate.interpreter.CErgoTreeEvaluator import sigma.ast.syntax.ValueOps +import sigma.compiler.{CompilerResult, CompilerSettings, SigmaCompiler} +import sigma.compiler.ir.IRContext import sigma.interpreter.ContextExtension -import sigmastate.lang.{CompilerResult, CompilerSettings, LangTests, SigmaCompiler} +import sigmastate.lang.LangTests import sigma.serialization.ErgoTreeSerializer.DefaultSerializer import sigmastate.CompilerTestsBase import sigma.{ContractsTestkit, Context => DContext} import scala.annotation.unused -import scala.util.Success +import scala.util.{Success, Try} trait ErgoScriptTestkit extends ContractsTestkit with LangTests with ValidationSpecification with CompilerTestsBase { self: BaseCtxTests => @@ -54,9 +56,8 @@ trait ErgoScriptTestkit extends ContractsTestkit with LangTests ergoCtx } - - lazy val boxA1 = newAliceBox(1, 100) - lazy val boxA2 = newAliceBox(2, 200) + lazy val boxA1 = newAliceBox(100) + lazy val boxA2 = newAliceBox(200) lazy val n1Sym = liftConst(n1) @@ -147,6 +148,31 @@ trait ErgoScriptTestkit extends ContractsTestkit with LangTests res } + private val SigmaM = SigmaProp.SigmaPropMethods + + /** Finds SigmaProp.isProven method calls in the given Lambda `f` */ + private def findIsProven[T](f: Ref[Context => T]): Option[Sym] = { + val Def(Lambda(lam,_,_,_)) = f + val s = lam.flatSchedule.find(sym => sym.node match { + case SigmaM.isValid(_) => true + case _ => false + }) + s + } + + /** Checks that if SigmaProp.isProven method calls exists in the given Lambda's schedule, + * then it is the last operation. */ + private def verifyIsProven[T](f: Ref[Context => T]): Try[Unit] = { + val isProvenOpt = findIsProven(f) + Try { + isProvenOpt match { + case Some(s) => + if (f.getLambda.y != s) !!!(s"Sigma.isProven found in none-root position", s) + case None => + } + } + } + def doReduce(): Unit = { val res = doCosting verifyIsProven(res.compiledGraph) shouldBe Success(()) @@ -183,13 +209,6 @@ trait ErgoScriptTestkit extends ContractsTestkit with LangTests } } - def Case(env: ScriptEnv, name: String, script: String, ctx: ErgoLikeContext, - calc: Ref[Context] => Ref[Any], - tree: SValue, - result: Result) = - EsTestCase(name, env, Code(script), Option(ctx), None, - Option(calc), Option(tree), result) - def reduce(env: ScriptEnv, name: String, script: String, ergoCtx: ErgoLikeContext, expectedResult: Any): Unit = { val tcase = EsTestCase(name, env, Code(script), Some(ergoCtx), expectedResult = Result(expectedResult)) tcase.doReduce() diff --git a/sc/shared/src/test/scala/sigmastate/eval/ErgoTreeBuildingTest.scala b/sc/shared/src/test/scala/sigmastate/eval/ErgoTreeBuildingTest.scala index 526012f212..11ef6f4fac 100644 --- a/sc/shared/src/test/scala/sigmastate/eval/ErgoTreeBuildingTest.scala +++ b/sc/shared/src/test/scala/sigmastate/eval/ErgoTreeBuildingTest.scala @@ -8,6 +8,7 @@ import scalan.BaseCtxTests import sigma.ast.syntax.ValueOps import sigmastate.lang.LangTests import sigma.ast.Apply +import sigma.compiler.ir.IRContext class ErgoTreeBuildingTest extends BaseCtxTests with LangTests with ExampleContracts with ErgoScriptTestkit { diff --git a/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala b/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala index 5fcbd0f220..f9a7edf0cb 100644 --- a/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala +++ b/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala @@ -81,24 +81,6 @@ class EvaluationTest extends BaseCtxTests | f(SELF) || g(SELF.R5[Coll[Int]].get) | }""".stripMargin, ctx, true) } - - test("Measure IRContext creation speed") { - var ctx: RuntimeIRContext = new RuntimeIRContext - measure(100, okShowIterTime = printDebugInfo, okShowTotalTime = printDebugInfo) { i => - ctx = new RuntimeIRContext - } - printDebug(s"Def count: ${ctx.defCount}") - /* - Iter 0: 4 ms - ... - Iter 96: 2 ms - Iter 97: 1 ms - Iter 98: 2 ms - Iter 99: 2 ms - Total time: 244 ms - Def count: 20 - */ - } test("SubstConst") { def script(pk: ProveDlog): SigmaPropValue = diff --git a/sc/shared/src/test/scala/sigmastate/eval/MeasureIRContext.scala b/sc/shared/src/test/scala/sigmastate/eval/MeasureIRContext.scala deleted file mode 100644 index 9f88588cc9..0000000000 --- a/sc/shared/src/test/scala/sigmastate/eval/MeasureIRContext.scala +++ /dev/null @@ -1,36 +0,0 @@ -package sigmastate.eval - -import scalan.{BaseCtxTests, Benchmark} -import sigma.util.BenchmarkUtil.measure - -object MeasureIRContext extends App { - var ctx: RuntimeIRContext = null - measure(1, false) { i => - ctx = new RuntimeIRContext - } - measure(10000, false) { i => - ctx = new RuntimeIRContext - } - print(s"Def count: ${ctx.defCount}") - /* - Total time: 2485 ms - Total time: 2714 ms - Def count: 20 - */ -} - -class SigmaLibraryTests extends BaseCtxTests { - - test("Benchmark SigmaLibrary creation time") { - new Benchmark(new RuntimeIRContext).run() - } -} - -object MeasureSigmaLibraryCreate extends App { - new Benchmark(new RuntimeIRContext).run() - /* - Total time: 12932 ms - Def count: 20, total: 15774 msec - */ -} - diff --git a/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala b/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala index 332ee902a2..3b1d6ba3dc 100644 --- a/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala +++ b/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala @@ -12,16 +12,17 @@ import sigma.data.{RType, SigmaBoolean} import sigma.validation.ValidationException import sigma.validation.ValidationRules.CheckSerializableTypeCode import sigma.ast.syntax.{SValue, SigmaPropValue} +import sigma.compiler.{CompilerSettings, SigmaCompiler} +import sigma.compiler.ir.IRContext import sigma.eval.{CostDetails, EvalSettings, Extensions, GivenCost, TracedCost} import sigmastate.helpers.TestingHelpers._ import sigma.interpreter.ContextExtension.VarBinding import sigmastate.interpreter.CErgoTreeEvaluator.DefaultProfiler import sigmastate.interpreter.Interpreter.ScriptEnv import sigmastate.interpreter._ -import sigmastate.lang.{CompilerSettings, SigmaCompiler} import sigma.serialization.SigmaSerializer import sigmastate.CompilerTestsBase -import sigmastate.eval.{CContext, IRContext} +import sigmastate.eval.CContext import scala.util.DynamicVariable @@ -29,8 +30,7 @@ trait CompilerTestingCommons extends TestingCommons with TestUtils with TestContexts with ValidationSpecification with CompilerTestsBase { - class TestingIRContext extends TestContext with IRContext { - } + class TestingIRContext extends TestContext with IRContext case class CompiledFunc[A,B] (script: String, bindings: Seq[VarBinding], expr: SValue, compiledTree: SValue, func: A => (B, CostDetails)) diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala index 1f758370a0..3c3687cb37 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala @@ -10,9 +10,12 @@ import sigma.ast.syntax.SValue import sigmastate._ import sigmastate.interpreter.Interpreter.ScriptEnv import SigmaPredef.PredefinedFuncRegistry +import sigma.VersionContext import sigma.ast.syntax._ +import sigma.compiler.phases.SigmaBinder import sigma.eval.SigmaDsl import sigma.exceptions.BinderException +import sigmastate.helpers.SigmaPPrint class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with LangTests { import StdSigmaBuilder._ @@ -28,6 +31,17 @@ class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat res } + /** Checks that parsing and binding results in the expected value. + * @return the inferred type of the expression + */ + def checkBound(env: ScriptEnv, x: String, expected: SValue) = { + val bound = bind(env, x) + if (expected != bound) { + SigmaPPrint.pprintln(bound, width = 100) + } + bound shouldBe expected + } + private def fail(env: ScriptEnv, x: String, expectedLine: Int, expectedCol: Int): Unit = { val builder = TransformingSigmaBuilder val ast = SigmaParser(x).get.value @@ -202,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/SigmaCompilerTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 88c75a90b6..90e5d21b1e 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -12,6 +12,8 @@ import sigma.ast.{Apply, MethodCall, ZKProofBlock} import sigma.exceptions.{GraphBuildingException, InvalidArguments, TyperException} import sigma.serialization.ValueSerializer import sigma.serialization.generators.ObjectGenerators + +import java.math.BigInteger import scala.annotation.unused class SigmaCompilerTest extends CompilerTestingCommons with LangTests with ObjectGenerators { @@ -127,6 +129,13 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec res shouldEqual SigmaPropConstant(dk1) } + property("bigInt") { + comp(""" bigInt("326674862673836209462483453386286740270338859283019276168539876024851191344") """) shouldBe + BigIntConstant(new BigInteger("326674862673836209462483453386286740270338859283019276168539876024851191344")) + comp(""" bigInt("-10") """) shouldBe + BigIntConstant(-10L) + } + property("fromBaseX") { comp(""" fromBase16("31") """) shouldBe ByteArrayConstant(Array[Byte](49)) comp(""" fromBase58("r") """) shouldBe ByteArrayConstant(Array[Byte](49)) diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTemplateCompilerTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTemplateCompilerTest.scala index df8f644172..a03d160654 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTemplateCompilerTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTemplateCompilerTest.scala @@ -2,13 +2,19 @@ package sigmastate.lang import org.ergoplatform.ErgoAddressEncoder import org.ergoplatform.sdk.Parameter -import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigma.ast.{IntConstant, SInt, SLong, SString, StringConstant} -import sigmastate.eval.CompiletimeIRContext +import sigma.VersionContext +import sigma.ast.ErgoTree.HeaderType +import sigma.ast.{BinAnd, BoolToSigmaProp, ConstantPlaceholder, ErgoTree, FalseLeaf, GT, Height, IntConstant, LT, SBoolean, SInt, SLong, SString, TrueLeaf} +import sigma.compiler.SigmaTemplateCompiler +import sigma.exceptions.TyperException +import sigmastate.CompilerTestsBase +import sigmastate.interpreter.Interpreter.ScriptEnv + +class SigmaTemplateCompilerTest extends AnyPropSpec with ScalaCheckPropertyChecks with CompilerTestsBase { + val templateCompiler = SigmaTemplateCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) -class SigmaTemplateCompilerTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers { property("compiles full contract template") { val source = """/** This is my contracts description. @@ -24,8 +30,7 @@ class SigmaTemplateCompilerTest extends AnyPropSpec with ScalaCheckPropertyCheck |@contract def contractName(p1: Int = 5, p2: String = "default string", param3: Long) = { | sigmaProp(true) |}""".stripMargin - val compiler = SigmaTemplateCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) - val template = compiler.compile(Map.empty, source) + val template = templateCompiler.compile(source) template.name shouldBe "contractName" template.description shouldBe "This is my contracts description. Here is another line describing what it does in more detail." @@ -36,16 +41,12 @@ class SigmaTemplateCompilerTest extends AnyPropSpec with ScalaCheckPropertyCheck ) template.constTypes should contain theSameElementsInOrderAs Seq(SInt, SString, SLong) template.constValues.get should contain theSameElementsInOrderAs IndexedSeq( - Some(IntConstant(5).asWrappedType), - Some(StringConstant("default string").asWrappedType), + Some(5), + Some("default string"), None ) - val sigmaCompiler = new SigmaCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) - implicit val ir = new CompiletimeIRContext - val result = sigmaCompiler.compile(Map.empty, "{ sigmaProp(true) }") - - template.expressionTree shouldBe result.buildTree + checkEquals(template.expressionTree, BoolToSigmaProp(TrueLeaf)) } property("compiles contract template without braces") { @@ -62,8 +63,7 @@ class SigmaTemplateCompilerTest extends AnyPropSpec with ScalaCheckPropertyCheck |*/ |@contract def contractName(p1: Int = 5, p2: String = "default string") = sigmaProp(true) |""".stripMargin - val compiler = SigmaTemplateCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) - val template = compiler.compile(Map.empty, source) + val template = templateCompiler.compile(source) template.name shouldBe "contractName" template.description shouldBe "This is my contracts description. Here is another line describing what it does in more detail." @@ -73,16 +73,137 @@ class SigmaTemplateCompilerTest extends AnyPropSpec with ScalaCheckPropertyCheck ) template.constTypes should contain theSameElementsInOrderAs Seq(SInt, SString) template.constValues.get should contain theSameElementsInOrderAs IndexedSeq( - Some(IntConstant(5).asWrappedType), - Some(StringConstant("default string").asWrappedType) + Some(5), + Some("default string") + ) + + checkEquals(template.expressionTree, BoolToSigmaProp(TrueLeaf)) + } + + property("uses default value from parameter definition") { + val source = + """/**/ + |@contract def contractName(p: Boolean = true) = sigmaProp(p && HEIGHT > 1000) + |""".stripMargin + val template = templateCompiler.compile(source) + + template.parameters should contain theSameElementsInOrderAs IndexedSeq( + Parameter("p", "", 0) + ) + template.constTypes should contain theSameElementsInOrderAs Seq(SBoolean) + template.constValues.get should contain theSameElementsInOrderAs IndexedSeq( + Some(true) + ) + + val expectedExpr = BoolToSigmaProp(BinAnd(ConstantPlaceholder(0, SBoolean), GT(Height, IntConstant(1000)))) + checkEquals(template.expressionTree, expectedExpr) + + val expectedTree = new ErgoTree( + HeaderType @@ 26.toByte, // use ErgoTreeUtils to get explanation + Vector(TrueLeaf), + Right(BoolToSigmaProp(BinAnd(ConstantPlaceholder(0, SBoolean), GT(Height, IntConstant(1000)))))) + + expectedTree.version shouldBe VersionContext.JitActivationVersion + expectedTree.hasSize shouldBe true + expectedTree.isConstantSegregation shouldBe true + + // apply using default values declared in the parameters + checkEquals( + template.applyTemplate( + version = Some(VersionContext.JitActivationVersion), + paramValues = Map.empty + ), + expectedTree + ) + + // apply overriding the default values + checkEquals( + template.applyTemplate( + version = Some(VersionContext.JitActivationVersion), + paramValues = Map("p" -> FalseLeaf) + ), + expectedTree.copy(constants = Vector(FalseLeaf)) + ) + } + + property("uses given environment when provided (overriding default value)") { + val source = + """/**/ + |@contract def contractName(low: Int = 0, high: Int) = sigmaProp(low < HEIGHT && HEIGHT < high) + |""".stripMargin + val template = templateCompiler.compile(source) + + template.parameters should contain theSameElementsInOrderAs IndexedSeq( + Parameter("low", "", 0), + Parameter("high", "", 1) + ) + template.constTypes should contain theSameElementsInOrderAs Seq(SInt, SInt) + // check parsed default values + template.constValues.get should contain theSameElementsInOrderAs IndexedSeq( + Some(0), + None + ) + checkEquals( + template.expressionTree, + BoolToSigmaProp( + BinAnd( + LT(ConstantPlaceholder(0, SInt), Height), + LT(Height, ConstantPlaceholder(1, SInt)) + ) + ) ) - val sigmaCompiler = new SigmaCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) - implicit val ir = new CompiletimeIRContext - val result = sigmaCompiler.compile(Map.empty, "sigmaProp(true)") + // incomplete application (missing `high` parameter) + assertExceptionThrown( + template.applyTemplate( + version = Some(VersionContext.JitActivationVersion), + paramValues = Map.empty + ), + exceptionLike[IllegalArgumentException]( + "requirement failed: value for parameter `high` was not provided while it does not have a default value.") + ) + + val expectedTree = new ErgoTree( + HeaderType @@ 26.toByte, + Vector(IntConstant(0), IntConstant(100)), + Right( + BoolToSigmaProp( + BinAnd(LT(ConstantPlaceholder(0, SInt), Height), LT(Height, ConstantPlaceholder(1, SInt))) + ) + ) + ) + + // apply providing the parameter without default value + checkEquals( + template.applyTemplate( + version = Some(VersionContext.JitActivationVersion), + paramValues = Map("high" -> IntConstant(100)) + ), + expectedTree + ) - template.expressionTree shouldBe result.buildTree + // apply providing all parameters overriding the default values + checkEquals( + template.applyTemplate( + version = Some(VersionContext.JitActivationVersion), + paramValues = Map("low" -> IntConstant(10), "high" -> IntConstant(100)) + ), + expectedTree.copy(constants = Vector(IntConstant(10), IntConstant(100))) + ) } + + property("fails when the parameter is not provided") { + // NOTE: parameter `condition` is not provided */ + val source = + """/**/ + |@contract def contractName(low: Int = 0, high: Int) = sigmaProp(low < HEIGHT && HEIGHT < high) && condition + |""".stripMargin + assertExceptionThrown( + templateCompiler.compile(source), + exceptionLike[TyperException]("Cannot assign type for variable 'condition' because it is not found in env") + ) + } + } diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 99ad2ae908..c86cb11afc 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -5,7 +5,7 @@ import org.ergoplatform._ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigma.Colls +import sigma.{Colls, VersionContext} import sigma.ast.SCollection._ import sigma.ast._ import sigma.ast.syntax.{SValue, SigmaPropValue, SigmaPropValueOps} @@ -19,7 +19,10 @@ import sigmastate.lang.parsers.ParserException import sigma.serialization.ErgoTreeSerializer import sigma.serialization.generators.ObjectGenerators import sigma.ast.Select +import sigma.compiler.phases.{SigmaBinder, SigmaTyper} import sigma.exceptions.TyperException +import sigmastate.exceptions.MethodNotFound +import sigmastate.helpers.SigmaPPrint class SigmaTyperTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with LangTests with ObjectGenerators { @@ -27,6 +30,7 @@ class SigmaTyperTest extends AnyPropSpec private val predefFuncRegistry = new PredefinedFuncRegistry(StdSigmaBuilder) import predefFuncRegistry._ + /** Checks that parsing, binding and typing of `x` results in the given expected value. */ def typecheck(env: ScriptEnv, x: String, expected: SValue = null): SType = { try { val builder = TransformingSigmaBuilder @@ -34,10 +38,16 @@ class SigmaTyperTest extends AnyPropSpec val predefinedFuncRegistry = new PredefinedFuncRegistry(builder) val binder = new SigmaBinder(env, builder, TestnetNetworkPrefix, predefinedFuncRegistry) val bound = binder.bind(parsed) - val typer = new SigmaTyper(builder, predefinedFuncRegistry, lowerMethodCalls = true) + val typeEnv = env.collect { case (k, v: SType) => k -> v } + val typer = new SigmaTyper(builder, predefinedFuncRegistry, typeEnv, lowerMethodCalls = true) val typed = typer.typecheck(bound) assertSrcCtxForAllNodes(typed) - if (expected != null) typed shouldBe expected + if (expected != null) { + if (expected != typed) { + SigmaPPrint.pprintln(typed, width = 100) + } + typed shouldBe expected + } typed.tpe } catch { case e: Exception => throw e @@ -51,7 +61,8 @@ class SigmaTyperTest extends AnyPropSpec val predefinedFuncRegistry = new PredefinedFuncRegistry(builder) val binder = new SigmaBinder(env, builder, TestnetNetworkPrefix, predefinedFuncRegistry) val bound = binder.bind(parsed) - val typer = new SigmaTyper(builder, predefinedFuncRegistry, lowerMethodCalls = true) + val typeEnv = env.collect { case (k, v: SType) => k -> v } + val typer = new SigmaTyper(builder, predefinedFuncRegistry, typeEnv, lowerMethodCalls = true) typer.typecheck(bound) }, { case te: TyperException => @@ -113,6 +124,7 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "min(HEIGHT, INPUTS.size)") shouldBe SInt typecheck(env, "max(1, 2)") shouldBe SInt typecheck(env, "max(1L, 2)") shouldBe SLong + typecheck(env, """bigInt("1111")""") shouldBe SBigInt typecheck(env, """fromBase16("1111")""") shouldBe SByteArray typecheck(env, """fromBase58("111")""") shouldBe SByteArray typecheck(env, """fromBase64("111")""") shouldBe SByteArray @@ -507,6 +519,48 @@ class SigmaTyperTest extends AnyPropSpec typefail(env, "1.toSuperBigInteger", 1, 1) } + property("toBytes method for numeric types") { + typecheck(env, "1.toByte.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + Select(IntConstant(1), "toByte", Some(SByte)), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SByte)), + Vector(), + Map() + )) shouldBe SByteArray + + typecheck(env, "1.toShort.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + Select(IntConstant(1), "toShort", Some(SShort)), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SShort)), + Vector(), + Map() + )) shouldBe SByteArray + + typecheck(env, "1.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + IntConstant(1), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SInt)), + Vector(), + Map() + )) shouldBe SByteArray + + typecheck(env, "1.toLong.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + Select(IntConstant(1), "toLong", Some(SLong)), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SLong)), + Vector(), + Map() + )) shouldBe SByteArray + + typecheck(env, "1.toBigInt.toBytes", + expected = MethodCall.typed[Value[SCollection[SByte.type]]]( + Select(IntConstant(1), "toBigInt", Some(SBigInt)), + SNumericTypeMethods.ToBytesMethod.withConcreteTypes(Map(STypeVar("TNum") -> SBigInt)), + Vector(), + Map() + )) shouldBe SByteArray + } + property("string concat") { typecheck(env, """ "a" + "b" """) shouldBe SString } @@ -629,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 } @@ -659,4 +723,36 @@ 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/serialization/ErgoTreeSerializerSpecification.scala b/sc/shared/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala index e20d8a6d1e..f7129a7723 100644 --- a/sc/shared/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala @@ -10,9 +10,9 @@ import sigma.util.Extensions.SigmaPropOps import sigma.validation.ValidationException import ErgoTree.EmptyConstants import ErgoTree.HeaderType +import sigma.compiler.ir.IRContext import sigma.eval.Extensions.SigmaBooleanOps import sigmastate._ -import sigmastate.eval.IRContext import sigmastate.helpers.CompilerTestingCommons import sigma.serialization.ErgoTreeSerializer.DefaultSerializer diff --git a/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala index 566bf5bd58..ca7f9dd7c7 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala @@ -9,20 +9,19 @@ import scorex.crypto.hash.{Blake2b256, Digest32} import sigma.ast.SCollection.SByteArray import sigma.ast._ import sigmastate._ -import sigmastate.eval.IRContext import sigmastate.eval._ import sigma.Extensions.ArrayOps import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp import sigma.ast.syntax._ -import sigma.Coll +import sigma.{AvlTree, Coll, Colls, Context} import sigma.ast.SAvlTree import sigma.ast.syntax.{GetVarByteArray, OptionValueOps} +import sigma.compiler.ir.IRContext import sigma.data.{AvlTreeData, AvlTreeFlags, CSigmaProp, TrivialProp} import sigma.eval.SigmaDsl import sigma.interpreter.ProverResult -import sigma.{AvlTree, Context} import sigmastate.eval.Extensions.AvlTreeOps @@ -30,7 +29,7 @@ class AVLTreeScriptsSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { suite => import org.ergoplatform.dsl.AvlTreeHelpers._ lazy val spec = TestContractSpec(suite)(new TestingIRContext) - lazy val prover = spec.ProvingParty("Alice") + lazy val prover = spec.ProvingParty("Alice") private implicit lazy val IR: IRContext = spec.IR private val reg1 = ErgoBox.nonMandatoryRegisters(0) @@ -39,9 +38,6 @@ class AVLTreeScriptsSpecification extends CompilerTestingCommons def genKey(str: String): ADKey = ADKey @@@ Blake2b256("key: " + str) def genValue(str: String): ADValue = ADValue @@@ Blake2b256("val: " + str) - val inKey = genKey("init key") - val inValue = genValue("init value") - property("avl tree - removals") { case class AvlTreeContract[Spec <: ContractSpec] (ops: Coll[Coll[Byte]], proof: Coll[Byte], prover: Spec#ProvingParty) @@ -204,7 +200,7 @@ class AVLTreeScriptsSpecification extends CompilerTestingCommons val treeData = new AvlTreeData(digest.toColl, AvlTreeFlags.ReadOnly, 32, None) - val env = Map("key" -> key, "proof" -> proof) + val env = Map("key" -> Colls.fromArray(key), "proof" -> Colls.fromArray(proof)) val prop = compile(env, """SELF.R4[AvlTree].get.contains(key, proof)""").asBoolValue.toSigmaProp val propExp = IR.builder.mkMethodCall( @@ -374,7 +370,7 @@ class AVLTreeScriptsSpecification extends CompilerTestingCommons val treeData = SigmaDsl.avlTree(new AvlTreeData(digest.toColl, AvlTreeFlags.ReadOnly, 32, None)) val env = Map("proofId" -> proofId.toLong, - "keys" -> ConcreteCollection.fromItems(genKey("3"), genKey("4"), genKey("5"))) + "keys" -> Colls.fromItems(Colls.fromArray(genKey("3")), Colls.fromArray(genKey("4")), Colls.fromArray(genKey("5")))) val prop = compile(env, """{ | val tree = SELF.R4[AvlTree].get diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 346ad69e1a..2194414ab0 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -2,26 +2,58 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ +import org.scalatest.Assertion +import scorex.crypto.authds.{ADKey, ADValue} +import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert} +import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.util.ByteArrayBuilder +import scorex.util.encode.Base16 +import org.scalatest.Assertion +import scorex.util.encode.Base16 +import scorex.utils.Ints +import scorex.util.serialization.VLQByteBufferWriter +import scorex.utils.Longs +import sigma.{Colls, SigmaTestingData} import sigma.Extensions.ArrayOps +import sigma.{SigmaTestingData, VersionContext} +import sigma.VersionContext.{V6SoftForkVersion, withVersions} +import sigma.VersionContext.V6SoftForkVersion +import sigma.VersionContext +import sigma.GroupElement +import sigma.VersionContext.V6SoftForkVersion import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps -import sigma.data.{AvlTreeData, CAnyValue} +import sigma.data.{AvlTreeData, CAnyValue, CBigInt, CGroupElement, CSigmaDslBuilder} +import sigma.data.{AvlTreeData, CAnyValue, CHeader, CSigmaDslBuilder} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAND, CAnyValue, CHeader, CSigmaDslBuilder, CSigmaProp} import sigma.util.StringUtil._ 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, SerializerException} +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.validation.ValidationException +import sigma.util.NBitsUtils +import sigma.serialization.{DataSerializer, ErgoTreeSerializer, SigmaByteWriter} +import sigma.util.Extensions +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 { @@ -42,7 +74,9 @@ class BasicOpsSpecification extends CompilerTestingCommons val booleanVar = 9.toByte val propVar1 = 10.toByte val propVar2 = 11.toByte - val lastExtVar = propVar2 + val propVar3 = 12.toByte + val propBytesVar1 = 13.toByte + val lastExtVar = propBytesVar1 val ext: Seq[VarBinding] = Seq( (intVar1, IntConstant(1)), (intVar2, IntConstant(2)), @@ -59,7 +93,8 @@ class BasicOpsSpecification extends CompilerTestingCommons "proofVar2" -> CAnyValue(propVar2) ) - def test(name: String, env: ScriptEnv, + def test(name: String, + env: ScriptEnv, ext: Seq[VarBinding], script: String, propExp: SValue, @@ -70,91 +105,2335 @@ class BasicOpsSpecification extends CompilerTestingCommons override lazy val contextExtenders: Map[Byte, EvaluatedValue[_ <: SType]] = { val p1 = dlogSecrets(0).publicImage val p2 = dlogSecrets(1).publicImage - (ext ++ Seq(propVar1 -> SigmaPropConstant(p1), propVar2 -> SigmaPropConstant(p2))).toMap + val d1 = dhSecrets(0).publicImage + + (ext ++ Seq( + propVar1 -> SigmaPropConstant(p1), + propVar2 -> SigmaPropConstant(p2), + propVar3 -> SigmaPropConstant(CSigmaProp(CAND(Seq(p1, d1)))), + propBytesVar1 -> ByteArrayConstant(CSigmaProp(CAND(Seq(p1, d1))).propBytes) + )).toMap + } + override val evalSettings: EvalSettings = DefaultEvalSettings.copy( + isMeasureOperationTime = true, + isDebug = true, + isTestRun = testExceededCost) + } + + val prop = if (script.isNullOrEmpty) { + // for some testcases the script cannot be compiled (i.e. the corresponding syntax + // is not supported by ErgoScript Compiler) + // In such cases we use expected property as the property to test + propExp.asSigmaProp + } else { + // compile with the latest compiler version, to get validation exception during execution, not compilation error + withVersions(VersionContext.MaxSupportedScriptVersion, ergoTreeVersionInTests) { + compile(env, script).asBoolValue.toSigmaProp } + } + + if (propExp != null) + prop shouldBe propExp + + val tree = ErgoTree.fromProposition(ergoTreeHeaderInTests, prop) + val p3 = prover.dlogSecrets(2).publicImage + val boxToSpend = testBox(10, tree, + additionalRegisters = additionalRegistersOpt.getOrElse(Map( + reg1 -> SigmaPropConstant(p3), + reg2 -> IntConstant(1)) + ), + creationHeight = 5) + + val newBox1 = testBox(10, tree, creationHeight = 0, boxIndex = 0, additionalRegisters = Map( + reg1 -> IntConstant(1), + reg2 -> IntConstant(10))) + 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), + spendingTransaction = tx, self = boxToSpend, activatedVersionInTests) + + val pr = prover.prove(env + (ScriptNameProp -> s"${name}_prove"), tree, ctx, fakeMessage).getOrThrow + + val ctxExt = ctx.withExtension(pr.extension) + + val testVerifier = new ErgoLikeTestInterpreter + + if (!onlyPositive) { + // test negative case + testVerifier.verify( + env + (ScriptNameProp -> s"${name}_verify"), + tree, ctx, pr.proof, fakeMessage) + .map(_._1) + .getOrElse(false) shouldBe false //context w/out extensions + } + + // this is helper verifier which respects the requested parameter testExceededCost for + // some test cases (when testExceededCost == false) it emit message in the console + // instead of failing the test and the failing case is tested separately in that case + val flexVerifier = new ErgoLikeTestInterpreter { override val evalSettings: EvalSettings = DefaultEvalSettings.copy( isMeasureOperationTime = true, isDebug = true, isTestRun = testExceededCost) } + val verifyEnv = env + (ScriptNameProp -> s"${name}_verify_ext") + 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 vectorBase = params.getVectorBase(); + PeddersenBase base = params.getBase(); + int n = vectorBase.getGs().size(); + T a = proof.getaI(); + T s = proof.getS(); + + BigInteger q = params.getGroup().groupOrder(); + BigInteger y = ProofUtils.computeChallenge(q, input, a, s); + + FieldVector ys = FieldVector.from(VectorX.iterate(n, BigInteger.ONE, y::multiply), q); + + BigInteger z = ProofUtils.challengeFromints(q, y); + + BigInteger zSquared = z.pow(2).mod(q); + BigInteger zCubed = z.pow(3).mod(q); + + FieldVector twos = FieldVector.from(VectorX.iterate(n, BigInteger.ONE, bi -> bi.shiftLeft(1)), q); + FieldVector twoTimesZSquared = twos.times(zSquared); + GeneratorVector tCommits = proof.gettCommits(); + + BigInteger x = ProofUtils.computeChallenge(q,z, tCommits); + + BigInteger tauX = proof.getTauX(); + BigInteger mu = proof.getMu(); + BigInteger t = proof.getT(); + BigInteger k = ys.sum().multiply(z.subtract(zSquared)).subtract(zCubed.shiftLeft(n).subtract(zCubed)); + T lhs = base.commit(t.subtract(k), tauX); + T rhs = tCommits.commit(Arrays.asList(x, x.pow(2))).add(input.multiply(zSquared)); + System.out.println("y " + y); + System.out.println("z " + z); + + System.out.println("x " + x);pow + equal(lhs, rhs, "Polynomial identity check failed, LHS: %s, RHS %s"); + BigInteger uChallenge = ProofUtils.challengeFromints(q, x, tauX, mu, t); + System.out.println("u " + uChallenge); + T u = base.g.multiply(uChallenge); + GeneratorVector hs = vectorBase.getHs(); + GeneratorVector gs = vectorBase.getGs(); + GeneratorVector hPrimes = hs.haddamard(ys.invert()); + FieldVector hExp = ys.times(z).add(twoTimesZSquared); + T P = a.add(s.multiply(x)).add(gs.sum().multiply(z.negate())).add(hPrimes.commit(hExp)).subtract(base.h.multiply(mu)).add(u.multiply(t)); + VectorBase primeBase = new VectorBase<>(gs, hPrimes, u); + // System.out.println("PVerify "+P.normalize()); + // System.out.println("XVerify" +x); + // System.out.println("YVerify" +y); + // System.out.println("ZVerify" +z); + // System.out.println("uVerify" +u); + + EfficientInnerProductVerifier verifier = new EfficientInnerProductVerifier<>(); + verifier.verify(primeBase, P, proof.getProductProof(), uChallenge); + */ + + val g = CGroupElement(SecP256K1Group.generator) + + def rangeTest() = { + test("range proof", env, ext, + s"""{ + | // range proof input data + | val input: GroupElement = getVar[GroupElement](0).get + | + | // proof data + | val ai: GroupElement = getVar[GroupElement](1).get + | val s: GroupElement = getVar[GroupElement](2).get + | val tCommits: Coll[GroupElement] = getVar[Coll[GroupElement]](3).get + | val tauX: UnsignedBigInt = getVar[UnsignedBigInt](4).get + | val mu: UnsignedBigInt = getVar[UnsignedBigInt](5).get + | val t: UnsignedBigInt = getVar[UnsignedBigInt](6).get + | + | // inner product proof + | val L: Coll[GroupElement] = getVar[Coll[GroupElement]](7).get + | val R: Coll[GroupElement] = getVar[Coll[GroupElement]](8)).get + | val a: UnsignedBigInt = getVar[UnsignedBigInt](9).get + | val b: UnsignedBigInt = getVar[UnsignedBigInt](10).get + | + | // proof verification: + | val Q = lWeights.size + | + | val q // group order = getVar[UnsignedBigInt](11).get + | + | val yBytes = sha256(q.toBytes ++ input.getEncoded ++ aI.getEncoded ++ s.getEncoded) + | + | val y = byteArrayToBigInt(yBytes).toUnsignedMod(q) + | + | val ys = + | + | val z = byteArrayToBigInt(sha256(q.toBytes ++ yBytes)).toUnsignedMod(q) + | val zSquared = z.multiplyMod(z, q) + | val zCubed = zSquared.multiplyMod(z, q) + | + | // def times() : // todo: implement + | + | // ops needed: modInverse, mod ops + | + | sigmaProp(zCubed > 0) + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy rangeTest() + } else { + rangeTest() + } + } + + // todo: complete + ignore("Bulletproof verification for a circuit proof") { + + val g = CGroupElement(SecP256K1Group.generator) + + def circuitTest() = { + test("schnorr", env, ext, + s"""{ + | // circuit data - should be provided via data input likely + | val lWeights = Coll[UnsignedBigInt] + | val rWeights: Coll[UnsignedBigInt] + | val oWeights: Coll[UnsignedBigInt] + | val commitmentWeights: Coll[UnsignedBigInt] + | + | val cs: Coll[UnsignedBigInt] + | val commitments: Coll[GroupElement] + | + | // proof data + | val ai: GroupElement + | val ao: GroupElement + | val s: GroupElement + | val tCommits: Coll[GroupElement] + | val tauX: UnsignedBigInt + | val mu: UnsignedBigInt + | val t: UnsignedBigInt + | + | // inner product proof + | val L: Coll[GroupElement] + | val R: Coll[GroupElement] + | val a: UnsignedBigInt + | val b: UnsignedBigInt + | + | // proof verification: + | val Q = lWeights.size + | + | val q // group order + | + | val yBytes = sha256(q.toBytes ++ aI.getEncoded ++ aO.getEncoded ++ s.getEncoded) + | + | val y = byteArrayToBigInt(yBytes) // should be to unsigned bigint + | + | val z = byteArrayToBigInt(sha256(y ++ q.toBytes)) + | + | + | + | sigmaProp(properSignature) + |}""".stripMargin, + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy circuitTest() + } else { + circuitTest() + } + } + + property("Byte.toBits") { + val customExt = Map( + 1.toByte -> ByteConstant(1) + ).toSeq + def toBitsTest() = test("Byte.toBits", env, customExt, + """{ + | val b = getVar[Byte](1).get + | b.toBits == Coll(false, false, false, false, false, false, false, true) + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(toBitsTest()) + } + } + + property("Long.toBits") { + val customExt = Map( + 1.toByte -> LongConstant(1) + ).toSeq + def toBitsTest() = test("Long.toBits", env, customExt, + """{ + | val b = getVar[Long](1).get + | val ba = b.toBits + | + | // only rightmost bit is set + | ba.size == 64 && ba(63) == true && ba.slice(0, 63).forall({ (b: Boolean ) => b == false }) + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(toBitsTest()) + } + } + + property("BigInt.toBits") { + def toBitsTest() = test("BigInt.toBits", env, ext, + s"""{ + | val b = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val ba = b.toBits + | ba.size == 256 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(toBitsTest()) + } + } + + + property("UnsignedBigInt.toBits") { + def toBitsTest() = test("UnsignedBigInt.toBits", env, ext, + s"""{ + | val b = unsignedBigInt("${CryptoConstants.groupOrder}") + | val ba = b.toBits + | ba.size == 256 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[ValidationException] shouldBe thrownBy(toBitsTest()) + } + } + + property("UnsignedBigInt.toBits - 2") { + def toBitsTest() = test("UnsignedBigInt.toBits", env, ext, + s"""{ + | val b = unsignedBigInt("5") + | val ba = b.toBits + | ba.size == 8 && ba == Coll(false, false, false, false, false, true, false, true) + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBitsTest() + } else { + an[ValidationException] shouldBe thrownBy(toBitsTest()) + } + } + + + property("BigInt.bitwiseInverse") { + def bitwiseInverseTest(): Assertion = test("BigInt.bitwiseInverse", env, ext, + s"""{ + | val b = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val bi = b.bitwiseInverse + | bi.bitwiseInverse == b + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseInverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) + } + } + + property("UnsignedBigInt.bitwiseInverse") { + def bitwiseInverseTest(): Assertion = test("UnsignedBigInt.bitwiseInverse", env, ext, + s"""{ + | val b = unsignedBigInt("${CryptoConstants.groupOrder}") + | val bi = b.bitwiseInverse + | bi.bitwiseInverse == b + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseInverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) + } + } + + + property("Byte.bitwiseInverse") { + def bitwiseInverseTest(): Assertion = test("Byte.bitwiseInverse", env, ext, + s"""{ + | val b = (126 + 1).toByte // max byte value + | b.bitwiseInverse == (-128).toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseInverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) + } + } + + property("Long.bitwiseInverse") { + val customExt = Map( + 1.toByte -> LongConstant(9223372036854775807L) + ).toSeq + def bitwiseInverseTest(): Assertion = test("Long.bitwiseInverse", env, customExt, + s"""{ + | val l = getVar[Long](1).get + | val lb = l.bitwiseInverse + | lb.bitwiseInverse == l + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseInverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) + } + } + + + property("Byte.bitwiseOr") { + val customExt = Map( + 1.toByte -> ByteConstant(127) + ).toSeq + def bitwiseOrTest(): Assertion = test("Byte.bitwiseOrTest", env, customExt, + s"""{ + | val x = getVar[Byte](1).get + | val y = (-128).toByte + | x.bitwiseOr(y) == -1 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseOrTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) + } + } + + property("BigInt.bitwiseOr") { + def bitwiseOrTest(): Assertion = test("BigInt.bitwiseOr", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | x.bitwiseOr(x) == x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseOrTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) + } + } + + property("UnsignedBigInt.bitwiseOr") { + def bitwiseOrTest(): Assertion = test("BigInt.bitwiseOr", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | x.bitwiseOr(x) == x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseOrTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) + } + } + + property("UnsignedBigInt.bitwiseOr - 2") { + def bitwiseOrTest(): Assertion = test("BigInt.bitwiseOr", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = unsignedBigInt("121") + | val z = unsignedBigInt("115792089237316195423570985008687907852837564279074904382605163141518161494393") + | x.bitwiseOr(y) == z && y.bitwiseOr(x) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseOrTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) + } + } + + property("BigInt.bitwiseAnd") { + def bitwiseAndTest(): Assertion = test("BigInt.bitwiseAnd", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val y = 0.toBigInt + | x.bitwiseAnd(y) == y + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) + } + } + + property("UnsignedBigInt.bitwiseAnd") { + def bitwiseAndTest(): Assertion = test("UnsignedBigInt.bitwiseAnd", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = 0.toBigInt.toUnsigned + | x.bitwiseAnd(y) == y + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) + } + } + + property("UnsignedBigInt.bitwiseAnd - 2") { + def bitwiseAndTest(): Assertion = test("UnsignedBigInt.bitwiseAnd", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = unsignedBigInt("1157920892373161954235709850086879078528375642790749043826051631415181614337") + | val z = unsignedBigInt("1157920892373161954235709850086879078522970439492889181512311797126516834561") + | + | // cross-checked with wolfram alpha + | x.bitwiseAnd(y) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) + } + } + + property("Short.bitwiseAnd") { + val customExt = Map( + 1.toByte -> ShortConstant(32767) + ).toSeq + def bitwiseAndTest(): Assertion = test("Short.bitwiseAnd", env, customExt, + s"""{ + | val x = getVar[Short](1).get + | val y = (-32768).toShort + | x.bitwiseAnd(y) == 0 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) + } + } + + property("Short.bitwiseXor") { + val customExt = Map( + 1.toByte -> ShortConstant(32767) + ).toSeq + def bitwiseXorTest(): Assertion = test("Short.bitwiseXor", env, customExt, + s"""{ + | val x = getVar[Short](1).get + | val y = (-32768).toShort + | x.bitwiseXor(y) == -1 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseXorTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseXorTest()) + } + } + + property("UnsignedBigInt.bitwiseXor") { + def bitwiseAndTest(): Assertion = test("UnsignedBigInt.bitwiseXor", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = unsignedBigInt("1157920892373161954235709850086879078528375642790749043826051631415181614337") + | val z = unsignedBigInt("114634168344943033469335275158601028774319999042879875063406591178680309439552") + | + | // cross-checked with wolfram alpha + | x.bitwiseXor(y) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + bitwiseAndTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) + } + } + + property("Byte.shiftLeft") { + def shiftLeftTest(): Assertion = test("Byte.shiftLeft", env, ext, + s"""{ + | val x = 4.toByte + | val y = 2 + | x.shiftLeft(y) == 16.toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftLeftTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("Byte.shiftLeft - over limit") { + def shiftLeftTest(): Assertion = test("Byte.shiftLeft2", env, ext, + s"""{ + | val x = 4.toByte + | val y = 2222 + | x.shiftLeft(y) == 0 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[IllegalArgumentException] shouldBe thrownBy(shiftLeftTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("Byte.shiftLeft - over limit 2") { + def shiftLeftTest(): Assertion = test("Byte.shiftLeft2", env, ext, + s"""{ + | val x = (-128).toByte + | val y = 1 + | x.shiftLeft(y) == 0 + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftLeftTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("BigInt.shiftLeft") { + def shiftLeftTest(): Assertion = test("BigInt.shiftLeft", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | val y = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | x.shiftLeft(2) == y + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftLeftTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("UnsignedBigInt.shiftLeft") { + def shiftLeftTest(): Assertion = test("UnsignedBigInt.shiftLeft", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | val y = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | x.shiftLeft(2) == y + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftLeftTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("UnsignedBigInt.shiftLeft over limits") { + def shiftLeftTest(): Assertion = test("UnsignedBigInt.shiftLeft", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | x.shiftLeft(1) > x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[ArithmeticException] shouldBe thrownBy(shiftLeftTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + + + property("UnsignedBigInt.shiftLeft - neg shift") { + def shiftLeftTest(): Assertion = test("UnsignedBigInt.shiftLeft", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | x.shiftLeft(-1) > x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[java.lang.IllegalArgumentException] shouldBe thrownBy(shiftLeftTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("BigInt.shiftLeft over limits") { + def shiftLeftTest(): Assertion = test("BigInt.shiftLeft", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | x.shiftLeft(1) > x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[ArithmeticException] shouldBe thrownBy(shiftLeftTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) + } + } + + property("Byte.shiftRight") { + def shiftRightTest(): Assertion = test("Byte.shiftRight", env, ext, + s"""{ + | val x = 8.toByte + | val y = 2 + | x.shiftRight(y) == 2.toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + + property("Byte.shiftRight - neg") { + def shiftRightTest(): Assertion = test("Byte.shiftRight", env, ext, + s"""{ + | val x = (-8).toByte + | val y = 2 + | x.shiftRight(y) == (-2).toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + + property("Byte.shiftRight - neg - neg shift") { + def shiftRightTest(): Assertion = test("Byte.shiftRight", env, ext, + s"""{ + | val x = (-8).toByte + | val y = -2 + | x.shiftRight(y) == (-1).toByte + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + + + property("Long.shiftRight - neg") { + def shiftRightTest(): Assertion = test("Long.shiftRight", env, ext, + s"""{ + | val x = -32L + | val y = 2 + | x.shiftRight(y) == -8L + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + + property("Long.shiftRight - neg - neg shift") { + def shiftRightTest(): Assertion = test("Long.shiftRight", env, ext, + s"""{ + | val x = -32L + | val y = -2 + | x.shiftRight(y) == -1L + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + + property("BigInt.shiftRight") { + def shiftRightTest(): Assertion = test("BigInt.shiftRight", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val y = 2 + | val z = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | x.shiftRight(y) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + + property("BigInt.shiftRight - neg shift") { + def shiftRightTest(): Assertion = test("BigInt.shiftRight", env, ext, + s"""{ + | val x = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val y = -2 + | val z = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | z.shiftRight(y) == x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + + property("UnsignedBigInt.shiftRight") { + def shiftRightTest(): Assertion = test("UnsignedBigInt.shiftRight", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder}") + | val y = 3 + | val z = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | x.shiftRight(y) == z + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + shiftRightTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + + property("UnsignedBigInt.shiftRight - neg shift") { + def shiftRightTest(): Assertion = test("UnsignedBigInt.shiftRight", env, ext, + s"""{ + | val x = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | val y = -2 + | val z = unsignedBigInt("${CryptoConstants.groupOrder.divide(new BigInteger("8"))}") + | z.shiftRight(y) == x + |}""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + an[java.lang.IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) + } + } + + property("getVarFromInput - invalid var") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 2).isDefined == false) }", + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + getVarTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy getVarTest() + } + } + + property("Coll.reverse"){ + def reverseTest() = test("reverse", env, ext, + """{ + | val c1 = Coll(1, 2, 3) + | val c2 = Coll(3, 2, 1) + | + | val b1 = Coll(INPUTS(0), OUTPUTS(0)) + | val b2 = Coll(OUTPUTS(0), INPUTS(0)) + | + | c1.reverse == c2 && b1.reverse == b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.distinct"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll(1, 2, 3, 3, 2) + | val c2 = Coll(3, 2, 1) + | + | val h1 = Coll(INPUTS(0), INPUTS(0)) + | val h2 = Coll(INPUTS(0)) + | + | c1.distinct.reverse == c2 && h1.distinct == h2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.startsWith"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll(1, 2, 3) + | val c2 = Coll(1, 2) + | val c3 = Coll(1, 3) + | val c4 = Coll[Int]() + | val c5 = Coll(1, 2, 3, 4) + | + | val b1 = c1.startsWith(c3) + | val b2 = c1.startsWith(c5) + | + | c1.startsWith(c2) && c1.startsWith(c4) && c1.startsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.startsWith - tuples"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll((1, 2), (3, 4), (5, 6)) + | val c2 = Coll((1, 2), (3, 4)) + | val c3 = Coll((1, 3)) + | val c4 = Coll[(Int, Int)]() + | val c5 = Coll((1, 2), (3, 4), (5, 6), (7, 8)) + | + | val b1 = c1.startsWith(c3) + | val b2 = c1.startsWith(c5) + | + | c1.startsWith(c2) && c1.startsWith(c4) && c1.startsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.endsWith"){ + def reverseTest() = test("distinct", env, ext, + """{ + | val c1 = Coll(1, 2, 3) + | val c2 = Coll(2, 3) + | val c3 = Coll(2, 2) + | val c4 = Coll[Int]() + | val c5 = Coll(1, 2, 3, 4) + | + | val b1 = c1.endsWith(c3) + | val b2 = c1.endsWith(c5) + | + | c1.endsWith(c2) && c1.endsWith(c4) && c1.endsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.endsWith - tuples"){ + def reverseTest() = test("endsWith tuples", env, ext, + """{ + | val c1 = Coll((1, 2), (2, 3)) + | val c2 = Coll((2, 3)) + | val c3 = Coll((2, 2)) + | val c4 = Coll[(Int, Int)]() + | val c5 = Coll((0, 2), (2, 3)) + | + | val b1 = c1.endsWith(c3) + | val b2 = c1.endsWith(c5) + | + | c1.endsWith(c2) && c1.endsWith(c4) && c1.endsWith(c1) && !b1 && !b2 + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + reverseTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) + } + } + + property("Coll.get"){ + def getTest() = test("get", env, ext, + """{ + | val c1 = Coll(1) + | val c2 = Coll[Int]() + | + | c2.get(0).getOrElse(c1.get(0).get) == c1.get(0).get + | }""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + getTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(getTest()) + } + } + + property("Global.fromBigEndianBytes - byte") { + def fromTest() = test("fromBigEndianBytes - byte", env, ext, + s"""{ + | val ba = Coll(5.toByte) + | Global.fromBigEndianBytes[Byte](ba) == 5 + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - short") { + def fromTest() = test("fromBigEndianBytes - short", env, ext, + s"""{ + | val ba = Coll(5.toByte, 5.toByte) + | Global.fromBigEndianBytes[Short](ba) != 0 + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - int") { + def fromTest() = test("fromBigEndianBytes - int", env, ext, + s"""{ + | val ba = fromBase16("${Base16.encode(Ints.toByteArray(Int.MaxValue))}") + | Global.fromBigEndianBytes[Int](ba) == ${Int.MaxValue} + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - long") { + def fromTest() = test("fromBigEndianBytes - long", env, ext, + s"""{ + | val l = 1088800L + | val ba = longToByteArray(l) + | Global.fromBigEndianBytes[Long](ba) == l + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - Long.toBytes") { + val customExt = Map( + 1.toByte -> LongConstant(1088800L) + ).toSeq + def fromTest() = test("fromBigEndianBytes - long", env, customExt, + s"""{ + | val l = getVar[Long](1).get + | val ba = l.toBytes + | Global.fromBigEndianBytes[Long](ba) == l + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) + } + } + + property("Global.fromBigEndianBytes - bigInt") { + val bi = new BigInteger("9785856985394593489356430476450674590674598659865986594859056865984690568904") + def fromTest() = test("fromBigEndianBytes - bigInt", env, ext, + s"""{ + | val ba = fromBase16("${Base16.encode(bi.toByteArray)}") + | Global.fromBigEndianBytes[BigInt](ba) == bigInt("$bi") + |} + |""".stripMargin, + null + ) + if(VersionContext.current.isV6SoftForkActivated) { + fromTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) + } + } + + property("Int.toBytes") { + val customExt = Map( + 1.toByte -> IntConstant(1) + ).toSeq + def toBytesTest() = test("Int.toBytes", env, customExt, + """{ + | val l = getVar[Int](1).get + | l.toBytes == Coll(0.toByte, 0.toByte, 0.toByte, 1.toByte) + | }""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) + } + } + + property("Int.toBits") { + val customExt = Map( + 1.toByte -> IntConstant(1477959696) + ).toSeq + def toBytesTest() = test("Int.toBytes", env, customExt, + """{ + | val l = getVar[Int](1).get + | l.toBits == Coll(false, true, false, true, true, false, false, false, false, false, false, true, false, true, true ,true, true, true, true, false, false, false, false, false, false, false, false, true, false, false, false, false) + | }""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) + } + } + + property("Byte.toBytes") { + val customExt = Map( + 1.toByte -> ByteConstant(10) + ).toSeq + def toBytesTest() = test("Byte.toBytes", env, customExt, + """{ + | val l = getVar[Byte](1).get + | l.toBytes == Coll(10.toByte) + | }""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) + } + } + + + property("BigInt.toBytes") { + def toBytesTest() = test("BigInt.toBytes", env, ext, + s"""{ + | val l = bigInt("${CryptoConstants.groupOrder.divide(new BigInteger("2"))}") + | l.toBytes.size == 32 + | }""".stripMargin, + null + ) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) + } + } + + property("UnsignedBigInt.toBytes") { + val script = s"""{ + | val l = unsignedBigInt("${CryptoConstants.groupOrder}") + | l.toBytes.size == 32 + | }""".stripMargin + + def toBytesTest() = test("UnsignedBigInt.toBytes", env, ext, script, null) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[ValidationException] shouldBe thrownBy(toBytesTest()) + } + } + + property("UnsignedBigInt.toBytes - 2") { + val script = s"""{ + | val l = unsignedBigInt("5") + | val bs = l.toBytes + | bs.size == 1 && bs == Coll(5.toByte) + | }""".stripMargin + + def toBytesTest() = test("UnsignedBigInt.toBytes", env, ext, script, null) + + if (VersionContext.current.isV6SoftForkActivated) { + toBytesTest() + } else { + an[ValidationException] shouldBe thrownBy(toBytesTest()) + } + } + + property("serialize - byte array") { + def deserTest() = test("serialize", env, ext, + s"""{ + val ba = fromBase16("c0ffee"); + Global.serialize(ba).size > ba.size + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("serialize - collection of boxes") { + def deserTest() = test("serialize", env, ext, + s"""{ + val boxes = INPUTS; + Global.serialize(boxes).size > 0 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("serialize - optional collection") { + def deserTest() = test("serialize", env, ext, + s"""{ + val opt = SELF.R1[Coll[Byte]]; + Global.serialize(opt).size > SELF.R1[Coll[Byte]].get.size + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("serialize(long) is producing different result from longToByteArray()") { + def deserTest() = test("serialize", env, ext, + s"""{ + val l = -1000L + val ba1 = Global.serialize(l); + val ba2 = longToByteArray(l) + ba1 != ba2 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + // the test shows that serialize(groupElement) is the same as groupElement.getEncoded + property("serialize - group element - equivalence with .getEncoded") { + val ge = Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b") + // val ba = Base16.encode(ge.getEncoded.toArray) + def deserTest() = test("serialize", env, Seq(21.toByte -> GroupElementConstant(ge)), + s"""{ + val ge = getVar[GroupElement](21).get + val ba = serialize(ge); + ba == ge.getEncoded + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + // the test shows that serialize(sigmaProp) is the same as sigmaProp.propBytes without first 2 bytes + property("serialize and .propBytes correspondence") { + def deserTest() = test("deserializeTo", env, ext, + s"""{ + val p1 = getVar[SigmaProp]($propVar1).get + val bytes = p1.propBytes + val ba = bytes.slice(2, bytes.size) + val ba2 = serialize(p1) + ba == ba2 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("serialize - collection of collection of headers") { + val td = new SigmaTestingData {} + val h1 = td.TestData.h1 + + val customExt = Seq(21.toByte -> HeaderConstant(h1)) + + def deserTest() = test("serialize", env, customExt, + s"""{ + val h1 = getVar[Header](21).get; + val c = Coll(Coll(h1)) + Global.serialize(c).size > 0 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + // todo: roundtrip tests with deserializeTo from https://github.com/ScorexFoundation/sigmastate-interpreter/pull/979 + + property("serialize - not spam") { + val customExt = Seq(21.toByte -> ShortArrayConstant((1 to Short.MaxValue).map(_.toShort).toArray), + 22.toByte -> ByteArrayConstant(Array.fill(1)(1.toByte))) + def deserTest() = test("serialize", env, customExt, + s"""{ + val indices = getVar[Coll[Short]](21).get + val base = getVar[Coll[Byte]](22).get + + def check(index:Short): Boolean = { serialize(base) != base } + indices.forall(check) + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an[sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } - val prop = if (script.isNullOrEmpty) { - // for some testcases the script cannot be compiled (i.e. the corresponding syntax - // 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 + property("serialize - spam attempt") { + val customExt = Seq(21.toByte -> ShortArrayConstant((1 to Short.MaxValue).map(_.toShort).toArray), + 22.toByte -> ByteArrayConstant(Array.fill(16000)(1.toByte))) + def deserTest() = test("serialize", env, customExt, + s"""{ + val indices = getVar[Coll[Short]](21).get + val base = getVar[Coll[Byte]](22).get - if (propExp != null) - prop shouldBe propExp + def check(index:Short): Boolean = { serialize(base) != base } + indices.forall(check) + }""", + null, + true + ) - val tree = ErgoTree.fromProposition(ergoTreeHeaderInTests, prop) - val p3 = prover.dlogSecrets(2).publicImage - val boxToSpend = testBox(10, tree, - additionalRegisters = additionalRegistersOpt.getOrElse(Map( - reg1 -> SigmaPropConstant(p3), - reg2 -> IntConstant(1)) - ), - creationHeight = 5) + if (activatedVersionInTests < V6SoftForkVersion) { + an[sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + // we have wrapped CostLimitException here + an[Exception] should be thrownBy deserTest() + } + } - val newBox1 = testBox(10, tree, creationHeight = 0, boxIndex = 0, additionalRegisters = Map( - reg1 -> IntConstant(1), - reg2 -> IntConstant(10))) - val tx = createTransaction(newBox1) + property("Lazy evaluation of default in Option.getOrElse") { + val customExt = Map ( + 1.toByte -> IntConstant(5) + ).toSeq + def optTest() = test("getOrElse", env, customExt, + """{ + | getVar[Int](1).getOrElse(getVar[Int](44).get) > 0 + |} + |""".stripMargin, + null + ) - val ctx = ErgoLikeContextTesting(currentHeight = 0, - lastBlockUtxoRoot = AvlTreeData.dummy, ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(boxToSpend), - spendingTransaction = tx, self = boxToSpend, activatedVersionInTests) + if (VersionContext.current.isV6SoftForkActivated) { + optTest() + } else { + assertExceptionThrown(optTest(), _.isInstanceOf[NoSuchElementException]) + } + } - val pr = prover.prove(env + (ScriptNameProp -> s"${name}_prove"), tree, ctx, fakeMessage).getOrThrow + property("Lazy evaluation of default in Coll.getOrElse") { + def optTest() = test("getOrElse", env, ext, + """{ + | val c = Coll[Int](1) + | c.getOrElse(0, getVar[Int](44).get) > 0 && + | c.getOrElse(1, c.getOrElse(0, getVar[Int](44).get)) > 0 + |} + |""".stripMargin, + null + ) - val ctxExt = ctx.withExtension(pr.extension) + if(VersionContext.current.isV6SoftForkActivated) { + optTest() + } else { + assertExceptionThrown(optTest(), _.isInstanceOf[NoSuchElementException]) + } + } - val testVerifier = new ErgoLikeTestInterpreter + property("checking Bitcoin PoW") { + val h = "00000020a82ff9c62e69a6cbed277b7f2a9ac9da3c7133a59a6305000000000000000000f6cd5708a6ba38d8501502b5b4e5b93627e8dcc9bd13991894c6e04ade262aa99582815c505b2e17479a751b" + val customExt = Map( + 1.toByte -> ByteArrayConstant(Base16.decode(h).get) + ).toSeq - if (!onlyPositive) { - // test negative case - testVerifier.verify( - env + (ScriptNameProp -> s"${name}_verify"), - tree, ctx, pr.proof, fakeMessage) - .map(_._1) - .getOrElse(false) shouldBe false //context w/out extensions + def powTest() = { + test("Prop1", env, customExt, + """{ + | def reverse4(bytes: Coll[Byte]): Coll[Byte] = { + | Coll(bytes(3), bytes(2), bytes(1), bytes(0)) + | } + | + | def reverse32(bytes: Coll[Byte]): Coll[Byte] = { + | Coll(bytes(31), bytes(30), bytes(29), bytes(28), bytes(27), bytes(26), bytes(25), bytes(24), + | bytes(23), bytes(22), bytes(21), bytes(20), bytes(19), bytes(18), bytes(17), bytes(16), + | bytes(15), bytes(14), bytes(13), bytes(12), bytes(11), bytes(10), bytes(9), bytes(8), + | bytes(7), bytes(6), bytes(5), bytes(4), bytes(3), bytes(2), bytes(1), bytes(0)) + | } + | + | val bitcoinHeader = getVar[Coll[Byte]](1).get + | val id = reverse32(sha256(sha256(bitcoinHeader))) + | val hit = byteArrayToBigInt(id) + | + | val nBitsBytes = reverse4(bitcoinHeader.slice(72, 76)) + | + | val pad = Coll[Byte](0.toByte, 0.toByte, 0.toByte, 0.toByte) + | + | val nbits = byteArrayToLong(pad ++ nBitsBytes) + | + | val difficulty = Global.decodeNbits(nbits) + | + | // <= according to https://bitcoin.stackexchange.com/a/105224 + | hit <= difficulty + |} + |""".stripMargin, + propExp = null, + testExceededCost = false + ) + } + if (activatedVersionInTests < V6SoftForkVersion) { + an[sigma.validation.ValidationException] should be thrownBy powTest() + } else { + powTest() } + } - // this is helper verifier which respects the requested parameter testExceededCost for - // some test cases (when testExceededCost == false) it emit message in the console - // instead of failing the test and the failing case is tested separately in that case - val flexVerifier = new ErgoLikeTestInterpreter { - override val evalSettings: EvalSettings = DefaultEvalSettings.copy( - isMeasureOperationTime = true, - isDebug = true, - isTestRun = testExceededCost) + property("decoding nbits from an Ergo block header") { + // bytes of real mainnet block header at height 1,398,482 + val headerBytes = "03720c6c532506a9bc4cceb6844efaa4096f66ba8a1d67ad7411ed1cb61dd5c008519fedd7d3b56984c43898f9e12aa866bc40e1ede2a6138940c9db2e20d633630024ee218d6a38392a8401c2b324b563e48d487c0f22dad940bd1c8a096084908ad71c77eec44ef083ba073deb8fa4a57b68d6296186c5d61e317849c760c16019b293cbfeb33288c9616f282288ee24c6d306577a76e7ac1cf87422ae0bdc6b44a449451a4e25070412d0d2ad55000000000295facb78290ac2b55f1453204d49df37be5bae9f185ed6704c1ba3ee372280c157221fa789df3f48" + val header1 = new CHeader(ErgoHeader.sigmaSerializer.fromBytes(Base16.decode(headerBytes).get)) + + val customExt = Seq(21.toByte -> HeaderConstant(header1)) + + def powTest() = { + test("Prop1", env, customExt, + """ + |{ + | val h = getVar[Header](21).get + | + | val n = h.nBits + | + | val target = Global.decodeNbits(n) + | + | target == bigInt("1146584469340160") + |} + |""".stripMargin, + propExp = null, + testExceededCost = false + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an[sigma.validation.ValidationException] should be thrownBy powTest() + } else { + powTest() } - val verifyEnv = env + (ScriptNameProp -> s"${name}_verify_ext") - flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } - property("Unit register") { - // TODO frontend: implement missing Unit support in compiler - // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 - test("R1", env, ext, - script = "", /* means cannot be compiled - the corresponding script is { SELF.R4[Unit].isDefined } */ - ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).isDefined.toSigmaProp, - additionalRegistersOpt = Some(Map( - reg1 -> UnitConstant.instance - )) + property("serialize - deserialize roundtrip") { + val customExt = Seq(21.toByte -> ShortArrayConstant((1 to 10).map(_.toShort).toArray)) + def deserTest() = test("serialize", env, customExt, + s"""{ + val src = getVar[Coll[Short]](21).get + val ba = serialize(src) + val restored = deserializeTo[Coll[Short]](ba) + src == restored + }""", + null, + true ) - test("R2", env, ext, - script = "", /* means cannot be compiled - the corresponding script is "{ SELF.R4[Unit].get == () }" */ - EQ(ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).get, UnitConstant.instance).toSigmaProp, - additionalRegistersOpt = Some(Map( - reg1 -> UnitConstant.instance - )) + if (activatedVersionInTests < V6SoftForkVersion) { + an[Exception] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - int") { + val value = -109253 + val w = new VLQByteBufferWriter(new ByteArrayBuilder()).putInt(value) + val bytes = Base16.encode(w.toBytes) + def deserTest() = {test("deserializeTo", env, ext, + s"""{ val ba = fromBase16("$bytes"); Global.deserializeTo[Int](ba) == $value }""", + null, + true + )} + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - coll[int]") { + val writer = new SigmaByteWriter(new VLQByteBufferWriter(new ByteArrayBuilder()), None, None, None) + DataSerializer.serialize[SCollection[SInt.type]](Colls.fromArray(Array(IntConstant(5).value)), SCollection(SInt), writer) + val bytes = Base16.encode(writer.toBytes) + + def deserTest() = { + test("deserializeTo", env, ext, + s"""{val ba = fromBase16("$bytes"); val coll = Global.deserializeTo[Coll[Int]](ba); coll(0) == 5 }""", + null, + true + ) + } + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - long") { + val value = -10009253L + + val w = new VLQByteBufferWriter(new ByteArrayBuilder()).putLong(value) + val bytes = Base16.encode(w.toBytes) + + def deserTest() = test("deserializeTo", env, ext, + s"""{ + val ba = fromBase16("$bytes"); + Global.deserializeTo[Long](ba) == ${value}L + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - box rountrip") { + def deserTest() = test("deserializeTo", env, ext, + s"""{ + val b = INPUTS(0); + val ba = b.bytes; + Global.deserializeTo[Box](ba) == b + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - bigint") { + + val bigInt = SecP256K1Group.q.divide(new BigInteger("512")) + val biBytes = bigInt.toByteArray + + val w = new VLQByteBufferWriter(new ByteArrayBuilder()).putUShort(biBytes.length) + val lengthBytes = w.toBytes + + val bytes = Base16.encode(lengthBytes ++ biBytes) + + def deserTest() = test("deserializeTo", env, ext, + s"""{ + val ba = fromBase16("$bytes"); + val b = Global.deserializeTo[BigInt](ba) + b == bigInt("${bigInt.toString}") + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - short") { + val s = (-1925).toShort + val w = new VLQByteBufferWriter(new ByteArrayBuilder()).putShort(s) + val bytes = Base16.encode(w.toBytes) + def deserTest() = test("deserializeTo", env, ext, + s"""{ + val ba = fromBase16("$bytes"); + Global.deserializeTo[Short](ba) == -1925 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - group element") { + val ge = Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b") + val ba = Base16.encode(ge.getEncoded.toArray) + def deserTest() = test("deserializeTo", env, Seq(21.toByte -> GroupElementConstant(ge)), + s"""{ + val ge = getVar[GroupElement](21).get + val ba = fromBase16("$ba"); + val ge2 = Global.deserializeTo[GroupElement](ba) + ba == ge2.getEncoded && ge == ge2 + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - sigmaprop roundtrip") { + + def deserTest() = test("deserializeTo", env, ext, + s"""{ + val bytes = getVar[Coll[Byte]]($propBytesVar1).get + val ba = bytes.slice(2, bytes.size) + val prop = Global.deserializeTo[SigmaProp](ba) + prop == getVar[SigmaProp]($propVar3).get && prop + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - .propBytes") { + def deserTest() = test("deserializeTo", env, ext, + s"""{ + val p1 = getVar[SigmaProp]($propVar1).get + val bytes = p1.propBytes + val ba = bytes.slice(2, bytes.size) + val prop = Global.deserializeTo[SigmaProp](ba) + prop == p1 && prop + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - sigmaprop roundtrip - non evaluated") { + + val script = GT(Height, IntConstant(-1)).toSigmaProp + val scriptBytes = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(ErgoTree.fromProposition(script)) + val customExt = Seq(21.toByte -> ByteArrayConstant(scriptBytes)) + + def deserTest() = test("deserializeTo", env, customExt, + s"""{ + val ba = getVar[Coll[Byte]](21).get + val prop = Global.deserializeTo[SigmaProp](ba) + prop + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an [sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + an [Exception] should be thrownBy deserTest() + } + } + + property("deserializeTo - avltree") { + val elements = Seq(123, 22) + val treeElements = elements.map(i => Longs.toByteArray(i)).map(s => (ADKey @@@ Blake2b256(s), ADValue @@ s)) + val avlProver = new BatchAVLProver[Digest32, Blake2b256.type](keyLength = 32, None) + treeElements.foreach(s => avlProver.performOneOperation(Insert(s._1, s._2))) + avlProver.generateProof() + val treeData = new AvlTreeData(avlProver.digest.toColl, AvlTreeFlags.ReadOnly, 32, None) + val treeBytes = AvlTreeData.serializer.toBytes(treeData) + + val customExt = Seq(21.toByte -> ByteArrayConstant(treeBytes)) + + def deserTest() = test("deserializeTo", env, customExt, + s"""{ + val ba = getVar[Coll[Byte]](21).get + val tree = Global.deserializeTo[AvlTree](ba) + tree.digest == fromBase16(${Base16.encode(treeData.digest.toArray)}) + && tree.enabledOperations == 0 + && tree.keyLength == 32 + && tree.valueLengthOpt.isEmpty + }""", + null, + true + ) + + an [Exception] should be thrownBy deserTest() + } + + property("deserializeTo - header") { + val td = new SigmaTestingData {} + val h1 = td.TestData.h1 + val headerBytes = h1.asInstanceOf[CHeader].ergoHeader.bytes + + val headerStateBytes = AvlTreeData.serializer.toBytes(Extensions.CoreAvlTreeOps(h1.stateRoot).toAvlTreeData) + val customExt = Seq(21.toByte -> ByteArrayConstant(headerBytes), 22.toByte -> ByteArrayConstant(headerStateBytes)) + + def deserTest() = test("deserializeTo", env, customExt, + s"""{ + val ba = getVar[Coll[Byte]](21).get + val header = Global.deserializeTo[Header](ba) + val ba2 = getVar[Coll[Byte]](22).get + val tree = Global.deserializeTo[AvlTree](ba2) + val id = fromBase16("${Base16.encode(h1.id.toArray)}") + header.height == ${h1.height} && header.stateRoot == tree && header.id == id + }""", + null, + true + ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an[sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } + } + + property("deserializeTo - header option") { + val td = new SigmaTestingData {} + val h1 = td.TestData.h1.asInstanceOf[CHeader].ergoHeader + val headerBytes = Colls.fromArray(Array(1.toByte) ++ h1.bytes) + + val customExt = Seq(21.toByte -> ByteArrayConstant(headerBytes)) + + def deserTest() = test("deserializeTo", env, customExt, + s"""{ + val ba = getVar[Coll[Byte]](21).get + val headerOpt = Global.deserializeTo[Option[Header]](ba) + val header = headerOpt.get + val id = fromBase16("${Base16.encode(h1.id.toArray)}") + header.height == ${h1.height} && header.id == id + }""", + null, + true ) + + if (activatedVersionInTests < V6SoftForkVersion) { + an[sigma.validation.ValidationException] should be thrownBy deserTest() + } else { + deserTest() + } } property("Relation operations") { @@ -339,7 +2618,7 @@ class BasicOpsSpecification extends CompilerTestingCommons }) val dataVar = (lastExtVar + 1).toByte - val Colls = IR.sigmaDslBuilderValue.Colls + val Colls = CSigmaDslBuilder.Colls implicit val eAny = sigma.AnyType val data = Colls.fromItems((Array[Byte](1,2,3).toColl, 10L)) val env1 = env + ("dataVar" -> CAnyValue(dataVar)) @@ -454,6 +2733,32 @@ class BasicOpsSpecification extends CompilerTestingCommons rootCause(_).isInstanceOf[NoSuchElementException]) } + property("higher order lambdas") { + def holTest() = test("HOL", env, ext, + """ + | { + | val c = Coll(Coll(1)) + | def fn(xs: Coll[Int]) = { + | val inc = { (x: Int) => x + 1 } + | def apply(in: (Int => Int, Int)) = in._1(in._2) + | val ys = xs.map { (x: Int) => apply((inc, x)) } + | ys.size == xs.size && ys != xs + | } + | + | c.exists(fn) + | } + |""".stripMargin, + null, + true + ) + + if(VersionContext.current.isV6SoftForkActivated) { + holTest() + } else { + an[sigma.validation.ValidationException] shouldBe thrownBy(holTest()) + } + } + property("OptionGetOrElse") { test("OptGet1", env, ext, "{ SELF.R5[Int].getOrElse(3) == 1 }", @@ -721,4 +3026,178 @@ class BasicOpsSpecification extends CompilerTestingCommons true ) } + + property("substConstants") { + val initTreeScript = + """ + | { + | val v1 = 1 // 0 + | val v2 = 2 // 2 + | val v3 = 3 // 4 + | val v4 = 4 // 3 + | val v5 = 5 // 1 + | sigmaProp(v1 == -v5 && v2 == -v4 && v3 == v2 + v4) + | } + |""".stripMargin + + val iet = ErgoTree.fromProposition(compile(Map.empty, initTreeScript).asInstanceOf[SigmaPropValue]) + + iet.constants.toArray shouldBe Array(IntConstant(1), IntConstant(5), IntConstant(2), IntConstant(4), IntConstant(3), IntConstant(6)) + + val originalBytes = Base16.encode(ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(iet)) + + val set = ErgoTree( + iet.header, + IndexedSeq(IntConstant(-2), IntConstant(2), IntConstant(-1), IntConstant(1), IntConstant(0), IntConstant(0)), + iet.toProposition(false) + ) + + val hostScript = + s""" + |{ + | val bytes = fromBase16("${originalBytes}") + | + | val substBytes = substConstants[Int](bytes, Coll[Int](0, 2, 4, 3, 1, 5), Coll[Int](-2, -1, 0, 1, 2, 0)) + | + | val checkSubst = substBytes == fromBase16("${Base16.encode(ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(set))}") + | + | sigmaProp(checkSubst) + |} + |""".stripMargin + + test("subst", env, ext, hostScript, null) + } + + property("Box.getReg") { + val customExt = Map( + 1.toByte -> IntConstant(0) + ).toSeq + def getRegTest(): Assertion = { + test("Box.getReg", env, customExt, + """{ + | val idx = getVar[Int](1).get + | val x = SELF + | x.getReg[Long](idx).get == SELF.value && + | x.getReg[Coll[(Coll[Byte], Long)]](2).get == SELF.tokens && + | x.getReg[Int](9).isEmpty + |}""".stripMargin, + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + getRegTest() + } else { + an[sigma.exceptions.ConstraintFailed] should be thrownBy getRegTest() + } + } + + property("Box.getReg - computable index") { + val ext: Seq[VarBinding] = Seq( + (intVar1, IntConstant(0)) + ) + def getRegTest(): Assertion = { + test("Box.getReg", env, ext, + """{ + | val x = SELF.getReg[Long](getVar[Int](1).get).get + | x == SELF.value + |}""".stripMargin, + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + getRegTest() + } else { + an[java.nio.BufferUnderflowException] should be thrownBy getRegTest() + } + } + + property("Unit register") { + // TODO frontend: implement missing Unit support in compiler + // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 + test("R1", env, ext, + script = "", /* means cannot be compiled + the corresponding script is { SELF.R4[Unit].isDefined } */ + ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).isDefined.toSigmaProp, + additionalRegistersOpt = Some(Map( + reg1 -> UnitConstant.instance + )) + ) + + test("R2", env, ext, + script = "", /* means cannot be compiled + the corresponding script is "{ SELF.R4[Unit].get == () }" */ + EQ(ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).get, UnitConstant.instance).toSigmaProp, + additionalRegistersOpt = Some(Map( + reg1 -> UnitConstant.instance + )) + ) + } + + property("Global.some") { + val ext: Seq[VarBinding] = Seq( + (intVar1, IntConstant(0)) + ) + def someTest(): Assertion = { + test("some", env, ext, + """{ + | val xo = Global.some[Int](5) + | xo.get == 5 + |}""".stripMargin, + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + someTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy someTest() + } + } + + property("Global.some - computable value") { + val ext: Seq[VarBinding] = Seq( + (intVar1, IntConstant(0)) + ) + def someTest(): Assertion = { + test("some", env, ext, + """{ + | val i = getVar[Int](1) + | val xo = Global.some[Int](i.get) + | xo == i + |}""".stripMargin, + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + someTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy someTest() + } + } + + property("Global.none") { + val ext: Seq[VarBinding] = Seq( + (intVar1, IntConstant(0)) + ) + def someTest(): Assertion = { + test("some", env, ext, + """{ + | val xo = Global.some[Long](5L) + | val xn = Global.none[Long]() + | xn.isDefined == false && xn != xo + |}""".stripMargin, + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + someTest() + } else { + an[sigma.validation.ValidationException] should be thrownBy someTest() + } + } + } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/ContextEnrichingSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/ContextEnrichingSpecification.scala index fe3dac68b3..3b2baa62fe 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/ContextEnrichingSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/ContextEnrichingSpecification.scala @@ -5,8 +5,8 @@ import scorex.crypto.hash.Blake2b256 import sigma.ast._ import sigma.ast.syntax._ import sigmastate._ -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, CompilerTestingCommons} -import sigma.Coll +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} +import sigma.{Coll, Colls} class ContextEnrichingSpecification extends CompilerTestingCommons @@ -19,7 +19,7 @@ class ContextEnrichingSpecification extends CompilerTestingCommons val preimage = prover.contextExtenders(1).value.asInstanceOf[Coll[Byte]] val pubkey = prover.dlogSecrets.head.publicImage - val env = Map("blake" -> Blake2b256(preimage.toArray), "pubkey" -> pubkey) + val env = Map("blake" -> Colls.fromArray(Blake2b256(preimage.toArray)), "pubkey" -> pubkey) val prop = compile(env, """{ | pubkey && blake2b256(getVar[Coll[Byte]](1).get) == blake @@ -49,7 +49,7 @@ class ContextEnrichingSpecification extends CompilerTestingCommons val preimage2 = prover.contextExtenders(2).value.asInstanceOf[Coll[Byte]] val pubkey = prover.dlogSecrets.head.publicImage - val env = Map("blake" -> Blake2b256(preimage1.append(preimage2).toArray), "pubkey" -> pubkey) + val env = Map("blake" -> Colls.fromArray(Blake2b256(preimage1.append(preimage2).toArray)), "pubkey" -> pubkey) val prop = compile(env, """{ | pubkey && blake2b256(getVar[Coll[Byte]](1).get ++ getVar[Coll[Byte]](2).get) == blake @@ -89,7 +89,7 @@ class ContextEnrichingSpecification extends CompilerTestingCommons .withContextExtender(k1, ByteArrayConstant(v1)) .withContextExtender(k2, ByteArrayConstant(v2)) - val env = Map("k1" -> k1.toInt, "k2" -> k2.toInt, "r" -> r) + val env = Map("k1" -> k1.toInt, "k2" -> k2.toInt, "r" -> Colls.fromArray(r)) val prop = compile(env, "{ xor(getVar[Coll[Byte]](k1).get, getVar[Coll[Byte]](k2).get) == r }").asBoolValue.toSigmaProp val propTree = mkTestErgoTree(prop) @@ -119,7 +119,7 @@ class ContextEnrichingSpecification extends CompilerTestingCommons val prover = new ContextEnrichingTestProvingInterpreter val preimage = prover.contextExtenders(1).value.asInstanceOf[Coll[Byte]] - val env = Map("blake" -> Blake2b256(preimage.toArray)) + val env = Map("blake" -> Colls.fromArray(Blake2b256(preimage.toArray))) val prop = compile(env, """{ | blake2b256(getVar[Coll[Byte]](1).get) == blake @@ -149,7 +149,7 @@ class ContextEnrichingSpecification extends CompilerTestingCommons val preimage1 = prover.contextExtenders(1).value.asInstanceOf[Coll[Byte]] val preimage2 = prover.contextExtenders(2).value.asInstanceOf[Coll[Byte]] - val env = Map("blake" -> Blake2b256(preimage2.append(preimage1).toArray)) + val env = Map("blake" -> Colls.fromArray(Blake2b256(preimage2.append(preimage1).toArray))) val prop = compile(env, """{ | blake2b256(getVar[Coll[Byte]](2).get ++ getVar[Coll[Byte]](1).get) == blake diff --git a/sc/shared/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala index 615a826649..f7adc194b5 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala @@ -5,11 +5,12 @@ import org.ergoplatform.ErgoBox.R4 import org.ergoplatform._ import org.scalatest.TryValues._ import scorex.crypto.hash.Blake2b256 +import sigma.{Colls, VersionContext} import sigma.ast.SCollection.SByteArray import sigma.ast._ import sigmastate._ import sigma.ast.syntax._ -import sigma.data.{AvlTreeData, ProveDHTuple, ProveDlog, TrivialProp} +import sigma.data.{AvlTreeData, CBox, ProveDHTuple, ProveDlog, TrivialProp} import sigma.util.Extensions.EcpOps import sigma.validation.ValidationException import sigmastate.eval._ @@ -51,8 +52,8 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons .withErgoTreeVersion(ergoTreeVersionInTests) val e = compile(Map( - "h1" -> ErgoTree.withSegregation(ergoTreeHeaderInTests, h1).bytes, - "h2" -> ErgoTree.withSegregation(ergoTreeHeaderInTests, h2).bytes), + "h1" -> Colls.fromArray(ErgoTree.withSegregation(ergoTreeHeaderInTests, h1).bytes), + "h2" -> Colls.fromArray(ErgoTree.withSegregation(ergoTreeHeaderInTests, h2).bytes)), "h1 == h1") val exp = TrueLeaf e shouldBe exp @@ -192,7 +193,7 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons val spendingTransaction = createTransaction(newBoxes) def mixingRequestProp(sender: ProveDlog, timeout: Int): ErgoTree = { - val env = Map("sender" -> sender, "timeout" -> timeout, "properHash" -> properHash) + val env = Map("sender" -> sender, "timeout" -> timeout, "properHash" -> Colls.fromArray(properHash)) val compiledProp = compile(env, """{ | val notTimePassed = HEIGHT <= timeout @@ -459,9 +460,14 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons val pubkey1 = prover.dlogSecrets.head.publicImage val pubkey2 = prover.dlogSecrets(1).publicImage + val tb = testBox(value = 10, ergoTree = mkTestErgoTree(pubkey1), creationHeight = 0) + val brother = if(VersionContext.current.isJitActivated) { + CBox(tb) + } else { + tb + } - val brother = testBox(value = 10, ergoTree = mkTestErgoTree(pubkey1), creationHeight = 0) - val brotherWithWrongId = testBox(value = 10, + val tbWithWrongId = testBox(value = 10, ergoTree = mkTestErgoTree(pubkey1), creationHeight = 0, boxIndex = 120: Short) @@ -482,7 +488,7 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons val propExpected = BinAnd( EQ(SizeOf(Inputs), IntConstant(2)), - EQ(ExtractId(ByIndex(Inputs, 0)), ExtractId(BoxConstant(brother)))).toSigmaProp + EQ(ExtractId(ByIndex(Inputs, 0)), ExtractId(BoxConstant(tb)))).toSigmaProp prop shouldBe propExpected // try a version of the script that matches the white paper @@ -496,18 +502,18 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons currentHeight = 50, lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, - boxesToSpend = IndexedSeq(brother, s), + boxesToSpend = IndexedSeq(tb, s), spendingTransaction, self = s, activatedVersionInTests) - val pr = prover.prove(emptyEnv + (ScriptNameProp -> "prove_prop"), propTree, ctx, fakeMessage).getOrThrow - verifier.verify(emptyEnv + (ScriptNameProp -> "verify_prop"), propTree, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true + val pr = prover.prove(emptyEnv, propTree, ctx, fakeMessage).getOrThrow + verifier.verify(emptyEnv, propTree, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true val wrongCtx = ErgoLikeContextTesting( currentHeight = 50, lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, - boxesToSpend = IndexedSeq(brotherWithWrongId, s), + boxesToSpend = IndexedSeq(tbWithWrongId, s), spendingTransaction, self = s, activatedVersionInTests) @@ -522,9 +528,9 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons }""".stripMargin).asBoolValue.toSigmaProp val prop2Tree = mkTestErgoTree(prop2) - prover.prove(emptyEnv + (ScriptNameProp -> "prove_prop2"), prop2Tree, ctx, fakeMessage).isFailure shouldBe true + prover.prove(emptyEnv, prop2Tree, ctx, fakeMessage).isFailure shouldBe true verifier - .verify(emptyEnv + (ScriptNameProp -> "verify_prop2"), prop2Tree, ctx, pr, fakeMessage) + .verify(emptyEnv, prop2Tree, ctx, pr, fakeMessage) .getOrThrow._1 shouldBe false } @@ -547,7 +553,12 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons val newBoxes = IndexedSeq(newBox) val spendingTransaction = createTransaction(newBoxes) - val env = Map("friend" -> friend) + val friendVar = if(VersionContext.current.isJitActivated){ + CBox(friend) + } else { + friend + } + val env = Map("friend" -> friendVar) val prop = compile(env, """{ | @@ -623,7 +634,7 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons val helloHash = Blake2b256.hash(preimageHello) - val env = Map("helloHash" -> helloHash) + val env = Map("helloHash" -> Colls.fromArray(helloHash)) val prop = compile(env, """{ | val cond = INPUTS(0).value > 10 diff --git a/sc/shared/src/test/scala/sigmastate/utxo/UsingContextPropertiesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/UsingContextPropertiesSpecification.scala index e3d43ab868..77e78aa6bf 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/UsingContextPropertiesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/UsingContextPropertiesSpecification.scala @@ -1,19 +1,19 @@ package sigmastate.utxo import sigmastate.CompilerCrossVersionProps -import sigmastate.eval.IRContext import sigma.Extensions.ArrayOps import sigma.Context import sigmastate.helpers.CompilerTestingCommons import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, TestContractSpec} import org.ergoplatform.ErgoBox import scorex.crypto.hash.Blake2b256 +import sigma.compiler.ir.IRContext import sigma.data.{CSigmaProp, TrivialProp} class UsingContextPropertiesSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { suite => lazy val spec = TestContractSpec(suite)(new TestingIRContext) - lazy val prover = spec.ProvingParty("Alice") + lazy val prover = spec.ProvingParty("Alice") private implicit lazy val IR: IRContext = spec.IR private val reg1 = ErgoBox.nonMandatoryRegisters(0) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala b/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala index 0dca8a4471..16b4e3e232 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala @@ -17,6 +17,7 @@ import scorex.util._ import sigma.Colls import sigma.data.{AvlTreeData, AvlTreeFlags} import ErgoTree.ZeroHeader +import sigma.compiler.ir.IRContext import sigma.eval.Extensions.SigmaBooleanOps import sigma.interpreter.ContextExtension import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala index 969439fe59..5e89915384 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala @@ -68,11 +68,9 @@ case class AssetsPartialFilling[Spec <: ContractSpec] val out = OUTPUTS(outIdx) val tokenData = out.R2[Coll[(Coll[Byte], Long)]].get(0) - val tokenId = tokenData._1 val tokenValue = tokenData._2 val selfTokenData = SELF.R2[Coll[(Coll[Byte], Long)]].get(0) - val selfTokenId = selfTokenData._1 val selfTokenValue = selfTokenData._2 val selfValue = SELF.value diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/AtomicSwapExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/AtomicSwapExampleSpecification.scala index 131aba22d0..7323dc6118 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/AtomicSwapExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/AtomicSwapExampleSpecification.scala @@ -39,7 +39,6 @@ class AtomicSwapExampleSpecification extends CompilerTestingCommons with Compile val deadlineB = 500 val env = Map( - ScriptNameProp -> "atomic", "height1" -> height1, "height2" -> height2, "deadlineBob" -> deadlineB, "deadlineAlice" -> deadlineA, "pubkeyA" -> pubkeyA, "pubkeyB" -> pubkeyB, "hx" -> hx) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala index 9bcfd81ecf..ef100a36a5 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala @@ -9,6 +9,7 @@ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingIn import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp import sigma.ast.syntax._ +import sigmastate.interpreter.Interpreter class ColdWalletAdvContractExampleSpecification extends CompilerTestingCommons @@ -35,7 +36,6 @@ class ColdWalletAdvContractExampleSpecification extends CompilerTestingCommons val minSpend = 100 val env = Map( - ScriptNameProp -> "env", "user1" -> alicePubKey, "user2" -> bobPubKey, "user3" -> carolPubKey, @@ -109,12 +109,9 @@ class ColdWalletAdvContractExampleSpecification extends CompilerTestingCommons ) ) - val dave = new ErgoLikeTestProvingInterpreter // paying to dave, some arbitrary user - val davePubKey = dave.dlogSecrets.head.publicImage - val firstWithdrawHeight = depositHeight + 1 // - val spendEnv = Map(ScriptNameProp -> "spendEnv") + val spendEnv = Interpreter.emptyEnv // One of Alice, Bob or Carol withdraws val firstWithdrawAmount1Key = depositAmount * percent1Key / 100 // less than or equal to percent diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala index 1948bc16d9..bfeb9732a8 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala @@ -31,7 +31,6 @@ class ColdWalletContractExampleSpecification extends CompilerTestingCommons val minSpend = 100 val env = Map( - ScriptNameProp -> "env", "alice" -> alicePubKey, "bob" -> bobPubKey, "blocksIn24h" -> IntConstant(blocksIn24h), diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala index 9246cc1c8e..60569d061e 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala @@ -38,7 +38,6 @@ class DHTupleExampleSpecification extends CompilerTestingCommons val g_x = alicePubKey.value // g_x is Alice's public key (g_x = g^x) val env = Map( - ScriptNameProp -> "env", "g" -> g.toGroupElement, "g_x" -> g_x.toGroupElement ) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala index 4ec6922c38..6d4ff117d3 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala @@ -49,7 +49,6 @@ class DemurrageExampleSpecification extends CompilerTestingCommons val regScript = userProver.dlogSecrets.head.publicImage val env = Map( - ScriptNameProp -> "Demurrage", "demurragePeriod" -> demurragePeriod, "demurrageCoeff" -> demurrageCoeff, "regScript" -> regScript diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/ExecuteFromExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/ExecuteFromExamplesSpecification.scala index b75b404aed..8090d87803 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/ExecuteFromExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/ExecuteFromExamplesSpecification.scala @@ -9,8 +9,6 @@ import sigma.ast.ByteArrayConstant class ExecuteFromExamplesSpecification extends CompilerTestingCommons { suite => implicit lazy val IR = new TestingIRContext - private val reg1 = ErgoBox.nonMandatoryRegisters(0) - case class OracleContract[Spec <: ContractSpec] (alice: Spec#ProvingParty) (implicit val spec: Spec) extends SigmaContractSyntax with StdContracts diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/IcoExample.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/IcoExample.scala index c930135bc2..9c81e4539d 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/IcoExample.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/IcoExample.scala @@ -22,6 +22,7 @@ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingIn import sigmastate.interpreter.Interpreter.ScriptNameProp import sigmastate.interpreter.Interpreter import sigma.ast.syntax._ +import sigma.compiler.ir.IRContext import sigma.eval.SigmaDsl import sigma.serialization.ErgoTreeSerializer import sigma.serialization.ErgoTreeSerializer.DefaultSerializer @@ -251,8 +252,7 @@ class IcoExample extends CompilerTestingCommons private val feeBytes = feeProp.bytes val env = Map( - ScriptNameProp -> "withdrawalScriptEnv", - "feeBytes" -> feeBytes, + "feeBytes" -> Colls.fromArray(feeBytes), "projectPubKey" -> project.secrets.head.publicImage ) lazy val withdrawalScript: SigmaPropValue = compile(env, @@ -312,7 +312,7 @@ class IcoExample extends CompilerTestingCommons Blake2b256(ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(withdrawalTree)) } - def issuanceScript: SigmaPropValue = compile(env.updated("nextStageScriptHash", wsHash), + def issuanceScript: SigmaPropValue = compile(env.updated("nextStageScriptHash", Colls.fromArray(wsHash)), """{ | val openTree = SELF.R5[AvlTree].get | @@ -348,7 +348,7 @@ class IcoExample extends CompilerTestingCommons Blake2b256(ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(tree)) } - def fundingScript: SigmaPropValue = compile(env.updated("nextStageScriptHash", issuanceHash), + def fundingScript: SigmaPropValue = compile(env.updated("nextStageScriptHash", Colls.fromArray(issuanceHash)), """{ | | val selfIndexIsZero = INPUTS(0).id == SELF.id diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala index ce4ed2114e..609a127bec 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala @@ -10,12 +10,12 @@ import sigma.data.{AvlTreeData, AvlTreeFlags, Digest32Coll, TrivialProp} import sigmastate.CompilerCrossVersionProps import sigma.ast.{AvlTreeConstant, ByteArrayConstant, LongConstant, SigmaPropConstant} import sigma.Extensions.ArrayOps -import sigmastate.eval.IRContext import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestProvingInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp import sigma.serialization.ErgoTreeSerializer import sigma.ast.syntax._ +import sigma.compiler.ir.IRContext import sigma.eval.SigmaDsl import scala.util.Random @@ -178,7 +178,7 @@ class LetsSpecification extends CompilerTestingCommons with CompilerCrossVersion val letsTokenId = Digest32Coll @@ Colls.fromArray(Array.fill(32)(Random.nextInt(100).toByte)) - val env = Map(ScriptNameProp -> "withdrawalScriptEnv", "letsToken" -> ByteArrayConstant(letsTokenId)) + val env = Map("letsToken" -> ByteArrayConstant(letsTokenId)) private val miningRewardsDelay = 720 private val feeProp = ErgoTreePredef.feeProposition(miningRewardsDelay) // create ErgoTree v0 @@ -235,7 +235,7 @@ class LetsSpecification extends CompilerTestingCommons with CompilerCrossVersion def userContractHash = Blake2b256(ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(exchangeTree)) - def managementScript = compile(env.updated("userContractHash", userContractHash), + def managementScript = compile(env.updated("userContractHash", Colls.fromArray(userContractHash)), """{ | | val selfOut = OUTPUTS(0) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala index fc19a485ee..fdb241a908 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo.examples import java.math.BigInteger import org.ergoplatform.ErgoBox.{R4, R5} import scorex.crypto.hash.Blake2b256 +import sigma.Colls import sigma.data.{AvlTreeData, ProveDHTuple, ProveDlog} import sigma.util.Extensions.EcpOps import sigmastate.CompilerCrossVersionProps @@ -41,7 +42,6 @@ class MixExampleSpecification extends CompilerTestingCommons // val alicePubKey:ProveDlog = ProveDlog(g_x) val fullMixEnv = Map( - ScriptNameProp -> "fullMixEnv", "g" -> g.toGroupElement, "gX" -> gX.toGroupElement ) @@ -60,10 +60,9 @@ class MixExampleSpecification extends CompilerTestingCommons ).asSigmaProp) val halfMixEnv = Map( - ScriptNameProp -> "halfMixEnv", "g" -> g.toGroupElement, "gX" -> gX.toGroupElement, - "fullMixScriptHash" -> Blake2b256(fullMixScript.bytes) + "fullMixScriptHash" -> Colls.fromArray(Blake2b256(fullMixScript.bytes)) ) // Note that below script allows Alice to spend the half-mix output anytime before Bob spends it. diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala index 9d0ffc880b..8d3d09de74 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala @@ -138,7 +138,7 @@ class OracleExamplesSpecification extends CompilerTestingCommons LastBlockUtxoRootHash, SAvlTreeMethods.getMethod, IndexedSeq(ExtractId(GetVarBox(22: Byte).get), GetVarByteArray(23: Byte).get)).asOption[SByteArray]), EQ(extract[SByteArray](ErgoBox.ScriptRegId), ByteArrayConstant(ErgoTree.fromSigmaBoolean(oraclePubKey).bytes)), - EQ(Exponentiate(GroupGenerator, extract[SBigInt.type](reg3)), + EQ(Exponentiate(GroupGenerator, extract[SBigInt.type](reg3)(SBigInt)), MultiplyGroup(extract[SGroupElement.type](reg2), Exponentiate(GroupElementConstant(oraclePubImage.value), ByteArrayToBigInt( diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala index c695b8e07d..3ca85e892b 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala @@ -4,6 +4,7 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5, R6, R7} import scorex.crypto.hash.Blake2b256 import scorex.utils.Random +import sigma.Colls import sigma.data.{AvlTreeData, ProveDlog} import sigma.ast.{ByteArrayConstant, ByteConstant, ErgoTree, IntConstant, SigmaPropConstant} import sigmastate._ @@ -51,9 +52,8 @@ class RPSGameExampleSpecification extends CompilerTestingCommons val h = Blake2b256(s :+ a) // Alice's commitment val fullGameEnv = Map( - ScriptNameProp -> "fullGameScriptEnv", "alice" -> alicePubKey, - "k" -> h + "k" -> Colls.fromArray(h) ) val fullGameScript = mkTestErgoTree(compile(fullGameEnv, @@ -78,9 +78,8 @@ class RPSGameExampleSpecification extends CompilerTestingCommons ).asSigmaProp) val halfGameEnv = Map( - ScriptNameProp -> "halfGameScript", "alice" -> alicePubKey, - "fullGameScriptHash" -> Blake2b256(fullGameScript.bytes) + "fullGameScriptHash" -> Colls.fromArray(Blake2b256(fullGameScript.bytes)) ) // Note that below script allows Alice to spend the half-game output anytime before Bob spends it. diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala index 4294dc5b4b..fad547460f 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala @@ -3,13 +3,14 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5} import org.ergoplatform._ import scorex.crypto.hash.Blake2b256 +import sigma.Colls import sigma.data.AvlTreeData import sigma.ast.{ErgoTree, IntConstant, SigmaPropConstant} import sigmastate._ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigmastate.interpreter.Interpreter.ScriptNameProp import sigma.ast.syntax._ +import sigmastate.interpreter.Interpreter class ReversibleTxExampleSpecification extends CompilerTestingCommons @@ -79,7 +80,6 @@ class ReversibleTxExampleSpecification extends CompilerTestingCommons val carolPubKey = carol.dlogSecrets.head.publicImage val withdrawEnv = Map( - ScriptNameProp -> "withdrawEnv", "carol" -> carolPubKey // this pub key can reverse payments ) @@ -94,12 +94,11 @@ class ReversibleTxExampleSpecification extends CompilerTestingCommons val blocksIn24h = 500 val feeProposition = ErgoTreePredef.feeProposition() val depositEnv = Map( - ScriptNameProp -> "depositEnv", "alice" -> alicePubKey, "blocksIn24h" -> blocksIn24h, "maxFee" -> 10L, - "feePropositionBytes" -> feeProposition.bytes, - "withdrawScriptHash" -> Blake2b256(withdrawScript.bytes) + "feePropositionBytes" -> Colls.fromArray(feeProposition.bytes), + "withdrawScriptHash" -> Colls.fromArray(Blake2b256(withdrawScript.bytes)) ) val depositScript = mkTestErgoTree(compile(depositEnv, @@ -189,7 +188,7 @@ class ReversibleTxExampleSpecification extends CompilerTestingCommons self = reversibleWithdrawOutput, activatedVersionInTests ) - val spendEnv = Map(ScriptNameProp -> "spendEnv") + val spendEnv = Interpreter.emptyEnv val proofBobSpend = bob.prove(spendEnv, withdrawScript, bobSpendContext, fakeMessage).get.proof diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala index 2657a49eb0..fb1c275fb8 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala @@ -27,7 +27,6 @@ class TimedPaymentExampleSpecification extends CompilerTestingCommons val bobPubKey = bob.dlogSecrets.head.publicImage val env = Map( - ScriptNameProp -> "env", "alice" -> alicePubKey ) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala index b3d6d4eff8..8821ccb72e 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala @@ -4,6 +4,7 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5, R6} import scorex.crypto.hash.Blake2b256 import scorex.utils.Random +import sigma.Colls import sigma.data.{AvlTreeData, ProveDlog} import sigma.ast.{ByteArrayConstant, ByteConstant, IntConstant, SigmaPropConstant} import sigmastate._ @@ -51,9 +52,8 @@ class XorGameExampleSpecification extends CompilerTestingCommons val h = Blake2b256(s :+ a) // Alice's commitment val fullGameEnv = Map( - ScriptNameProp -> "fullGameScriptEnv", "alice" -> alicePubKey, - "h" -> h + "h" -> Colls.fromArray(h) ) val fullGameScript = mkTestErgoTree(compile(fullGameEnv, @@ -73,9 +73,8 @@ class XorGameExampleSpecification extends CompilerTestingCommons ).asSigmaProp) val halfGameEnv = Map( - ScriptNameProp -> "halfGameScript", "alice" -> alicePubKey, - "fullGameScriptHash" -> Blake2b256(fullGameScript.bytes) + "fullGameScriptHash" -> Colls.fromArray(Blake2b256(fullGameScript.bytes)) ) // Note that below script allows Alice to spend the half-game output anytime before Bob spends it. diff --git a/sc/shared/src/test/scala/special/wrappers/WOptionTests.scala b/sc/shared/src/test/scala/special/wrappers/WOptionTests.scala index 7e046739f0..e952ba7c00 100644 --- a/sc/shared/src/test/scala/special/wrappers/WOptionTests.scala +++ b/sc/shared/src/test/scala/special/wrappers/WOptionTests.scala @@ -1,7 +1,6 @@ package special.wrappers import scala.language.reflectiveCalls -import scalan.Library class WOptionTests extends WrappersTests { diff --git a/sc/shared/src/test/scala/special/wrappers/WSpecialPredefTests.scala b/sc/shared/src/test/scala/special/wrappers/WSpecialPredefTests.scala deleted file mode 100644 index e1097ebf6c..0000000000 --- a/sc/shared/src/test/scala/special/wrappers/WSpecialPredefTests.scala +++ /dev/null @@ -1,24 +0,0 @@ -package special.wrappers - - -import scala.language.reflectiveCalls -import sigma.data.RType - -class WSpecialPredefTests extends WrappersTests { - - lazy val ctx = new WrappersCtx - import ctx._ - import WSpecialPredef._ - - lazy val SPCM = WSpecialPredefCompanionMethods - - test("some") { - val x: Ref[Int] = 10 - val opt = RWSpecialPredef.some(x) - opt match { - case SPCM.some(_x) => _x shouldBe x - case _ => assert(false) - } - } - -} diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala index ef53e13dbd..2220c9827a 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala @@ -12,7 +12,7 @@ import scala.scalajs.js.annotation.JSExportTopLevel class Header( /** Hex representation of ModifierId of this Header */ val id: String, - /** Block version, to be increased on every soft and hardfork. */ + /** Block version, to be increased on every soft- or hard-fork. */ val version: Byte, /** Hex representation of ModifierId of the parent block */ val parentId: String, @@ -48,5 +48,7 @@ class Header( val powDistance: js.BigInt, /** Miner votes for changing system parameters. */ - val votes: String + val votes: String, + + val unparsedBytes: String ) extends js.Object diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala index f6393f62bb..84f2b21da8 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala @@ -1,93 +1,48 @@ package org.ergoplatform.sdk.js -import org.ergoplatform.ErgoBox._ import org.ergoplatform._ import org.ergoplatform.sdk.ExtendedInputBox -import org.ergoplatform.sdk.JavaHelpers.UniversalConverter import org.ergoplatform.sdk.wallet.protocol.context -import scorex.crypto.authds.ADKey -import scorex.util.ModifierId -import scorex.util.encode.Base16 -import sigma.Extensions.CollBytesOps -import sigma.ast.syntax.GroupElementConstant -import sigma.ast.{Constant, GroupElementConstant, SType} +import sigma.ast.{Constant, SType} +import sigma.data.Iso import sigma.data.Iso.{isoStringToArray, isoStringToColl} -import sigma.data.{CBigInt, CGroupElement, Digest32Coll, Digest32CollRType, Iso} +import sigma.data.js.{Isos => DataIsos} +import sigma.data.CHeader import sigma.interpreter.{ContextExtension, ProverResult} -import sigma.js.{AvlTree, GroupElement} -import sigma.serialization.{ErgoTreeSerializer, ValueSerializer} -import sigma.{Coll, Colls} -import sigmastate.eval.{CHeader, CPreHeader} +import sigma.js.AvlTree +import sigmastate.eval.CPreHeader import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.Box -import sigmastate.fleetSdkCommon.distEsmTypesCommonMod.HexString import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters import sigmastate.fleetSdkCommon.distEsmTypesTokenMod.TokenAmount import sigmastate.fleetSdkCommon.distEsmTypesTransactionsMod.{SignedTransaction, UnsignedTransaction} -import sigmastate.fleetSdkCommon.{distEsmTypesBoxesMod => boxesMod, distEsmTypesCommonMod => commonMod, distEsmTypesContextExtensionMod => contextExtensionMod, distEsmTypesInputsMod => inputsMod, distEsmTypesProverResultMod => proverResultMod, distEsmTypesRegistersMod => registersMod, distEsmTypesTokenMod => tokenMod} +import sigmastate.fleetSdkCommon.{distEsmTypesCommonMod => commonMod, distEsmTypesContextExtensionMod => contextExtensionMod, distEsmTypesInputsMod => inputsMod, distEsmTypesProverResultMod => proverResultMod} -import java.math.BigInteger import scala.collection.immutable.ListMap import scala.scalajs.js import scala.scalajs.js.Object -/** Definitions of isomorphisms. */ +/** Definitions of isomorphisms for sigma-sdk module. + * @see sigma.data.Iso + */ object Isos { - val isoStringToGroupElement: Iso[String, sigma.GroupElement] = new Iso[String, sigma.GroupElement] { - override def to(x: String): sigma.GroupElement = { - val bytes = Base16.decode(x).get - ValueSerializer.deserialize(bytes).asInstanceOf[GroupElementConstant].value - } - override def from(x: sigma.GroupElement): String = { - val bytes = ValueSerializer.serialize(GroupElementConstant(x)) - Base16.encode(bytes) - } - } - - val isoGroupElement: Iso[GroupElement, sigma.GroupElement] = new Iso[GroupElement, sigma.GroupElement] { - override def to(x: GroupElement): sigma.GroupElement = { - CGroupElement(x.point) - } - override def from(x: sigma.GroupElement): GroupElement = { - new GroupElement(x.asInstanceOf[CGroupElement].wrappedValue) - } - } - - implicit val isoBoxId: Iso[boxesMod.BoxId, ErgoBox.BoxId] = new Iso[boxesMod.BoxId, ErgoBox.BoxId] { - override def to(x: boxesMod.BoxId): ErgoBox.BoxId = ADKey @@@ isoStringToArray.to(x) - - override def from(x: ErgoBox.BoxId): boxesMod.BoxId = isoStringToArray.from(x) - } - - implicit val isoHexStringToConstant: Iso[HexString, Constant[SType]] = new Iso[HexString, Constant[SType]] { - override def to(x: HexString): Constant[SType] = { - val bytes = isoStringToArray.to(x) - val value = ValueSerializer.deserialize(bytes) - value.asInstanceOf[Constant[SType]] - } - override def from(x: Constant[SType]): HexString = { - val bytes = ValueSerializer.serialize(x) - isoStringToArray.from(bytes) - } - } - implicit val isoHeader: Iso[Header, sigma.Header] = new Iso[Header, sigma.Header] { override def to(a: Header): sigma.Header = { CHeader( - id = isoStringToColl.to(a.id), version = a.version, parentId = isoStringToColl.to(a.parentId), ADProofsRoot = isoStringToColl.to(a.ADProofsRoot), - stateRoot = AvlTree.isoAvlTree.to(a.stateRoot), + stateRootDigest = AvlTree.isoAvlTree.to(a.stateRoot).digest, transactionsRoot = isoStringToColl.to(a.transactionsRoot), timestamp = sigma.js.Isos.isoBigIntToLong.to(a.timestamp), nBits = sigma.js.Isos.isoBigIntToLong.to(a.nBits), height = a.height, extensionRoot = isoStringToColl.to(a.extensionRoot), - minerPk = isoGroupElement.to(a.minerPk), - powOnetimePk = isoGroupElement.to(a.powOnetimePk), + minerPk = DataIsos.isoGroupElement.to(a.minerPk), + powOnetimePk = DataIsos.isoGroupElement.to(a.powOnetimePk), powNonce = isoStringToColl.to(a.powNonce), powDistance = sigma.js.Isos.isoBigInt.to(a.powDistance), - votes = isoStringToColl.to(a.votes) + votes = isoStringToColl.to(a.votes), + unparsedBytes = isoStringToColl.to(a.unparsedBytes) ) } override def from(b: sigma.Header): Header = { @@ -103,11 +58,12 @@ object Isos { nBits = sigma.js.Isos.isoBigIntToLong.from(header.nBits), height = header.height, extensionRoot = isoStringToColl.from(header.extensionRoot), - minerPk = isoGroupElement.from(header.minerPk), - powOnetimePk = isoGroupElement.from(header.powOnetimePk), + minerPk = DataIsos.isoGroupElement.from(header.minerPk), + powOnetimePk = DataIsos.isoGroupElement.from(header.powOnetimePk), powNonce = isoStringToColl.from(header.powNonce), powDistance = sigma.js.Isos.isoBigInt.from(header.powDistance), - votes = isoStringToColl.from(header.votes) + votes = isoStringToColl.from(header.votes), + unparsedBytes = isoStringToColl.from(header.unparsedBytes) ) } } @@ -120,7 +76,7 @@ object Isos { timestamp = sigma.js.Isos.isoBigIntToLong.to(a.timestamp), nBits = sigma.js.Isos.isoBigIntToLong.to(a.nBits), height = a.height, - minerPk = isoGroupElement.to(a.minerPk), + minerPk = DataIsos.isoGroupElement.to(a.minerPk), votes = isoStringToColl.to(a.votes) ) } @@ -132,7 +88,7 @@ object Isos { timestamp = sigma.js.Isos.isoBigIntToLong.from(header.timestamp), nBits = sigma.js.Isos.isoBigIntToLong.from(header.nBits), height = header.height, - minerPk = isoGroupElement.from(header.minerPk), + minerPk = DataIsos.isoGroupElement.from(header.minerPk), votes = isoStringToColl.from(header.votes) ) } @@ -195,7 +151,7 @@ object Isos { val keys = js.Object.keys(x).sorted for ( k <- keys ) { val id = k.toInt.toByte - val c = isoHexStringToConstant.to(x.apply(id).get.get) + val c = DataIsos.isoHexStringToConstant.to(x.apply(id).get.get) map = map + (id -> c) } ContextExtension(map) @@ -204,7 +160,7 @@ object Isos { override def from(x: ContextExtension): contextExtensionMod.ContextExtension = { val res = new Object().asInstanceOf[contextExtensionMod.ContextExtension] x.values.foreach { case (k, v: Constant[_]) => - val hex = isoHexStringToConstant.from(v) + val hex = DataIsos.isoHexStringToConstant.from(v) res.update(k, hex) } res @@ -213,10 +169,10 @@ object Isos { implicit val isoUnsignedInput: Iso[inputsMod.UnsignedInput, UnsignedInput] = new Iso[inputsMod.UnsignedInput, UnsignedInput] { override def to(x: inputsMod.UnsignedInput): UnsignedInput = - new UnsignedInput(x.boxId.convertTo[ErgoBox.BoxId], isoContextExtension.to(x.extension)) + new UnsignedInput(DataIsos.isoBoxId.to(x.boxId), isoContextExtension.to(x.extension)) override def from(x: UnsignedInput): inputsMod.UnsignedInput = - inputsMod.UnsignedInput(x.boxId.convertTo[boxesMod.BoxId], isoContextExtension.from(x.extension)) + inputsMod.UnsignedInput(DataIsos.isoBoxId.from(x.boxId), isoContextExtension.from(x.extension)) } implicit val isoProverResult: Iso[proverResultMod.ProverResult, ProverResult] = new Iso[proverResultMod.ProverResult, ProverResult] { @@ -236,135 +192,17 @@ object Isos { implicit val isoSignedInput: Iso[inputsMod.SignedInput, Input] = new Iso[inputsMod.SignedInput, Input] { override def to(x: inputsMod.SignedInput): Input = - Input(x.boxId.convertTo[ErgoBox.BoxId], isoProverResult.to(x.spendingProof)) + Input(DataIsos.isoBoxId.to(x.boxId), isoProverResult.to(x.spendingProof)) override def from(x: Input): inputsMod.SignedInput = - inputsMod.SignedInput(x.boxId.convertTo[boxesMod.BoxId], isoProverResult.from(x.spendingProof)) + inputsMod.SignedInput(DataIsos.isoBoxId.from(x.boxId), isoProverResult.from(x.spendingProof)) } implicit val isoDataInput: Iso[inputsMod.DataInput, DataInput] = new Iso[inputsMod.DataInput, DataInput] { - override def to(x: inputsMod.DataInput): DataInput = DataInput(x.boxId.convertTo[ErgoBox.BoxId]) - - override def from(x: DataInput): inputsMod.DataInput = inputsMod.DataInput(x.boxId.convertTo[boxesMod.BoxId]) - } - - implicit val isoAmount: Iso[commonMod.Amount, Long] = new Iso[commonMod.Amount, Long] { - override def to(x: commonMod.Amount): Long = x.asInstanceOf[Any] match { - case s: String => BigInt(s).toLong - case _ => java.lang.Long.parseLong(x.asInstanceOf[js.BigInt].toString(10)) - } - override def from(x: Long): commonMod.Amount = x.toString - } - - implicit val isoToken: Iso[tokenMod.TokenAmount[commonMod.Amount], Token] = - new Iso[tokenMod.TokenAmount[commonMod.Amount], Token] { - override def to(x: tokenMod.TokenAmount[commonMod.Amount]): Token = - (Digest32Coll @@@ Colls.fromArray(Base16.decode(x.tokenId).get), isoAmount.to(x.amount)) + override def to(x: inputsMod.DataInput): DataInput = + DataInput(DataIsos.isoBoxId.to(x.boxId)) - override def from(x: Token): tokenMod.TokenAmount[commonMod.Amount] = - tokenMod.TokenAmount[commonMod.Amount](isoAmount.from(x._2), x._1.toHex) - } - - val isoTokenArray: Iso[js.Array[tokenMod.TokenAmount[commonMod.Amount]], Coll[Token]] = - new Iso[js.Array[tokenMod.TokenAmount[commonMod.Amount]], Coll[Token]] { - override def to(x: js.Array[tokenMod.TokenAmount[commonMod.Amount]]): Coll[Token] = { - sigma.js.Isos.isoArrayToColl(isoToken).to(x) - } - override def from(x: Coll[Token]): js.Array[tokenMod.TokenAmount[commonMod.Amount]] = { - sigma.js.Isos.isoArrayToColl(isoToken).from(x) - } - } - - val isoNonMandatoryRegisters: Iso[registersMod.NonMandatoryRegisters, AdditionalRegisters] = - new Iso[registersMod.NonMandatoryRegisters, AdditionalRegisters] { - override def to(x: registersMod.NonMandatoryRegisters): AdditionalRegisters = { - val regs = Seq( - x.R4 -> R4, - x.R5 -> R5, - x.R6 -> R6, - x.R7 -> R7, - x.R8 -> R8, - x.R9 -> R9 - ).collect { - case (regOpt, id) if regOpt.isDefined => id -> isoHexStringToConstant.to(regOpt.get) - } - Map(regs:_*) - } - override def from(regs: AdditionalRegisters): registersMod.NonMandatoryRegisters = { - def regHexOpt(t: NonMandatoryRegisterId): Option[HexString] = - regs.get(t).map(v => isoHexStringToConstant.from(v.asInstanceOf[Constant[SType]])) - - val resRegs = NonMandatoryRegisters() - regHexOpt(R4).foreach(resRegs.setR4(_)) - regHexOpt(R5).foreach(resRegs.setR5(_)) - regHexOpt(R6).foreach(resRegs.setR6(_)) - regHexOpt(R7).foreach(resRegs.setR7(_)) - regHexOpt(R8).foreach(resRegs.setR8(_)) - regHexOpt(R9).foreach(resRegs.setR9(_)) - resRegs - } - } - - implicit val isoBoxCandidate: Iso[boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters], ErgoBoxCandidate] = new Iso[boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters], ErgoBoxCandidate] { - override def to(x: boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters]): ErgoBoxCandidate = { - val ergoBoxCandidate = new ErgoBoxCandidate( - value = isoAmount.to(x.value), - ergoTree = { - val bytes = Base16.decode(x.ergoTree).get - ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(bytes) - }, - x.creationHeight.toInt, - additionalTokens = isoTokenArray.to(x.assets), - additionalRegisters = isoNonMandatoryRegisters.to(x.additionalRegisters) - ) - ergoBoxCandidate - } - - override def from(x: ErgoBoxCandidate): boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters] = { - val ergoTree = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(x.ergoTree) - val ergoTreeStr = Base16.encode(ergoTree) - val assets = isoTokenArray.from(x.additionalTokens) - boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters]( - ergoTree = ergoTreeStr, - value = isoAmount.from(x.value), - assets = assets, - creationHeight = x.creationHeight, - additionalRegisters = isoNonMandatoryRegisters.from(x.additionalRegisters) - ) - } - } - - val isoBox: Iso[Box[commonMod.Amount, NonMandatoryRegisters], ErgoBox] = new Iso[Box[commonMod.Amount, NonMandatoryRegisters], ErgoBox] { - override def to(x: Box[commonMod.Amount, NonMandatoryRegisters]): ErgoBox = { - val ergoBox = new ErgoBox( - value = isoAmount.to(x.value), - ergoTree = { - val bytes = Base16.decode(x.ergoTree).get - ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(bytes) - }, - creationHeight = x.creationHeight.toInt, - additionalTokens = isoTokenArray.to(x.assets), - additionalRegisters = isoNonMandatoryRegisters.to(x.additionalRegisters), - transactionId = ModifierId @@ x.transactionId, - index = x.index.toShort - ) - ergoBox - } - - override def from(x: ErgoBox): Box[commonMod.Amount, NonMandatoryRegisters] = { - val ergoTree = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(x.ergoTree) - val ergoTreeStr = Base16.encode(ergoTree) - val assets = isoTokenArray.from(x.additionalTokens) - Box[commonMod.Amount, NonMandatoryRegisters]( - boxId = Base16.encode(x.id), - ergoTree = ergoTreeStr, - value = isoAmount.from(x.value), - assets = assets, - creationHeight = x.creationHeight, - additionalRegisters = isoNonMandatoryRegisters.from(x.additionalRegisters), - transactionId = x.transactionId, - index = x.index - ) - } + override def from(x: DataInput): inputsMod.DataInput = + inputsMod.DataInput(DataIsos.isoBoxId.from(x.boxId)) } val isoEIP12UnsignedInput: Iso[inputsMod.EIP12UnsignedInput, ExtendedInputBox] = @@ -380,12 +218,12 @@ object Isos { transactionId = x.transactionId, index = x.index ) - val ergoBox = isoBox.to(box) + val ergoBox = sigma.js.Box.isoBox.to(box) val extendedInputBox = ExtendedInputBox(ergoBox, isoContextExtension.to(x.extension)) extendedInputBox } override def from(x: ExtendedInputBox): inputsMod.EIP12UnsignedInput = { - val box = isoBox.from(x.box) + val box = sigma.js.Box.isoBox.from(x.box) val ext = isoContextExtension.from(x.extension) inputsMod.EIP12UnsignedInput( boxId = box.boxId, @@ -407,7 +245,7 @@ object Isos { new UnsignedErgoLikeTransaction( inputs = sigma.js.Isos.isoArrayToIndexed(isoUnsignedInput).to(a.inputs), dataInputs = sigma.js.Isos.isoArrayToIndexed(isoDataInput).to(a.dataInputs), - outputCandidates = sigma.js.Isos.isoArrayToIndexed(isoBoxCandidate).to(a.outputs), + outputCandidates = sigma.js.Isos.isoArrayToIndexed(DataIsos.isoBoxCandidate).to(a.outputs), ) } @@ -415,7 +253,7 @@ object Isos { UnsignedTransaction( inputs = sigma.js.Isos.isoArrayToIndexed(isoUnsignedInput).from(b.inputs), dataInputs = sigma.js.Isos.isoArrayToIndexed(isoDataInput).from(b.dataInputs), - outputs = sigma.js.Isos.isoArrayToIndexed(isoBoxCandidate).from(b.outputCandidates) + outputs = sigma.js.Isos.isoArrayToIndexed(DataIsos.isoBoxCandidate).from(b.outputCandidates) ) } } @@ -426,14 +264,14 @@ object Isos { new ErgoLikeTransaction( inputs = sigma.js.Isos.isoArrayToIndexed(isoSignedInput).to(a.inputs), dataInputs = sigma.js.Isos.isoArrayToIndexed(isoDataInput).to(a.dataInputs), - outputCandidates = sigma.js.Isos.isoArrayToIndexed(isoBox).to(a.outputs), + outputCandidates = sigma.js.Isos.isoArrayToIndexed(sigma.js.Box.isoBox).to(a.outputs), ) } override def from(tx: ErgoLikeTransaction): SignedTransaction = { val inputs = sigma.js.Isos.isoArrayToIndexed(isoSignedInput).from(tx.inputs) val dataInputs = sigma.js.Isos.isoArrayToIndexed(isoDataInput).from(tx.dataInputs) - val outputs = sigma.js.Isos.isoArrayToIndexed(isoBox).from(tx.outputs) + val outputs = sigma.js.Isos.isoArrayToIndexed(sigma.js.Box.isoBox).from(tx.outputs) SignedTransaction(dataInputs, tx.id, inputs, outputs) } } diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala index 12ecf1d03f..7ab95c187e 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala @@ -6,6 +6,7 @@ import org.ergoplatform.sdk.SecretString import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel import Isos._ +import sigma.data.js.{Isos => DataIsos} import sigma.eval.SigmaDsl /** Equivalent of [[sdk.ProverBuilder]] available from JS. @@ -66,10 +67,10 @@ class ProverBuilder(parameters: BlockchainParameters, network: Byte) extends js. */ def withDHTSecret(g: String, h: String, u: String, v: String, x: js.BigInt): ProverBuilder = { _builder.withDHTData( - isoStringToGroupElement.to(g), - isoStringToGroupElement.to(h), - isoStringToGroupElement.to(u), - isoStringToGroupElement.to(v), + DataIsos.isoStringToGroupElement.to(g), + DataIsos.isoStringToGroupElement.to(h), + DataIsos.isoStringToGroupElement.to(u), + DataIsos.isoStringToGroupElement.to(v), SigmaDsl.toBigInteger(sigma.js.Isos.isoBigInt.to(x)) ) this diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala index 0a6aca4830..693c0717bb 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala @@ -51,8 +51,8 @@ class SigmaProver(_prover: sdk.SigmaProver) extends js.Object { val unreducedTx = sdk.UnreducedTransaction( unsignedTx = isoUnsignedTransaction.to(unsignedTx), boxesToSpend = sigma.js.Isos.isoArrayToIndexed(isoEIP12UnsignedInput).to(boxesToSpend), - dataInputs = sigma.js.Isos.isoArrayToIndexed(isoBox).to(dataInputs), - tokensToBurn = sigma.js.Isos.isoArrayToIndexed(isoToken.andThen(sdk.SdkIsos.isoErgoTokenToPair.inverse)).to(tokensToBurn) + dataInputs = sigma.js.Isos.isoArrayToIndexed(sigma.js.Box.isoBox).to(dataInputs), + tokensToBurn = sigma.js.Isos.isoArrayToIndexed(sigma.data.js.Isos.isoToken.andThen(sdk.SdkIsos.isoErgoTokenToPair.inverse)).to(tokensToBurn) ) val ctx = isoBlockchainStateContext.to(stateCtx) val reducedTx = _prover.reduce(ctx, unreducedTx, baseCost) diff --git a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala index a366e33d3c..31b8a84c2b 100644 --- a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala +++ b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala @@ -1,8 +1,9 @@ package org.ergoplatform.sdk.js +import io.circe.parser.parse import org.ergoplatform.ErgoBox.{AdditionalRegisters, BoxId, TokenId} import org.ergoplatform._ -import org.ergoplatform.sdk.ExtendedInputBox +import org.ergoplatform.sdk.{ExtendedInputBox, JsonCodecs} import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext import org.scalacheck.Arbitrary import sigma.ast.{Constant, SType} @@ -10,6 +11,7 @@ import sigma.data.Iso import sigma.interpreter.{ContextExtension, ProverResult} import sigma.js.AvlTree import sigma.{Coll, GroupElement} +import sigma.data.js.{Isos => DataIsos} import scala.scalajs.js @@ -31,19 +33,19 @@ class IsosSpec extends IsosSpecBase with sdk.generators.ObjectGenerators { property("Iso.isoStringToGroupElement") { forAll() { (bytes: GroupElement) => - roundtrip(Isos.isoStringToGroupElement)(bytes) + roundtrip(DataIsos.isoStringToGroupElement)(bytes) } } property("Iso.isoBoxId") { forAll(boxIdGen) { (id: BoxId) => - roundtrip(Isos.isoBoxId)(id) + roundtrip(DataIsos.isoBoxId)(id) } } property("Iso.isoHexStringToConstant") { forAll(constantGen, MinSuccessful(100)) { (c: Constant[SType]) => - roundtrip(Isos.isoHexStringToConstant)(c) + roundtrip(DataIsos.isoHexStringToConstant)(c) } } @@ -115,20 +117,20 @@ class IsosSpec extends IsosSpecBase with sdk.generators.ObjectGenerators { property("Iso.isoAmount") { forAll { (c: Long) => - roundtrip(Isos.isoAmount)(c) - Isos.isoAmount.to(js.BigInt(c.toString)) shouldBe c + roundtrip(DataIsos.isoAmount)(c) + DataIsos.isoAmount.to(js.BigInt(c.toString)) shouldBe c } } property("Iso.isoToken") { forAll(tokenIdGen, Arbitrary.arbLong.arbitrary) { (id: TokenId, amount: Long) => - roundtrip(Isos.isoToken)((id, amount)) + roundtrip(DataIsos.isoToken)((id, amount)) } } property("Iso.isoTokenArray") { forAll(ergoBoxTokens(tokensGen.sample.get)) { tokens => - roundtrip(Isos.isoTokenArray)(tokens) + roundtrip(DataIsos.isoTokenArray)(tokens) } } @@ -140,19 +142,19 @@ class IsosSpec extends IsosSpecBase with sdk.generators.ObjectGenerators { property("Iso.isoNonMandatoryRegisters") { forAll(additionalRegistersGen) { (x: AdditionalRegisters) => - roundtrip(Isos.isoNonMandatoryRegisters)(x) + roundtrip(DataIsos.isoNonMandatoryRegisters)(x) } } property("Iso.isoBoxCandidate") { forAll { (box: ErgoBoxCandidate) => - roundtrip(Isos.isoBoxCandidate)(box) + roundtrip(DataIsos.isoBoxCandidate)(box) } } property("Iso.isoBox") { forAll { (b: ErgoBox) => - roundtrip(Isos.isoBox)(b) + roundtrip(sigma.js.Box.isoBox)(b) } } diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 5e491630bd..8da420bee6 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -153,7 +153,7 @@ case class ContractTemplate( .map(p => p.name) requiredParameterNames.foreach(name => require( paramValues.contains(name), - s"value for parameter $name was not provided while it does not have a default value.")) + s"value for parameter `$name` was not provided while it does not have a default value.")) val parameterizedConstantIndices = this.parameters.map(p => p.constantIndex).toSet val constIndexToParamIndex = this.parameters.zipWithIndex.map(pi => pi._1.constantIndex -> pi._2).toMap diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala index fc95b77e61..6c0c866baf 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala @@ -122,6 +122,10 @@ object DataJsonEncoder { val w = SigmaSerializer.startWriter() DataSerializer.serialize(v, tpe, w) encodeBytes(w.toBytes) + case SHeader => + val w = SigmaSerializer.startWriter() + DataSerializer.serialize(v, tpe, w) + encodeBytes(w.toBytes) case SAvlTree => val w = SigmaSerializer.startWriter() DataSerializer.serialize(v, tpe, w) @@ -203,6 +207,10 @@ object DataJsonEncoder { val str = decodeBytes(json) val r = SigmaSerializer.startReader(str) DataSerializer.deserialize(SSigmaProp, r) + case SHeader => // for Sigma < 6.0 , exception will be thrown by DataSerializer + val str = decodeBytes(json) + val r = SigmaSerializer.startReader(str) + DataSerializer.deserialize(SHeader, r) case SBox => val value = decodeData(json.hcursor.downField(s"value").focus.get, SLong) val tree = ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(decodeBytes(json.hcursor.downField(s"ergoTree").focus.get)) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala index ae14fd831a..98e8011bcc 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala @@ -12,7 +12,7 @@ import scorex.crypto.hash.Digest32 import scorex.util.ModifierId import sigma.Extensions.ArrayOps import sigma.ast.{ErgoTree, EvaluatedValue, SType} -import sigma.data.{AvlTreeData, AvlTreeFlags, CBigInt, Digest32Coll, WrapperOf} +import sigma.data.{AvlTreeData, AvlTreeFlags, CBigInt, CHeader, Digest32Coll, WrapperOf} import sigma.eval.Extensions.EvalIterableOps import sigma.eval.SigmaDsl import sigma.interpreter.{ContextExtension, ProverResult} @@ -125,13 +125,18 @@ trait JsonCodecs { "powOnetimePk" -> h.powOnetimePk.getEncoded.asJson, "powNonce" -> h.powNonce.asJson, "powDistance" -> h.powDistance.asJson, - "votes" -> h.votes.asJson + "votes" -> h.votes.asJson, + "unparsedBytes" -> h.unparsedBytes.asJson ).asJson }) + /** + * JSON decoder for Header instance. Field "unparsedBytes" is optional for now, to preserve compatibility + * with clients using older JSON decoders (before node 5.0.23). Better to add an (empty) field anyway if possible. + * This field could become mandatory in future. + */ implicit val headerDecoder: Decoder[Header] = Decoder.instance({ cursor => for { - id <- cursor.downField("id").as[Coll[Byte]] version <- cursor.downField("version").as[Byte] parentId <- cursor.downField("parentId").as[Coll[Byte]] adProofsRoot <- cursor.downField("adProofsRoot").as[Coll[Byte]] @@ -146,8 +151,10 @@ trait JsonCodecs { powNonce <- cursor.downField("powNonce").as[Coll[Byte]] powDistance <- cursor.downField("powDistance").as[sigma.BigInt] votes <- cursor.downField("votes").as[Coll[Byte]] - } yield new CHeader(id, version, parentId, adProofsRoot, stateRoot, transactionsRoot, timestamp, nBits, - height, extensionRoot, SigmaDsl.decodePoint(minerPk), SigmaDsl.decodePoint(powOnetimePk), powNonce, powDistance, votes) + unparsedBytes <- cursor.downField("unparsedBytes").as[Option[Coll[Byte]]] + } yield CHeader(version, parentId, adProofsRoot, stateRoot.digest, transactionsRoot, timestamp, nBits, + height, extensionRoot, SigmaDsl.decodePoint(minerPk), SigmaDsl.decodePoint(powOnetimePk), powNonce, powDistance, + votes, unparsedBytes.getOrElse(Colls.emptyColl)) }) implicit val preHeaderEncoder: Encoder[PreHeader] = Encoder.instance({ v: PreHeader => diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/ErgoTreeUtils.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/ErgoTreeUtils.scala new file mode 100644 index 0000000000..ffa6505377 --- /dev/null +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/ErgoTreeUtils.scala @@ -0,0 +1,26 @@ +package org.ergoplatform.sdk.utils + +import sigma.ast.ErgoTree +import sigma.ast.ErgoTree.HeaderType +import sigma.util.Extensions.BooleanOps + +/** SDK level utilities and helper methods to work with ErgoTrees. */ +object ErgoTreeUtils { + /** Prints description of the bits in the given ErgoTree header. */ + def explainTreeHeader(header: HeaderType): String = { + // convert byte to hex + val byteToHex = (b: Byte) => f"Ox${b & 0xff}%02x" + val hasSize = ErgoTree.hasSize(header) + s""" + |Header: ${byteToHex(header)} (${header.toString}) + |Bit 0: ${header.toByte & 0x01} \\ + |Bit 1: ${(header.toByte & 0x02) >> 1}\t-- ErgoTree version ${ErgoTree.getVersion(header)} + |Bit 2: ${(header.toByte & 0x04) >> 2} / + |Bit 3: ${hasSize.toByte} \t-- size of the whole tree is serialized after the header byte + |Bit 4: ${ErgoTree.isConstantSegregation(header).toByte} \t-- constant segregation is used for this ErgoTree + |Bit 5: ${header.toByte & 0x20} \t-- reserved (should be 0) + |Bit 6: ${header.toByte & 0x40} \t-- reserved (should be 0) + |Bit 7: ${header.toByte & 0x80} \t-- header contains more than 1 byte (default == 0) + |""".stripMargin + } +} diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala index 78dfe2dc94..a337ad44a8 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala @@ -1,7 +1,7 @@ package org.ergoplatform.sdk.utils import org.ergoplatform.ErgoBox -import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, CBigInt, CGroupElement, CSigmaProp, CollType, FuncType, OptionType, PairType, RType, TrivialProp, TupleType} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, CBigInt, CGroupElement, CSigmaProp, CUnsignedBigInt, CollType, FuncType, OptionType, PairType, RType, TrivialProp, TupleType} import sigma.data.RType._ import scorex.crypto.authds.avltree.batch.BatchAVLProver import scorex.crypto.hash.{Blake2b256, Digest32} @@ -11,6 +11,7 @@ import sigma._ import sigma.ast.ErgoTree import ErgoTree.HeaderType import sigma.crypto.CryptoConstants + import java.math.BigInteger import scala.language.implicitConversions @@ -48,6 +49,7 @@ object Zero extends ZeroLowPriority { implicit val IntIsZero: Zero[Int] = CZero(0) implicit val LongIsZero: Zero[Long] = CZero(0L) implicit val BigIntIsZero: Zero[BigInt] = CZero(CBigInt(BigInteger.ZERO)) + implicit val UnsignedBigIntIsZero: Zero[UnsignedBigInt] = CZero(CUnsignedBigInt(BigInteger.ZERO)) implicit val GroupElementIsZero: Zero[GroupElement] = CZero(CGroupElement(CryptoConstants.dlogGroup.identity)) implicit val AvlTreeIsZero: Zero[AvlTree] = CZero({ val avlProver = new BatchAVLProver[Digest32, Blake2b256.type](keyLength = 32, None) @@ -88,6 +90,7 @@ object Zero extends ZeroLowPriority { case LongType => Zero[Long] case UnitType => Zero[Unit] case BigIntRType => Zero[BigInt] + case UnsignedBigIntRType => Zero[UnsignedBigInt] case BoxRType => Zero[Box] case GroupElementRType => Zero[GroupElement] case AvlTreeRType => Zero[AvlTree] diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala index c3f7b43af4..7e73b6c93d 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala @@ -13,7 +13,7 @@ import sigma.serialization.SerializerException import sigma.util.Extensions.{BigIntegerOps, EcpOps, SigmaBooleanOps} import sigma.Extensions.ArrayOps import sigma.eval.SigmaDsl -import sigma.{AvlTree, Box, Colls, Evaluation} +import sigma.{AvlTree, Box, Colls, Evaluation, Header, VersionContext} import sigma.serialization.SerializationSpecification import scala.annotation.nowarn @@ -22,16 +22,29 @@ import scala.reflect.ClassTag class DataJsonEncoderSpecification extends SerializationSpecification { object JsonCodecs extends JsonCodecs - def roundtrip[T <: SType](obj: T#WrappedType, tpe: T) = { - val json = DataJsonEncoder.encode(obj, tpe) - val res = DataJsonEncoder.decode(json) - res shouldBe obj + def roundtrip[T <: SType](obj: T#WrappedType, tpe: T, withVersion: Option[Byte] = None) = { + def test() = { + val json = DataJsonEncoder.encode(obj, tpe) + val res = DataJsonEncoder.decode(json) + res shouldBe obj + } + + withVersion match { + case Some(ver) => + VersionContext.withVersions(ver, 0) { + test() + } + case None => + test() + } } def testCollection[T <: SType](tpe: T) = { implicit val wWrapped = wrappedTypeGen(tpe) implicit val tT = Evaluation.stypeToRType(tpe) implicit val tagT = tT.classTag + + forAll { xs: Array[T#WrappedType] => roundtrip[SCollection[T]](xs.toColl, SCollection(tpe)) roundtrip[SType](xs.toColl.map(x => (x, x)).asWrappedType, SCollection(STuple(tpe, tpe))) @@ -54,6 +67,7 @@ class DataJsonEncoderSpecification extends SerializationSpecification { val tT = Evaluation.stypeToRType(tpe) @nowarn implicit val tag : ClassTag[T#WrappedType] = tT.classTag @nowarn implicit val tAny : RType[Any] = sigma.AnyType + forAll { in: (T#WrappedType, T#WrappedType) => val (x,y) = (in._1, in._2) roundtrip[SType]((x, y).asWrappedType, STuple(tpe, tpe)) @@ -98,6 +112,7 @@ class DataJsonEncoderSpecification extends SerializationSpecification { forAll { x: AvlTree => roundtrip[SAvlTree.type](x, SAvlTree) } forAll { x: Array[Byte] => roundtrip[SByteArray](x.toColl, SByteArray) } forAll { x: Box => roundtrip[SBox.type](x, SBox) } + forAll { x: Header => roundtrip[SHeader.type](x, SHeader, Some(VersionContext.V6SoftForkVersion)) } forAll { x: Option[Byte] => roundtrip[SOption[SByte.type]](x, SOption[SByte.type]) } testCollection(SOption[SLong.type]) testTuples(SOption[SLong.type]) @@ -187,25 +202,44 @@ class DataJsonEncoderSpecification extends SerializationSpecification { val tT = Evaluation.stypeToRType(tpe) @nowarn implicit val tag = tT.classTag @nowarn implicit val tAny = sigma.AnyType - forAll { x: T#WrappedType => - an[SerializerException] should be thrownBy { - DataJsonEncoder.encode(TupleColl(x, x, x).asWrappedType, STuple(tpe, tpe, tpe)) - } - // supported case - DataJsonEncoder.encode(SigmaDsl.Colls.fromItems(TupleColl(x, x)).asWrappedType, SCollection(STuple(tpe, tpe))) - // not supported case - an[SerializerException] should be thrownBy { - DataJsonEncoder.encode(SigmaDsl.Colls.fromItems(TupleColl(x, x, x)).asWrappedType, SCollection(STuple(tpe, tpe, tpe))) + def test() = { + forAll { x: T#WrappedType => + an[SerializerException] should be thrownBy { + DataJsonEncoder.encode(TupleColl(x, x, x).asWrappedType, STuple(tpe, tpe, tpe)) + } + + // supported case + DataJsonEncoder.encode(SigmaDsl.Colls.fromItems(TupleColl(x, x)).asWrappedType, SCollection(STuple(tpe, tpe))) + + // not supported case + an[SerializerException] should be thrownBy { + DataJsonEncoder.encode(SigmaDsl.Colls.fromItems(TupleColl(x, x, x)).asWrappedType, SCollection(STuple(tpe, tpe, tpe))) + } + } + } + + if (tpe == SHeader) { + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 0) { + test() } + } else { + test() } } property("AnyValue") { forAll { t: SPredefType => - testAnyValue(t) - testAnyValue(SOption(t)) + if (t == SHeader) { + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + testAnyValue(t) + testAnyValue(SOption(t)) + } + } else { + testAnyValue(t) + testAnyValue(SOption(t)) + } } } diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/utils/ErgoTreeUtilsSpec.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/utils/ErgoTreeUtilsSpec.scala new file mode 100644 index 0000000000..8dd20ecf81 --- /dev/null +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/utils/ErgoTreeUtilsSpec.scala @@ -0,0 +1,23 @@ +package org.ergoplatform.sdk.utils + +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigma.ast.ErgoTree.HeaderType + +class ErgoTreeUtilsSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers { + property("explainTreeHeader") { + ErgoTreeUtils.explainTreeHeader(HeaderType @@ 26.toByte) shouldBe + """| + |Header: Ox1a (26) + |Bit 0: 0 \ + |Bit 1: 1 -- ErgoTree version 2 + |Bit 2: 0 / + |Bit 3: 1 -- size of the whole tree is serialized after the header byte + |Bit 4: 1 -- constant segregation is used for this ErgoTree + |Bit 5: 0 -- reserved (should be 0) + |Bit 6: 0 -- reserved (should be 0) + |Bit 7: 0 -- header contains more than 1 byte (default == 0) + |""".stripMargin + } +} diff --git a/sigma-js/README.md b/sigma-js/README.md index ff867e1cb6..c1f30f3497 100644 --- a/sigma-js/README.md +++ b/sigma-js/README.md @@ -46,14 +46,15 @@ with `$` suffix, thus if X is the JS class, then X$ is the JS object, which corr ## The list of modules and their exported classes - [sigma-core module](../core/js) - contains core classes of Sigma.js library - [Type](../core/js/src/main/scala/sigma/js/Type.scala) - - [Value](../core/js/src/main/scala/sigma/js/Value.scala) - [GroupElement](../core/js/src/main/scala/sigma/js/GroupElement.scala) - [SigmaProp](../core/js/src/main/scala/sigma/js/SigmaProp.scala) - [AvlTree](../core/js/src/main/scala/sigma/js/AvlTree.scala) - [sigma-data module](../data/js) - contains classes for working with ErgoTree, addresses and all related serializers - all classes from sigma-core module + - [Value](../data/js/src/main/scala/sigma/js/Value.scala) - [ErgoTree](../data/js/src/main/scala/sigma/ast/js/ErgoTree.scala) + - [Box](../data/js/src/main/scala/sigma/js/Box.scala) - [Address](../data/js/src/main/scala/org/ergoplatform/js/Address.scala) - [Expr](../data/js/src/main/scala/sigma/ast/js/Expr.scala) diff --git a/sigma-js/package-lock.json b/sigma-js/package-lock.json index 893a0ee141..c34f802f6e 100644 --- a/sigma-js/package-lock.json +++ b/sigma-js/package-lock.json @@ -1,12 +1,12 @@ { "name": "sigmastate-js", - "version": "0.3.0", + "version": "0.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "sigmastate-js", - "version": "0.3.0", + "version": "0.4.0", "license": "MIT", "dependencies": { "@fleet-sdk/common": "0.1.3", diff --git a/sigma-js/package.json b/sigma-js/package.json index 77ff920585..f6900571bd 100644 --- a/sigma-js/package.json +++ b/sigma-js/package.json @@ -1,6 +1,6 @@ { "name": "sigmastate-js", - "version": "0.4.0", + "version": "0.4.2", "description": "Sigma.js library", "files": [ "dist/", diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index b70ca39258..53baa3b026 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -1,7 +1,7 @@ declare module "sigmastate-js/main" { import { Amount, - Box, + Box as FBox, EIP12UnsignedInput, NonMandatoryRegisters, SignedTransaction, TokenAmount, UnsignedTransaction @@ -173,6 +173,10 @@ declare module "sigmastate-js/main" { valueLengthOpt: number | undefined; } + export declare class Box { + box: FBox; + } + export declare class PreHeader { /** Block version, to be increased on every soft and hardfork. */ version: number; @@ -301,6 +305,8 @@ declare module "sigmastate-js/main" { static ofSigmaProp(pointHex: string): Value; + static ofBox(box: FBox): Value; + static pairOf(left: Value, right: Value): Value<[R, L]>; static collOf(items: T[], elemType: Type): Value; @@ -498,7 +504,7 @@ declare module "sigmastate-js/main" { stateCtx: BlockchainStateContext, unsignedTx: UnsignedTransaction, boxesToSpend: EIP12UnsignedInput[], - dataInputs: Box[], + dataInputs: FBox[], tokensToBurn: TokenAmount[], baseCost: number): ReducedTransaction; diff --git a/sigma-js/tests/js/Box.spec.js b/sigma-js/tests/js/Box.spec.js new file mode 100644 index 0000000000..24238013ba --- /dev/null +++ b/sigma-js/tests/js/Box.spec.js @@ -0,0 +1,28 @@ +const {Box$, Value$} = require("sigmastate-js/main"); + +describe("Box", () => { + it("should serialize from/to hex", () => { + let boxHex = "63b96000d1968302010100ff83020193040204020100c0843d000401010e32297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d6505a4a7b5a2e7a4a4dd3a05feffffffffffffffff01003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e01" + let deserialized = Value$.fromHex(boxHex); + let expected = { + additionalRegisters: { + R4: '0101', + R5: '0e32297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d65', + R6: '05a4a7b5a2e7a4a4dd3a', + R7: '05feffffffffffffffff01' + }, + assets: [], + boxId: '3a0089be265460e29ca47d26e5b55a6f3e3ffaf5b4aed941410a2437913848ad', + creationHeight: 1000000, + ergoTree: '00d1968302010100ff83020193040204020100', + index: 1, + transactionId: '003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e', + value: '12345' + } + let fleetBox = deserialized.data.box + expect(fleetBox).toEqual(expected) + + let newBoxValue = Value$.ofBox(fleetBox) + expect(newBoxValue.toHex()).toEqual(boxHex) + }); +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000000..161dcfd54a --- /dev/null +++ b/yarn.lock @@ -0,0 +1,3322 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@fleet-sdk/common@0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@fleet-sdk/common/-/common-0.1.3.tgz#f9e478b896e9192f03d2d5b30570af4e8632eb15" + integrity sha512-gYEkHhgGpgIcmCL3nCw8E9zHkT2WLmR+mPdxFlUE6fwcwISURbJrP6W9mF7D5Y0ShAP5Is2w3edh7AyIc7ctIQ== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@noble/hashes@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.4.tgz#2611ebf5764c1bf754da7c7794de4fb30512336d" + integrity sha512-+PYsVPrTSqtVjatKt2A/Proukn2Yrz61OBThOCKErc5w2/r1Fh37vbDv0Eah7pyNltrmacjwTvdw3JoR+WE4TA== + +"@noble/hashes@^1.1.4": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@types/eslint-scope@^3.7.0": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "9.6.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.0.tgz#51d4fe4d0316da9e9f2c80884f2c20ed5fb022ff" + integrity sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/estree@^0.0.46": + version "0.0.46" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" + integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== + +"@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/minimatch@*": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/node@*": + version "22.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.0.0.tgz#04862a2a71e62264426083abe1e27e87cac05a30" + integrity sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw== + dependencies: + undici-types "~6.11.1" + +"@webassemblyjs/ast@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f" + integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + +"@webassemblyjs/floating-point-hex-parser@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz#34d62052f453cd43101d72eab4966a022587947c" + integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA== + +"@webassemblyjs/helper-api-error@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz#aaea8fb3b923f4aaa9b512ff541b013ffb68d2d4" + integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w== + +"@webassemblyjs/helper-buffer@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz#d026c25d175e388a7dbda9694e91e743cbe9b642" + integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA== + +"@webassemblyjs/helper-numbers@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz#7ab04172d54e312cc6ea4286d7d9fa27c88cd4f9" + integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.0" + "@webassemblyjs/helper-api-error" "1.11.0" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz#85fdcda4129902fe86f81abf7e7236953ec5a4e1" + integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA== + +"@webassemblyjs/helper-wasm-section@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz#9ce2cc89300262509c801b4af113d1ca25c1a75b" + integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + +"@webassemblyjs/ieee754@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz#46975d583f9828f5d094ac210e219441c4e6f5cf" + integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.0.tgz#f7353de1df38aa201cba9fb88b43f41f75ff403b" + integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.0.tgz#86e48f959cf49e0e5091f069a709b862f5a2cadf" + integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw== + +"@webassemblyjs/wasm-edit@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz#ee4a5c9f677046a210542ae63897094c2027cb78" + integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/helper-wasm-section" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + "@webassemblyjs/wasm-opt" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + "@webassemblyjs/wast-printer" "1.11.0" + +"@webassemblyjs/wasm-gen@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz#3cdb35e70082d42a35166988dda64f24ceb97abe" + integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/ieee754" "1.11.0" + "@webassemblyjs/leb128" "1.11.0" + "@webassemblyjs/utf8" "1.11.0" + +"@webassemblyjs/wasm-opt@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz#1638ae188137f4bb031f568a413cd24d32f92978" + integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + +"@webassemblyjs/wasm-parser@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz#3e680b8830d5b13d1ec86cc42f38f3d4a7700754" + integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-api-error" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/ieee754" "1.11.0" + "@webassemblyjs/leb128" "1.11.0" + "@webassemblyjs/utf8" "1.11.0" + +"@webassemblyjs/wast-printer@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz#680d1f6a5365d6d401974a8e949e05474e1fab7e" + integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.0.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== + +"@webpack-cli/info@^1.2.2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.3.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn@^8.0.4, acorn@^8.8.2: + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-keywords@^3.1.0, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.1.0, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== + +async-each@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.6.tgz#52f1d9403818c179b7561e11a5d1b77eb2160e77" + integrity sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^2.6.4: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +body-parser@1.20.2: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg== + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +browserslist@^4.14.5: + version "4.23.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.2.tgz#244fe803641f1c19c28c48c4b6ec9736eb3d32ed" + integrity sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA== + dependencies: + caniuse-lite "^1.0.30001640" + electron-to-chromium "^1.4.820" + node-releases "^2.0.14" + update-browserslist-db "^1.1.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +call-bind@^1.0.2, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-lite@^1.0.30001640: + version "1.0.30001645" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001645.tgz#4c4b7427683dea1170a152cd1654be8d0da7bd71" + integrity sha512-GFtY2+qt91kzyMk6j48dJcwJVq5uTkk71XxE3RtScx7XWRLsO7bU44LOFkOZYR8w9YMS0UhPSYpN/6rAMImmLw== + +chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +colorette@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +component-emitter@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" + integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +concat-with-sourcemaps@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.0.7.tgz#9420e100fb984cbde11a78dca2d818306bc8f0d2" + integrity sha512-5i4Spc9NNvVXzkR77x2kjcYCDZMNPLzP7ZBzJMNKZjXzk+E6tRVL/lPlYw60VM3hb7gf+iBQn2x1T8TpMN0SEw== + dependencies: + source-map "^0.6.1" + +connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.1.1: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +deep-equal@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.2.tgz#78a561b7830eef3134c7f6f3a3d6af272a678761" + integrity sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg== + dependencies: + is-arguments "^1.1.1" + is-date-object "^1.0.5" + is-regex "^1.1.4" + object-is "^1.1.5" + object-keys "^1.1.1" + regexp.prototype.flags "^1.5.1" + +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== + +dns-packet@^1.3.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" + integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ== + dependencies: + buffer-indexof "^1.0.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.820: + version "1.5.4" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz#cd477c830dd6fca41fbd5465c1ff6ce08ac22343" + integrity sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.7.0: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enquirer@^2.3.6: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== + dependencies: + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" + +envinfo@^7.7.3: + version "7.13.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31" + integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== + +errno@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e" + integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA== + +escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +eventsource@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" + integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +express@^4.17.1: + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +faye-websocket@^0.11.3, faye-websocket@^0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +follow-redirects@^1.0.0: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA== + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.3, glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw== + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +hasown@^2.0.0, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc" + integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-middleware@0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== + +ip@^1.1.0, ip@^1.1.5: + version "1.1.9" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.9.tgz#8dfbcc99a754d07f425310b86a99546b1151e396" + integrity sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ== + +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + +is-accessor-descriptor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz#3223b10628354644b86260db29b3e693f5ceedd4" + integrity sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA== + dependencies: + hasown "^2.0.0" + +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q== + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-core-module@^2.13.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" + integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== + dependencies: + hasown "^2.0.2" + +is-data-descriptor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz#2109164426166d32ea38c405c1e0945d9e6a4eeb" + integrity sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw== + dependencies: + hasown "^2.0.0" + +is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-descriptor@^0.1.0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.7.tgz#2727eb61fd789dcd5bdf0ed4569f551d2fe3be33" + integrity sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg== + dependencies: + is-accessor-descriptor "^1.0.1" + is-data-descriptor "^1.0.1" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.3.tgz#92d27cb3cd311c4977a4db47df457234a13cb306" + integrity sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw== + dependencies: + is-accessor-descriptor "^1.0.1" + is-data-descriptor "^1.0.1" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw== + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== + dependencies: + kind-of "^3.0.2" + +is-path-cwd@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== + dependencies: + is-buffer "^1.1.5" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash@^4.17.11, lodash@^4.17.14: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loglevel@^1.6.8: + version "1.9.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.1.tgz#d63976ac9bcd03c7c873116d41c2a85bafff1be7" + integrity sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg== + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== + dependencies: + object-visit "^1.0.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memory-fs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +"mime-db@>= 1.43.0 < 2": + version "1.53.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" + integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== + +mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.4.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.1, mkdirp@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ== + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + +nan@^2.12.1: + version "2.20.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" + integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + +node-releases@^2.0.14: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== + dependencies: + isobject "^3.0.1" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +portfinder@^1.0.26: + version "1.0.32" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" + integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg== + dependencies: + async "^2.6.4" + debug "^3.2.7" + mkdirp "^0.5.6" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +qs@^6.12.3: + version "6.12.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.3.tgz#e43ce03c8521b9c7fd7f1f13e514e5ca37727754" + integrity sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ== + dependencies: + side-channel "^1.0.6" + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^2.0.1, readable-stream@^2.0.2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== + dependencies: + resolve "^1.9.0" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg== + dependencies: + resolve-from "^3.0.0" + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== + +resolve@^1.9.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +schema-utils@^3.0.0, schema-utils@^3.1.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^1.10.8: + version "1.10.14" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.14.tgz#ee51d84d9dcecc61e07e4aba34f229ab525c1574" + integrity sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA== + dependencies: + node-forge "^0.10.0" + +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +sigmajs-crypto-facade@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/sigmajs-crypto-facade/-/sigmajs-crypto-facade-0.0.7.tgz#b81dd53750c38a6a7856153aad2705269bc5afb6" + integrity sha512-4XK8ZS9NKAbo8aGnU6o5GkBW6Upl8+OK8A1KreVDMAamfvZ0iq4LoVH8rHaeEPf9moVtaC4QZY5RYI+0OwiydA== + dependencies: + "@noble/hashes" "^1.1.4" + +signal-exit@^3.0.0, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs-client@^1.5.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.6.1.tgz#350b8eda42d6d52ddc030c39943364c11dcad806" + integrity sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw== + dependencies: + debug "^3.2.7" + eventsource "^2.0.2" + faye-websocket "^0.11.4" + inherits "^2.0.4" + url-parse "^1.5.10" + +sockjs@^0.3.21: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-list-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-loader@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-2.0.0.tgz#6651dc5a46beb2b914abacd00b8f9dd0e7757820" + integrity sha512-DJLK+gR9hlx+58yGU54EDAQZzR/TksgrtvRtyEBWnd5DR7O4n0RgdyO/KBwJ76zF+wDiFRT/1vdV3SdLUR68Lg== + dependencies: + abab "^2.0.5" + iconv-lite "^0.6.2" + source-map "^0.6.1" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.1.1: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.31.3" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.3.tgz#b24b7beb46062f4653f049eea4f0cd165d0f0c38" + integrity sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +undici-types@~6.11.1: + version "6.11.1" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.11.1.tgz#432ea6e8efd54a48569705a699e62d8f4981b197" + integrity sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +update-browserslist-db@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" + integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== + +url-parse@^1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.4.tgz#adca77b3562d56b72746e76b330b7f27b6721f3c" + integrity sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg== + dependencies: + punycode "^1.4.1" + qs "^6.12.3" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-compile-cache@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz#cdada8bec61e15865f05d097c5f4fd30e94dc128" + integrity sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +watchpack@^2.0.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff" + integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webpack-cli@4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.5.0.tgz#b5213b84adf6e1f5de6391334c9fa53a48850466" + integrity sha512-wXg/ef6Ibstl2f50mnkcHblRPN/P9J4Nlod5Hg9HGFgSeF8rsqDGHJeVe4aR26q9l62TUJi6vmvC2Qz96YJw1Q== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.0.1" + "@webpack-cli/info" "^1.2.2" + "@webpack-cli/serve" "^1.3.0" + colorette "^1.2.1" + commander "^7.0.0" + enquirer "^2.3.6" + execa "^5.0.0" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + v8-compile-cache "^2.2.0" + webpack-merge "^5.7.3" + +webpack-dev-middleware@^3.7.2: + version "3.7.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" + integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@3.11.2: + version "3.11.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz#695ebced76a4929f0d5de7fd73fafe185fe33708" + integrity sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.8" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.3.1" + http-proxy-middleware "0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.3" + killable "^1.0.1" + loglevel "^1.6.8" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.26" + schema-utils "^1.0.0" + selfsigned "^1.10.8" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "^0.3.21" + sockjs-client "^1.5.0" + spdy "^4.0.2" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.2" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "^13.3.2" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd" + integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + +webpack@5.24.3: + version "5.24.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.24.3.tgz#6ec0f5059f8d7c7961075fa553cfce7b7928acb3" + integrity sha512-x7lrWZ7wlWAdyKdML6YPvfVZkhD1ICuIZGODE5SzKJjqI9A4SpqGTjGJTc6CwaHqn19gGaoOR3ONJ46nYsn9rw== + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.46" + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/wasm-edit" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + acorn "^8.0.4" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.7.0" + es-module-lexer "^0.4.0" + eslint-scope "^5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.4" + json-parse-better-errors "^1.0.2" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.0.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.1" + watchpack "^2.0.0" + webpack-sources "^2.1.1" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.3.tgz#ccc96e4add5fd6fedbc491903075c85c5a11d9ee" + integrity sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA== + dependencies: + async-limiter "~1.0.0" + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2"