diff --git a/.gitignore b/.gitignore
index df18ce8ab1..d328645cde 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
*.fdb_latexmk
*.gz
+yarn.lock
*.log
docs/spec/out/
test-out/
diff --git a/core/js/src/main/scala/sigma/crypto/Platform.scala b/core/js/src/main/scala/sigma/crypto/Platform.scala
index 88001ba140..777789ba24 100644
--- a/core/js/src/main/scala/sigma/crypto/Platform.scala
+++ b/core/js/src/main/scala/sigma/crypto/Platform.scala
@@ -253,6 +253,7 @@ object Platform {
case _: Boolean => tpe == SBoolean
case _: Byte | _: Short | _: Int | _: Long => tpe.isInstanceOf[SNumericType]
case _: BigInt => tpe == SBigInt
+ case _: UnsignedBigInt => tpe == SUnsignedBigInt
case _: String => tpe == SString
case _: GroupElement => tpe.isGroupElement
case _: SigmaProp => tpe.isSigmaProp
diff --git a/core/js/src/main/scala/sigma/js/Isos.scala b/core/js/src/main/scala/sigma/js/Isos.scala
index 767a358d62..bc88c46457 100644
--- a/core/js/src/main/scala/sigma/js/Isos.scala
+++ b/core/js/src/main/scala/sigma/js/Isos.scala
@@ -1,7 +1,7 @@
package sigma.js
import sigma.{Coll, Colls}
-import sigma.data.{CBigInt, Iso, RType}
+import sigma.data.{CBigInt, CUnsignedBigInt, Iso, RType}
import java.math.BigInteger
import scala.reflect.ClassTag
@@ -42,6 +42,18 @@ object Isos {
}
}
+ implicit val isoUnsignedBigInt: Iso[js.BigInt, sigma.UnsignedBigInt] = new Iso[js.BigInt, sigma.UnsignedBigInt] {
+ override def to(x: js.BigInt): sigma.UnsignedBigInt = {
+ CUnsignedBigInt(new BigInteger(x.toString(10)))
+ }
+
+ override def from(x: sigma.UnsignedBigInt): js.BigInt = {
+ val bi = x.asInstanceOf[CUnsignedBigInt].wrappedValue
+ val s = bi.toString(10)
+ js.BigInt(s)
+ }
+ }
+
implicit val isoBigIntToLong: Iso[js.BigInt, Long] = new Iso[js.BigInt, Long] {
override def to(x: js.BigInt): Long = java.lang.Long.parseLong(x.toString(10))
diff --git a/core/js/src/main/scala/sigma/js/Type.scala b/core/js/src/main/scala/sigma/js/Type.scala
index b323273a0c..ff391eba66 100644
--- a/core/js/src/main/scala/sigma/js/Type.scala
+++ b/core/js/src/main/scala/sigma/js/Type.scala
@@ -35,6 +35,9 @@ object Type extends js.Object {
/** Descriptor of ErgoScript type BigInt. */
val BigInt = new Type(sigma.BigIntRType)
+ /** Descriptor of ErgoScript type UnsignedBigInt. */
+ val UnsignedBigInt = new Type(sigma.UnsignedBigIntRType)
+
/** Descriptor of ErgoScript type GroupElement. */
val GroupElement = new Type(sigma.GroupElementRType)
diff --git a/core/jvm/src/main/scala/sigma/crypto/Platform.scala b/core/jvm/src/main/scala/sigma/crypto/Platform.scala
index b71694e81b..13c8d6515e 100644
--- a/core/jvm/src/main/scala/sigma/crypto/Platform.scala
+++ b/core/jvm/src/main/scala/sigma/crypto/Platform.scala
@@ -185,6 +185,7 @@ object Platform {
case _: Int => tpe == SInt
case _: Long => tpe == SLong
case _: BigInt => tpe == SBigInt
+ case _: UnsignedBigInt => tpe == SUnsignedBigInt
case _: String => tpe == SString // TODO v6.0: remove this case (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
case _: GroupElement => tpe.isGroupElement
case _: SigmaProp => tpe.isSigmaProp
diff --git a/core/shared/src/main/scala/sigma/Colls.scala b/core/shared/src/main/scala/sigma/Colls.scala
index 05ac99caac..6cb8a19c2e 100644
--- a/core/shared/src/main/scala/sigma/Colls.scala
+++ b/core/shared/src/main/scala/sigma/Colls.scala
@@ -45,6 +45,19 @@ trait Coll[@specialized A] {
*/
def apply(i: Int): A
+ /** The element at given index or None if there is no such element. Indices start at `0`.
+ *
+ * @param i the index
+ * @return the element at the given index, or None if there is no such element
+ */
+ def get(i: Int): Option[A] = {
+ if (isDefinedAt(i)) {
+ Some(apply(i))
+ } else {
+ None
+ }
+ }
+
/** Tests whether this $coll contains given index.
*
* The implementations of methods `apply` and `isDefinedAt` turn a `Coll[A]` into
@@ -76,6 +89,18 @@ trait Coll[@specialized A] {
* produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */
def zip[@specialized B](ys: Coll[B]): Coll[(A, B)]
+ /**
+ * @return true if first elements of this collection form given `ys` collection, false otherwise.
+ * E.g. [1,2,3] starts with [1,2]
+ */
+ def startsWith(ys: Coll[A]): Boolean
+
+ /**
+ * @return true if last elements of this collection form given `ys` collection, false otherwise.
+ * E.g. [1,2,3] ends with [2,3]
+ */
+ def endsWith(ys: Coll[A]): Boolean
+
/** Tests whether a predicate holds for at least one element of this collection.
* @param p the predicate used to test elements.
* @return `true` if the given predicate `p` is satisfied by at least one element of this collection, otherwise `false`
diff --git a/core/shared/src/main/scala/sigma/Evaluation.scala b/core/shared/src/main/scala/sigma/Evaluation.scala
index d86b7c1650..c3ffcc8896 100644
--- a/core/shared/src/main/scala/sigma/Evaluation.scala
+++ b/core/shared/src/main/scala/sigma/Evaluation.scala
@@ -25,6 +25,7 @@ object Evaluation {
case SAny => AnyType
case SUnit => UnitType
case SBigInt => BigIntRType
+ case SUnsignedBigInt => UnsignedBigIntRType
case SBox => BoxRType
case SContext => ContextRType
case SGlobal => SigmaDslBuilderRType
@@ -67,6 +68,7 @@ object Evaluation {
case AnyType => SAny
case UnitType => SUnit
case BigIntRType => SBigInt
+ case UnsignedBigIntRType => SUnsignedBigInt
case GroupElementRType => SGroupElement
case AvlTreeRType => SAvlTree
case ot: OptionType[_] => SOption(rtypeToSType(ot.tA))
diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala
index df2b419273..1f62290ac3 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,208 @@ 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 UnsignedBigInteger 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 UnsignedBigInteger 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)
+
+ /**
+ * @return UnsignedBigInteger whose value is multiplicative inverse mod `m` ,
+ * or throw exception if there is no inverse
+ */
+ 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 +365,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 +662,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 +776,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 +925,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 +965,34 @@ 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): UnsignedBigInt
+
+ /** 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
+
+ /** Constructs wrapped into optional type `value` */
+ def some[T](value: T)(implicit cT: RType[T]): Option[T]
+
+ /** Constructs empty optional value */
+ 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..fad8cba6a5 100644
--- a/core/shared/src/main/scala/sigma/VersionContext.scala
+++ b/core/shared/src/main/scala/sigma/VersionContext.scala
@@ -1,12 +1,12 @@
package sigma
-import VersionContext.JitActivationVersion
+import VersionContext.{JitActivationVersion, V6SoftForkVersion}
import scala.util.DynamicVariable
/** Represent currently activated protocol version and currently executed ErgoTree version.
*
- * This parameters, once set in DynamicVariable can be accessed everywhere on the current
+ * These parameters, once set in DynamicVariable can be accessed everywhere on the current
* thread.
*
* @param activatedVersion Currently activated script version == Block.headerVersion - 1
@@ -15,12 +15,23 @@ import scala.util.DynamicVariable
* @see
*/
case class VersionContext(activatedVersion: Byte, ergoTreeVersion: Byte) {
- require(ergoTreeVersion <= activatedVersion,
+ // ergoTreeVersion <= activatedVersion condition added in 5.0,
+ // and we check this condition only since 5.0 then
+ require(activatedVersion < VersionContext.JitActivationVersion || ergoTreeVersion <= activatedVersion,
s"In a valid VersionContext ergoTreeVersion must never exceed activatedVersion: $this")
/** @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 tree version is corresponding to 6.0
+ */
+ def isV3OrLaterErgoTreeVersion: Boolean = ergoTreeVersion >= V6SoftForkVersion
+
+ /** @return true, if the activated script version of Ergo protocol on the network is
+ * including v6.0 update. */
+ def isV6Activated: Boolean = activatedVersion >= V6SoftForkVersion
+
}
object VersionContext {
@@ -31,13 +42,19 @@ 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. */
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
@@ -83,7 +100,7 @@ object VersionContext {
_versionContext.withValue(VersionContext(activatedVersion, ergoTreeVersion))(block)
/** Checks the version context has the given versions*/
- def checkVersions(activatedVersion: Byte, ergoTreeVersion: Byte) = {
+ def checkVersions(activatedVersion: Byte, ergoTreeVersion: Byte): Unit = {
val ctx = VersionContext.current
if (ctx.activatedVersion != activatedVersion || ctx.ergoTreeVersion != ergoTreeVersion) {
val expected = VersionContext(activatedVersion, ergoTreeVersion)
diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala
index f75cbc9e8b..66a46b00de 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
@@ -102,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.isV3OrLaterErgoTreeVersion) {
+ v6PredefTypes
+ } else {
+ v5PredefTypes
+ }
+ }
+
+ private val v5Types = Seq(
+ SBoolean, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader,
+ SAvlTree, SBox, SOption, SCollection, SBigInt
+ )
+ 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
/** 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 ErgoTrees 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 ErgoTrees 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 ErgoTree.
+ *
+ * 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,
- SAvlTree, SBox, SOption, SCollection, SBigInt
- ).map { t => (t.typeId, t) }.toMap
+ def types: Map[Byte, STypeCompanion] = if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
+ 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.
@@ -156,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.isV3OrLaterErgoTreeVersion => x.isInstanceOf[UnsignedBigInt]
case SGroupElement => x.isInstanceOf[GroupElement]
case SSigmaProp => x.isInstanceOf[SigmaProp]
case SBox => x.isInstanceOf[Box]
@@ -219,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. */
@@ -286,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
@@ -338,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
@@ -375,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.isV3OrLaterErgoTreeVersion => bi.toByte // toByteExact from int is called under the hood
+ case ubi: UnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => ubi.toByte // toByteExact from int is called under the hood
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
@@ -396,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.isV3OrLaterErgoTreeVersion => bi.toShort // toShortExact from int is called under the hood
+ case ubi: UnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => ubi.toShort // toShortExact from int is called under the hood
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
@@ -419,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.isV3OrLaterErgoTreeVersion => bi.toInt
+ case ubi: UnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => ubi.toInt
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
@@ -444,45 +484,93 @@ 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.isV3OrLaterErgoTreeVersion => bi.toLong
+ case ubi: UnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => 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)
/** The maximum size of BigInteger value in byte array representation. */
val MaxSizeInBytes: Long = SigmaConstants.MaxBigIntSizeInBytes.value
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.isV3OrLaterErgoTreeVersion => 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.isV3OrLaterErgoTreeVersion => 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
+
+ /** The maximum size of BigInteger value in byte array representation. */
+ val MaxSizeInBytes: Long = SigmaConstants.MaxBigIntSizeInBytes.value
+
+ 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")
+ }
}
}
@@ -583,7 +671,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 }
}
@@ -602,7 +690,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}]"
@@ -623,33 +712,34 @@ 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)
- implicit val SByteArrayOption = SOption(SByteArray)
- implicit val SShortOption = SOption(SShort)
- implicit val SIntOption = SOption(SInt)
- implicit val SLongOption = SOption(SLong)
- implicit val SBigIntOption = SOption(SBigInt)
- implicit val SBooleanOption = SOption(SBoolean)
- implicit val SAvlTreeOption = SOption(SAvlTree)
- implicit val SGroupElementOption = SOption(SGroupElement)
- implicit val SSigmaPropOption = SOption(SSigmaProp)
- implicit val SBoxOption = SOption(SBox)
+ lazy val SByteOption = SOption(SByte)
+ lazy val SByteArrayOption = SOption(SByteArray)
+ lazy val SShortOption = SOption(SShort)
+ lazy val SIntOption = SOption(SInt)
+ lazy val SLongOption = SOption(SLong)
+ lazy val SBigIntOption = SOption(SBigInt)
+ lazy val SUnsignedBigIntOption = SOption(SUnsignedBigInt)
+ lazy val SBooleanOption = SOption(SBoolean)
+ lazy val SAvlTreeOption = SOption(SAvlTree)
+ lazy val SGroupElementOption = SOption(SGroupElement)
+ lazy val SSigmaPropOption = SOption(SSigmaProp)
+ lazy val SBoxOption = SOption(SBox)
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
@@ -685,7 +775,7 @@ object SCollection extends STypeCompanion {
override val reprClass: RClass[_] = RClass(classOf[Coll[_]])
override def typeId = SCollectionType.CollectionTypeCode
- /** Costructs a collection type with the given type of elements. */
+ /** Constructs a collection type with the given type of elements. */
implicit def typeCollection[V <: SType](implicit tV: V): SCollection[V] = SCollection[V](tV)
/** Helper descriptors reused across different method descriptors. */
@@ -701,29 +791,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..5684da8142 100644
--- a/core/shared/src/main/scala/sigma/ast/package.scala
+++ b/core/shared/src/main/scala/sigma/ast/package.scala
@@ -2,6 +2,8 @@ package sigma
import sigma.kiama.rewriting.Rewriter.{everywherebu, rewrite, rule}
+import scala.annotation.tailrec
+
package object ast {
/** Type alias for a substitution of type variables with their corresponding types. */
@@ -33,6 +35,7 @@ package object ast {
private val unifiedWithoutSubst = Some(EmptySubst)
/** Finds a substitution `subst` of type variables such that unifyTypes(applySubst(t1, subst), t2) shouldBe Some(emptySubst) */
+ @tailrec
def unifyTypes(t1: SType, t2: SType): Option[STypeSubst] = (t1, t2) match {
case (_ @ STypeVar(n1), _ @ STypeVar(n2)) =>
if (n1 == n2) unifiedWithoutSubst else None
@@ -137,6 +140,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 100%
rename from data/shared/src/main/scala/sigma/crypto/BigIntegers.scala
rename to core/shared/src/main/scala/sigma/crypto/BigIntegers.scala
diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala
index bbf1a85e46..5721a43e9c 100644
--- a/core/shared/src/main/scala/sigma/data/CBigInt.scala
+++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala
@@ -1,7 +1,7 @@
package sigma.data
import sigma.util.Extensions.BigIntegerOps
-import sigma.{BigInt, Coll, Colls}
+import sigma.{BigInt, Coll, Colls, UnsignedBigInt, VersionContext}
import java.math.BigInteger
@@ -11,6 +11,14 @@ import java.math.BigInteger
*/
case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with WrapperOf[BigInteger] {
+ // the check is identical to `fitsIn256Bits` in Extensions
+ // the check is used in >= 3 trees as there are ways to ask for big int
+ // deserialization now aside of register / context var deserialization
+ // e.g. Header w. Autolykos v1 deserialization
+ if (VersionContext.current.isV3OrLaterErgoTreeVersion && wrappedValue.bitLength() > 255) {
+ throw new ArithmeticException(s"Too big bigint value $wrappedValue")
+ }
+
override def toByte: Byte = wrappedValue.toByteExact
override def toShort: Short = wrappedValue.toShortExact
@@ -28,11 +36,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 +52,31 @@ 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))
+
+ // there is no need to do .toSignedBigIntValueExact check, as this method is introduced in trees v3,
+ // and for trees v3, the check done in constructor
+ override def xor(that: BigInt): BigInt = CBigInt(wrappedValue.xor(that.asInstanceOf[CBigInt].wrappedValue))
+
+ // there is no need to do .toSignedBigIntValueExact check, as this method is introduced in trees v3,
+ // and for trees v3, the check done in constructor
+ override def shiftLeft(n: Int): BigInt = CBigInt(wrappedValue.shiftLeft(n).toSignedBigIntValueExact)
+
+ // there is no need to do .toSignedBigIntValueExact check, as this method is introduced in trees v3,
+ // and for trees v3, the check done in constructor
+ override def shiftRight(n: Int): BigInt = CBigInt(wrappedValue.shiftRight(n).toSignedBigIntValueExact)
+
+ override def toUnsigned: UnsignedBigInt = {
+ CUnsignedBigInt(this.wrappedValue)
+ }
+
+ override def toUnsignedMod(m: UnsignedBigInt): UnsignedBigInt = {
+ CUnsignedBigInt(this.wrappedValue.mod(m.asInstanceOf[CUnsignedBigInt].wrappedValue))
+ }
+
}
+
diff --git a/core/shared/src/main/scala/sigma/data/CGroupElement.scala b/core/shared/src/main/scala/sigma/data/CGroupElement.scala
index ed4849f0d7..047896f28f 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.
*
@@ -13,14 +13,18 @@ case class CGroupElement(override val wrappedValue: Ecp) extends GroupElement wi
override def toString: String = s"GroupElement(${wrappedValue.showECPoint})"
- override def getEncoded: Coll[Byte] =
+ override def getEncoded: Coll[Byte] = {
Colls.fromArray(GroupElementSerializer.toBytes(wrappedValue))
+ }
override def isIdentity: Boolean = CryptoFacade.isInfinityPoint(wrappedValue)
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/CUnsignedBigInt.scala b/core/shared/src/main/scala/sigma/data/CUnsignedBigInt.scala
new file mode 100644
index 0000000000..56ffc2029b
--- /dev/null
+++ b/core/shared/src/main/scala/sigma/data/CUnsignedBigInt.scala
@@ -0,0 +1,101 @@
+package sigma.data
+
+import sigma.{BigInt, Coll, Colls, UnsignedBigInt}
+import sigma.crypto.BigIntegers
+import sigma.util.Extensions.BigIntegerOps
+
+import java.math.BigInteger
+
+
+/** 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 ArithmeticException(s"Attempt to create unsigned value from negative big integer $wrappedValue")
+ }
+
+ if (wrappedValue.bitLength() > 256) {
+ throw new ArithmeticException(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))
+
+ override def shiftRight(n: Int): UnsignedBigInt = CUnsignedBigInt(wrappedValue.shiftRight(n))
+
+ 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/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala
index 980d5992d6..a95dd66839 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}
@@ -39,6 +41,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) {
@@ -135,14 +141,44 @@ 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 =>
+ // in v6, we do compare PairColl with CollOverArray for tree version >= 3
+ // see https://github.com/ergoplatform/sigmastate-interpreter/issues/909
+ if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
+ 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) {
@@ -237,8 +273,17 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder =>
class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R]) extends PairColl[L,R] {
- override def equals(that: scala.Any) = (this eq that.asInstanceOf[AnyRef]) || (that match {
- case that: PairColl[_,_] if that.tItem == this.tItem => ls == that.ls && rs == that.rs
+ override def equals(that: scala.Any): Boolean = (this eq that.asInstanceOf[AnyRef]) || (that match {
+ case that: PairColl[_, _] if that.tItem == this.tItem =>
+ ls == that.ls && rs == that.rs
+ case that: CollOverArray[Any] if that.tItem == this.tItem =>
+ // in v6, we do compare PairColl with CollOverArray for tree version >= 3
+ // see https://github.com/ergoplatform/sigmastate-interpreter/issues/909
+ if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
+ equalsPairCollWithCollOverArray(this.asInstanceOf[PairColl[Any, Any]], that)
+ } else {
+ false
+ }
case _ => false
})
@@ -350,6 +395,16 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R
def zip[@specialized B](ys: Coll[B]): PairColl[(L,R), B] = builder.pairColl(this, ys)
+ def startsWith(ys: Coll[(L, R)]): Boolean = 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
override def flatMap[B: RType](f: ((L, R)) => Coll[B]): Coll[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 df737a94ae..506e623590 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,15 @@ 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, "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 +341,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 +357,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 +420,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 +518,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/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 b4b20feb4c..9655065adc 100644
--- a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala
+++ b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala
@@ -2,6 +2,7 @@ package sigma.serialization
import debox.cfor
import sigma.ast._
+import sigma.crypto.BigIntegers
import sigma.data._
import sigma.util.Extensions.{BigIntOps, BigIntegerOps, CoreAvlTreeOps, GroupElementOps, SigmaPropOps}
import sigma.validation.ValidationRules.CheckSerializableTypeCode
@@ -35,6 +36,10 @@ class CoreDataSerializer {
val data = bi.toByteArray
w.putUShort(data.length)
w.putBytes(data)
+ case SUnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ 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 =>
@@ -70,7 +75,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.isV3OrLaterErgoTreeVersion =>
+ 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)")
@@ -105,6 +115,13 @@ class CoreDataSerializer {
}
val valueBytes = r.getBytes(size)
CBigInt(new BigInteger(valueBytes))
+ case SUnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ 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 =>
@@ -120,6 +137,10 @@ class CoreDataSerializer {
}.toArray[Any]
val coll = Colls.fromArray(arr)(sigma.AnyType)
Evaluation.toDslTuple(coll, tuple)
+ case tOption: SOption[_] if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ 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")
@@ -128,7 +149,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..69d9015772 100644
--- a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala
+++ b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala
@@ -1,11 +1,11 @@
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}
+import sigma.validation.ValidationRules.{CheckPrimitiveTypeCode, CheckPrimitiveTypeCodeV6, CheckTypeCode, CheckTypeCodeV6}
import java.nio.charset.StandardCharsets
@@ -13,8 +13,14 @@ import java.nio.charset.StandardCharsets
class TypeSerializer {
import TypeSerializer._
- def getEmbeddableType(code: Int): SType = {
- CheckPrimitiveTypeCode(code.toByte)
+ private def getEmbeddableType(code: Int): SType = {
+ // the #1007 check replaced with one with identical behavior but different opcode (1017), to activate
+ // ReplacedRule(1007 -> 1017) during 6.0 activation
+ if (VersionContext.current.isV6Activated) {
+ CheckPrimitiveTypeCodeV6(code.toByte)
+ } else {
+ CheckPrimitiveTypeCode(code.toByte)
+ }
embeddableIdToType(code)
}
@@ -101,6 +107,18 @@ class TypeSerializer {
// `Tuple` type with more than 4 items `(Int, Byte, Box, Boolean, Int)`
serializeTuple(tup, w)
}
+ // implemented in 6.0, https://github.com/ergoplatform/sigmastate-interpreter/issues/847
+ case SFunc(tDom, tRange, tpeParams) if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ 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,8 +207,29 @@ class TypeSerializer {
case SHeader.typeCode => SHeader
case SPreHeader.typeCode => SPreHeader
case SGlobal.typeCode => SGlobal
+ // SFunc serialization implemented in 6.0, https://github.com/ergoplatform/sigmastate-interpreter/issues/847
+ case SFunc.FuncTypeCode if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ val tdLength = r.getUByte()
+
+ val tDom = (1 to tdLength).map { _ =>
+ deserialize(r, depth + 1)
+ }
+ val tRange = deserialize(r, depth + 1)
+ val tpeParamsLength = r.getUByte()
+ val tpeParams = (1 to tpeParamsLength).map { _ =>
+ val ident = deserialize(r, depth + 1)
+ require(ident.isInstanceOf[STypeVar])
+ STypeParam(ident.asInstanceOf[STypeVar])
+ }
+ SFunc(tDom, tRange, tpeParams)
case _ =>
- CheckTypeCode(c.toByte)
+ // the #1008 check replaced with one with identical behavior but different opcode (1018), to activate
+ // ReplacedRule(1008 -> 1018) during 6.0 activation
+ if (VersionContext.current.isV6Activated) {
+ CheckTypeCodeV6(c.toByte)
+ } else {
+ CheckTypeCode(c.toByte)
+ }
NoType
}
}
@@ -215,6 +254,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: Array[SType] = {
+ if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
+ 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 517a69206c..6b257620d6 100644
--- a/core/shared/src/main/scala/sigma/util/Extensions.scala
+++ b/core/shared/src/main/scala/sigma/util/Extensions.scala
@@ -216,14 +216,39 @@ object Extensions {
* not exactly fit in a 256 bit range.
* @see BigInteger#longValueExact
*/
- @inline final def to256BitValueExact: BigInteger = {
+ @inline final def toSignedBigIntValueExact: BigInteger = {
if (fitsIn256Bits) x
else
throw new ArithmeticException("BigInteger out of 256 bit range");
}
+ /** Checks this {@code BigInteger} can be cast to unsigned 256 bit representation,
+ * If the value of this {@code BigInteger}
+ * is out of the range of the 256 bits, then an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code BigInteger} if the check is successful
+ * @throws ArithmeticException if the value of {@code this} will
+ * not exactly fit in a 256 bit range.
+ * @see BigInteger#longValueExact
+ */
+ @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/main/scala/sigma/validation/SigmaValidationSettings.scala b/core/shared/src/main/scala/sigma/validation/SigmaValidationSettings.scala index 51fb97c8ad..828ef39604 100644 --- a/core/shared/src/main/scala/sigma/validation/SigmaValidationSettings.scala +++ b/core/shared/src/main/scala/sigma/validation/SigmaValidationSettings.scala @@ -1,5 +1,7 @@ package sigma.validation +import sigma.VersionContext + /** * Configuration of validation. Each `ValidationRule` instance should be * implemented as an `object` to facilitate type-safe usage. It then should be @@ -44,11 +46,22 @@ abstract class SigmaValidationSettings extends Iterable[(Short, (ValidationRule, def get(id: Short): Option[(ValidationRule, RuleStatus)] def getStatus(id: Short): Option[RuleStatus] def updated(id: Short, newStatus: RuleStatus): SigmaValidationSettings - def isSoftFork(ve: ValidationException): Boolean = isSoftFork(ve.rule.id, ve) - def isSoftFork(ruleId: Short, ve: ValidationException): Boolean = { + + /** + * Check if validation exception is a subject to soft-fork, and so can be skipped + * @param ve - exception with a validation rule embedded into + * @return true if exception can be tolerated, false if not + */ + def isSoftFork(ve: ValidationException): Boolean = { + val ruleId = ve.rule.id val infoOpt = get(ruleId) infoOpt match { - case Some((_, ReplacedRule(_))) => true + // we do not consider replaced 5.0 rules after 6.0 activation as ones which can be tolerated + case Some((vr, ReplacedRule(_))) => if ((vr.id == 1011 || vr.id == 1007 || vr.id == 1008) && VersionContext.current.isV6Activated) { + false + } else { + true + } case Some((rule, status)) => rule.isSoftFork(this, rule.id, status, ve.args) case None => false } @@ -66,6 +79,7 @@ sealed class MapSigmaValidationSettings(private val map: Map[Short, (ValidationR val res = if (statusOpt.isDefined) Some(statusOpt.get._2) else None res } + override def updated(id: Short, newStatus: RuleStatus): MapSigmaValidationSettings = { val (rule,_) = map(id) new MapSigmaValidationSettings(map.updated(id, (rule, newStatus))) diff --git a/core/shared/src/main/scala/sigma/validation/ValidationRules.scala b/core/shared/src/main/scala/sigma/validation/ValidationRules.scala index 249e2fce0a..bf054b41c7 100644 --- a/core/shared/src/main/scala/sigma/validation/ValidationRules.scala +++ b/core/shared/src/main/scala/sigma/validation/ValidationRules.scala @@ -1,6 +1,6 @@ package sigma.validation -import sigma.SigmaException +import sigma.{SigmaException, VersionContext} import sigma.ast.{SGlobal, SOption, TypeCodes} import sigma.serialization.{ReaderPositionLimitExceeded, SerializerException} import sigma.util.Extensions.toUByte @@ -77,7 +77,7 @@ object ValidationRules { /** The id of the first validation rule. Can be used as the beginning of the rules id range. */ val FirstRuleId = 1000.toShort - object CheckPrimitiveTypeCode extends ValidationRule(1007, + class CheckPrimitiveTypeCodeTemplate(ruleId: Short) extends ValidationRule(ruleId, "Check the primitive type code is supported or is added via soft-fork") with SoftForkWhenCodeAdded { override protected lazy val settings: SigmaValidationSettings = coreSettings @@ -93,10 +93,14 @@ object ValidationRules { } } - object CheckTypeCode extends ValidationRule(1008, + object CheckPrimitiveTypeCode extends CheckPrimitiveTypeCodeTemplate(1007) + + object CheckPrimitiveTypeCodeV6 extends CheckPrimitiveTypeCodeTemplate(1017) + + class CheckTypeCodeTemplate(ruleId: Short) extends ValidationRule(ruleId, "Check the non-primitive type code is supported or is added via soft-fork") with SoftForkWhenCodeAdded { - override protected lazy val settings: SigmaValidationSettings = coreSettings + override protected def settings: SigmaValidationSettings = coreSettings final def apply[T](typeCode: Byte): Unit = { checkRule() @@ -109,10 +113,14 @@ object ValidationRules { } } + object CheckTypeCode extends CheckTypeCodeTemplate(1008) + + object CheckTypeCodeV6 extends CheckTypeCodeTemplate(1018) + object CheckSerializableTypeCode extends ValidationRule(1009, "Check the data values of the type (given by type code) can be serialized") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = coreSettings + override protected def settings: SigmaValidationSettings = coreSettings /** Creates an exception which is used as a cause when throwing a ValidationException. */ def throwValidationException(typeCode: Byte): Nothing = { @@ -141,7 +149,7 @@ object ValidationRules { object CheckTypeWithMethods extends ValidationRule(1010, "Check the type (given by type code) supports methods") with SoftForkWhenCodeAdded { - override protected lazy val settings: SigmaValidationSettings = coreSettings + override protected def settings: SigmaValidationSettings = coreSettings final def apply[T](typeCode: Byte, cond: Boolean): Unit = { checkRule() @@ -160,7 +168,7 @@ object ValidationRules { */ object CheckPositionLimit extends ValidationRule(1014, "Check that the Reader has not exceeded the position limit.") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = coreSettings + override protected def settings: SigmaValidationSettings = coreSettings /** Wraps the given cause into [[ValidationException]] and throws it. */ def throwValidationException(cause: ReaderPositionLimitExceeded): Nothing = { @@ -183,7 +191,7 @@ object ValidationRules { } } - private val ruleSpecs: Seq[ValidationRule] = Seq( + private val ruleSpecsV5: Seq[ValidationRule] = Seq( CheckPrimitiveTypeCode, CheckTypeCode, CheckSerializableTypeCode, @@ -191,17 +199,39 @@ object ValidationRules { CheckPositionLimit ) + private val ruleSpecsV6: Seq[ValidationRule] = Seq( + CheckPrimitiveTypeCodeV6, + CheckTypeCodeV6, + CheckSerializableTypeCode, + CheckTypeWithMethods, + CheckPositionLimit + ) + + private def coreSettingsTemplate(ruleSpecs: Seq[ValidationRule]): SigmaValidationSettings = { + new MapSigmaValidationSettings({ + val map = ruleSpecs.map(r => r.id -> (r, EnabledRule)).toMap + assert(map.size == ruleSpecs.size, s"Duplicate ruleIds ${ruleSpecs.groupBy(_.id).filter(g => g._2.length > 1)}") + map + }) + } + + private lazy val coreSettingsV5 = coreSettingsTemplate(ruleSpecsV5) + + private lazy val coreSettingsV6 = coreSettingsTemplate(ruleSpecsV6) + /** Validation settings that correspond to the current version of the ErgoScript implementation. * Different version of the code will have a different set of rules here. * This variable is globally available and can be use wherever checking of the rules is necessary. * This is immutable data structure, it can be augmented with RuleStates from block extension * sections of the blockchain, but that augmentation is only available in stateful context. */ - val coreSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ - val map = ruleSpecs.map(r => r.id -> (r, EnabledRule)).toMap - assert(map.size == ruleSpecs.size, s"Duplicate ruleIds ${ruleSpecs.groupBy(_.id).filter(g => g._2.length > 1)}") - map - }) + def coreSettings: SigmaValidationSettings = { + if (VersionContext.current.isV6Activated) { + coreSettingsV6 + } else { + coreSettingsV5 + } + } /** Executes the given `block` catching [[ValidationException]] and checking possible * soft-fork condition in the context of the given [[SigmaValidationSettings]]. diff --git a/core/shared/src/test/scala/sigma/CollsTests.scala b/core/shared/src/test/scala/sigma/CollsTests.scala index 4886112742..31d3c05e81 100644 --- a/core/shared/src/test/scala/sigma/CollsTests.scala +++ b/core/shared/src/test/scala/sigma/CollsTests.scala @@ -64,8 +64,7 @@ class CollsTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers } } VersionContext.withVersions(VersionContext.JitActivationVersion, VersionContext.JitActivationVersion) { -// TODO v5.0: make it work -// equalLengthMapped(pairs, squared(inc)) // problem fixed in v5.0 + equalLengthMapped(pairs, squared(inc)) // problem fixed in v5.0 } equalLength(pairs.append(pairs)) @@ -76,8 +75,7 @@ class CollsTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers } } VersionContext.withVersions(VersionContext.JitActivationVersion, VersionContext.JitActivationVersion) { -// TODO v5.0: make it work -// equalLengthMapped(pairs.append(pairs), squared(inc)) // problem fixed in v5.0 + equalLengthMapped(pairs.append(pairs), squared(inc)) // problem fixed in v5.0 } } } @@ -386,6 +384,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..f3e300b325 100644 --- a/core/shared/src/test/scala/sigma/VersionTesting.scala +++ b/core/shared/src/test/scala/sigma/VersionTesting.scala @@ -75,8 +75,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..9db049b368 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.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean)) + case arr: Array[Byte] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SByte.type](arr, SByte)) + case arr: Array[Short] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SShort.type](arr, SShort)) + case arr: Array[Int] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SInt.type](arr, SInt)) + case arr: Array[Long] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SLong.type](arr, SLong)) + case arr: Array[BigInteger] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SBigInt.type](arr.map[BigInt](n => CBigInt(n)), SBigInt)) + case arr: Array[String] if !VersionContext.current.isV3OrLaterErgoTreeVersion => 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.isV3OrLaterErgoTreeVersion => 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.isV3OrLaterErgoTreeVersion => Nullable(mkConstant[SString.type](v, SString)) + case h: Header if VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkConstant[SHeader.type](h, SHeader)) + case h: PreHeader if VersionContext.current.isV3OrLaterErgoTreeVersion => 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.isV3OrLaterErgoTreeVersion => 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.isV3OrLaterErgoTreeVersion => 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/js/Value.scala b/data/js/src/main/scala/sigma/js/Value.scala index a65156bd43..1fedb30250 100644 --- a/data/js/src/main/scala/sigma/js/Value.scala +++ b/data/js/src/main/scala/sigma/js/Value.scala @@ -81,6 +81,9 @@ object Value extends js.Object { case sigma.BigIntRType => val v = data.asInstanceOf[js.BigInt] CBigInt(new BigInteger(v.toString(16), 16)) + case sigma.UnsignedBigIntRType => + val v = data.asInstanceOf[js.BigInt] + CUnsignedBigInt(new BigInteger(v.toString(16), 16)) case sigma.GroupElementRType => val ge = data.asInstanceOf[GroupElement] CGroupElement(ge.point) @@ -121,6 +124,9 @@ object Value extends js.Object { case sigma.BigIntRType => val hex = value.asInstanceOf[sigma.BigInt].toBigInteger.toString(10) js.BigInt(hex) + case sigma.UnsignedBigIntRType => + val hex = value.asInstanceOf[sigma.BigInt].toBigInteger.toString(10) + js.BigInt(hex) case sigma.GroupElementRType => val point = value.asInstanceOf[CGroupElement].wrappedValue.asInstanceOf[Platform.Ecp] new GroupElement(point) @@ -158,6 +164,8 @@ object Value extends js.Object { n case sigma.BigIntRType => data.asInstanceOf[js.BigInt] + case sigma.UnsignedBigIntRType => + data.asInstanceOf[js.BigInt] case sigma.GroupElementRType => data.asInstanceOf[GroupElement] case sigma.SigmaPropRType => diff --git a/data/jvm/src/main/scala/sigma/Platform.scala b/data/jvm/src/main/scala/sigma/Platform.scala index bd39d75ab9..766b9ee7d7 100644 --- a/data/jvm/src/main/scala/sigma/Platform.scala +++ b/data/jvm/src/main/scala/sigma/Platform.scala @@ -2,7 +2,7 @@ package sigma import org.ergoplatform.ErgoBox import sigma.ast._ -import sigma.data.{AvlTreeData, Nullable, SigmaBoolean} +import sigma.data.{AvlTreeData, Nullable, OptionType, SigmaBoolean} import sigma.eval.SigmaDsl import java.math.BigInteger @@ -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.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean)) + case arr: Array[Byte] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SByte.type](arr, SByte)) + case arr: Array[Short] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SShort.type](arr, SShort)) + case arr: Array[Int] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SInt.type](arr, SInt)) + case arr: Array[Long] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SLong.type](arr, SLong)) + case arr: Array[BigInteger] if !VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt(_)), SBigInt)) + case arr: Array[String] if !VersionContext.current.isV3OrLaterErgoTreeVersion => 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.isV3OrLaterErgoTreeVersion => 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.isV3OrLaterErgoTreeVersion => Nullable(mkConstant[SHeader.type](h, SHeader)) + case h: PreHeader if VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkConstant[SPreHeader.type](h, SPreHeader)) + case n: sigma.UnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => Nullable(mkConstant[SUnsignedBigInt.type](n, SUnsignedBigInt)) + + case v: String if !VersionContext.current.isV3OrLaterErgoTreeVersion => 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.isV3OrLaterErgoTreeVersion => 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.isV3OrLaterErgoTreeVersion => 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/ErgoBoxCandidate.scala b/data/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala index ba306159d4..e36f6319ab 100644 --- a/data/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala +++ b/data/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala @@ -2,6 +2,7 @@ package org.ergoplatform import debox.cfor import org.ergoplatform.ErgoBox._ +import org.ergoplatform.validation.ValidationRules.CheckV6Type import scorex.util.encode.Base16 import scorex.util.{ModifierId, bytesToId} import sigma.Extensions.{ArrayOps, CollOps} @@ -228,6 +229,7 @@ object ErgoBoxCandidate { cfor(0)(_ < nRegs, _ + 1) { iReg => val reg = ErgoBox.nonMandatoryRegisters(iReg) val v = r.getValue().asInstanceOf[EvaluatedValue[SType]] // READ + CheckV6Type(v) b += ((reg, v)) // don't use `->` since it incur additional wrapper overhead } r.positionLimit = previousPositionLimit 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..972c7c1283 --- /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 sigma.util.Extensions.BigIntegerOps + +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 = BigIntegers.fromUnsignedByteArray(r.getBytes(dBytesLength)).toSignedBigIntValueExact + 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/org/ergoplatform/validation/ValidationRules.scala b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala index 9d4de47a99..a6c25aa0b1 100644 --- a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala +++ b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala @@ -1,7 +1,7 @@ package org.ergoplatform.validation -import sigma.SigmaException -import sigma.ast.{DeserializeContext, ErgoTree, MethodsContainer, SMethod} +import sigma.{SigmaException, VersionContext} +import sigma.ast._ import sigma.ast.TypeCodes.LastConstantCode import sigma.serialization.{InvalidOpCode, SerializerException} import sigma.util.Extensions.toUByte @@ -23,7 +23,7 @@ object ValidationRules { object CheckDeserializedScriptType extends ValidationRule(FirstRuleId, "Deserialized script should have expected type") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings final def apply[T](d: DeserializeContext[_], script: SValue): Unit = { checkRule() @@ -38,7 +38,7 @@ object ValidationRules { object CheckDeserializedScriptIsSigmaProp extends ValidationRule(1001, "Deserialized script should have SigmaProp type") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings /** @param root candidate node before it is added as a root of ErgoTree */ final def apply[T](root: SValue): Unit = { @@ -54,7 +54,7 @@ object ValidationRules { object CheckValidOpCode extends ValidationRule(1002, "Check the opcode is supported by registered serializer or is added via soft-fork") with SoftForkWhenCodeAdded { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings final def apply[T](ser: ValueSerializer[_], opCode: OpCode): Unit = { checkRule() @@ -69,18 +69,18 @@ object ValidationRules { /** Not used since v5.0.1. */ object CheckIsSupportedIndexExpression extends ValidationRule(1003, "Check the index expression for accessing collection element is supported.") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings } /** Not used since v5.0.3 */ object CheckCostFunc extends ValidationRule(1004, "Cost function should contain only operations from specified list.") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings } object CheckCalcFunc extends ValidationRule(1005, "If SigmaProp.isProven method calls exists in the given function,\n then it is the last operation") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings } /** This rule is not use in v5.x, keep the commented code below as a precise @@ -88,7 +88,7 @@ object ValidationRules { */ object CheckTupleType extends ValidationRule(1006, "Supported tuple type.") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings // final def apply[Ctx <: IRContext, T](ctx: Ctx)(e: ctx.Elem[_]): Unit = { // checkRule() @@ -102,15 +102,16 @@ object ValidationRules { // } } - object CheckAndGetMethod extends ValidationRule(1011, + class CheckAndGetMethodTemplate(ruleId: Short) extends ValidationRule(ruleId, "Check the type has the declared method.") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings final def apply[T](objType: MethodsContainer, methodId: Byte): SMethod = { checkRule() val methodOpt = objType.getMethodById(methodId) - if (methodOpt.isDefined) methodOpt.get - else { + if (methodOpt.isDefined) { + methodOpt.get + } else { throwValidationException( new SerializerException(s"The method with code $methodId doesn't declared in the type $objType."), Array[Any](objType, methodId)) @@ -120,17 +121,23 @@ object ValidationRules { override def isSoftFork(vs: SigmaValidationSettings, ruleId: Short, status: RuleStatus, - args: Seq[Any]): Boolean = (status, args) match { - case (ChangedRule(newValue), Seq(objType: MethodsContainer, methodId: Byte)) => - val key = Array(objType.ownerType.typeId, methodId) - newValue.grouped(2).exists(java.util.Arrays.equals(_, key)) - case _ => false + args: Seq[Any]): Boolean = { + (status, args) match { + case (ChangedRule(newValue), Seq(objType: MethodsContainer, methodId: Byte)) => + val key = Array(objType.ownerType.typeId, methodId) + newValue.grouped(2).exists(java.util.Arrays.equals(_, key)) + case _ => false + } } } + object CheckAndGetMethod extends CheckAndGetMethodTemplate(1011) + + object CheckAndGetMethodV6 extends CheckAndGetMethodTemplate(1016) + object CheckHeaderSizeBit extends ValidationRule(1012, "For version greater then 0, size bit should be set.") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings final def apply(header: HeaderType): Unit = { checkRule() @@ -146,16 +153,58 @@ object ValidationRules { /** Not used since v5.0.3 */ object CheckCostFuncOperation extends ValidationRule(1013, "Check the opcode is allowed in cost function") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings } /** Not used since v5.0.1 */ object CheckLoopLevelInCostFunction extends ValidationRule(1015, "Check that loop level is not exceeded.") { + override protected def settings: SigmaValidationSettings = currentSettings + } + + object CheckV6Type extends ValidationRule(1019, + "Check the type has the declared method.") { override protected lazy val settings: SigmaValidationSettings = currentSettings + + final def apply[T](v: EvaluatedValue[_]): Unit = { + checkRule() + + def v6TypeCheck(tpe: SType): Unit = { + if (tpe.isOption || tpe.typeCode == SHeader.typeCode || tpe.typeCode == SUnsignedBigInt.typeCode) { + throwValidationException( + SerializerException(s"V6 type used in register or context var extension: $tpe"), + Array[Any](tpe)) + } + } + + def step(s: SType): Unit = { + s match { + case st: STuple => st.items.foreach(step) + case sc: SCollection[_] => step(sc.elemType) // this case should be after STuple as STuple deriving from SCollection + case s: SType => v6TypeCheck(s) + } + } + + v match { + case c: Constant[_] => step(c.tpe) + case t: Tuple => t.items.foreach(i => step(i.tpe)) + case c: EvaluatedCollection[_, _] => step(c.elementType) + case GroupGenerator => + } + } + + override def isSoftFork(vs: SigmaValidationSettings, + ruleId: Short, + status: RuleStatus, + args: Seq[Any]): Boolean = (status, args) match { + case (ChangedRule(newValue), Seq(objType: MethodsContainer, methodId: Byte)) => + val key = Array(objType.ownerType.typeId, methodId) + newValue.grouped(2).exists(java.util.Arrays.equals(_, key)) + case _ => false + } } - val ruleSpecs: Seq[ValidationRule] = Seq( + private val ruleSpecsV5: Seq[ValidationRule] = Seq( CheckDeserializedScriptType, CheckDeserializedScriptIsSigmaProp, CheckValidOpCode, @@ -171,16 +220,30 @@ object ValidationRules { CheckHeaderSizeBit, CheckCostFuncOperation, CheckPositionLimit, - CheckLoopLevelInCostFunction + CheckLoopLevelInCostFunction, + CheckV6Type ) + // v6 validation rules below + private val ruleSpecsV6: Seq[ValidationRule] = { + ruleSpecsV5.filter(_.id != CheckAndGetMethod.id) ++ Seq(CheckAndGetMethodV6) + } + + def ruleSpecs: Seq[ValidationRule] = { + if (VersionContext.current.isV6Activated) { + ruleSpecsV6 + } else { + ruleSpecsV5 + } + } + /** Validation settings that correspond to the current version of the ErgoScript implementation. * Different version of the code will have a different set of rules here. * This variable is globally available and can be use wherever checking of the rules is necessary. * This is immutable data structure, it can be augmented with RuleStates from block extension * sections of the blockchain, but that augmentation is only available in stateful context. */ - val currentSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ + def currentSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ val map = ruleSpecs.map(r => r.id -> (r, EnabledRule)).toMap assert(map.size == ruleSpecs.size, s"Duplicate ruleIds ${ruleSpecs.groupBy(_.id).filter(g => g._2.length > 1)}") map diff --git a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala index ed7ea7e6dd..0a57536462 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 => @@ -220,12 +236,6 @@ object SigmaDataReflection { { val clazz = SAvlTreeMethods.getClass registerClassEntry(clazz, methods = Map( - mkMethod(clazz, "update_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SAvlTreeMethods.type].update_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[AvlTree], - args(2).asInstanceOf[KeyValueColl], - args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, mkMethod(clazz, "contains_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => obj.asInstanceOf[SAvlTreeMethods.type].contains_eval(args(0).asInstanceOf[MethodCall], args(1).asInstanceOf[AvlTree], @@ -255,6 +265,18 @@ object SigmaDataReflection { args(1).asInstanceOf[AvlTree], args(2).asInstanceOf[KeyValueColl], args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "update_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SAvlTreeMethods.type].update_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[AvlTree], + args(2).asInstanceOf[KeyValueColl], + args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "insertOrUpdate_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SAvlTreeMethods.type].insertOrUpdate_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[AvlTree], + args(2).asInstanceOf[KeyValueColl], + args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) } ) ) @@ -309,6 +331,18 @@ 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, "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 +356,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..bde4c869fa 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,38 @@ 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 SUnsignedBigInt => 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 = 500 + + // 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 68d69abd91..ec902a20eb 100644 --- a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala +++ b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala @@ -375,13 +375,13 @@ object ErgoTree { * 3) write the `tree` to the Writer's buffer obtaining `treeBytes`; * 4) deserialize `tree` with ConstantPlaceholders. * - * @param headerFlags additional header flags to combine with + * @param header additional header flags to combine with * ConstantSegregationHeader flag. * @param prop expression to be transformed into 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 @@ -411,4 +411,5 @@ object ErgoTree { def fromBytes(bytes: Array[Byte]): ErgoTree = { ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(bytes) } + } diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala index 669625ef1e..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 */ @@ -106,7 +117,12 @@ case class SMethod( /** Invoke this method on the given object with the arguments. * This is used for methods with FixedCost costKind. */ def invokeFixed(obj: Any, args: Array[Any]): Any = { - javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) + userDefinedInvoke match { + case Some(h) => + h(this, obj, args) + case None => + javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) + } } // TODO optimize: avoid lookup when this SMethod is created via `specializeFor` @@ -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 d892398ac6..588c6b3c0c 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -8,7 +8,6 @@ import sigma.data._ import sigma.{Colls} 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.data.RType.asType @@ -142,6 +141,18 @@ object SigmaPredef { Seq(ArgInfo("condition", "boolean value to embed in SigmaProp value"))) ) + 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( @@ -187,6 +198,22 @@ object SigmaPredef { Seq(ArgInfo("", ""))) ) + val UBigIntFromStringFunc = PredefinedFunc("unsignedBigInt", + Lambda(Array("input" -> SString), SUnsignedBigInt, None), + PredefFuncInfo( + { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => + val bi = new BigInteger(arg.value) + if (bi.compareTo(BigInteger.ZERO) >= 0) { + UnsignedBigIntConstant(bi) + } else { + throw new InvalidArguments(s"Negative argument for unsignedBigInt()") + } + }), + OperationInfo(Constant, + """Parsing string literal argument as a 256-bit unsigned big integer.""".stripMargin, + Seq(ArgInfo("", ""))) + ) + val FromBase16Func = PredefinedFunc("fromBase16", Lambda(Array("input" -> SString), SByteArray, None), PredefFuncInfo( @@ -419,6 +446,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 ExecuteFromSelfRegFunc = PredefinedFunc("executeFromSelfReg", Lambda( Seq(paramT), @@ -459,6 +542,7 @@ object SigmaPredef { GetVarFunc, DeserializeFunc, BigIntFromStringFunc, + UBigIntFromStringFunc, FromBase16Func, FromBase64Func, FromBase58Func, @@ -473,8 +557,12 @@ object SigmaPredef { AvlTreeFunc, SubstConstantsFunc, ExecuteFromVarFunc, + ExecuteFromSelfRegFunc, ExecuteFromSelfRegWithDefaultFunc, - ExecuteFromSelfRegFunc + SerializeFunc, + DeserializeToFunc, + GetVarFromInputFunc, + FromBigEndianBytesFunc ).map(f => f.name -> f).toMap def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { @@ -588,7 +676,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( diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 135d9cf4b8..01c66a62b6 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,16 +2,27 @@ 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.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} +import sigma.data.UnsignedBigIntNumericOps.UnsignedBigIntIsExactIntegral +import sigma.data.{CBigInt, CUnsignedBigInt, 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 @@ -51,18 +62,54 @@ sealed trait MethodsContainer { */ protected def getMethods(): Seq[SMethod] = Nil + private var _v5Methods: Seq[SMethod] = null + private var _v6Methods: Seq[SMethod] = null + /** Returns all the methods of this type. */ - lazy val methods: Seq[SMethod] = { - 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) => - assert(ms.map(_.methodId).distinct.length == ms.length, s"Duplicate method ids in $comp: $ms") + def methods: Seq[SMethod] = { + def calc() = { + 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) => + assert(ms.map(_.methodId).distinct.length == ms.length, s"Duplicate method ids in $comp: $ms") + } + ms + } + + if (VersionContext.current.isV3OrLaterErgoTreeVersion) { + if (_v6Methods == null) { + _v6Methods = calc() + } + _v6Methods + } else { + if (_v5Methods == null) { + _v5Methods = calc() + } + _v5Methods + } + } + + private var _v5MethodsMap: Map[Byte, Map[Byte, SMethod]] = null + private var _v6MethodsMap: Map[Byte, Map[Byte, SMethod]] = null + + private def _methodsMap: Map[Byte, Map[Byte, SMethod]] = { + def calc() = { + methods + .groupBy(_.objType.typeId) + .map { case (typeId, ms) => (typeId -> ms.map(m => m.methodId -> m).toMap) } + } + if (VersionContext.current.isV3OrLaterErgoTreeVersion) { + if (_v6MethodsMap == null) { + _v6MethodsMap = calc() + } + _v6MethodsMap + } else { + if (_v5MethodsMap == null) { + _v5MethodsMap = calc() + } + _v5MethodsMap } - ms } - private lazy val _methodsMap: Map[Byte, Map[Byte, SMethod]] = methods - .groupBy(_.objType.typeId) - .map { case (typeId, ms) => (typeId -> ms.map(m => m.methodId -> m).toMap) } /** Lookup method by its id in this type. */ @inline def getMethodById(methodId: Byte): Option[SMethod] = @@ -73,13 +120,19 @@ sealed trait MethodsContainer { /** Lookup method in this type by method's id or throw ValidationException. * This method can be used in trySoftForkable section to either obtain valid method - * or catch ValidatioinException which can be checked for soft-fork condition. + * or catch ValidationException which can be checked for soft-fork condition. * It delegate to getMethodById to lookup method. * * @see getMethodById */ def methodById(methodId: Byte): SMethod = { - ValidationRules.CheckAndGetMethod(this, methodId) + // the #1011 check replaced with one with identical behavior but different opcode (1016), to activate + // ReplacedRule(1011 -> 1016) during 6.0 activation + if (VersionContext.current.isV6Activated) { + ValidationRules.CheckAndGetMethodV6(this, methodId) + } else { + ValidationRules.CheckAndGetMethod(this, methodId) + } } /** Finds a method descriptor [[SMethod]] for the given name. */ @@ -90,7 +143,7 @@ sealed trait MethodsContainer { } object MethodsContainer { - private val containers = new SparseArrayContainer[MethodsContainer](Array( + private val methodsV5 = Array( SByteMethods, SShortMethods, SIntMethods, @@ -111,11 +164,29 @@ object MethodsContainer { STupleMethods, SUnitMethods, SAnyMethods - ).map(m => (m.typeId, m))) + ) - def contains(typeId: TypeCode): Boolean = containers.contains(typeId) + private val methodsV6 = methodsV5 ++ Seq(SUnsignedBigIntMethods) - def apply(typeId: TypeCode): MethodsContainer = containers(typeId) + private val containersV5 = new SparseArrayContainer[MethodsContainer](methodsV5.map(m => (m.typeId, m))) + + private val containersV6 = new SparseArrayContainer[MethodsContainer](methodsV6.map(m => (m.typeId, m))) + + def contains(typeId: TypeCode): Boolean = { + if (VersionContext.current.isV3OrLaterErgoTreeVersion) { + containersV6.contains(typeId) + } else { + containersV5.contains(typeId) + } + } + + def apply(typeId: TypeCode): MethodsContainer = { + if (VersionContext.current.isV3OrLaterErgoTreeVersion) { + containersV6(typeId) + } else { + containersV5(typeId) + } + } /** Finds the method of the give type. * @@ -127,7 +198,11 @@ object MethodsContainer { case tup: STuple => STupleMethods.getTupleMethod(tup, methodName) case _ => - containers.get(tpe.typeCode).flatMap(_.method(methodName)) + if (VersionContext.current.isV3OrLaterErgoTreeVersion) { + containersV6.get(tpe.typeCode).flatMap(_.method(methodName)) + } else { + containersV5.get(tpe.typeCode).flatMap(_.method(methodName)) + } } } @@ -156,14 +231,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.isV3OrLaterErgoTreeVersion) { + super.getMethods() ++ v6Methods + } else { + super.getMethods() ++ v5Methods } } } object SNumericTypeMethods extends MethodsContainer { + /** Type for which this container defines methods. */ override def ownerType: STypeCompanion = SNumericType @@ -216,6 +311,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 @@ -229,12 +334,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 @@ -244,6 +468,19 @@ 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). */ val castMethods: Array[String] = Array(ToByteMethod, ToShortMethod, ToIntMethod, ToLongMethod, ToBigIntMethod) @@ -272,12 +509,6 @@ case object SBooleanMethods extends MonoTypeMethods { val ToByte = "toByte" protected override def getMethods() = super.getMethods() - /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - ++ Seq( - SMethod(this, ToByte, SFunc(this, SByte), 1) - .withInfo(PropertyCall, "Convert true to 1 and false to 0"), - ) - */ } /** Methods of ErgoTree type `Byte`. */ @@ -309,28 +540,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.isV3OrLaterErgoTreeVersion) { + 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(150)), 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`. */ @@ -362,6 +653,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), _) => @@ -377,16 +674,20 @@ case object SGroupElementMethods extends MonoTypeMethods { .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, "Inverse element of the group.") - protected override def getMethods(): Seq[SMethod] = super.getMethods() ++ Seq( - /* 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 - ) + protected override def getMethods(): Seq[SMethod] = { + val v5Methods = Seq( + GetEncodedMethod, + ExponentiateMethod, + MultiplyMethod, + NegateMethod) + + super.getMethods() ++ (if (VersionContext.current.isV3OrLaterErgoTreeVersion) { + v5Methods ++ Seq(ExponentiateUnsignedMethod) + } else { + v5Methods + }) + } + } /** Methods of type `SigmaProp` which represent sigma-protocol propositions. */ @@ -471,18 +772,6 @@ object SOptionMethods extends MethodsContainer { |return the result of evaluating \lst{default}. """.stripMargin, ArgInfo("default", "the default value")) -// TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 -// val FoldMethod = SMethod( -// this, Fold, SFunc(Array(ThisType, tR, SFunc(tT, tR)), tR, Array[STypeParam](tT, tR)), 5, FixedCost(JitCost(1))) -// .withInfo(MethodCall, -// """Returns the result of applying \lst{f} to this option's -// | value if the option is nonempty. Otherwise, evaluates -// | expression \lst{ifEmpty}. -// | This is equivalent to \lst{option map f getOrElse ifEmpty}. -// """.stripMargin, -// ArgInfo("ifEmpty", "the expression to evaluate if empty"), -// ArgInfo("f", "the function to apply if nonempty")) - val MapMethod = SMethod(this, "map", SFunc(Array(ThisType, SFunc(tT, tR)), SOption(tR), Array(paramT, paramR)), 7, FixedCost(JitCost(20))) .withIRInfo(MethodCallIrBuilder) @@ -505,9 +794,6 @@ object SOptionMethods extends MethodsContainer { IsDefinedMethod, GetMethod, GetOrElseMethod, - /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - FoldMethod, - */ MapMethod, FilterMethod ) @@ -785,7 +1071,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. @@ -817,8 +1103,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, "") @@ -834,29 +1119,113 @@ 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, "Returns inversed collection.") + + /** 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 startsWithCostKind = Zip_CostKind + + val StartsWithMethod = SMethod(this, "startsWith", + SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 31, 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), 32, 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)), 33, 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, + 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.isV3OrLaterErgoTreeVersion) { + v6Methods + } else { + v5Methods + } + } + } object STupleMethods extends MethodsContainer { @@ -870,7 +1239,6 @@ object STupleMethods extends MethodsContainer { /** A list of Coll methods inherited from Coll type and available as method of tuple. */ lazy val colMethods: Seq[SMethod] = { val subst = Map(SType.tIV -> SAny) - // TODO: implement other methods val activeMethods = Set(1.toByte /*Coll.size*/, 10.toByte /*Coll.apply*/) SCollectionMethods.methods.filter(m => activeMethods.contains(m.methodId)).map { m => m.copy(stype = applySubst(m.stype, subst).asFunc) @@ -958,7 +1326,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, "getRegV5", SFunc(Array(SBox, SInt), SOption(tT), Array(paramT)), 7, ExtractRegisterAs.costKind) .withInfo(ExtractRegisterAs, """ Extracts register by id and type. @@ -967,23 +1335,48 @@ 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)), 19, 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 + tokensMethod, + getRegMethodV5 ) ++ registers(8) + + lazy val v5Methods = commonBoxMethods + + lazy val v6Methods = commonBoxMethods ++ Array( + getRegMethodV6 + ) + + // should be lazy to solve recursive initialization + protected override def getMethods() = { + if (VersionContext.current.isV3OrLaterErgoTreeVersion) { + v6Methods + } else { + v5Methods + } + } + } /** Type descriptor of `AvlTree` type of ErgoTree. */ @@ -1274,23 +1667,63 @@ case object SAvlTreeMethods extends MonoTypeMethods { OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) } - protected override def getMethods(): Seq[SMethod] = super.getMethods() ++ Seq( - digestMethod, - enabledOperationsMethod, - keyLengthMethod, - valueLengthOptMethod, - isInsertAllowedMethod, - isUpdateAllowedMethod, - isRemoveAllowedMethod, - updateOperationsMethod, - containsMethod, - getMethod, - getManyMethod, - insertMethod, - updateMethod, - removeMethod, - updateDigestMethod - ) + // 6.0 methods below + lazy val insertOrUpdateMethod = SMethod(this, "insertOrUpdate", + SFunc(Array(SAvlTree, CollKeyValue, SByteArray), SAvlTreeOption), 16, DynamicCost) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """ + | /** Perform insertions or updates of key-value entries into this tree using proof `proof`. + | * Throws exception if proof is incorrect + | * Return Some(newTree) if successful + | * Return None if operations were not performed. + | * + | * @note CAUTION! Pairs must be ordered the same way they were in insert ops before proof was generated. + | * @param operations collection of key-value pairs to insert or update in this authenticated dictionary. + | * @param proof + | */ + | + """.stripMargin) + + /** Implements evaluation of AvlTree.insertOrUpdate method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def insertOrUpdate_eval(mc: MethodCall, tree: AvlTree, entries: KeyValueColl, proof: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Option[AvlTree] = { + E.insertOrUpdate_eval(mc, tree, entries, proof) + } + + lazy val v5Methods = { + super.getMethods() ++ Seq( + digestMethod, + enabledOperationsMethod, + keyLengthMethod, + valueLengthOptMethod, + isInsertAllowedMethod, + isUpdateAllowedMethod, + isRemoveAllowedMethod, + updateOperationsMethod, + containsMethod, + getMethod, + getManyMethod, + insertMethod, + updateMethod, + removeMethod, + updateDigestMethod + ) + } + + lazy val v6Methods = v5Methods ++ Seq(insertOrUpdateMethod) + + protected override def getMethods(): Seq[SMethod] = { + if (VersionContext.current.isV3OrLaterErgoTreeVersion) { + v6Methods + } else { + v5Methods + } + } + } /** Type descriptor of `Context` type of ErgoTree. */ @@ -1313,16 +1746,42 @@ 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 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, getVarV5Method ) + private lazy val v5Methods = commonMethods + + private lazy val v6Methods = commonMethods ++ Seq( + getVarFromInputMethod + ) + + protected override def getMethods(): Seq[SMethod] = { + if (VersionContext.current.isV3OrLaterErgoTreeVersion) { + 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( @@ -1351,11 +1810,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.isV3OrLaterErgoTreeVersion) { + v6Methods + } else { + v5Methods + } + } } /** Type descriptor of `PreHeader` type of ErgoTree. */ @@ -1405,6 +1881,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), SUnsignedBigInt), 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): UnsignedBigInt = { + val cost = PowHitCostKind.cost(k, msg, nonce, h) + E.addCost(FixedCost(cost), powHitMethod.opDesc) + CUnsignedBigInt(Autolykos2PowValidation.hitForVersion2ForMessageWithChecks(k, msg.toArray, nonce.toArray, h.toArray, N).bigInteger) + } + + private val deserializeCostKind = PerItemCost(baseCost = JitCost(100), perChunkCost = JitCost(32), 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 @@ -1414,9 +1919,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.isV3OrLaterErgoTreeVersion) { + 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 c82e6bd498..757c663ac2 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.isV3OrLaterErgoTreeVersion) { + // lazy evaluation of default in ErgoTree v3 or later + 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.isV3OrLaterErgoTreeVersion) { + // lazy evaluation of default in 6.0 + val inputV = input.evalTo[Option[V#WrappedType]](env) + addCost(OptionGetOrElse.costKind) + inputV.getOrElse { + val dV = default.evalTo[V#WrappedType](env) + Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased + dV + } + } else { + val inputV = input.evalTo[Option[V#WrappedType]](env) + val dV = default.evalTo[V#WrappedType](env) + Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased + addCost(OptionGetOrElse.costKind) + inputV.getOrElse(dV) + } } } object OptionGetOrElse extends ValueCompanion with FixedCostValueCompanion { diff --git a/data/shared/src/main/scala/sigma/ast/trees.scala b/data/shared/src/main/scala/sigma/ast/trees.scala index 39e666a389..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} @@ -414,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 @@ -652,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 @@ -683,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) } @@ -875,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..bfe3ca0982 100644 --- a/data/shared/src/main/scala/sigma/ast/values.scala +++ b/data/shared/src/main/scala/sigma/ast/values.scala @@ -8,7 +8,8 @@ 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.data.{AvlTreeData, CAvlTree, CSigmaDslBuilder, CSigmaProp, Nullable, RType, SigmaBoolean} import sigma.eval.ErgoTreeEvaluator.DataEnv import sigma.eval.{ErgoTreeEvaluator, SigmaDsl} import sigma.exceptions.InterpreterException @@ -306,7 +307,7 @@ trait PerItemCostValueCompanion extends ValueCompanion { * * @see Constant, ConcreteCollection, Tuple */ -abstract class EvaluatedValue[+S <: SType] extends Value[S] { +sealed trait EvaluatedValue[+S <: SType] extends Value[S] { /** The evaluated data value of the corresponding underlying data type. */ val value: S#WrappedType @@ -499,6 +500,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) @@ -536,6 +551,7 @@ object SigmaPropConstant { object AvlTreeConstant { def apply(value: AvlTree): Constant[SAvlTree.type] = Constant[SAvlTree.type](value, SAvlTree) + def apply(value: AvlTreeData): Constant[SAvlTree.type] = Constant[SAvlTree.type](CAvlTree(value), SAvlTree) } object PreHeaderConstant { @@ -556,6 +572,7 @@ object HeaderConstant { } } + trait NotReadyValueInt extends NotReadyValue[SInt.type] { override def tpe = SInt } @@ -1298,6 +1315,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..d7f87a055c --- /dev/null +++ b/data/shared/src/main/scala/sigma/data/CHeader.scala @@ -0,0 +1,146 @@ +package sigma.data + +import org.ergoplatform.{AutolykosSolution, ErgoHeader, 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..f5048cf975 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,15 @@ 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 = { + // Result is limited to 256 bits with .toSignedBigIntValueExact + CBigInt(NBitsUtils.decodeCompactBits(l).bigInteger.toSignedBigIntValueExact) + } + /** * @return the identity of the Dlog group used in ErgoTree */ @@ -191,7 +212,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 +221,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): UnsignedBigInt = { + val bi = Autolykos2PowValidation.hitForVersion2ForMessageWithChecks(k, msg.toArray, nonce.toArray, h.toArray, N) + this.UnsignedBigInt(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..bd2dbc9513 --- /dev/null +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -0,0 +1,155 @@ +package sigma.data + +import debox.cfor +import scorex.util.encode.Base16 +import sigma._ +import sigma.data.UnsignedBigIntOrderingOps.UnsignedBigIntOrdering +import sigma.eval.Extensions.IntExt + +import scala.math.{Integral, Ordering} + +object UnsignedBigIntOrderingOps { + def apply[T](implicit ord: Ordering[T]) = ord + + trait UnsignedBigIntOrdering extends Ordering[UnsignedBigInt] { + def compare(x: UnsignedBigInt, y: UnsignedBigInt) = x.compareTo(y) + } + implicit object UnsignedBigIntOrdering extends UnsignedBigIntOrdering +} + +object UnsignedBigIntNumericOps { + + /** Base implementation of Integral methods for UnsignedBigInt. */ + trait UnsignedBigIntIsIntegral extends Integral[UnsignedBigInt] { + /** This method should not be used in v4.x */ + def quot(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.divide(y) + + /** This method is used in ErgoTreeEvaluator based interpreter, to implement + * '%' operation of ErgoTree (i.e. `%: (T, T) => T` operation) for all + * numeric types T including BigInt. + * + * In the v4.x interpreter, however, the `%` operation is implemented using + * [[CBigInt]].mod method , which delegates to [[java.math.BigInteger]].mod method. + * + * Even though this method is called `rem`, the semantics of ErgoTree + * language requires it to correspond to [[java.math.BigInteger]].mod + * method. + * + * For this reason we define implementation of this `rem` method using + * [[BigInt]].mod. + * + * NOTE: This method should not be used in v4.x + */ + def rem(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.mod(y) + + def plus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.add(y) + def minus(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.subtract(y) + def times(x: UnsignedBigInt, y: UnsignedBigInt): UnsignedBigInt = x.multiply(y) + def negate(x: UnsignedBigInt): UnsignedBigInt = ??? + def fromInt(x: Int): UnsignedBigInt = x.toUnsignedBigInt + def toInt(x: UnsignedBigInt): Int = x.toInt + def toLong(x: UnsignedBigInt): Long = x.toLong + def toFloat(x: UnsignedBigInt): Float = 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/AvlTreeVerifier.scala b/data/shared/src/main/scala/sigma/eval/AvlTreeVerifier.scala index bc4a625458..c8e52a6a4e 100644 --- a/data/shared/src/main/scala/sigma/eval/AvlTreeVerifier.scala +++ b/data/shared/src/main/scala/sigma/eval/AvlTreeVerifier.scala @@ -50,6 +50,19 @@ trait AvlTreeVerifier { */ def performUpdate(key: Array[Byte], value: Array[Byte]): Try[Option[Array[Byte]]] + /** + * Returns Failure if the proof does not verify. + * Otherwise, successfully modifies tree and so returns Success. + * After one failure, all subsequent operations with this verifier will fail and digest + * is None. + * + * @param key key to look up + * @param value value to check it was inserted or updated + * @return Success(Some(oldValue)) if there was some oldValue associated with the key, + * Success(None) in case of insertion, or Failure if proof invalid + */ + def performInsertOrUpdate(key: Array[Byte], value: Array[Byte]): Try[Option[Array[Byte]]] + /** Check the key has been removed in the tree. * If `key` exists in the tree and the operation succeeds, * returns `Success(Some(v))`, where v is old value associated with `key`. diff --git a/data/shared/src/main/scala/sigma/eval/ErgoTreeEvaluator.scala b/data/shared/src/main/scala/sigma/eval/ErgoTreeEvaluator.scala index 52f839354c..ba3f6d5c95 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. @@ -134,6 +134,13 @@ abstract class ErgoTreeEvaluator { mc: MethodCall, tree: AvlTree, operations: KeyValueColl, proof: Coll[Byte]): Option[AvlTree] + /** Implements evaluation of AvlTree.insertOrUpdate method call ErgoTree node. */ + def insertOrUpdate_eval( + mc: MethodCall, + tree: AvlTree, + entries: KeyValueColl, + proof: Coll[Byte]): Option[AvlTree] + /** Implements evaluation of AvlTree.remove method call ErgoTree node. */ def remove_eval( mc: MethodCall, tree: AvlTree, 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..7207848bd8 100644 --- a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala +++ b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala @@ -1,5 +1,6 @@ package sigma.interpreter +import org.ergoplatform.validation.ValidationRules.CheckV6Type import sigma.ast.{EvaluatedValue, SType} import sigma.interpreter.ContextExtension.VarBinding import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} @@ -16,8 +17,19 @@ 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 = + + /** + * @return this extension with `bindings` added + */ + 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 { @@ -42,7 +54,12 @@ object ContextExtension { if (extSize < 0) error(s"Negative amount of context extension values: $extSize") val values = (0 until extSize) - .map(_ => (r.getByte(), r.getValue().asInstanceOf[EvaluatedValue[_ <: SType]])) + .map{_ => + val k = r.getByte() + val v = r.getValue().asInstanceOf[EvaluatedValue[_ <: SType]] + CheckV6Type(v) + (k, v) + } ContextExtension(values.toMap) } } 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..4955db5bdc 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.isV3OrLaterErgoTreeVersion => + 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.isV3OrLaterErgoTreeVersion => + 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 43e41f91ff..5f71d0920d 100644 --- a/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala @@ -3,7 +3,7 @@ package sigma.serialization import org.ergoplatform.validation.ValidationRules.{CheckDeserializedScriptIsSigmaProp, CheckHeaderSizeBit} import sigma.ast.{Constant, DeserializationSigmaBuilder, ErgoTree, SType, UnparsedErgoTree} import sigma.ast.syntax.ValueOps -import sigma.ast.ErgoTree.{EmptyConstants, HeaderType} +import sigma.ast.ErgoTree.{EmptyConstants, HeaderType, getVersion} import sigma.util.safeNewArray import debox.cfor import sigma.VersionContext @@ -103,20 +103,26 @@ class ErgoTreeSerializer { * Doesn't apply any transformations and guarantee to preserve original * structure after deserialization. */ def serializeErgoTree(ergoTree: ErgoTree): Array[Byte] = { - val res = ergoTree.root match { - case Left(UnparsedErgoTree(bytes, _)) => bytes.array.asInstanceOf[Array[Byte]] - case _ => - val bytes = serializeWithoutSize(ergoTree) - if (ergoTree.hasSize) { - val w = SigmaSerializer.startWriter() - val header = bytes(0) - val contentLength = bytes.length - 1 - w.put(header) - w.putUInt(contentLength) - w.putBytes(bytes, 1, contentLength) - w.toBytes - } - else bytes + val treeVersion = ergoTree.version + // we set script version to tree version, if current activated version is below it, + // to serialize all the tree features anyway + val scriptVersion = Math.max(VersionContext.current.activatedVersion, treeVersion).toByte + val res = VersionContext.withVersions(scriptVersion, treeVersion) { + ergoTree.root match { + case Left(UnparsedErgoTree(bytes, _)) => bytes.array.asInstanceOf[Array[Byte]] + case _ => + val bytes = serializeWithoutSize(ergoTree) + if (ergoTree.hasSize) { + val w = SigmaSerializer.startWriter() + val header = bytes(0) + val contentLength = bytes.length - 1 + w.put(header) + w.putUInt(contentLength) + w.putBytes(bytes, 1, contentLength) + w.toBytes + } + else bytes + } } res } @@ -138,43 +144,53 @@ class ErgoTreeSerializer { r.positionLimit = r.position + maxTreeSizeBytes val (h, sizeOpt) = deserializeHeaderAndSize(r) val bodyPos = r.position + + val treeVersion = getVersion(h) + // Unlike serialization, during deserialization activated script version is used + val scriptVersion = VersionContext.current.activatedVersion + val tree = try { try { // nested try-catch to intercept size limit exceptions and rethrow them as ValidationExceptions - val cs = deserializeConstants(h, r) - val previousConstantStore = r.constantStore - // reader with constant store attached is required (to get tpe for a constant placeholder) - r.constantStore = new ConstantStore(cs) + VersionContext.withVersions(scriptVersion, treeVersion) { + val cs = deserializeConstants(h, r) + val previousConstantStore = r.constantStore + // reader with constant store attached is required (to get tpe for a constant placeholder) + r.constantStore = new ConstantStore(cs) - val wasDeserialize_saved = r.wasDeserialize - r.wasDeserialize = false + val wasDeserialize_saved = r.wasDeserialize + r.wasDeserialize = false - val wasUsingBlockchainContext_saved = r.wasUsingBlockchainContext - r.wasUsingBlockchainContext = false + val wasUsingBlockchainContext_saved = r.wasUsingBlockchainContext + r.wasUsingBlockchainContext = false - val root = ValueSerializer.deserialize(r) - val hasDeserialize = r.wasDeserialize // == true if there was deserialization node - r.wasDeserialize = wasDeserialize_saved + val root = ValueSerializer.deserialize(r) + val hasDeserialize = r.wasDeserialize // == true if there was deserialization node + r.wasDeserialize = wasDeserialize_saved - val isUsingBlockchainContext = r.wasUsingBlockchainContext // == true if there was a node using the blockchain context - r.wasUsingBlockchainContext = wasUsingBlockchainContext_saved + val isUsingBlockchainContext = r.wasUsingBlockchainContext // == true if there was a node using the blockchain context + r.wasUsingBlockchainContext = wasUsingBlockchainContext_saved - if (checkType) { - CheckDeserializedScriptIsSigmaProp(root) - } + if (checkType) { + CheckDeserializedScriptIsSigmaProp(root) + } - r.constantStore = previousConstantStore - // now we know the end position of propositionBytes, read them all at once into array - val treeSize = r.position - startPos - r.position = startPos - val propositionBytes = r.getBytes(treeSize) + r.constantStore = previousConstantStore + // now we know the end position of propositionBytes, read them all at once into array + val treeSize = r.position - startPos + r.position = startPos + val propositionBytes = r.getBytes(treeSize) - new ErgoTree( - h, cs, Right(root.asSigmaProp), - propositionBytes, Some(hasDeserialize), Some(isUsingBlockchainContext)) + new ErgoTree( + h, cs, Right(root.asSigmaProp), + propositionBytes, Some(hasDeserialize), Some(isUsingBlockchainContext)) + } } catch { case e: ReaderPositionLimitExceeded => CheckPositionLimit.throwValidationException(e) + case iae: IllegalArgumentException => + throw SerializerException( + s"Tree version ($treeVersion) is above activated script version ($scriptVersion)", Some(iae)) } } catch { @@ -186,8 +202,9 @@ class ErgoTreeSerializer { val bytes = r.getBytes(numBytes) new ErgoTree(ErgoTree.DefaultHeader, EmptyConstants, Left(UnparsedErgoTree(bytes, ve)), bytes, None, None) case None => - throw new SerializerException( - s"Cannot handle ValidationException, ErgoTree serialized without size bit.", Some(ve)) + throw SerializerException( + s"Cannot handle ValidationException, ErgoTree serialized without size bit.", + Some(ve)) } } finally { @@ -287,6 +304,9 @@ class ErgoTreeSerializer { * allow to use serialized scripts as pre-defined templates. * See [[SubstConstants]] for details. * + * Note, this operation doesn't require (de)serialization of ErgoTree expression, + * thus it is more efficient than serialization roundtrip. + * * @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1. * @param positions zero based indexes in ErgoTree.constants array which * should be replaced with new values @@ -304,63 +324,90 @@ class ErgoTreeSerializer { s"expected positions and newVals to have the same length, got: positions: ${positions.toSeq},\n newVals: ${newVals.toSeq}") val r = SigmaSerializer.startReader(scriptBytes) val (header, _, constants, treeBytes) = deserializeHeaderWithTreeBytes(r) - val w = SigmaSerializer.startWriter() - w.put(header) - - 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)) { + 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() + + // 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)) { + constW.putUInt(constants.length) + } + + // The following is optimized O(nConstants + position.length) implementation + if (nConstants > 0) { + val backrefs = getPositionsBackref(positions, nConstants) + cfor(0)(_ < nConstants, _ + 1) { i => + val c = constants(i) + val iPos = backrefs(i) // index to `positions` + if (iPos == -1) { + // no position => no substitution, serialize original constant + constantSerializer.serialize(c, constW) + } else { + 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, 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.isV3OrLaterErgoTreeVersion) { + // 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) - } - // 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 => - val c = constants(i) - val iPos = backrefs(i) // index to `positions` - if (iPos == -1) { - // no position => no substitution, serialize original constant - constantSerializer.serialize(c, w) - } else { - assert(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}") + // the following is v4.x O(nConstants * positions.length) inefficient implementation + constants.zipWithIndex.foreach { + case (c, i) if positions.contains(i) => + 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(Some(constantStore)) + valW.putValue(newVal) + val newConsts = constantStore.getAll + require(newConsts.length == 1) + val newConst = newConsts.head + require(c.tpe == newConst.tpe, s"expected new constant to have the same ${c.tpe} tpe, got ${newConst.tpe}") constantSerializer.serialize(newConst, w) - } + case (c, _) => + constantSerializer.serialize(c, w) } - } - } else { - // for v4.x compatibility we save constants.length here (see the above comment to - // understand the consequences) - w.putUInt(constants.length) - - // the following is v4.x O(nConstants * positions.length) inefficient implementation - constants.zipWithIndex.foreach { - case (c, i) if positions.contains(i) => - 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) - valW.putValue(newVal) - val newConsts = constantStore.getAll - require(newConsts.length == 1) - val newConst = newConsts.head - require(c.tpe == newConst.tpe, s"expected new constant to have the same ${c.tpe} tpe, got ${newConst.tpe}") - constantSerializer.serialize(newConst, w) - 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..dcd4c70afa 100644 --- a/data/shared/src/main/scala/sigma/serialization/OpCodes.scala +++ b/data/shared/src/main/scala/sigma/serialization/OpCodes.scala @@ -144,6 +144,7 @@ object OpCodes { val MethodCallCode: OpCode = newOpCode(108) val GlobalCode : OpCode = newOpCode(109) + // todo: not used and should be removed, as Some / None constructors are implemented as Global methods val SomeValueCode: OpCode = newOpCode(110) val NoneValueCode: OpCode = newOpCode(111) // reserved 112 - 114 (3) @@ -153,6 +154,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) @@ -177,10 +179,12 @@ object OpCodes { val BitShiftLeftCode : OpCode = newOpCode(135) val BitShiftRightZeroedCode : OpCode = newOpCode(136) + // todo: not used, remove val CollShiftRightCode : OpCode = newOpCode(137) val CollShiftLeftCode : OpCode = newOpCode(138) val CollShiftRightZeroedCode : OpCode = newOpCode(139) + // todo: not used, remove val CollRotateLeftCode : OpCode = newOpCode(140) val CollRotateRightCode : OpCode = newOpCode(141) 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 9e5a35706c..5565126e2e 100644 --- a/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala @@ -4,6 +4,7 @@ import java.nio.ByteBuffer import scorex.util.ByteArrayBuilder import scorex.util.serialization._ import sigma.data.SigmaConstants +import sigma.serialization.SigmaByteWriter.{FixedCostCallback, PerItemCostCallback} import sigma.serialization.ValueCodes.OpCode object SigmaSerializer { @@ -51,14 +52,18 @@ object SigmaSerializer { def startWriter(): SigmaByteWriter = { val b = new ByteArrayBuilder() val wi = new VLQByteBufferWriter(b) - val w = new SigmaByteWriter(wi, constantExtractionStore = None) + val w = new SigmaByteWriter(wi, constantExtractionStore = None, addFixedCostCallbackOpt = None, addPerItemCostCallbackOpt = None) w } - def startWriter(constantExtractionStore: ConstantStore): SigmaByteWriter = { + def startWriter( + constantExtractionStore: Option[ConstantStore], + addFixedCostCallback: Option[FixedCostCallback] = None, + addPerItemCostCallback: Option[PerItemCostCallback] = None + ): SigmaByteWriter = { val b = new ByteArrayBuilder() val wi = new VLQByteBufferWriter(b) - val w = new SigmaByteWriter(wi, constantExtractionStore = Some(constantExtractionStore)) + val w = new SigmaByteWriter(wi, constantExtractionStore = constantExtractionStore, addFixedCostCallback, addPerItemCostCallback) w } } diff --git a/data/shared/src/main/scala/sigma/serialization/TupleSerializer.scala b/data/shared/src/main/scala/sigma/serialization/TupleSerializer.scala index 89a7ea457c..f7357e460b 100644 --- a/data/shared/src/main/scala/sigma/serialization/TupleSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/TupleSerializer.scala @@ -26,7 +26,7 @@ case class TupleSerializer(cons: Seq[Value[SType]] => Value[SType]) override def parse(r: SigmaByteReader): Value[SType] = { val size = r.getByte() - // note, in v4.x, v5.x tuples always has 2 elements, this may change in v6.0 + // note, in v4.x, v5.x, v6.x tuples always has 2 elements, this may change in v7.0 // in which case allocation can be avoided for empty tuples val values = safeNewArray[SValue](size) cfor(0)(_ < size, _ + 1) { i => diff --git a/data/shared/src/main/scala/sigma/serialization/ValueSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ValueSerializer.scala index 3eb6c75713..406ab62637 100644 --- a/data/shared/src/main/scala/sigma/serialization/ValueSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ValueSerializer.scala @@ -90,7 +90,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { LogicalTransformerSerializer(AND, mkAND), LogicalTransformerSerializer(OR, mkOR), LogicalTransformerSerializer(XorOf, mkXorOf), - TaggedVariableSerializer(mkTaggedVariable), // TODO v6.0: remove this serializer https://github.com/ScorexFoundation/sigmastate-interpreter/issues/584 + TaggedVariableSerializer(mkTaggedVariable), // TODO v7.0: remove this serializer https://github.com/ScorexFoundation/sigmastate-interpreter/issues/584 GetVarSerializer(mkGetVar), MapCollectionSerializer(mkMapCollection), BooleanTransformerSerializer[SType](Exists, mkExists), diff --git a/docs/LangSpec.md b/docs/LangSpec.md index 4553b48ec5..7de1a3dd9f 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 syntax treeы which are 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 language 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, i.e. 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,29 @@ 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] + } /** Represents data of the block headers available in scripts. */ class Header { + /** Bytes representation of ModifierId of this Header */ def id: Coll[Byte] @@ -272,7 +388,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 +400,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 +562,12 @@ class GroupElement { * @returnthis 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
@@ -629,10 +743,24 @@ class AvlTree {
* Return None if operations were not performed.
* @param operations collection of key-value pairs to update in this
* authenticated dictionary.
- * @param proof data to reconstruct part of the tree
+ * @param proof subtree which is enough to check operations
*/
def update(operations: Coll[(Coll[Byte], Coll[Byte])], proof: Coll[Byte]): Option[AvlTree]
+
+ /** Perform insertions or updates of key-value entries into this tree using proof `proof`.
+ * Throws exception if proof is incorrect
+ *
+ * @note CAUTION! Pairs must be ordered the same way they were in ops
+ * before proof was generated.
+ * Return Some(newTree) if successful
+ * Return None if operations were not performed.
+ * @param operations collection of key-value pairs to insert or update in this
+ * authenticated dictionary.
+ * @param proof subtree which is enough to check operations
+ */
+ def insertOrUpdate(operations: Coll[(Coll[Byte], Coll[Byte])], proof: Coll[Byte]): Option[AvlTree]
+
/** Perform removal of entries into this tree using proof `proof`.
* Throws exception if proof is incorrect
* Return Some(newTree) if successful
@@ -665,8 +793,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.
*/
@@ -838,6 +965,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
+
+
}
```
@@ -847,6 +995,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.
@@ -861,7 +1017,73 @@ satisfying some predicate (condition)
val ok = OUTPUTS.exists { (box: Box) => box.value > 1000 }
```
-### Predefined global functions
+### 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): UnsignedBigInt
+
+ /** 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 functions
ErgoScript standard library include predefined functions that can be called
@@ -896,6 +1118,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
@@ -912,7 +1138,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.
@@ -969,6 +1195,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.
@@ -988,6 +1226,12 @@ def proveDlog(value: GroupElement): SigmaProp
*/
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.
@@ -1089,6 +1333,17 @@ def executeFromSelfRegWithDefault[T](id: Int, default: T): T
def substConstants[T](scriptBytes: Coll[Byte], positions: Coll[Int], newValues: Coll[T]): Coll[Byte]
```
+## Known Limitations
+
+* You can use methods added in 6.0 and new `UnsignedBigInt` type only within an ErgoTree with version >= 3
+* 6.0 methods and other changes can be found in [EIP-50](https://github.com/ergoplatform/eips/pull/100)
+* You can't put values of `Option[]`, `Header`, `UnsignedBigInt` types into registers or context extension variables to
+avoid versioning issues with 5.0 clients. To bypass the limitation, you can write typed value as bytes and call `Global.deserialize` in a script
+to get an instance of those types.
+* Higher order lambdas supported since 6.0 (you need to use ErgoTree v3 to have them parsed), see a higher order lambda example @
+[https://github.com/ergoplatform/sigmastate-interpreter/blob/b754e143cf38ed86d95698ede744a470dfa053d6/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala#L10040](https://github.com/ergoplatform/sigmastate-interpreter/blob/b754e143cf38ed86d95698ede744a470dfa053d6/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala#L10040)
+
+
## Examples
See [white paper for examples](https://ergoplatform.org/docs/ErgoScript.pdf)
diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala
index e421e8fdfe..4b1366d9fd 100644
--- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala
+++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala
@@ -168,7 +168,7 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData,
syntax.error(s"Undefined context property: currentErgoTreeVersion"))
CContext(
dataInputs, headers, preHeader, inputs, outputs, preHeader.height, selfBox, selfIndex, avlTree,
- preHeader.minerPk.getEncoded, vars, activatedScriptVersion, ergoTreeVersion)
+ preHeader.minerPk.getEncoded, vars, spendingTransaction, activatedScriptVersion, ergoTreeVersion)
}
diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CAvlTreeVerifier.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CAvlTreeVerifier.scala
index 5739e65ade..53736ee0bd 100644
--- a/interpreter/shared/src/main/scala/sigmastate/eval/CAvlTreeVerifier.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/eval/CAvlTreeVerifier.scala
@@ -1,6 +1,6 @@
package sigmastate.eval
-import scorex.crypto.authds.avltree.batch.{BatchAVLVerifier, Insert, Lookup, Remove, Update}
+import scorex.crypto.authds.avltree.batch.{BatchAVLVerifier, Insert, InsertOrUpdate, Lookup, Remove, Update}
import scorex.crypto.authds.{ADDigest, ADKey, ADValue, SerializedAdProof}
import scorex.crypto.hash.{Blake2b256, Digest32}
import sigma.data.CAvlTree
@@ -32,6 +32,9 @@ class CAvlTreeVerifier private(
override def performUpdate(key: Array[Byte], value: Array[Byte]): Try[Option[Array[Byte]]] =
performOneOperation(Update(ADKey @@ key, ADValue @@ value))
+ override def performInsertOrUpdate(key: Array[Byte], value: Array[Byte]): Try[Option[Array[Byte]]] =
+ performOneOperation(InsertOrUpdate(ADKey @@ key, ADValue @@ value))
+
override def performRemove(key: Array[Byte]): Try[Option[Array[Byte]]] =
performOneOperation(Remove(ADKey @@ key))
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/Extensions.scala b/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala
index f9cb0e9f75..ce8758bd0b 100644
--- a/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala
@@ -3,7 +3,7 @@ package sigmastate.eval
import debox.cfor
import org.ergoplatform.ErgoBox
import org.ergoplatform.ErgoBox.TokenId
-import scorex.crypto.authds.avltree.batch.{Insert, Lookup, Remove, Update}
+import scorex.crypto.authds.avltree.batch.{Insert, InsertOrUpdate, Lookup, Remove, Update}
import scorex.crypto.authds.{ADKey, ADValue}
import scorex.util.encode.Base16
import sigma.ast.SType.AnyOps
@@ -91,7 +91,12 @@ object Extensions {
val bv = CAvlTreeVerifier(tree, proof)
entries.forall { case (key, value) =>
val insertRes = bv.performOneOperation(Insert(ADKey @@ key.toArray, ADValue @@ value.toArray))
- if (insertRes.isFailure) {
+ // For versioned change details, see see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908
+ // Tree-versioned condition added in 6.0 interpreter code, so after 6.0 activation:
+ // * 5.0 interpreter will skip v3 tree validation at all
+ // * 6.0 won't throw exception
+ // so both clients wont throw exception
+ if (insertRes.isFailure && !VersionContext.current.isV3OrLaterErgoTreeVersion) {
syntax.error(s"Incorrect insert for $tree (key: $key, value: $value, digest: ${tree.digest}): ${insertRes.failed.get}}")
}
insertRes.isSuccess
@@ -120,6 +125,24 @@ object Extensions {
}
}
+ def insertOrUpdate(
+ entries: Coll[(Coll[Byte], Coll[Byte])],
+ proof: Coll[Byte]): Option[AvlTree] = {
+ if (!tree.isInsertAllowed || !tree.isUpdateAllowed) {
+ None
+ } else {
+ val bv = CAvlTreeVerifier(tree, proof)
+ entries.forall { case (key, value) =>
+ val insertRes = bv.performOneOperation(InsertOrUpdate(ADKey @@ key.toArray, ADValue @@ value.toArray))
+ insertRes.isSuccess
+ }
+ bv.digest match {
+ case Some(d) => Some(tree.updateDigest(Colls.fromArray(d)))
+ case _ => None
+ }
+ }
+ }
+
def remove(operations: Coll[Coll[Byte]], proof: Coll[Byte]): Option[AvlTree] = {
if (!tree.isRemoveAllowed) {
None
diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala
index de8aa6b620..0a56088e08 100644
--- a/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala
@@ -5,7 +5,7 @@ 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
@@ -142,9 +142,12 @@ class CErgoTreeEvaluator(
// the cost of tree lookup is O(bv.treeHeight)
addSeqCost(InsertIntoAvlTree_Info, nItems) { () =>
val insertRes = bv.performInsert(key.toArray, value.toArray)
- // TODO v6.0: throwing exception is not consistent with update semantics
- // however it preserves v4.0 semantics (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908)
- if (insertRes.isFailure) {
+ // For versioned change details, see see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908
+ // Tree-versioned condition added in 6.0 interpreter code, so after 6.0 activation:
+ // * 5.0 interpreter will skip v3 tree validation at all
+ // * 6.0 won't throw exception
+ // so both clients wont throw exception
+ if (insertRes.isFailure && !VersionContext.current.isV3OrLaterErgoTreeVersion) {
syntax.error(s"Incorrect insert for $tree (key: $key, value: $value, digest: ${tree.digest}): ${insertRes.failed.get}}")
}
res = insertRes.isSuccess
@@ -171,7 +174,7 @@ class CErgoTreeEvaluator(
// when the tree is empty we still need to add the insert cost
val nItems = Math.max(bv.treeHeight, 1)
- // here we use forall as looping with fast break on first failed tree oparation
+ // here we use forall as looping with fast break on first failed tree operation
operations.forall { case (key, value) =>
var res = true
// the cost of tree update is O(bv.treeHeight)
@@ -190,6 +193,40 @@ class CErgoTreeEvaluator(
}
}
+ override def insertOrUpdate_eval(
+ mc: MethodCall, tree: AvlTree,
+ operations: KeyValueColl, proof: Coll[Byte]): Option[AvlTree] = {
+ addCost(isUpdateAllowed_Info)
+ addCost(isInsertAllowed_Info)
+ if (!(tree.isUpdateAllowed && tree.isInsertAllowed)) {
+ None
+ } else {
+ val bv = createVerifier(tree, proof)
+ // when the tree is empty we still need to add the insert cost
+ val nItems = Math.max(bv.treeHeight, 1)
+
+ // here we use forall as looping with fast break on first failed tree operation
+ operations.forall { case (key, value) =>
+ var res = true
+ // the cost of tree update is O(bv.treeHeight)
+ // Here (and in the previous methods) the cost is not properly approximated.
+ // When the tree is small (or empty), but there are many `operations`, the treeHeight will grow on every iteration.
+ // So should the cost on every iteration.
+ addSeqCost(UpdateAvlTree_Info, nItems) { () =>
+ val updateRes = bv.performInsertOrUpdate(key.toArray, value.toArray)
+ res = updateRes.isSuccess
+ }
+ res
+ }
+ bv.digest match {
+ case Some(d) =>
+ addCost(updateDigest_Info)
+ Some(tree.updateDigest(Colls.fromArray(d)))
+ case _ => None
+ }
+ }
+ }
+
override def remove_eval(
mc: MethodCall, tree: AvlTree,
operations: Coll[Coll[Byte]], proof: Coll[Byte]): Option[AvlTree] = {
@@ -293,7 +330,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 +342,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 +487,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 8e8da22462..c2f853cafa 100644
--- a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala
@@ -247,7 +247,16 @@ 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 V6 protocol activation, the deserialization cost was not added to the total cost
+ // Since V6, it is fixed, so deserialization cost is added
+ // This change is okay for older clients (< V6), as they are getting cost which is less than for new clients
+ // So when new clients are accepting V6 block with deserialization costs added, V5 clients will accept them also
+ // And blocks are generated by V6 clients (90+% at least) after V6 activation
+ applyDeserializeContextJITC(if (VersionContext.current.isV6Activated) {
+ context1
+ } else {
+ context
+ }, prop)
}
// here we assume that when `propTree` is TrueProp then `reduceToCrypto` always succeeds
@@ -355,7 +364,6 @@ trait Interpreter {
case None => // proceed normally
}
VersionContext.withVersions(context.activatedScriptVersion, ergoTree.version) {
- // NOTE, ergoTree.complexity is not acrued to the cost in v5.0
val reduced = fullReduction(ergoTree, context, env)
reduced.value match {
case TrivialProp.TrueProp => (true, reduced.cost)
@@ -593,4 +601,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/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/org/ergoplatform/validation/ValidationSpecification.scala b/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala
index b223355c5b..47068a9102 100644
--- a/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala
+++ b/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala
@@ -3,5 +3,5 @@ package org.ergoplatform.validation
import sigma.validation.SigmaValidationSettings
trait ValidationSpecification {
- implicit val vs: SigmaValidationSettings = ValidationRules.currentSettings
+ def vs: SigmaValidationSettings = ValidationRules.currentSettings
}
diff --git a/interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala b/interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala
index acf8e74bd2..ce007d0e7f 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ // 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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 43e9cf9e5d..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,14 +57,19 @@ 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)
}
}
@@ -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 7da3c3819f..875720f3ab 100644
--- a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala
@@ -3,10 +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.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
@@ -14,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
@@ -23,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, ver) {
+ test()
+ }
+ case None =>
+ test()
+ }
}
def testCollection[T <: SType](tpe: T) = {
@@ -53,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
)
}
}
@@ -81,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, VersionContext.V6SoftForkVersion) {
+ 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)))
+ }
}
}
@@ -98,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") {
@@ -139,6 +193,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, VersionContext.V6SoftForkVersion) {
+ 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, VersionContext.V6SoftForkVersion) {
+ 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..6324f666cb 100644
--- a/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala
@@ -1,6 +1,12 @@
package sigma.serialization
+import scorex.util.encode.Base16
+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 +27,186 @@ 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, VersionContext.V6SoftForkVersion) {
+ code
+ }
+
+ a[SerializerException] should be thrownBy (
+ VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) {
+ code
+ }
+ )
+
+ a[sigma.serialization.SerializerException] should be thrownBy (
+ VersionContext.withVersions(VersionContext.V6SoftForkVersion, (VersionContext.V6SoftForkVersion - 1).toByte) {
+ 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, VersionContext.V6SoftForkVersion) {
+ code
+ }
+
+ a[ValidationException] should be thrownBy (
+ VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) {
+ code
+ }
+ )
+
+ a[ValidationException] should be thrownBy (
+ VersionContext.withVersions(VersionContext.V6SoftForkVersion, (VersionContext.V6SoftForkVersion - 1).toByte) {
+ 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, VersionContext.V6SoftForkVersion) {
+ code
+ }
+
+ a[ValidationException] should be thrownBy (
+ VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) {
+ code
+ }
+ )
+
+ a[ValidationException] should be thrownBy (
+ VersionContext.withVersions(VersionContext.V6SoftForkVersion, (VersionContext.V6SoftForkVersion - 1).toByte) {
+ 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)
+ }
+
+ VersionContext.withVersions(VersionContext.V6SoftForkVersion, VersionContext.V6SoftForkVersion) {
+ code
+ }
+
+ a[SerializerException] should be thrownBy (
+ VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) {
+ code
+ }
+ )
+
+ a[sigma.serialization.SerializerException] should be thrownBy (
+ VersionContext.withVersions(VersionContext.V6SoftForkVersion, (VersionContext.V6SoftForkVersion - 1).toByte) {
+ 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, VersionContext.V6SoftForkVersion) {
+ code
+ }
+
+ a[ValidationException] should be thrownBy (
+ VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) {
+ code
+ }
+ )
+
+ a[ValidationException] should be thrownBy (
+ VersionContext.withVersions(VersionContext.V6SoftForkVersion, (VersionContext.V6SoftForkVersion - 1).toByte) {
+ 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, VersionContext.V6SoftForkVersion) {
+ code
+ }
+
+ a[ValidationException] should be thrownBy (
+ VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) {
+ code
+ }
+ )
+
+ a[ValidationException] should be thrownBy (
+ VersionContext.withVersions(VersionContext.V6SoftForkVersion, (VersionContext.V6SoftForkVersion - 1).toByte) {
+ code
+ }
+ )
+ }
+
+ property("eq") {
+ println("sv: " + VersionContext.current.activatedVersion)
+ println("tv: " + VersionContext.current.ergoTreeVersion)
+ val bs = "10010400d801d601b2a5730000d1ed93c2a7c2720193e4dc640ae4c67201046402e4c67201050ee4c67201070ee4c67201060e"
+ println(ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(Base16.decode(bs).get))
+ }
+
}
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..ce0f941308 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, ver) {
+ 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..200b1ba84d 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}
@@ -42,7 +42,9 @@ trait ObjectGenerators extends TypeGenerators
with ConcreteCollectionGenerators
with TestsBase {
- val ThresholdLimit = 10
+ private val ThresholdLimit = 10
+
+ private val MaxBigIntValue = new BigInteger("7F" + "ff" * 31, 16)
implicit lazy val statusArb: Arbitrary[RuleStatus] = Arbitrary(statusGen)
implicit lazy val arbMapCollection: Arbitrary[MapCollection[SInt.type, SInt.type]] = Arbitrary(mapCollectionGen)
@@ -81,8 +83,9 @@ trait ObjectGenerators extends TypeGenerators
implicit lazy val arbProveDlog : Arbitrary[ProveDlog] = Arbitrary(proveDlogGen)
implicit lazy val arbProveDHT: Arbitrary[ProveDHTuple] = Arbitrary(proveDHTGen)
implicit lazy val arbRegisterIdentifier: Arbitrary[RegisterId] = Arbitrary(registerIdentifierGen)
- implicit lazy val arbBigInteger: Arbitrary[BigInteger] = Arbitrary(Arbitrary.arbBigInt.arbitrary.map(_.bigInteger))
+ implicit lazy val arbBigInteger: Arbitrary[BigInteger] = Arbitrary(Arbitrary.arbBigInt.arbitrary.map(_.bigInteger).map(_.mod(MaxBigIntValue)))
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 +145,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 +310,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 +331,11 @@ trait ObjectGenerators extends TypeGenerators
longConstGen,
booleanConstGen,
bigIntConstGen,
+ if(VersionContext.current.isV3OrLaterErgoTreeVersion) {
+ unsignedBigIntConstGen
+ } else {
+ bigIntConstGen
+ },
groupElementConstGen,
getVar[SInt.type],
getVar[SLong.type],
@@ -422,7 +434,7 @@ trait ObjectGenerators extends TypeGenerators
import ValidationRules._
- val numRules = currentSettings.size
+ def numRules = currentSettings.size
val replacedRuleIdGen = Gen.chooseNum((FirstRuleId + numRules).toShort, Short.MaxValue)
@@ -694,7 +706,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 +718,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..53bb8de156 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.isV3OrLaterErgoTreeVersion){
+ 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.isV3OrLaterErgoTreeVersion) {
+ unsignedBigIntTypeGen
+ } else {
+ bigIntTypeGen
+ }
))
} yield STuple(values.toIndexedSeq)
diff --git a/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala b/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala
index e9ba273e17..f80a244d0d 100644
--- a/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala
@@ -2,12 +2,21 @@ 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 => SigmaDsl, TrivialProp}
+import sigma.data.{CBigInt, CUnsignedBigInt, TrivialProp}
+import sigma.eval.SigmaDsl
import sigma.util.Extensions.SigmaBooleanOps
+import sigma.util.NBitsUtils
import java.math.BigInteger
import sigma.{ContractsTestkit, SigmaProp}
+import sigmastate.interpreter.{CErgoTreeEvaluator, CostAccumulator}
+import sigmastate.interpreter.CErgoTreeEvaluator.DefaultProfiler
+import sigma.{Box, VersionContext}
import scala.language.implicitConversions
@@ -63,4 +72,89 @@ class BasicOpsTests extends AnyFunSuite with ContractsTestkit with Matchers {
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 CUnsignedBigInt(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.UnsignedBigInt](Map.empty)(evaluator)
+
+ res should be(CUnsignedBigInt(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 b04e9c150f..ba04df1347 100644
--- a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala
+++ b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala
@@ -47,7 +47,7 @@ trait ContractsTestkit {
new CContext(
noInputs.toColl, noHeaders, dummyPreHeader,
inputs.toColl, outputs.toColl, height, self, inputs.indexOf(self), tree,
- minerPk.toColl, vars.toColl, activatedScriptVersion, currErgoTreeVersion)
+ minerPk.toColl, vars.toColl, null, activatedScriptVersion, currErgoTreeVersion)
def newContext(
height: Int,
diff --git a/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala b/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala
index f113a484ef..15bf535e4a 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,13 +12,13 @@ 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
import sigmastate.utils.Helpers
import sigma.ast.{SBoolean, SSigmaProp}
-import sigma.crypto.EcPointType
+import sigma.crypto.{EcPointType, SecP256K1Group}
import ErgoTree.HeaderType
import sigma.eval.SigmaDsl
@@ -69,12 +69,15 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators {
def createBigIntMaxValue(): BigInt = BigIntMaxValue_instances.getNext
- // TODO v6.0: this values have bitCount == 255 (see to256BitValueExact) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554)
val BigIntMinValue = CBigInt(new BigInteger("-7F" + "ff" * 31, 16))
val BigIntMaxValue = createBigIntMaxValue()
- val BigIntOverlimit = CBigInt(new BigInteger("7F" + "ff" * 33, 16))
+ val BigIntOverlimit = {
+ VersionContext.withVersions(2,2) {
+ CBigInt(new BigInteger("7F" + "ff" * 33, 16))
+ }
+ }
val ge1str = "03358d53f01276211f92d0aefbd278805121d4ff6eb534b777af1ee8abae5b2056"
@@ -240,17 +243,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,
+ 1.toByte,
Helpers.decodeBytes("0180dd805b0000ff5400b997fd7f0b9b00de00fb03c47e37806a8186b94f07ff"),
Helpers.decodeBytes("01f07f60d100ffb970c3007f60ff7f24d4070bb8fffa7fca7f34c10001ffe39d"),
- CAvlTree(createAvlTreeData()),
+ CAvlTree(createAvlTreeData()).digest,
Helpers.decodeBytes("804101ff01000080a3ffbd006ac080098df132a7017f00649311ec0e00000100"),
1L,
-1L,
@@ -259,15 +261,19 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators {
Helpers.decodeGroupElement("039bdbfa0b49cc6bef58297a85feff45f7bbeb500a9d2283004c74fcedd4bd2904"),
Helpers.decodeGroupElement("0361299207fa392231e23666f6945ae3e867b978e021d8d702872bde454e9abe9c"),
Helpers.decodeBytes("7f4f09012a807f01"),
- CBigInt(new BigInteger("-e24990c47e15ed4d0178c44f1790cc72155d516c43c3e8684e75db3800a288", 16)),
- Helpers.decodeBytes("7f0180")
+ CBigInt(SecP256K1Group.order.divide(new BigInteger("2"))),
+ 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/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/LangTests.scala b/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala
index 32943bca44..de83070ac3 100644
--- a/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala
+++ b/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala
@@ -79,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 02b28f86ca..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
@@ -894,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/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 9eec42a4fb..7bcf10d593 100644
--- a/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala
+++ b/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala
@@ -1,6 +1,7 @@
package org.ergoplatform
import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
+import sigma.Colls
import sigma.ast.SType
import sigma.ast.syntax.SigmaPropValue
import sigma.ast.Value
@@ -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/ContractSyntax.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala
index 11cbaff739..34598627db 100644
--- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala
+++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala
@@ -55,6 +55,7 @@ trait ContractSyntax { contract: SigmaContract =>
case _: String => StringType
case _: Unit => UnitType
case _: sigma.BigInt => BigIntRType
+ case _: sigma.UnsignedBigInt => UnsignedBigIntRType
case _: GroupElement => GroupElementRType
case _: ErgoBox => syntax.ErgoBoxRType // TODO remove this RType
case _: Box => BoxRType
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala
index 0ffb92b54a..aad3ecf274 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala
@@ -1,24 +1,34 @@
package sigma.compiler.ir
import org.ergoplatform._
+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.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.util.Extensions.ByteOps
import sigma.{SigmaException, ast}
-import sigmastate.interpreter.Interpreter.ScriptEnv
+import sigma.VersionContext
import scala.collection.mutable.ArrayBuffer
import scala.language.{existentials,implicitConversions}
+
/** Perform translation of typed expression given by [[Value]] to a graph in IRContext.
* Which be than be translated to [[ErgoTree]] by using [[TreeBuilding]].
*
@@ -30,6 +40,7 @@ import scala.language.{existentials,implicitConversions}
trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
import AvlTree._
import BigInt._
+ import UnsignedBigInt._
import Box._
import Coll._
import CollBuilder._
@@ -255,6 +266,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
case SString => StringElement
case SAny => AnyElement
case SBigInt => bigIntElement
+ case SUnsignedBigInt => unsignedBigIntElement
case SBox => boxElement
case SContext => contextElement
case SGlobal => sigmaDslBuilderElement
@@ -281,6 +293,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
case StringElement => SString
case AnyElement => SAny
case _: BigIntElem[_] => SBigInt
+ case _: UnsignedBigIntElem[_] => SUnsignedBigInt
case _: GroupElementElem[_] => SGroupElement
case _: AvlTreeElem[_] => SAvlTree
case oe: WOptionElem[_, _] => SOption(elemToSType(oe.eItem))
@@ -308,6 +321,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
case StringElement => StringIsLiftable
case UnitElement => UnitIsLiftable
case _: BigIntElem[_] => LiftableBigInt
+ case _: UnsignedBigIntElem[_] => LiftableUnsignedBigInt
case _: GroupElementElem[_] => LiftableGroupElement
case ce: CollElem[t,_] =>
implicit val lt = liftableFromElem[t](ce.eItem)
@@ -328,20 +342,24 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
(ShortElement, ShortIsExactIntegral),
(IntElement, IntIsExactIntegral),
(LongElement, LongIsExactIntegral),
- (bigIntElement, BigIntIsExactIntegral)
+ (bigIntElement, BigIntIsExactIntegral),
+ (unsignedBigIntElement, UnsignedBigIntIsExactIntegral)
)
private lazy val elemToExactIntegralMap = Map[Elem[_], ExactIntegral[_]](
(ByteElement, ByteIsExactIntegral),
(ShortElement, ShortIsExactIntegral),
(IntElement, IntIsExactIntegral),
- (LongElement, LongIsExactIntegral)
+ (LongElement, LongIsExactIntegral),
+ (bigIntElement, BigIntIsExactIntegral),
+ (unsignedBigIntElement, UnsignedBigIntIsExactIntegral)
)
protected lazy val elemToExactOrderingMap = Map[Elem[_], ExactOrdering[_]](
(ByteElement, ByteIsExactOrdering),
(ShortElement, ShortIsExactOrdering),
(IntElement, IntIsExactOrdering),
(LongElement, LongIsExactOrdering),
- (bigIntElement, BigIntIsExactOrdering)
+ (bigIntElement, BigIntIsExactOrdering),
+ (unsignedBigIntElement, UnsignedBigIntIsExactOrdering)
)
/** @return [[ExactNumeric]] instance for the given type */
@@ -438,8 +456,8 @@ trait GraphBuilding extends Base with DefRewriting { 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 Constant(v, tpe) => v match {
@@ -451,6 +469,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
assert(tpe == SBigInt)
val resV = liftConst(bi)
resV
+ case ubi: SUnsignedBigInt =>
+ assert(tpe == SUnsignedBigInt)
+ val resV = liftConst(ubi)
+ resV
case p: SGroupElement =>
assert(tpe == SGroupElement)
val resV = liftConst(p)
@@ -937,7 +959,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
sigmaDslBuilder.decodePoint(bytes)
// fallback rule for MethodCall, should be the last case in the list
- case sigma.ast.MethodCall(obj, method, args, _) =>
+ case sigma.ast.MethodCall(obj, method, args, typeSubst) =>
val objV = eval(obj)
val argsV = args.map(eval)
(objV, method.objType) match {
@@ -994,7 +1016,18 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
val i = asRep[Int](argsV(0))
val d = asRep[t](argsV(1))
xs.getOrElse(i, d)
- case _ => throwError
+ case SCollectionMethods.ReverseMethod.name =>
+ xs.reverse
+ 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 =>
@@ -1008,7 +1041,7 @@ trait GraphBuilding extends Base with DefRewriting { 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 =>
@@ -1021,12 +1054,19 @@ trait GraphBuilding extends Base with DefRewriting { 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.isV3OrLaterErgoTreeVersion =>
+ 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 =>
@@ -1049,7 +1089,12 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
ctx.LastBlockUtxoRootHash
case SContextMethods.minerPubKeyMethod.name =>
ctx.minerPubKey
- case _ => throwError
+ case SContextMethods.getVarFromInputMethod.name if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ 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 =>
@@ -1096,7 +1141,11 @@ trait GraphBuilding extends Base with DefRewriting { 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 SAvlTreeMethods.insertOrUpdateMethod.name =>
+ val operations = asRep[Coll[(Coll[Byte], Coll[Byte])]](argsV(0))
+ val proof = asRep[Coll[Byte]](argsV(1))
+ tree.insertOrUpdate(operations, proof)
+ case _ => throwError()
}
case (ph: Ref[PreHeader]@unchecked, SPreHeaderMethods) => method.name match {
case SPreHeaderMethods.versionMethod.name =>
@@ -1113,7 +1162,7 @@ trait GraphBuilding extends Base with DefRewriting { 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 =>
@@ -1146,7 +1195,9 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
h.powDistance
case SHeaderMethods.votesMethod.name =>
h.votes
- case _ => throwError
+ case SHeaderMethods.checkPowMethod.name if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ h.checkPow
+ case _ => throwError()
}
case (g: Ref[SigmaDslBuilder]@unchecked, SGlobalMethods) => method.name match {
case SGlobalMethods.groupGeneratorMethod.name =>
@@ -1155,13 +1206,116 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
val c1 = asRep[Coll[Byte]](argsV(0))
val c2 = asRep[Coll[Byte]](argsV(1))
g.xor(c1, c2)
- case _ => throwError
+ case SGlobalMethods.encodeNBitsMethod.name if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ val c1 = asRep[BigInt](argsV(0))
+ g.encodeNbits(c1)
+ case SGlobalMethods.decodeNBitsMethod.name if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ val c1 = asRep[Long](argsV(0))
+ g.decodeNbits(c1)
+ case SGlobalMethods.powHitMethod.name if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ 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.isV3OrLaterErgoTreeVersion =>
+ val c1 = asRep[BigInt](argsV(0))
+ g.encodeNbits(c1)
+ case SGlobalMethods.decodeNBitsMethod.name if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ val c1 = asRep[Long](argsV(0))
+ g.decodeNbits(c1)
+ case SGlobalMethods.deserializeToMethod.name if VersionContext.current.isV3OrLaterErgoTreeVersion =>
+ 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/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
index 69736a0224..77613c58bf 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
@@ -1,5 +1,8 @@
package sigma.compiler.ir
+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
@@ -114,6 +117,59 @@ object GraphIRReflection {
},
mkMethod(clazz, "divide", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.BigInt].divide(args(0).asInstanceOf[ctx.Ref[ctx.BigInt]])
+ },
+ mkMethod(clazz, "toUnsigned", Array[Class[_]]()) { (obj, _) =>
+ obj.asInstanceOf[ctx.BigInt].toUnsigned()
+ },
+ mkMethod(clazz, "toUnsignedMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.BigInt].toUnsignedMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ }
+ )
+ )
+ }
+
+ { val clazz = classOf[SigmaDsl#UnsignedBigInt]
+ val ctx = null.asInstanceOf[SigmaDsl] // ok! type level only
+ registerClassEntry(clazz,
+ methods = Map(
+ mkMethod(clazz, "add", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].add(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "max", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].max(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "min", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].min(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "subtract", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].subtract(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "multiply", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].multiply(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "mod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].mod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "divide", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].divide(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "plusMod", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].plusMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "subtractMod", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].subtractMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "multiplyMod", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].multiplyMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "mod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].mod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "modInverse", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].modInverse(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
+ mkMethod(clazz, "toSigned", Array[Class[_]]()) { (obj, _) =>
+ obj.asInstanceOf[ctx.UnsignedBigInt].toSigned
}
)
)
@@ -202,6 +258,19 @@ 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, "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]])
}
)
)
@@ -218,10 +287,6 @@ object GraphIRReflection {
obj.asInstanceOf[ctx.AvlTree].getMany(args(0).asInstanceOf[ctx.Ref[ctx.Coll[ctx.Coll[Byte]]]],
args(1).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])
},
- mkMethod(clazz, "update", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) =>
- obj.asInstanceOf[ctx.AvlTree].update(args(0).asInstanceOf[ctx.Ref[ctx.Coll[(ctx.Coll[Byte], ctx.Coll[Byte])]]],
- args(1).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])
- },
mkMethod(clazz, "keyLength", Array[Class[_]]()) { (obj, _) =>
obj.asInstanceOf[ctx.AvlTree].keyLength
},
@@ -238,6 +303,14 @@ object GraphIRReflection {
obj.asInstanceOf[ctx.AvlTree].insert(args(0).asInstanceOf[ctx.Ref[ctx.Coll[(ctx.Coll[Byte], ctx.Coll[Byte])]]],
args(1).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])
},
+ mkMethod(clazz, "update", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.AvlTree].update(args(0).asInstanceOf[ctx.Ref[ctx.Coll[(ctx.Coll[Byte], ctx.Coll[Byte])]]],
+ args(1).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])
+ },
+ mkMethod(clazz, "insertOrUpdate", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.AvlTree].insertOrUpdate(args(0).asInstanceOf[ctx.Ref[ctx.Coll[(ctx.Coll[Byte], ctx.Coll[Byte])]]],
+ args(1).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])
+ },
mkMethod(clazz, "isRemoveAllowed", Array[Class[_]]()) { (obj, _) =>
obj.asInstanceOf[ctx.AvlTree].isRemoveAllowed
},
@@ -332,6 +405,9 @@ object GraphIRReflection {
mkMethod(clazz, "getVar", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.Context].getVar(args(0).asInstanceOf[ctx.Ref[Byte]])(args(1).asInstanceOf[ctx.Elem[_]])
},
+ mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.Context].getVarFromInput(args(0).asInstanceOf[ctx.Ref[Short]], args(1).asInstanceOf[ctx.Ref[Byte]])(args(2).asInstanceOf[ctx.Elem[_]])
+ },
mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, args) =>
obj.asInstanceOf[ctx.Context].headers
}
@@ -346,6 +422,9 @@ object GraphIRReflection {
mkMethod(clazz, "exp", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.GroupElement].exp(args(0).asInstanceOf[ctx.Ref[ctx.BigInt]])
},
+ mkMethod(clazz, "expUnsigned", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
+ obj.asInstanceOf[ctx.GroupElement].expUnsigned(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]])
+ },
mkMethod(clazz, "multiply", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.GroupElement].multiply(args(0).asInstanceOf[ctx.Ref[ctx.GroupElement]])
},
@@ -407,6 +486,9 @@ 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
}
)
)
@@ -504,6 +586,33 @@ 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]])
}
)
)
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala b/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala
index c60bc0882f..a22962f987 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala
@@ -153,7 +153,7 @@ trait IRContext
override def invokeUnlifted(e: Elem[_], mc: MethodCall, dataEnv: DataEnv): Any = e match {
case _: CollElem[_,_] => mc match {
case CollMethods.map(_, f) =>
- val newMC = mc.copy(args = mc.args :+ f.elem.eRange)(mc.resultType, mc.isAdapterCall)
+ val newMC = mc.copy(args = mc.args :+ f.elem.eRange)(mc.resultType, mc.isAdapterCall, mc.typeSubst)
super.invokeUnlifted(e, newMC, dataEnv)
case _ =>
super.invokeUnlifted(e, mc, dataEnv)
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala
index 876f0e9d7e..089b76cae4 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala
@@ -1,6 +1,7 @@
package sigma.compiler.ir
import debox.{cfor, Buffer => DBuffer}
+import sigma.ast.{SType, STypeVar}
import sigma.compiler.DelayInvokeException
import sigma.reflection.RMethod
import sigma.util.CollectionUtil.TraversableOps
@@ -26,7 +27,9 @@ trait MethodCalls extends Base { self: IRContext =>
* given `method`.
*/
case class MethodCall private[MethodCalls](receiver: Sym, method: RMethod, args: Seq[AnyRef], neverInvoke: Boolean)
- (val resultType: Elem[Any], val isAdapterCall: Boolean = false) extends Def[Any] {
+ (val resultType: Elem[Any],
+ val isAdapterCall: Boolean = false,
+ val typeSubst: Map[STypeVar, SType]) extends Def[Any] {
override def mirror(t: Transformer): Ref[Any] = {
val len = args.length
@@ -99,9 +102,14 @@ trait MethodCalls extends Base { self: IRContext =>
}
/** Creates new MethodCall node and returns its node ref. */
- def mkMethodCall(receiver: Sym, method: RMethod, args: Seq[AnyRef],
- neverInvoke: Boolean, isAdapterCall: Boolean, resultElem: Elem[_]): Sym = {
- reifyObject(MethodCall(receiver, method, args, neverInvoke)(asElem[Any](resultElem), isAdapterCall))
+ 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
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala
index 69a2cbe7cf..cf2ea09e33 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala
@@ -1,11 +1,14 @@
package sigma.compiler.ir
import org.ergoplatform._
+import sigma.VersionContext
+import sigma.Evaluation.{rtypeToSType, stypeToRType}
+import sigma.ast.SType.tT
import sigma.ast._
import sigma.ast.syntax.{ValueOps, _}
-import sigma.data.{ProveDHTuple, ProveDlog}
-import sigma.serialization.ConstantStore
import sigma.serialization.OpCodes._
+import sigma.serialization.ConstantStore
+import sigma.data.{ProveDHTuple, ProveDlog}
import sigma.serialization.ValueCodes.OpCode
import scala.collection.mutable.ArrayBuffer
@@ -106,6 +109,27 @@ trait TreeBuilding extends Base { 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
}
}
@@ -179,7 +203,11 @@ trait TreeBuilding extends Base { 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)
@@ -198,6 +226,37 @@ trait TreeBuilding extends Base { 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)
@@ -226,6 +285,13 @@ trait TreeBuilding extends Base { 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)) =>
@@ -296,13 +362,10 @@ trait TreeBuilding extends Base { 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)
@@ -405,13 +468,14 @@ trait TreeBuilding extends Base { IR: IRContext =>
mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement)
// Fallback MethodCall rule: should be the last in this list of cases
- case Def(MethodCall(objSym, m, argSyms, _)) =>
+ case Def(mc @ MethodCall(objSym, m, argSyms, _)) =>
val obj = recurse[SType](objSym)
val args = argSyms.collect { case argSym: Sym => recurse[SType](argSym) }
MethodsContainer.getMethod(obj.tpe, m.getName) match {
case Some(method) =>
- val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe))
- builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, Map())
+ val typeSubst = mc.typeSubst
+ val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)).withConcreteTypes(typeSubst)
+ builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, typeSubst)
case None =>
error(s"Cannot find method ${m.getName} in object $obj")
}
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala
index 4e732bbb5f..5b858d8586 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/NumericOps.scala
@@ -14,6 +14,7 @@ trait NumericOps extends Base { self: IRContext =>
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: IRContext =>
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: IRContext =>
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/sigma/compiler/ir/primitives/UnBinOps.scala b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala
index 23f8bc3800..b55596052e 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/primitives/UnBinOps.scala
@@ -45,6 +45,30 @@ trait UnBinOps extends Base { self: IRContext =>
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]
@@ -68,6 +92,19 @@ trait UnBinOps extends Base { self: IRContext =>
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/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala
index 9b4a002a14..f7c80f741f 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/CollsUnit.scala
@@ -14,6 +14,7 @@ import sigma.compiler.ir.{Base, IRContext}
implicit def eA: Elem[A];
def length: Ref[Int];
def apply(i: Ref[Int]): Ref[A];
+ def get(index: Ref[Int]): Ref[WOption[A]];
def getOrElse(index: Ref[Int], default: Ref[A]): Ref[A];
def map[B](f: Ref[scala.Function1[A, B]]): Ref[Coll[B]];
def zip[B](ys: Ref[Coll[B]]): Ref[Coll[scala.Tuple2[A, B]]];
@@ -29,6 +30,9 @@ import sigma.compiler.ir.{Base, IRContext}
def updateMany(indexes: Ref[Coll[Int]], values: Ref[Coll[A]]): Ref[Coll[A]];
def slice(from: Ref[Int], until: Ref[Int]): Ref[Coll[A]];
def append(other: Ref[Coll[A]]): Ref[Coll[A]];
+ def reverse: Ref[Coll[A]]
+ def startsWith(ys: Ref[Coll[A]]): Ref[Boolean];
+ def endsWith(ys: Ref[Coll[A]]): Ref[Boolean];
};
trait CollBuilder extends Def[CollBuilder] {
def fromItems[T](items: Ref[T]*)(implicit cT: Elem[T]): Ref[Coll[T]];
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala
index 2a6a341686..05b3e6f952 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala
@@ -13,9 +13,26 @@ import scalan._
def mod(m: Ref[BigInt]): Ref[BigInt];
def min(that: Ref[BigInt]): Ref[BigInt];
def max(that: Ref[BigInt]): Ref[BigInt];
+ def toUnsigned(): Ref[UnsignedBigInt];
+ def toUnsignedMod(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]
+ };
+ trait UnsignedBigInt extends Def[UnsignedBigInt] {
+ def add(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt];
+ def subtract(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt];
+ def multiply(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt];
+ def divide(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt];
+ def mod(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt];
+ def min(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt];
+ def max(that: Ref[UnsignedBigInt]): Ref[UnsignedBigInt];
+ def modInverse(m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]
+ def plusMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]
+ def subtractMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]
+ def multiplyMod(that: Ref[UnsignedBigInt], m: Ref[UnsignedBigInt]): Ref[UnsignedBigInt]
+ def toSigned(): Ref[BigInt]
};
trait GroupElement extends Def[GroupElement] {
def exp(k: Ref[BigInt]): Ref[GroupElement];
+ def expUnsigned(k: Ref[UnsignedBigInt]): Ref[GroupElement];
def multiply(that: Ref[GroupElement]): Ref[GroupElement];
def negate: Ref[GroupElement];
def getEncoded: Ref[Coll[Byte]]
@@ -51,6 +68,7 @@ import scalan._
def getMany(keys: Ref[Coll[Coll[Byte]]], proof: Ref[Coll[Byte]]): Ref[Coll[WOption[Coll[Byte]]]];
def insert(operations: Ref[Coll[scala.Tuple2[Coll[Byte], Coll[Byte]]]], proof: Ref[Coll[Byte]]): Ref[WOption[AvlTree]];
def update(operations: Ref[Coll[scala.Tuple2[Coll[Byte], Coll[Byte]]]], proof: Ref[Coll[Byte]]): Ref[WOption[AvlTree]];
+ def insertOrUpdate(operations: Ref[Coll[scala.Tuple2[Coll[Byte], Coll[Byte]]]], proof: Ref[Coll[Byte]]): Ref[WOption[AvlTree]];
def remove(operations: Ref[Coll[Coll[Byte]]], proof: Ref[Coll[Byte]]): Ref[WOption[AvlTree]]
};
trait PreHeader extends Def[PreHeader] {
@@ -78,6 +96,7 @@ import scalan._
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]];
@@ -91,6 +110,7 @@ import scalan._
def preHeader: Ref[PreHeader];
def minerPubKey: Ref[Coll[Byte]];
def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]];
+ def getVarFromInput[T](inputId: Ref[Short], id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]];
};
trait SigmaDslBuilder extends Def[SigmaDslBuilder] {
def Colls: Ref[CollBuilder];
@@ -114,6 +134,14 @@ import scalan._
/** This method will be used in v6.0 to handle CreateAvlTree operation in GraphBuilding */
def avlTree(operationFlags: Ref[Byte], digest: Ref[Coll[Byte]], keyLength: Ref[Int], valueLengthOpt: Ref[WOption[Int]]): Ref[AvlTree];
def xor(l: Ref[Coll[Byte]], r: Ref[Coll[Byte]]): Ref[Coll[Byte]]
+ def 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[UnsignedBigInt];
+ 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;
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala
index 0a18ea586a..4909a5584d 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/CollsImpl.scala
@@ -63,6 +63,14 @@ class CollCls extends EntityObject("Coll") {
true, false, element[A]))
}
+ override def get(index: Ref[Int]): Ref[WOption[A]] = {
+ asRep[WOption[A]](mkMethodCall(self,
+ CollClass.getMethod("get", classOf[Sym]),
+ Array[AnyRef](index),
+ true, false, element[WOption[A]]))
+ }
+
+
override def map[B](f: Ref[A => B]): Ref[Coll[B]] = {
implicit val eB = f.elem.eRange
asRep[Coll[B]](mkMethodCall(self,
@@ -164,6 +172,27 @@ 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]]))
+ }
+
+ def startsWith(ys: Ref[Coll[A]]): Ref[Boolean] = {
+ asRep[Boolean](mkMethodCall(self,
+ CollClass.getMethod("startsWith", classOf[Sym]),
+ Array[AnyRef](ys),
+ true, false, element[Boolean]))
+ }
+
+ def endsWith(ys: Ref[Coll[A]]): Ref[Boolean] = {
+ asRep[Boolean](mkMethodCall(self,
+ CollClass.getMethod("endsWith", classOf[Sym]),
+ Array[AnyRef](ys),
+ true, false, element[Boolean]))
+ }
}
case class LiftableColl[SA, A](lA: Liftable[SA, A])
@@ -210,6 +239,13 @@ class CollCls extends EntityObject("Coll") {
true, true, element[A]))
}
+ def get(index: Ref[Int]): Ref[WOption[A]] = {
+ asRep[WOption[A]](mkMethodCall(source,
+ CollClass.getMethod("get", classOf[Sym]),
+ Array[AnyRef](index),
+ true, true, element[WOption[A]]))
+ }
+
def map[B](f: Ref[A => B]): Ref[Coll[B]] = {
implicit val eB = f.elem.eRange
asRep[Coll[B]](mkMethodCall(source,
@@ -311,6 +347,27 @@ 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 startsWith(ys: Ref[Coll[A]]): Ref[Boolean] = {
+ asRep[Boolean](mkMethodCall(source,
+ CollClass.getMethod("startsWith", classOf[Sym]),
+ Array[AnyRef](ys),
+ true, true, element[Boolean]))
+ }
+
+ def endsWith(ys: Ref[Coll[A]]): Ref[Boolean] = {
+ asRep[Boolean](mkMethodCall(source,
+ CollClass.getMethod("endsWith", classOf[Sym]),
+ Array[AnyRef](ys),
+ true, true, element[Boolean]))
+ }
}
// entityUnref: single unref method for each type family
diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala
index c113cb7de3..ff5dd22391 100644
--- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala
@@ -8,6 +8,8 @@ import sigma.compiler.ir.wrappers.sigma.impl.SigmaDslDefs
import scala.collection.compat.immutable.ArraySeq
package impl {
+ import sigma.Evaluation
+ import sigma.ast.SType.tT
import sigma.compiler.ir.meta.ModuleInfo
import sigma.compiler.ir.wrappers.sigma.SigmaDsl
import sigma.compiler.ir.{Base, GraphIRReflection, IRContext}
@@ -97,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
@@ -165,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
@@ -182,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"
))
}
}
@@ -267,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._
@@ -291,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]),
@@ -338,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]),
@@ -620,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)]] = {
@@ -695,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)]] = {
@@ -928,6 +1208,13 @@ object AvlTree extends EntityObject("AvlTree") {
true, false, element[WOption[AvlTree]]))
}
+ override def insertOrUpdate(operations: Ref[Coll[(Coll[Byte], Coll[Byte])]], proof: Ref[Coll[Byte]]): Ref[WOption[AvlTree]] = {
+ asRep[WOption[AvlTree]](mkMethodCall(self,
+ AvlTreeClass.getMethod("insertOrUpdate", classOf[Sym], classOf[Sym]),
+ Array[AnyRef](operations, proof),
+ true, false, element[WOption[AvlTree]]))
+ }
+
override def remove(operations: Ref[Coll[Coll[Byte]]], proof: Ref[Coll[Byte]]): Ref[WOption[AvlTree]] = {
asRep[WOption[AvlTree]](mkMethodCall(self,
AvlTreeClass.getMethod("remove", classOf[Sym], classOf[Sym]),
@@ -1052,6 +1339,13 @@ object AvlTree extends EntityObject("AvlTree") {
true, true, element[WOption[AvlTree]]))
}
+ def insertOrUpdate(operations: Ref[Coll[(Coll[Byte], Coll[Byte])]], proof: Ref[Coll[Byte]]): Ref[WOption[AvlTree]] = {
+ asRep[WOption[AvlTree]](mkMethodCall(source,
+ AvlTreeClass.getMethod("insertOrUpdate", classOf[Sym], classOf[Sym]),
+ Array[AnyRef](operations, proof),
+ true, true, element[WOption[AvlTree]]))
+ }
+
def remove(operations: Ref[Coll[Coll[Byte]]], proof: Ref[Coll[Byte]]): Ref[WOption[AvlTree]] = {
asRep[WOption[AvlTree]](mkMethodCall(source,
AvlTreeClass.getMethod("remove", classOf[Sym], classOf[Sym]),
@@ -1075,7 +1369,7 @@ object AvlTree extends EntityObject("AvlTree") {
override protected def collectMethods: Map[RMethod, MethodDesc] = {
super.collectMethods ++
Elem.declaredMethods(RClass(classOf[AvlTree]), RClass(classOf[SAvlTree]), Set(
- "digest", "enabledOperations", "keyLength", "valueLengthOpt", "isInsertAllowed", "isUpdateAllowed", "isRemoveAllowed", "updateDigest", "updateOperations", "contains", "get", "getMany", "insert", "update", "remove"
+ "digest", "enabledOperations", "keyLength", "valueLengthOpt", "isInsertAllowed", "isUpdateAllowed", "isRemoveAllowed", "updateDigest", "updateOperations", "contains", "get", "getMany", "insert", "update", "insertOrUpdate", "remove"
))
}
}
@@ -1368,6 +1662,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
@@ -1492,6 +1794,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
@@ -1509,7 +1818,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"
))
}
}
@@ -1608,10 +1917,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)))
}
}
@@ -1705,10 +2023,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)))
}
}
@@ -1727,7 +2054,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"
))
}
}
@@ -1945,6 +2272,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[UnsignedBigInt] = {
+ asRep[UnsignedBigInt](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[UnsignedBigInt](UnsignedBigInt.unsignedBigIntElement)))
+ }
+
+ 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
@@ -2104,6 +2487,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[UnsignedBigInt] = {
+ asRep[UnsignedBigInt](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[UnsignedBigInt](UnsignedBigInt.unsignedBigIntElement)))
+ }
+
+ 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
@@ -2121,7 +2561,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"
))
}
}
@@ -2300,6 +2742,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 {
diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala
index af5be938be..d4943ef892 100644
--- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaBinder.scala
@@ -105,6 +105,9 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder,
case a @ Apply(PKFunc.symNoType, args) =>
Some(PKFunc.irInfo.irBuilder(PKFunc.sym, args).withPropagatedSrcCtx(a.sourceContext))
+ case a @ Apply(predefFuncRegistry.SerializeFunc.symNoType, args) =>
+ Some(predefFuncRegistry.SerializeFunc.irInfo.irBuilder(PKFunc.sym, args).withPropagatedSrcCtx(a.sourceContext))
+
case sel @ Select(obj, "isEmpty", _) =>
Some(mkLogicalNot(mkSelect(obj, "isDefined").asBoolValue).withPropagatedSrcCtx(sel.sourceContext))
diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala
index f6477ea3b2..7d31b91c1e 100644
--- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala
+++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala
@@ -37,13 +37,14 @@ class SigmaTyper(val builder: SigmaBuilder,
private def processGlobalMethod(srcCtx: Nullable[SourceContext],
method: SMethod,
- args: IndexedSeq[SValue]): 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
@@ -134,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
@@ -154,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.
@@ -220,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)
@@ -408,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)
@@ -511,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/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala
index 6a7ef5a512..4b9b6e44fd 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.isV3OrLaterErgoTreeVersion) {
+ 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/validation/SigmaValidationSettingsSerializerSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala
index ee1c1a339a..a46e74ae35 100644
--- a/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala
+++ b/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala
@@ -20,8 +20,10 @@ class SigmaValidationSettingsSerializerSpec extends SerializationSpecification w
property("SigmaValidationSettings round trip") {
forAll(ruleIdGen, statusGen, MinSuccessful(100)) { (ruleId, status) =>
- val vs = currentSettings.updated(ruleId, status)
- roundtrip(vs)
+ whenever(currentSettings.getStatus(ruleId).isDefined) {
+ val vs = currentSettings.updated(ruleId, status)
+ roundtrip(vs)
+ }
}
}
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 85%
rename from sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala
rename to sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala
index a5a382d2a8..a8f3d0548d 100644
--- a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala
+++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala
@@ -5,31 +5,31 @@ 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.crypto.SecP256K1Group
+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,137 +38,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
- // isDebug = true uncomment to enable debug mode
- )
-
- /** Set this to true to enable debug console output in tests */
- override val printDebugInfo: Boolean = false
-
- 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(
@@ -236,17 +117,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 }",
@@ -266,7 +136,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),
@@ -295,14 +167,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)
}
@@ -331,7 +203,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),
@@ -368,10 +242,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)
}
@@ -401,10 +275,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)
}
@@ -449,7 +323,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),
@@ -465,7 +339,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) }",
@@ -479,8 +353,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)) }",
@@ -497,8 +371,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))) }",
@@ -518,8 +392,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)))) }",
@@ -553,7 +427,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)) }",
@@ -582,7 +456,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,
@@ -608,7 +482,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)),
@@ -640,7 +514,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(
@@ -690,7 +564,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)),
@@ -733,7 +607,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)),
@@ -750,7 +625,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)),
@@ -767,7 +643,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)),
@@ -784,7 +661,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)),
@@ -801,7 +679,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)))),
@@ -819,7 +698,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"))),
@@ -918,6 +798,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)]
)(
@@ -936,12 +822,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,
@@ -966,7 +859,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),
@@ -1007,16 +902,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),
@@ -1058,41 +955,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"))),
@@ -1111,7 +985,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)),
@@ -1128,7 +1003,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)),
@@ -1145,7 +1021,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)),
@@ -1162,7 +1039,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)))),
@@ -1180,7 +1058,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"))),
@@ -1272,7 +1151,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),
@@ -1312,15 +1193,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),
@@ -1362,40 +1245,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"))),
@@ -1414,7 +1275,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"))),
@@ -1433,7 +1295,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)),
@@ -1448,7 +1311,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)),
@@ -1463,7 +1327,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)))),
@@ -1480,57 +1345,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(
@@ -1573,7 +1440,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),
@@ -1614,16 +1483,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),
@@ -1665,32 +1534,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
@@ -1701,7 +1548,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"))),
@@ -1722,7 +1569,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"))),
@@ -1743,7 +1590,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"))),
@@ -1764,7 +1612,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)),
@@ -1781,7 +1629,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)))),
@@ -1801,83 +1649,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))
+ )
)
)
)
@@ -1886,13 +1736,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),
@@ -1933,16 +1782,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),
@@ -1984,36 +1833,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))
@@ -2034,11 +1861,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"))),
@@ -2160,8 +1987,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),
@@ -2197,30 +2023,30 @@ class SigmaDslSpecification extends SigmaDslTesting
(BigIntMaxValue, BigIntMinValue) -> expect(false),
(BigIntMaxValue, -47.toBigInt) -> expect(false),
(BigIntMaxValue, BigIntMaxValue) -> expect(false),
- (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v6.0: reject this overlimit cases (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554)
+ (BigIntMaxValue, BigIntOverlimit) -> expect(true),
(BigIntOverlimit, BigIntOverlimit) -> expect(false)
)
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)(_ != _)
}
property("BigInt LE, GE") {
val o = NumericOps.BigIntIsExactOrdering
- // TODO v6.0: this values have bitCount == 255 (see to256BitValueExact) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554)
val BigIntMinValue = CBigInt(new BigInteger("-7F" + "ff" * 31, 16))
val BigIntMaxValue = CBigInt(new BigInteger("7F" + "ff" * 31, 16))
- val BigIntOverlimit = CBigInt(new BigInteger("7F" + "ff" * 33, 16))
+ val BigIntOverlimit = VersionContext.withVersions(2,2) {
+ 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),
@@ -2257,81 +2083,28 @@ class SigmaDslSpecification extends SigmaDslTesting
(BigIntMaxValue, BigIntMinValue) -> expect(false),
(BigIntMaxValue, -47.toBigInt) -> expect(false),
(BigIntMaxValue, BigIntMaxValue) -> expect(true),
- (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v6.0: reject this overlimit cases (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554)
+ (BigIntMaxValue, BigIntOverlimit) -> expect(true),
(BigIntOverlimit, BigIntOverlimit) -> expect(true)
)
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
@@ -2344,11 +2117,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") {
@@ -2356,14 +2129,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") {
@@ -2372,30 +2145,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") {
@@ -2479,15 +2251,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") {
@@ -2499,63 +2271,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
@@ -2564,38 +2344,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(_))
}
@@ -2619,10 +2401,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)]]]
@@ -2665,8 +2447,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(
@@ -2680,14 +2462,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))),
@@ -2718,7 +2501,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)),
@@ -2748,7 +2531,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"))),
@@ -2766,8 +2549,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"))),
@@ -2790,7 +2574,7 @@ class SigmaDslSpecification extends SigmaDslTesting
FixedCostItem(Exponentiate)
)
)
- verifyCases(cases(1873, costDetails),
+ verifyCases(cases(1873, costDetails, Seq.fill(4)(2121)),
existingFeature(
scalaFunc,
script,
@@ -2817,7 +2601,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,
@@ -2864,7 +2648,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"))),
@@ -2927,7 +2711,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"))),
@@ -2940,7 +2724,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)),
@@ -2953,7 +2737,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)),
@@ -2966,11 +2750,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,
@@ -2979,7 +2763,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)),
@@ -2992,7 +2776,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)),
@@ -3005,7 +2789,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)),
@@ -3260,7 +3044,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))
@@ -3338,8 +3122,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)
@@ -3347,14 +3132,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
@@ -3363,7 +3149,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
@@ -3373,7 +3159,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)))
}
{
@@ -3390,11 +3176,6 @@ class SigmaDslSpecification extends SigmaDslTesting
type BatchProver = BatchAVLProver[Digest32, Blake2b256.type]
- def performInsert(avlProver: BatchProver, key: Coll[Byte], value: Coll[Byte]) = {
- avlProver.performOneOperation(Insert(ADKey @@ key.toArray, ADValue @@ value.toArray))
- val proof = avlProver.generateProof().toColl
- proof
- }
def performUpdate(avlProver: BatchProver, key: Coll[Byte], value: Coll[Byte]) = {
avlProver.performOneOperation(Update(ADKey @@ key.toArray, ADValue @@ value.toArray))
@@ -3418,9 +3199,9 @@ class SigmaDslSpecification extends SigmaDslTesting
type KV = (Coll[Byte], Coll[Byte])
- property("AvlTree.insert equivalence") {
- val insert = existingFeature((t: (AvlTree, (Coll[KV], Coll[Byte]))) => t._1.insert(t._2._1, t._2._2),
- "{ (t: (AvlTree, (Coll[(Coll[Byte], Coll[Byte])], Coll[Byte]))) => t._1.insert(t._2._1, t._2._2) }",
+ property("AvlTree.update equivalence") {
+ val update = existingFeature((t: (AvlTree, (Coll[KV], Coll[Byte]))) => t._1.update(t._2._1, t._2._2),
+ "{ (t: (AvlTree, (Coll[(Coll[Byte], Coll[Byte])], Coll[Byte]))) => t._1.update(t._2._1, t._2._2) }",
FuncValue(
Vector(
(
@@ -3465,7 +3246,7 @@ class SigmaDslSpecification extends SigmaDslTesting
),
1.toByte
),
- SAvlTreeMethods.getMethodByName("insert"),
+ SAvlTreeMethods.getMethodByName("update"),
Vector(
SelectField.typed[Value[SCollection[STuple]]](
ValUse(
@@ -3504,160 +3285,22 @@ class SigmaDslSpecification extends SigmaDslTesting
FixedCostItem(SelectField),
FixedCostItem(ValUse),
FixedCostItem(SelectField),
- FixedCostItem(SAvlTreeMethods.isInsertAllowedMethod, FixedCost(JitCost(15)))
+ FixedCostItem(SAvlTreeMethods.isUpdateAllowedMethod, FixedCost(JitCost(15)))
)
val costDetails1 = TracedCost(testTraceBase)
val costDetails2 = TracedCost(
testTraceBase ++ Array(
- ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 70),
- ast.SeqCostItem(NamedDesc("InsertIntoAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 1),
+ ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 111),
+ ast.SeqCostItem(NamedDesc("UpdateAvlTree"), PerItemCost(JitCost(120), JitCost(20), 1), 1),
FixedCostItem(SAvlTreeMethods.updateDigestMethod, FixedCost(JitCost(40)))
)
)
-
- forAll(keyCollGen, bytesCollGen) { (key, value) =>
- val (tree, avlProver) = createAvlTreeAndProver()
- val preInsertDigest = avlProver.digest.toColl
- val insertProof = performInsert(avlProver, key, value)
- val kvs = Colls.fromItems((key -> value))
-
- { // positive
- val preInsertTree = createTree(preInsertDigest, insertAllowed = true)
- val input = (preInsertTree, (kvs, insertProof))
- val (res, _) = insert.checkEquality(input).getOrThrow
- res.isDefined shouldBe true
- insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796))
- }
-
- { // negative: readonly tree
- val readonlyTree = createTree(preInsertDigest)
- val input = (readonlyTree, (kvs, insertProof))
- val (res, _) = insert.checkEquality(input).getOrThrow
- res.isDefined shouldBe false
- insert.checkExpected(input, Expected(Success(res), 1772, costDetails1, 1772))
- }
-
- { // negative: invalid key
- val tree = createTree(preInsertDigest, insertAllowed = true)
- val invalidKey = key.map(x => (-x).toByte) // any other different from key
- val invalidKvs = Colls.fromItems((invalidKey -> value)) // NOTE, insertProof is based on `key`
- 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))
- }
-
- { // negative: invalid proof
- val tree = createTree(preInsertDigest, insertAllowed = true)
- val invalidProof = insertProof.map(x => (-x).toByte) // any other different from proof
- val res = insert.checkEquality((tree, (kvs, invalidProof)))
- res.isFailure shouldBe true
- }
- }
- }
-
- property("AvlTree.update equivalence") {
- val update = existingFeature((t: (AvlTree, (Coll[KV], Coll[Byte]))) => t._1.update(t._2._1, t._2._2),
- "{ (t: (AvlTree, (Coll[(Coll[Byte], Coll[Byte])], Coll[Byte]))) => t._1.update(t._2._1, t._2._2) }",
- FuncValue(
- Vector(
- (
- 1,
- STuple(
- Vector(
- SAvlTree,
- STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
- )
- )
- )
- ),
- BlockValue(
- Vector(
- ValDef(
- 3,
- List(),
- SelectField.typed[Value[STuple]](
- ValUse(
- 1,
- STuple(
- Vector(
- SAvlTree,
- STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
- )
- )
- ),
- 2.toByte
- )
- )
- ),
- MethodCall.typed[Value[SOption[SAvlTree.type]]](
- SelectField.typed[Value[SAvlTree.type]](
- ValUse(
- 1,
- STuple(
- Vector(
- SAvlTree,
- STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
- )
- )
- ),
- 1.toByte
- ),
- SAvlTreeMethods.getMethodByName("update"),
- Vector(
- SelectField.typed[Value[SCollection[STuple]]](
- ValUse(
- 3,
- STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
- ),
- 1.toByte
- ),
- SelectField.typed[Value[SCollection[SByte.type]]](
- ValUse(
- 3,
- STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
- ),
- 2.toByte
- )
- ),
- Map()
- )
- )
- ))
-
- val testTraceBase = Array(
- FixedCostItem(Apply),
- FixedCostItem(FuncValue),
- FixedCostItem(GetVar),
- FixedCostItem(OptionGet),
- FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))),
- ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1),
- FixedCostItem(ValUse),
- FixedCostItem(SelectField),
- FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))),
- FixedCostItem(ValUse),
- FixedCostItem(SelectField),
- FixedCostItem(MethodCall),
- FixedCostItem(ValUse),
- FixedCostItem(SelectField),
- FixedCostItem(ValUse),
- FixedCostItem(SelectField),
- FixedCostItem(SAvlTreeMethods.isUpdateAllowedMethod, FixedCost(JitCost(15)))
- )
- val costDetails1 = TracedCost(testTraceBase)
- val costDetails2 = TracedCost(
- testTraceBase ++ Array(
- ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 111),
- ast.SeqCostItem(NamedDesc("UpdateAvlTree"), PerItemCost(JitCost(120), JitCost(20), 1), 1),
- FixedCostItem(SAvlTreeMethods.updateDigestMethod, FixedCost(JitCost(40)))
- )
- )
- val costDetails3 = TracedCost(
- testTraceBase ++ Array(
- ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 111),
- ast.SeqCostItem(NamedDesc("UpdateAvlTree"), PerItemCost(JitCost(120), JitCost(20), 1), 1)
- )
- )
+ val costDetails3 = TracedCost(
+ testTraceBase ++ Array(
+ ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 111),
+ ast.SeqCostItem(NamedDesc("UpdateAvlTree"), PerItemCost(JitCost(120), JitCost(20), 1), 1)
+ )
+ )
val key = Colls.fromArray(Array[Byte](1,0,1,1,73,-67,-128,1,1,0,93,0,127,87,95,51,1,127,1,-3,74,-66,-128,1,89,-18,1,-1,-62,0,-33,51))
val value = Colls.fromArray(Array[Byte](1,-50,1,-128,120,1))
@@ -3674,7 +3317,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)
@@ -3682,13 +3325,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
@@ -3696,7 +3339,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)
@@ -3705,15 +3348,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)))
}
}
@@ -3824,7 +3467,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)))
}
{
@@ -3841,13 +3484,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
@@ -3855,14 +3498,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)))
}
}
}
@@ -3871,7 +3514,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"))),
@@ -3891,20 +3534,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")))
@@ -3919,7 +3562,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"))),
@@ -3941,7 +3584,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")))
@@ -3954,7 +3597,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))
@@ -3967,7 +3610,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"
@@ -3982,7 +3625,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"
@@ -3999,7 +3642,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"
@@ -4016,7 +3659,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,
@@ -4032,15 +3675,13 @@ class SigmaDslSpecification extends SigmaDslTesting
"{ (x: Box) => x.creationInfo }",
FuncValue(Vector((1, SBox)), ExtractCreationInfo(ValUse(1, SBox)))))
- // TODO v6.0: fix collections equality and remove map(identity)(see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/909)
- // (PairOfColl should be equal CollOverArray)
verifyCases(
Seq(
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 }",
@@ -4055,16 +3696,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))
@@ -4119,11 +3750,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) =>
@@ -4248,10 +3879,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) =>
@@ -4345,7 +3976,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,
@@ -4357,7 +3988,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,
@@ -4369,7 +4000,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 }",
@@ -4380,7 +4011,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 }",
@@ -4391,7 +4022,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 }",
@@ -4410,10 +4041,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(
@@ -4485,117 +4116,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("4e18a26849e98a35a3b7dd25fa9a00c9f33fc8655568c265ffe42165b6d8f3c5")),
+ 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(1.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))),
+ CBigInt(SecP256K1Group.order.divide(new BigInteger("2")))),
+ 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 }))
}
@@ -4658,18 +4289,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,
@@ -4679,7 +4302,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(
@@ -4687,7 +4311,7 @@ class SigmaDslSpecification extends SigmaDslTesting
headers = Coll[Header](header),
preHeader = CPreHeader(
0.toByte,
- Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"),
+ header.id,
-755484979487531112L,
9223372036854775807L,
11,
@@ -4766,6 +4390,7 @@ class SigmaDslSpecification extends SigmaDslTesting
.append(Coll[AnyValue](
CAnyValue(Helpers.decodeBytes("00")),
CAnyValue(true))),
+ spendingTransaction = null,
activatedScriptVersion = activatedVersionInTests,
currentErgoTreeVersion = ergoTreeVersionInTests
)
@@ -4809,7 +4434,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) },
@@ -4834,7 +4459,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 }",
@@ -4895,7 +4520,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 }",
@@ -4932,7 +4557,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 } }",
@@ -4992,7 +4617,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) =>
@@ -5084,11 +4709,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(
@@ -5099,7 +4733,7 @@ class SigmaDslSpecification extends SigmaDslTesting
Vector(),
Map()
)
- )),
+ ), activationType = ActivationByScriptVersion),
preGeneratedSamples = Some(samples))
// test vectors to reproduce v4.x bug (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/603)
@@ -5114,7 +4748,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))
@@ -5127,7 +4761,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 }",
@@ -5148,18 +4782,10 @@ 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))
-// TODO v6.0: implement support of Option[T] in DataSerializer (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659)
-// this will allow passing optional values in registers and also in constants
-// testCases2(
-// Seq(
-// ctx -> Expected(Success(Some(true), cost = 0)),
-// ctx2 -> Expected(Success(None, cost = 0)),
-// ctx3 -> Expected(Success(None, cost = 0))
-// ),
testCases(
Seq(
ctx -> Success(Some(true)),
@@ -5188,7 +4814,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))))),
@@ -5222,7 +4848,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) =>
@@ -5284,11 +4910,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
@@ -5351,6 +4985,7 @@ class SigmaDslSpecification extends SigmaDslTesting
)
)
),
+ activationType = ActivationByScriptVersion,
allowNewToSucceed = true
),
preGeneratedSamples = Some(ArraySeq.empty))
@@ -5450,12 +5085,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) =>
@@ -5658,22 +5293,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) =>
@@ -5891,15 +5526,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) =>
@@ -6124,15 +5759,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) =>
@@ -6237,7 +5872,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))),
@@ -6262,18 +5905,21 @@ 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) }",
- FuncValue(Vector((1, SBooleanArray)), XorOf(ValUse(1, SBooleanArray)))))
+ FuncValue(Vector((1, SBooleanArray)), XorOf(ValUse(1, SBooleanArray))),
+ activationType = ActivationByScriptVersion
+ ))
}
property("LogicalNot equivalence") {
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)))))
@@ -6283,7 +5929,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)),
@@ -6302,7 +5949,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)),
@@ -6320,7 +5967,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)),
@@ -6337,7 +5985,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)),
@@ -6354,7 +6003,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)))),
@@ -6392,7 +6042,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"))))
@@ -6411,7 +6061,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"))))
@@ -6436,37 +6086,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)
+ )
+ )))
}
}
@@ -6502,7 +6153,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))),
@@ -6513,9 +6164,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"),
@@ -6524,10 +6182,11 @@ 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) }",
- if (lowerMethodCallsInTests)
+ {if (lowerMethodCallsInTests)
FuncValue(
Vector((1, STuple(Vector(SByteArray, SByteArray)))),
Xor(
@@ -6559,7 +6218,8 @@ class SigmaDslSpecification extends SigmaDslTesting
),
Map()
)
- )
+ )},
+ activationType = ActivationByScriptVersion
))
}
@@ -6657,11 +6317,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 }) },
@@ -6714,13 +6374,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 }) },
@@ -6753,11 +6413,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) },
@@ -6783,7 +6444,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)),
@@ -6808,7 +6469,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)),
@@ -6864,9 +6525,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) {
@@ -6943,9 +6604,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 }) },
@@ -7036,11 +6697,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(
@@ -7151,11 +6812,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(
@@ -7276,28 +6937,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(
@@ -7335,11 +7002,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(
@@ -7405,7 +7078,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))),
@@ -7494,7 +7167,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))),
@@ -7571,7 +7244,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"))),
@@ -7633,36 +7306,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(
@@ -7748,15 +7391,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(
@@ -8009,15 +7652,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(
@@ -8128,11 +7771,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)),
@@ -8194,7 +7842,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"))),
@@ -8255,10 +7904,11 @@ class SigmaDslSpecification extends SigmaDslTesting
)
)
)
+ if(!VersionContext.current.isV3OrLaterErgoTreeVersion) {
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)),
@@ -8325,7 +7975,7 @@ class SigmaDslSpecification extends SigmaDslTesting
)
)
)
- ))
+ ))}
}
property("Tuple size method equivalence") {
@@ -8341,7 +7991,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))
@@ -8356,7 +8006,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(
@@ -8365,7 +8015,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(
@@ -8407,9 +8057,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")))
)
},
@@ -8503,10 +8153,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")))
)
@@ -8554,24 +8204,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") {
@@ -8629,29 +8279,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") {
@@ -8678,48 +8329,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))")
@@ -8769,37 +8421,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(
@@ -8876,33 +8529,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.isV3OrLaterErgoTreeVersion) {
+ 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))),
@@ -8917,8 +8572,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 }) }",
@@ -8980,10 +8635,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 }) }",
@@ -9044,10 +8699,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 ) },
@@ -9074,17 +8729,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(
@@ -9111,20 +8755,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)
@@ -9155,6 +8809,7 @@ class SigmaDslSpecification extends SigmaDslTesting
LongConstant(5L)
)
),
+ activationType = ActivationByScriptVersion,
allowNewToSucceed = true),
preGeneratedSamples = Some(Nil))
}
@@ -9189,19 +8844,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),
@@ -9214,19 +8872,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),
@@ -9236,10 +8897,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)))))
@@ -9266,7 +8928,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(
@@ -9294,7 +8956,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"))
),
@@ -9326,31 +8988,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(
@@ -9370,9 +9033,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(
@@ -9401,32 +9064,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(
@@ -9450,12 +9114,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(
@@ -9493,25 +9158,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,
@@ -9520,39 +9185,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) }",
@@ -9561,19 +9209,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) }",
@@ -9586,10 +9235,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) }",
@@ -9609,18 +9259,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) }",
@@ -9661,7 +9312,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()),
@@ -9673,8 +9324,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(
@@ -9683,21 +9342,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))
},
@@ -9715,7 +9383,7 @@ class SigmaDslSpecification extends SigmaDslTesting
),
ConcreteCollection(Array(BoolToSigmaProp(FalseLeaf)), SSigmaProp)
)
- )))
+ ), activationType = ActivationByScriptVersion))
}
// Original issue: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/604
@@ -9749,6 +9417,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(
@@ -9756,107 +9430,111 @@ 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
+ ),
+ activationType = ActivationByScriptVersion,
+ allowDifferentErrors = true,
+ allowNewToSucceed = true
),
preGeneratedSamples = Some(ArraySeq.empty)
)
@@ -9867,8 +9545,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])) =>
@@ -9994,30 +9672,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..2b20ce79ba
--- /dev/null
+++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala
@@ -0,0 +1,3081 @@
+package sigma
+
+import org.ergoplatform.{ErgoBox, ErgoHeader, ErgoLikeTransaction, Input}
+import scorex.util.encode.Base16
+import sigma.VersionContext.{JitActivationVersion, V6SoftForkVersion}
+import org.ergoplatform.ErgoBox.Token
+import org.ergoplatform.settings.ErgoAlgos
+import scorex.crypto.authds.{ADKey, ADValue}
+import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert, InsertOrUpdate}
+import scorex.crypto.hash.{Blake2b256, Digest32}
+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.{CGroupElement, CUnsignedBigInt}
+import sigma.crypto.SecP256K1Group
+import sigma.eval.{CostDetails, SigmaDsl, TracedCost}
+import sigma.serialization.ValueCodes.OpCode
+import sigma.util.Extensions.{BooleanOps, IntOps}
+import sigmastate.eval.{CContext, CPreHeader}
+import sigma.pow.Autolykos2PowValidation
+import sigmastate.exceptions.MethodNotFound
+import sigmastate.utils.Extensions.ByteOpsForSigma
+import sigmastate.utils.Helpers
+import sigma.Extensions.ArrayOps
+import sigma.crypto.CryptoConstants
+import sigma.data.CSigmaDslBuilder.Colls
+import sigma.exceptions.InterpreterException
+import sigma.interpreter.{ContextExtension, ProverResult}
+
+import java.lang.reflect.InvocationTargetException
+import java.math.BigInteger
+import scala.util.{Failure, Success, Try}
+
+/** 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 (ergoTreeVersionInTests < 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))
+ }
+ }
+ }
+
+ 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")),
+ costOpt = None,
+ expectedDetails = CostDetails.ZeroCost,
+ newCostOpt = None,
+ 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),
+ costOpt = None,
+ expectedDetails = CostDetails.ZeroCost,
+ newCostOpt = None,
+ newVersionedResults = Seq(
+ 0 -> (ExpectedResult(Success(expectedTreeBytes_beforeV6), Some(2015)) -> Some(costDetails(1))),
+ 1 -> (ExpectedResult(Success(expectedTreeBytes_beforeV6), Some(2015)) -> Some(costDetails(1))),
+ 2 -> (ExpectedResult(Success(expectedTreeBytes_beforeV6), Some(2015)) -> Some(costDetails(1))),
+ 3 -> (ExpectedResult(Success(expectedTreeBytes_V6), Some(2065)) -> Some(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)
+ )
+ ),
+ activationType = ActivationByTreeVersion
+ )
+ )
+
+ // before v6.0 the expected tree is not parsable
+ VersionContext.withVersions(JitActivationVersion, 0) {
+ Try(ErgoTree.fromBytes(expectedTreeBytes_beforeV6.toArray)).isSuccess shouldBe false
+ }
+
+ VersionContext.withVersions(V6SoftForkVersion, 0) {
+ // 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.UnsignedBigInt] = 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
+ CUnsignedBigInt(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 = CUnsignedBigInt(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("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 = {
+ Seq(
+ 0 -> (ExpectedResult(Failure(new java.lang.ArithmeticException("/ by zero")), Some(2029)) -> Some(trace)),
+ 1 -> (ExpectedResult(Failure(new java.lang.ArithmeticException("/ by zero")), Some(2029)) -> Some(trace)),
+ 2 -> (ExpectedResult(Failure(new java.lang.ArithmeticException("/ by zero")), Some(2029)) -> Some(trace)),
+ 3 -> (ExpectedResult(Success(2L), Some(2015)) -> Some(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.isV3OrLaterErgoTreeVersion) {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.isV3OrLaterErgoTreeVersion) {
+ 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 = {
+ Seq(
+ 0 -> (ExpectedResult(Failure(new java.lang.ArithmeticException("/ by zero")), Some(2029)) -> Some(trace)),
+ 1 -> (ExpectedResult(Failure(new java.lang.ArithmeticException("/ by zero")), Some(2029)) -> Some(trace)),
+ 2 -> (ExpectedResult(Failure(new java.lang.ArithmeticException("/ by zero")), Some(2029)) -> Some(trace)),
+ 3 -> (ExpectedResult(Success(1), Some(2029)) -> Some(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.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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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]() }",
+ FuncValue(
+ Array((1, SByte)),
+ MethodCall.typed[Value[SOption[SByte.type]]](
+ Global,
+ SGlobalMethods.noneMethod.withConcreteTypes(Map(STypeVar("T") -> SByte)),
+ IndexedSeq(),
+ Map(STypeVar("T") -> SByte)
+ )
+ ),
+ sinceVersion = V6SoftForkVersion)
+ val cases = Seq(
+ (0.toByte, Success(None)),
+ (1.toByte, Success(None))
+ )
+
+ testCases(cases, some)
+ }
+
+ type BatchProver = BatchAVLProver[Digest32, Blake2b256.type]
+
+ type KV = (Coll[Byte], Coll[Byte])
+
+ def performInsertOrUpdate(avlProver: BatchProver, keys: Seq[Coll[Byte]], values: Seq[Coll[Byte]]) = {
+ keys.zip(values).foreach{case (key, value) =>
+ avlProver.performOneOperation(InsertOrUpdate(ADKey @@ key.toArray, ADValue @@ value.toArray))
+ }
+ val proof = avlProver.generateProof().toColl
+ proof
+ }
+ def performInsert(avlProver: BatchProver, key: Coll[Byte], value: Coll[Byte]) = {
+ avlProver.performOneOperation(Insert(ADKey @@ key.toArray, ADValue @@ value.toArray))
+ val proof = avlProver.generateProof().toColl
+ proof
+ }
+
+ def createTree(digest: Coll[Byte], insertAllowed: Boolean = false, updateAllowed: Boolean = false, removeAllowed: Boolean = false) = {
+ val flags = AvlTreeFlags(insertAllowed, updateAllowed, removeAllowed).serializeToByte
+ val tree = SigmaDsl.avlTree(flags, digest, 32, None)
+ tree
+ }
+
+ property("AvlTree.insert equivalence") {
+ import sigmastate.eval.Extensions.AvlTreeOps
+ import sigmastate.utils.Helpers._
+
+ val insert = existingFeature(
+ (t: (AvlTree, (Coll[KV], Coll[Byte]))) => t._1.insert(t._2._1, t._2._2),
+ "{ (t: (AvlTree, (Coll[(Coll[Byte], Coll[Byte])], Coll[Byte]))) => t._1.insert(t._2._1, t._2._2) }",
+ FuncValue(
+ Vector(
+ (
+ 1,
+ STuple(
+ Vector(
+ SAvlTree,
+ STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
+ )
+ )
+ )
+ ),
+ BlockValue(
+ Vector(
+ ValDef(
+ 3,
+ List(),
+ SelectField.typed[Value[STuple]](
+ ValUse(
+ 1,
+ STuple(
+ Vector(
+ SAvlTree,
+ STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
+ )
+ )
+ ),
+ 2.toByte
+ )
+ )
+ ),
+ MethodCall.typed[Value[SOption[SAvlTree.type]]](
+ SelectField.typed[Value[SAvlTree.type]](
+ ValUse(
+ 1,
+ STuple(
+ Vector(
+ SAvlTree,
+ STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
+ )
+ )
+ ),
+ 1.toByte
+ ),
+ SAvlTreeMethods.getMethodByName("insert"),
+ Vector(
+ SelectField.typed[Value[SCollection[STuple]]](
+ ValUse(
+ 3,
+ STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
+ ),
+ 1.toByte
+ ),
+ SelectField.typed[Value[SCollection[SByte.type]]](
+ ValUse(
+ 3,
+ STuple(Vector(SCollectionType(STuple(Vector(SByteArray, SByteArray))), SByteArray))
+ ),
+ 2.toByte
+ )
+ ),
+ Map()
+ )
+ )
+ ))
+
+ val testTraceBase = Array(
+ FixedCostItem(Apply),
+ FixedCostItem(FuncValue),
+ FixedCostItem(GetVar),
+ FixedCostItem(OptionGet),
+ FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))),
+ ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1),
+ FixedCostItem(ValUse),
+ FixedCostItem(SelectField),
+ FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))),
+ FixedCostItem(ValUse),
+ FixedCostItem(SelectField),
+ FixedCostItem(MethodCall),
+ FixedCostItem(ValUse),
+ FixedCostItem(SelectField),
+ FixedCostItem(ValUse),
+ FixedCostItem(SelectField),
+ FixedCostItem(SAvlTreeMethods.isInsertAllowedMethod, FixedCost(JitCost(15)))
+ )
+ val costDetails1 = TracedCost(testTraceBase)
+ val costDetails2 = TracedCost(
+ testTraceBase ++ Array(
+ ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 70),
+ ast.SeqCostItem(NamedDesc("InsertIntoAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 1),
+ FixedCostItem(SAvlTreeMethods.updateDigestMethod, FixedCost(JitCost(40)))
+ )
+ )
+
+ forAll(keyCollGen, bytesCollGen) { (key, value) =>
+ val (tree, avlProver) = createAvlTreeAndProver()
+ val preInsertDigest = avlProver.digest.toColl
+ val insertProof = performInsert(avlProver, key, value)
+ val kvs = Colls.fromItems((key -> value))
+
+ { // positive
+ val preInsertTree = createTree(preInsertDigest, insertAllowed = true)
+ val input = (preInsertTree, (kvs, insertProof))
+ val (res, _) = insert.checkEquality(input).getOrThrow
+ res.isDefined shouldBe true
+ insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796, Seq.fill(4)(2102)))
+ }
+
+ { // negative: readonly tree
+ val readonlyTree = createTree(preInsertDigest)
+ val input = (readonlyTree, (kvs, insertProof))
+ val (res, _) = insert.checkEquality(input).getOrThrow
+ res.isDefined shouldBe false
+ insert.checkExpected(input, Expected(Success(res), 1772, costDetails1, 1772, Seq.fill(4)(2078)))
+ }
+
+ { // positive: invalid key, but proof is enough to validate insert
+ val tree = createTree(preInsertDigest, insertAllowed = true)
+ val negKey = key.map(x => (-x).toByte)
+ val kvs = Colls.fromItems((negKey -> value))
+ val input = (tree, (kvs, insertProof))
+ val (res, _) = insert.checkEquality(input).getOrThrow
+ res.isDefined shouldBe true
+ insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796, Seq.fill(4)(2102)))
+ }
+
+ { // nagative: duplicate keys
+ val tree = createTree(preInsertDigest, insertAllowed = true)
+ val invalidKvs = Colls.fromItems((key -> value), (key -> value))
+ val input = (tree, (invalidKvs, insertProof))
+ if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
+ insert.verifyCase(input, new Expected(ExpectedResult(Success(None), Some(2103))))
+ } else {
+ val res = insert.checkEquality(input)
+ res.isFailure shouldBe true
+ }
+ }
+
+
+ { // negative: invalid proof
+ val tree = createTree(preInsertDigest, insertAllowed = true)
+ val invalidProof = insertProof.map(x => (-x).toByte) // any other different from proof
+ val input = (tree, (kvs, invalidProof))
+ if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
+ insert.verifyCase(input, new Expected(ExpectedResult(Success(None), Some(2103))))
+ } else {
+ val res = insert.checkEquality(input)
+ res.isFailure shouldBe true
+ }
+ }
+ }
+ }
+
+ property("AvlTree.insertOrUpdate") {
+ import sigmastate.eval.Extensions.AvlTreeOps
+
+ lazy val iou = newFeature(
+ (t: (AvlTree, (Coll[KV], Coll[Byte]))) => t._1.insertOrUpdate(t._2._1, t._2._2),
+ "{ (t: (AvlTree, (Coll[(Coll[Byte], Coll[Byte])], Coll[Byte]))) => t._1.insertOrUpdate(t._2._1, t._2._2) }",
+ FuncValue(
+ Array((1, SPair(SAvlTree, SPair(SCollectionType(SPair(SByteArray, SByteArray)), SByteArray)))),
+ BlockValue(
+ Array(
+ ValDef(
+ 3,
+ List(),
+ SelectField.typed[Value[STuple]](
+ ValUse(
+ 1,
+ SPair(SAvlTree, SPair(SCollectionType(SPair(SByteArray, SByteArray)), SByteArray))
+ ),
+ 2.toByte
+ )
+ )
+ ),
+ MethodCall.typed[Value[SOption[SAvlTree.type]]](
+ SelectField.typed[Value[SAvlTree.type]](
+ ValUse(
+ 1,
+ SPair(SAvlTree, SPair(SCollectionType(SPair(SByteArray, SByteArray)), SByteArray))
+ ),
+ 1.toByte
+ ),
+ SAvlTreeMethods.insertOrUpdateMethod,
+ Array(
+ SelectField.typed[Value[SCollection[STuple]]](
+ ValUse(3, SPair(SCollectionType(SPair(SByteArray, SByteArray)), SByteArray)),
+ 1.toByte
+ ),
+ SelectField.typed[Value[SCollection[SByte.type]]](
+ ValUse(3, SPair(SCollectionType(SPair(SByteArray, SByteArray)), SByteArray)),
+ 2.toByte
+ )
+ ),
+ Map()
+ )
+ )
+ ),
+ sinceVersion = V6SoftForkVersion)
+
+ val key = keyCollGen.sample.get
+ val value = bytesCollGen.sample.get
+ val (_, avlProver) = createAvlTreeAndProver()
+ val preInsertDigest = avlProver.digest.toColl
+ val tree = createTree(preInsertDigest, insertAllowed = true, updateAllowed = true)
+ val insertProof = performInsertOrUpdate(avlProver, Seq(key, key), Seq(value, value))
+ val kvs = Colls.fromItems((key -> value), (key -> value))
+ val input1 = (tree, (kvs, insertProof))
+
+ val digest = avlProver.digest
+ val updTree = tree.updateDigest(Colls.fromArray(digest))
+
+ val cases = Seq(input1 -> Success((Some(updTree))))
+
+ testCases(cases, iou, preGeneratedSamples = Some(Seq.empty))
+ }
+
+}
diff --git a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala
index bcd4b21129..266c5e66e5 100644
--- a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala
+++ b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala
@@ -34,8 +34,6 @@ class SigmaDslStaginTests extends BaseCtxTests with ErgoScriptTestkit with BaseL
val ctx: SContext = newContext(10, boxA1, VersionContext.MaxSupportedScriptVersion, VersionContext.MaxSupportedScriptVersion)
.withInputs(boxA2)
.withVariables(Map(1 -> toAnyValue(30), 2 -> toAnyValue(40)))
- val p1: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(true))
- val p2: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(false))
cake.check(dsl, { env: EnvRep[RSigmaDslBuilder] =>
for { dsl <- env; arg <- lifted(true) } yield dsl.sigmaProp(arg) }, dsl.sigmaProp(true))
diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala
index ed6d380cf2..077c169343 100644
--- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala
+++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala
@@ -4,6 +4,7 @@ import debox.cfor
import org.ergoplatform._
import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, TestContractSpec}
import org.ergoplatform.validation.ValidationRules
+import org.ergoplatform.validation.ValidationRules.CheckV6Type
import org.scalacheck.Arbitrary._
import org.scalacheck.Gen.frequency
import org.scalacheck.{Arbitrary, Gen}
@@ -14,6 +15,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._
@@ -49,8 +51,12 @@ class SigmaDslTesting extends AnyPropSpec
with Matchers
with SigmaTestingData with SigmaContractSyntax with CompilerTestingCommons
with ObjectGenerators { suite =>
+
override def Coll[T](items: T*)(implicit cT: RType[T]): Coll[T] = super.Coll(items:_*)
+ protected val ActivationByScriptVersion: Byte = 0.toByte
+ protected val ActivationByTreeVersion: Byte = 1.toByte
+
lazy val spec: ContractSpec = TestContractSpec(suite)(new TestingIRContext)
override def contractEnv: ScriptEnv = Map()
@@ -123,6 +129,15 @@ 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
+
+ /** 0 = script version based activation, 1 = tree version based activation */
+ def activationType: Byte
+
/** Script containing this feature. */
def script: String
@@ -177,13 +192,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*/
@@ -254,6 +269,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)
}
@@ -321,7 +337,9 @@ class SigmaDslTesting extends AnyPropSpec
val pkAlice = prover.pubKeys.head.toSigmaPropValue
val env = Map("pkAlice" -> pkAlice)
// Compile script the same way it is performed by applications (i.e. via Ergo Appkit)
- val prop = compile(env, code)(IR).asSigmaProp
+ val prop = VersionContext.withVersions(3, 3) {
+ compile(env, code)(IR).asSigmaProp
+ }
// Add additional operations which are not yet implemented in ErgoScript compiler
val multisig = AtLeast(
@@ -330,7 +348,9 @@ class SigmaDslTesting extends AnyPropSpec
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))
}
@@ -380,8 +400,9 @@ class SigmaDslTesting extends AnyPropSpec
parsed.bytes shouldBe box.bytes
}
catch {
- case ValidationException(_, r: CheckSerializableTypeCode.type, Seq(SOption.OptionTypeCode), _) =>
- // ignore the problem with Option serialization, but test all the other cases
+ // ignore the problem with Option serialization, but test all the other cases
+ case ValidationException(_, _: CheckV6Type.type, Seq(SOption(_)), _) =>
+ case ValidationException(_, _: CheckSerializableTypeCode.type, Seq(SOption.OptionTypeCode), _) =>
}
ErgoLikeContextTesting.dummy(box, activatedVersionInTests)
@@ -394,10 +415,13 @@ class SigmaDslTesting extends AnyPropSpec
ctx
}
- val (expectedResult, expectedCost) = if (activatedVersionInTests < VersionContext.JitActivationVersion)
+ val (expectedResult, expectedCost) = if (
+ (activationType == ActivationByScriptVersion && activatedVersionInTests < sinceVersion) ||
+ (activationType == ActivationByTreeVersion && ergoTreeVersionInTests < sinceVersion)
+ ) {
(expected.oldResult, expected.verificationCostOpt)
- else {
- val res = expected.newResults(ergoTreeVersionInTests)
+ } else {
+ val res = expected.newResults(sinceVersion)
(res._1, res._1.verificationCost)
}
@@ -453,7 +477,8 @@ 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} " +
+ s"(script version: ${VersionContext.current.activatedVersion}, tree version: ${VersionContext.current.ergoTreeVersion})")(verificationCost)
}
case Failure(t) => throw t
@@ -501,6 +526,12 @@ class SigmaDslTesting extends AnyPropSpec
implicit val cs = compilerSettingsInTests
+ override def sinceVersion: Byte = 0
+
+ override val activationType = ActivationByScriptVersion
+
+ 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)
@@ -635,10 +666,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
@@ -658,6 +690,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,
@@ -665,12 +698,17 @@ class SigmaDslTesting extends AnyPropSpec
printExpectedExpr: Boolean = true,
logScript: Boolean = LogScriptDefault,
allowNewToSucceed: Boolean = false,
+ override val activationType: Byte = ActivationByTreeVersion,
override val allowDifferentErrors: Boolean = false
)(implicit IR: IRContext, override val evalSettings: EvalSettings, val tA: RType[A], val tB: RType[B])
extends Feature[A, B] { feature =>
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])
@@ -696,11 +734,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)
}
@@ -748,7 +792,13 @@ class SigmaDslTesting extends AnyPropSpec
checkEq(scalaFuncNew)(newF)(input)
}
- if (!VersionContext.current.isJitActivated) {
+ val checkOld = if(changedInVersion < V6SoftForkVersion) {
+ VersionContext.current.activatedVersion < changedInVersion
+ } else {
+ VersionContext.current.ergoTreeVersion < changedInVersion
+ }
+
+ if (checkOld) {
// check the old implementation with Scala semantic
val expectedOldRes = expected.value
@@ -833,34 +883,53 @@ 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],
printExpectedExpr: Boolean = true,
+ override val activationType: Byte = ActivationByTreeVersion,
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 && vc.ergoTreeVersion >= 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 = {
@@ -880,7 +949,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
}
@@ -889,8 +961,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
+ }
}
}
@@ -958,6 +1041,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
@@ -976,6 +1073,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.
*
@@ -986,8 +1107,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 = {
@@ -998,6 +1121,21 @@ class SigmaDslTesting extends AnyPropSpec
commonNewResults.updateMany(newVersionedResults).toSeq
}
}
+
+ def apply[A](value: Try[A],
+ costOpt: Option[Int],
+ expectedDetails: CostDetails,
+ newCostOpt: Option[Int],
+ newVersionedResults: Seq[(Int, (ExpectedResult[A], Option[CostDetails]))]): Expected[A] =
+ new Expected[A](ExpectedResult(value, costOpt)) {
+ override val newResults = {
+ val commonNewResults = defaultNewResults.map {
+ case (res, _) =>
+ (ExpectedResult(res.value, newCostOpt), Option(expectedDetails))
+ }
+ commonNewResults.updateMany(newVersionedResults).toSeq
+ }
+ }
}
/** Describes existing language feature which should be equally supported in both
@@ -1030,21 +1168,27 @@ 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,
+ activationType: Byte = ActivationByTreeVersion
+ )
(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)
+ allowDifferentErrors = allowDifferentErrors,
+ activationType = activationType
+ )
}
/** 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
@@ -1052,9 +1196,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())
@@ -1275,6 +1419,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
@@ -1303,7 +1448,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/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
index 8ef619289c..1c3b1bfa45 100644
--- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
@@ -7,10 +7,12 @@ 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 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
@@ -20,14 +22,14 @@ import sigma.compiler.CompilerSettings
import sigma.eval.EvalSettings
import sigma.exceptions.{CostLimitException, InterpreterException}
import sigma.serialization.ErgoTreeSerializer.DefaultSerializer
-import sigmastate.Plus
-import sigmastate.utils.Helpers.TryOps
+import sigmastate.{CrossVersionProps, Plus}
+import sigmastate.utils.Helpers.{TryOps, decodeGroupElement}
/** 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, "")
@@ -263,7 +265,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit {
val typeCodes = Table(
("constant", "expectedValue"),
- (SPrimType.LastPrimTypeCode, 8),
+ (SPrimType.LastPrimTypeCode, 9),
(SPrimType.MaxPrimTypeCode, 11)
)
@@ -284,6 +286,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),
@@ -312,45 +315,172 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit {
*/
case class MInfo(methodId: Byte, method: SMethod, isResolvableFromIds: Boolean = true)
+ def isV6Activated = VersionContext.current.isV3OrLaterErgoTreeVersion
+
// 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.isV3OrLaterErgoTreeVersion) {
+ Seq(MInfo(6, ExponentiateUnsignedMethod))
+ } else {
+ Seq.empty
+ }
+ }, true)
},
{ import SSigmaPropMethods._
(SSigmaProp.typeId, Seq(
@@ -366,9 +496,13 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit {
MInfo(4, BytesWithoutRefMethod),
MInfo(5, IdMethod),
MInfo(6, creationInfoMethod),
- MInfo(7, getRegMethod),
+ MInfo(7, getRegMethodV5),
MInfo(8, tokensMethod)
- ) ++ registers(idOfs = 8)
+ ) ++ (if (isV6Activated) {
+ Seq(MInfo(19, getRegMethodV6))
+ } else {
+ Seq()
+ }) ++ registers(idOfs = 8)
.zipWithIndex
.map { case (m,i) => MInfo((8 + i + 1).toByte, m) }, true)
},
@@ -389,7 +523,11 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit {
MInfo(13, updateMethod),
MInfo(14, removeMethod),
MInfo(15, updateDigestMethod)
- ), true)
+ ) ++ (if (isV6Activated) {
+ Seq(MInfo(16, insertOrUpdateMethod))
+ } else {
+ Seq.empty
+ }), true)
},
{ import SHeaderMethods._
(SHeader.typeId, Seq(
@@ -398,7 +536,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(
@@ -412,13 +554,21 @@ 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), MInfo(11, getVarV5Method)
+ ) ++ (if(VersionContext.current.isV3OrLaterErgoTreeVersion){
+ Seq(MInfo(12, getVarFromInputMethod))
+ } else {
+ Seq.empty[MInfo]
+ }), 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(
@@ -432,49 +582,33 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit {
MInfo(8, FilterMethod),
MInfo(9, AppendMethod),
MInfo(10, ApplyMethod),
- /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479
- BitShiftLeftMethod,
- BitShiftRightMethod,
- BitShiftRightZeroedMethod,
- */
MInfo(14, IndicesMethod),
MInfo(15, FlatMapMethod),
MInfo(19, PatchMethod),
MInfo(20, UpdatedMethod),
MInfo(21, UpdateManyMethod),
- /*TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479
- UnionSetsMethod,
- DiffMethod,
- IntersectMethod,
- PrefixLengthMethod,
- */
MInfo(26, IndexOfMethod),
- /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479
- LastIndexOfMethod,
- FindMethod,
- */
MInfo(29, ZipMethod)
- /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479
- DistinctMethod,
- StartsWithMethod,
- EndsWithMethod,
- MapReduceMethod,
- */
- ), true)
+ ) ++ (if (isV6Activated) {
+ Seq(MInfo(30, ReverseMethod), MInfo(31, StartsWithMethod), MInfo(32, EndsWithMethod), MInfo(33, GetMethod))
+ } else Seq.empty), true)
},
{ import SOptionMethods._
(SOption.typeId, Seq(
MInfo(2, IsDefinedMethod),
MInfo(3, GetMethod),
MInfo(4, GetOrElseMethod),
- /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479
- FoldMethod,
- */
MInfo(7, MapMethod),
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) =>
@@ -483,7 +617,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) {
@@ -507,21 +643,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 ecc1dace81..153aae28d5 100644
--- a/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala
@@ -273,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")
@@ -297,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 c8b9f06399..1dfd3a3091 100644
--- a/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala
@@ -3,7 +3,7 @@ package sigmastate
import org.ergoplatform._
import org.ergoplatform.validation.ValidationRules._
import org.scalatest.BeforeAndAfterAll
-import sigma.{Colls, SigmaTestingData}
+import sigma.{Colls, SigmaTestingData, VersionContext}
import sigma.ast._
import sigma.ast.SPrimType.MaxPrimTypeCode
import sigma.ast.TypeCodes.LastConstantCode
@@ -289,7 +289,7 @@ class SoftForkabilitySpecification extends SigmaTestingData
trySoftForkable(false) {
action
true
- }
+ }(vs)
}, {
case ve: ValidationException if ve.rule == rule => true
case _ => false
@@ -340,12 +340,14 @@ class SoftForkabilitySpecification extends SigmaTestingData
}
property("CheckMethod rule") {
- val freeMethodId = 16.toByte
- val mcBytes = Array[Byte](OpCodes.PropertyCallCode, SCollection.typeId, freeMethodId, Outputs.opCode)
- val v2vs = vs.updated(CheckAndGetMethod.id, ChangedRule(Array(SCollection.typeId, freeMethodId)))
- checkRule(CheckAndGetMethod, v2vs, {
- ValueSerializer.deserialize(mcBytes)
- })
+ VersionContext.withVersions(3,0) {
+ val freeMethodId = 16.toByte
+ val mcBytes = Array[Byte](OpCodes.PropertyCallCode, SCollection.typeId, freeMethodId, Outputs.opCode)
+ val v2vs = vs.updated(CheckAndGetMethodV6.id, ChangedRule(Array(SCollection.typeId, freeMethodId)))
+ checkRule(CheckAndGetMethodV6, v2vs, {
+ ValueSerializer.deserialize(mcBytes)
+ })
+ }
}
override protected def afterAll(): Unit = {
diff --git a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala
index 446f1972a7..054f957b49 100644
--- a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala
@@ -6,17 +6,23 @@ 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 sigma.data.{AvlTreeData, CAND, ProveDlog, SigmaBoolean, TrivialProp}
+import scorex.util.encode.{Base16, Base58}
+import sigma.Colls
+import sigma.VersionContext.{V6SoftForkVersion, withVersions}
+import sigma.data.{CAND, CAvlTree, CBox, CHeader, ProveDlog, SigmaBoolean, TrivialProp}
+import sigma.interpreter.ContextExtension
+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
@@ -29,12 +35,29 @@ class TestingInterpreterSpecification extends CompilerTestingCommons
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 =>
@@ -116,15 +139,23 @@ 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)))))
+ 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
@@ -201,6 +232,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 (ergoTreeVersionInTests >= V6SoftForkVersion) {
+ testEval(source)
+ } else {
+ an [sigmastate.exceptions.MethodNotFound] should be thrownBy testEval(source)
+ }
+ }
+
property("Evaluate numeric casting ops") {
def testWithCasting(castSuffix: String): Unit = {
testEval(s"OUTPUTS.size.toByte.$castSuffix == 0.$castSuffix")
@@ -215,6 +261,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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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")
@@ -251,6 +337,27 @@ class TestingInterpreterSpecification extends CompilerTestingCommons
testEval("Coll(1, 1).getOrElse(3, 1 + 1) == 2")
}
+ property("Evaluate powHit") {
+ val source =
+ """
+ |{
+ | val b = unsignedBigInt("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 (ergoTreeVersionInTests >= V6SoftForkVersion) {
+ testEval(source)
+ } else {
+ an [sigmastate.exceptions.MethodNotFound] should be thrownBy testEval(source)
+ }
+ }
+
property("Evaluation example #1") {
val dk1 = prover.dlogSecrets(0).publicImage
val dk2 = prover.dlogSecrets(1).publicImage
@@ -337,6 +444,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(
@@ -371,6 +496,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 (ergoTreeVersionInTests >= V6SoftForkVersion) {
+ testEval(source)
+ } else {
+ an [Exception] should be thrownBy 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/EvaluationTest.scala b/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala
index f9a7edf0cb..6139183b07 100644
--- a/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala
+++ b/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala
@@ -9,7 +9,6 @@ import scalan.BaseCtxTests
import sigma.ast.syntax.SigmaPropValue
import sigma.data.ProveDlog
import sigmastate.lang.LangTests
-import sigma.util.BenchmarkUtil._
import sigmastate.crypto.DLogProtocol.DLogProverInput
import sigma.serialization.ErgoTreeSerializer.DefaultSerializer
@@ -102,4 +101,5 @@ class EvaluationTest extends BaseCtxTests
ctx,
true)
}
+
}
diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala
index b4b4ad20cd..3c3687cb37 100644
--- a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala
+++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala
@@ -10,10 +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._
@@ -29,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
@@ -203,4 +216,15 @@ class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat
e.source shouldBe Some(SourceContext(2, 5, "val x = 10"))
}
+ property("predefined `serialize` should be transformed to MethodCall") {
+ runWithVersion(VersionContext.V6SoftForkVersion) {
+ checkBound(env, "serialize(1)",
+ MethodCall.typed[Value[SCollection[SByte.type]]](
+ Global,
+ SGlobalMethods.getMethodByName("serialize").withConcreteTypes(Map(STypeVar("T") -> SInt)),
+ Array(IntConstant(1)),
+ Map()
+ ))
+ }
+ }
}
diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala
index 1ca3e5c020..c97b28b83f 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}
@@ -21,6 +21,8 @@ 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 {
@@ -28,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
@@ -39,7 +42,12 @@ class SigmaTyperTest extends AnyPropSpec
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
@@ -180,7 +188,6 @@ class SigmaTyperTest extends AnyPropSpec
typecheck(env, "{ (a: Int) => (1, 2L)(a) }") shouldBe SFunc(IndexedSeq(SInt), SAny)
}
- // TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479
ignore("tuple advanced operations") {
typecheck(env, "(1, 2L).getOrElse(2, 3)") shouldBe SAny
typecheck(env, "(1, 2L).slice(0, 2)") shouldBe SCollection(SAny)
@@ -255,10 +262,6 @@ class SigmaTyperTest extends AnyPropSpec
typecheck(env, "{ (a: Int) => { val b = a + 1; b } }") shouldBe SFunc(IndexedSeq(SInt), SInt)
typecheck(env, "{ (a: Int, box: Box) => a + box.value }") shouldBe
SFunc(IndexedSeq(SInt, SBox), SLong)
- /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479
- typecheck(env, "{ (p: (Int, GroupElement), box: Box) => p._1 > box.value && p._2.isIdentity }") shouldBe
- SFunc(IndexedSeq(STuple(SInt, SGroupElement), SBox), SBoolean)
- */
typecheck(env, "{ (p: (Int, SigmaProp), box: Box) => p._1 > box.value && p._2.isProven }") shouldBe
SFunc(IndexedSeq(STuple(SInt, SSigmaProp), SBox), SBoolean)
@@ -299,8 +302,6 @@ class SigmaTyperTest extends AnyPropSpec
typecheck(env, "SELF.R1[Int].isDefined") shouldBe SBoolean
typecheck(env, "SELF.R1[Int].isEmpty") shouldBe SBoolean
typecheck(env, "SELF.R1[Int].get") shouldBe SInt
- // TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416
- // typecheck(env, "SELF.getReg[Int](1)") shouldBe SOption.SIntOption
typefail(env, "x[Int]", 1, 1)
typefail(env, "arr1[Int]", 1, 1)
typecheck(env, "SELF.R1[(Int,Boolean)]") shouldBe SOption(STuple(SInt, SBoolean))
@@ -511,6 +512,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
}
@@ -632,15 +675,20 @@ class SigmaTyperTest extends AnyPropSpec
typecheck(env, "SELF.tokens") shouldBe ErgoBox.STokensRegType
}
-// TODO soft-fork: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479
-// property("SOption.toColl") {
-// typecheck(env, "getVar[Int](1).toColl") shouldBe SIntArray
-// }
-
property("SContext.dataInputs") {
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
}
@@ -671,4 +719,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/DeserializationResilience.scala b/sc/shared/src/test/scala/sigmastate/serialization/DeserializationResilience.scala
index 3649a158db..af4b7347e6 100644
--- a/sc/shared/src/test/scala/sigmastate/serialization/DeserializationResilience.scala
+++ b/sc/shared/src/test/scala/sigmastate/serialization/DeserializationResilience.scala
@@ -1,18 +1,18 @@
package sigma.serialization
-import org.ergoplatform.ErgoBoxCandidate
+import org.ergoplatform.ErgoBox.R4
+import org.ergoplatform.{ErgoBoxCandidate, ErgoTreePredef}
import org.scalacheck.Gen
import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert}
import scorex.crypto.authds.{ADKey, ADValue}
import scorex.crypto.hash.{Blake2b256, Digest32}
import scorex.util.serialization.{Reader, VLQByteBufferReader}
-import sigma.ast.{SBoolean, SInt, SizeOf}
+import sigma.ast.{SBoolean, SInt, SizeOf, _}
import sigma.data.{AvlTreeData, AvlTreeFlags, CAND, SigmaBoolean}
import sigma.util.{BenchmarkUtil, safeNewArray}
import sigma.validation.ValidationException
import sigma.validation.ValidationRules.CheckPositionLimit
-import sigma.{Colls, Environment}
-import sigma.ast._
+import sigma.{Colls, Environment, VersionContext}
import sigma.ast.syntax._
import sigmastate._
import sigma.Extensions.ArrayOps
@@ -25,6 +25,7 @@ import sigmastate.helpers.{CompilerTestingCommons, ErgoLikeContextTesting, ErgoL
import sigma.serialization.OpCodes._
import sigmastate.utils.Helpers._
+import java.math.BigInteger
import java.nio.ByteBuffer
import scala.collection.immutable.Seq
import scala.collection.mutable
@@ -426,4 +427,69 @@ class DeserializationResilience extends DeserializationResilienceTesting {
// NOTE, even though performOneOperation fails, some AvlTree$ methods used in Interpreter
// (remove_eval, update_eval, contains_eval) won't throw, while others will.
}
+
+
+ property("impossible to use v6 types in box registers") {
+ val trueProp = ErgoTreePredef.TrueProp(ErgoTree.defaultHeaderWithVersion(3))
+
+ val b = new ErgoBoxCandidate(1L, trueProp, 1,
+ additionalRegisters = Map(R4 -> UnsignedBigIntConstant(new BigInteger("2"))))
+ VersionContext.withVersions(3, 3) {
+ val bs = ErgoBoxCandidate.serializer.toBytes(b)
+ a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
+ }
+
+ val b2 = new ErgoBoxCandidate(1L, trueProp, 1,
+ additionalRegisters = Map(R4 -> Constant[SOption[SInt.type]](Some(2), SOption(SInt))))
+ VersionContext.withVersions(3, 3) {
+ val bs2 = ErgoBoxCandidate.serializer.toBytes(b2)
+ a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs2)
+ }
+
+ val b3 = new ErgoBoxCandidate(1L, trueProp, 1,
+ additionalRegisters = Map(R4 -> Tuple(UnsignedBigIntConstant(new BigInteger("1")), IntConstant(1))))
+ VersionContext.withVersions(3, 3) {
+ val bs = ErgoBoxCandidate.serializer.toBytes(b3)
+ a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
+ }
+
+ val b4 = new ErgoBoxCandidate(1L, trueProp, 1,
+ additionalRegisters = Map(R4 -> ConcreteCollection(Seq(UnsignedBigIntConstant(new BigInteger("1"))), SUnsignedBigInt)))
+ VersionContext.withVersions(3, 3) {
+ val bs = ErgoBoxCandidate.serializer.toBytes(b4)
+ a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
+ }
+
+ val b5 = new ErgoBoxCandidate(1L, trueProp, 1,
+ additionalRegisters = Map(R4 -> Tuple(IntConstant(1), UnsignedBigIntConstant(new BigInteger("1")))))
+ VersionContext.withVersions(3, 3) {
+ val bs = ErgoBoxCandidate.serializer.toBytes(b5)
+ a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
+ }
+
+ val reader = new SigmaByteReader(new VLQByteBufferReader(ByteBuffer.wrap(decodeBytes("5402030209050a050105").toArray)), new ConstantStore(), false)
+ val v = VersionContext.withVersions(3, 3) {ConstantSerializer(DeserializationSigmaBuilder).parse(reader).asInstanceOf[Constant[STuple]] }
+ val b6 = new ErgoBoxCandidate(1L, trueProp, 1,
+ additionalRegisters = Map(R4 -> v))
+ VersionContext.withVersions(3, 3) {
+ val bs6 = ErgoBoxCandidate.serializer.toBytes(b6)
+ a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs6)
+ }
+
+ val b7 = new ErgoBoxCandidate(1L, trueProp, 1,
+ additionalRegisters = Map(R4 -> ConcreteCollection(Seq(Tuple(IntConstant(1), UnsignedBigIntConstant(new BigInteger("1")))), STuple(SInt, SUnsignedBigInt))))
+ VersionContext.withVersions(3, 3) {
+ val bs = ErgoBoxCandidate.serializer.toBytes(b7)
+ a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
+ }
+
+ val b8 = new ErgoBoxCandidate(1L, trueProp, 1,
+ additionalRegisters = Map(R4 -> ConcreteCollection(Seq(ConcreteCollection(Seq(UnsignedBigIntConstant(new BigInteger("1"))), SUnsignedBigInt)), (SCollection(SUnsignedBigInt)))))
+ VersionContext.withVersions(3, 3) {
+ val bs = ErgoBoxCandidate.serializer.toBytes(b8)
+ a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
+ }
+
+ }
+
}
diff --git a/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala
index 6b0c0080c9..ca7f9dd7c7 100644
--- a/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala
@@ -15,14 +15,13 @@ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingIn
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
@@ -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 cc2c62529f..42ae44a665 100644
--- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala
@@ -2,16 +2,26 @@ package sigmastate.utxo
import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8}
import org.ergoplatform._
-import sigma.Colls
+import org.scalatest.Assertion
+import scorex.crypto.authds.{ADKey, ADValue}
+import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert, InsertOrUpdate}
+import scorex.crypto.hash.{Blake2b256, Digest32}
+import scorex.util.{ByteArrayBuilder, idToBytes}
+import scorex.util.encode.Base16
+import scorex.utils.Ints
+import scorex.util.serialization.VLQByteBufferWriter
+import scorex.utils.Longs
+import sigma.{Coll, Colls, GroupElement, SigmaTestingData, VersionContext}
import sigma.Extensions.ArrayOps
+import sigma.VersionContext.{V6SoftForkVersion, withVersions}
import sigma.ast.SCollection.SByteArray
+import sigma.ast.SType.{AnyOps, tD}
+import sigma.data.{AvlTreeData, AvlTreeFlags, CAND, CAnyValue, CBigInt, CGroupElement, CHeader, CSigmaDslBuilder, CSigmaProp}
import sigma.ast.SOption
-import sigma.ast.SType.AnyOps
-import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder}
import sigma.util.StringUtil._
import sigma.ast._
import sigma.ast.syntax._
-import sigma.crypto.CryptoConstants
+import sigma.crypto.{CryptoConstants, SecP256K1Group}
import sigmastate._
import sigmastate.helpers.TestingHelpers._
import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter}
@@ -19,12 +29,22 @@ import sigma.interpreter.ContextExtension.VarBinding
import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings
import sigmastate.interpreter.Interpreter._
import sigma.ast.Apply
-import sigma.eval.EvalSettings
-import sigma.exceptions.{InterpreterException, InvalidType}
-import sigma.serialization.ValueSerializer
+import sigma.eval.{EvalSettings, SigmaDsl}
+import sigma.exceptions.InvalidType
+import sigma.serialization.ErgoTreeSerializer
+import sigma.serialization.{DataSerializer, SigmaByteWriter, ValueSerializer}
+import sigma.interpreter.{ContextExtension, ProverResult}
+import sigma.validation.ValidationException
+import sigma.util.Extensions
+import sigmastate.utils.Helpers
+import sigma.exceptions.InterpreterException
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 {
@@ -45,7 +65,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)),
@@ -62,7 +84,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,
@@ -73,7 +96,14 @@ 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,
@@ -86,78 +116,2416 @@ class BasicOpsSpecification extends CompilerTestingCommons
// is not supported by ErgoScript Compiler)
// In such cases we use expected property as the property to test
propExp.asSigmaProp
- } else
- compile(env, script).asBoolValue.toSigmaProp
+ } else {
+ // compile with the latest compiler version, to get validation exception during execution, not compilation error
+ withVersions(VersionContext.MaxSupportedScriptVersion, VersionContext.MaxSupportedScriptVersion) {
+ 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, ergoTreeVersionInTests)
+
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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.isV3OrLaterErgoTreeVersion) {
+ 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 (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] 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 (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] 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 (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] 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 (ergoTreeVersionInTests < 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 (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] 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 (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] 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 (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.serialization.SerializerException] should be thrownBy conversionTest()
+ } else {
+ conversionTest()
+ }
+ }
+
+ property("unsigned bigint - subtract") {
+ def conversionTest() = {test("subtract", env, ext,
+ s"""{
+ | val a = unsignedBigInt("10")
+ | val b = unsignedBigInt("5")
+ | a - b == b
+ | } """.stripMargin,
+ null,
+ true
+ )}
+
+ if (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.serialization.SerializerException] should be thrownBy conversionTest()
+ } else {
+ conversionTest()
+ }
+ }
+
+ property("unsigned bigint - multiply") {
+ def conversionTest() = {test("multiply", env, ext,
+ s"""{
+ | val a = unsignedBigInt("10")
+ | val b = unsignedBigInt("50")
+ | a * b == unsignedBigInt("500")
+ | } """.stripMargin,
+ null,
+ true
+ )}
+
+ if (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.serialization.SerializerException] 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 (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.serialization.SerializerException] 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 (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] 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 (ergoTreeVersionInTests < 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 td = new SigmaTestingData {}
+
+ val g = CGroupElement(SecP256K1Group.generator)
+
+ def randBigInt: BigInt = {
+ val random = new SecureRandom()
+ val values = new Array[Byte](32)
+ random.nextBytes(values)
+ BigInt(values).mod(td.TestData.BigIntMaxValue.asInstanceOf[CBigInt].wrappedValue)
+ }
+
+ @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 bi = CBigInt(holderSecret.bigInteger)
+ val holderPk = g.exp(bi)
+
+ 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 (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] should be thrownBy schnorrTest()
+ } else {
+ schnorrTest()
+ }
+ }
+
+ property("unsigned bigint - arith") {
+ def miTest() = {
+ test("arith", 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 (ergoTreeVersionInTests < 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) == unsignedBigInt("554794378")
+ |}""".stripMargin,
+ null,
+ true
+ )
+ }
+
+ if (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] should be thrownBy miTest()
+ } else {
+ miTest()
+ }
+ }
+
+ property("modInverse") {
+ def miTest() = {
+ test("modInverse", env, ext,
+ s"""{
+ | val bi = unsignedBigInt("3")
+ | val m = unsignedBigInt("7")
+ | bi.modInverse(m) == unsignedBigInt("5")
+ |}""".stripMargin,
+ null,
+ true
+ )
+ }
+
+ if (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] should be thrownBy miTest()
+ } else {
+ miTest()
+ }
+ }
+
+ property("modInverse - zero") {
+ def miTest() = {
+ test("modInverse", env, ext,
+ s"""{
+ | val bi = unsignedBigInt("248486720836984554860790790898080606")
+ | val m = unsignedBigInt("0")
+ | bi.modInverse(m) > 0
+ |}""".stripMargin,
+ null,
+ true
+ )
+ }
+
+ if (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] should be thrownBy miTest()
+ } else {
+ an[Exception] should be thrownBy miTest()
+ }
+ }
+
+ property("mod ops - plus") {
+ def miTest() = {
+ test("mod plus", env, ext,
+ s"""{
+ | val bi1 = unsignedBigInt("248486720836984554860790790898080606")
+ | val bi2 = unsignedBigInt("2484867208369845548607907908980997780606")
+ | val m = unsignedBigInt("575879797")
+ | bi1.plusMod(bi2, m) == unsignedBigInt("88450889")
+ |}""".stripMargin,
+ null,
+ true
+ )
+ }
+
+ if (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] 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) == unsignedBigInt("575879795")
+ |}""".stripMargin,
+ null,
+ true
+ )
+ }
+
+ if (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] 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) == unsignedBigInt("532796569")
+ |}""".stripMargin,
+ null,
+ true
+ )
+ }
+
+ if (ergoTreeVersionInTests < V6SoftForkVersion) {
+ an[sigma.validation.ValidationException] 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