diff --git a/library-api/src/main/scala/special/collection/Colls.scala b/library-api/src/main/scala/special/collection/Colls.scala index 231e3fba71..e177638057 100644 --- a/library-api/src/main/scala/special/collection/Colls.scala +++ b/library-api/src/main/scala/special/collection/Colls.scala @@ -367,6 +367,12 @@ trait Coll[@specialized A] { */ def reverse: Coll[A] + /** Checks if this collection is a replication of the given value a given number of times. + * + * @param len how many times the `value` is replicated + * @param value the replicated value + * @return true if this collection is element-wise equal to a collection of replicated values. + */ @Internal private[collection] def isReplArray(len: Int, value: A): Boolean @@ -427,9 +433,26 @@ trait ReplColl[@specialized A] extends Coll[A] { @scalan.Liftable @WithMethodCallRecognizers trait CollBuilder { + /** Monoid builder associated with this collections builder. + * It should be used to create monoids which are required by some of the Coll methods. + */ def Monoids: MonoidBuilder + + /** Constructs a new collection of pairs out of the pair of collections by zipping them. + * The resulting collection is semantically equivalent to `as.zip(bs)`. + * @param as collection of first items + * @param bs collection of second items + * @return an instance of [[PairColl]] interface with represents the resulting collection of pairs. + */ def pairColl[@specialized A, @specialized B](as: Coll[A], bs: Coll[B]): PairColl[A,B] + /** Constructs a new collection of pairs out of the pair of arrays by wrapping them in collections + * and delegating to [[pairColl]] method. + * The resulting collection is semantically equivalent to as.zip(bs). + * @param as collection of first items + * @param bs collection of second items + * @return an instance of [[PairColl]] interface with represents the resulting collection of pairs. + */ @Internal def pairCollFromArrays[A: RType, B: RType](as: Array[A], bs: Array[B]): PairColl[A,B] = pairColl(fromArray(as), fromArray(bs)) @@ -471,6 +494,16 @@ trait CollBuilder { @Internal def makeView[@specialized A, @specialized B: RType](source: Coll[A], f: A => B): Coll[B] + /** Create a new view using pre-calculated results of `f`. + * + * @param source the collection the view is based on. + * @param f view function, which transforms each pre-image element to the + * corresponding `image` element of the resulting collection + * @param calculated array of flags marking which element where pre-calculated + * @param calculatedItems pre-calculated images to be used in the resulting collection + * so that `calculated.length == calculatedItems.length` + * @return collection of images of `f` + */ @Internal def makePartialView[@specialized A, @specialized B: RType](source: Coll[A], f: A => B, calculated: Array[Boolean], calculatedItems: Array[B]): Coll[B] @@ -492,11 +525,10 @@ trait CollBuilder { (left: Coll[(K, L)], right: Coll[(K, R)]) (l: ((K,L)) => O, r: ((K,R)) => O, inner: ((K,(L,R))) => O): Coll[(K,O)] - /** Flattens a two-dimensional array by concatenating all its rows - * into a single array. + /** Flattens a two-dimensional collection by concatenating all its rows + * into a single collection. * - * @tparam U Type of row elements. - * @param asTrav A function that converts elements of this array to rows - arrays of type `U`. + * @tparam A Type of row elements. * @return An array obtained by concatenating rows of this array. */ def flattenColl[A:RType](coll: Coll[Coll[A]]): Coll[A] diff --git a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala index 51640b2aad..bc0cc0ae28 100644 --- a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala +++ b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala @@ -184,7 +184,7 @@ class CollOverArray[@specialized A](val toArray: Array[A])(implicit tA: RType[A] } @Internal - def isReplArray(len: Int, value: A): Boolean = { + override def isReplArray(len: Int, value: A): Boolean = { length == len && { if (tItem.classTag.runtimeClass.isPrimitive) { isAllPrimValue(value) @@ -210,7 +210,14 @@ class CollOverArray[@specialized A](val toArray: Array[A])(implicit tA: RType[A] class CollOverArrayBuilder extends CollBuilder { override def Monoids: MonoidBuilder = new MonoidBuilderInst - @inline def pairColl[@specialized A, @specialized B](as: Coll[A], bs: Coll[B]): PairColl[A, B] = new PairOfCols(as, bs) + @inline override def pairColl[@specialized A, @specialized B](as: Coll[A], bs: Coll[B]): PairColl[A, B] = { + // TODO HF: use minimal length and slice longer collection + // The current implementation doesn't check the case when `as` and `bs` have different lengths. + // in which case the implementation of `PairOfCols` has inconsistent semantics of `map`, `exists` etc methods. + // To fix the problem, the longer collection have to be truncated (which is consistent + // with how zip is implemented for Arrays) + new PairOfCols(as, bs) + } @Internal override def fromMap[K: RType, V: RType](m: Map[K, V]): Coll[(K, V)] = { @@ -233,7 +240,7 @@ class CollOverArrayBuilder extends CollBuilder { @NeverInline @Reified("T") - def fromItems[T](items: T*)(implicit cT: RType[T]): Coll[T] = cT match { + override def fromItems[T](items: T*)(implicit cT: RType[T]): Coll[T] = cT match { case pt: PairType[a,b] => val tA = pt.tFst val tB = pt.tSnd @@ -243,7 +250,7 @@ class CollOverArrayBuilder extends CollBuilder { } @NeverInline - def fromArray[@specialized T: RType](arr: Array[T]): Coll[T] = RType[T] match { + override def fromArray[@specialized T: RType](arr: Array[T]): Coll[T] = RType[T] match { case pt: PairType[a,b] => val tA = pt.tFst val tB = pt.tSnd @@ -253,7 +260,7 @@ class CollOverArrayBuilder extends CollBuilder { } @NeverInline - def replicate[@specialized T: RType](n: Int, v: T): Coll[T] = RType[T] match { + override def replicate[@specialized T: RType](n: Int, v: T): Coll[T] = RType[T] match { case pt: PairType[a,b] => val tA = pt.tFst val tB = pt.tSnd @@ -264,15 +271,15 @@ class CollOverArrayBuilder extends CollBuilder { } @NeverInline - def makeView[@specialized A, @specialized B: RType](source: Coll[A], f: A => B): Coll[B] = new CViewColl(source, f) + override def makeView[@specialized A, @specialized B: RType](source: Coll[A], f: A => B): Coll[B] = new CViewColl(source, f) @NeverInline - def makePartialView[@specialized A, @specialized B: RType](source: Coll[A], f: A => B, calculated: Array[Boolean], calculatedItems: Array[B]): Coll[B] = { + override def makePartialView[@specialized A, @specialized B: RType](source: Coll[A], f: A => B, calculated: Array[Boolean], calculatedItems: Array[B]): Coll[B] = { new CViewColl(source, f).fromPartialCalculation(calculated, calculatedItems) } @NeverInline - def unzip[@specialized A, @specialized B](xs: Coll[(A,B)]): (Coll[A], Coll[B]) = xs match { + override def unzip[@specialized A, @specialized B](xs: Coll[(A,B)]): (Coll[A], Coll[B]) = xs match { case pa: PairColl[_,_] => (pa.ls, pa.rs) case _ => val limit = xs.length @@ -289,7 +296,7 @@ class CollOverArrayBuilder extends CollBuilder { } @NeverInline - def xor(left: Coll[Byte], right: Coll[Byte]): Coll[Byte] = left.zip(right).map { case (l, r) => (l ^ r).toByte } + override def xor(left: Coll[Byte], right: Coll[Byte]): Coll[Byte] = left.zip(right).map { case (l, r) => (l ^ r).toByte } @NeverInline override def emptyColl[T](implicit cT: RType[T]): Coll[T] = cT match { diff --git a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala index 761443d989..c4c92b7d4b 100644 --- a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala +++ b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala @@ -270,10 +270,13 @@ trait SigmaProp { @OverloadId("or_bool") def ||(other: Boolean): SigmaProp } +/** Represents any value paired with type descriptor. */ @scalan.Liftable @WithMethodCallRecognizers trait AnyValue { + /** The data value wrapped by this instance. */ def value: Any + /** The type descriptor of the `value` instance. */ def tVal: RType[Any] } @@ -697,7 +700,16 @@ trait SigmaContract { (implicit cT: RType[T]): Coll[Byte] = this.builder.substConstants(scriptBytes, positions, newValues) } -/** Runtime representation of Global ErgoTree type. */ +/** Runtime representation of SGlobal ErgoTree type. + * The only instance of SGlobal type can be referenced as `Global` variable in ErgoScript. + * It is represented as [[org.ergoplatform.Global]] node of ErgoTree, which evaluates to + * the default singleton instance of this interface. + * + * CostingSigmaDslBuilder object serves as the default singleton instance of Global + * object, which implements global ErgoTree functions. + * + * @see SGlobal.WrappedType, CostingSigmaDslBuilder + */ @scalan.Liftable @WithMethodCallRecognizers trait SigmaDslBuilder { diff --git a/sigma-impl/src/main/scala/special/sigma/TestContext.scala b/sigma-impl/src/main/scala/special/sigma/TestContext.scala index bf3c98e5d3..52102112b3 100644 --- a/sigma-impl/src/main/scala/special/sigma/TestContext.scala +++ b/sigma-impl/src/main/scala/special/sigma/TestContext.scala @@ -1,9 +1,17 @@ package special.sigma +import scalan.OverloadHack.Overloaded1 import scalan.RType +// TODO refactor: move to sigmastate package and rename to CAnyValue + +/** Default implementation of AnyValue interface. */ case class TestValue[A](value: A, tVal: RType[Any]) extends AnyValue { def tA: RType[A] = tVal.asInstanceOf[RType[A]] - override def toString = s"Value($value)" + override def toString = s"TestValue($value)" } +object TestValue { + def apply[A](value: A, t: RType[A])(implicit o: Overloaded1): TestValue[A] = + new TestValue(value, t.asInstanceOf[RType[Any]]) +} diff --git a/sigmastate/src/main/scala/sigmastate/eval/BigIntegerOps.scala b/sigmastate/src/main/scala/sigmastate/eval/BigIntegerOps.scala index 6f50a7a729..9c5561a483 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/BigIntegerOps.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/BigIntegerOps.scala @@ -4,24 +4,23 @@ import java.math.BigInteger import scalan.{ExactNumeric, ExactIntegral, ExactOrderingImpl} -import scala.math.{LowPriorityOrderingImplicits, Integral, Ordering} +import scala.math.{Integral, Ordering} import special.sigma._ -import scalan.util.Extensions._ import sigmastate.eval.Extensions._ -import sigmastate.eval.NumericOps.BigIntIsExactNumeric.n +import special.collection.Coll -object OrderingOps extends LowPriorityOrderingImplicits { +object OrderingOps { def apply[T](implicit ord: Ordering[T]) = ord trait BigIntegerOrdering extends Ordering[BigInteger] { def compare(x: BigInteger, y: BigInteger) = x.compareTo(y) } - implicit object BigInteger extends BigIntegerOrdering + implicit object BigIntegerOrdering extends BigIntegerOrdering trait BigIntOrdering extends Ordering[BigInt] { def compare(x: BigInt, y: BigInt) = x.compareTo(y) } - implicit object BigInt extends BigIntOrdering + implicit object BigIntOrdering extends BigIntOrdering } object NumericOps { diff --git a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala index a4858a5684..5d979ca418 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala @@ -4,6 +4,7 @@ import java.math.BigInteger import java.util import org.bouncycastle.math.ec.ECPoint +import org.ergoplatform.settings.ErgoAlgos import org.ergoplatform.{ErgoBox, SigmaConstants} import org.ergoplatform.validation.ValidationRules import scorex.crypto.authds.avltree.batch._ @@ -621,6 +622,13 @@ class CostingSigmaDslBuilder extends TestSigmaDslBuilder { dsl => this.GroupElement(CryptoConstants.dlogGroup.generator) } + /** + * @return the identity of the Dlog group used in ErgoTree + */ + def groupIdentity: GroupElement = { + this.GroupElement(CryptoConstants.dlogGroup.identity) + } + override def substConstants[T](scriptBytes: Coll[Byte], positions: Coll[Int], newValues: Coll[T]) @@ -635,8 +643,10 @@ class CostingSigmaDslBuilder extends TestSigmaDslBuilder { dsl => val p = GroupElementSerializer.parse(r) this.GroupElement(p) } + } +/** Default singleton instance of Global object, which implements global ErgoTree functions. */ object CostingSigmaDslBuilder extends CostingSigmaDslBuilder /** A default implementation of [[Context]] interface. diff --git a/sigmastate/src/main/scala/sigmastate/utils/Helpers.scala b/sigmastate/src/main/scala/sigmastate/utils/Helpers.scala index ba15440558..cb79246333 100644 --- a/sigmastate/src/main/scala/sigmastate/utils/Helpers.scala +++ b/sigmastate/src/main/scala/sigmastate/utils/Helpers.scala @@ -3,6 +3,11 @@ package sigmastate.utils import java.util import io.circe.Decoder +import org.ergoplatform.settings.ErgoAlgos +import sigmastate.eval.{Colls, SigmaDsl} +import sigmastate.interpreter.CryptoConstants.EcPointType +import special.collection.Coll +import special.sigma.GroupElement import scala.reflect.ClassTag import scala.util.{Failure, Try, Either, Success, Right} @@ -127,6 +132,27 @@ object Helpers { } } + /** Decodes the given hex string into byte array and then uses + * [[SigmaDsl.decodePoint()]] to construct [[GroupElement]] instance. + */ + def decodeGroupElement(hexString: String): GroupElement = { + val bytes = ErgoAlgos.decodeUnsafe(hexString) + SigmaDsl.decodePoint(Colls.fromArray(bytes)) + } + + /** Decodes the given hex string into [[GroupElement]] and then extracts the underlying + * [[EcPointType]] instance + */ + def decodeECPoint(hexString: String): EcPointType = { + val ge = decodeGroupElement(hexString) + SigmaDsl.toECPoint(ge).asInstanceOf[EcPointType] + } + + /** Decodes the given hex string into a collection of bytes. */ + def decodeBytes(base16String: String): Coll[Byte] = { + val bytes = ErgoAlgos.decodeUnsafe(base16String) + Colls.fromArray(bytes) + } } object Overloading { diff --git a/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrint.scala b/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrint.scala index 67f9e4825a..03721c684c 100644 --- a/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrint.scala +++ b/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrint.scala @@ -2,37 +2,52 @@ package sigmastate.helpers import java.math.BigInteger +import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.RegisterId +import org.ergoplatform.settings.ErgoAlgos import scala.collection.mutable import pprint.{Tree, PPrinter} +import scalan.RType +import scalan.RType.PrimitiveType import sigmastate.SCollection._ -import sigmastate.Values.{ValueCompanion, ConstantNode} +import sigmastate.Values.{ValueCompanion, ConstantNode, ErgoTree} +import sigmastate.interpreter.CryptoConstants.EcPointType import sigmastate.lang.SigmaTyper import sigmastate.lang.Terms.MethodCall +import sigmastate.serialization.GroupElementSerializer import sigmastate.utxo.SelectField -import sigmastate.{SByte, SPair, STypeCompanion, STuple, ArithOp, SOption, SType, SCollectionType, SPredefType, SCollection} +import sigmastate._ +import special.collection.Coll +import special.sigma.GroupElement import scala.collection.mutable.ArrayBuffer +import scala.reflect.ClassTag /** Pretty-printer customized to print [[sigmastate.Values.Value]] instances * into a valid Scala code (can be cut-and-pasted).*/ object SigmaPPrint extends PPrinter { - def treeifySeq(xs: Seq[Any]): Iterator[Tree] = { - xs.iterator.map(treeify) + /** Apply [[treeify]] for each element of the given sequence producing the iterator of resulting trees. */ + protected def treeifySeq(xs: Seq[Any]): Iterator[Tree] = { + xs.iterator.map(_ match { + case t: Tree => t + case x => treeify(x) + }) } - def treeifyMany(head: Any, tail: Any*): Iterator[Tree] = { + /** Helper overload to call [[treeifySeq]]. */ + protected def treeifyMany(head: Any, tail: Any*): Iterator[Tree] = { treeifySeq(head +: tail) } - def tpeName(tpe: SType): String = { + private def tpeName(tpe: SType): String = { val name = tpe.toTermString if (name == "Boolean") "Bool" else name } - def typeName(tpe: SType): String = tpe match { + /** Valid Scala type for the given SType. */ + private[helpers] def typeName(tpe: SType): String = tpe match { case _: SPredefType => val name = tpe.getClass.getSimpleName.replace("$", "") s"$name.type" // SByte.type, SInt.type, etc @@ -46,12 +61,13 @@ object SigmaPPrint extends PPrinter { sys.error(s"Cannot get typeName($tpe)") } - def valueType(tpe: SType): String = { + /** Valid Scala type of the Value with the given underlying SType. */ + private def valueType(tpe: SType): String = { val tn = typeName(tpe) s"Value[$tn]" } - val typeHandlers: PartialFunction[Any, Tree] = { + private val typeHandlers: PartialFunction[Any, Tree] = { case SByteArray => Tree.Literal("SByteArray") case SByteArray2 => @@ -60,24 +76,91 @@ object SigmaPPrint extends PPrinter { Tree.Literal("SBooleanArray") case SPair(l, r) => Tree.Apply("SPair", treeifySeq(Array(l, r))) + case t: PrimitiveType[_] => + Tree.Literal(s"RType.${t.name}Type") } - val exceptionHandlers: PartialFunction[Any, Tree] = { - case ex: ArithmeticException => - Tree.Apply("new ArithmeticException", treeifySeq(Seq(ex.getMessage))) + private val exceptionHandlers: PartialFunction[Any, Tree] = { + case ex: Exception => + Tree.Apply(s"new ${ex.getClass.getSimpleName}", treeifySeq(Seq(ex.getMessage))) } - val dataHandlers: PartialFunction[Any, Tree] = { + /** Generated Scala code which creates the given byte array from a hex string literal. */ + private def treeifyByteArray(bytes: Array[Byte]): Tree = { + val hexString = ErgoAlgos.encode(bytes) + Tree.Apply("ErgoAlgos.decodeUnsafe", treeifyMany(hexString)) + } + + private val dataHandlers: PartialFunction[Any, Tree] = { case v: Byte => Tree.Literal(s"$v.toByte") + case v: Short => Tree.Literal(s"$v.toShort") + case v: BigInteger => Tree.Apply("new BigInteger", treeifyMany(v.toString(16), 16)) + + case wa: mutable.WrappedArray[Byte @unchecked] if wa.elemTag == ClassTag.Byte => + treeifyByteArray(wa.array) + case wa: mutable.WrappedArray[_] => Tree.Apply("Array", treeifySeq(wa)) + + case arr: Array[Byte @unchecked] if arr.elemTag == ClassTag.Byte => + treeifyByteArray(arr) + + case arr: Array[_] => + Tree.Apply("Array", treeifySeq(arr)) + case buf: ArrayBuffer[_] => Tree.Apply("Seq", treeifySeq(buf)) + + case ecp: EcPointType => + val hexString = ErgoAlgos.encode(GroupElementSerializer.toBytes(ecp)) + Tree.Apply("Helpers.decodeECPoint", treeifyMany(hexString)) + + case ge: GroupElement => + val hexString = ErgoAlgos.encode(ge.getEncoded) + Tree.Apply("Helpers.decodeGroupElement", treeifyMany(hexString)) + + case coll: Coll[Byte @unchecked] if coll.tItem == RType.ByteType => + val hexString = ErgoAlgos.encode(coll) + Tree.Apply("Helpers.decodeBytes", treeifyMany(hexString)) + + case coll: Coll[_] => + val elemTpe = coll.tItem.name + Tree.Apply(s"Coll[$elemTpe]", treeifySeq(coll.toArray)) + + case t: AvlTreeData => + Tree.Apply("AvlTreeData", treeifyMany( + Tree.Apply("ADDigest @@ ", treeifyMany(t.digest)), + t.treeFlags, + t.keyLength, + t.valueLengthOpt)) + + case t: ErgoTree => + Tree.Apply("new ErgoTree", treeifyMany( + t.header, + Tree.Apply("Vector", t.constants.map(treeify).iterator), + t.root + )) + + case b: ErgoBox => + val tokens = Tree.Apply("Coll", + b.additionalTokens.toArray.map { case (id, v) => + val idTree = Tree.Apply("Digest32 @@ ", treeifyMany(id)) + Tree.Apply("", treeifyMany(idTree, v)) + }.iterator) + Tree.Apply("new ErgoBox", treeifyMany( + b.value, + b.ergoTree, + tokens, + b.additionalRegisters, + Tree.Apply("ModifierId @@ ", treeifyMany(b.transactionId)), + b.index, + b.creationHeight + )) } override val additionalHandlers: PartialFunction[Any, Tree] = @@ -101,8 +184,13 @@ object SigmaPPrint extends PPrinter { val resTpe = sf.input.tpe.items(sf.fieldIndex - 1) val resTpeName = valueType(resTpe) Tree.Apply(s"SelectField.typed[$resTpeName]", treeifySeq(Array(sf.input, sf.fieldIndex))) + + case ConstantNode(v, SCollectionType(elemType)) if elemType.isInstanceOf[SPredefType] => + Tree.Apply(tpeName(elemType) + "ArrayConstant", treeifySeq(Seq(v))) + case c: ConstantNode[_] if c.tpe.isInstanceOf[SPredefType] => Tree.Apply(tpeName(c.tpe) + "Constant", treeifySeq(Seq(c.value))) + case ArithOp(l, r, code) => val args = treeifySeq(Seq(l, r)).toSeq :+ Tree.Apply("OpCode @@ ", treeifySeq(Seq(code))) Tree.Apply("ArithOp", args.iterator) diff --git a/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala b/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala index b1bd7957ed..d629d379a1 100644 --- a/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala +++ b/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala @@ -2,20 +2,25 @@ package sigmastate.helpers import java.math.BigInteger +import org.ergoplatform.settings.ErgoAlgos import org.ergoplatform.{Outputs, ErgoBox} -import org.scalatest.prop.PropertyChecks -import org.scalatest.{PropSpec, Matchers} +import scalan.RType +import scorex.crypto.authds.ADDigest +import scorex.crypto.hash.Digest32 +import scorex.util.ModifierId import sigmastate.Values._ import sigmastate.lang.Terms.MethodCall import sigmastate.serialization.OpCodes import sigmastate.utxo.SelectField import sigmastate._ +import sigmastate.eval._ +import sigmastate.utils.Helpers +import special.collection.CollType +import special.sigma.SigmaDslTesting import scala.collection.mutable.ArrayBuffer -class SigmaPPrintSpec extends PropSpec - with PropertyChecks - with Matchers { +class SigmaPPrintSpec extends SigmaDslTesting { property("typeName") { @@ -30,7 +35,8 @@ class SigmaPPrintSpec extends PropSpec property("Special cases") { def test(x: Any, expected: String) = { - SigmaPPrint(x).plainText shouldBe expected + val res = SigmaPPrint(x).plainText + res shouldBe expected } // type handlers @@ -39,6 +45,9 @@ class SigmaPPrintSpec extends PropSpec test(SCollectionType(SBoolean), "SBooleanArray") test(STuple(Vector(SBoolean, SInt)), "SPair(SBoolean, SInt)") + test(RType.BooleanType, "RType.BooleanType") + test(CollType(RType.ByteType), "CollType(RType.ByteType)") + // exception handlers test(new ArithmeticException("msg"), "new ArithmeticException(\"msg\")") @@ -53,9 +62,79 @@ class SigmaPPrintSpec extends PropSpec negative.toString(10) shouldBe "-10" test(negative, """new BigInteger("-a", 16)""") + test(ErgoAlgos.decodeUnsafe("00ffaa"), "ErgoAlgos.decodeUnsafe(\"00ffaa\")") + test(ErgoAlgos.decodeUnsafe("00ffaa"): Seq[Byte], "ErgoAlgos.decodeUnsafe(\"00ffaa\")") + + test(Array(10), "Array(10)") test(Array(10): Seq[Int], "Array(10)") test({val buf = ArrayBuffer.empty[Int]; buf += (10); buf}, "Seq(10)") + test(Helpers.decodeBytes("00ff"), "Helpers.decodeBytes(\"00ff\")") + val ge1 = "03358d53f01276211f92d0aefbd278805121d4ff6eb534b777af1ee8abae5b2056" + + test(Helpers.decodeGroupElement(ge1), s"""Helpers.decodeGroupElement("${ge1}")""") + test(Helpers.decodeECPoint(ge1), s"""Helpers.decodeECPoint("${ge1}")""") + val t1 = AvlTreeData( + ADDigest @@ ErgoAlgos.decodeUnsafe("000183807f66b301530120ff7fc6bd6601ff01ff7f7d2bedbbffff00187fe89094"), + AvlTreeFlags(false, true, true), + 1, + Some(1) + ) + test(t1, + """AvlTreeData( + | ADDigest @@ ( + | ErgoAlgos.decodeUnsafe("000183807f66b301530120ff7fc6bd6601ff01ff7f7d2bedbbffff00187fe89094") + | ), + | AvlTreeFlags(false, true, true), + | 1, + | Some(1) + |)""".stripMargin) + test( + new ErgoTree( + 16.toByte, + Vector(IntArrayConstant(Array(10, 20))), + Right(BoolToSigmaProp(TrueLeaf)) + ), + """new ErgoTree( + | 16.toByte, + | Vector(IntArrayConstant(Coll[Int](10, 20))), + | Right(BoolToSigmaProp(TrueLeaf)) + |)""".stripMargin) + test( + CostingBox( + false, + new ErgoBox( + 9223372036854775807L, + new ErgoTree(0.toByte, Vector(), Right(BoolToSigmaProp(FalseLeaf))), + Coll( + (Digest32 @@ (ErgoAlgos.decodeUnsafe("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001")), 10000000L) + ), + Map(), + ModifierId @@ ("bc80ffc00100d60101ffd3d3ab7f73800aff80487fff7fffbb010080ff7f0837"), + 0.toShort, + 1000000 + ) + ), + """CostingBox( + | false, + | new ErgoBox( + | 9223372036854775807L, + | new ErgoTree(0.toByte, Vector(), Right(BoolToSigmaProp(FalseLeaf))), + | Coll( + | ( + | Digest32 @@ ( + | ErgoAlgos.decodeUnsafe("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001") + | ), + | 10000000L + | ) + | ), + | Map(), + | ModifierId @@ ("bc80ffc00100d60101ffd3d3ab7f73800aff80487fff7fffbb010080ff7f0837"), + | 0.toShort, + | 1000000 + | ) + |)""".stripMargin + ) // additionalHandlers test(SGlobal, "SGlobal") test(SCollection, "SCollection") diff --git a/sigmastate/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala b/sigmastate/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala index 2fdcb553f6..c8f716aa1b 100644 --- a/sigmastate/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala @@ -68,7 +68,7 @@ class SigSerializerSpecification extends SigmaTestingCommons with ObjectGenerato } property("SigSerializer round trip") { - forAll { sb: SigmaBoolean => + forAll(configParams = MinSuccessful(100)) { sb: SigmaBoolean => val expr = sb.toSigmaProp val challenge = Array.fill(32)(Random.nextInt(100).toByte) diff --git a/sigmastate/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala b/sigmastate/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala index 3663a8b837..63c6435612 100644 --- a/sigmastate/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala +++ b/sigmastate/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala @@ -2,27 +2,27 @@ package sigmastate.serialization.generators import org.ergoplatform.ErgoBox._ import org.ergoplatform.SigmaConstants.MaxPropositionBytes -import org.ergoplatform.ErgoScriptPredef.{FalseProp, TrueProp} import org.ergoplatform.validation._ import org.ergoplatform._ -import org.scalacheck.Arbitrary.{arbOption, arbAnyVal, arbShort, arbitrary, arbUnit, arbString, arbInt, arbLong, arbBool, arbByte} -import org.scalacheck.Gen.frequency +import org.scalacheck.Arbitrary._ +import org.scalacheck.Gen.{choose, frequency} +import org.scalacheck.util.Buildable import org.scalacheck.{Arbitrary, Gen} import scalan.RType import scorex.crypto.authds.{ADDigest, ADKey} import scorex.crypto.hash.Digest32 import scorex.util.encode.{Base64, Base58} import scorex.util.{bytesToId, ModifierId} -import sigmastate.Values.{ShortConstant, LongConstant, StringConstant, BoxConstant, FuncValue, FalseLeaf, EvaluatedValue, TrueLeaf, TaggedAvlTree, TaggedLong, BigIntConstant, BlockValue, AvlTreeConstant, GetVarInt, SigmaPropConstant, CollectionConstant, ConstantPlaceholder, Value, SigmaPropValue, Tuple, IntConstant, ErgoTree, SigmaBoolean, TaggedBox, ByteConstant, TaggedInt, ValDef, GroupElementConstant, ValUse, TaggedVariable} +import sigmastate.Values._ import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.ProveDHTuple import sigmastate.eval.Extensions._ import sigmastate.eval.{CostingBox, SigmaDsl, _} import sigmastate.interpreter.CryptoConstants.EcPointType import sigmastate.interpreter.{ProverResult, ContextExtension, CryptoConstants} -import sigmastate.lang.TransformingSigmaBuilder.{mkModulo, mkExtractCreationInfo, mkExtractScriptBytes, mkFilter, mkPlus, mkTaggedVariable, mkSlice, mkFold, mkAtLeast, mkExtractBytesWithNoRef, mkExists, mkByteArrayToBigInt, mkForAll, mkDivide, mkSigmaOr, mkSigmaAnd, mkGT, mkGE, mkMapCollection, mkDeserializeRegister, mkExtractBytes, mkBoolToSigmaProp, mkNEQ, mkExtractAmount, mkMultiply, mkByteArrayToLong, mkConstant, mkExtractId, mkTuple, mkMax, mkLT, mkLE, mkDowncast, mkSizeOf, mkCollectionConstant, mkMin, mkDeserializeContext, mkEQ, mkAppend, mkMinus} +import sigmastate.lang.TransformingSigmaBuilder._ import sigmastate._ -import sigmastate.utxo.{ExtractBytesWithNoRef, OptionGet, ExtractRegisterAs, Append, ExtractScriptBytes, ExtractCreationInfo, GetVar, ExtractId, MapCollection, ExtractAmount, ForAll, ByIndex, OptionGetOrElse, OptionIsDefined, DeserializeContext, Exists, DeserializeRegister, Transformer, Fold, Slice, SizeOf, Filter, ExtractBytes} +import sigmastate.utxo._ import special.collection.Coll import special.sigma._ @@ -306,10 +306,12 @@ trait ObjectGenerators extends TypeGenerators with ValidationSpecification with lazy val modifierIdBytesGen: Gen[Coll[Byte]] = Gen.listOfN(32, arbByte.arbitrary) .map(id => bytesToId(id.toArray).toBytes.toColl) + val MaxTokens = 10 + val ergoBoxGen: Gen[ErgoBox] = for { tId <- modifierIdGen boxId <- unsignedShortGen - tokensCount <- Gen.chooseNum[Int](0, 20) + tokensCount <- Gen.chooseNum[Int](0, MaxTokens) tokens <- Gen.sequence(additionalTokensGen(tokensCount)).map(_.asScala.map(_._1)) candidate <- ergoBoxCandidateGen(tokens) } yield candidate.toBox(tId, boxId) @@ -319,17 +321,24 @@ trait ObjectGenerators extends TypeGenerators with ValidationSpecification with regs <- Gen.sequence(additionalRegistersGen(regNum)) } yield regs.asScala.toMap + def arrayOfN[T](n: Int, g: Gen[T])(implicit evb: Buildable[T,Array[T]]): Gen[Array[T]] = { + Gen.containerOfN[Array, T](n, g) + } + def ergoBoxCandidateGen(availableTokens: Seq[Digest32]): Gen[ErgoBoxCandidate] = for { l <- arbLong.arbitrary b <- ergoTreeGen.filter(t => t.bytes.length < MaxPropositionBytes.value) ar <- additionalRegistersGen - tokensCount <- Gen.chooseNum[Int](0, 20) - tokens <- if(availableTokens.nonEmpty) { - Gen.listOfN(tokensCount, Gen.oneOf(availableTokens)) - } else { - Gen.oneOf(Seq(List[Digest32]())) - } - tokenAmounts <- Gen.listOfN(tokensCount, Gen.oneOf(1, 500, 20000, 10000000, Long.MaxValue)) + tokens <- + if(availableTokens.nonEmpty) { + for { + tokensCount <- Gen.chooseNum[Int](0, MaxTokens) + ts <- arrayOfN(tokensCount, Gen.oneOf(availableTokens).map(_.toColl)) + } yield ts.distinct.map(coll => Digest32 @@ coll.toArray) + } else { + Gen.oneOf(Seq(Array[Digest32]())) + } + tokenAmounts <- arrayOfN(tokens.length, Gen.oneOf(1, 500, 20000, 10000000, Long.MaxValue)) creationHeight <- heightGen } yield new ErgoBoxCandidate(l, b, creationHeight, tokens.toColl.zip(tokenAmounts.toColl), ar) @@ -342,8 +351,8 @@ trait ObjectGenerators extends TypeGenerators with ValidationSpecification with val tokenIdGen: Gen[Digest32] = digest32Gen val tokensGen: Gen[Seq[Digest32]] = for { - count <- Gen.chooseNum(10, 50) - tokens <- Gen.listOfN(count, tokenIdGen) + count <- Gen.chooseNum(1, MaxTokens) + tokens <- arrayOfN(count, tokenIdGen) } yield tokens val digest32CollGen: Gen[Digest32Coll] = digest32Gen.map(Digest32Coll @@ _.toColl) @@ -661,12 +670,6 @@ trait ObjectGenerators extends TypeGenerators with ValidationSpecification with ErgoTree.withoutSegregation)) } yield treeBuilder(prop) - val headerGen: Gen[Header] = for { - stateRoot <- avlTreeGen - parentId <- modifierIdBytesGen - header <- headerGen(stateRoot, parentId) - } yield header - def headerGen(stateRoot: AvlTree, parentId: Coll[Byte]): Gen[Header] = for { id <- modifierIdBytesGen version <- arbByte.arbitrary @@ -684,19 +687,23 @@ trait ObjectGenerators extends TypeGenerators with ValidationSpecification with } yield CHeader(id, version, parentId, adProofsRoot, stateRoot, transactionRoot, timestamp, nBits, height, extensionRoot, minerPk, powOnetimePk, powNonce, powDistance, votes) + val headerGen: Gen[Header] = for { + stateRoot <- avlTreeGen + parentId <- modifierIdBytesGen + header <- headerGen(stateRoot, parentId) + } yield header + + implicit val arbHeader = Arbitrary(headerGen) + + val MaxHeaders = 2 def headersGen(stateRoot: AvlTree): Gen[Seq[Header]] = for { - size <- Gen.chooseNum(0, 10) + size <- Gen.chooseNum(0, MaxHeaders) } yield if (size == 0) Seq() else (0 to size) .foldLeft(List[Header](headerGen(stateRoot, modifierIdBytesGen.sample.get).sample.get)) { (h, _) => h :+ headerGen(stateRoot, h.last.id).sample.get }.reverse - val preHeaderGen: Gen[PreHeader] = for { - parentId <- modifierIdBytesGen - preHeader <- preHeaderGen(parentId) - } yield preHeader - def preHeaderGen(parentId: Coll[Byte]): Gen[PreHeader] = for { version <- arbByte.arbitrary timestamp <- arbLong.arbitrary @@ -706,6 +713,13 @@ trait ObjectGenerators extends TypeGenerators with ValidationSpecification with votes <- minerVotesGen } yield CPreHeader(version, parentId, timestamp, nBits, height, minerPk, votes) + val preHeaderGen: Gen[PreHeader] = for { + parentId <- modifierIdBytesGen + preHeader <- preHeaderGen(parentId) + } yield preHeader + + implicit val arbPreHeader = Arbitrary(preHeaderGen) + val ergoLikeTransactionGen: Gen[ErgoLikeTransaction] = for { inputBoxesIds <- Gen.nonEmptyListOf(boxIdGen) dataInputBoxIds <- Gen.listOf(boxIdGen) @@ -714,9 +728,9 @@ trait ObjectGenerators extends TypeGenerators with ValidationSpecification with def ergoLikeTransactionGen(inputBoxesIds: Seq[BoxId], dataInputBoxIds: Seq[BoxId]): Gen[ErgoLikeTransaction] = for { tokens <- tokensGen - outputsCount <- Gen.chooseNum(50, 200) - outputCandidates <- Gen.listOfN(outputsCount, ergoBoxCandidateGen(tokens)) - proofs <- Gen.listOfN(inputBoxesIds.length, serializedProverResultGen) + outputsCount <- Gen.chooseNum(1, MaxOutputBoxes) + outputCandidates <- arrayOfN(outputsCount, ergoBoxCandidateGen(tokens)) + proofs <- arrayOfN(inputBoxesIds.length, serializedProverResultGen) } yield new ErgoLikeTransaction( inputs = inputBoxesIds.zip(proofs).map(t => Input(t._1, t._2)).toIndexedSeq, dataInputs = dataInputBoxIds.map(DataInput).toIndexedSeq, @@ -731,9 +745,9 @@ trait ObjectGenerators extends TypeGenerators with ValidationSpecification with def unsignedErgoLikeTransactionGen(inputBoxesIds: Seq[BoxId], dataInputBoxIds: Seq[BoxId]): Gen[UnsignedErgoLikeTransaction] = for { tokens <- tokensGen - outputsCount <- Gen.chooseNum(50, 200) - outputCandidates <- Gen.listOfN(outputsCount, ergoBoxCandidateGen(tokens)) - contextExtensions <- Gen.listOfN(inputBoxesIds.length, contextExtensionGen) + outputsCount <- Gen.chooseNum(1, MaxOutputBoxes) + outputCandidates <- arrayOfN(outputsCount, ergoBoxCandidateGen(tokens)) + contextExtensions <- arrayOfN(inputBoxesIds.length, contextExtensionGen) } yield new UnsignedErgoLikeTransaction( inputs = inputBoxesIds.zip(contextExtensions).map(t => new UnsignedInput(t._1, t._2)).toIndexedSeq, dataInputs = dataInputBoxIds.map(DataInput).toIndexedSeq, @@ -743,12 +757,16 @@ trait ObjectGenerators extends TypeGenerators with ValidationSpecification with val ergoLikeTransactionTemplateGen: Gen[ErgoLikeTransactionTemplate[_ <: UnsignedInput]] = Gen.oneOf(unsignedErgoLikeTransactionGen, ergoLikeTransactionGen) + val MaxDataBoxes = 5 + val MaxInputBoxes = 5 + val MaxOutputBoxes = 100 + val ergoLikeContextGen: Gen[ErgoLikeContext] = for { stateRoot <- avlTreeGen headers <- headersGen(stateRoot) preHeader <- preHeaderGen(headers.headOption.map(_.id).getOrElse(modifierIdBytesGen.sample.get)) - dataBoxes <- Gen.listOf(ergoBoxGen) - boxesToSpend <- Gen.nonEmptyListOf(ergoBoxGen) + dataBoxes <- choose(0, MaxDataBoxes).flatMap(arrayOfN(_, ergoBoxGen)) + boxesToSpend <- choose(1, MaxInputBoxes).flatMap(arrayOfN(_, ergoBoxGen)).suchThat(_.size > 0) extension <- contextExtensionGen costLimit <- arbLong.arbitrary initCost <- arbLong.arbitrary diff --git a/sigmastate/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala b/sigmastate/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala index ceb58bd3fe..8521703809 100644 --- a/sigmastate/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala +++ b/sigmastate/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala @@ -30,7 +30,7 @@ trait TypeGenerators { Gen.oneOf[SPredefType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp, SUnit, SBox, SAvlTree) implicit val arbPredefType = Arbitrary(predefTypeGen) - implicit def getToArbitrary[T: Gen]: Arbitrary[T] = Arbitrary(implicitly[Gen[T]]) + implicit def genToArbitrary[T: Gen]: Arbitrary[T] = Arbitrary(implicitly[Gen[T]]) def sTupleGen(min: Int, max: Int): Gen[STuple] = for { length <- Gen.chooseNum(min, max) diff --git a/sigmastate/src/test/scala/special/sigma/SigmaDslSpec.scala b/sigmastate/src/test/scala/special/sigma/SigmaDslSpec.scala index 3348b8f141..52c36afa65 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaDslSpec.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaDslSpec.scala @@ -4,27 +4,38 @@ import java.math.BigInteger import org.ergoplatform.ErgoScriptPredef.TrueProp import org.ergoplatform._ -import org.scalacheck.{Gen, Arbitrary} +import org.ergoplatform.settings.ErgoAlgos +import org.scalacheck.{Arbitrary, Gen} +import org.scalatest.prop.{PropertyChecks, TableFor2} +import org.scalatest.{PropSpec, Matchers, Tag} import scalan.{ExactNumeric, RType} import scorex.crypto.authds.avltree.batch._ -import scorex.crypto.authds.{ADKey, ADValue} +import scorex.crypto.authds.{ADDigest, ADKey, ADValue} import scorex.crypto.hash.{Digest32, Blake2b256} import scalan.util.Extensions._ import sigma.util.Extensions._ import sigmastate.SCollection._ import sigmastate.Values.IntConstant import sigmastate._ +import sigmastate.basics.DLogProtocol._ import sigmastate.Values._ import sigmastate.lang.Terms.Apply import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.lang.Terms.MethodCall import sigmastate.utxo._ -import special.collection.Coll +import special.collection._ import sigmastate.serialization.OpCodes.OpCode +import sigmastate.utils.Helpers -import scala.util.{Success, Failure, Try} +import scala.reflect.ClassTag +import scala.util.{DynamicVariable, Success, Failure, Try} +import OrderingOps._ +import scorex.util.ModifierId +import sigmastate.basics.{ProveDHTuple, DLogProtocol} +import sigmastate.helpers.SigmaPPrint +import scala.math.Ordering /** This suite tests every method of every SigmaDsl type to be equivalent to * the evaluation of the corresponding ErgoScript operation */ @@ -32,31 +43,6 @@ class SigmaDslSpec extends SigmaDslTesting { suite => override implicit val generatorDrivenConfig = PropertyCheckConfiguration(minSuccessful = 30) - val PrintTestCasesDefault: Boolean = false - - def testCases[A: Ordering,B](cases: Seq[(A, Try[B])], f: FeatureTest[A,B], printTestCases: Boolean = PrintTestCasesDefault): Unit = { - val table = Table(("x", "y"), cases.sortBy(_._1):_*) - forAll(table) { (x: A, expectedRes: Try[B]) => - val res = f.checkEquality(x, printTestCases) - - // TODO HF: remove this if once newImpl is implemented - if (f.featureType == ExistingFeature) { - (res, expectedRes) match { - case (Failure(exception), Failure(expectedException)) => - exception.getClass shouldBe expectedException.getClass - case _ => - res shouldBe expectedRes - } - } - } - } - - def test[A: Arbitrary,B](f: FeatureTest[A,B], printTestCases: Boolean = PrintTestCasesDefault): Unit = { - forAll { (x: A) => - f.checkEquality(x, printTestCases) - } - } - ///===================================================== /// Boolean type operations ///----------------------------------------------------- @@ -563,77 +549,156 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Short methods equivalence") { - val toByte = existingFeature((x: Short) => x.toByteExact, - "{ (x: Short) => x.toByte }", - FuncValue(Vector((1, SShort)), Downcast(ValUse(1, SShort), SByte))) - val toShort = existingFeature((x: Short) => x.toShort, - "{ (x: Short) => x.toShort }", - FuncValue(Vector((1, SShort)), ValUse(1, SShort))) - val toInt = existingFeature((x: Short) => x.toInt, - "{ (x: Short) => x.toInt }", - FuncValue(Vector((1, SShort)), Upcast(ValUse(1, SShort), SInt))) - val toLong = existingFeature((x: Short) => x.toLong, - "{ (x: Short) => x.toLong }", - FuncValue(Vector((1, SShort)), Upcast(ValUse(1, SShort), SLong))) - val toBigInt = existingFeature((x: Short) => x.toBigInt, - "{ (x: Short) => x.toBigInt }", - FuncValue(Vector((1, SShort)), Upcast(ValUse(1, SShort), SBigInt))) + testCases( + Seq( + (Short.MinValue, Failure(new ArithmeticException("Byte overflow"))), + (-21626.toShort, Failure(new ArithmeticException("Byte overflow"))), + (Byte.MinValue.toShort, Success(Byte.MinValue)), + (-1.toShort, Success(-1.toByte)), + (0.toShort, Success(0.toByte)), + (1.toShort, Success(1.toByte)), + (Byte.MaxValue.toShort, Success(Byte.MaxValue)), + (11768.toShort, Failure(new ArithmeticException("Byte overflow"))), + (Short.MaxValue, Failure(new ArithmeticException("Byte overflow"))) + ), + existingFeature((x: Short) => x.toByteExact, + "{ (x: Short) => x.toByte }", + FuncValue(Vector((1, SShort)), Downcast(ValUse(1, SShort), SByte)))) - lazy val toBytes = newFeature((x: Short) => x.toBytes, "{ (x: Short) => x.toBytes }") - lazy val toBits = newFeature((x: Short) => x.toBits, "{ (x: Short) => x.toBits }") - 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) }") + testCases( + Seq( + (-32768.toShort, Success(-32768.toShort)), + (-27798.toShort, Success(-27798.toShort)), + (-1.toShort, Success(-1.toShort)), + (0.toShort, Success(0.toShort)), + (1.toShort, Success(1.toShort)), + (27929.toShort, Success(27929.toShort)), + (32767.toShort, Success(32767.toShort)) + ), + existingFeature((x: Short) => x.toShort, + "{ (x: Short) => x.toShort }", + FuncValue(Vector((1, SShort)), ValUse(1, SShort)))) + + testCases( + Seq( + (-32768.toShort, Success(-32768)), + (-21064.toShort, Success(-21064)), + (-1.toShort, Success(-1)), + (0.toShort, Success(0)), + (1.toShort, Success(1)), + (18388.toShort, Success(18388)), + (32767.toShort, Success(32767)) + ), + existingFeature((x: Short) => x.toInt, + "{ (x: Short) => x.toInt }", + FuncValue(Vector((1, SShort)), Upcast(ValUse(1, SShort), SInt)))) + + testCases( + Seq( + (-32768.toShort, Success(-32768L)), + (-23408.toShort, Success(-23408L)), + (-1.toShort, Success(-1L)), + (0.toShort, Success(0L)), + (1.toShort, Success(1L)), + (23318.toShort, Success(23318L)), + (32767.toShort, Success(32767L)) + ), + existingFeature((x: Short) => x.toLong, + "{ (x: Short) => x.toLong }", + FuncValue(Vector((1, SShort)), Upcast(ValUse(1, SShort), SLong)))) + + testCases( + Seq( + (-32768.toShort, Success(CBigInt(new BigInteger("-8000", 16)))), + (-26248.toShort, Success(CBigInt(new BigInteger("-6688", 16)))), + (-1.toShort, Success(CBigInt(new BigInteger("-1", 16)))), + (0.toShort, Success(CBigInt(new BigInteger("0", 16)))), + (1.toShort, Success(CBigInt(new BigInteger("1", 16)))), + (22845.toShort, Success(CBigInt(new BigInteger("593d", 16)))), + (32767.toShort, Success(CBigInt(new BigInteger("7fff", 16)))) + ), + existingFeature((x: Short) => x.toBigInt, + "{ (x: Short) => x.toBigInt }", + FuncValue(Vector((1, SShort)), Upcast(ValUse(1, SShort), SBigInt)))) val n = ExactNumeric.ShortIsExactNumeric - lazy val arithOps = existingFeature( - { (x: (Short, Short)) => - 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).toShortExact - val mod = (a % b).toShortExact - (plus, (minus, (mul, (div, mod)))) - }, - """{ (x: (Short, Short)) => - | 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(SShort, SShort)))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[ShortValue](ValUse(1, STuple(Vector(SShort, SShort))), 1.toByte) - ), - ValDef( - 4, - List(), - SelectField.typed[ShortValue](ValUse(1, STuple(Vector(SShort, SShort))), 2.toByte) - ) - ), - Tuple( + testCases( + Seq( + ((-32768.toShort, 1.toShort), Failure(new ArithmeticException("Short overflow"))), + ((-32768.toShort, 4006.toShort), Failure(new ArithmeticException("Short overflow"))), + ((-21384.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), + ((-19027.toShort, 6073.toShort), Failure(new ArithmeticException("Short overflow"))), + ((-16800.toShort, 32767.toShort), Failure(new ArithmeticException("Short overflow"))), + ((-1.toShort, -30005.toShort), Success((-30006.toShort, (30004.toShort, (30005.toShort, (0.toShort, -1.toShort)))))), + ((-1.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), + ((0.toShort, -1.toShort), Success((-1.toShort, (1.toShort, (0.toShort, (0.toShort, 0.toShort)))))), + ((0.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), + ((0.toShort, 1.toShort), Success((1.toShort, (-1.toShort, (0.toShort, (0.toShort, 0.toShort)))))), + ((0.toShort, 25105.toShort), Success((25105.toShort, (-25105.toShort, (0.toShort, (0.toShort, 0.toShort)))))), + ((1.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), + ((1.toShort, -1.toShort), Success((0.toShort, (2.toShort, (-1.toShort, (-1.toShort, 0.toShort)))))), + ((1.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), + ((605.toShort, 7698.toShort), Failure(new ArithmeticException("Short overflow"))), + ((5094.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), + ((5350.toShort, -1.toShort), Success((5349.toShort, (5351.toShort, (-5350.toShort, (-5350.toShort, 0.toShort)))))), + ((8115.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), + ((14217.toShort, 32767.toShort), Failure(new ArithmeticException("Short overflow"))), + ((16223.toShort, -11686.toShort), Failure(new ArithmeticException("Short overflow"))), + ((16989.toShort, 1.toShort), Success((16990.toShort, (16988.toShort, (16989.toShort, (16989.toShort, 0.toShort)))))), + ((20397.toShort, -4450.toShort), Failure(new ArithmeticException("Short overflow"))), + ((20488.toShort, 1.toShort), Success((20489.toShort, (20487.toShort, (20488.toShort, (20488.toShort, 0.toShort)))))), + ((32767.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), + ((32767.toShort, -13423.toShort), Failure(new ArithmeticException("Short overflow"))), + ((32767.toShort, 32767.toShort), Failure(new ArithmeticException("Short overflow"))) + ), + existingFeature( + { (x: (Short, Short)) => + 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).toShortExact + val mod = (a % b).toShortExact + (plus, (minus, (mul, (div, mod)))) + }, + """{ (x: (Short, Short)) => + | 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(SShort, SShort)))), + BlockValue( Vector( - ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-102.toByte)), - Tuple( - Vector( - ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-103.toByte)), - Tuple( - Vector( - ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-100.toByte)), - Tuple( - Vector( - ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-99.toByte)), - ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-98.toByte)) + ValDef( + 3, + List(), + SelectField.typed[ShortValue](ValUse(1, STuple(Vector(SShort, SShort))), 1.toByte) + ), + ValDef( + 4, + List(), + SelectField.typed[ShortValue](ValUse(1, STuple(Vector(SShort, SShort))), 2.toByte) + ) + ), + Tuple( + Vector( + ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-102.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-103.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-100.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-99.toByte)), + ArithOp(ValUse(3, SShort), ValUse(4, SShort), OpCode @@ (-98.toByte)) + ) ) ) ) @@ -643,8 +708,16 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) ) - ) - ) + )) + } + + property("Short methods equivalence (new features)") { + lazy val toBytes = newFeature((x: Short) => x.toBytes, "{ (x: Short) => x.toBytes }") + lazy val toBits = newFeature((x: Short) => x.toBits, "{ (x: Short) => x.toBits }") + 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 }, @@ -655,37 +728,116 @@ class SigmaDslSpec extends SigmaDslTesting { suite => "{ (x: (Short, Short)) => x._1 & x._2 }") forAll { x: Short => - Seq(toByte, toShort, toInt, toLong, toBigInt, toBytes, toBits, toAbs).foreach(_.checkEquality(x)) + Seq(toBytes, toBits, toAbs).foreach(_.checkEquality(x)) } forAll { x: (Short, Short) => - Seq(compareTo, arithOps, bitOr, bitAnd).foreach(_.checkEquality(x)) + Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) } } property("Int methods equivalence") { - val toByte = existingFeature((x: Int) => x.toByteExact, - "{ (x: Int) => x.toByte }", - FuncValue(Vector((1, SInt)), Downcast(ValUse(1, SInt), SByte))) - val toShort = existingFeature((x: Int) => x.toShortExact, - "{ (x: Int) => x.toShort }", - FuncValue(Vector((1, SInt)), Downcast(ValUse(1, SInt), SShort))) - val toInt = existingFeature((x: Int) => x.toInt, - "{ (x: Int) => x.toInt }", - FuncValue(Vector((1, SInt)), ValUse(1, SInt))) - val toLong = existingFeature((x: Int) => x.toLong, - "{ (x: Int) => x.toLong }", - FuncValue(Vector((1, SInt)), Upcast(ValUse(1, SInt), SLong))) - val toBigInt = existingFeature((x: Int) => x.toBigInt, - "{ (x: Int) => x.toBigInt }", - FuncValue(Vector((1, SInt)), Upcast(ValUse(1, SInt), SBigInt))) - lazy val toBytes = newFeature((x: Int) => x.toBytes, "{ (x: Int) => x.toBytes }") - lazy val toBits = newFeature((x: Int) => x.toBits, "{ (x: Int) => x.toBits }") - 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) }") + testCases( + Seq( + (Int.MinValue, Failure(new ArithmeticException("Byte overflow"))), + (-2014394379, Failure(new ArithmeticException("Byte overflow"))), + (Byte.MinValue.toInt, Success(Byte.MinValue)), + (-1, Success(-1.toByte)), + (0, Success(0.toByte)), + (1, Success(1.toByte)), + (Byte.MaxValue.toInt, Success(Byte.MaxValue)), + (181686429, Failure(new ArithmeticException("Byte overflow"))), + (Int.MaxValue, Failure(new ArithmeticException("Byte overflow"))) + ), + existingFeature((x: Int) => x.toByteExact, + "{ (x: Int) => x.toByte }", + FuncValue(Vector((1, SInt)), Downcast(ValUse(1, SInt), SByte)))) + + testCases( + Seq( + (Int.MinValue, Failure(new ArithmeticException("Short overflow"))), + (Short.MinValue - 1, Failure(new ArithmeticException("Short overflow"))), + (Short.MinValue.toInt, Success(Short.MinValue)), + (-1, Success(-1.toShort)), + (0, Success(0.toShort)), + (1, Success(1.toShort)), + (Short.MaxValue.toInt, Success(Short.MaxValue)), + (Short.MaxValue + 1, Failure(new ArithmeticException("Short overflow"))), + (Int.MaxValue, Failure(new ArithmeticException("Short overflow"))) + ), + existingFeature((x: Int) => x.toShortExact, + "{ (x: Int) => x.toShort }", + FuncValue(Vector((1, SInt)), Downcast(ValUse(1, SInt), SShort)))) + + testCases( + Seq( + (Int.MinValue, Success(Int.MinValue)), + (-1, Success(-1)), + (0, Success(0)), + (1, Success(1)), + (Int.MaxValue, Success(Int.MaxValue)) + ), + existingFeature((x: Int) => x.toInt, + "{ (x: Int) => x.toInt }", + FuncValue(Vector((1, SInt)), ValUse(1, SInt)))) + + testCases( + Seq( + (Int.MinValue, Success(Int.MinValue.toLong)), + (-1, Success(-1L)), + (0, Success(0L)), + (1, Success(1L)), + (Int.MaxValue, Success(Int.MaxValue.toLong)) + ), + existingFeature((x: Int) => x.toLong, + "{ (x: Int) => x.toLong }", + FuncValue(Vector((1, SInt)), Upcast(ValUse(1, SInt), SLong)))) + + testCases( + Seq( + (Int.MinValue, Success(CBigInt(new BigInteger("-80000000", 16)))), + (-1937187314, Success(CBigInt(new BigInteger("-737721f2", 16)))), + (-1, Success(CBigInt(new BigInteger("-1", 16)))), + (0, Success(CBigInt(new BigInteger("0", 16)))), + (1, Success(CBigInt(new BigInteger("1", 16)))), + (1542171288, Success(CBigInt(new BigInteger("5bebaa98", 16)))), + (Int.MaxValue, Success(CBigInt(new BigInteger("7fffffff", 16)))) + ), + existingFeature((x: Int) => x.toBigInt, + "{ (x: Int) => x.toBigInt }", + FuncValue(Vector((1, SInt)), Upcast(ValUse(1, SInt), SBigInt)))) val n = ExactNumeric.IntIsExactNumeric - lazy val arithOps = existingFeature( + testCases( + Seq( + ((Int.MinValue, 449583993), Failure(new ArithmeticException("integer overflow"))), + ((-1589633733, 2147483647), Failure(new ArithmeticException("integer overflow"))), + ((-1585471506, -1), Success((-1585471507, (-1585471505, (1585471506, (1585471506, 0)))))), + ((-1569005179, 1230236634), Failure(new ArithmeticException("integer overflow"))), + ((-1493733356, -1319619597), Failure(new ArithmeticException("integer overflow"))), + ((-1100263120, -880052091), Failure(new ArithmeticException("integer overflow"))), + ((-1055955857, 309147303), Failure(new ArithmeticException("integer overflow"))), + ((-569807371, 0), Failure(new ArithmeticException("/ by zero"))), + ((-522264843, 2147483647), Failure(new ArithmeticException("integer overflow"))), + ((-109552389, 0), Failure(new ArithmeticException("/ by zero"))), + ((-1, -2147483648), Failure(new ArithmeticException("integer overflow"))), + ((-1, -1), Success((-2, (0, (1, (1, 0)))))), + ((-1, 0), Failure(new ArithmeticException("/ by zero"))), + ((0, -2147483648), Failure(new ArithmeticException("integer overflow"))), + ((1, -1525049432), Success((-1525049431, (1525049433, (-1525049432, (0, 1)))))), + ((1, 0), Failure(new ArithmeticException("/ by zero"))), + ((1, 805353746), Success((805353747, (-805353745, (805353746, (0, 1)))))), + ((1, 2147483647), Failure(new ArithmeticException("integer overflow"))), + ((475797978, 0), Failure(new ArithmeticException("/ by zero"))), + ((782343922, -1448560539), Failure(new ArithmeticException("integer overflow"))), + ((928769361, 542647292), Failure(new ArithmeticException("integer overflow"))), + ((1568062151, 0), Failure(new ArithmeticException("/ by zero"))), + ((1698252401, -1), Success((1698252400, (1698252402, (-1698252401, (-1698252401, 0)))))), + ((1949795740, -1575667037), Failure(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, -1), Failure(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, 1), Failure(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, 1738276576), Failure(new ArithmeticException("integer overflow"))) + ), + existingFeature( { (x: (Int, Int)) => val a = x._1; val b = x._2 val plus = n.plus(a, b) @@ -741,7 +893,15 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) ) - )) + ))) + } + + property("Int methods equivalence (new features)") { + lazy val toBytes = newFeature((x: Int) => x.toBytes, "{ (x: Int) => x.toBytes }") + lazy val toBits = newFeature((x: Int) => x.toBits, "{ (x: Int) => x.toBits }") + 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 }, @@ -752,140 +912,119 @@ class SigmaDslSpec extends SigmaDslTesting { suite => "{ (x: (Int, Int)) => x._1 & x._2 }") forAll { x: Int => - Seq(toByte, toShort, toInt, toLong, toBigInt, toBytes, toBits, toAbs).foreach(_.checkEquality(x)) + Seq(toBytes, toBits, toAbs).foreach(_.checkEquality(x)) } forAll { x: (Int, Int) => - Seq(compareTo, arithOps, bitOr, bitAnd).foreach(_.checkEquality(x)) + Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) } } property("Long methods equivalence") { - val toByte = existingFeature((x: Long) => x.toByteExact, - "{ (x: Long) => x.toByte }", - FuncValue(Vector((1, SLong)), Downcast(ValUse(1, SLong), SByte))) - val toShort = existingFeature((x: Long) => x.toShortExact, - "{ (x: Long) => x.toShort }", - FuncValue(Vector((1, SLong)), Downcast(ValUse(1, SLong), SShort))) - val toInt = existingFeature((x: Long) => x.toIntExact, - "{ (x: Long) => x.toInt }", - FuncValue(Vector((1, SLong)), Downcast(ValUse(1, SLong), SInt))) - val toLong = existingFeature((x: Long) => x.toLong, - "{ (x: Long) => x.toLong }", - FuncValue(Vector((1, SLong)), ValUse(1, SLong))) - val toBigInt = existingFeature((x: Long) => x.toBigInt, - "{ (x: Long) => x.toBigInt }", - FuncValue(Vector((1, SLong)), Upcast(ValUse(1, SLong), SBigInt))) - lazy val toBytes = newFeature((x: Long) => x.toBytes, "{ (x: Long) => x.toBytes }") - lazy val toBits = newFeature((x: Long) => x.toBits, "{ (x: Long) => x.toBits }") - 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) }") - - val n = ExactNumeric.LongIsExactNumeric - lazy val arithOps = 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( - 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)) - ) - ) - ) - ) - ) - ) - ) - ) - ) - )) - - 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(toByte, toShort, toInt, toLong, toBigInt, toBytes, toBits, toAbs).foreach(_.checkEquality(x)) - } - forAll { x: (Long, Long) => - Seq(compareTo, arithOps, bitOr, bitAnd).foreach(_.checkEquality(x)) - } - } - - property("BigInt methods equivalence") { - 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))) + testCases( + Seq( + (Long.MinValue, Failure(new ArithmeticException("Byte overflow"))), + (Byte.MinValue.toLong - 1, Failure(new ArithmeticException("Byte overflow"))), + (Byte.MinValue.toLong, Success(Byte.MinValue)), + (-1L, Success(-1.toByte)), + (0L, Success(0.toByte)), + (1L, Success(1.toByte)), + (Byte.MaxValue.toLong, Success(Byte.MaxValue)), + (Byte.MaxValue.toLong + 1, Failure(new ArithmeticException("Byte overflow"))), + (Long.MinValue, Failure(new ArithmeticException("Byte overflow"))) + ), + existingFeature((x: Long) => x.toByteExact, + "{ (x: Long) => x.toByte }", + FuncValue(Vector((1, SLong)), Downcast(ValUse(1, SLong), SByte)))) - val toInt = newFeature((x: BigInt) => x.toInt, - "{ (x: BigInt) => x.toInt }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SInt))) + testCases( + Seq( + (Long.MinValue, Failure(new ArithmeticException("Short overflow"))), + (Short.MinValue.toLong - 1, Failure(new ArithmeticException("Short overflow"))), + (Short.MinValue.toLong, Success(Short.MinValue)), + (-1L, Success(-1.toShort)), + (0L, Success(0.toShort)), + (1L, Success(1.toShort)), + (Short.MaxValue.toLong, Success(Short.MaxValue)), + (Short.MaxValue.toLong + 1, Failure(new ArithmeticException("Short overflow"))), + (Long.MinValue, Failure(new ArithmeticException("Short overflow"))) + ), + existingFeature((x: Long) => x.toShortExact, + "{ (x: Long) => x.toShort }", + FuncValue(Vector((1, SLong)), Downcast(ValUse(1, SLong), SShort)))) - val toLong = newFeature((x: BigInt) => x.toLong, - "{ (x: BigInt) => x.toLong }", - FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SLong))) + testCases( + Seq( + (Long.MinValue, Failure(new ArithmeticException("Int overflow"))), + (Int.MinValue.toLong - 1, Failure(new ArithmeticException("Int overflow"))), + (Int.MinValue.toLong, Success(Int.MinValue)), + (-1L, Success(-1.toInt)), + (0L, Success(0.toInt)), + (1L, Success(1.toInt)), + (Int.MaxValue.toLong, Success(Int.MaxValue)), + (Int.MaxValue.toLong + 1, Failure(new ArithmeticException("Int overflow"))), + (Long.MinValue, Failure(new ArithmeticException("Int overflow"))) + ), + existingFeature((x: Long) => x.toIntExact, + "{ (x: Long) => x.toInt }", + FuncValue(Vector((1, SLong)), Downcast(ValUse(1, SLong), SInt)))) - val toBigInt = existingFeature((x: BigInt) => x, - "{ (x: BigInt) => x.toBigInt }", - FuncValue(Vector((1, SBigInt)), ValUse(1, SBigInt))) + testCases( + Seq( + (Long.MinValue, Success(Long.MinValue)), + (-1L, Success(-1L)), + (0L, Success(0L)), + (1L, Success(1L)), + (Long.MaxValue, Success(Long.MaxValue)) + ), + existingFeature((x: Long) => x.toLong, + "{ (x: Long) => x.toLong }", + FuncValue(Vector((1, SLong)), ValUse(1, SLong)))) - lazy val toBytes = newFeature((x: BigInt) => x.toBytes, "{ (x: BigInt) => x.toBytes }") - lazy val toBits = newFeature((x: BigInt) => x.toBits, "{ (x: BigInt) => x.toBits }") - 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) }") + testCases( + Seq( + (Long.MinValue, Success(CBigInt(new BigInteger("-8000000000000000", 16)))), + (-1074651039980347209L, Success(CBigInt(new BigInteger("-ee9ed6d57885f49", 16)))), + (-1L, Success(CBigInt(new BigInteger("-1", 16)))), + (0L, Success(CBigInt(new BigInteger("0", 16)))), + (1L, Success(CBigInt(new BigInteger("1", 16)))), + (1542942726564696512L, Success(CBigInt(new BigInteger("1569a23c25a951c0", 16)))), + (Long.MaxValue, Success(CBigInt(new BigInteger("7fffffffffffffff", 16)))) + ), + existingFeature((x: Long) => x.toBigInt, + "{ (x: Long) => x.toBigInt }", + FuncValue(Vector((1, SLong)), Upcast(ValUse(1, SLong), SBigInt)))) - val n = NumericOps.BigIntIsExactNumeric - lazy val arithOps = existingFeature( - { (x: (BigInt, BigInt)) => + val n = ExactNumeric.LongIsExactNumeric + testCases( + Seq( + ((Long.MinValue, -4677100190307931395L), Failure(new ArithmeticException("long overflow"))), + ((Long.MinValue, -1L), Failure(new ArithmeticException("long overflow"))), + ((Long.MinValue, 1L), Failure(new ArithmeticException("long overflow"))), + ((-9223372036854775808L, 0L), Failure(new ArithmeticException("/ by zero"))), + ((-5828066432064138816L, 9105034716270510411L), Failure(new ArithmeticException("long overflow"))), + ((-4564956247298949325L, -1L), Success( + (-4564956247298949326L, (-4564956247298949324L, (4564956247298949325L, (4564956247298949325L, 0L)))) + )), + ((-1499553565058783253L, -3237683216870282569L), Failure(new ArithmeticException("long overflow"))), + ((-1368457031689886112L, 9223372036854775807L), Failure(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), Failure(new ArithmeticException("/ by zero"))), + ((0L, 2112386634269044172L), Success((2112386634269044172L, (-2112386634269044172L, (0L, (0L, 0L)))))), + ((2254604056782701370L, -5878231674026236574L), Failure(new ArithmeticException("long overflow"))), + ((2903872550238813643L, 1L), Success( + (2903872550238813644L, (2903872550238813642L, (2903872550238813643L, (2903872550238813643L, 0L)))) + )), + ((5091129735284641762L, -427673944382373638L), Failure(new ArithmeticException("long overflow"))), + ((6029085020194630780L, 2261786144956037939L), Failure(new ArithmeticException("long overflow"))), + ((8126382074515995418L, -4746652047588907829L), Failure(new ArithmeticException("long overflow"))), + ((Long.MaxValue, 1L), Failure(new ArithmeticException("long overflow"))), + ((Long.MaxValue, -1L), Failure(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) @@ -894,7 +1033,7 @@ class SigmaDslSpec extends SigmaDslTesting { suite => val mod = a % b (plus, (minus, (mul, (div, mod)))) }, - """{ (x: (BigInt, BigInt)) => + """{ (x: (Long, Long)) => | val a = x._1; val b = x._2 | val plus = a + b | val minus = a - b @@ -904,33 +1043,33 @@ class SigmaDslSpec extends SigmaDslTesting { suite => | (plus, (minus, (mul, (div, mod)))) |}""".stripMargin, FuncValue( - Vector((1, STuple(Vector(SBigInt, SBigInt)))), + Vector((1, STuple(Vector(SLong, SLong)))), BlockValue( Vector( ValDef( 3, List(), - SelectField.typed[BigIntValue](ValUse(1, STuple(Vector(SBigInt, SBigInt))), 1.toByte) + SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 1.toByte) ), ValDef( 4, List(), - SelectField.typed[BigIntValue](ValUse(1, STuple(Vector(SBigInt, SBigInt))), 2.toByte) + SelectField.typed[LongValue](ValUse(1, STuple(Vector(SLong, SLong))), 2.toByte) ) ), Tuple( Vector( - ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-102.toByte)), + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-102.toByte)), Tuple( Vector( - ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-103.toByte)), + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-103.toByte)), Tuple( Vector( - ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-100.toByte)), + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-100.toByte)), Tuple( Vector( - ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-99.toByte)), - ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-98.toByte)) + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-99.toByte)), + ArithOp(ValUse(3, SLong), ValUse(4, SLong), OpCode @@ (-98.toByte)) ) ) ) @@ -940,104 +1079,330 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) ) - ) - ) + ))) + } + + property("Long methods equivalence (new features)") { + lazy val toBytes = newFeature((x: Long) => x.toBytes, "{ (x: Long) => x.toBytes }") + lazy val toBits = newFeature((x: Long) => x.toBits, "{ (x: Long) => x.toBits }") + 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: (BigInt, BigInt)) => x._1 | x._2 }, - "{ (x: (BigInt, BigInt)) => x._1 | x._2 }") + { (x: (Long, Long)) => x._1 | x._2 }, + "{ (x: (Long, Long)) => x._1 | x._2 }") lazy val bitAnd = newFeature( - { (x: (BigInt, BigInt)) => x._1 & x._2 }, - "{ (x: (BigInt, BigInt)) => x._1 & x._2 }") + { (x: (Long, Long)) => x._1 & x._2 }, + "{ (x: (Long, Long)) => x._1 & x._2 }") - forAll { x: BigInt => - Seq(toByte, toShort, toInt, toLong, toBigInt, toBytes, toBits, toAbs).foreach(_.checkEquality(x)) + forAll { x: Long => + Seq(toBytes, toBits, toAbs).foreach(_.checkEquality(x)) } - forAll { x: (BigInt, BigInt) => - Seq(compareTo, arithOps, bitOr, bitAnd).foreach(_.checkEquality(x)) + forAll { x: (Long, Long) => + Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) } } - property("GroupElement methods equivalence") { - val getEncoded = existingFeature((x: GroupElement) => x.getEncoded, - "{ (x: GroupElement) => x.getEncoded }", - FuncValue( - Vector((1, SGroupElement)), - MethodCall(ValUse(1, SGroupElement), SGroupElement.getMethodByName("getEncoded"), Vector(), Map()) - )) + property("BigInt methods equivalence") { + testCases( + Seq( + (CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)), Success( + CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)) + )), + (CBigInt(new BigInteger("-8000000000000000", 16)), Success(CBigInt(new BigInteger("-8000000000000000", 16)))), + (CBigInt(new BigInteger("-1", 16)), Success(CBigInt(new BigInteger("-1", 16)))), + (CBigInt(new BigInteger("0", 16)), Success(CBigInt(new BigInteger("0", 16)))), + (CBigInt(new BigInteger("1", 16)), Success(CBigInt(new BigInteger("1", 16)))), + (CBigInt(new BigInteger("7fffffffffffffff", 16)), Success(CBigInt(new BigInteger("7fffffffffffffff", 16)))), + (CBigInt(new BigInteger("bdd56c22eb3eace8bc4e1c38c65dfdb2e4ffdcf421ae78c36b93b9ff37dc0", 16)), Success( + CBigInt(new BigInteger("bdd56c22eb3eace8bc4e1c38c65dfdb2e4ffdcf421ae78c36b93b9ff37dc0", 16)) + )) + ), + existingFeature((x: BigInt) => x, + "{ (x: BigInt) => x.toBigInt }", + FuncValue(Vector((1, SBigInt)), ValUse(1, SBigInt)))) - val decode = existingFeature({ (x: GroupElement) => decodePoint(x.getEncoded) == x }, - "{ (x: GroupElement) => decodePoint(x.getEncoded) == x }", + val n = NumericOps.BigIntIsExactNumeric + testCases( + Seq( + ((CBigInt(new BigInteger("-8683d1cd99d5fcf0e6eff6295c285c36526190e13dbde008c49e5ae6fddc1c", 16)), + CBigInt(new BigInteger("-2ef55db3f245feddacf0182e299dd", 16))), + Failure(new ArithmeticException("BigInteger out of 256 bit range"))), + + ((CBigInt(new BigInteger("-68e1136872f98fb0245ec5aa4bef46e16273e860746c892", 16)), + CBigInt(new BigInteger("-352aaa769b41a327", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("-39fc00ebf09080cbd8408dd38c4b7490bea533447047140", 16)), + CBigInt(new BigInteger("31de9e96177dbd39", 16))), + Success(( + CBigInt(new BigInteger("-39fc00ebf09080cbd8408dd38c4b748da0bb49e2f86b407", 16)), + (CBigInt(new BigInteger("-39fc00ebf09080cbd8408dd38c4b7493dc8f1ca5e822e79", 16)), + (CBigInt(new BigInteger("-b4ba8a17d328dac74ef014d7be35597a1259f8b16f0ff1c9820dea23d97740", 16)), + (CBigInt(new BigInteger("-129a8045376e104f0d3771b6c2c128fc", 16)), + CBigInt(new BigInteger("12fe89836fc97815", 16)))))) )), + + ((CBigInt(new BigInteger("-8000000000000000", 16)), CBigInt(new BigInteger("8000000000000000", 16))), + Success(( + CBigInt(new BigInteger("0", 16)), + (CBigInt(new BigInteger("-10000000000000000", 16)), + (CBigInt(new BigInteger("-40000000000000000000000000000000", 16)), + (CBigInt(new BigInteger("-1", 16)), CBigInt(new BigInteger("0", 16)))))) )), + + ((CBigInt(new BigInteger("-47dede8d3e4804bb", 16)), CBigInt(new BigInteger("-388828eb6dfce683", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("-4fde491150ea00d", 16)), CBigInt(new BigInteger("-80000001", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("-80000001", 16)), CBigInt(new BigInteger("-80000001", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("0", 16)), CBigInt(new BigInteger("-8000000000000000", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("0", 16)), CBigInt(new BigInteger("0", 16))), + Failure(new ArithmeticException("BigInteger divide by zero"))), + + ((CBigInt(new BigInteger("1", 16)), + CBigInt(new BigInteger("-86063f66e06d6d535c95862cd506309a95d10102422fee", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("80000000", 16)), CBigInt(new BigInteger("4e592ce5b544b8f7a91f97ec9ea2f2c3660111360297a4", 16))), + Success(( + CBigInt(new BigInteger("4e592ce5b544b8f7a91f97ec9ea2f2c3660111b60297a4", 16)), + (CBigInt(new BigInteger("-4e592ce5b544b8f7a91f97ec9ea2f2c3660110b60297a4", 16)), + (CBigInt(new BigInteger("272c9672daa25c7bd48fcbf64f517961b300889b014bd200000000", 16)), + (CBigInt(new BigInteger("0", 16)), CBigInt(new BigInteger("80000000", 16)))))) )), + + ((CBigInt(new BigInteger("3d31398dc4783303", 16)), + CBigInt(new BigInteger("-37b381db4e6e927e202a2a421d5a09ca", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("5524814a26357cb71488b6fb26af2d3", 16)), + CBigInt(new BigInteger("c413b7d975a9972427f46996299fe57cfe79479ac954a7", 16))), + Failure(new ArithmeticException("BigInteger out of 256 bit range"))) + ), + existingFeature( + { (x: (BigInt, BigInt)) => + 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: (BigInt, BigInt)) => + | 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, SGroupElement)), - EQ( - DecodePoint( - MethodCall.typed[Value[SCollection[SByte.type]]]( - ValUse(1, SGroupElement), - SGroupElement.getMethodByName("getEncoded"), - Vector(), - Map() + Vector((1, STuple(Vector(SBigInt, SBigInt)))), + BlockValue( + Vector( + ValDef( + 3, + List(), + SelectField.typed[BigIntValue](ValUse(1, STuple(Vector(SBigInt, SBigInt))), 1.toByte) + ), + ValDef( + 4, + List(), + SelectField.typed[BigIntValue](ValUse(1, STuple(Vector(SBigInt, SBigInt))), 2.toByte) ) ), - ValUse(1, SGroupElement) + Tuple( + Vector( + ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-102.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-103.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-100.toByte)), + Tuple( + Vector( + ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-99.toByte)), + ArithOp(ValUse(3, SBigInt), ValUse(4, SBigInt), OpCode @@ (-98.toByte)) + ) + ) + ) + ) + ) + ) + ) + ) ) - )) + ) + ), true) + } - val negate = existingFeature({ (x: GroupElement) => x.negate }, - "{ (x: GroupElement) => x.negate }", - FuncValue( - Vector((1, SGroupElement)), - MethodCall(ValUse(1, SGroupElement), SGroupElement.getMethodByName("negate"), Vector(), Map()) - )) + property("BigInt methods equivalence (new features)") { + val toByte = newFeature((x: BigInt) => x.toByte, + "{ (x: BigInt) => x.toByte }", + FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SByte))) - //TODO soft-fork: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - // val isIdentity = existingFeature({ (x: GroupElement) => x.isIdentity }, - // "{ (x: GroupElement) => x.isIdentity }") + 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 toBits = newFeature((x: BigInt) => x.toBits, "{ (x: BigInt) => x.toBits }") + 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 }") - forAll { x: GroupElement => - Seq(getEncoded, decode, negate/*, isIdentity*/).foreach(_.checkEquality(x)) + 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, toBits, toAbs).foreach(_.checkEquality(x)) + } + forAll { x: (BigInt, BigInt) => + Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) } + } - val exp = existingFeature({ (x: (GroupElement, BigInt)) => x._1.exp(x._2) }, - "{ (x: (GroupElement, BigInt)) => x._1.exp(x._2) }", - FuncValue( - Vector((1, STuple(Vector(SGroupElement, SBigInt)))), - Exponentiate( - SelectField.typed[Value[SGroupElement.type]]( - ValUse(1, STuple(Vector(SGroupElement, SBigInt))), - 1.toByte - ), - SelectField.typed[Value[SBigInt.type]]( - ValUse(1, STuple(Vector(SGroupElement, SBigInt))), - 2.toByte + property("GroupElement methods equivalence") { + val ge1 = "03358d53f01276211f92d0aefbd278805121d4ff6eb534b777af1ee8abae5b2056" + val ge2 = "02dba7b94b111f3894e2f9120b577da595ec7d58d488485adf73bf4e153af63575" + val ge3 = "0290449814f5671172dd696a61b8aa49aaa4c87013f56165e27d49944e98bc414d" + + testCases( + Seq( + (Helpers.decodeGroupElement(ge1), Success(Helpers.decodeBytes(ge1))), + (Helpers.decodeGroupElement(ge2), Success(Helpers.decodeBytes(ge2))), + (Helpers.decodeGroupElement(ge3), Success(Helpers.decodeBytes(ge3))), + (SigmaDsl.groupGenerator, + Success(Helpers.decodeBytes("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (SigmaDsl.groupIdentity, + Success(Helpers.decodeBytes("000000000000000000000000000000000000000000000000000000000000000000"))) + ), + existingFeature((x: GroupElement) => x.getEncoded, + "{ (x: GroupElement) => x.getEncoded }", + FuncValue( + Vector((1, SGroupElement)), + MethodCall(ValUse(1, SGroupElement), SGroupElement.getMethodByName("getEncoded"), Vector(), Map()) + ))) + + testCases( + Seq( + (Helpers.decodeGroupElement(ge1), Success(true)), + (Helpers.decodeGroupElement(ge2), Success(true)), + (Helpers.decodeGroupElement(ge3), Success(true)), + (SigmaDsl.groupGenerator, Success(true)), + (SigmaDsl.groupIdentity, Success(true)) + ), + existingFeature({ (x: GroupElement) => decodePoint(x.getEncoded) == x }, + "{ (x: GroupElement) => decodePoint(x.getEncoded) == x }", + FuncValue( + Vector((1, SGroupElement)), + EQ( + DecodePoint( + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SGroupElement), + SGroupElement.getMethodByName("getEncoded"), + Vector(), + Map() + ) + ), + ValUse(1, SGroupElement) ) - ) - )) + ))) - forAll { x: (GroupElement, BigInt) => - exp.checkEquality(x) - } + testCases( + Seq( + (Helpers.decodeGroupElement(ge1), Success(Helpers.decodeGroupElement("02358d53f01276211f92d0aefbd278805121d4ff6eb534b777af1ee8abae5b2056"))), + (Helpers.decodeGroupElement(ge2), Success(Helpers.decodeGroupElement("03dba7b94b111f3894e2f9120b577da595ec7d58d488485adf73bf4e153af63575"))), + (Helpers.decodeGroupElement(ge3), Success(Helpers.decodeGroupElement("0390449814f5671172dd696a61b8aa49aaa4c87013f56165e27d49944e98bc414d"))), + (SigmaDsl.groupGenerator, Success(Helpers.decodeGroupElement("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (SigmaDsl.groupIdentity, Success(Helpers.decodeGroupElement("000000000000000000000000000000000000000000000000000000000000000000"))) + ), + existingFeature({ (x: GroupElement) => x.negate }, + "{ (x: GroupElement) => x.negate }", + FuncValue( + Vector((1, SGroupElement)), + MethodCall(ValUse(1, SGroupElement), SGroupElement.getMethodByName("negate"), Vector(), Map()) + ))) - val multiply = existingFeature({ (x: (GroupElement, GroupElement)) => x._1.multiply(x._2) }, - "{ (x: (GroupElement, GroupElement)) => x._1.multiply(x._2) }", - FuncValue( - Vector((1, STuple(Vector(SGroupElement, SGroupElement)))), - MultiplyGroup( - SelectField.typed[Value[SGroupElement.type]]( - ValUse(1, STuple(Vector(SGroupElement, SGroupElement))), - 1.toByte - ), - SelectField.typed[Value[SGroupElement.type]]( - ValUse(1, STuple(Vector(SGroupElement, SGroupElement))), - 2.toByte + //TODO HF: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + // val isIdentity = existingFeature({ (x: GroupElement) => x.isIdentity }, + // "{ (x: GroupElement) => x.isIdentity }") + + testCases( + Seq( + ((Helpers.decodeGroupElement(ge1), CBigInt(new BigInteger("-25c80b560dd7844e2efd10f80f7ee57d", 16))), + Success(Helpers.decodeGroupElement("023a850181b7b73f92a5bbfa0bfc78f5bbb6ff00645ddde501037017e1a2251e2e"))), + ((Helpers.decodeGroupElement(ge2), CBigInt(new BigInteger("2488741265082fb02b09f992be3dd8d60d2bbe80d9e2630", 16))), + Success(Helpers.decodeGroupElement("032045b928fb7774a4cd9ef5fa8209f4e493cd4cc5bd536b52746a53871bf73431"))), + ((Helpers.decodeGroupElement(ge3), CBigInt(new BigInteger("-33e8fbdb13d2982e92583445e1fdcb5901a178a7aa1e100", 16))), + Success(Helpers.decodeGroupElement("036128efaf14d8ac2812a662f6494dc617b87986a3dc6b4a59440048a7ac7d2729"))), + ((Helpers.decodeGroupElement(ge3), CBigInt(new BigInteger("1", 16))), + Success(Helpers.decodeGroupElement(ge3))) + ), + existingFeature({ (x: (GroupElement, BigInt)) => x._1.exp(x._2) }, + "{ (x: (GroupElement, BigInt)) => x._1.exp(x._2) }", + FuncValue( + Vector((1, STuple(Vector(SGroupElement, SBigInt)))), + Exponentiate( + SelectField.typed[Value[SGroupElement.type]]( + ValUse(1, STuple(Vector(SGroupElement, SBigInt))), + 1.toByte + ), + SelectField.typed[Value[SBigInt.type]]( + ValUse(1, STuple(Vector(SGroupElement, SBigInt))), + 2.toByte + ) ) - ) - )) + ))) - forAll { x: (GroupElement, GroupElement) => - multiply.checkEquality(x) - } + testCases( + Seq( + ((Helpers.decodeGroupElement(ge1), Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), + Success(Helpers.decodeGroupElement("02bc48937b4a66f249a32dfb4d2efd0743dc88d46d770b8c5d39fd03325ba211df"))), + ((Helpers.decodeGroupElement(ge2), Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), + Success(Helpers.decodeGroupElement("0359c3bb2ac4ea4dbd7b1e09d7b11198141a3263834fb84a88039629ec1e9311d1"))), + ((Helpers.decodeGroupElement(ge3), Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), + Success(Helpers.decodeGroupElement("02eca42e28548d3fb9fa77cdd0c983066c3ad141ebb086b5044ce46b9ba9b5a714"))), + ((Helpers.decodeGroupElement(ge3), SigmaDsl.groupIdentity), + Success(Helpers.decodeGroupElement(ge3))) + ), + existingFeature({ (x: (GroupElement, GroupElement)) => x._1.multiply(x._2) }, + "{ (x: (GroupElement, GroupElement)) => x._1.multiply(x._2) }", + FuncValue( + Vector((1, STuple(Vector(SGroupElement, SGroupElement)))), + MultiplyGroup( + SelectField.typed[Value[SGroupElement.type]]( + ValUse(1, STuple(Vector(SGroupElement, SGroupElement))), + 1.toByte + ), + SelectField.typed[Value[SGroupElement.type]]( + ValUse(1, STuple(Vector(SGroupElement, SGroupElement))), + 2.toByte + ) + ) + ))) } property("AvlTree properties equivalence") { @@ -1052,46 +1417,100 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) } - val digest = existingFeature((t: AvlTree) => t.digest, - "{ (t: AvlTree) => t.digest }", - expectedExprFor("digest")) - - val enabledOperations = existingFeature((t: AvlTree) => t.enabledOperations, - "{ (t: AvlTree) => t.enabledOperations }", - expectedExprFor("enabledOperations")) - - val keyLength = existingFeature((t: AvlTree) => t.keyLength, - "{ (t: AvlTree) => t.keyLength }", - expectedExprFor("keyLength")) - - val valueLengthOpt = existingFeature((t: AvlTree) => t.valueLengthOpt, - "{ (t: AvlTree) => t.valueLengthOpt }", - expectedExprFor("valueLengthOpt")) - - val isInsertAllowed = existingFeature((t: AvlTree) => t.isInsertAllowed, - "{ (t: AvlTree) => t.isInsertAllowed }", - expectedExprFor("isInsertAllowed")) - - val isUpdateAllowed = existingFeature((t: AvlTree) => t.isUpdateAllowed, - "{ (t: AvlTree) => t.isUpdateAllowed }", - expectedExprFor("isUpdateAllowed")) - - val isRemoveAllowed = existingFeature((t: AvlTree) => t.isRemoveAllowed, - "{ (t: AvlTree) => t.isRemoveAllowed }", - expectedExprFor("isRemoveAllowed")) - - val newTree = sampleAvlTree.updateOperations(1.toByte) - val trees = Array(sampleAvlTree, newTree) - - for (tree <- trees) { - Seq(digest, - enabledOperations, - keyLength, - valueLengthOpt, - isInsertAllowed, - isUpdateAllowed, - isRemoveAllowed).foreach(_.checkEquality(tree)) - } + val t1 = CAvlTree( + AvlTreeData( + ADDigest @@ ErgoAlgos.decodeUnsafe("000183807f66b301530120ff7fc6bd6601ff01ff7f7d2bedbbffff00187fe89094"), + AvlTreeFlags(false, true, true), + 1, + Some(1) + ) + ) + val t2 = CAvlTree( + AvlTreeData( + ADDigest @@ ErgoAlgos.decodeUnsafe("ff000d937f80ffd731ed802d24358001ff8080ff71007f00ad37e0a7ae43fff95b"), + AvlTreeFlags(false, false, false), + 32, + Some(64) + ) + ) + val t3 = CAvlTree( + AvlTreeData( + ADDigest @@ ErgoAlgos.decodeUnsafe("3100d2e101ff01fc047c7f6f00ff80129df69a5090012f01ffca99f5bfff0c8036"), + AvlTreeFlags(true, false, false), + 128, + None + ) + ) + + testCases( + Seq( + (t1, Success(Helpers.decodeBytes("000183807f66b301530120ff7fc6bd6601ff01ff7f7d2bedbbffff00187fe89094"))), + (t2, Success(Helpers.decodeBytes("ff000d937f80ffd731ed802d24358001ff8080ff71007f00ad37e0a7ae43fff95b"))), + (t3, Success(Helpers.decodeBytes("3100d2e101ff01fc047c7f6f00ff80129df69a5090012f01ffca99f5bfff0c8036"))) + ), + existingFeature((t: AvlTree) => t.digest, + "{ (t: AvlTree) => t.digest }", + expectedExprFor("digest"))) + + testCases( + Seq( + (t1, Success(6.toByte)), + (t2, Success(0.toByte)), + (t3, Success(1.toByte)) + ), + existingFeature((t: AvlTree) => t.enabledOperations, + "{ (t: AvlTree) => t.enabledOperations }", + expectedExprFor("enabledOperations"))) + + testCases( + Seq( + (t1, Success(1)), + (t2, Success(32)), + (t3, Success(128)) + ), + existingFeature((t: AvlTree) => t.keyLength, + "{ (t: AvlTree) => t.keyLength }", + expectedExprFor("keyLength"))) + + testCases( + Seq( + (t1, Success(Some(1))), + (t2, Success(Some(64))), + (t3, Success(None)) + ), + existingFeature((t: AvlTree) => t.valueLengthOpt, + "{ (t: AvlTree) => t.valueLengthOpt }", + expectedExprFor("valueLengthOpt"))) + + testCases( + Seq( + (t1, Success(false)), + (t2, Success(false)), + (t3, Success(true)) + ), + existingFeature((t: AvlTree) => t.isInsertAllowed, + "{ (t: AvlTree) => t.isInsertAllowed }", + expectedExprFor("isInsertAllowed"))) + + testCases( + Seq( + (t1, Success(true)), + (t2, Success(false)), + (t3, Success(false)) + ), + existingFeature((t: AvlTree) => t.isUpdateAllowed, + "{ (t: AvlTree) => t.isUpdateAllowed }", + expectedExprFor("isUpdateAllowed"))) + + testCases( + Seq( + (t1, Success(true)), + (t2, Success(false)), + (t3, Success(false)) + ), + existingFeature((t: AvlTree) => t.isRemoveAllowed, + "{ (t: AvlTree) => t.isRemoveAllowed }", + expectedExprFor("isRemoveAllowed"))) } property("AvlTree.{contains, get, getMany, updateDigest, updateOperations} equivalence") { @@ -1637,30 +2056,61 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("longToByteArray equivalence") { - val feature = existingFeature((x: Long) => SigmaDsl.longToByteArray(x), - "{ (x: Long) => longToByteArray(x) }", - FuncValue(Vector((1, SLong)), LongToByteArray(ValUse(1, SLong)))) - forAll { x: Long => feature.checkEquality(x) } + testCases( + Seq( + (-9223372036854775808L, Success(Helpers.decodeBytes("8000000000000000"))), + (-1148502660425090565L, Success(Helpers.decodeBytes("f00fb2ea55c579fb"))), + (-1L, Success(Helpers.decodeBytes("ffffffffffffffff"))), + (0L, Success(Helpers.decodeBytes("0000000000000000"))), + (1L, Success(Helpers.decodeBytes("0000000000000001"))), + (238790047448232028L, Success(Helpers.decodeBytes("03505a48720cf05c"))), + (9223372036854775807L, Success(Helpers.decodeBytes("7fffffffffffffff"))) + ), + existingFeature((x: Long) => SigmaDsl.longToByteArray(x), + "{ (x: Long) => longToByteArray(x) }", + FuncValue(Vector((1, SLong)), LongToByteArray(ValUse(1, SLong))))) } property("byteArrayToBigInt equivalence") { - val eq = existingFeature((x: Coll[Byte]) => SigmaDsl.byteArrayToBigInt(x), - "{ (x: Coll[Byte]) => byteArrayToBigInt(x) }", - FuncValue(Vector((1, SByteArray)), ByteArrayToBigInt(ValUse(1, SByteArray)))) - forAll(collOfN[Byte](SigmaConstants.MaxBigIntSizeInBytes.value.toInt)) { x: Coll[Byte] => - eq.checkEquality(x) - } + testCases( + Seq( + (Helpers.decodeBytes(""), + Failure(new NumberFormatException("Zero length BigInteger"))), + (Helpers.decodeBytes("00"), + Success(CBigInt(new BigInteger("0", 16)))), + (Helpers.decodeBytes("01"), + Success(CBigInt(new BigInteger("1", 16)))), + (Helpers.decodeBytes("ff"), + Success(CBigInt(new BigInteger("-1", 16)))), + (Helpers.decodeBytes("80d6c201"), + Success(CBigInt(new BigInteger("-7f293dff", 16)))), + (Helpers.decodeBytes("70d6c201"), + Success(CBigInt(new BigInteger("70d6c201", 16)))), + (Helpers.decodeBytes( + "80e0ff7f02807fff72807f0a00ff7fb7c57f75c11ba2802970fd250052807fc37f6480ffff007fff18eeba44" + ), Failure(new ArithmeticException("BigInteger out of 256 bit range"))) + ), + existingFeature((x: Coll[Byte]) => SigmaDsl.byteArrayToBigInt(x), + "{ (x: Coll[Byte]) => byteArrayToBigInt(x) }", + FuncValue(Vector((1, SByteArray)), ByteArrayToBigInt(ValUse(1, SByteArray))))) } property("byteArrayToLong equivalence") { - val eq = existingFeature((x: Coll[Byte]) => SigmaDsl.byteArrayToLong(x), - "{ (x: Coll[Byte]) => byteArrayToLong(x) }", - FuncValue(Vector((1, SByteArray)), ByteArrayToLong(ValUse(1, SByteArray)))) - forAll { x: Array[Byte] => - whenever(x.length >= 8) { - eq.checkEquality(Colls.fromArray(x)) - } - } + testCases( + Seq( + (Helpers.decodeBytes(""), Failure(new IllegalArgumentException("array too small: 0 < 8"))), + (Helpers.decodeBytes("81"), Failure(new IllegalArgumentException("array too small: 1 < 8"))), + (Helpers.decodeBytes("812d7f00ff807f"), Failure(new IllegalArgumentException("array too small: 7 < 8"))), + (Helpers.decodeBytes("812d7f00ff807f7f"), Success(-9138508426601529473L)), + (Helpers.decodeBytes("ffffffffffffffff"), Success(-1L)), + (Helpers.decodeBytes("0000000000000000"), Success(0L)), + (Helpers.decodeBytes("0000000000000001"), Success(1L)), + (Helpers.decodeBytes("712d7f00ff807f7f"), Success(8155314142501175167L)), + (Helpers.decodeBytes("812d7f00ff807f7f0101018050757f0580ac009680f2ffc1"), Success(-9138508426601529473L)) + ), + existingFeature((x: Coll[Byte]) => SigmaDsl.byteArrayToLong(x), + "{ (x: Coll[Byte]) => byteArrayToLong(x) }", + FuncValue(Vector((1, SByteArray)), ByteArrayToLong(ValUse(1, SByteArray))))) } // TODO soft-fork: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/427 @@ -1675,111 +2125,278 @@ class SigmaDslSpec extends SigmaDslTesting { suite => lazy val ctx = ergoCtx.toSigmaContext(IR, false) property("Box properties equivalence") { - val id = existingFeature({ (x: Box) => x.id }, - "{ (x: Box) => x.id }", - FuncValue(Vector((1, SBox)), ExtractId(ValUse(1, SBox)))) + val b1 = CostingBox( + false, + new ErgoBox( + 9223372036854775807L, + new ErgoTree( + 16.toByte, + Array( + SigmaPropConstant( + CSigmaProp( + ProveDlog( + Helpers.decodeECPoint( + "0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e" + ) + ) + ) + ) + ), + Right(ConstantPlaceholder(0, SSigmaProp)) + ), + Coll( + (Digest32 @@ (ErgoAlgos.decodeUnsafe("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001")), + 10000000L), + (Digest32 @@ (ErgoAlgos.decodeUnsafe("a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600")), + 500L) + ), + Map( + ErgoBox.R5 -> ByteArrayConstant(Helpers.decodeBytes("7fc87f7f01ff")), + ErgoBox.R4 -> FalseLeaf + ), + ModifierId @@ ("218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080"), + 22588.toShort, + 677407 + ) + ) - val value = existingFeature({ (x: Box) => x.value }, - "{ (x: Box) => x.value }", - FuncValue(Vector((1, SBox)), ExtractAmount(ValUse(1, SBox)))) + val b2 = CostingBox( + false, + new ErgoBox( + 12345L, + new ErgoTree( + 0.toByte, + Vector(), + Right( + BoolToSigmaProp( + AND( + ConcreteCollection( + Array( + FalseLeaf, + XorOf( + ConcreteCollection(Array(EQ(IntConstant(1), IntConstant(1)), FalseLeaf), SBoolean) + ) + ), + SBoolean + ) + ) + ) + ) + ), + Coll(), + Map( + ErgoBox.R5 -> ByteArrayConstant( + Helpers.decodeBytes( + "297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d65" + ) + ), + ErgoBox.R4 -> TrueLeaf, + ErgoBox.R7 -> LongConstant(9223372036854775807L), + ErgoBox.R6 -> LongConstant(2115927197107005906L) + ), + ModifierId @@ ("003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e"), + 1.toShort, + 1000000 + ) + ) - val propositionBytes = existingFeature({ (x: Box) => x.propositionBytes }, - "{ (x: Box) => x.propositionBytes }", - FuncValue(Vector((1, SBox)), ExtractScriptBytes(ValUse(1, SBox)))) + testCases( + Seq( + (b1, Success(Helpers.decodeBytes("5ee78f30ae4e770e44900a46854e9fecb6b12e8112556ef1cd19aef633b4421e"))), + (b2, Success(Helpers.decodeBytes("3a0089be265460e29ca47d26e5b55a6f3e3ffaf5b4aed941410a2437913848ad"))) + ), + existingFeature({ (x: Box) => x.id }, + "{ (x: Box) => x.id }", + FuncValue(Vector((1, SBox)), ExtractId(ValUse(1, SBox))))) - val bytes = existingFeature({ (x: Box) => x.bytes }, - "{ (x: Box) => x.bytes }", - FuncValue(Vector((1, SBox)), ExtractBytes(ValUse(1, SBox)))) + testCases( + Seq( + (b1, Success(9223372036854775807L)), + (b2, Success(12345L)) + ), + existingFeature({ (x: Box) => x.value }, + "{ (x: Box) => x.value }", + FuncValue(Vector((1, SBox)), ExtractAmount(ValUse(1, SBox))))) - val bytesWithoutRef = existingFeature({ (x: Box) => x.bytesWithoutRef }, - "{ (x: Box) => x.bytesWithoutRef }", - FuncValue(Vector((1, SBox)), ExtractBytesWithNoRef(ValUse(1, SBox)))) + testCases( + Seq( + (b1, Success(Helpers.decodeBytes( + "100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e7300" + ))), + (b2, Success(Helpers.decodeBytes("00d1968302010100ff83020193040204020100"))) + ), + existingFeature({ (x: Box) => x.propositionBytes }, + "{ (x: Box) => x.propositionBytes }", + FuncValue(Vector((1, SBox)), ExtractScriptBytes(ValUse(1, SBox))))) - val creationInfo = existingFeature({ (x: Box) => x.creationInfo }, - "{ (x: Box) => x.creationInfo }", - FuncValue(Vector((1, SBox)), ExtractCreationInfo(ValUse(1, SBox)))) + testCases( + Seq( + (b1, Success(Helpers.decodeBytes( + "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080bcb001" + ))), + (b2, Success(Helpers.decodeBytes( + "b96000d1968302010100ff83020193040204020100c0843d000401010e32297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d6505a4a7b5a2e7a4a4dd3a05feffffffffffffffff01003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e01" + ))) + ), + existingFeature({ (x: Box) => x.bytes }, + "{ (x: Box) => x.bytes }", + FuncValue(Vector((1, SBox)), ExtractBytes(ValUse(1, SBox))))) - val tokens = existingFeature({ (x: Box) => x.tokens }, - "{ (x: Box) => x.tokens }", - FuncValue( - Vector((1, SBox)), - MethodCall.typed[Value[SCollection[STuple]]]( - ValUse(1, SBox), - SBox.getMethodByName("tokens"), - Vector(), - Map() - ) - )) + testCases( + Seq( + (b1, Success(Helpers.decodeBytes( + "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff" + ))), + (b2, Success(Helpers.decodeBytes( + "b96000d1968302010100ff83020193040204020100c0843d000401010e32297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d6505a4a7b5a2e7a4a4dd3a05feffffffffffffffff01" + ))) + ), + existingFeature({ (x: Box) => x.bytesWithoutRef }, + "{ (x: Box) => x.bytesWithoutRef }", + FuncValue(Vector((1, SBox)), ExtractBytesWithNoRef(ValUse(1, SBox))))) - // TODO HF: 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 }") + testCases( + Seq( + (b1, Success(( + 677407, + Helpers.decodeBytes("218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080583c") + ))), + (b2, Success(( + 1000000, + Helpers.decodeBytes("003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e0001") + ))) + ), + existingFeature({ (x: Box) => x.creationInfo }, + "{ (x: Box) => x.creationInfo }", + FuncValue(Vector((1, SBox)), ExtractCreationInfo(ValUse(1, SBox))))) + + // TODO HF: fix collections equality and remove map(identity) + // (PairOfColl should be equal CollOverArray) + testCases( + Seq( + (b1, Success(Coll[(Coll[Byte], Long)]( + (Helpers.decodeBytes("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001"), 10000000L), + (Helpers.decodeBytes("a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600"), 500L) + ).map(identity)) + ), + (b2, Success(Coll[(Coll[Byte], Long)]().map(identity))) + ), + existingFeature({ (x: Box) => x.tokens }, + "{ (x: Box) => x.tokens }", + FuncValue( + Vector((1, SBox)), + MethodCall.typed[Value[SCollection[STuple]]]( + ValUse(1, SBox), + SBox.getMethodByName("tokens"), + Vector(), + Map() + ) + ))) + + } + + property("Box properties equivalence (new features)") { + // TODO HF: 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(id, value, propositionBytes, bytes, bytesWithoutRef, creationInfo, tokens, getReg) - .foreach(_.checkEquality(box)) + Seq(getReg).foreach(_.checkEquality(box)) } } property("Advanced Box test") { val (tree, _) = createAvlTreeAndProver() - val box = ErgoBox(20, TrueProp, 0, Seq(), Map( - ErgoBox.nonMandatoryRegisters(0) -> ByteConstant(1.toByte), - ErgoBox.nonMandatoryRegisters(1) -> ShortConstant(1024.toShort), - ErgoBox.nonMandatoryRegisters(2) -> IntConstant(1024 * 1024), - ErgoBox.nonMandatoryRegisters(3) -> LongConstant(1024.toLong), - ErgoBox.nonMandatoryRegisters(4) -> BigIntConstant(222L), - ErgoBox.nonMandatoryRegisters(5) -> AvlTreeConstant(tree) - )) - - lazy val byteReg = existingFeature((x: Box) => x.R4[Byte].get, - "{ (x: Box) => x.R4[Byte].get }", - FuncValue( - Vector((1, SBox)), - OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R4, SOption(SByte))) - )) + val box1 = SigmaDsl.Box(ErgoBox(20, TrueProp, 0, Seq(), Map( + ErgoBox.R4 -> ByteConstant(1.toByte), + ErgoBox.R5 -> ShortConstant(1024.toShort), + ErgoBox.R6 -> IntConstant(1024 * 1024), + ErgoBox.R7 -> LongConstant(1024.toLong), + ErgoBox.R8 -> BigIntConstant(222L), + ErgoBox.R9 -> AvlTreeConstant(tree) + ))) - lazy val shortReg = existingFeature((x: Box) => x.R5[Short].get, - "{ (x: Box) => x.R5[Short].get }", - FuncValue( - Vector((1, SBox)), - OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R5, SOption(SShort))) - )) - lazy val intReg = existingFeature((x: Box) => x.R6[Int].get, - "{ (x: Box) => x.R6[Int].get }", - FuncValue( - Vector((1, SBox)), - OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R6, SOption(SInt))) - )) + val box2 = SigmaDsl.Box(ErgoBox(20, TrueProp, 0, Seq(), Map( + ErgoBox.R4 -> ByteArrayConstant(Coll(1.toByte)) + ))) - lazy val longReg = existingFeature((x: Box) => x.R7[Long].get, - "{ (x: Box) => x.R7[Long].get }", - FuncValue( - Vector((1, SBox)), - OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R7, SOption(SLong))) - )) + testCases( + Seq( + (box1, Success(1.toByte)), + (box2, Failure(new InvalidType("Cannot getReg[Byte](4): invalid type of value Value(Coll(1)) at id=4"))) + ), + existingFeature((x: Box) => x.R4[Byte].get, + "{ (x: Box) => x.R4[Byte].get }", + FuncValue( + Vector((1, SBox)), + OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R4, SOption(SByte))) + ))) - lazy val bigIntReg = existingFeature((x: Box) => x.R8[BigInt].get, - "{ (x: Box) => x.R8[BigInt].get }", - FuncValue( - Vector((1, SBox)), - OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R8, SOption(SBigInt))) - )) + testCases( + Seq( + (box1, Success(1024.toShort)), + (box2, Failure(new NoSuchElementException("None.get"))) + ), + existingFeature((x: Box) => x.R5[Short].get, + "{ (x: Box) => x.R5[Short].get }", + FuncValue( + Vector((1, SBox)), + OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R5, SOption(SShort))) + ))) - lazy val avlTreeReg = existingFeature((x: Box) => x.R9[AvlTree].get, - "{ (x: Box) => x.R9[AvlTree].get }", - FuncValue( - Vector((1, SBox)), - OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R9, SOption(SAvlTree))) - )) + testCases( + Seq( + (box1, Success(1024 * 1024)) + ), + existingFeature((x: Box) => x.R6[Int].get, + "{ (x: Box) => x.R6[Int].get }", + FuncValue( + Vector((1, SBox)), + OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R6, SOption(SInt))) + ))) - Seq( - byteReg, - shortReg, - intReg, - longReg, - bigIntReg, avlTreeReg).foreach(_.checkEquality(box)) + testCases( + Seq( + (box1, Success(1024.toLong)) + ), + existingFeature((x: Box) => x.R7[Long].get, + "{ (x: Box) => x.R7[Long].get }", + FuncValue( + Vector((1, SBox)), + OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R7, SOption(SLong))) + ))) + + testCases( + Seq( + (box1, Success(CBigInt(BigInteger.valueOf(222L)))) + ), + existingFeature((x: Box) => x.R8[BigInt].get, + "{ (x: Box) => x.R8[BigInt].get }", + FuncValue( + Vector((1, SBox)), + OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R8, SOption(SBigInt))) + ))) + + testCases( + Seq( + (box1, Success(CAvlTree( + AvlTreeData( + ADDigest @@ ( + ErgoAlgos.decodeUnsafe("4ec61f485b98eb87153f7c57db4f5ecd75556fddbc403b41acf8441fde8e160900") + ), + AvlTreeFlags(true, true, true), + 32, + None + ) + ))) + ), + existingFeature((x: Box) => x.R9[AvlTree].get, + "{ (x: Box) => x.R9[AvlTree].get }", + FuncValue( + Vector((1, SBox)), + OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R9, SOption(SAvlTree))) + ))) } def existingPropTest[A: RType, B: RType](propName: String, scalaFunc: A => B) = { @@ -1795,164 +2412,459 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("PreHeader properties equivalence") { - val h = ctx.preHeader - val version = existingPropTest("version", { (x: PreHeader) => x.version }) - val parentId = existingPropTest("parentId", { (x: PreHeader) => x.parentId }) - val timestamp = existingPropTest("timestamp", { (x: PreHeader) => x.timestamp }) - val nBits = existingPropTest("nBits", { (x: PreHeader) => x.nBits }) - val height = existingPropTest("height", { (x: PreHeader) => x.height }) - val minerPk = existingPropTest("minerPk", { (x: PreHeader) => x.minerPk }) - val votes = existingPropTest("votes", { (x: PreHeader) => x.votes }) - Seq(version, parentId, timestamp, nBits, height, minerPk, votes).foreach(_.checkEquality(h)) + val h1 = CPreHeader( + 0.toByte, + Helpers.decodeBytes("7fff7fdd6f62018bae0001006d9ca888ff7f56ff8006573700a167f17f2c9f40"), + 6306290372572472443L, + -3683306095029417063L, + 1, + Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b"), + Helpers.decodeBytes("ff8087") + ) + + testCases( + Seq((h1, Success(0.toByte))), + existingPropTest("version", { (x: PreHeader) => x.version })) + + testCases( + Seq((h1, Success(Helpers.decodeBytes("7fff7fdd6f62018bae0001006d9ca888ff7f56ff8006573700a167f17f2c9f40")))), + existingPropTest("parentId", { (x: PreHeader) => x.parentId })) + + testCases( + Seq((h1, Success(6306290372572472443L))), + existingPropTest("timestamp", { (x: PreHeader) => x.timestamp })) + + testCases( + Seq((h1, Success(-3683306095029417063L))), + existingPropTest("nBits", { (x: PreHeader) => x.nBits })) + + testCases( + Seq((h1, Success(1))), + existingPropTest("height", { (x: PreHeader) => x.height })) + + testCases( + Seq((h1, Success(Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b")))), + existingPropTest("minerPk", { (x: PreHeader) => x.minerPk })) + + testCases( + Seq((h1, Success(Helpers.decodeBytes("ff8087")))), + existingPropTest("votes", { (x: PreHeader) => x.votes })) } property("Header properties equivalence") { - val h = ctx.headers(0) - val id = existingPropTest("id", { (x: Header) => x.id }) - val version = existingPropTest("version", { (x: Header) => x.version }) - val parentId = existingPropTest("parentId", { (x: Header) => x.parentId }) - val ASDProofsRoot = existingPropTest("ADProofsRoot", { (x: Header) => x.ADProofsRoot}) - val stateRoot = existingPropTest("stateRoot", { (x: Header) => x.stateRoot }) - val transactionsRoot = existingPropTest("transactionsRoot", { (x: Header) => x.transactionsRoot }) - val timestamp = existingPropTest("timestamp", { (x: Header) => x.timestamp }) - val nBits = existingPropTest("nBits", { (x: Header) => x.nBits }) - val height = existingPropTest("height", { (x: Header) => x.height }) - val extensionRoot = existingPropTest("extensionRoot", { (x: Header) => x.extensionRoot }) - val minerPk = existingPropTest("minerPk", { (x: Header) => x.minerPk }) - val powOnetimePk = existingPropTest("powOnetimePk", { (x: Header) => x.powOnetimePk }) - val powNonce = existingPropTest("powNonce", { (x: Header) => x.powNonce }) - val powDistance = existingPropTest("powDistance", { (x: Header) => x.powDistance }) - val votes = existingPropTest("votes", { (x: Header) => x.votes }) - - Seq(id, version, parentId, ASDProofsRoot, stateRoot, transactionsRoot, - timestamp, nBits, height, extensionRoot, minerPk, powOnetimePk, - powNonce, powDistance, votes).foreach(_.checkEquality(h)) + val treeData = AvlTreeData( + ADDigest @@ ( + ErgoAlgos.decodeUnsafe("010180017f7f7b7f720c00007f7f7f0f01e857a626f37f1483d06af8077a008080") + ), + AvlTreeFlags(false, true, false), + 728138553, + Some(2147483647) + ) + val h1 = CHeader( + Helpers.decodeBytes("957f008001808080ffe4ffffc8f3802401df40006aa05e017fa8d3f6004c804a"), + 0.toByte, + Helpers.decodeBytes("0180dd805b0000ff5400b997fd7f0b9b00de00fb03c47e37806a8186b94f07ff"), + Helpers.decodeBytes("01f07f60d100ffb970c3007f60ff7f24d4070bb8fffa7fca7f34c10001ffe39d"), + CAvlTree(treeData), + Helpers.decodeBytes("804101ff01000080a3ffbd006ac080098df132a7017f00649311ec0e00000100"), + 1L, + -1L, + 1, + Helpers.decodeBytes("e57f80885601b8ff348e01808000bcfc767f2dd37f0d01015030ec018080bc62"), + Helpers.decodeGroupElement("039bdbfa0b49cc6bef58297a85feff45f7bbeb500a9d2283004c74fcedd4bd2904"), + Helpers.decodeGroupElement("0361299207fa392231e23666f6945ae3e867b978e021d8d702872bde454e9abe9c"), + Helpers.decodeBytes("7f4f09012a807f01"), + CBigInt(new BigInteger("-e24990c47e15ed4d0178c44f1790cc72155d516c43c3e8684e75db3800a288", 16)), + Helpers.decodeBytes("7f0180") + ) + + testCases( + Seq((h1, Success(Helpers.decodeBytes("957f008001808080ffe4ffffc8f3802401df40006aa05e017fa8d3f6004c804a")))), + existingPropTest("id", { (x: Header) => x.id })) + + testCases( + Seq((h1, Success(0.toByte))), + existingPropTest("version", { (x: Header) => x.version })) + + testCases( + Seq((h1, Success(Helpers.decodeBytes("0180dd805b0000ff5400b997fd7f0b9b00de00fb03c47e37806a8186b94f07ff")))), + existingPropTest("parentId", { (x: Header) => x.parentId })) + + testCases( + Seq((h1, Success(Helpers.decodeBytes("01f07f60d100ffb970c3007f60ff7f24d4070bb8fffa7fca7f34c10001ffe39d")))), + existingPropTest("ADProofsRoot", { (x: Header) => x.ADProofsRoot})) + + testCases( + Seq((h1, Success(CAvlTree(treeData)))), + existingPropTest("stateRoot", { (x: Header) => x.stateRoot })) + + testCases( + Seq((h1, Success(Helpers.decodeBytes("804101ff01000080a3ffbd006ac080098df132a7017f00649311ec0e00000100")))), + existingPropTest("transactionsRoot", { (x: Header) => x.transactionsRoot })) + + testCases( + Seq((h1, Success(1L))), + existingPropTest("timestamp", { (x: Header) => x.timestamp })) + + testCases( + Seq((h1, Success(-1L))), + existingPropTest("nBits", { (x: Header) => x.nBits })) + + testCases( + Seq((h1, Success(1))), + existingPropTest("height", { (x: Header) => x.height })) + + testCases( + Seq((h1, Success(Helpers.decodeBytes("e57f80885601b8ff348e01808000bcfc767f2dd37f0d01015030ec018080bc62")))), + existingPropTest("extensionRoot", { (x: Header) => x.extensionRoot })) + + testCases( + Seq((h1, Success(Helpers.decodeGroupElement("039bdbfa0b49cc6bef58297a85feff45f7bbeb500a9d2283004c74fcedd4bd2904")))), + existingPropTest("minerPk", { (x: Header) => x.minerPk })) + + testCases( + Seq((h1, Success(Helpers.decodeGroupElement("0361299207fa392231e23666f6945ae3e867b978e021d8d702872bde454e9abe9c")))), + existingPropTest("powOnetimePk", { (x: Header) => x.powOnetimePk })) + + testCases( + Seq((h1, Success(Helpers.decodeBytes("7f4f09012a807f01")))), + existingPropTest("powNonce", { (x: Header) => x.powNonce })) + + testCases( + Seq((h1, Success(CBigInt(new BigInteger("-e24990c47e15ed4d0178c44f1790cc72155d516c43c3e8684e75db3800a288", 16))))), + existingPropTest("powDistance", { (x: Header) => x.powDistance })) + + testCases( + Seq((h1, Success(Helpers.decodeBytes("7f0180")))), + existingPropTest("votes", { (x: Header) => x.votes })) } property("Context properties equivalence") { - val eq = EqualityChecker(ctx) - val dataInputs = existingPropTest("dataInputs", { (x: Context) => x.dataInputs }) - - val dataInputs0 = existingFeature({ (x: Context) => x.dataInputs(0) }, - "{ (x: Context) => x.dataInputs(0) }", - FuncValue( - Vector((1, SContext)), - ByIndex( - MethodCall.typed[Value[SCollection[SBox.type]]]( - ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), - Vector(), - Map() + val samples = genSamples[Context](MinSuccessful(5)) + + val input = CostingBox( + false, + new ErgoBox( + 80946L, + new ErgoTree( + 16.toByte, + Vector( + SigmaPropConstant( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb"), + Helpers.decodeECPoint("023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d03"), + Helpers.decodeECPoint("03d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72"), + Helpers.decodeECPoint("037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441") + ) + ) + ) ), - IntConstant(0), + Right(ConstantPlaceholder(0, SSigmaProp)) + ), + Coll(), + Map( + ErgoBox.R4 -> ByteArrayConstant(Helpers.decodeBytes("34")), + ErgoBox.R5 -> TrueLeaf + ), + ModifierId @@ ("0000bfe96a7c0001e7a5ee00aafb80ff057fbe7f8c6680e33a3dc18001820100"), + 1.toShort, + 5 + ) + ) + + val dataBox = CostingBox( + false, + new ErgoBox( + -1L, + new ErgoTree( + 0.toByte, + Vector(), + Right(SigmaPropConstant(CSigmaProp(ProveDlog(Helpers.decodeECPoint("02af645874c3b53465a5e9d820eb207d6001258c3b708f0d31d7c2e342833dce64"))))) + ), + Coll((Digest32 @@ (ErgoAlgos.decodeUnsafe("8f0000ff009e7fff012427ff7fffcc35dfe680017f004ef3be1280e57fc40101")), 500L)), + Map( + ErgoBox.R9 -> LongConstant(-6985752043373238161L), + ErgoBox.R4 -> LongConstant(-7374898275229807247L), + ErgoBox.R6 -> ByteArrayConstant(Helpers.decodeBytes("00")), + ErgoBox.R5 -> LongConstant(-135729055492651903L), + ErgoBox.R8 -> TrueLeaf, + ErgoBox.R7 -> ByteArrayConstant( + Helpers.decodeBytes( + "5a017f1f9d2e01ff004f007f807f21b87f899e3380014900010c0101da80e9809d2a85ff010125cc017f74ed8c7f96b55efffadf7f7fffa700012e8085a915007f7f0001ffd5013e0180d58bb300c5b77f231e7f1c01013d807afd387f80287f80a51900" + ) + ) + ), + ModifierId @@ ("ff3f4e00d400ff00ffae3680927f782affc0004b9f0092ca98010080f60100c1"), + 9495.toShort, + 1000000 + ) + ) + + val header = CHeader( + Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), + 0.toByte, + Helpers.decodeBytes("7a7fe5347f09017818010062000001807f86808000ff7f66ffb07f7ad27f3362"), + Helpers.decodeBytes("c1d70ad9b1ffc1fb9a715fff19807f2401017fcd8b73db017f1cff77727fff08"), + CAvlTree( + AvlTreeData( + ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), + AvlTreeFlags(true, true, false), + 2147483647, None ) - )) + ), + 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") + ) - val dataInputs0id = existingFeature({ (x: Context) => x.dataInputs(0).id }, - "{ (x: Context) => x.dataInputs(0).id }", - FuncValue( - Vector((1, SContext)), - ExtractId( - ByIndex( - MethodCall.typed[Value[SCollection[SBox.type]]]( - ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), + val ctx = CostingDataContext( + _dataInputs = Coll[Box](dataBox), + headers = Coll[Header](header), + preHeader = CPreHeader( + 0.toByte, + Helpers.decodeBytes("1c597f88969600d2fffffdc47f00d8ffc555a9e85001000001c505ff80ff8f7f"), + -755484979487531112L, + 9223372036854775807L, + 11, + Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + Helpers.decodeBytes("007f00") + ), + inputs = Coll[Box](input), + outputs = Coll[Box]( + CostingBox( + false, + new ErgoBox( + 1000000L, + new ErgoTree( + 16.toByte, + Vector( + SigmaPropConstant( + CSigmaProp( + COR( + List( + ProveDHTuple( + Helpers.decodeECPoint("021b4c0f54304b9c427d5c87846dd56a2fa361cd34a6cb8a2090aef043c9383198"), + Helpers.decodeECPoint("026826a4a9d0ec937c24d72da381ee6b5e74e49fb79a6a23a03fe0aa2cab3448ba"), + Helpers.decodeECPoint("02535153378ce30df1b31137680748de728f8512d29dfeeb1f331ac6a787cd00d8"), + Helpers.decodeECPoint("03d00d0174cdffd7ce3b77ef45ef9573c18fb76929fb3340f7ceea8d0be9bf5c4a") + ), + ProveDlog(Helpers.decodeECPoint("02c702d83f83a5ec9674e17e5eb3ab3ae579768c945590f0fb10c1c4a388353c7c")), + ProveDHTuple( + Helpers.decodeECPoint("03bef02fb10347eef473730711ec313b2f013322e6dad32515bd172249420f25e5"), + Helpers.decodeECPoint("0365160972ed72d232f0cb5fa7909ac1647eb122942b421493def6a6051005d141"), + Helpers.decodeECPoint("035060119f4b47ccf12c4502657e9ee38dba92fc6b6b1807b75d5cdc1986754751"), + Helpers.decodeECPoint("02db7a6c1b51847ce5b1ba8e8c89b4ea5e68c5667f430e8bbe075ff4ea2877233a") + ) + ) + ) + ) + ) + ), + Right(ConstantPlaceholder(0, SSigmaProp)) + ), + Coll((Digest32 @@ (ErgoAlgos.decodeUnsafe("6f070152007f00005a00893ea1e98045ffa28f72da01ff7f01ff2d48eb793fd6")), 20000L)), + Map(ErgoBox.R5 -> LongConstant(1L), ErgoBox.R4 -> LongConstant(5008366408131208436L)), + ModifierId @@ ("26485d14a94ef18ec36227a838b98e11e910087be4c7e634f51391e4ea4d16ff"), + 0.toShort, + 11 + ) + ), + CostingBox( + false, + new ErgoBox( + 2769336982721999022L, + new ErgoTree( + 0.toByte, Vector(), - Map() + Right(SigmaPropConstant(CSigmaProp(ProveDlog(Helpers.decodeECPoint("02d13e1a8c31f32194761adc1cdcbaa746b3e049e682bba9308d8ee84576172991"))))) ), - IntConstant(0), - None + Coll((Digest32 @@ (ErgoAlgos.decodeUnsafe("6f070152007f00005a00893ea1e98045ffa28f72da01ff7f01ff2d48eb793fd6")), 500L)), + Map(), + ModifierId @@ ("26485d14a94ef18ec36227a838b98e11e910087be4c7e634f51391e4ea4d16ff"), + 1.toShort, + 0 ) ) - )) + ), + height = 11, + selfBox = input.copy(), // TODO HF: in 3.x implementation selfBox is never the same instance as input (see toSigmaContext) + lastBlockUtxoRootHash = CAvlTree( + AvlTreeData( + ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), + AvlTreeFlags(true, true, true), + 1211925457, + None + ) + ), + _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + vars = Coll[AnyValue](null, TestValue(Helpers.decodeBytes("00"), CollType(RType.ByteType)), TestValue(true, RType.BooleanType)), + false + ) - val preHeader = existingPropTest("preHeader", { (x: Context) => x.preHeader }) - val headers = existingPropTest("headers", { (x: Context) => x.headers }) - - val outputs = existingFeature({ (x: Context) => x.OUTPUTS }, - "{ (x: Context) => x.OUTPUTS }", FuncValue(Vector((1, SContext)), Outputs)) - val inputs = existingFeature({ (x: Context) => x.INPUTS }, - "{ (x: Context) => x.INPUTS }", FuncValue(Vector((1, SContext)), Inputs)) - val height = existingFeature({ (x: Context) => x.HEIGHT }, - "{ (x: Context) => x.HEIGHT }", FuncValue(Vector((1, SContext)), Height)) - val self = existingFeature({ (x: Context) => x.SELF }, - "{ (x: Context) => x.SELF }", FuncValue(Vector((1, SContext)), Self)) - val inputsMap = existingFeature( - { (x: Context) => x.INPUTS.map { (b: Box) => b.value } }, - "{ (x: Context) => x.INPUTS.map { (b: Box) => b.value } }", - FuncValue( - Vector((1, SContext)), - MapCollection(Inputs, FuncValue(Vector((3, SBox)), ExtractAmount(ValUse(3, SBox)))) - )) + test(samples, existingPropTest("dataInputs", { (x: Context) => x.dataInputs })) - val inputsMap2 = existingFeature( - { (x: Context) => x.INPUTS.map { (b: Box) => (b.value, b.value) } }, - """{ (x: Context) => - | x.INPUTS.map { (b: Box) => (b.value, b.value) } - |}""".stripMargin, - FuncValue( - Vector((1, SContext)), - MapCollection( - Inputs, - FuncValue( - Vector((3, SBox)), - BlockValue( - Vector(ValDef(5, List(), ExtractAmount(ValUse(3, SBox)))), - Tuple(Vector(ValUse(5, SLong), ValUse(5, SLong))) - ) - ) - ) - )) + testCases( + Seq( + (ctx, Success(dataBox)), + (ctx.copy(_dataInputs = Coll()), Failure(new ArrayIndexOutOfBoundsException("0"))) + ), + existingFeature({ (x: Context) => x.dataInputs(0) }, + "{ (x: Context) => x.dataInputs(0) }", + FuncValue( + Vector((1, SContext)), + ByIndex( + MethodCall.typed[Value[SCollection[SBox.type]]]( + ValUse(1, SContext), + SContext.getMethodByName("dataInputs"), + Vector(), + Map() + ), + IntConstant(0), + None + ) + )), + preGeneratedSamples = Some(samples)) + testCases( + Seq( + (ctx, Success(Helpers.decodeBytes("7da4b55971f19a78d007638464580f91a020ab468c0dbe608deb1f619e245bc3"))) + ), + existingFeature({ (x: Context) => x.dataInputs(0).id }, + "{ (x: Context) => x.dataInputs(0).id }", + FuncValue( + Vector((1, SContext)), + ExtractId( + ByIndex( + MethodCall.typed[Value[SCollection[SBox.type]]]( + ValUse(1, SContext), + SContext.getMethodByName("dataInputs"), + Vector(), + Map() + ), + IntConstant(0), + None + ) + ) + )), + preGeneratedSamples = Some(samples)) + + test(samples, existingPropTest("preHeader", { (x: Context) => x.preHeader })) + + test(samples, existingPropTest("headers", { (x: Context) => x.headers })) + + test(samples, existingFeature({ (x: Context) => x.OUTPUTS }, + "{ (x: Context) => x.OUTPUTS }", FuncValue(Vector((1, SContext)), Outputs))) + + test(samples, existingFeature({ (x: Context) => x.INPUTS }, + "{ (x: Context) => x.INPUTS }", FuncValue(Vector((1, SContext)), Inputs))) + + test(samples, existingFeature({ (x: Context) => x.HEIGHT }, + "{ (x: Context) => x.HEIGHT }", FuncValue(Vector((1, SContext)), Height))) + + test(samples, existingFeature({ (x: Context) => x.SELF }, + "{ (x: Context) => x.SELF }", FuncValue(Vector((1, SContext)), Self))) - val inputsMap3 = existingFeature( - { (x: Context) => - x.INPUTS.map { (b: Box) => - val pk = b.R4[Int].get - val value = longToByteArray(b.value) - (pk, value) - } - }, - """{ (x: Context) => - | x.INPUTS.map { (b: Box) => - | val pk = b.R4[Int].get - | val value = longToByteArray(b.value) - | (pk, value) - | } - |}""".stripMargin, - FuncValue( - Vector((1, SContext)), - MapCollection( - Inputs, - FuncValue( - Vector((3, SBox)), - Tuple( - Vector( - OptionGet(ExtractRegisterAs(ValUse(3, SBox), ErgoBox.R4, SOption(SInt))), - LongToByteArray(ExtractAmount(ValUse(3, SBox))) - ) - ) - ) - ) - )) + testCases( + Seq((ctx, Success(Coll[Long](80946L)))), + existingFeature( + { (x: Context) => x.INPUTS.map { (b: Box) => b.value } }, + "{ (x: Context) => x.INPUTS.map { (b: Box) => b.value } }", + FuncValue( + Vector((1, SContext)), + MapCollection(Inputs, FuncValue(Vector((3, SBox)), ExtractAmount(ValUse(3, SBox)))) + )), + preGeneratedSamples = Some(samples)) - val selfBoxIndex = existingFeature({ (x: Context) => x.selfBoxIndex }, - "{ (x: Context) => x.selfBoxIndex }", - FuncValue( - Vector((1, SContext)), - MethodCall.typed[Value[SInt.type]]( - ValUse(1, SContext), - SContext.getMethodByName("selfBoxIndex"), - Vector(), - Map() - ) - )) - ctx.selfBoxIndex shouldBe -1 // TODO HF: see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/603 + testCases( + Seq((ctx, Success(Coll((80946L, 80946L))))), + existingFeature( + { (x: Context) => x.INPUTS.map { (b: Box) => (b.value, b.value) } }, + """{ (x: Context) => + | x.INPUTS.map { (b: Box) => (b.value, b.value) } + |}""".stripMargin, + FuncValue( + Vector((1, SContext)), + MapCollection( + Inputs, + FuncValue( + Vector((3, SBox)), + BlockValue( + Vector(ValDef(5, List(), ExtractAmount(ValUse(3, SBox)))), + Tuple(Vector(ValUse(5, SLong), ValUse(5, SLong))) + ) + ) + ) + )), + preGeneratedSamples = Some(samples)) + + + testCases( + Seq((ctx, Failure(new InvalidType("Cannot getReg[Int](4): invalid type of value Value(Coll(52)) at id=4")))), + existingFeature( + { (x: Context) => + x.INPUTS.map { (b: Box) => + val pk = b.R4[Int].get + val value = longToByteArray(b.value) + (pk, value) + } + }, + """{ (x: Context) => + | x.INPUTS.map { (b: Box) => + | val pk = b.R4[Int].get + | val value = longToByteArray(b.value) + | (pk, value) + | } + |}""".stripMargin, + FuncValue( + Vector((1, SContext)), + MapCollection( + Inputs, + FuncValue( + Vector((3, SBox)), + Tuple( + Vector( + OptionGet(ExtractRegisterAs(ValUse(3, SBox), ErgoBox.R4, SOption(SInt))), + LongToByteArray(ExtractAmount(ValUse(3, SBox))) + ) + ) + ) + ) + )), + preGeneratedSamples = Some(samples)) + + testCases( + Seq((ctx, Success(-1))), + existingFeature({ (x: Context) => x.selfBoxIndex }, + "{ (x: Context) => x.selfBoxIndex }", + FuncValue( + Vector((1, SContext)), + MethodCall.typed[Value[SInt.type]]( + ValUse(1, SContext), + SContext.getMethodByName("selfBoxIndex"), + Vector(), + Map() + ) + )), + preGeneratedSamples = Some(samples)) + + // TODO HF: see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/603 + samples.foreach { c => + ctx.selfBoxIndex shouldBe -1 + } - val rootHash = existingPropTest("LastBlockUtxoRootHash", { (x: Context) => x.LastBlockUtxoRootHash }) + test(samples, + existingPropTest("LastBlockUtxoRootHash", { (x: Context) => x.LastBlockUtxoRootHash })) - val rootHashFlag = existingFeature( + test(samples, existingFeature( { (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }, "{ (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }", FuncValue( @@ -1968,212 +2880,415 @@ class SigmaDslSpec extends SigmaDslTesting { suite => Vector(), Map() ) - )) + ))) - val minerPubKey = existingPropTest("minerPubKey", { (x: Context) => x.minerPubKey }) + test(samples, existingPropTest("minerPubKey", { (x: Context) => x.minerPubKey })) - val getVar = existingFeature((x: Context) => x.getVar[Int](2).get, - "{ (x: Context) => getVar[Int](2).get }", - FuncValue(Vector((1, SContext)), OptionGet(GetVar(2.toByte, SOption(SInt))))) + testCases( + Seq((ctx, Failure(new InvalidType("Cannot getVar[Int](2): invalid type of value Value(true) at id=2")))), + existingFeature((x: Context) => x.getVar[Int](2).get, + "{ (x: Context) => getVar[Int](2).get }", + FuncValue(Vector((1, SContext)), OptionGet(GetVar(2.toByte, SOption(SInt))))), + preGeneratedSamples = Some(samples)) - Seq( - dataInputs, dataInputs0, dataInputs0id, preHeader, headers, - outputs, inputs, height, self, inputsMap, inputsMap2, inputsMap3, selfBoxIndex, - rootHash, rootHashFlag, minerPubKey, getVar).foreach(_.checkEquality(ctx)) + testCases( + Seq((ctx, Success(true))), + existingFeature((x: Context) => x.getVar[Boolean](2).get, + "{ (x: Context) => getVar[Boolean](2).get }", + FuncValue(Vector((1, SContext)), OptionGet(GetVar(2.toByte, SOption(SBoolean))))), + preGeneratedSamples = Some(samples)) } property("xorOf equivalence") { - val xor = existingFeature((x: Coll[Boolean]) => SigmaDsl.xorOf(x), - "{ (x: Coll[Boolean]) => xorOf(x) }", - FuncValue(Vector((1, SBooleanArray)), XorOf(ValUse(1, SBooleanArray)))) - forAll { x: Array[Boolean] => - xor.checkEquality(Colls.fromArray(x)) - } + // TODO HF: see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/640 + testCases( + Seq( + (Coll[Boolean](false), Success(false)), + (Coll[Boolean](true), Success(false)), + (Coll[Boolean](false, false), Success(false)), + (Coll[Boolean](false, true), Success(true)), + (Coll[Boolean](true, false), Success(true)), + (Coll[Boolean](true, true), Success(false)), + (Coll[Boolean](false, false, false), Success(false)), + (Coll[Boolean](false, false, true), Success(true)), + (Coll[Boolean](false, true, false), Success(true)), + (Coll[Boolean](false, true, true), Success(true)), + (Coll[Boolean](true, false, false), Success(true)), + (Coll[Boolean](true, false, true), Success(true)), + (Coll[Boolean](true, true, false), Success(true)), + (Coll[Boolean](true, true, true), Success(false)), + (Coll[Boolean](false, false, false, false), Success(false)), + (Coll[Boolean](false, false, false, true), Success(true)), + (Coll[Boolean](false, false, true, false), Success(true)), + (Coll[Boolean](false, false, true, true), Success(true)) + ), + existingFeature((x: Coll[Boolean]) => SigmaDsl.xorOf(x), + "{ (x: Coll[Boolean]) => xorOf(x) }", + FuncValue(Vector((1, SBooleanArray)), XorOf(ValUse(1, SBooleanArray))))) } property("LogicalNot equivalence") { - val not = existingFeature((x: Boolean) => !x, - "{ (x: Boolean) => !x }", - FuncValue(Vector((1, SBoolean)), LogicalNot(ValUse(1, SBoolean)))) - forAll { x: Boolean => not.checkEquality(x) } + testCases( + Seq( + (true, Success(false)), + (false, Success(true))), + existingFeature((x: Boolean) => !x, + "{ (x: Boolean) => !x }", + FuncValue(Vector((1, SBoolean)), LogicalNot(ValUse(1, SBoolean)))), true) } property("Numeric Negation equivalence") { - val negByte = existingFeature((x: Byte) => (-x).toByte, - "{ (x: Byte) => -x }", - FuncValue(Vector((1, SByte)), Negation(ValUse(1, SByte)))) - forAll { x: Byte => negByte.checkEquality(x) } + testCases( + Seq( + (Byte.MinValue, Success(Byte.MinValue)), // !!! + (-40.toByte, Success(40.toByte)), + (-1.toByte, Success(1.toByte)), + (0.toByte, Success(0.toByte)), + (1.toByte, Success(-1.toByte)), + (45.toByte, Success(-45.toByte)), + (127.toByte, Success(-127.toByte))), + existingFeature((x: Byte) => (-x).toByte, + "{ (x: Byte) => -x }", + FuncValue(Vector((1, SByte)), Negation(ValUse(1, SByte))))) - val negShort = existingFeature((x: Short) => (-x).toShort, - "{ (x: Short) => -x }", - FuncValue(Vector((1, SShort)), Negation(ValUse(1, SShort)))) - forAll { x: Short => negShort.checkEquality(x) } + testCases( + Seq( + (Short.MinValue, Success(Short.MinValue)), // special case! + ((Short.MinValue + 1).toShort, Success(32767.toShort)), + (-1528.toShort, Success(1528.toShort)), + (-1.toShort, Success(1.toShort)), + (0.toShort, Success(0.toShort)), + (1.toShort, Success(-1.toShort)), + (7586.toShort, Success(-7586.toShort)), + (Short.MaxValue, Success(-32767.toShort))), + existingFeature((x: Short) => (-x).toShort, + "{ (x: Short) => -x }", + FuncValue(Vector((1, SShort)), Negation(ValUse(1, SShort))))) - val negInt = existingFeature((x: Int) => -x, - "{ (x: Int) => -x }", - FuncValue(Vector((1, SInt)), Negation(ValUse(1, SInt)))) - forAll { x: Int => negInt.checkEquality(x) } + testCases( + Seq( + (Int.MinValue, Success(Int.MinValue)), // special case! + (Int.MinValue + 1, Success(2147483647)), + (-63509744, Success(63509744)), + (-1, Success(1)), + (0, Success(0)), + (1, Success(-1)), + (677062351, Success(-677062351)), + (Int.MaxValue, Success(-2147483647))), + existingFeature((x: Int) => -x, + "{ (x: Int) => -x }", + FuncValue(Vector((1, SInt)), Negation(ValUse(1, SInt))))) - val negLong = existingFeature((x: Long) => -x, - "{ (x: Long) => -x }", - FuncValue(Vector((1, SLong)), Negation(ValUse(1, SLong)))) - forAll { x: Long => negLong.checkEquality(x) } + testCases( + Seq( + (Long.MinValue, Success(Long.MinValue)), // special case! + (Long.MinValue + 1, Success(9223372036854775807L)), + (-957264171003115006L, Success(957264171003115006L)), + (-1L, Success(1L)), + (0L, Success(0L)), + (1L, Success(-1L)), + (340835904095777627L, Success(-340835904095777627L)), + (9223372036854775807L, Success(-9223372036854775807L))), + existingFeature((x: Long) => -x, + "{ (x: Long) => -x }", + FuncValue(Vector((1, SLong)), Negation(ValUse(1, SLong))))) - val negBigInt = existingFeature((x: BigInt) => x.negate(), - "{ (x: BigInt) => -x }", - FuncValue(Vector((1, SBigInt)), Negation(ValUse(1, SBigInt)))) - forAll { x: BigInt => negBigInt.checkEquality(x) } + testCases( + Seq( + (CBigInt(new BigInteger("-1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)), Success(CBigInt(new BigInteger("1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)))), + (CBigInt(new BigInteger("-1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)), Success(CBigInt(new BigInteger("1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)))), + (CBigInt(new BigInteger("-1ec9cca2c346cb72a1e65481eaa0627d", 16)), Success(CBigInt(new BigInteger("1ec9cca2c346cb72a1e65481eaa0627d", 16)))), + (CBigInt(new BigInteger("-8000000000000001", 16)), Success(CBigInt(new BigInteger("8000000000000001", 16)))), + (CBigInt(new BigInteger("-8000000000000000", 16)), Success(CBigInt(new BigInteger("8000000000000000", 16)))), + (CBigInt(new BigInteger("-48afe3e059821cd6", 16)), Success(CBigInt(new BigInteger("48afe3e059821cd6", 16)))), + (CBigInt(new BigInteger("-80000001", 16)), Success(CBigInt(new BigInteger("80000001", 16)))), + (CBigInt(new BigInteger("-80000000", 16)), Success(CBigInt(new BigInteger("80000000", 16)))), + (CBigInt(new BigInteger("-1", 16)), Success(CBigInt(new BigInteger("1", 16)))), + (CBigInt(new BigInteger("0", 16)), Success(CBigInt(new BigInteger("0", 16)))), + (CBigInt(new BigInteger("1", 16)), Success(CBigInt(new BigInteger("-1", 16)))), + (CBigInt(new BigInteger("7fffffff", 16)), Success(CBigInt(new BigInteger("-7fffffff", 16)))), + (CBigInt(new BigInteger("80000000", 16)), Success(CBigInt(new BigInteger("-80000000", 16)))), + (CBigInt(new BigInteger("90e8c3b6e8df65c", 16)), Success(CBigInt(new BigInteger("-90e8c3b6e8df65c", 16)))), + (CBigInt(new BigInteger("36aa93288257dcca141d0c01c5cef14c9d1c0f8507872e3fdd839a759636c78", 16)), Success(CBigInt(new BigInteger("-36aa93288257dcca141d0c01c5cef14c9d1c0f8507872e3fdd839a759636c78", 16))))), + existingFeature((x: BigInt) => x.negate(), + "{ (x: BigInt) => -x }", + FuncValue(Vector((1, SBigInt)), Negation(ValUse(1, SBigInt))))) } property("global functions equivalence") { - val groupGenerator = existingFeature({ (x: Int) => SigmaDsl.groupGenerator }, - "{ (x: Int) => groupGenerator }", - FuncValue( - Vector((1, SInt)), - MethodCall.typed[Value[SGroupElement.type]]( - Global, - SGlobal.getMethodByName("groupGenerator"), - Vector(), - Map() - ) - )) - - val groupGenerator2 = existingFeature({ (x: Int) => SigmaDsl.groupGenerator }, - "{ (x: Int) => Global.groupGenerator }", - FuncValue( - Vector((1, SInt)), - MethodCall.typed[Value[SGroupElement.type]]( - Global, - SGlobal.getMethodByName("groupGenerator"), - Vector(), - Map() - ) - )) - - forAll { dummyValue: Int => - Seq(groupGenerator, groupGenerator2).foreach(_.checkEquality(dummyValue)) - } - - - val exp = existingFeature({ (n: BigInt) => SigmaDsl.groupGenerator.exp(n) }, - "{ (n: BigInt) => groupGenerator.exp(n) }", - FuncValue( - Vector((1, SBigInt)), - Exponentiate( + testCases( + Seq( + (-1, Success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (1, Success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))), + existingFeature({ (x: Int) => SigmaDsl.groupGenerator }, + "{ (x: Int) => groupGenerator }", + FuncValue( + Vector((1, SInt)), MethodCall.typed[Value[SGroupElement.type]]( Global, SGlobal.getMethodByName("groupGenerator"), Vector(), Map() - ), - ValUse(1, SBigInt) - ) - )) + ) + ))) - forAll { n: BigInt => - exp.checkEquality(n) - } + testCases( + Seq( + (-1, Success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (1, Success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))), + existingFeature({ (x: Int) => SigmaDsl.groupGenerator }, + "{ (x: Int) => Global.groupGenerator }", + FuncValue( + Vector((1, SInt)), + MethodCall.typed[Value[SGroupElement.type]]( + Global, + SGlobal.getMethodByName("groupGenerator"), + Vector(), + Map() + ) + ))) - val xor = existingFeature((x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), - "{ (x: (Coll[Byte], Coll[Byte])) => xor(x._1, x._2) }", - FuncValue( - Vector((1, STuple(Vector(SByteArray, SByteArray)))), - Xor( - SelectField.typed[Value[SCollection[SByte.type]]]( - ValUse(1, STuple(Vector(SByteArray, SByteArray))), - 1.toByte - ), - SelectField.typed[Value[SCollection[SByte.type]]]( - ValUse(1, STuple(Vector(SByteArray, SByteArray))), - 2.toByte + testCases( + 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, + SGlobal.getMethodByName("groupGenerator"), + Vector(), + Map() + ), + ValUse(1, SBigInt) ) - ) - )) - forAll { x: (Coll[Byte], Coll[Byte]) => - xor.checkEquality(x) - } + ))) + + // TODO HF: fix semantics when the left collection is longer + testCases( + Seq( + ((Helpers.decodeBytes(""), Helpers.decodeBytes("")), Success(Helpers.decodeBytes(""))), + ((Helpers.decodeBytes("01"), Helpers.decodeBytes("01")), Success(Helpers.decodeBytes("00"))), + ((Helpers.decodeBytes("0100"), Helpers.decodeBytes("0101")), Success(Helpers.decodeBytes("0001"))), + ((Helpers.decodeBytes("01"), Helpers.decodeBytes("0101")), Success(Helpers.decodeBytes("00"))), + ((Helpers.decodeBytes("0100"), Helpers.decodeBytes("01")), Failure(new ArrayIndexOutOfBoundsException("1"))), + ((Helpers.decodeBytes("800136fe89afff802acea67128a0ff007fffe3498c8001806080012b"), + Helpers.decodeBytes("648018010a5d5800f5b400a580e7b4809b0cd273ff1230bfa800017f7fdb002749b3ac2b86ff")), + Success(Helpers.decodeBytes("e4812eff83f2a780df7aa6d4a8474b80e4f3313a7392313fc8800054"))) + ), + existingFeature((x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), + "{ (x: (Coll[Byte], Coll[Byte])) => xor(x._1, x._2) }", + FuncValue( + Vector((1, STuple(Vector(SByteArray, SByteArray)))), + Xor( + SelectField.typed[Value[SCollection[SByte.type]]]( + ValUse(1, STuple(Vector(SByteArray, SByteArray))), + 1.toByte + ), + SelectField.typed[Value[SCollection[SByte.type]]]( + ValUse(1, STuple(Vector(SByteArray, SByteArray))), + 2.toByte + ) + ) + ))) } property("Coll[Box] methods equivalence") { + val samples = genSamples[Coll[Box]](collOfN[Box](5), MinSuccessful(20)) + val b1 = CostingBox( + false, + new ErgoBox( + 1L, + new ErgoTree( + 0.toByte, + Vector(), + Right( + SigmaPropConstant( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b"), + Helpers.decodeECPoint("027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e419"), + Helpers.decodeECPoint("0257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281"), + Helpers.decodeECPoint("033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd5") + ) + ) + ) + ) + ), + Coll(), + Map( + ErgoBox.R4 -> ByteArrayConstant( + Helpers.decodeBytes( + "7200004cccdac3008001bc80ffc7ff9633bca3e501801380ff007900019d7f0001a8c9dfff5600d964011617ca00583f989c7f80007fee7f99b07f7f870067dc315180828080307fbdf400" + ) + ), + ErgoBox.R7 -> LongConstant(0L), + ErgoBox.R6 -> FalseLeaf, + ErgoBox.R5 -> ByteArrayConstant(Helpers.decodeBytes("7f")) + ), + ModifierId @@ ("7dffff48ab0000c101a2eac9ff17017f6180aa7fc6f2178000800179499380a5"), + 21591.toShort, + 638768 + ) + ) + val b2 = CostingBox( + false, + new ErgoBox( + 1000000000L, + new ErgoTree( + 0.toByte, + Vector(), + Right(BoolToSigmaProp(OR(ConcreteCollection(Array(FalseLeaf, AND(ConcreteCollection(Array(FalseLeaf, FalseLeaf), SBoolean))), SBoolean)))) + ), + Coll(), + Map(), + ModifierId @@ ("008677ffff7ff36dff00f68031140400007689ff014c9201ce8000a9ffe6ceff"), + 32767.toShort, + 32827 + ) + ) - val filter = existingFeature({ (x: Coll[Box]) => x.filter({ (b: Box) => b.value > 1 }) }, - "{ (x: Coll[Box]) => x.filter({(b: Box) => b.value > 1 }) }", - FuncValue( - Vector((1, SCollectionType(SBox))), - Filter( - ValUse(1, SCollectionType(SBox)), - FuncValue(Vector((3, SBox)), GT(ExtractAmount(ValUse(3, SBox)), LongConstant(1L))) - ) - )) + testCases( + Seq( + (Coll[Box](), Success(Coll[Box]())), + (Coll[Box](b1), Success(Coll[Box]())), + (Coll[Box](b1, b2), Success(Coll[Box](b2))) + ), + existingFeature({ (x: Coll[Box]) => x.filter({ (b: Box) => b.value > 1 }) }, + "{ (x: Coll[Box]) => x.filter({(b: Box) => b.value > 1 }) }", + FuncValue( + Vector((1, SCollectionType(SBox))), + Filter( + ValUse(1, SCollectionType(SBox)), + FuncValue(Vector((3, SBox)), GT(ExtractAmount(ValUse(3, SBox)), LongConstant(1L))) + ) + )), + preGeneratedSamples = Some(samples)) - val flatMap = existingFeature({ (x: Coll[Box]) => x.flatMap({ (b: Box) => b.propositionBytes }) }, - "{ (x: Coll[Box]) => x.flatMap({(b: Box) => b.propositionBytes }) }", - FuncValue( - Vector((1, SCollectionType(SBox))), - MethodCall.typed[Value[SCollection[SByte.type]]]( - ValUse(1, SCollectionType(SBox)), - SCollection.getMethodByName("flatMap").withConcreteTypes( - Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SByte) - ), - Vector(FuncValue(Vector((3, SBox)), ExtractScriptBytes(ValUse(3, SBox)))), - Map() - ) - )) + testCases( + Seq( + (Coll[Box](), Success(Coll[Byte]())), + (Coll[Box](b1), Success(Helpers.decodeBytes( + "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd5" + ))), + (Coll[Box](b1, b2), Success(Helpers.decodeBytes( + "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd500d197830201010096850200" + ))) + ), + existingFeature({ (x: Coll[Box]) => x.flatMap({ (b: Box) => b.propositionBytes }) }, + "{ (x: Coll[Box]) => x.flatMap({(b: Box) => b.propositionBytes }) }", + FuncValue( + Vector((1, SCollectionType(SBox))), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SCollectionType(SBox)), + SCollection.getMethodByName("flatMap").withConcreteTypes( + Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SByte) + ), + Vector(FuncValue(Vector((3, SBox)), ExtractScriptBytes(ValUse(3, SBox)))), + Map() + ) + )), + preGeneratedSamples = Some(samples)) - val zip = existingFeature({ (x: Coll[Box]) => x.zip(x) }, - "{ (x: Coll[Box]) => x.zip(x) }", - FuncValue( - Vector((1, SCollectionType(SBox))), - MethodCall.typed[Value[SCollection[STuple]]]( - ValUse(1, SCollectionType(SBox)), - SCollection.getMethodByName("zip").withConcreteTypes( - Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SBox) - ), - Vector(ValUse(1, SCollectionType(SBox))), - Map() - ) - )) + testCases( + Seq( + (Coll[Box](), Success(Coll[(Box, Box)]())), + (Coll[Box](b1), Success(Coll[(Box, Box)]((b1, b1)))), + (Coll[Box](b1, b2), Success(Coll[(Box, Box)]((b1, b1), (b2, b2)))) + ), + existingFeature({ (x: Coll[Box]) => x.zip(x) }, + "{ (x: Coll[Box]) => x.zip(x) }", + FuncValue( + Vector((1, SCollectionType(SBox))), + MethodCall.typed[Value[SCollection[STuple]]]( + ValUse(1, SCollectionType(SBox)), + SCollection.getMethodByName("zip").withConcreteTypes( + Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SBox) + ), + Vector(ValUse(1, SCollectionType(SBox))), + Map() + ) + )), + preGeneratedSamples = Some(samples)) - val size = existingFeature({ (x: Coll[Box]) => x.size }, - "{ (x: Coll[Box]) => x.size }", - FuncValue(Vector((1, SCollectionType(SBox))), SizeOf(ValUse(1, SCollectionType(SBox))))) + testCases( + Seq( + (Coll[Box](), Success(0)), + (Coll[Box](b1), Success(1)), + (Coll[Box](b1, b2), Success(2)) + ), + existingFeature({ (x: Coll[Box]) => x.size }, + "{ (x: Coll[Box]) => x.size }", + FuncValue(Vector((1, SCollectionType(SBox))), SizeOf(ValUse(1, SCollectionType(SBox))))), + preGeneratedSamples = Some(samples)) - val indices = existingFeature({ (x: Coll[Box]) => x.indices }, - "{ (x: Coll[Box]) => x.indices }", - FuncValue( - Vector((1, SCollectionType(SBox))), - MethodCall.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SCollectionType(SBox)), - SCollection.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SBox)), - Vector(), - Map() - ) - )) + testCases( + Seq( + (Coll[Box](), Success(Coll[Int]())), + (Coll[Box](b1), Success(Coll[Int](0))), + (Coll[Box](b1, b2), Success(Coll[Int](0, 1))) + ), + existingFeature({ (x: Coll[Box]) => x.indices }, + "{ (x: Coll[Box]) => x.indices }", + FuncValue( + Vector((1, SCollectionType(SBox))), + MethodCall.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SCollectionType(SBox)), + SCollection.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SBox)), + Vector(), + Map() + ) + )), + preGeneratedSamples = Some(samples)) - val forall = existingFeature({ (x: Coll[Box]) => x.forall({ (b: Box) => b.value > 1 }) }, - "{ (x: Coll[Box]) => x.forall({(b: Box) => b.value > 1 }) }", - FuncValue( - Vector((1, SCollectionType(SBox))), - ForAll( - ValUse(1, SCollectionType(SBox)), - FuncValue(Vector((3, SBox)), GT(ExtractAmount(ValUse(3, SBox)), LongConstant(1L))) - ) - )) + testCases( + Seq( + (Coll[Box](), Success(true)), + (Coll[Box](b1), Success(false)), + (Coll[Box](b1, b2), Success(false)) + ), + existingFeature({ (x: Coll[Box]) => x.forall({ (b: Box) => b.value > 1 }) }, + "{ (x: Coll[Box]) => x.forall({(b: Box) => b.value > 1 }) }", + FuncValue( + Vector((1, SCollectionType(SBox))), + ForAll( + ValUse(1, SCollectionType(SBox)), + FuncValue(Vector((3, SBox)), GT(ExtractAmount(ValUse(3, SBox)), LongConstant(1L))) + ) + )), + preGeneratedSamples = Some(samples)) - val exists = existingFeature({ (x: Coll[Box]) => x.exists({ (b: Box) => b.value > 1 }) }, - "{ (x: Coll[Box]) => x.exists({(b: Box) => b.value > 1 }) }", - FuncValue( - Vector((1, SCollectionType(SBox))), - Exists( - ValUse(1, SCollectionType(SBox)), - FuncValue(Vector((3, SBox)), GT(ExtractAmount(ValUse(3, SBox)), LongConstant(1L))) - ) - )) - - forAll(collOfN[Box](10)) { boxes: Coll[Box] => - Seq(filter, flatMap, zip, size, indices, forall, exists).foreach(_.checkEquality(boxes)) - } + testCases( + Seq( + (Coll[Box](), Success(false)), + (Coll[Box](b1), Success(false)), + (Coll[Box](b1, b2), Success(true)) + ), + existingFeature({ (x: Coll[Box]) => x.exists({ (b: Box) => b.value > 1 }) }, + "{ (x: Coll[Box]) => x.exists({(b: Box) => b.value > 1 }) }", + FuncValue( + Vector((1, SCollectionType(SBox))), + Exists( + ValUse(1, SCollectionType(SBox)), + FuncValue(Vector((3, SBox)), GT(ExtractAmount(ValUse(3, SBox)), LongConstant(1L))) + ) + )), + preGeneratedSamples = Some(samples)) } val collWithRangeGen = for { @@ -2182,152 +3297,184 @@ class SigmaDslSpec extends SigmaDslTesting { suite => r <- Gen.choose(l, arr.length - 1) } yield (arr, (l, r)) property("Coll patch method equivalence") { - val patch = existingFeature( - { (x: (Coll[Int], (Int, Int))) => - val coll = x._1 - val l = x._2._1; val r = x._2._2 - coll.patch(l, coll, r) - }, - """{ (x: (Coll[Int], (Int, Int))) => - | val coll = x._1 - | val l = x._2._1; val r = x._2._2 - | coll.patch(l, coll, r) - |}""".stripMargin, - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 1.toByte + val samples = genSamples(collWithRangeGen, MinSuccessful(50)) + testCases( + Seq( + ((Coll[Int](), (0, 0)), Success(Coll[Int]())), + ((Coll[Int](1), (0, 0)), Success(Coll[Int](1, 1))), + ((Coll[Int](1), (0, 1)), Success(Coll[Int](1))), + ((Coll[Int](1, 2), (0, 0)), Success(Coll[Int](1, 2, 1, 2))), + ((Coll[Int](1, 2), (1, 0)), Success(Coll[Int](1, 1, 2, 2))), + ((Coll[Int](1, 2), (0, 2)), Success(Coll[Int](1, 2))), + ((Coll[Int](1, 2), (0, 3)), Success(Coll[Int](1, 2))), + ((Coll[Int](1, 2), (1, 2)), Success(Coll[Int](1, 1, 2))), + ((Coll[Int](1, 2), (2, 0)), Success(Coll[Int](1, 2, 1, 2))), + ((Coll[Int](1, 2), (3, 0)), Success(Coll[Int](1, 2, 1, 2))), + ((Coll[Int](1, 2), (3, 1)), Success(Coll[Int](1, 2, 1, 2))), + ((Coll[Int](1, 2), (-1, 1)), Success(Coll[Int](1, 2, 2))) + ), + existingFeature( + { (x: (Coll[Int], (Int, Int))) => + val coll = x._1 + val l = x._2._1; val r = x._2._2 + coll.patch(l, coll, r) + }, + """{ (x: (Coll[Int], (Int, Int))) => + | val coll = x._1 + | val l = x._2._1; val r = x._2._2 + | coll.patch(l, coll, r) + |}""".stripMargin, + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), + BlockValue( + Vector( + ValDef( + 3, + List(), + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 1.toByte + ) + ), + ValDef( + 4, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 2.toByte + ) ) ), - ValDef( - 4, - List(), - SelectField.typed[Value[STuple]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 2.toByte - ) - ) - ), - MethodCall.typed[Value[SCollection[SInt.type]]]( - ValUse(3, SCollectionType(SInt)), - SCollection.getMethodByName("patch").withConcreteTypes(Map(STypeVar("IV") -> SInt)), - Vector( - SelectField.typed[Value[SInt.type]](ValUse(4, SPair(SInt, SInt)), 1.toByte), + MethodCall.typed[Value[SCollection[SInt.type]]]( ValUse(3, SCollectionType(SInt)), - SelectField.typed[Value[SInt.type]](ValUse(4, SPair(SInt, SInt)), 2.toByte) - ), - Map() + SCollection.getMethodByName("patch").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + Vector( + SelectField.typed[Value[SInt.type]](ValUse(4, SPair(SInt, SInt)), 1.toByte), + ValUse(3, SCollectionType(SInt)), + SelectField.typed[Value[SInt.type]](ValUse(4, SPair(SInt, SInt)), 2.toByte) + ), + Map() + ) ) - ) - )) + )), + preGeneratedSamples = Some(samples)) - forAll(collWithRangeGen) { data => - patch.checkEquality(data) - } } property("Coll updated method equivalence") { - val updated = existingFeature( - (x: (Coll[Int], (Int, Int))) => x._1.updated(x._2._1, x._2._2), - "{ (x: (Coll[Int], (Int, Int))) => x._1.updated(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 - ) - ) - ), - MethodCall.typed[Value[SCollection[SInt.type]]]( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 1.toByte - ), - SCollection.getMethodByName("updated").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + testCases( + // (coll, (index, elem)) + Seq( + ((Coll[Int](), (0, 0)), Failure(new IndexOutOfBoundsException("0"))), + ((Coll[Int](1), (0, 0)), Success(Coll[Int](0))), + ((Coll[Int](1, 2), (0, 0)), Success(Coll[Int](0, 2))), + ((Coll[Int](1, 2), (1, 0)), Success(Coll[Int](1, 0))), + ((Coll[Int](1, 2, 3), (2, 0)), Success(Coll[Int](1, 2, 0))), + ((Coll[Int](1, 2), (2, 0)), Failure(new IndexOutOfBoundsException("2"))), + ((Coll[Int](1, 2), (3, 0)), Failure(new IndexOutOfBoundsException("3"))), + ((Coll[Int](1, 2), (-1, 0)), Failure(new IndexOutOfBoundsException("-1"))) + ), + existingFeature( + (x: (Coll[Int], (Int, Int))) => x._1.updated(x._2._1, x._2._2), + "{ (x: (Coll[Int], (Int, Int))) => x._1.updated(x._2._1, x._2._2) }", + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), + BlockValue( Vector( - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 2.toByte + ) + ) ), - Map() + MethodCall.typed[Value[SCollection[SInt.type]]]( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 1.toByte + ), + SCollection.getMethodByName("updated").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + Vector( + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) + ), + Map() + ) ) - ) - )) - forAll { x: (Array[Int], Int) => - val size = x._1.size - whenever (size > 1) { - val index = getArrayIndex(size) - updated.checkEquality((Colls.fromArray(x._1), (index, x._2))) - } - } + ))) } property("Coll updateMany method equivalence") { - val updateMany = existingFeature( - (x: (Coll[Int], (Coll[Int], Coll[Int]))) => x._1.updateMany(x._2._1, x._2._2), - "{ (x: (Coll[Int], (Coll[Int], Coll[Int]))) => x._1.updateMany(x._2._1, x._2._2) }", - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SPair(SCollectionType(SInt), SCollectionType(SInt))))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[Value[STuple]]( - ValUse( - 1, - SPair(SCollectionType(SInt), SPair(SCollectionType(SInt), SCollectionType(SInt))) - ), - 2.toByte + val samples = genSamples( + for { + coll <- collGen[Int] + is <- genIndices(coll.length) + vs <- collOfN[Int](is.length) + } yield (coll, (is.toColl, vs)), + MinSuccessful(20)) + + testCases( + // (coll, (indexes, values)) + Seq( + ((Coll[Int](), (Coll(0), Coll(0))), Failure(new IndexOutOfBoundsException("0"))), + ((Coll[Int](), (Coll(0, 1), Coll(0, 0))), Failure(new IndexOutOfBoundsException("0"))), + ((Coll[Int](), (Coll(0, 1), Coll(0))), Failure(new IllegalArgumentException("requirement failed: Collections should have same length but was 2 and 1:\n xs=Coll(0,1);\n ys=Coll(0)"))), + ((Coll[Int](1), (Coll(0), Coll(0))), Success(Coll[Int](0))), + ((Coll[Int](1), (Coll(0, 1), Coll(0, 0))), Failure(new IndexOutOfBoundsException("1"))), + ((Coll[Int](1, 2), (Coll(0), Coll(0))), Success(Coll[Int](0, 2))), + ((Coll[Int](1, 2), (Coll(0, 1), Coll(0, 0))), Success(Coll[Int](0, 0))), + ((Coll[Int](1, 2), (Coll(0, 1, 2), Coll(0, 0, 0))), Failure(new IndexOutOfBoundsException("2"))), + ((Coll[Int](1, 2), (Coll(1), Coll(0))), Success(Coll[Int](1, 0))), + ((Coll[Int](1, 2, 3), (Coll(2), Coll(0))), Success(Coll[Int](1, 2, 0))), + ((Coll[Int](1, 2), (Coll(2), Coll(0))), Failure(new IndexOutOfBoundsException("2"))), + ((Coll[Int](1, 2), (Coll(3), Coll(0))), Failure(new IndexOutOfBoundsException("3"))), + ((Coll[Int](1, 2), (Coll(-1), Coll(0))), Failure(new IndexOutOfBoundsException("-1"))), + ((Coll[Int](10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140), + (Coll[Int](12, 12, 4, 11, 1, 8, 0, 1), Coll[Int](-10, -20, -30, -40, -50, -60, -70, -80))), + Success(Coll[Int](-70, -80, 30, 40, -30, 60, 70, 80, -60, 100, 110, -40, -20, 140))) + ), + existingFeature( + (x: (Coll[Int], (Coll[Int], Coll[Int]))) => x._1.updateMany(x._2._1, x._2._2), + "{ (x: (Coll[Int], (Coll[Int], Coll[Int]))) => x._1.updateMany(x._2._1, x._2._2) }", + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SPair(SCollectionType(SInt), SCollectionType(SInt))))), + BlockValue( + Vector( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse( + 1, + SPair(SCollectionType(SInt), SPair(SCollectionType(SInt), SCollectionType(SInt))) + ), + 2.toByte + ) ) - ) - ), - MethodCall.typed[Value[SCollection[SInt.type]]]( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SCollectionType(SInt), SCollectionType(SInt)))), - 1.toByte ), - SCollection.getMethodByName("updateMany").withConcreteTypes(Map(STypeVar("IV") -> SInt)), - Vector( + MethodCall.typed[Value[SCollection[SInt.type]]]( SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(3, SPair(SCollectionType(SInt), SCollectionType(SInt))), + ValUse(1, SPair(SCollectionType(SInt), SPair(SCollectionType(SInt), SCollectionType(SInt)))), 1.toByte ), - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(3, SPair(SCollectionType(SInt), SCollectionType(SInt))), - 2.toByte - ) - ), - Map() + SCollection.getMethodByName("updateMany").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + Vector( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(3, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 1.toByte + ), + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(3, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 2.toByte + ) + ), + Map() + ) ) - ) - )) - - val dataGen = for { - arr <- arrayGen[Int]; - len <- Gen.choose(0, arr.length) - indices <- Gen.containerOfN[Array, Int](len, Gen.choose(0, arr.length - 1)) - } yield (Colls.fromArray(arr), Colls.fromArray(indices)) - - forAll(dataGen) { data => - val coll = data._1 - val indices = data._2 - whenever (coll.length > 1) { - val vs = indices.reverse - val input = (coll, (indices, vs)) - updateMany.checkEquality(input) - } - } + )), + preGeneratedSamples = Some(samples)) } // TODO HF: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 @@ -2361,329 +3508,418 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Coll fold method equivalence") { - val fold = existingFeature( - { (x: (Coll[Byte], Int)) => x._1.foldLeft(x._2, { i: (Int, Byte) => i._1 + i._2 }) }, - "{ (x: (Coll[Byte], Int)) => x._1.fold(x._2, { (i1: Int, i2: Byte) => i1 + i2 }) }", - FuncValue( - Vector((1, SPair(SByteArray, SInt))), - Fold( - SelectField.typed[Value[SCollection[SByte.type]]](ValUse(1, SPair(SByteArray, SInt)), 1.toByte), - SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByteArray, SInt)), 2.toByte), - FuncValue( - Vector((3, SPair(SInt, SByte))), - ArithOp( - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SByte)), 1.toByte), - Upcast(SelectField.typed[Value[SByte.type]](ValUse(3, SPair(SInt, SByte)), 2.toByte), SInt), - OpCode @@ (-102.toByte) + val n = ExactNumeric.IntIsExactNumeric + testCases( + // (coll, initState) + Seq( + ((Coll[Byte](), 0), Success(0)), + ((Coll[Byte](), Int.MaxValue), Success(Int.MaxValue)), + ((Coll[Byte](1), Int.MaxValue - 1), Success(Int.MaxValue)), + ((Coll[Byte](1), Int.MaxValue), Failure(new ArithmeticException("integer overflow"))), + ((Coll[Byte](-1), Int.MinValue + 1), Success(Int.MinValue)), + ((Coll[Byte](-1), Int.MinValue), Failure(new ArithmeticException("integer overflow"))), + ((Coll[Byte](1, 2), 0), Success(3)), + ((Coll[Byte](1, -1), 0), Success(0)), + ((Coll[Byte](1, -1, 1), 0), Success(1)) + ), + existingFeature( + { (x: (Coll[Byte], Int)) => x._1.foldLeft(x._2, { i: (Int, Byte) => n.plus(i._1, i._2) }) }, + "{ (x: (Coll[Byte], Int)) => x._1.fold(x._2, { (i1: Int, i2: Byte) => i1 + i2 }) }", + FuncValue( + Vector((1, SPair(SByteArray, SInt))), + Fold( + SelectField.typed[Value[SCollection[SByte.type]]](ValUse(1, SPair(SByteArray, SInt)), 1.toByte), + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByteArray, SInt)), 2.toByte), + FuncValue( + Vector((3, SPair(SInt, SByte))), + ArithOp( + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SByte)), 1.toByte), + Upcast(SelectField.typed[Value[SByte.type]](ValUse(3, SPair(SInt, SByte)), 2.toByte), SInt), + OpCode @@ (-102.toByte) + ) ) ) - ) - )) + ))) + } - val indexOf = existingFeature( - { (x: (Coll[Byte], (Byte, Int))) => x._1.indexOf(x._2._1, x._2._2) }, - "{ (x: (Coll[Byte], (Byte, Int))) => x._1.indexOf(x._2._1, x._2._2) }", - FuncValue( - Vector((1, SPair(SByteArray, SPair(SByte, SInt)))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[Value[STuple]](ValUse(1, SPair(SByteArray, SPair(SByte, SInt))), 2.toByte) - ) - ), - MethodCall.typed[Value[SInt.type]]( - SelectField.typed[Value[SCollection[SByte.type]]]( - ValUse(1, SPair(SByteArray, SPair(SByte, SInt))), - 1.toByte - ), - SCollection.getMethodByName("indexOf").withConcreteTypes(Map(STypeVar("IV") -> SByte)), + property("Coll indexOf method equivalence") { + testCases( + // (coll, (elem: Byte, from: Int)) + Seq( + ((Coll[Byte](), (0.toByte, 0)), Success(-1)), + ((Coll[Byte](), (0.toByte, -1)), Success(-1)), + ((Coll[Byte](), (0.toByte, 1)), Success(-1)), + ((Coll[Byte](1), (0.toByte, 0)), Success(-1)), + ((Coll[Byte](1), (1.toByte, 0)), Success(0)), + ((Coll[Byte](1), (1.toByte, 1)), Success(-1)), + ((Coll[Byte](1, 1), (0.toByte, -1)), Success(-1)), + ((Coll[Byte](1, 1), (0.toByte, 0)), Success(-1)), + ((Coll[Byte](1, 1), (1.toByte, -1)), Success(0)), + ((Coll[Byte](1, 1), (1.toByte, 0)), Success(0)), + ((Coll[Byte](1, 1), (1.toByte, 1)), Success(1)), + ((Coll[Byte](1, 1), (1.toByte, 2)), Success(-1)), + ((Coll[Byte](1, 1), (1.toByte, 3)), Success(-1)), + ((Coll[Byte](1, 2, 3), (3.toByte, 0)), Success(2)), + ((Coll[Byte](1, 2, 3), (3.toByte, 1)), Success(2)), + ((Coll[Byte](1, 2, 3), (3.toByte, 2)), Success(2)), + ((Coll[Byte](1, 2, 3), (3.toByte, 3)), Success(-1)), + ((Helpers.decodeBytes("8085623fb7cd6b7f01801f00800100"), (0.toByte, -1)), Success(11)) + ), + existingFeature( + { (x: (Coll[Byte], (Byte, Int))) => x._1.indexOf(x._2._1, x._2._2) }, + "{ (x: (Coll[Byte], (Byte, Int))) => x._1.indexOf(x._2._1, x._2._2) }", + FuncValue( + Vector((1, SPair(SByteArray, SPair(SByte, SInt)))), + BlockValue( Vector( - SelectField.typed[Value[SByte.type]](ValUse(3, SPair(SByte, SInt)), 1.toByte), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SByte, SInt)), 2.toByte) + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]](ValUse(1, SPair(SByteArray, SPair(SByte, SInt))), 2.toByte) + ) ), - Map() + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SPair(SByteArray, SPair(SByte, SInt))), + 1.toByte + ), + SCollection.getMethodByName("indexOf").withConcreteTypes(Map(STypeVar("IV") -> SByte)), + Vector( + SelectField.typed[Value[SByte.type]](ValUse(3, SPair(SByte, SInt)), 1.toByte), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SByte, SInt)), 2.toByte) + ), + Map() + ) ) - ) - )) - - forAll { (coll: Coll[Byte], start: Short, value: Byte, from: Int) => - fold.checkEquality((coll, start)) - indexOf.checkEquality((coll, (value, from))) - } + ))) } property("Coll apply method equivalence") { - val apply = existingFeature((x: (Coll[Int], Int)) => x._1(x._2), - "{ (x: (Coll[Int], Int)) => x._1(x._2) }", - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SInt))), - ByIndex( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SInt)), - 1.toByte - ), - SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SCollectionType(SInt), SInt)), 2.toByte), - None - ) - )) - - forAll { (x: Coll[Int], i: Int) => - apply.checkEquality((x, i)) - } - } - - property("Coll getOrElse method equivalence") { - val getOrElse = existingFeature((x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2), - "{ (x: (Coll[Int], (Int, Int))) => x._1.getOrElse(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 - ) - ) - ), + testCases( + Seq( + ((Coll[Int](), 0), Failure(new ArrayIndexOutOfBoundsException("0"))), + ((Coll[Int](), -1), Failure(new ArrayIndexOutOfBoundsException("-1"))), + ((Coll[Int](1), 0), Success(1)), + ((Coll[Int](1), 1), Failure(new ArrayIndexOutOfBoundsException("1"))), + ((Coll[Int](1), -1), Failure(new ArrayIndexOutOfBoundsException("-1"))), + ((Coll[Int](1, 2), 1), Success(2)), + ((Coll[Int](1, 2), 1), Success(2)), + ((Coll[Int](1, 2), 2), Failure(new ArrayIndexOutOfBoundsException("2"))) + ), + existingFeature((x: (Coll[Int], Int)) => x._1(x._2), + "{ (x: (Coll[Int], Int)) => x._1(x._2) }", + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SInt))), ByIndex( SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + ValUse(1, SPair(SCollectionType(SInt), SInt)), 1.toByte ), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), - Some(SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte)) + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SCollectionType(SInt), SInt)), 2.toByte), + None ) - ) - )) + ))) + } - forAll { x: (Coll[Int], (Int, Int)) => - getOrElse.checkEquality(x) - } + property("Coll getOrElse method equivalence") { + val default = 10 + testCases( + // (coll, (index, default)) + Seq( + ((Coll[Int](), (0, default)), Success(default)), + ((Coll[Int](), (-1, default)), Success(default)), + ((Coll[Int](1), (0, default)), Success(1)), + ((Coll[Int](1), (1, default)), Success(default)), + ((Coll[Int](1), (-1, default)), Success(default)), + ((Coll[Int](1, 2), (0, default)), Success(1)), + ((Coll[Int](1, 2), (1, default)), Success(2)), + ((Coll[Int](1, 2), (2, default)), Success(default)), + ((Coll[Int](1, 2), (-1, default)), Success(default)) + ), + existingFeature((x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2), + "{ (x: (Coll[Int], (Int, Int))) => x._1.getOrElse(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 + ) + ) + ), + ByIndex( + 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), + Some(SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte)) + ) + ) + ))) } property("Tuple size method equivalence") { - val size = existingFeature((x: (Int, Int)) => 2, - "{ (x: (Int, Int)) => x.size }", - FuncValue(Vector((1, SPair(SInt, SInt))), IntConstant(2))) - forAll { x: (Int, Int) => - size.checkExpected(x, 2) - } + testCases( + Seq( + ((0, 0), Success(2)), + ((1, 2), Success(2)) + ), + existingFeature((x: (Int, Int)) => 2, + "{ (x: (Int, Int)) => x.size }", + FuncValue(Vector((1, SPair(SInt, SInt))), IntConstant(2)))) } property("Tuple apply method equivalence") { - val apply1 = existingFeature((x: (Int, Int)) => x._1, - "{ (x: (Int, Int)) => x(0) }", - FuncValue( - Vector((1, SPair(SInt, SInt))), - SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte) - )) - val apply2 = existingFeature((x: (Int, Int)) => x._2, - "{ (x: (Int, Int)) => x(1) }", - FuncValue( - Vector((1, SPair(SInt, SInt))), - SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 2.toByte) - )) - forAll { x: (Int, Int) => - apply1.checkExpected(x, x._1) - apply2.checkExpected(x, x._2) - } + val samples = genSamples[(Int, Int)](DefaultMinSuccessful) + testCases( + Seq(((1, 2), Success(1))), + existingFeature((x: (Int, Int)) => x._1, + "{ (x: (Int, Int)) => x(0) }", + FuncValue( + Vector((1, SPair(SInt, SInt))), + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte) + )), + preGeneratedSamples = Some(samples)) + testCases( + Seq(((1, 2), Success(2))), + existingFeature((x: (Int, Int)) => x._2, + "{ (x: (Int, Int)) => x(1) }", + FuncValue( + Vector((1, SPair(SInt, SInt))), + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 2.toByte) + )), + preGeneratedSamples = Some(samples)) } property("Coll map method equivalence") { val n = ExactNumeric.IntIsExactNumeric - val map = existingFeature((x: Coll[Int]) => x.map({ (v: Int) => n.plus(v, 1) }), - "{ (x: Coll[Int]) => x.map({ (v: Int) => v + 1 }) }") - forAll { x: Coll[Int] => - map.checkEquality(x) - } + testCases( + Seq( + (Coll[Int](), Success(Coll[Int]())), + (Coll[Int](1), Success(Coll[Int](2))), + (Coll[Int](1, 2), Success(Coll[Int](2, 3))), + (Coll[Int](1, 2, Int.MaxValue), Failure(new ArithmeticException("integer overflow"))) + ), + existingFeature((x: Coll[Int]) => x.map({ (v: Int) => n.plus(v, 1) }), + "{ (x: Coll[Int]) => x.map({ (v: Int) => v + 1 }) }", + FuncValue( + Vector((1, SCollectionType(SInt))), + MapCollection( + ValUse(1, SCollectionType(SInt)), + FuncValue(Vector((3, SInt)), ArithOp(ValUse(3, SInt), IntConstant(1), OpCode @@ (-102.toByte))) + ) + ))) } property("Coll slice method equivalence") { - val slice = 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 samples = genSamples(collWithRangeGen, DefaultMinSuccessful) + testCases( + // (coll, (from, until)) + Seq( + ((Coll[Int](), (-1, 0)), Success(Coll[Int]())), + ((Coll[Int](), (0, 0)), Success(Coll[Int]())), + ((Coll[Int](1), (0, 0)), Success(Coll[Int]())), + ((Coll[Int](1), (0, -1)), Success(Coll[Int]())), + ((Coll[Int](1), (1, 1)), Success(Coll[Int]())), + ((Coll[Int](1), (-1, 1)), Success(Coll[Int](1))), + ((Coll[Int](1, 2), (1, 1)), Success(Coll[Int]())), + ((Coll[Int](1, 2), (1, 0)), Success(Coll[Int]())), + ((Coll[Int](1, 2), (1, 2)), Success(Coll[Int](2))), + ((Coll[Int](1, 2, 3, 4), (1, 3)), Success(Coll[Int](2, 3))) + ), + 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) - ) - ) - )) - forAll(collWithRangeGen) { x => - slice.checkEquality(x) - } - } - - property("Coll append method equivalence") { - val append = existingFeature( - { (x: (Coll[Int], (Int, Int))) => - val sliced: Coll[Int] = x._1.slice(x._2._1, x._2._2) - val toAppend: Coll[Int] = x._1 - sliced.append(toAppend) - }, - """{ (x: (Coll[Int], (Int, Int))) => - |val sliced: Coll[Int] = x._1.slice(x._2._1, x._2._2) - |val toAppend: Coll[Int] = x._1 - |sliced.append(toAppend) - |}""".stripMargin, - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), - BlockValue( - Vector( - ValDef( - 3, - List(), + Slice( SelectField.typed[Value[SCollection[SInt.type]]]( ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), 1.toByte - ) - ), - ValDef( - 4, - List(), - SelectField.typed[Value[STuple]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 2.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)) + } + + property("Coll append method equivalence") { + testCases( + Seq( + (Coll[Int](), Coll[Int]()) -> Success(Coll[Int]()), + (Coll[Int](), Coll[Int](1)) -> Success(Coll[Int](1)), + (Coll[Int](1), Coll[Int]()) -> Success(Coll[Int](1)), + (Coll[Int](1), Coll[Int](2)) -> Success(Coll[Int](1, 2)), + (Coll[Int](1), Coll[Int](2, 3)) -> Success(Coll[Int](1, 2, 3)), + (Coll[Int](1, 2), Coll[Int](3)) -> Success(Coll[Int](1, 2, 3)), + (Coll[Int](1, 2), Coll[Int](3, 4)) -> Success(Coll[Int](1, 2, 3, 4)) + ), + 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( - Slice( - ValUse(3, SCollectionType(SInt)), - SelectField.typed[Value[SInt.type]](ValUse(4, SPair(SInt, SInt)), 1.toByte), - SelectField.typed[Value[SInt.type]](ValUse(4, SPair(SInt, SInt)), 2.toByte) + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 1.toByte ), - ValUse(3, SCollectionType(SInt)) + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), + 2.toByte + ) ) - ) - )) - - forAll(collWithRangeGen) { x => - append.checkEquality(x) - } + ))) } property("Option methods equivalence") { - val get = existingFeature({ (x: Option[Long]) => x.get }, - "{ (x: Option[Long]) => x.get }", - FuncValue(Vector((1, SOption(SLong))), OptionGet(ValUse(1, SOption(SLong))))) + testCases( + Seq( + (None -> Failure(new NoSuchElementException("None.get"))), + (Some(10L) -> Success(10L))), + existingFeature({ (x: Option[Long]) => x.get }, + "{ (x: Option[Long]) => x.get }", + FuncValue(Vector((1, SOption(SLong))), OptionGet(ValUse(1, SOption(SLong)))))) - val isDefined = existingFeature({ (x: Option[Long]) => x.isDefined }, - "{ (x: Option[Long]) => x.isDefined }", - FuncValue(Vector((1, SOption(SLong))), OptionIsDefined(ValUse(1, SOption(SLong))))) + testCases( + Seq( + (None -> Success(false)), + (Some(10L) -> Success(true))), + existingFeature({ (x: Option[Long]) => x.isDefined }, + "{ (x: Option[Long]) => x.isDefined }", + FuncValue(Vector((1, SOption(SLong))), OptionIsDefined(ValUse(1, SOption(SLong)))))) - val getOrElse = 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)))) + testCases( + Seq( + (None -> Success(1L)), + (Some(10L) -> Success(10L))), + 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))))) - val filter = existingFeature({ (x: Option[Long]) => x.filter({ (v: Long) => v == 1} ) }, - "{ (x: Option[Long]) => x.filter({ (v: Long) => v == 1 }) }", - FuncValue( - Vector((1, SOption(SLong))), - MethodCall.typed[Value[SOption[SLong.type]]]( - ValUse(1, SOption(SLong)), - SOption.getMethodByName("filter").withConcreteTypes(Map(STypeVar("T") -> SLong)), - Vector(FuncValue(Vector((3, SLong)), EQ(ValUse(3, SLong), LongConstant(1L)))), - Map() - ) - )) + testCases( + Seq( + (None -> Success(None)), + (Some(10L) -> Success(None)), + (Some(1L) -> Success(Some(1L)))), + existingFeature({ (x: Option[Long]) => x.filter({ (v: Long) => v == 1} ) }, + "{ (x: Option[Long]) => x.filter({ (v: Long) => v == 1 }) }", + FuncValue( + Vector((1, SOption(SLong))), + MethodCall.typed[Value[SOption[SLong.type]]]( + ValUse(1, SOption(SLong)), + SOption.getMethodByName("filter").withConcreteTypes(Map(STypeVar("T") -> SLong)), + Vector(FuncValue(Vector((3, SLong)), EQ(ValUse(3, SLong), LongConstant(1L)))), + Map() + ) + ))) val n = ExactNumeric.LongIsExactNumeric - val map = existingFeature({ (x: Option[Long]) => x.map( (v: Long) => n.plus(v, 1) ) }, - "{ (x: Option[Long]) => x.map({ (v: Long) => v + 1 }) }", - FuncValue( - Vector((1, SOption(SLong))), - MethodCall.typed[Value[SOption[SLong.type]]]( - ValUse(1, SOption(SLong)), - SOption.getMethodByName("map").withConcreteTypes( - Map(STypeVar("T") -> SLong, STypeVar("R") -> SLong) - ), - Vector( - FuncValue( - Vector((3, SLong)), - ArithOp(ValUse(3, SLong), LongConstant(1L), OpCode @@ (-102.toByte)) - ) - ), - Map() - ) - )) - - val isEmpty = newFeature({ (x: Option[Long]) => x.isEmpty }, - "{ (x: Option[Long]) => x.isEmpty }") - - forAll { x: Option[Long] => - Seq(get, isDefined, getOrElse, filter, map, isEmpty).foreach(_.checkEquality(x)) - } + testCases( + Seq( + (None -> Success(None)), + (Some(10L) -> Success(Some(11L))), + (Some(Long.MaxValue) -> Failure(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 }) }", + FuncValue( + Vector((1, SOption(SLong))), + MethodCall.typed[Value[SOption[SLong.type]]]( + ValUse(1, SOption(SLong)), + SOption.getMethodByName("map").withConcreteTypes( + Map(STypeVar("T") -> SLong, STypeVar("R") -> SLong) + ), + Vector( + FuncValue( + Vector((3, SLong)), + ArithOp(ValUse(3, SLong), LongConstant(1L), OpCode @@ (-102.toByte)) + ) + ), + Map() + ) + ))) } // TODO HF: implement Option.fold - property("Option fold method") { + property("Option new methods") { + val isEmpty = newFeature({ (x: Option[Long]) => x.isEmpty }, + "{ (x: Option[Long]) => x.isEmpty }") + 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] => - fold.checkEquality(x) + Seq(isEmpty, fold).map(_.checkEquality(x)) } } property("Option fold workaround method") { val n = ExactNumeric.LongIsExactNumeric - val foldWorkaround = existingFeature({ (x: Option[Long]) => x.fold(5.toLong)( (v: Long) => n.plus(v, 1) ) }, - """{(x: Option[Long]) => - | def f(opt: Long): Long = opt + 1 - | if (x.isDefined) f(x.get) else 5L - |}""".stripMargin, - FuncValue( - Vector((1, SOption(SLong))), - If( - OptionIsDefined(ValUse(1, SOption(SLong))), - Apply( - FuncValue( - Vector((3, SLong)), - ArithOp(ValUse(3, SLong), LongConstant(1L), OpCode @@ (-102.toByte)) + testCases( + Seq( + (None -> Success(5L)), + (Some(0L) -> Success(1L)), + (Some(Long.MaxValue) -> Failure(new ArithmeticException("long overflow"))) + ), + existingFeature({ (x: Option[Long]) => x.fold(5.toLong)( (v: Long) => n.plus(v, 1) ) }, + """{(x: Option[Long]) => + | def f(opt: Long): Long = opt + 1 + | if (x.isDefined) f(x.get) else 5L + |}""".stripMargin, + FuncValue( + Vector((1, SOption(SLong))), + If( + OptionIsDefined(ValUse(1, SOption(SLong))), + Apply( + FuncValue( + Vector((3, SLong)), + ArithOp(ValUse(3, SLong), LongConstant(1L), OpCode @@ (-102.toByte)) + ), + Array(OptionGet(ValUse(1, SOption(SLong)))) ), - Array(OptionGet(ValUse(1, SOption(SLong)))) - ), - LongConstant(5L) - ) - )) - forAll { x: Option[Long] => - foldWorkaround.checkEquality(x) - } + LongConstant(5L) + ) + ))) } property("blake2b256, sha256 equivalence") { - val blake2b256 = existingFeature((x: Coll[Byte]) => SigmaDsl.blake2b256(x), - "{ (x: Coll[Byte]) => blake2b256(x) }", - FuncValue(Vector((1, SByteArray)), CalcBlake2b256(ValUse(1, SByteArray)))) - - val sha256 = existingFeature((x: Coll[Byte]) => SigmaDsl.sha256(x), - "{ (x: Coll[Byte]) => sha256(x) }", - FuncValue(Vector((1, SByteArray)), CalcSha256(ValUse(1, SByteArray)))) + testCases( + Seq( + Coll[Byte]() -> Success(Helpers.decodeBytes("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8")), + Helpers.decodeBytes("e0ff0105ffffac31010017ff33") -> + Success(Helpers.decodeBytes("33707eed9aab64874ff2daa6d6a378f61e7da36398fb36c194c7562c9ff846b5")) + ), + existingFeature((x: Coll[Byte]) => SigmaDsl.blake2b256(x), + "{ (x: Coll[Byte]) => blake2b256(x) }", + FuncValue(Vector((1, SByteArray)), CalcBlake2b256(ValUse(1, SByteArray))))) - forAll { x: Coll[Byte] => - Seq(blake2b256, sha256).foreach(_.checkEquality(x)) - } + testCases( + Seq( + Coll[Byte]() -> Success(Helpers.decodeBytes("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")), + Helpers.decodeBytes("e0ff0105ffffac31010017ff33") -> + Success(Helpers.decodeBytes("367d0ec2cdc14aac29d5beb60c2bfc86d5a44a246308659af61c1b85fa2ca2cc")) + ), + existingFeature((x: Coll[Byte]) => SigmaDsl.sha256(x), + "{ (x: Coll[Byte]) => sha256(x) }", + FuncValue(Vector((1, SByteArray)), CalcSha256(ValUse(1, SByteArray))))) } property("print") { @@ -2691,106 +3927,204 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("sigmaProp equivalence") { - lazy val eq = existingFeature((x: Boolean) => sigmaProp(x), - "{ (x: Boolean) => sigmaProp(x) }", - FuncValue(Vector((1, SBoolean)), BoolToSigmaProp(ValUse(1, SBoolean)))) - forAll { x: Boolean => eq.checkEquality(x) } + testCases( + Seq( + (false, Success(CSigmaProp(TrivialProp.FalseProp))), + (true, Success(CSigmaProp(TrivialProp.TrueProp)))), + existingFeature((x: Boolean) => sigmaProp(x), + "{ (x: Boolean) => sigmaProp(x) }", + FuncValue(Vector((1, SBoolean)), BoolToSigmaProp(ValUse(1, SBoolean))))) } property("atLeast equivalence") { - lazy val atLeast = existingFeature((x: Coll[SigmaProp]) => SigmaDsl.atLeast(x.size - 1, x), - "{ (x: Coll[SigmaProp]) => atLeast(x.size - 1, x) }", - FuncValue( - Vector((1, SCollectionType(SSigmaProp))), - AtLeast( - ArithOp(SizeOf(ValUse(1, SCollectionType(SSigmaProp))), IntConstant(1), OpCode @@ (-103.toByte)), - ValUse(1, SCollectionType(SSigmaProp)) + testCases( + Seq( + Coll[SigmaProp]( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("028cf1686a3275e54441c68d789151bdec40d34d62188bbadce170d96d4ce399b0"), + Helpers.decodeECPoint("03e53afbe18efff6586f5b8bda1c3262aac65ede384f12b4a56ecb74e16d73efc5"), + Helpers.decodeECPoint("02614b14a8c6c6b4b7ce017d72fbca7f9218b72c16bdd88f170ffb300b106b9014"), + Helpers.decodeECPoint("034cc5572276adfa3e283a3f1b0f0028afaadeaa362618c5ec43262d8cefe7f004") + ) + )) -> Success(CSigmaProp(TrivialProp.TrueProp)), + Coll[SigmaProp]( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("028cf1686a3275e54441c68d789151bdec40d34d62188bbadce170d96d4ce399b0"), + Helpers.decodeECPoint("03e53afbe18efff6586f5b8bda1c3262aac65ede384f12b4a56ecb74e16d73efc5"), + Helpers.decodeECPoint("02614b14a8c6c6b4b7ce017d72fbca7f9218b72c16bdd88f170ffb300b106b9014"), + Helpers.decodeECPoint("034cc5572276adfa3e283a3f1b0f0028afaadeaa362618c5ec43262d8cefe7f004") + ) + ), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03f7eacae7476a9ef082513a6a70ed6b208aafad0ade5f614ac6cfa2176edd0d69"))), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("023bddd50b917388cd2c4f478f3ea9281bf03a252ee1fefe9c79f800afaa8d86ad"))) + ) -> Success( + CSigmaProp( + CTHRESHOLD( + 2, + Array( + ProveDHTuple( + Helpers.decodeECPoint("028cf1686a3275e54441c68d789151bdec40d34d62188bbadce170d96d4ce399b0"), + Helpers.decodeECPoint("03e53afbe18efff6586f5b8bda1c3262aac65ede384f12b4a56ecb74e16d73efc5"), + Helpers.decodeECPoint("02614b14a8c6c6b4b7ce017d72fbca7f9218b72c16bdd88f170ffb300b106b9014"), + Helpers.decodeECPoint("034cc5572276adfa3e283a3f1b0f0028afaadeaa362618c5ec43262d8cefe7f004") + ), + ProveDlog(Helpers.decodeECPoint("03f7eacae7476a9ef082513a6a70ed6b208aafad0ade5f614ac6cfa2176edd0d69")), + ProveDlog(Helpers.decodeECPoint("023bddd50b917388cd2c4f478f3ea9281bf03a252ee1fefe9c79f800afaa8d86ad")) + ) + ) + ) ) - )) - forAll(collGen[SigmaProp]) { x: Coll[SigmaProp] => - atLeast.checkEquality(x) - } + ), + existingFeature((x: Coll[SigmaProp]) => SigmaDsl.atLeast(x.size - 1, x), + "{ (x: Coll[SigmaProp]) => atLeast(x.size - 1, x) }", + FuncValue( + Vector((1, SCollectionType(SSigmaProp))), + AtLeast( + ArithOp(SizeOf(ValUse(1, SCollectionType(SSigmaProp))), IntConstant(1), OpCode @@ (-103.toByte)), + ValUse(1, SCollectionType(SSigmaProp)) + ) + ))) } property("&& sigma equivalence") { - lazy val SigmaAnd1 = existingFeature( - (x: (SigmaProp, SigmaProp)) => x._1 && x._2, - "{ (x:(SigmaProp, SigmaProp)) => x._1 && x._2 }", - FuncValue( - Vector((1, SPair(SSigmaProp, SSigmaProp))), - SigmaAnd( - Seq( - SelectField.typed[Value[SSigmaProp.type]](ValUse(1, SPair(SSigmaProp, SSigmaProp)), 1.toByte), - SelectField.typed[Value[SSigmaProp.type]](ValUse(1, SPair(SSigmaProp, SSigmaProp)), 2.toByte) - ) - ) - )) - lazy val SigmaAnd2 = existingFeature( - (x: (SigmaProp, Boolean)) => x._1 && sigmaProp(x._2), - "{ (x:(SigmaProp, Boolean)) => x._1 && sigmaProp(x._2) }", - FuncValue( - Vector((1, SPair(SSigmaProp, SBoolean))), - SigmaAnd( - Seq( - SelectField.typed[Value[SSigmaProp.type]](ValUse(1, SPair(SSigmaProp, SBoolean)), 1.toByte), - BoolToSigmaProp( - SelectField.typed[Value[SBoolean.type]](ValUse(1, SPair(SSigmaProp, SBoolean)), 2.toByte) + testCases( + Seq( + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + Success( + CSigmaProp( + CAND( + Seq( + ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), + ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) + ) + ) + ) + ), + (CSigmaProp(TrivialProp.TrueProp), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(TrivialProp.FalseProp), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + Success(CSigmaProp(TrivialProp.FalseProp)), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), + CSigmaProp(TrivialProp.TrueProp)) -> + Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), + CSigmaProp(TrivialProp.FalseProp)) -> + Success(CSigmaProp(TrivialProp.FalseProp)) + ), + existingFeature( + (x: (SigmaProp, SigmaProp)) => x._1 && x._2, + "{ (x:(SigmaProp, SigmaProp)) => x._1 && x._2 }", + FuncValue( + Vector((1, SPair(SSigmaProp, SSigmaProp))), + SigmaAnd( + Seq( + SelectField.typed[Value[SSigmaProp.type]](ValUse(1, SPair(SSigmaProp, SSigmaProp)), 1.toByte), + SelectField.typed[Value[SSigmaProp.type]](ValUse(1, SPair(SSigmaProp, SSigmaProp)), 2.toByte) ) ) - ) - )) + ))) - forAll { x: (SigmaProp, SigmaProp) => - SigmaAnd1.checkEquality(x) - } - forAll { x: (SigmaProp, Boolean) => - SigmaAnd2.checkEquality(x) - } + testCases( + Seq( + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> + Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> + Success(CSigmaProp(TrivialProp.FalseProp)) + ), + existingFeature( + (x: (SigmaProp, Boolean)) => x._1 && sigmaProp(x._2), + "{ (x:(SigmaProp, Boolean)) => x._1 && sigmaProp(x._2) }", + FuncValue( + Vector((1, SPair(SSigmaProp, SBoolean))), + SigmaAnd( + Seq( + SelectField.typed[Value[SSigmaProp.type]](ValUse(1, SPair(SSigmaProp, SBoolean)), 1.toByte), + BoolToSigmaProp( + SelectField.typed[Value[SBoolean.type]](ValUse(1, SPair(SSigmaProp, SBoolean)), 2.toByte) + ) + ) + ) + ))) } property("|| sigma equivalence") { - lazy val SigmaOr1 = existingFeature( - (x: (SigmaProp, SigmaProp)) => x._1 || x._2, - "{ (x:(SigmaProp, SigmaProp)) => x._1 || x._2 }", - FuncValue( - Vector((1, SPair(SSigmaProp, SSigmaProp))), - SigmaOr( - Seq( - SelectField.typed[SigmaPropValue](ValUse(1, SPair(SSigmaProp, SSigmaProp)), 1.toByte), - SelectField.typed[SigmaPropValue](ValUse(1, SPair(SSigmaProp, SSigmaProp)), 2.toByte) - ) - ) - )) - lazy val SigmaOr2 = existingFeature( - (x: (SigmaProp, Boolean)) => x._1 || sigmaProp(x._2), - "{ (x:(SigmaProp, Boolean)) => x._1 || sigmaProp(x._2) }", - FuncValue( - Vector((1, SPair(SSigmaProp, SBoolean))), - SigmaOr( - Seq( - SelectField.typed[SigmaPropValue](ValUse(1, SPair(SSigmaProp, SBoolean)), 1.toByte), - BoolToSigmaProp( - SelectField.typed[BoolValue](ValUse(1, SPair(SSigmaProp, SBoolean)), 2.toByte) + testCases( + Seq( + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + Success( + CSigmaProp( + COR( + Seq( + ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), + ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) + ) + ) + ) + ), + (CSigmaProp(TrivialProp.FalseProp), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(TrivialProp.TrueProp), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + Success(CSigmaProp(TrivialProp.TrueProp)), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), + CSigmaProp(TrivialProp.FalseProp)) -> + Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), + CSigmaProp(TrivialProp.TrueProp)) -> + Success(CSigmaProp(TrivialProp.TrueProp)) + ), + existingFeature( + (x: (SigmaProp, SigmaProp)) => x._1 || x._2, + "{ (x:(SigmaProp, SigmaProp)) => x._1 || x._2 }", + FuncValue( + Vector((1, SPair(SSigmaProp, SSigmaProp))), + SigmaOr( + Seq( + SelectField.typed[SigmaPropValue](ValUse(1, SPair(SSigmaProp, SSigmaProp)), 1.toByte), + SelectField.typed[SigmaPropValue](ValUse(1, SPair(SSigmaProp, SSigmaProp)), 2.toByte) ) ) - ) - )) + ))) - forAll { x: (SigmaProp, SigmaProp) => - SigmaOr1.checkEquality(x) - } - forAll { x: (SigmaProp, Boolean) => - SigmaOr2.checkEquality(x) - } + testCases( + Seq( + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> + Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> + Success(CSigmaProp(TrivialProp.TrueProp)) + ), + existingFeature( + (x: (SigmaProp, Boolean)) => x._1 || sigmaProp(x._2), + "{ (x:(SigmaProp, Boolean)) => x._1 || sigmaProp(x._2) }", + FuncValue( + Vector((1, SPair(SSigmaProp, SBoolean))), + SigmaOr( + Seq( + SelectField.typed[SigmaPropValue](ValUse(1, SPair(SSigmaProp, SBoolean)), 1.toByte), + BoolToSigmaProp( + SelectField.typed[BoolValue](ValUse(1, SPair(SSigmaProp, SBoolean)), 2.toByte) + ) + ) + ) + ))) } property("SigmaProp.propBytes equivalence") { - lazy val propBytes = existingFeature((x: SigmaProp) => x.propBytes, - "{ (x: SigmaProp) => x.propBytes }", - FuncValue(Vector((1, SSigmaProp)), SigmaPropBytes(ValUse(1, SSigmaProp)))) - forAll { x: SigmaProp => - propBytes.checkEquality(x) - } + testCases( + Seq( + CSigmaProp(ProveDlog(Helpers.decodeECPoint("039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6f"))) -> + Success(Helpers.decodeBytes("0008cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6f")) + ), + existingFeature((x: SigmaProp) => x.propBytes, + "{ (x: SigmaProp) => x.propBytes }", + FuncValue(Vector((1, SSigmaProp)), SigmaPropBytes(ValUse(1, SSigmaProp))))) } // TODO HF: implement allZK func https://github.com/ScorexFoundation/sigmastate-interpreter/issues/543 @@ -2812,49 +4146,81 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("allOf equivalence") { - lazy val allOf = existingFeature((x: Coll[Boolean]) => SigmaDsl.allOf(x), - "{ (x: Coll[Boolean]) => allOf(x) }", - FuncValue(Vector((1, SBooleanArray)), AND(ValUse(1, SBooleanArray)))) - - forAll { x: Coll[Boolean] => - allOf.checkEquality(x) - } + testCases( + Seq( + (Coll[Boolean]() -> Success(true)), + (Coll[Boolean](true) -> Success(true)), + (Coll[Boolean](false) -> Success(false)), + (Coll[Boolean](false, false) -> Success(false)), + (Coll[Boolean](false, true) -> Success(false)), + (Coll[Boolean](true, false) -> Success(false)), + (Coll[Boolean](true, true) -> Success(true)), + (Coll[Boolean](true, false, false) -> Success(false)), + (Coll[Boolean](true, false, true) -> Success(false)), + (Coll[Boolean](true, true, false) -> Success(false)), + (Coll[Boolean](true, true, true) -> Success(true)) + ), + existingFeature((x: Coll[Boolean]) => SigmaDsl.allOf(x), + "{ (x: Coll[Boolean]) => allOf(x) }", + FuncValue(Vector((1, SBooleanArray)), AND(ValUse(1, SBooleanArray))))) } property("anyOf equivalence") { - lazy val anyOf = existingFeature((x: Coll[Boolean]) => SigmaDsl.anyOf(x), - "{ (x: Coll[Boolean]) => anyOf(x) }", - FuncValue(Vector((1, SBooleanArray)), OR(ValUse(1, SBooleanArray)))) - - forAll { x: Coll[Boolean] => - anyOf.checkEquality(x) - } + testCases( + Seq( + (Coll[Boolean]() -> Success(false)), + (Coll[Boolean](true) -> Success(true)), + (Coll[Boolean](false) -> Success(false)), + (Coll[Boolean](false, false) -> Success(false)), + (Coll[Boolean](false, true) -> Success(true)), + (Coll[Boolean](true, false) -> Success(true)), + (Coll[Boolean](true, true) -> Success(true)), + (Coll[Boolean](true, false, false) -> Success(true)), + (Coll[Boolean](true, false, true) -> Success(true)), + (Coll[Boolean](true, true, false) -> Success(true)), + (Coll[Boolean](true, true, true) -> Success(true)) + ), + existingFeature((x: Coll[Boolean]) => SigmaDsl.anyOf(x), + "{ (x: Coll[Boolean]) => anyOf(x) }", + FuncValue(Vector((1, SBooleanArray)), OR(ValUse(1, SBooleanArray))))) } property("proveDlog equivalence") { - val proveDlog = existingFeature({ (x: GroupElement) => SigmaDsl.proveDlog(x) }, - "{ (x: GroupElement) => proveDlog(x) }", - FuncValue(Vector((1, SGroupElement)), CreateProveDlog(ValUse(1, SGroupElement)))) - forAll { x: GroupElement => - proveDlog.checkEquality(x) - } + testCases( + Seq( + (Helpers.decodeGroupElement("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69") -> + Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69"))))) + ), + existingFeature({ (x: GroupElement) => SigmaDsl.proveDlog(x) }, + "{ (x: GroupElement) => proveDlog(x) }", + FuncValue(Vector((1, SGroupElement)), CreateProveDlog(ValUse(1, SGroupElement))))) } property("proveDHTuple equivalence") { - val proveDHTuple = existingFeature({ (x: GroupElement) => SigmaDsl.proveDHTuple(x, x, x, x) }, - "{ (x: GroupElement) => proveDHTuple(x, x, x, x) }", - FuncValue( - Vector((1, SGroupElement)), - CreateProveDHTuple( - ValUse(1, SGroupElement), - ValUse(1, SGroupElement), - ValUse(1, SGroupElement), - ValUse(1, SGroupElement) - ) - )) - forAll { x: GroupElement => - proveDHTuple.checkEquality(x) - } + testCases( + Seq( + (Helpers.decodeGroupElement("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") -> Success( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") + ) + ) + )) + ), + existingFeature({ (x: GroupElement) => SigmaDsl.proveDHTuple(x, x, x, x) }, + "{ (x: GroupElement) => proveDHTuple(x, x, x, x) }", + FuncValue( + Vector((1, SGroupElement)), + CreateProveDHTuple( + ValUse(1, SGroupElement), + ValUse(1, SGroupElement), + ValUse(1, SGroupElement), + ValUse(1, SGroupElement) + ) + ))) } } diff --git a/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala b/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala index d6f5acc5d2..1d0e4a8c0a 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala @@ -2,16 +2,19 @@ package special.sigma import org.scalatest.prop.PropertyChecks import sigmastate.interpreter.Interpreter.ScriptEnv -import org.scalacheck.Gen -import org.scalactic.source -import org.scalatest.{PropSpec, Matchers, Tag} +import org.scalacheck.{Arbitrary, Gen} +import org.scalatest.{PropSpec, Matchers} -import scala.util.{DynamicVariable, Success, Failure, Try} +import scala.util.{Success, Failure, Try} import sigmastate.Values.SValue import scalan.RType import org.ergoplatform.dsl.{SigmaContractSyntax, TestContractSpec} -import sigmastate.eval.IRContext +import sigmastate.eval.{IRContext, SigmaDsl} import sigmastate.helpers.SigmaPPrint +import special.collection.Coll + +import scala.math.Ordering +import scala.reflect.ClassTag class SigmaDslTesting extends PropSpec with PropertyChecks @@ -66,6 +69,15 @@ class SigmaDslTesting extends PropSpec index.sample.get } + /** Generate indices for an array of a given length. + * @return unordered array of indices with possibly repeated elements + */ + def genIndices(arrLength: Int): Gen[Array[Int]] = for { + nIndexes <- Gen.choose(0, arrLength) + indices <- Gen.containerOfN[Array, Int](nIndexes, Gen.choose(0, arrLength - 1)) + } yield indices + + case class EqualityChecker[T: RType](obj: T) { def apply[R: RType](dslFunc: T => R)(script: String) = checkEq(func[T, R](script))(dslFunc)(obj) @@ -76,7 +88,7 @@ class SigmaDslTesting extends PropSpec case object ExistingFeature extends FeatureType case object AddedFeature extends FeatureType - val LogInputOutputDefault: Boolean = false + val LogScriptDefault: Boolean = false /** Test case descriptor of the language feature. * @@ -93,7 +105,7 @@ class SigmaDslTesting extends PropSpec oldImpl: () => CompiledFunc[A, B], newImpl: () => CompiledFunc[A, B], printExpectedExpr: Boolean = true, - logInputOutput: Boolean = LogInputOutputDefault + logScript: Boolean = LogScriptDefault ) { def printExpectedExprOff = copy(printExpectedExpr = false) @@ -156,8 +168,8 @@ class SigmaDslTesting extends PropSpec val newRes = checkEq(scalaFunc)(newF)(input) newRes shouldBe oldRes } - if (this.logInputOutput || logInputOutput) - println(s"(${SigmaPPrint(input)}, ${SigmaPPrint(oldRes)}), // $script") + if (logInputOutput) + println(s"(${SigmaPPrint(input, height = 550, width = 150)}, ${SigmaPPrint(oldRes, height = 550, width = 150)}),${if (logScript) " // " + script else ""}") oldRes case AddedFeature => val oldRes = Try(oldF(input)) @@ -229,18 +241,192 @@ class SigmaDslTesting extends PropSpec FeatureTest(AddedFeature, script, scalaFunc, Option(expectedExpr), oldImpl, newImpl) } - val targetVersion = new DynamicVariable[Int](4) + val contextGen: Gen[Context] = ergoLikeContextGen.map(c => c.toSigmaContext(IR, false)) + implicit val arbContext = Arbitrary(contextGen) + + /** NOTE, this should be `def` to allow overriding of generatorDrivenConfig in derived Spec classes. */ + def DefaultMinSuccessful: MinSuccessful = MinSuccessful(generatorDrivenConfig.minSuccessful) + + val PrintTestCasesDefault: Boolean = false + val FailOnTestVectorsDefault: Boolean = true + + /** Test the given test cases with expected results (aka test vectors). + * NOTE, is some cases (such as Context, Box, etc) sample generation is time consuming, so it + * makes sense to factor it out. + * @param preGeneratedSamples optional pre-generated samples to reduce execution time + */ + def testCases[A: Ordering : Arbitrary : ClassTag, B] + (cases: Seq[(A, Try[B])], + f: FeatureTest[A, B], + printTestCases: Boolean = PrintTestCasesDefault, + failOnTestVectors: Boolean = FailOnTestVectorsDefault, + preGeneratedSamples: Option[Seq[A]] = None): Unit = { - val versions: Seq[Int] = Array(4) + val table = Table(("x", "y"), cases:_*) + forAll(table) { (x: A, expectedRes: Try[B]) => + val res = f.checkEquality(x, printTestCases) - protected override def property(testName: String, testTags: Tag*)(testFun: => Any /* Assertion */)(implicit pos: source.Position): Unit = { - super.property(testName, testTags:_*) { - for (version <- versions) { - targetVersion.withValue(version) { - val a = testFun + // TODO HF: remove this `if` once newImpl is implemented + if (f.featureType == ExistingFeature) { + (res, expectedRes) match { + case (Failure(exception), Failure(expectedException)) => + exception.getClass shouldBe expectedException.getClass + case _ => + if (failOnTestVectors) { + assertResult(expectedRes, s"Actual: ${SigmaPPrint(res, height = 150).plainText}")(res) + } + else { + if (expectedRes != res) { + print("\nSuggested Expected Result: ") + SigmaPPrint.pprintln(res, height = 150) + } + } } } } + preGeneratedSamples match { + case Some(samples) => + test(samples, f, printTestCases) + case None => + test(f, printTestCases) + } + } + + /** Generate samples in sorted order. + * @param gen generator to be used for sample generation + * @param config generation configuration + * @return array-backed ordered sequence of samples + */ + def genSamples[A: Ordering: ClassTag](gen: Gen[A], config: PropertyCheckConfigParam): Seq[A] = { + implicit val arb = Arbitrary(gen) + genSamples[A](config) } + /** Generate samples in sorted order. + * @param config generation configuration + * @return array-backed ordered sequence of samples + */ + def genSamples[A: Arbitrary: Ordering: ClassTag](config: PropertyCheckConfigParam): Seq[A] = { + val inputs = scala.collection.mutable.ArrayBuilder.make[A]() + forAll(config) { (x: A) => + inputs += x + } + inputs.result().sorted + } + + def test[A: Ordering : ClassTag, B] + (samples: Seq[A], + f: FeatureTest[A, B], + printTestCases: Boolean): Unit = { + + // then tests them in the sorted order, this will output a nice log of test cases + samples.foreach { x => + f.checkEquality(x, printTestCases) + } + } + + def test[A: Ordering : ClassTag, B](samples: Seq[A], f: FeatureTest[A, B]): Unit = { + test(samples, f, PrintTestCasesDefault) + } + + def test[A: Arbitrary : Ordering : ClassTag, B] + (f: FeatureTest[A, B], + printTestCases: Boolean = PrintTestCasesDefault): Unit = { + // first generate all test inputs + val samples = genSamples[A](DefaultMinSuccessful) + // then test them + test(samples, f, printTestCases) + } + + trait GroupElementOrdering extends Ordering[GroupElement] { + /** Compares `x: ECPoint` string representation with `y: ECPoint` string for order. + * @returns a negative integer, zero, or a positive integer as the + * `x` is less than, equal to, or greater than `y`. + */ + def compare(x: GroupElement, y: GroupElement) = { + SigmaDsl.toECPoint(x).toString.compareTo(SigmaDsl.toECPoint(y).toString) + } + } + implicit object GroupElementOrdering extends GroupElementOrdering + + trait AvlTreeOrdering extends Ordering[AvlTree] { + /** Compares this `x: AvlTree` string representation with `y: AvlTree` string for order. + * @returns a negative integer, zero, or a positive integer as the + * `x` is less than, equal to, or greater than `y`. + */ + def compare(x: AvlTree, y: AvlTree) = { + x.toString.compareTo(y.toString) + } + } + implicit object AvlTreeOrdering extends AvlTreeOrdering + + class CollOrdering[T: Ordering] extends Ordering[Coll[T]] { + implicit val O = implicitly[Ordering[Iterable[T]]] + + /** Compares this `x: Coll` with `y: Coll` using Ordering for underlying Array. + * @returns a negative integer, zero, or a positive integer as the + * `x` is less than, equal to, or greater than `y`. + */ + def compare(x: Coll[T], y: Coll[T]) = { + O.compare(x.toArray, y.toArray) + } + } + implicit def collOrdering[T: Ordering]: Ordering[Coll[T]] = new CollOrdering[T] + + trait BoxOrdering extends Ordering[Box] { + /** Compares this `x: Box` string representation with `y: Box` string for order. + * @returns a negative integer, zero, or a positive integer as the + * `x` is less than, equal to, or greater than `y`. + */ + def compare(x: Box, y: Box) = { + x.toString.compareTo(y.toString) + } + } + implicit object BoxOrdering extends BoxOrdering + + trait PreHeaderOrdering extends Ordering[PreHeader] { + /** Compares this `x: PreHeader` with `y: PreHeader` using block height. + * @returns a negative integer, zero, or a positive integer as the + * `x` is less than, equal to, or greater than `y`. + */ + def compare(x: PreHeader, y: PreHeader) = { + Ordering.Int.compare(x.height, y.height) + } + } + implicit object PreHeaderOrdering extends PreHeaderOrdering + + trait HeaderOrdering extends Ordering[Header] { + /** Compares this `x: Header` with `y: Header` using block height. + * @returns a negative integer, zero, or a positive integer as the + * `x` is less than, equal to, or greater than `y`. + */ + def compare(x: Header, y: Header) = { + Ordering.Int.compare(x.height, y.height) + } + } + implicit object HeaderOrdering extends HeaderOrdering + + trait ContextOrdering extends Ordering[Context] { + val O = Ordering[(Int, Coll[Byte])] + + /** Compares this `x: Context` with `y: Context` using block height and SELF.id. + * @returns a negative integer, zero, or a positive integer as the + * `x` is less than, equal to, or greater than `y`. + */ + def compare(x: Context, y: Context) = { + O.compare((x.HEIGHT, x.SELF.id), (y.HEIGHT, y.SELF.id)) + } + } + implicit object ContextOrdering extends ContextOrdering + + trait SigmaPropOrdering extends Ordering[SigmaProp] { + /** Compares this `x: SigmaProp` with `y: SigmaProp` using string representation. + * @returns a negative integer, zero, or a positive integer as the + * `x` is less than, equal to, or greater than `y`. + */ + def compare(x: SigmaProp, y: SigmaProp) = { + x.toString.compareTo(y.toString) + } + } + implicit object SigmaPropOrdering extends SigmaPropOrdering } diff --git a/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala b/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala index 832e6e4ab8..da555c8165 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala @@ -23,7 +23,10 @@ trait SigmaTestingData extends SigmaTestingCommons with SigmaTypeGens { containerOfN[Array, T](n, g).map(Colls.fromArray(_)) } - val bytesGen: Gen[Array[Byte]] = containerOfN[Array, Byte](100, Arbitrary.arbByte.arbitrary) + val bytesGen: Gen[Array[Byte]] = for { + len <- Gen.choose(0, 100) + arr <- containerOfN[Array, Byte](len, Arbitrary.arbByte.arbitrary) + } yield arr val bytesCollGen = bytesGen.map(Colls.fromArray(_)) val intsCollGen = arrayGen[Int].map(Colls.fromArray(_))