From dd62bd7475f3a4f4d1399bdca8e9dd2361e36255 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 11 Jun 2024 17:15:07 +0300 Subject: [PATCH 01/22] failing tests --- .../utxo/BasicOpsSpecification.scala | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..87a3697462 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import sigma.Extensions.ArrayOps +import sigma.VersionContext import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -157,6 +158,47 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Lazy evaluation of default in Option.getOrElse") { + val customExt = Map ( + 1.toByte -> IntConstant(5) + ).toSeq + def optTest() = test("getOrElse", env, customExt, + """{ + | getVar[Int](1).getOrElse(getVar[Int](44).get) > 0 + |} + | + |""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + optTest() + } else { + an[Exception] shouldBe thrownBy(optTest()) + } + } + + property("Lazy evaluation of default in Coll.getOrElse") { + val customExt = Map ( + 1.toByte -> IntConstant(5) + ).toSeq + def optTest() = test("getOrElse", env, customExt, + """{ + | val c = Coll[Int](1) + | c.getOrElse(0, getVar[Int](44).get) > 0 + |} + | + |""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + optTest() + } else { + an[Exception] shouldBe thrownBy(optTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From aaef77e76f8b4be4c97520a56cc6b1492bb70791 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 11 Jun 2024 17:46:20 +0300 Subject: [PATCH 02/22] close #906: lazy evaluation of default in Option.getOrElse and Coll.getOrElse --- .../main/scala/sigma/ast/transformers.scala | 43 ++++++++++++++----- .../utxo/BasicOpsSpecification.scala | 10 ++--- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/transformers.scala b/data/shared/src/main/scala/sigma/ast/transformers.scala index 939da79d98..8d7e689a18 100644 --- a/data/shared/src/main/scala/sigma/ast/transformers.scala +++ b/data/shared/src/main/scala/sigma/ast/transformers.scala @@ -10,7 +10,7 @@ import sigma.eval.ErgoTreeEvaluator.DataEnv import sigma.serialization.CoreByteWriter.ArgInfo import sigma.serialization.OpCodes import sigma.serialization.ValueCodes.OpCode -import sigma.{Box, Coll, Evaluation} +import sigma.{Box, Coll, Evaluation, VersionContext} // TODO refactor: remove this trait as it doesn't have semantic meaning @@ -258,10 +258,22 @@ case class ByIndex[V <: SType](input: Value[SCollection[V]], val indexV = index.evalTo[Int](env) default match { case Some(d) => - val dV = d.evalTo[V#WrappedType](env) - Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased - addCost(ByIndex.costKind) - inputV.getOrElse(indexV, dV) + if (VersionContext.current.isV6SoftForkActivated) { + // lazy evaluation of default in 6.0 + addCost(ByIndex.costKind) + if (inputV.isDefinedAt(indexV)) { + inputV.apply(indexV) + } else { + val dV = d.evalTo[V#WrappedType](env) + Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased + inputV.getOrElse(indexV, dV) + } + } else { + val dV = d.evalTo[V#WrappedType](env) + Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased + addCost(ByIndex.costKind) + inputV.getOrElse(indexV, dV) + } case _ => addCost(ByIndex.costKind) inputV.apply(indexV) @@ -613,11 +625,22 @@ case class OptionGetOrElse[V <: SType](input: Value[SOption[V]], default: Value[ override val opType = SFunc(IndexedSeq(input.tpe, tpe), tpe) override def tpe: V = input.tpe.elemType protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - val inputV = input.evalTo[Option[V#WrappedType]](env) - val dV = default.evalTo[V#WrappedType](env) // TODO v6.0: execute lazily (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/906) - Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased - addCost(OptionGetOrElse.costKind) - inputV.getOrElse(dV) + if(VersionContext.current.isV6SoftForkActivated) { + // lazy evaluation of default in 6.0 + val inputV = input.evalTo[Option[V#WrappedType]](env) + addCost(OptionGetOrElse.costKind) + inputV.getOrElse { + val dV = default.evalTo[V#WrappedType](env) + Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased + dV + } + } else { + val inputV = input.evalTo[Option[V#WrappedType]](env) + val dV = default.evalTo[V#WrappedType](env) + Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased + addCost(OptionGetOrElse.costKind) + inputV.getOrElse(dV) + } } } object OptionGetOrElse extends ValueCompanion with FixedCostValueCompanion { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 87a3697462..c1489355e6 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -166,7 +166,6 @@ class BasicOpsSpecification extends CompilerTestingCommons """{ | getVar[Int](1).getOrElse(getVar[Int](44).get) > 0 |} - | |""".stripMargin, null ) @@ -179,15 +178,12 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("Lazy evaluation of default in Coll.getOrElse") { - val customExt = Map ( - 1.toByte -> IntConstant(5) - ).toSeq - def optTest() = test("getOrElse", env, customExt, + def optTest() = test("getOrElse", env, ext, """{ | val c = Coll[Int](1) - | c.getOrElse(0, getVar[Int](44).get) > 0 + | c.getOrElse(0, getVar[Int](44).get) > 0 && + | c.getOrElse(1, c.getOrElse(0, getVar[Int](44).get)) > 0 |} - | |""".stripMargin, null ) From f7f31e3614cbce8e8694d12773f2e17b2b391cf3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 13 Jun 2024 13:01:57 +0300 Subject: [PATCH 03/22] fix w/out tests (only manual tests are done) --- .../scala/sigma/data/CollsOverArrays.scala | 19 ++++++++++++++++--- .../utxo/BasicOpsSpecification.scala | 7 +++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index 2413f7f427..01cad08471 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -135,7 +135,13 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil override def equals(obj: scala.Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match { case obj: CollOverArray[_] if obj.tItem == this.tItem => - java.util.Objects.deepEquals(obj.toArray, toArray) + java.util.Objects.deepEquals(obj.toArray, this.toArray) + case obj: PairColl[_, _] if obj.tItem == this.tItem => + if(VersionContext.current.isV6SoftForkActivated) { + java.util.Objects.deepEquals(obj.toArray, this.toArray) + } else { + false + } case _ => false }) @@ -237,8 +243,15 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R]) extends PairColl[L,R] { - override def equals(that: scala.Any) = (this eq that.asInstanceOf[AnyRef]) || (that match { - case that: PairColl[_,_] if that.tItem == this.tItem => ls == that.ls && rs == that.rs + override def equals(that: scala.Any): Boolean = (this eq that.asInstanceOf[AnyRef]) || (that match { + case that: PairColl[_,_] if that.tItem == this.tItem => + ls == that.ls && rs == that.rs + case that: CollOverArray[_] if that.tItem == this.tItem => + if (VersionContext.current.isV6SoftForkActivated) { + java.util.Objects.deepEquals(that.toArray, this.toArray) + } else { + false + } case _ => false }) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..705d83b56a 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -157,6 +157,13 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("coll equality") { + test("R1", env, ext, + "{ SELF.tokens == Coll[(Coll[Byte], Long)]() }", + null + ) + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From b7e2a38132c43054fb73ada852ecba86e45cce32 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 19 Jun 2024 12:39:33 +0300 Subject: [PATCH 04/22] first failing test --- .../src/main/scala/sigma/ast/methods.scala | 21 ++++++++++++++++++- .../sigma/compiler/phases/SigmaTyper.scala | 5 +++++ .../utxo/BasicOpsSpecification.scala | 19 +++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index e4cf0007e0..d114bb8f5f 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1418,16 +1418,35 @@ case object SContextMethods extends MonoTypeMethods { lazy val selfBoxIndexMethod = propertyCall("selfBoxIndex", SInt, 8, FixedCost(JitCost(20))) lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) + lazy val getVarMethod = SMethod( this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind) .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", ArgInfo("varId", "\\lst{Byte} identifier of context variable")) - protected override def getMethods() = super.getMethods() ++ Seq( + lazy val getVarFromInputMethod = SMethod( + this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) + .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", + ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + + private lazy val v5Methods = super.getMethods() ++ Seq( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod ) + private lazy val v6Methods = super.getMethods() ++ Seq( + dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, + selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod, getVarFromInputMethod + ) + + protected override def getMethods(): Seq[SMethod] = { + if(VersionContext.current.isV6SoftForkActivated) { + v6Methods + } else { + v5Methods + } + } + /** Names of methods which provide blockchain context. * This value can be reused where necessary to avoid allocations. */ val BlockchainContextMethodNames: IndexedSeq[String] = Array( diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index ac30a6cd0a..1348dff993 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -221,6 +221,11 @@ class SigmaTyper(val builder: SigmaBuilder, case (Ident(GetVarFunc.name | ExecuteFromVarFunc.name, _), Seq(id: Constant[SNumericType]@unchecked)) if id.tpe.isNumType => Seq(ByteConstant(SByte.downcast(id.value.asInstanceOf[AnyVal])).withSrcCtx(id.sourceContext)) + case (Ident(SContextMethods.getVarFromInputMethod.name, _), + Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) + if inputId.tpe.isNumType && varId.tpe.isNumType => + Seq(ShortConstant(SShort.downcast(inputId.value.asInstanceOf[AnyVal])).withSrcCtx(inputId.sourceContext), + ByteConstant(SByte.downcast(varId.value.asInstanceOf[AnyVal])).withSrcCtx(varId.sourceContext)) case _ => typedArgs } val actualTypes = adaptedTypedArgs.map(_.tpe) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..5ec8336deb 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import sigma.Extensions.ArrayOps +import sigma.VersionContext import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -157,6 +158,24 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("getVarFromInput") { + def getVarTest() = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ CONTEXT.getVarFromInput[Int](0, 1) == 5 }", + null + ) + } + + if(VersionContext.current.isV6SoftForkActivated) { + getVarTest() + } else { + an[Exception] should be thrownBy getVarTest() + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 828ba270b0f413a0c336814aba0fcd0eebd03873 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 27 Jun 2024 18:13:27 +0300 Subject: [PATCH 05/22] passing test from getVar from another input --- .../src/main/scala/sigma/SigmaDsl.scala | 2 ++ .../sigma/reflection/ReflectionData.scala | 3 +++ .../src/main/scala/sigma/ast/methods.scala | 14 +++++++++++-- .../src/main/scala/sigma/ast/values.scala | 2 +- .../sigma/interpreter/ContextExtension.scala | 5 ++++- .../org/ergoplatform/ErgoLikeContext.scala | 2 +- .../main/scala/sigmastate/eval/CContext.scala | 12 +++++++++++ .../special/sigma/ContractsTestkit.scala | 2 +- .../sigma/compiler/ir/GraphBuilding.scala | 8 +++++++- .../scala/sigma/compiler/ir/IRContext.scala | 2 +- .../scala/sigma/compiler/ir/MethodCalls.scala | 7 ++++--- .../sigma/compiler/ir/TreeBuilding.scala | 7 ++++--- .../ir/wrappers/sigma/SigmaDslUnit.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 20 ++++++++++++++++++- .../sigma/compiler/phases/SigmaTyper.scala | 18 +++++++++++++---- .../scala/sigma/SigmaDslSpecification.scala | 1 + .../utxo/BasicOpsSpecification.scala | 13 ++++++++---- 17 files changed, 96 insertions(+), 23 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index df2b419273..ab06306635 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -557,6 +557,8 @@ trait Context { */ def getVar[T](id: Byte)(implicit cT: RType[T]): Option[T] + def getVarFromInput[T](inputId: Short, id: Byte)(implicit cT: RType[T]): Option[T] + def vars: Coll[AnyValue] /** Maximum version of ErgoTree currently activated on the network. diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index 028e68bf72..76072a2fa4 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -273,6 +273,9 @@ object ReflectionData { mkMethod(clazz, "getVar", Array[Class[_]](classOf[Byte], classOf[RType[_]])) { (obj, args) => obj.asInstanceOf[Context].getVar(args(0).asInstanceOf[Byte])(args(1).asInstanceOf[RType[_]]) }, + mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Short], classOf[Byte], classOf[RType[_]])) { (obj, args) => + obj.asInstanceOf[Context].getVarFromInput(args(0).asInstanceOf[Byte], args(1).asInstanceOf[Byte])(args(2).asInstanceOf[RType[_]]) + }, mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[Context].headers } diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index d114bb8f5f..9f6c323524 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,6 +2,7 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ +import sigma.Evaluation.stypeToRType import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} @@ -1424,10 +1425,19 @@ case object SContextMethods extends MonoTypeMethods { .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + // todo: costing, desc lazy val getVarFromInputMethod = SMethod( this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) - .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", - ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) + + def getVarFromInput_eval[T](mc: MethodCall, ctx: sigma.Context, inputId: Short, varId: Byte) + (implicit E: ErgoTreeEvaluator): Option[T] = { + // E.addCost(getVarFromInputMethod.costKind) + val rt = stypeToRType(mc.typeSubst.get(tT).get) + val res = ctx.getVarFromInput(inputId, varId)(rt).asInstanceOf[Option[T]] + res + } private lazy val v5Methods = super.getMethods() ++ Seq( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala index 87c661a00a..dace767d9f 100644 --- a/data/shared/src/main/scala/sigma/ast/values.scala +++ b/data/shared/src/main/scala/sigma/ast/values.scala @@ -1312,7 +1312,7 @@ case class MethodCall( val objV = obj.evalTo[Any](env) addCost(MethodCall.costKind) // MethodCall overhead method.costKind match { - case fixed: FixedCost => + case fixed: FixedCost if method.explicitTypeArgs.isEmpty => val extra = method.extraDescriptors val extraLen = extra.length val len = args.length diff --git a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala index e8cdb7d709..fd269c177c 100644 --- a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala +++ b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala @@ -16,8 +16,11 @@ import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} * @param values internal container of the key-value pairs */ case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ <: SType]]) { - def add(bindings: VarBinding*): ContextExtension = + def add(bindings: VarBinding*): ContextExtension = { ContextExtension(values ++ bindings) + } + + def get(varId: Byte): Option[EvaluatedValue[_ <: SType]] = values.get(varId) } object ContextExtension { diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala index e421e8fdfe..4b1366d9fd 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala @@ -168,7 +168,7 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData, syntax.error(s"Undefined context property: currentErgoTreeVersion")) CContext( dataInputs, headers, preHeader, inputs, outputs, preHeader.height, selfBox, selfIndex, avlTree, - preHeader.minerPk.getEncoded, vars, activatedScriptVersion, ergoTreeVersion) + preHeader.minerPk.getEncoded, vars, spendingTransaction, activatedScriptVersion, ergoTreeVersion) } diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala index 2b076403ad..1c464fed37 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala @@ -1,8 +1,11 @@ package sigmastate.eval import debox.cfor +import org.ergoplatform.{ErgoLikeTransactionTemplate, UnsignedInput} +import sigma.Evaluation.{stypeToRType, toDslTuple} import sigma.Extensions.ArrayOps import sigma._ +import sigma.ast.SType import sigma.data._ import sigma.exceptions.InvalidType @@ -24,6 +27,7 @@ case class CContext( lastBlockUtxoRootHash: AvlTree, _minerPubKey: Coll[Byte], vars: Coll[AnyValue], + spendingTransaction: ErgoLikeTransactionTemplate[_ <: UnsignedInput], override val activatedScriptVersion: Byte, override val currentErgoTreeVersion: Byte ) extends Context { @@ -69,6 +73,14 @@ case class CContext( } else None } + override def getVarFromInput[T](inputId: Short, id: Byte)(implicit tT: RType[T]): Option[T] = { + spendingTransaction.inputs.unapply(inputId).flatMap(_.extension.get(id)) match { + case Some(v) if stypeToRType[SType](v.tpe) == tT => Some(v.value.asInstanceOf[T]) + case _ => + None + } + } + /** Return a new context instance with variables collection updated. * @param bindings a new binding of the context variables with new values. * @return a new instance (if `bindings` non-empty) with the specified bindings. diff --git a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala index b04e9c150f..ba04df1347 100644 --- a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala +++ b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala @@ -47,7 +47,7 @@ trait ContractsTestkit { new CContext( noInputs.toColl, noHeaders, dummyPreHeader, inputs.toColl, outputs.toColl, height, self, inputs.indexOf(self), tree, - minerPk.toColl, vars.toColl, activatedScriptVersion, currErgoTreeVersion) + minerPk.toColl, vars.toColl, null, activatedScriptVersion, currErgoTreeVersion) def newContext( height: Int, diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 7c7b80d39a..3ed0e414b8 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1,6 +1,7 @@ package sigma.compiler.ir import org.ergoplatform._ +import sigma.ast.SType.tT import sigma.ast.TypeCodes.LastConstantCode import sigma.ast.Value.Typed import sigma.ast.syntax.{SValue, ValueOps} @@ -928,7 +929,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => sigmaDslBuilder.decodePoint(bytes) // fallback rule for MethodCall, should be the last case in the list - case sigma.ast.MethodCall(obj, method, args, _) => + case sigma.ast.MethodCall(obj, method, args, typeSubst) => val objV = eval(obj) val argsV = args.map(eval) (objV, method.objType) match { @@ -1040,6 +1041,11 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => ctx.LastBlockUtxoRootHash case SContextMethods.minerPubKeyMethod.name => ctx.minerPubKey + case SContextMethods.getVarFromInputMethod.name => + val c1 = asRep[Short](argsV(0)) + val c2 = asRep[Byte](argsV(1)) + val c3 = stypeToElem(typeSubst.apply(tT)) + ctx.getVarFromInput(c1, c2)(c3) case _ => throwError } case (tree: Ref[AvlTree]@unchecked, SAvlTreeMethods) => method.name match { diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala b/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala index c60bc0882f..a22962f987 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/IRContext.scala @@ -153,7 +153,7 @@ trait IRContext override def invokeUnlifted(e: Elem[_], mc: MethodCall, dataEnv: DataEnv): Any = e match { case _: CollElem[_,_] => mc match { case CollMethods.map(_, f) => - val newMC = mc.copy(args = mc.args :+ f.elem.eRange)(mc.resultType, mc.isAdapterCall) + val newMC = mc.copy(args = mc.args :+ f.elem.eRange)(mc.resultType, mc.isAdapterCall, mc.typeSubst) super.invokeUnlifted(e, newMC, dataEnv) case _ => super.invokeUnlifted(e, mc, dataEnv) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala index 876f0e9d7e..48248f2165 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala @@ -1,6 +1,7 @@ package sigma.compiler.ir import debox.{cfor, Buffer => DBuffer} +import sigma.ast.{SType, STypeVar} import sigma.compiler.DelayInvokeException import sigma.reflection.RMethod import sigma.util.CollectionUtil.TraversableOps @@ -26,7 +27,7 @@ trait MethodCalls extends Base { self: IRContext => * given `method`. */ case class MethodCall private[MethodCalls](receiver: Sym, method: RMethod, args: Seq[AnyRef], neverInvoke: Boolean) - (val resultType: Elem[Any], val isAdapterCall: Boolean = false) extends Def[Any] { + (val resultType: Elem[Any], val isAdapterCall: Boolean = false, val typeSubst: Map[STypeVar, SType] = Map()) extends Def[Any] { override def mirror(t: Transformer): Ref[Any] = { val len = args.length @@ -100,8 +101,8 @@ trait MethodCalls extends Base { self: IRContext => /** Creates new MethodCall node and returns its node ref. */ def mkMethodCall(receiver: Sym, method: RMethod, args: Seq[AnyRef], - neverInvoke: Boolean, isAdapterCall: Boolean, resultElem: Elem[_]): Sym = { - reifyObject(MethodCall(receiver, method, args, neverInvoke)(asElem[Any](resultElem), isAdapterCall)) + neverInvoke: Boolean, isAdapterCall: Boolean, resultElem: Elem[_], typeSubst: Map[STypeVar, SType] = Map.empty): Sym = { + reifyObject(MethodCall(receiver, method, args, neverInvoke)(asElem[Any](resultElem), isAdapterCall, typeSubst)) } @tailrec diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala index 725e3b1d19..6826321ccd 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -399,13 +399,14 @@ trait TreeBuilding extends Base { IR: IRContext => mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement) // Fallback MethodCall rule: should be the last in this list of cases - case Def(MethodCall(objSym, m, argSyms, _)) => + case Def(mc @ MethodCall(objSym, m, argSyms, _)) => val obj = recurse[SType](objSym) val args = argSyms.collect { case argSym: Sym => recurse[SType](argSym) } MethodsContainer.getMethod(obj.tpe, m.getName) match { case Some(method) => - val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)) - builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, Map()) + val typeSubst = mc.typeSubst + val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)).withConcreteTypes(typeSubst) + builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, typeSubst) case None => error(s"Cannot find method ${m.getName} in object $obj") } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala index 2a6a341686..91013f9071 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/SigmaDslUnit.scala @@ -91,6 +91,7 @@ import scalan._ def preHeader: Ref[PreHeader]; def minerPubKey: Ref[Coll[Byte]]; def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]]; + def getVarFromInput[T](inputId: Ref[Short], id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]]; }; trait SigmaDslBuilder extends Def[SigmaDslBuilder] { def Colls: Ref[CollBuilder]; diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index c113cb7de3..d5bd87d306 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -8,6 +8,8 @@ import sigma.compiler.ir.wrappers.sigma.impl.SigmaDslDefs import scala.collection.compat.immutable.ArraySeq package impl { + import sigma.Evaluation + import sigma.ast.SType.tT import sigma.compiler.ir.meta.ModuleInfo import sigma.compiler.ir.wrappers.sigma.SigmaDsl import sigma.compiler.ir.{Base, GraphIRReflection, IRContext} @@ -1614,6 +1616,14 @@ object Context extends EntityObject("Context") { true, false, element[WOption[T]])) } + override def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) + asRep[WOption[T]](mkMethodCall(self, + ContextClass.getMethod("getVarFromInput", classOf[Sym], classOf[Sym], classOf[Elem[_]]), + Array[AnyRef](inputId, varId, cT), + true, false, element[WOption[T]], Map(tT -> st))) + } + } implicit object LiftableContext @@ -1710,6 +1720,14 @@ object Context extends EntityObject("Context") { Array[AnyRef](id, cT), true, true, element[WOption[T]])) } + + def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) + asRep[WOption[T]](mkMethodCall(source, + ContextClass.getMethod("getVarFromInput", classOf[Sym], classOf[Sym], classOf[Elem[_]]), + Array[AnyRef](inputId, varId, cT), + true, true, element[WOption[T]], Map(tT -> st))) + } } // entityUnref: single unref method for each type family @@ -1727,7 +1745,7 @@ object Context extends EntityObject("Context") { override protected def collectMethods: Map[RMethod, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[Context]), RClass(classOf[SContext]), Set( - "OUTPUTS", "INPUTS", "dataInputs", "HEIGHT", "SELF", "selfBoxIndex", "LastBlockUtxoRootHash", "headers", "preHeader", "minerPubKey", "getVar", "vars" + "OUTPUTS", "INPUTS", "dataInputs", "HEIGHT", "SELF", "selfBoxIndex", "LastBlockUtxoRootHash", "headers", "preHeader", "minerPubKey", "getVar", "getVarFromInput", "vars" )) } } diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index 1348dff993..0d53802239 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -134,8 +134,18 @@ class SigmaTyper(val builder: SigmaBuilder, res case Apply(ApplyTypes(sel @ Select(obj, n, _), Seq(rangeTpe)), args) => + val nArgs = if (n == SContextMethods.getVarFromInputMethod.name && + args.length == 2 && + args(0).isInstanceOf[Constant[_]] && + args(1).isInstanceOf[Constant[_]] && + args(0).tpe.isNumType && + args(1).tpe.isNumType) { + IndexedSeq(ShortConstant(SShort.downcast(args(0).asInstanceOf[Constant[SNumericType]].value.asInstanceOf[AnyVal])).withSrcCtx(args(0).sourceContext), + ByteConstant(SByte.downcast(args(1).asInstanceOf[Constant[SNumericType]].value.asInstanceOf[AnyVal])).withSrcCtx(args(1).sourceContext)) + } else args + val newObj = assignType(env, obj) - val newArgs = args.map(assignType(env, _)) + val newArgs = nArgs.map(assignType(env, _)) obj.tpe match { case p: SProduct => MethodsContainer.getMethod(p, n) match { @@ -222,10 +232,10 @@ class SigmaTyper(val builder: SigmaBuilder, if id.tpe.isNumType => Seq(ByteConstant(SByte.downcast(id.value.asInstanceOf[AnyVal])).withSrcCtx(id.sourceContext)) case (Ident(SContextMethods.getVarFromInputMethod.name, _), - Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) - if inputId.tpe.isNumType && varId.tpe.isNumType => + Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) + if inputId.tpe.isNumType && varId.tpe.isNumType => Seq(ShortConstant(SShort.downcast(inputId.value.asInstanceOf[AnyVal])).withSrcCtx(inputId.sourceContext), - ByteConstant(SByte.downcast(varId.value.asInstanceOf[AnyVal])).withSrcCtx(varId.sourceContext)) + ByteConstant(SByte.downcast(varId.value.asInstanceOf[AnyVal])).withSrcCtx(varId.sourceContext)) case _ => typedArgs } val actualTypes = adaptedTypedArgs.map(_.tpe) diff --git a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala b/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala index c820e65e73..b7ce178d6c 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala @@ -4762,6 +4762,7 @@ class SigmaDslSpecification extends SigmaDslTesting .append(Coll[AnyValue]( CAnyValue(Helpers.decodeBytes("00")), CAnyValue(true))), + spendingTransaction = null, activatedScriptVersion = activatedVersionInTests, currentErgoTreeVersion = ergoTreeVersionInTests ) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 5ec8336deb..83d405a1a1 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -2,6 +2,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ +import org.scalatest.Assertion import sigma.Extensions.ArrayOps import sigma.VersionContext import sigma.ast.SCollection.SByteArray @@ -20,9 +21,11 @@ import sigmastate.interpreter.Interpreter._ import sigma.ast.Apply import sigma.eval.EvalSettings import sigma.exceptions.InvalidType +import sigma.interpreter.{ContextExtension, ProverResult} import sigmastate.utils.Helpers._ import java.math.BigInteger +import scala.collection.compat.immutable.ArraySeq class BasicOpsSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -102,7 +105,8 @@ class BasicOpsSpecification extends CompilerTestingCommons val newBox1 = testBox(10, tree, creationHeight = 0, boxIndex = 0, additionalRegisters = Map( reg1 -> IntConstant(1), reg2 -> IntConstant(10))) - val tx = createTransaction(newBox1) + val ce = ContextExtension(prover.contextExtenders) + val tx = new ErgoLikeTransaction(IndexedSeq(Input(boxToSpend.id, ProverResult(Array.empty, ce))), ArraySeq.empty, IndexedSeq(newBox1)) val ctx = ErgoLikeContextTesting(currentHeight = 0, lastBlockUtxoRoot = AvlTreeData.dummy, ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(boxToSpend), @@ -159,17 +163,17 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("getVarFromInput") { - def getVarTest() = { + def getVarTest(): Assertion = { val customExt = Map( 1.toByte -> IntConstant(5) ).toSeq test("R1", env, customExt, - "{ CONTEXT.getVarFromInput[Int](0, 1) == 5 }", + "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 1).get == 5) }", null ) } - if(VersionContext.current.isV6SoftForkActivated) { + if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { an[Exception] should be thrownBy getVarTest() @@ -740,4 +744,5 @@ class BasicOpsSpecification extends CompilerTestingCommons true ) } + } From b80ef294039826cd43a5ccef7e9ae946f6f448dc Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 28 Jun 2024 14:07:08 +0300 Subject: [PATCH 06/22] invalid tests --- .../utxo/BasicOpsSpecification.scala | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 83d405a1a1..74a51e63d0 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -180,6 +180,42 @@ class BasicOpsSpecification extends CompilerTestingCommons } } + property("getVarFromInput - invalid input") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ sigmaProp(CONTEXT.getVarFromInput[Int](1, 1).get == 5) }", + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + an[Exception] should be thrownBy getVarTest() + } else { + an[Exception] should be thrownBy getVarTest() + } + } + + property("getVarFromInput - invalid var") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 2).get == 5) }", + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + an[Exception] should be thrownBy getVarTest() + } else { + an[Exception] should be thrownBy getVarTest() + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 6a3cbac7540baf0d15dcfbbb11f0c42fb9432eeb Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 29 Jun 2024 20:00:52 +0300 Subject: [PATCH 07/22] initial failing test & impl --- .../src/main/scala/sigma/ast/methods.scala | 20 +++++++++++++------ .../sigmastate/ErgoTreeSpecification.scala | 2 +- .../utxo/BasicOpsSpecification.scala | 16 +++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 9f6c323524..aecf63e588 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1420,11 +1420,16 @@ case object SContextMethods extends MonoTypeMethods { lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) - lazy val getVarMethod = SMethod( + lazy val getVarV5Method = SMethod( this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind) .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + lazy val getVarV6Method = SMethod( + this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind, Seq(tT)) + .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", + ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + // todo: costing, desc lazy val getVarFromInputMethod = SMethod( this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) @@ -1439,18 +1444,21 @@ case object SContextMethods extends MonoTypeMethods { res } - private lazy val v5Methods = super.getMethods() ++ Seq( + private lazy val commonMethods = Array( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, - selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod + selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod + ) + + private lazy val v5Methods = super.getMethods() ++ Seq( + getVarV5Method ) private lazy val v6Methods = super.getMethods() ++ Seq( - dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, - selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod, getVarFromInputMethod + getVarV6Method, getVarFromInputMethod ) protected override def getMethods(): Seq[SMethod] = { - if(VersionContext.current.isV6SoftForkActivated) { + if (VersionContext.current.isV6SoftForkActivated) { v6Methods } else { v5Methods diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 7539bd5e48..c39a5269bc 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -413,7 +413,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(1, dataInputsMethod), MInfo(2, headersMethod), MInfo(3, preHeaderMethod), MInfo(4, inputsMethod), MInfo(5, outputsMethod), MInfo(6, heightMethod), MInfo(7, selfMethod), MInfo(8, selfBoxIndexMethod), MInfo(9, lastBlockUtxoRootHashMethod), - MInfo(10, minerPubKeyMethod), MInfo(11, getVarMethod) + MInfo(10, minerPubKeyMethod), MInfo(11, getVarV5Method) ), true) }, { import SGlobalMethods._ diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 74a51e63d0..5a89ebe360 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -454,6 +454,22 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Context.getVar") { + def varTest() = { + test("GetVar1", env, ext, + "{ CONTEXT.getVar[Int](intVar2).get == 2 }", + null + ) + } + + if(VersionContext.current.isV6SoftForkActivated) { + varTest() + } else { + an[Exception] should be thrownBy(varTest()) + } + + } + property("GetVar") { test("GetVar1", env, ext, "{ getVar[Int](intVar2).get == 2 }", From af92a3fba5c4c0079f770809fdf1c565cfa03d70 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 29 Jun 2024 22:55:31 +0300 Subject: [PATCH 08/22] test passing --- data/shared/src/main/scala/sigma/ast/methods.scala | 12 ++++++------ .../main/scala/sigma/compiler/ir/GraphBuilding.scala | 4 ++++ .../sigmastate/utxo/BasicOpsSpecification.scala | 1 - 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index aecf63e588..8d8e4e9056 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1427,8 +1427,8 @@ case object SContextMethods extends MonoTypeMethods { lazy val getVarV6Method = SMethod( this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind, Seq(tT)) - .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", - ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") // todo: desc // todo: costing, desc lazy val getVarFromInputMethod = SMethod( @@ -1449,19 +1449,19 @@ case object SContextMethods extends MonoTypeMethods { selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod ) - private lazy val v5Methods = super.getMethods() ++ Seq( + private lazy val v5Methods = commonMethods ++ Seq( getVarV5Method ) - private lazy val v6Methods = super.getMethods() ++ Seq( + private lazy val v6Methods = commonMethods ++ Seq( getVarV6Method, getVarFromInputMethod ) protected override def getMethods(): Seq[SMethod] = { if (VersionContext.current.isV6SoftForkActivated) { - v6Methods + super.getMethods() ++ v6Methods } else { - v5Methods + super.getMethods() ++ v5Methods } } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 3ed0e414b8..82cfcb4469 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1041,6 +1041,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => ctx.LastBlockUtxoRootHash case SContextMethods.minerPubKeyMethod.name => ctx.minerPubKey + case SContextMethods.getVarV6Method.name => + val c2 = asRep[Byte](argsV(0)) + val c3 = stypeToElem(typeSubst.apply(tT)) + ctx.getVar(c2)(c3) case SContextMethods.getVarFromInputMethod.name => val c1 = asRep[Short](argsV(0)) val c2 = asRep[Byte](argsV(1)) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 5a89ebe360..5b6f71ecfc 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -467,7 +467,6 @@ class BasicOpsSpecification extends CompilerTestingCommons } else { an[Exception] should be thrownBy(varTest()) } - } property("GetVar") { From 4c63a60e981743fd10c29a4b87b8e996ed5b749d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 30 Jul 2024 19:30:54 +0300 Subject: [PATCH 09/22] LSV5 & LSV6 tests --- .../scala/sigma/LanguageSpecificationV5.scala | 146 +++++++++--------- .../scala/sigma/LanguageSpecificationV6.scala | 60 ++++++- 2 files changed, 134 insertions(+), 72 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index 700b48fd13..347a7b90f4 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -7957,77 +7957,81 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) ) - verifyCases( - // (coll, (index, default)) - { - def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773) - 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) }", - if (lowerMethodCallsInTests) - 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 + + if(!VersionContext.current.isV6SoftForkActivated) { + verifyCases( + // (coll, (index, default)) + { + def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773) + + 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) }", + if (lowerMethodCallsInTests) + 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)) + 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)) + ) ) ) - ) - else - FuncValue( - Array((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), - BlockValue( - Array( - ValDef( - 3, - List(), - SelectField.typed[Value[STuple]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 2.toByte + else + FuncValue( + Array((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), + BlockValue( + Array( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 2.toByte + ) ) - ) - ), - MethodCall.typed[Value[SInt.type]]( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 1.toByte ), - SCollectionMethods.getMethodByName("getOrElse").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() + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 1.toByte + ), + SCollectionMethods.getMethodByName("getOrElse").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() + ) ) ) - ) - )) + )) + } } property("Tuple size method equivalence") { @@ -8591,13 +8595,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => "{ (x: Option[Long]) => x.isDefined }", FuncValue(Vector((1, SOption(SLong))), OptionIsDefined(ValUse(1, SOption(SLong)))))) - verifyCases( - Seq( - (None -> Expected(Success(1L), 1766, costDetails3, 1766)), - (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766))), - existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, - "{ (x: Option[Long]) => x.getOrElse(1L) }", - FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) + if (!VersionContext.current.isV6SoftForkActivated) { + verifyCases( + Seq( + (None -> Expected(Success(1L), 1766, costDetails3, 1766)), + (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766))), + existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, + "{ (x: Option[Long]) => x.getOrElse(1L) }", + FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) + } verifyCases( Seq( diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 3b3de407b5..612f0e33c3 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,8 +1,9 @@ package sigma -import sigma.ast.{Apply, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, JitCost, OptionGet, SBigInt, SByte, SInt, SLong, SShort, ValUse} +import sigma.ast.{Apply, ArithOp, BlockValue, ByIndex, CompanionDesc, Constant, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, IntConstant, JitCost, LongConstant, MethodCall, OptionGet, OptionGetOrElse, PerItemCost, SBigInt, SByte, SCollection, SCollectionMethods, SCollectionType, SInt, SLong, SOption, SPair, SShort, STuple, STypeVar, SelectField, ValDef, ValUse, Value} import sigma.data.{CBigInt, ExactNumeric} -import sigma.eval.SigmaDsl +import sigma.eval.{SigmaDsl, TracedCost} +import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound @@ -168,7 +169,62 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) } } + } + + property("Option.getOrElse with lazy default") { + def getOrElse = newFeature( + { (x: Option[Long]) => x.getOrElse(1 / 0L) }, + "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", + FuncValue( + Array((1, SOption(SLong))), + OptionGetOrElse( + ValUse(1, SOption(SLong)), + ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) + ) + ) + ) + if (VersionContext.current.isV6SoftForkActivated) { + forAll { x: Option[Long] => + Seq(getOrElse).map(_.checkEquality(x)) + } + } else { + forAll { x: Option[Long] => + if (x.isEmpty) { + Seq(getOrElse).map(_.checkEquality(x)) + } + } + } + } + + property("Coll getOrElse with lazy default") { + def getOrElse = newFeature( + (x: (Coll[Int], Int)) => x._1.toArray.unapply(x._2).getOrElse(1 / 0), + "{ (x: (Coll[Int], Int)) => x._1.getOrElse(x._2, 1 / 0) }", + FuncValue( + Array((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), + Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) + ) + ) + ) + + if (VersionContext.current.isV6SoftForkActivated) { + forAll { x: (Coll[Int], Int) => + Seq(getOrElse).map(_.checkEquality(x)) + } + } else { + forAll { x: (Coll[Int], Int) => + if (x._1.isEmpty) { + Seq(getOrElse).map(_.checkEquality(x)) + } + } + } } property("BigInt methods equivalence (new features)") { From 61f90cd339b9a79ce20154f45f3db2980be9dc4c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 31 Jul 2024 14:04:30 +0300 Subject: [PATCH 10/22] assertEXceptionThrown --- sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala | 2 +- .../test/scala/sigmastate/utxo/BasicOpsSpecification.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 612f0e33c3..b4fb5b12fb 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -199,7 +199,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Coll getOrElse with lazy default") { def getOrElse = newFeature( - (x: (Coll[Int], Int)) => x._1.toArray.unapply(x._2).getOrElse(1 / 0), + (x: (Coll[Int], Int)) => x._1.toArray.toIndexedSeq.unapply(x._2).getOrElse(1 / 0), "{ (x: (Coll[Int], Int)) => x._1.getOrElse(x._2, 1 / 0) }", FuncValue( Array((1, SPair(SCollectionType(SInt), SInt))), diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index c1489355e6..3d25326660 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -170,10 +170,10 @@ class BasicOpsSpecification extends CompilerTestingCommons null ) - if(VersionContext.current.isV6SoftForkActivated) { + if (VersionContext.current.isV6SoftForkActivated) { optTest() } else { - an[Exception] shouldBe thrownBy(optTest()) + assertExceptionThrown(optTest(), _.isInstanceOf[NoSuchElementException]) } } From 26809c94f0ed8aca3d56fabb10995fb30e2889e0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 2 Aug 2024 13:58:07 +0300 Subject: [PATCH 11/22] passing but improper changedFeature test --- .gitignore | 1 + .../scala/sigma/LanguageSpecificationV6.scala | 48 +++++++++---------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index df18ce8ab1..d328645cde 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.fdb_latexmk *.gz +yarn.lock *.log docs/spec/out/ test-out/ diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index b4fb5b12fb..9e9827df1f 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,14 +1,14 @@ package sigma -import sigma.ast.{Apply, ArithOp, BlockValue, ByIndex, CompanionDesc, Constant, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, IntConstant, JitCost, LongConstant, MethodCall, OptionGet, OptionGetOrElse, PerItemCost, SBigInt, SByte, SCollection, SCollectionMethods, SCollectionType, SInt, SLong, SOption, SPair, SShort, STuple, STypeVar, SelectField, ValDef, ValUse, Value} +import sigma.ast.{Apply, ByIndex, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, IntConstant, JitCost, LongConstant, MethodCall, OptionGet, OptionGetOrElse, PerItemCost, SBigInt, SByte, SCollection, SCollectionMethods, SCollectionType, SInt, SLong, SOption, SPair, SShort, STuple, STypeVar, SelectField, ValDef, ValUse, Value} import sigma.data.{CBigInt, ExactNumeric} -import sigma.eval.{SigmaDsl, TracedCost} +import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound import java.math.BigInteger -import scala.util.Success +import scala.util.{Failure, Success} /** This suite tests all operations for v6.0 version of the language. * The base classes establish the infrastructure for the tests. @@ -172,29 +172,29 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => } property("Option.getOrElse with lazy default") { - def getOrElse = newFeature( - { (x: Option[Long]) => x.getOrElse(1 / 0L) }, - "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", - FuncValue( - Array((1, SOption(SLong))), - OptionGetOrElse( - ValUse(1, SOption(SLong)), - ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) - ) + verifyCases( + Seq( + Some(0L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, CostDetails.ZeroCost, 1793, + newVersionedResults = { + Seq(0 -> (ExpectedResult(Success(6L), Some(1793)) -> None)) + } ), + None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6) + ), + changedFeature( + { (x: Option[Long]) => val default = 1 / 0L; x.getOrElse(default) }, + { (x: Option[Long]) => if (VersionContext.current.isV6SoftForkActivated) {x.getOrElse(1 / 0L)} else {val default = 1 / 0L; x.getOrElse(default)} }, + "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", + FuncValue( + Array((1, SOption(SLong))), + OptionGetOrElse( + ValUse(1, SOption(SLong)), + ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) + ) + ), + allowNewToSucceed = true, + changedIn = VersionContext.V6SoftForkVersion ) ) - - if (VersionContext.current.isV6SoftForkActivated) { - forAll { x: Option[Long] => - Seq(getOrElse).map(_.checkEquality(x)) - } - } else { - forAll { x: Option[Long] => - if (x.isEmpty) { - Seq(getOrElse).map(_.checkEquality(x)) - } - } - } } property("Coll getOrElse with lazy default") { From 8da35750e295b0fa95e440c21316d147c45b1f1d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 9 Aug 2024 17:33:12 +0300 Subject: [PATCH 12/22] fixed tests in LSV6 --- .../scala/sigma/LanguageSpecificationV6.scala | 79 ++++++++++++------- .../test/scala/sigma/SigmaDslTesting.scala | 13 ++- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 4e6641984b..d63d6aae65 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -467,7 +467,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Option.getOrElse with lazy default") { - val someTrace = TracedCost( + val trace = TracedCost( Array( FixedCostItem(Apply), FixedCostItem(FuncValue), @@ -481,11 +481,11 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => verifyCases( Seq( - Some(2L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, someTrace, 1793, + Some(2L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793, newVersionedResults = { - expectedSuccessForAllTreeVersions(2L, 2015, someTrace) + expectedSuccessForAllTreeVersions(2L, 2015, trace) } ), - // None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6) + None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793) ), changedFeature( changedInVersion = VersionContext.V6SoftForkVersion, @@ -503,35 +503,56 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) ) } -/* + property("Coll getOrElse with lazy default") { - def getOrElse = newFeature( - (x: (Coll[Int], Int)) => x._1.toArray.toIndexedSeq.unapply(x._2).getOrElse(1 / 0), - "{ (x: (Coll[Int], Int)) => x._1.getOrElse(x._2, 1 / 0) }", - FuncValue( - Array((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), - Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) - ) + + val trace = TracedCost( + Array( + FixedCostItem(Apply), + FixedCostItem(FuncValue), + FixedCostItem(GetVar), + FixedCostItem(OptionGet), + FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), + FixedCostItem(ValUse), + FixedCostItem(Constant), + FixedCostItem(ByIndex) ) ) - if (VersionContext.current.isV6SoftForkActivated) { - forAll { x: (Coll[Int], Int) => - Seq(getOrElse).map(_.checkEquality(x)) - } - } else { - forAll { x: (Coll[Int], Int) => - if (x._1.isEmpty) { - Seq(getOrElse).map(_.checkEquality(x)) - } - } + def scalaFuncNew(x: Coll[Int]) = { + if (VersionContext.current.isV6SoftForkActivated) { + x.toArray.toIndexedSeq.headOption.getOrElse(1 / 0) + } else scalaFuncOld(x) + } + + def scalaFuncOld(x: Coll[Int]) = { + x.getOrElse(0, 1 / 0) } + + verifyCases( + Seq( + Coll(1) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793, + newVersionedResults = { + expectedSuccessForAllTreeVersions(1, 2029, trace) + } ), + Coll[Int]() -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793) + ), + changedFeature( + changedInVersion = VersionContext.V6SoftForkVersion, + scalaFuncOld, + scalaFuncNew, + "{ (x: Coll[Int]) => x.getOrElse(0, 1 / 0) }", + FuncValue( + Array((1, SCollectionType(SInt))), + ByIndex( + ValUse(1, SCollectionType(SInt)), + IntConstant(0), + Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) + ) + ), + allowNewToSucceed = true + ) + ) } - */ + } diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 58873449b4..cf2fd98ebf 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -261,6 +261,7 @@ class SigmaDslTesting extends AnyPropSpec s"""Should succeed with the same value or fail with the same exception, but was: |First result: $b1 |Second result: $b2 + |Input: $x |Root cause: $cause |""".stripMargin) } @@ -715,11 +716,17 @@ class SigmaDslTesting extends AnyPropSpec override def checkEquality(input: A, logInputOutput: Boolean = false): Try[(B, CostDetails)] = { // check the old implementation against Scala semantic function var oldRes: Try[(B, CostDetails)] = null - if (ergoTreeVersionInTests < VersionContext.JitActivationVersion) oldRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { try checkEq(scalaFunc)(oldF)(input) catch { - case e: TestFailedException => throw e + case e: TestFailedException => + if(activatedVersionInTests < changedInVersion) { + throw e + } else { + // old ergoscript may succeed in new version while old scalafunc may fail, + // see e.g. "Option.getOrElse with lazy default" test + Failure(e) + } case t: Throwable => Failure(t) } @@ -764,7 +771,7 @@ class SigmaDslTesting extends AnyPropSpec override def checkExpected(input: A, expected: Expected[B]): Unit = { // check the new implementation with Scala semantic function val newRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { - checkEq(scalaFuncNew)(newF)(input) + checkEq(scalaFuncNew)(newF)(input) } if (VersionContext.current.activatedVersion < changedInVersion) { From 12e888d1b776d276e4013ea21c4187f1f32f4417 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 12 Aug 2024 14:38:54 +0300 Subject: [PATCH 13/22] scala 2.11 compilation fix --- .../shared/src/main/scala/sigmastate/eval/CContext.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala index 1c464fed37..bed0c4b013 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala @@ -2,7 +2,7 @@ package sigmastate.eval import debox.cfor import org.ergoplatform.{ErgoLikeTransactionTemplate, UnsignedInput} -import sigma.Evaluation.{stypeToRType, toDslTuple} +import sigma.Evaluation.stypeToRType import sigma.Extensions.ArrayOps import sigma._ import sigma.ast.SType @@ -74,7 +74,7 @@ case class CContext( } override def getVarFromInput[T](inputId: Short, id: Byte)(implicit tT: RType[T]): Option[T] = { - spendingTransaction.inputs.unapply(inputId).flatMap(_.extension.get(id)) match { + spendingTransaction.inputs.lift(inputId).flatMap(_.extension.get(id)) match { case Some(v) if stypeToRType[SType](v.tpe) == tT => Some(v.value.asInstanceOf[T]) case _ => None From b0fbfbbd152bf3c7565135d9e31fd21874c97be7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 13 Aug 2024 16:54:09 +0300 Subject: [PATCH 14/22] fixing getVar typing during tree building, simpler execution for getVarFromInput --- .../src/main/scala/sigma/SigmaDsl.scala | 9 +++ .../sigma/reflection/ReflectionData.scala | 2 +- .../src/main/scala/sigma/ast/SMethod.scala | 5 ++ .../src/main/scala/sigma/ast/methods.scala | 25 +++---- .../src/main/scala/sigma/ast/values.scala | 2 +- .../sigma/compiler/ir/GraphIRReflection.scala | 3 + .../scala/sigma/compiler/ir/MethodCalls.scala | 2 +- .../sigma/compiler/ir/TreeBuilding.scala | 1 + .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 6 +- .../sigma/compiler/phases/SigmaTyper.scala | 7 +- .../scala/sigma/SigmaDslStaginTests.scala | 2 - .../sigmastate/lang/SigmaTyperTest.scala | 4 ++ .../utxo/BasicOpsSpecification.scala | 68 +++++++++---------- .../utxo/examples/AssetsPartialFilling.scala | 2 - 14 files changed, 75 insertions(+), 63 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index ab06306635..8aa154b34e 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -557,6 +557,15 @@ trait Context { */ def getVar[T](id: Byte)(implicit cT: RType[T]): Option[T] + /** + * A variant of `getVar` to extract a context variable by id and type from any input + * + * @param inputId - input index + * @param id - context variable id + * @tparam T - expected type of the variable + * @return Some(value) if the variable is defined in the context AND has the given type. + * None otherwise + */ def getVarFromInput[T](inputId: Short, id: Byte)(implicit cT: RType[T]): Option[T] def vars: Coll[AnyValue] diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index 76072a2fa4..af9b9135d1 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -274,7 +274,7 @@ object ReflectionData { obj.asInstanceOf[Context].getVar(args(0).asInstanceOf[Byte])(args(1).asInstanceOf[RType[_]]) }, mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Short], classOf[Byte], classOf[RType[_]])) { (obj, args) => - obj.asInstanceOf[Context].getVarFromInput(args(0).asInstanceOf[Byte], args(1).asInstanceOf[Byte])(args(2).asInstanceOf[RType[_]]) + obj.asInstanceOf[Context].getVarFromInput(args(0).asInstanceOf[Short], args(1).asInstanceOf[Byte])(args(2).asInstanceOf[RType[_]]) }, mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[Context].headers diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala index 5a17038c54..828be052b4 100644 --- a/data/shared/src/main/scala/sigma/ast/SMethod.scala +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -300,6 +300,11 @@ object SMethod { (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2]): RMethod = RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass) + def javaMethodOf[T, A1, A2, A3] + (methodName: String) + (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2], cA3: ClassTag[A3]): RMethod = + RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass, cA3.runtimeClass) + /** Default fallback method call recognizer which builds MethodCall ErgoTree nodes. */ val MethodCallIrBuilder: PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue] = { case (builder, obj, method, args, tparamSubst) => diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 8d8e4e9056..3dbe2d707a 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -2,7 +2,6 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ -import sigma.Evaluation.stypeToRType import sigma._ import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} @@ -1427,24 +1426,22 @@ case object SContextMethods extends MonoTypeMethods { lazy val getVarV6Method = SMethod( this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind, Seq(tT)) - .withIRInfo(MethodCallIrBuilder) + .withIRInfo( + MethodCallIrBuilder, + javaMethodOf[Context, Byte, RType[_]]("getVar"), + { mtype => Array(mtype.tRange.asOption[SType].elemType) }) .withInfo(MethodCall, "") // todo: desc // todo: costing, desc lazy val getVarFromInputMethod = SMethod( this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) - .withIRInfo(MethodCallIrBuilder) + .withIRInfo( + MethodCallIrBuilder, + javaMethodOf[Context, Short, Byte, RType[_]]("getVarFromInput"), + { mtype => Array(mtype.tRange.asOption[SType].elemType) }) .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) - def getVarFromInput_eval[T](mc: MethodCall, ctx: sigma.Context, inputId: Short, varId: Byte) - (implicit E: ErgoTreeEvaluator): Option[T] = { - // E.addCost(getVarFromInputMethod.costKind) - val rt = stypeToRType(mc.typeSubst.get(tT).get) - val res = ctx.getVarFromInput(inputId, varId)(rt).asInstanceOf[Option[T]] - res - } - - private lazy val commonMethods = Array( + private lazy val commonMethods = super.getMethods() ++ Array( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod ) @@ -1459,9 +1456,9 @@ case object SContextMethods extends MonoTypeMethods { protected override def getMethods(): Seq[SMethod] = { if (VersionContext.current.isV6SoftForkActivated) { - super.getMethods() ++ v6Methods + v6Methods } else { - super.getMethods() ++ v5Methods + v5Methods } } diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala index dace767d9f..87c661a00a 100644 --- a/data/shared/src/main/scala/sigma/ast/values.scala +++ b/data/shared/src/main/scala/sigma/ast/values.scala @@ -1312,7 +1312,7 @@ case class MethodCall( val objV = obj.evalTo[Any](env) addCost(MethodCall.costKind) // MethodCall overhead method.costKind match { - case fixed: FixedCost if method.explicitTypeArgs.isEmpty => + case fixed: FixedCost => val extra = method.extraDescriptors val extraLen = extra.length val len = args.length diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala index 69736a0224..9d1f6c045c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -332,6 +332,9 @@ object GraphIRReflection { mkMethod(clazz, "getVar", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => obj.asInstanceOf[ctx.Context].getVar(args(0).asInstanceOf[ctx.Ref[Byte]])(args(1).asInstanceOf[ctx.Elem[_]]) }, + mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => + obj.asInstanceOf[ctx.Context].getVarFromInput(args(0).asInstanceOf[ctx.Ref[Short]], args(1).asInstanceOf[ctx.Ref[Byte]])(args(2).asInstanceOf[ctx.Elem[_]]) + }, mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, args) => obj.asInstanceOf[ctx.Context].headers } diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala index 48248f2165..7e7840ab23 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/MethodCalls.scala @@ -27,7 +27,7 @@ trait MethodCalls extends Base { self: IRContext => * given `method`. */ case class MethodCall private[MethodCalls](receiver: Sym, method: RMethod, args: Seq[AnyRef], neverInvoke: Boolean) - (val resultType: Elem[Any], val isAdapterCall: Boolean = false, val typeSubst: Map[STypeVar, SType] = Map()) extends Def[Any] { + (val resultType: Elem[Any], val isAdapterCall: Boolean = false, val typeSubst: Map[STypeVar, SType]) extends Def[Any] { override def mirror(t: Transformer): Ref[Any] = { val len = args.length diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala index 6826321ccd..41559d53ff 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala @@ -1,6 +1,7 @@ package sigma.compiler.ir import org.ergoplatform._ +import sigma.VersionContext import sigma.ast._ import sigma.ast.syntax.{ValueOps, _} import sigma.data.{ProveDHTuple, ProveDlog} diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index d5bd87d306..dad43e318a 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -1610,10 +1610,11 @@ object Context extends EntityObject("Context") { } override def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) asRep[WOption[T]](mkMethodCall(self, ContextClass.getMethod("getVar", classOf[Sym], classOf[Elem[_]]), Array[AnyRef](id, cT), - true, false, element[WOption[T]])) + true, false, element[WOption[T]], Map(tT -> st))) } override def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { @@ -1715,10 +1716,11 @@ object Context extends EntityObject("Context") { } def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { + val st = Evaluation.rtypeToSType(cT.sourceType) asRep[WOption[T]](mkMethodCall(source, ContextClass.getMethod("getVar", classOf[Sym], classOf[Elem[_]]), Array[AnyRef](id, cT), - true, true, element[WOption[T]])) + true, true, element[WOption[T]], Map(tT -> st))) } def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = { diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index 0d53802239..51805b2dea 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -165,7 +165,7 @@ class SigmaTyper(val builder: SigmaBuilder, .getOrElse(mkMethodCall(newObj, method, newArgs, subst)) } else { val newSelect = mkSelect(newObj, n, Some(concrFunTpe)).withSrcCtx(sel.sourceContext) - mkApply(newSelect, newArgs.toArray[SValue]) + mkApply(newSelect, newArgs) } case Some(method) => error(s"Don't know how to handle method $method in obj $p", sel.sourceContext) @@ -424,11 +424,6 @@ class SigmaTyper(val builder: SigmaBuilder, error(s"Invalid application of type arguments $app: function $input doesn't have type parameters", input.sourceContext) } -// case app @ ApplyTypes(in, targs) => -// val newIn = assignType(env, in) -// ApplyTypes(newIn, targs) -// error(s"Invalid application of type arguments $app: expression doesn't have type parameters") - case If(c, t, e) => val c1 = assignType(env, c).asValue[SBoolean.type] val t1 = assignType(env, t) diff --git a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala index bcd4b21129..266c5e66e5 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala @@ -34,8 +34,6 @@ class SigmaDslStaginTests extends BaseCtxTests with ErgoScriptTestkit with BaseL val ctx: SContext = newContext(10, boxA1, VersionContext.MaxSupportedScriptVersion, VersionContext.MaxSupportedScriptVersion) .withInputs(boxA2) .withVariables(Map(1 -> toAnyValue(30), 2 -> toAnyValue(40))) - val p1: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(true)) - val p2: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(false)) cake.check(dsl, { env: EnvRep[RSigmaDslBuilder] => for { dsl <- env; arg <- lifted(true) } yield dsl.sigmaProp(arg) }, dsl.sigmaProp(true)) diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index c68c37a4dc..4989a265d1 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -640,6 +640,10 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "CONTEXT.dataInputs") shouldBe SCollection(SBox) } + property("SContext.getVar") { + typecheck(env, "CONTEXT.getVar[Int](1.toByte).get") shouldBe SInt + } + property("SAvlTree.digest") { typecheck(env, "getVar[AvlTree](1).get.digest") shouldBe SByteArray } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index f5c9e1cd32..da6c9f361d 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -142,26 +142,19 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } - property("Unit register") { - // TODO frontend: implement missing Unit support in compiler - // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 - test("R1", env, ext, - script = "", /* means cannot be compiled - the corresponding script is { SELF.R4[Unit].isDefined } */ - ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).isDefined.toSigmaProp, - additionalRegistersOpt = Some(Map( - reg1 -> UnitConstant.instance - )) - ) + property("Context.getVar") { + def varTest() = { + test("GetVar1", env, ext, + "{ CONTEXT.getVar[Int](intVar2.toByte).get == 2 }", + null + ) + } - test("R2", env, ext, - script = "", /* means cannot be compiled - the corresponding script is "{ SELF.R4[Unit].get == () }" */ - EQ(ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).get, UnitConstant.instance).toSigmaProp, - additionalRegistersOpt = Some(Map( - reg1 -> UnitConstant.instance - )) - ) + if(VersionContext.current.isV6SoftForkActivated) { + varTest() + } else { + an[Exception] should be thrownBy(varTest()) + } } property("getVarFromInput") { @@ -456,21 +449,6 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } - property("Context.getVar") { - def varTest() = { - test("GetVar1", env, ext, - "{ CONTEXT.getVar[Int](intVar2).get == 2 }", - null - ) - } - - if(VersionContext.current.isV6SoftForkActivated) { - varTest() - } else { - an[Exception] should be thrownBy(varTest()) - } - } - property("GetVar") { test("GetVar1", env, ext, "{ getVar[Int](intVar2).get == 2 }", @@ -839,4 +817,26 @@ class BasicOpsSpecification extends CompilerTestingCommons test("subst", env, ext, hostScript, null) } + property("Unit register") { + // TODO frontend: implement missing Unit support in compiler + // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/820 + test("R1", env, ext, + script = "", /* means cannot be compiled + the corresponding script is { SELF.R4[Unit].isDefined } */ + ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).isDefined.toSigmaProp, + additionalRegistersOpt = Some(Map( + reg1 -> UnitConstant.instance + )) + ) + + test("R2", env, ext, + script = "", /* means cannot be compiled + the corresponding script is "{ SELF.R4[Unit].get == () }" */ + EQ(ExtractRegisterAs[SUnit.type](Self, reg1)(SUnit).get, UnitConstant.instance).toSigmaProp, + additionalRegistersOpt = Some(Map( + reg1 -> UnitConstant.instance + )) + ) + } + } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala index 969439fe59..5e89915384 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsPartialFilling.scala @@ -68,11 +68,9 @@ case class AssetsPartialFilling[Spec <: ContractSpec] val out = OUTPUTS(outIdx) val tokenData = out.R2[Coll[(Coll[Byte], Long)]].get(0) - val tokenId = tokenData._1 val tokenValue = tokenData._2 val selfTokenData = SELF.R2[Coll[(Coll[Byte], Long)]].get(0) - val selfTokenId = selfTokenData._1 val selfTokenValue = selfTokenData._2 val selfValue = SELF.value From a3f25ef4353e0fc7bfc67b55d5fc3156b4471c16 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 30 Sep 2024 14:28:30 +0300 Subject: [PATCH 15/22] LSV6 tests --- .../scala/sigma/LanguageSpecificationV6.scala | 156 +++++++++++++++++- 1 file changed, 150 insertions(+), 6 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 7605043cea..822611ea71 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,24 +1,26 @@ package sigma -import org.ergoplatform.ErgoHeader +import org.ergoplatform.{ErgoBox, ErgoHeader, ErgoLikeTransaction, Input} import scorex.util.encode.Base16 import sigma.VersionContext.V6SoftForkVersion -import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.Token +import org.ergoplatform.settings.ErgoAlgos import scorex.util.ModifierId import scorex.utils.{Ints, Longs, Shorts} -import sigma.ast.ErgoTree.ZeroHeader +import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} import sigma.ast.SCollection.SByteArray import sigma.ast.syntax.TrueSigmaProp import sigma.ast.{SInt, _} -import sigma.data.{CBigInt, CBox, CHeader, ExactNumeric} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAnyValue, CAvlTree, CBigInt, CBox, CHeader, CSigmaProp, ExactNumeric, ProveDHTuple, RType} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode -import sigma.data.{RType} -import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} +import sigma.util.Extensions.{BooleanOps, IntOps} +import sigmastate.eval.{CContext, CPreHeader} import sigmastate.exceptions.MethodNotFound import sigmastate.utils.Extensions.ByteOpsForSigma import sigmastate.utils.Helpers +import sigma.Extensions.{ArrayOps, CollOps} +import sigma.interpreter.{ContextExtension, ProverResult} import java.math.BigInteger import scala.util.{Failure, Success} @@ -1523,4 +1525,146 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + property("Context.getVar and getVarFromInput") { + + def contextData() = { + val input = CBox( + new ErgoBox( + 80946L, + new ErgoTree( + HeaderType @@ 16.toByte, + Vector( + SigmaPropConstant( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb"), + Helpers.decodeECPoint("023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d03"), + Helpers.decodeECPoint("03d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72"), + Helpers.decodeECPoint("037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441") + ) + ) + ) + ), + Right(ConstantPlaceholder(0, SSigmaProp)) + ), + Coll(), + Map( + ErgoBox.R4 -> ByteArrayConstant(Helpers.decodeBytes("34")), + ErgoBox.R5 -> TrueLeaf + ), + ModifierId @@ ("0000bfe96a7c0001e7a5ee00aafb80ff057fbe7f8c6680e33a3dc18001820100"), + 1.toShort, + 5 + ) + ) + + val tx = ErgoLikeTransaction( + IndexedSeq(), + IndexedSeq(input.wrappedValue) + ) + + val tx2 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(true)))))), + IndexedSeq(input.wrappedValue) + ) + + val tx3 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> IntConstant(0)))))), + IndexedSeq(input.wrappedValue) + ) + + val tx4 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(false)))))), + IndexedSeq(input.wrappedValue) + ) + + val ctx = CContext( + _dataInputs = Coll[Box](), + headers = Coll[Header](), + preHeader = CPreHeader( + 0.toByte, + Colls.fromArray(Array.fill(32)(0.toByte)), + -755484979487531112L, + 9223372036854775807L, + 11, + Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + Helpers.decodeBytes("007f00") + ), + inputs = Coll[Box](input), + outputs = Coll[Box](), + height = 11, + selfBox = input.copy(), // in 3.x, 4.x implementation selfBox is never the same instance as input (see toSigmaContext) + selfIndex = 0, + lastBlockUtxoRootHash = CAvlTree( + AvlTreeData( + ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17").toColl, + AvlTreeFlags(true, true, true), + 1211925457, + None + ) + ), + _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + vars = Colls + .replicate[AnyValue](10, null) // reserve 10 vars + .append(Coll[AnyValue]( + CAnyValue(Helpers.decodeBytes("00")), + CAnyValue(true))), + spendingTransaction = tx, + activatedScriptVersion = activatedVersionInTests, + currentErgoTreeVersion = ergoTreeVersionInTests + ) + val ctx2 = ctx.copy(spendingTransaction = tx2) + val ctx3 = ctx.copy(spendingTransaction = tx3, vars = ctx.vars.patch(11, Coll(CAnyValue(0)), 1)) + val ctx4 = ctx.copy(spendingTransaction = tx4, vars = ctx.vars.patch(11, Coll(CAnyValue(false)), 1)) + + (ctx, ctx2, ctx3, ctx4) + } + + def getVarFromInput = { + newFeature( + { (x: Context) => x.getVarFromInput[Boolean](0, 11)}, + "{ (x: Context) => x.getVarFromInput[Boolean](0, 11) }", + FuncValue( + Array((1, SContext)), + MethodCall.typed[Value[SOption[SBoolean.type]]]( + ValUse(1, SContext), + SContextMethods.getVarFromInputMethod.withConcreteTypes(Map(STypeVar("T") -> SBoolean)), + Array(ShortConstant(0.toShort), ByteConstant(11.toByte)), + Map(STypeVar("T") -> SBoolean) + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + val (ctx, ctx2, ctx3, ctx4) = contextData() + + verifyCases( + Seq( + ctx -> new Expected(ExpectedResult(Success(None), None)), // input with # provided does not exist + ctx2 -> new Expected(ExpectedResult(Success(Some(true)), None)), + ctx3 -> new Expected(ExpectedResult(Success(None), None)), // not expected type in context var + ctx4 -> new Expected(ExpectedResult(Success(Some(false)), None)) + ), + getVarFromInput + ) + + def getVar = { + newFeature( + { (x: Context) => x.getVar[Boolean](11)}, + "{ (x: Context) => CONTEXT.getVar[Boolean](11.toByte) }", + sinceVersion = VersionContext.V6SoftForkVersion + ) + } + + verifyCases( + Seq( + ctx2 -> new Expected(ExpectedResult(Success(Some(true)), None)), + ctx3 -> new Expected(ExpectedResult(Failure(new sigma.exceptions.InvalidType("Cannot getVar[Boolean](11): invalid type of value TestValue(0) at id=11")), None)), // not expected type in context var + ctx4 -> new Expected(ExpectedResult(Success(Some(false)), None)) + ), + getVar + ) + } + } From f24e9ad44614ed9c91949c7594eb68bb19f5569d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 30 Sep 2024 16:04:25 +0300 Subject: [PATCH 16/22] predefined fn, LangSpec notes, polishing PR --- .../src/main/scala/sigma/ast/SMethod.scala | 7 +++++ .../main/scala/sigma/ast/SigmaPredef.scala | 15 ++++++++- .../src/main/scala/sigma/ast/methods.scala | 8 +++-- .../sigma/interpreter/ContextExtension.scala | 4 +++ docs/LangSpec.md | 14 ++++++++- .../sigma/compiler/phases/SigmaTyper.scala | 1 + .../utxo/BasicOpsSpecification.scala | 31 ++++++++++++++++--- 7 files changed, 70 insertions(+), 10 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala index 5c3e5faf40..e5481cee5b 100644 --- a/data/shared/src/main/scala/sigma/ast/SMethod.scala +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -302,6 +302,13 @@ object SMethod { (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2]): RMethod = RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass) + /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. + * @param methodName the name of the method to lookup + * @param cT the class where to search the methodName + * @param cA1 the class of the method's first argument + * @param cA2 the class of the method's second argument + * @param cA3 the class of the method's third argument + */ def javaMethodOf[T, A1, A2, A3] (methodName: String) (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2], cA3: ClassTag[A3]): RMethod = diff --git a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 8b89851938..7a28ee6dd9 100644 --- a/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -146,6 +146,18 @@ object SigmaPredef { Seq(ArgInfo("varId", "\\lst{Byte} identifier of context variable"))) ) + val GetVarFromInputFunc = PredefinedFunc("getVarFromInput", + Lambda(Array(paramT), Array("inputId" -> SShort, "varId" -> SByte), SOption(tT), None), + PredefFuncInfo( + { case (Ident(_, SFunc(_, SOption(rtpe), _)), Seq(inputId: Constant[SNumericType]@unchecked, varId: Constant[SNumericType]@unchecked)) => + mkMethodCall(Context, SContextMethods.getVarFromInputMethod, IndexedSeq(SShort.downcast(inputId.value.asInstanceOf[AnyVal]), SByte.downcast(varId.value.asInstanceOf[AnyVal])), Map(tT -> rtpe)) + }), + OperationInfo(MethodCall, + "Get context variable with given \\lst{varId} and type.", + Seq(ArgInfo("inputId", "\\lst{Byte} index of input to read context variable from"), + ArgInfo("varId", "\\lst{Byte} identifier of context variable"))) + ) + def PKFunc(networkPrefix: NetworkPrefix) = PredefinedFunc("PK", Lambda(Array("input" -> SString), SSigmaProp, None), PredefFuncInfo( @@ -448,7 +460,8 @@ object SigmaPredef { SubstConstantsFunc, ExecuteFromVarFunc, ExecuteFromSelfRegFunc, - SerializeFunc + SerializeFunc, + GetVarFromInputFunc ).map(f => f.name -> f).toMap def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index ae05152630..33ffa9c9b2 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -1586,16 +1586,18 @@ case object SContextMethods extends MonoTypeMethods { MethodCallIrBuilder, javaMethodOf[Context, Byte, RType[_]]("getVar"), { mtype => Array(mtype.tRange.asOption[SType].elemType) }) - .withInfo(MethodCall, "") // todo: desc + .withInfo(MethodCall, "Get context variable with given \\lst{varId} and type.") - // todo: costing, desc lazy val getVarFromInputMethod = SMethod( this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT)) .withIRInfo( MethodCallIrBuilder, javaMethodOf[Context, Short, Byte, RType[_]]("getVarFromInput"), { mtype => Array(mtype.tRange.asOption[SType].elemType) }) - .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) + .withInfo(MethodCall, "Get context variable with given \\lst{varId} and type.", + ArgInfo("inputIdx", "Index of input to read variable from."), + ArgInfo("varId", "Index of variable.") + ) private lazy val commonMethods = super.getMethods() ++ Array( dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, diff --git a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala index fd269c177c..f03d076d43 100644 --- a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala +++ b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala @@ -20,6 +20,10 @@ case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ ContextExtension(values ++ bindings) } + /** + * @param varId - index of context variable + * @return context variable with provided index or None if it is not there + */ def get(varId: Byte): Option[EvaluatedValue[_ <: SType]] = values.get(varId) } diff --git a/docs/LangSpec.md b/docs/LangSpec.md index 5b72a8659d..7ec7dd7729 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -919,7 +919,7 @@ def longToByteArray(input: Long): Coll[Byte] def decodePoint(bytes: Coll[Byte]): GroupElement -/** Extracts Context variable by id and type. +/** Extracts Context variable from self input by id and type. * ErgoScript is typed, so accessing a the variables is an operation which involves * some expected type given in brackets. Thus `getVar[Int](id)` expression should * evaluate to a valid value of the `Option[Int]` type. @@ -976,6 +976,18 @@ def decodePoint(bytes: Coll[Byte]): GroupElement */ def getVar[T](tag: Int): Option[T] +/** Extracts Context variable from any input by input id, variable id and variable type. + * Unlike getVar, it is not throwing exception when expected type does not match real type of the variable. + * Thus it can be used to get context variable from self without exception, using selfBoxIndex, e.g. + *
+  *   {
+  *       val idx = CONTEXT.selfBoxIndex
+  *       sigmaProp(CONTEXT.getVarFromInput[Int](idx.toShort, 1.toByte).get == 5)
+  *   }
+  * 
+ */ +def getVarFromInput[T](inputId: Short, varId: Byte): Option[T] + /** Construct a new SigmaProp value representing public key of Diffie Hellman * signature protocol. When executed as part of Sigma protocol allow to provide * for a verifier a zero-knowledge proof of secret knowledge. diff --git a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala index cc771cbac9..833bd413b9 100644 --- a/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala @@ -134,6 +134,7 @@ class SigmaTyper(val builder: SigmaBuilder, res case Apply(ApplyTypes(sel @ Select(obj, n, _), Seq(rangeTpe)), args) => + // downcast getVarFromInput arguments to short and byte val nArgs = if (n == SContextMethods.getVarFromInputMethod.name && args.length == 2 && args(0).isInstanceOf[Constant[_]] && diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index df4e85de3b..b43fcdd90e 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -167,7 +167,7 @@ class BasicOpsSpecification extends CompilerTestingCommons 1.toByte -> IntConstant(5) ).toSeq test("R1", env, customExt, - "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 1).get == 5) }", + "{ sigmaProp(getVarFromInput[Int](0, 1).get == 5) }", null ) } @@ -179,19 +179,40 @@ class BasicOpsSpecification extends CompilerTestingCommons } } - property("getVarFromInput - invalid input") { + property("getVarFromInput - self index") { def getVarTest(): Assertion = { val customExt = Map( 1.toByte -> IntConstant(5) ).toSeq test("R1", env, customExt, - "{ sigmaProp(CONTEXT.getVarFromInput[Int](1, 1).get == 5) }", + """{ + | val idx = CONTEXT.selfBoxIndex + | sigmaProp(CONTEXT.getVarFromInput[Int](idx.toShort, 1.toByte).get == 5) + | }""".stripMargin, null ) } if (VersionContext.current.isV6SoftForkActivated) { + getVarTest() + } else { an[Exception] should be thrownBy getVarTest() + } + } + + property("getVarFromInput - invalid input") { + def getVarTest(): Assertion = { + val customExt = Map( + 1.toByte -> IntConstant(5) + ).toSeq + test("R1", env, customExt, + "{ sigmaProp(CONTEXT.getVarFromInput[Int](1, 1).isDefined == false) }", + null + ) + } + + if (VersionContext.current.isV6SoftForkActivated) { + getVarTest() } else { an[Exception] should be thrownBy getVarTest() } @@ -593,13 +614,13 @@ class BasicOpsSpecification extends CompilerTestingCommons 1.toByte -> IntConstant(5) ).toSeq test("R1", env, customExt, - "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 2).get == 5) }", + "{ sigmaProp(CONTEXT.getVarFromInput[Int](0, 2).isDefined == false) }", null ) } if (VersionContext.current.isV6SoftForkActivated) { - an[Exception] should be thrownBy getVarTest() + getVarTest() } else { an[Exception] should be thrownBy getVarTest() } From edc91baa371048578d5fdeb08b5c15cf33526b32 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 30 Sep 2024 16:55:58 +0300 Subject: [PATCH 17/22] tests --- .../ErgoLikeTransactionSpec.scala | 32 ++++++++++++------- .../utxo/BasicOpsSpecification.scala | 7 ---- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala index 6a7ef5a512..3fc9c7fd3d 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala @@ -7,8 +7,7 @@ import org.ergoplatform.settings.ErgoAlgos import scorex.util.encode.Base16 import scorex.util.{ModifierId, Random} import sigma.Extensions._ -import sigma.SigmaDslTesting -import sigma.ast.SCollection.SByteArray +import sigma.{SigmaDslTesting, VersionContext} import sigma.ast.SType._ import sigma.ast.syntax.{ErgoBoxCandidateRType, TrueSigmaProp} import sigma.ast._ @@ -20,9 +19,11 @@ import sigmastate.helpers.TestingHelpers.copyTransaction import sigmastate.utils.Helpers import sigma.SigmaDslTesting import sigma.Extensions._ +import sigma.ast.SCollection.SByteArray +import sigmastate.CrossVersionProps import sigmastate.utils.Helpers.EitherOps // required for Scala 2.11 -class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { +class ErgoLikeTransactionSpec extends SigmaDslTesting with CrossVersionProps with JsonCodecs { property("ErgoBox test vectors") { val token1 = "6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001" @@ -99,14 +100,24 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { { // test case for R2 val res = b1.get(ErgoBox.R2).get - val exp = Coll( - (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, - (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token2).toColl) -> 500L - ).map(identity).toConstant - // TODO v6.0 (16h): fix collections equality and remove map(identity) - // (PairOfColl should be equal CollOverArray but now it is not) + + // We have versioned check here due to fixed collections equality in 6.0.0 + // (PairOfColl equal CollOverArray now) // see (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/909) - res shouldBe exp + if(VersionContext.current.isV6SoftForkActivated) { + val exp = Coll( + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token2).toColl) -> 500L + ).toConstant + res shouldBe exp + exp shouldBe res + } else { + val exp = Coll( + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, + (Digest32Coll @@ ErgoAlgos.decodeUnsafe(token2).toColl) -> 500L + ).map(identity).toConstant + res shouldBe exp + } } { // test case for R3 @@ -470,7 +481,6 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { // test equivalence of "from Json" and "from bytes" deserialization tx2.id shouldBe tx.id tx2.id shouldBe "d5c0a7908bbb8eefe72ad70a9f668dd47b748239fd34378d3588d5625dd75c82" - println(tx2.id) } property("Tuple in register test vector") { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 431c5166e1..68fe1022e7 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -804,13 +804,6 @@ class BasicOpsSpecification extends CompilerTestingCommons } } - property("coll equality") { - def collTest() = test("R1", env, ext, - "{ SELF.tokens == Coll[(Coll[Byte], Long)]() }", - null - ) - } - property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From 5cabedd6f73a3d0737a221919e51771a0c5653a2 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 18 Oct 2024 14:42:40 +0300 Subject: [PATCH 18/22] equalsPairCollWithCollOverArray --- .../scala/sigma/data/CollsOverArrays.scala | 86 +++++++++++++------ .../ErgoLikeTransactionSpec.scala | 2 +- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index dc73253628..2d6a4a5cdf 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -1,6 +1,8 @@ package sigma.data import debox.{Buffer, cfor} +import sigma.Evaluation.stypeToRType +import sigma.data.CollOverArray.equalsPairCollWithCollOverArray import sigma.data.RType._ import sigma.util.{CollectionUtil, MaxArrayLength, safeConcatArrays_v5} import sigma.{Coll, CollBuilder, PairColl, VersionContext, requireSameLength} @@ -12,7 +14,9 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil s"Cannot create collection with size ${toArray.length} greater than $MaxArrayLength") override def tItem: RType[A] = tA + @inline def length: Int = toArray.length + @inline def apply(i: Int): A = toArray.apply(i) override def isEmpty: Boolean = length == 0 @@ -29,8 +33,11 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil } def foreach(f: A => Unit): Unit = toArray.foreach(f) + def exists(p: A => Boolean): Boolean = toArray.exists(p) + def forall(p: A => Boolean): Boolean = toArray.forall(p) + def filter(p: A => Boolean): Coll[A] = builder.fromArray(toArray.filter(p)) def foldLeft[B](zero: B, op: ((B, A)) => B): B = toArray.foldLeft(zero)((b, a) => op((b, a))) @@ -117,12 +124,14 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil override def unionSet(that: Coll[A]): Coll[A] = { val set = debox.Set.ofSize[A](this.length) val res = Buffer.ofSize[A](this.length) + @inline def addItemToSet(x: A) = { if (!set(x)) { set.add(x) res += x } } + def addToSet(arr: Array[A]) = { val limit = arr.length cfor(0)(_ < limit, _ + 1) { i => @@ -140,19 +149,41 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil override def equals(obj: scala.Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match { case obj: CollOverArray[_] if obj.tItem == this.tItem => java.util.Objects.deepEquals(obj.toArray, this.toArray) - case obj: PairColl[_, _] if obj.tItem == this.tItem => - if(VersionContext.current.isV6SoftForkActivated) { - java.util.Objects.deepEquals(obj.toArray, this.toArray) + case obj: PairColl[Any, Any] if obj.tItem == this.tItem => + if (VersionContext.current.isV6SoftForkActivated) { + equalsPairCollWithCollOverArray(obj, this.asInstanceOf[CollOverArray[Any]]) } else { false } case _ => false }) - override def hashCode() = CollectionUtil.deepHashCode(toArray) + override def hashCode(): Int = CollectionUtil.deepHashCode(toArray) } -private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => +object CollOverArray { + + // comparing PairColl and CollOverArray instances + private[data] def equalsPairCollWithCollOverArray(pc: PairColl[Any, Any], coa: CollOverArray[Any]): Boolean = { + val ls = pc.ls + val rs = pc.rs + val ts = coa.toArray + if (ts.length == ls.length && ts.isInstanceOf[Array[(Any, Any)]]) { + val ta = ts.asInstanceOf[Array[(Any, Any)]] + var eq = true + cfor(0)(_ < ta.length && eq, _ + 1) { i => + eq = java.util.Objects.deepEquals(ta(i)._1, ls(i)) && java.util.Objects.deepEquals(ta(i)._2, rs(i)) + } + eq + } else { + false + } + } + +} + +private[sigma] class CollOverArrayBuilder extends CollBuilder { + builder => @inline override def pairColl[@specialized A, @specialized B](as: Coll[A], bs: Coll[B]): PairColl[A, B] = { if (VersionContext.current.isJitActivated) { @@ -176,12 +207,12 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => } } - private def fromBoxedPairs[A, B](seq: Seq[(A, B)])(implicit tA: RType[A], tB: RType[B]): PairColl[A,B] = { + private def fromBoxedPairs[A, B](seq: Seq[(A, B)])(implicit tA: RType[A], tB: RType[B]): PairColl[A, B] = { val len = seq.length val resA = Array.ofDim[A](len)(tA.classTag) val resB = Array.ofDim[B](len)(tB.classTag) cfor(0)(_ < len, _ + 1) { i => - val item = seq.apply(i).asInstanceOf[(A,B)] + val item = seq.apply(i).asInstanceOf[(A, B)] resA(i) = item._1 resB(i) = item._2 } @@ -189,7 +220,7 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => } override def fromItems[T](items: T*)(implicit cT: RType[T]): Coll[T] = cT match { - case pt: PairType[a,b] => + case pt: PairType[a, b] => val tA = pt.tFst val tB = pt.tSnd fromBoxedPairs(items)(tA, tB) @@ -198,16 +229,16 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => } override def fromArray[@specialized T: RType](arr: Array[T]): Coll[T] = RType[T] match { - case pt: PairType[a,b] => + case pt: PairType[a, b] => val tA = pt.tFst val tB = pt.tSnd - fromBoxedPairs[a,b](arr.asInstanceOf[Array[(a,b)]])(tA, tB) + fromBoxedPairs[a, b](arr.asInstanceOf[Array[(a, b)]])(tA, tB) case _ => new CollOverArray(arr, builder) } override def replicate[@specialized T: RType](n: Int, v: T): Coll[T] = RType[T] match { - case pt: PairType[a,b] => + case pt: PairType[a, b] => val tA = pt.tFst val tB = pt.tSnd val tuple = v.asInstanceOf[(a, b)] @@ -216,8 +247,8 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => fromArray(Array.fill(n)(v)) } - override def unzip[@specialized A, @specialized B](xs: Coll[(A,B)]): (Coll[A], Coll[B]) = xs match { - case pa: PairColl[_,_] => (pa.ls, pa.rs) + override def unzip[@specialized A, @specialized B](xs: Coll[(A, B)]): (Coll[A], Coll[B]) = xs match { + case pa: PairColl[_, _] => (pa.ls, pa.rs) case _ => val limit = xs.length implicit val tA = xs.tItem.tFst @@ -236,7 +267,7 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => left.zip(right).map { case (l, r) => (l ^ r).toByte } override def emptyColl[T](implicit cT: RType[T]): Coll[T] = cT match { - case pt: PairType[a,b] => + case pt: PairType[a, b] => val ls = emptyColl(pt.tFst) val rs = emptyColl(pt.tSnd) pairColl(ls, rs).asInstanceOf[Coll[T]] @@ -245,14 +276,14 @@ private[sigma] class CollOverArrayBuilder extends CollBuilder { builder => } } -class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R]) extends PairColl[L,R] { +class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R]) extends PairColl[L, R] { override def equals(that: scala.Any): Boolean = (this eq that.asInstanceOf[AnyRef]) || (that match { - case that: PairColl[_,_] if that.tItem == this.tItem => + case that: PairColl[_, _] if that.tItem == this.tItem => ls == that.ls && rs == that.rs - case that: CollOverArray[_] if that.tItem == this.tItem => + case that: CollOverArray[Any] if that.tItem == this.tItem => if (VersionContext.current.isV6SoftForkActivated) { - java.util.Objects.deepEquals(that.toArray, this.toArray) + equalsPairCollWithCollOverArray(this.asInstanceOf[PairColl[Any, Any]], that) } else { false } @@ -260,7 +291,9 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R }) override def hashCode() = ls.hashCode() * 41 + rs.hashCode() + @inline implicit def tL: RType[L] = ls.tItem + @inline implicit def tR: RType[R] = rs.tItem override lazy val tItem: RType[(L, R)] = { @@ -268,8 +301,11 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R } override def builder: CollBuilder = ls.builder + override def toArray: Array[(L, R)] = ls.toArray.zip(rs.toArray) + @inline override def length: Int = if (ls.length <= rs.length) ls.length else rs.length + @inline override def apply(i: Int): (L, R) = (ls(i), rs(i)) override def isEmpty: Boolean = length == 0 @@ -317,7 +353,7 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R true } - override def filter(p: ((L, R)) => Boolean): Coll[(L,R)] = { + override def filter(p: ((L, R)) => Boolean): Coll[(L, R)] = { val len = ls.length val resL: Buffer[L] = Buffer.empty[L](ls.tItem.classTag) val resR: Buffer[R] = Buffer.empty[R](rs.tItem.classTag) @@ -346,9 +382,9 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R state } - override def slice(from: Int, until: Int): PairColl[L,R] = builder.pairColl(ls.slice(from, until), rs.slice(from, until)) + override def slice(from: Int, until: Int): PairColl[L, R] = builder.pairColl(ls.slice(from, until), rs.slice(from, until)) - def append(other: Coll[(L, R)]): Coll[(L,R)] = { + def append(other: Coll[(L, R)]): Coll[(L, R)] = { val arrs = builder.unzip(other) builder.pairColl(ls.append(arrs._1), rs.append(arrs._2)) } @@ -365,7 +401,7 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R } } - def zip[@specialized B](ys: Coll[B]): PairColl[(L,R), B] = builder.pairColl(this, ys) + def zip[@specialized B](ys: Coll[B]): PairColl[(L, R), B] = builder.pairColl(this, ys) def startsWith(ys: Coll[(L, R)]): Boolean = ys match { case yp: PairOfCols[L, R] => ls.startsWith(yp.ls) && rs.startsWith(yp.rs) @@ -421,18 +457,20 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R } override def unionSet(that: Coll[(L, R)]): Coll[(L, R)] = { - val set = new java.util.HashSet[(L,R)](32) + val set = new java.util.HashSet[(L, R)](32) implicit val ctL = ls.tItem.classTag implicit val ctR = rs.tItem.classTag val resL = Buffer.empty[L] val resR = Buffer.empty[R] - def addToSet(item: (L,R)) = { + + def addToSet(item: (L, R)) = { if (!set.contains(item)) { set.add(item) resL += item._1 resR += item._2 } } + var i = 0 val thisLen = math.min(ls.length, rs.length) while (i < thisLen) { diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala index 3fc9c7fd3d..4b3aa2eab5 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala @@ -23,7 +23,7 @@ import sigma.ast.SCollection.SByteArray import sigmastate.CrossVersionProps import sigmastate.utils.Helpers.EitherOps // required for Scala 2.11 -class ErgoLikeTransactionSpec extends SigmaDslTesting with CrossVersionProps with JsonCodecs { + class ErgoLikeTransactionSpec extends SigmaDslTesting with CrossVersionProps with JsonCodecs { property("ErgoBox test vectors") { val token1 = "6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001" From 9036aec8769a1dd724231d258d11eadf005cb400 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 21 Oct 2024 23:04:16 +0300 Subject: [PATCH 19/22] improving tests in BasicOpsSpecification, type test for getVarFromInput --- .../src/main/scala/sigma/SigmaDsl.scala | 4 +- .../main/scala/sigmastate/eval/CContext.scala | 4 +- .../sigmastate/lang/SigmaTyperTest.scala | 6 + .../utxo/BasicOpsSpecification.scala | 221 ++++++++++-------- 4 files changed, 134 insertions(+), 101 deletions(-) diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 422e4b7969..16331febfd 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -592,13 +592,13 @@ trait Context { /** * A variant of `getVar` to extract a context variable by id and type from any input * - * @param inputId - input index + * @param inputIndex - input index * @param id - context variable id * @tparam T - expected type of the variable * @return Some(value) if the variable is defined in the context AND has the given type. * None otherwise */ - def getVarFromInput[T](inputId: Short, id: Byte)(implicit cT: RType[T]): Option[T] + def getVarFromInput[T](inputIndex: Short, id: Byte)(implicit cT: RType[T]): Option[T] def vars: Coll[AnyValue] diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala index bed0c4b013..b0e5b01186 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala @@ -73,8 +73,8 @@ case class CContext( } else None } - override def getVarFromInput[T](inputId: Short, id: Byte)(implicit tT: RType[T]): Option[T] = { - spendingTransaction.inputs.lift(inputId).flatMap(_.extension.get(id)) match { + override def getVarFromInput[T](inputIndex: Short, id: Byte)(implicit tT: RType[T]): Option[T] = { + spendingTransaction.inputs.lift(inputIndex).flatMap(_.extension.get(id)) match { case Some(v) if stypeToRType[SType](v.tpe) == tT => Some(v.value.asInstanceOf[T]) case _ => None diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 5bfb72cd4f..c86cb11afc 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -687,6 +687,12 @@ class SigmaTyperTest extends AnyPropSpec typecheck(env, "CONTEXT.getVar[Int](1.toByte).get") shouldBe SInt } + property("SContext.getVarFromInput") { + runWithVersion(VersionContext.V6SoftForkVersion) { + typecheck(env, "CONTEXT.getVarFromInput[Int](1.toShort, 1.toByte).get") shouldBe SInt + } + } + property("SAvlTree.digest") { typecheck(env, "getVar[AvlTree](1).get.digest") shouldBe SByteArray } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index ea9539a65a..0eba3c9878 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -7,7 +7,7 @@ import scorex.util.encode.Base16 import scorex.utils.Ints import sigma.Extensions.ArrayOps import sigma.{SigmaTestingData, VersionContext} -import sigma.VersionContext.V6SoftForkVersion +import sigma.VersionContext.{V6SoftForkVersion, withVersions} import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -19,7 +19,7 @@ import sigmastate._ import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigma.interpreter.ContextExtension.VarBinding -import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings +import sigmastate.interpreter.CErgoTreeEvaluator.{DefaultEvalSettings, currentEvaluator} import sigmastate.interpreter.Interpreter._ import sigma.ast.Apply import sigma.eval.EvalSettings @@ -92,8 +92,11 @@ class BasicOpsSpecification extends CompilerTestingCommons // is not supported by ErgoScript Compiler) // In such cases we use expected property as the property to test propExp.asSigmaProp - } else - compile(env, script).asBoolValue.toSigmaProp + } else { + withVersions(VersionContext.MaxSupportedScriptVersion, ergoTreeVersionInTests) { + compile(env, script).asBoolValue.toSigmaProp + } + } if (propExp != null) prop shouldBe propExp @@ -145,20 +148,6 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } - property("Context.getVar") { - def varTest() = { - test("GetVar1", env, ext, - "{ CONTEXT.getVar[Int](intVar2.toByte).get == 2 }", - null - ) - } - - if(VersionContext.current.isV6SoftForkActivated) { - varTest() - } else { - an[Exception] should be thrownBy(varTest()) - } - } property("getVarFromInput") { def getVarTest(): Assertion = { @@ -174,7 +163,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { - an[Exception] should be thrownBy getVarTest() + an[sigma.validation.ValidationException] should be thrownBy getVarTest() } } @@ -195,7 +184,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { - an[Exception] should be thrownBy getVarTest() + an[sigma.validation.ValidationException] should be thrownBy getVarTest() } } @@ -213,13 +202,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { - an[Exception] should be thrownBy getVarTest() + an[sigma.validation.ValidationException] should be thrownBy getVarTest() } } + property("Byte.toBits") { - def toBitsTest() = test("Byte.toBits", env, ext, + val customExt = Map( + 1.toByte -> ByteConstant(1) + ).toSeq + def toBitsTest() = test("Byte.toBits", env, customExt, """{ - | val b = 1.toByte + | val b = getVar[Byte](1).get | b.toBits == Coll(false, false, false, false, false, false, false, true) |}""".stripMargin, null @@ -228,14 +221,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBitsTest() } else { - an[Exception] shouldBe thrownBy(toBitsTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBitsTest()) } } property("Long.toBits") { - def toBitsTest() = test("Long.toBits", env, ext, + val customExt = Map( + 1.toByte -> LongConstant(1) + ).toSeq + def toBitsTest() = test("Long.toBits", env, customExt, """{ - | val b = 1L + | val b = getVar[Long](1).get | val ba = b.toBits | | // only rightmost bit is set @@ -247,7 +243,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBitsTest() } else { - an[Exception] shouldBe thrownBy(toBitsTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBitsTest()) } } @@ -264,7 +260,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBitsTest() } else { - an[Exception] shouldBe thrownBy(toBitsTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBitsTest()) } } @@ -281,10 +277,11 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseInverseTest() } else { - an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) } } + property("Byte.bitwiseInverse") { def bitwiseInverseTest(): Assertion = test("Byte.bitwiseInverse", env, ext, s"""{ @@ -297,14 +294,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseInverseTest() } else { - an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) } } property("Long.bitwiseInverse") { - def bitwiseInverseTest(): Assertion = test("Long.bitwiseInverse", env, ext, + val customExt = Map( + 1.toByte -> LongConstant(9223372036854775807L) + ).toSeq + def bitwiseInverseTest(): Assertion = test("Long.bitwiseInverse", env, customExt, s"""{ - | val l = 9223372036854775807L + | val l = getVar[Long](1).get | val lb = l.bitwiseInverse | lb.bitwiseInverse == l |}""".stripMargin, @@ -314,14 +314,18 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseInverseTest() } else { - an[Exception] shouldBe thrownBy(bitwiseInverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseInverseTest()) } } + property("Byte.bitwiseOr") { - def bitwiseOrTest(): Assertion = test("Byte.bitwiseOrTest", env, ext, + val customExt = Map( + 1.toByte -> ByteConstant(127) + ).toSeq + def bitwiseOrTest(): Assertion = test("Byte.bitwiseOrTest", env, customExt, s"""{ - | val x = 127.toByte + | val x = getVar[Byte](1).get | val y = (-128).toByte | x.bitwiseOr(y) == -1 |}""".stripMargin, @@ -331,7 +335,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseOrTest() } else { - an[Exception] shouldBe thrownBy(bitwiseOrTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) } } @@ -347,7 +351,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseOrTest() } else { - an[Exception] shouldBe thrownBy(bitwiseOrTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseOrTest()) } } @@ -364,14 +368,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseAndTest() } else { - an[Exception] shouldBe thrownBy(bitwiseAndTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) } } property("Short.bitwiseAnd") { - def bitwiseAndTest(): Assertion = test("Short.bitwiseAnd", env, ext, + val customExt = Map( + 1.toByte -> ShortConstant(32767) + ).toSeq + def bitwiseAndTest(): Assertion = test("Short.bitwiseAnd", env, customExt, s"""{ - | val x = (32767).toShort + | val x = getVar[Short](1).get | val y = (-32768).toShort | x.bitwiseAnd(y) == 0 |}""".stripMargin, @@ -381,14 +388,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseAndTest() } else { - an[Exception] shouldBe thrownBy(bitwiseAndTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseAndTest()) } } property("Short.bitwiseXor") { - def bitwiseXorTest(): Assertion = test("Short.bitwiseXor", env, ext, + val customExt = Map( + 1.toByte -> ShortConstant(32767) + ).toSeq + def bitwiseXorTest(): Assertion = test("Short.bitwiseXor", env, customExt, s"""{ - | val x = (32767).toShort + | val x = getVar[Short](1).get | val y = (-32768).toShort | x.bitwiseXor(y) == -1 |}""".stripMargin, @@ -398,7 +408,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { bitwiseXorTest() } else { - an[Exception] shouldBe thrownBy(bitwiseXorTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(bitwiseXorTest()) } } @@ -415,7 +425,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftLeftTest() } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -432,7 +442,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[IllegalArgumentException] shouldBe thrownBy(shiftLeftTest()) } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -449,7 +459,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftLeftTest() } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -466,7 +476,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftLeftTest() } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -482,7 +492,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[ArithmeticException] shouldBe thrownBy(shiftLeftTest()) } else { - an[Exception] shouldBe thrownBy(shiftLeftTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftLeftTest()) } } @@ -499,7 +509,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftRightTest() } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -516,7 +526,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftRightTest() } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -533,10 +543,11 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } + property("Long.shiftRight - neg") { def shiftRightTest(): Assertion = test("Long.shiftRight", env, ext, s"""{ @@ -550,7 +561,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftRightTest() } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -567,7 +578,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -585,7 +596,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { shiftRightTest() } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -603,7 +614,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { an[IllegalArgumentException] shouldBe thrownBy(shiftRightTest()) } else { - an[Exception] shouldBe thrownBy(shiftRightTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(shiftRightTest()) } } @@ -621,7 +632,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getVarTest() } else { - an[Exception] should be thrownBy getVarTest() + an[sigma.validation.ValidationException] should be thrownBy getVarTest() } } @@ -642,7 +653,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -663,7 +674,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -687,7 +698,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -711,7 +722,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -735,7 +746,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -759,7 +770,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { reverseTest() } else { - an[Exception] shouldBe thrownBy(reverseTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(reverseTest()) } } @@ -777,7 +788,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { getTest() } else { - an[Exception] shouldBe thrownBy(getTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(getTest()) } } @@ -793,7 +804,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } @@ -809,7 +820,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } @@ -825,7 +836,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } @@ -842,14 +853,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } property("Global.fromBigEndianBytes - Long.toBytes") { - def fromTest() = test("fromBigEndianBytes - long", env, ext, + val customExt = Map( + 1.toByte -> LongConstant(1088800L) + ).toSeq + def fromTest() = test("fromBigEndianBytes - long", env, customExt, s"""{ - | val l = 1088800L + | val l = getVar[Long](1).get | val ba = l.toBytes | Global.fromBigEndianBytes[Long](ba) == l |} @@ -859,7 +873,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } @@ -876,14 +890,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { fromTest() } else { - an[Exception] should be thrownBy(fromTest()) + an[sigma.validation.ValidationException] should be thrownBy(fromTest()) } } property("Int.toBytes") { - def toBytesTest() = test("Int.toBytes", env, ext, + val customExt = Map( + 1.toByte -> IntConstant(1) + ).toSeq + def toBytesTest() = test("Int.toBytes", env, customExt, """{ - | val l = 1 + | val l = getVar[Int](1).get | l.toBytes == Coll(0.toByte, 0.toByte, 0.toByte, 1.toByte) | }""".stripMargin, null @@ -892,14 +909,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[Exception] shouldBe thrownBy(toBytesTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) } } property("Int.toBits") { - def toBytesTest() = test("Int.toBytes", env, ext, + val customExt = Map( + 1.toByte -> IntConstant(1477959696) + ).toSeq + def toBytesTest() = test("Int.toBytes", env, customExt, """{ - | val l = 1477959696 + | val l = getVar[Int](1).get | l.toBits == Coll(false, true, false, true, true, false, false, false, false, false, false, true, false, true, true ,true, true, true, true, false, false, false, false, false, false, false, false, true, false, false, false, false) | }""".stripMargin, null @@ -908,14 +928,17 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[Exception] shouldBe thrownBy(toBytesTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) } } property("Byte.toBytes") { - def toBytesTest() = test("Byte.toBytes", env, ext, + val customExt = Map( + 1.toByte -> ByteConstant(10) + ).toSeq + def toBytesTest() = test("Byte.toBytes", env, customExt, """{ - | val l = 10.toByte + | val l = getVar[Byte](1).get | l.toBytes == Coll(10.toByte) | }""".stripMargin, null @@ -924,7 +947,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[Exception] shouldBe thrownBy(toBytesTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) } } @@ -941,7 +964,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[Exception] shouldBe thrownBy(toBytesTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(toBytesTest()) } } @@ -956,7 +979,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -973,7 +996,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -990,7 +1013,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1009,7 +1032,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1030,7 +1053,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [Exception] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1051,7 +1074,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [Exception] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1075,7 +1098,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an [sigma.exceptions.TyperException] should be thrownBy deserTest() + an [sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1100,7 +1123,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an[Exception] should be thrownBy deserTest() + an[sigma.validation.ValidationException] should be thrownBy deserTest() } else { deserTest() } @@ -1122,7 +1145,7 @@ class BasicOpsSpecification extends CompilerTestingCommons ) if (activatedVersionInTests < V6SoftForkVersion) { - an[Exception] should be thrownBy deserTest() + an[sigma.validation.ValidationException] should be thrownBy deserTest() } else { // we have wrapped CostLimitException here an[Exception] should be thrownBy deserTest() @@ -1162,7 +1185,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { optTest() } else { - an[Exception] shouldBe thrownBy(optTest()) + assertExceptionThrown(optTest(), _.isInstanceOf[NoSuchElementException]) } } @@ -1485,7 +1508,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if(VersionContext.current.isV6SoftForkActivated) { holTest() } else { - an[Exception] shouldBe thrownBy(holTest()) + an[sigma.validation.ValidationException] shouldBe thrownBy(holTest()) } } @@ -1799,11 +1822,15 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("Box.getReg") { + val customExt = Map( + 1.toByte -> IntConstant(0) + ).toSeq def getRegTest(): Assertion = { - test("Box.getReg", env, ext, + test("Box.getReg", env, customExt, """{ + | val idx = getVar[Int](1).get | val x = SELF - | x.getReg[Long](0).get == SELF.value && + | x.getReg[Long](idx).get == SELF.value && | x.getReg[Coll[(Coll[Byte], Long)]](2).get == SELF.tokens && | x.getReg[Int](9).isEmpty |}""".stripMargin, @@ -1814,7 +1841,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getRegTest() } else { - an[Exception] should be thrownBy getRegTest() + an[sigma.exceptions.ConstraintFailed] should be thrownBy getRegTest() } } @@ -1835,7 +1862,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { getRegTest() } else { - an[Exception] should be thrownBy getRegTest() + an[java.nio.BufferUnderflowException] should be thrownBy getRegTest() } } From 8ae51e45a58bdd7d611f7a7692c6b1883fdb7ac4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 21 Oct 2024 23:15:19 +0300 Subject: [PATCH 20/22] split in LSV6 --- .../scala/sigma/LanguageSpecificationV6.scala | 177 +++++++++--------- 1 file changed, 90 insertions(+), 87 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 34b156fc79..15ce673332 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1530,104 +1530,104 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } - property("Context.getVar and getVarFromInput") { - - def contextData() = { - val input = CBox( - new ErgoBox( - 80946L, - new ErgoTree( - HeaderType @@ 16.toByte, - Vector( - SigmaPropConstant( - CSigmaProp( - ProveDHTuple( - Helpers.decodeECPoint("03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb"), - Helpers.decodeECPoint("023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d03"), - Helpers.decodeECPoint("03d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72"), - Helpers.decodeECPoint("037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441") - ) + private def contextData() = { + val input = CBox( + new ErgoBox( + 80946L, + new ErgoTree( + HeaderType @@ 16.toByte, + Vector( + SigmaPropConstant( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb"), + Helpers.decodeECPoint("023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d03"), + Helpers.decodeECPoint("03d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72"), + Helpers.decodeECPoint("037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441") ) ) - ), - Right(ConstantPlaceholder(0, SSigmaProp)) - ), - Coll(), - Map( - ErgoBox.R4 -> ByteArrayConstant(Helpers.decodeBytes("34")), - ErgoBox.R5 -> TrueLeaf + ) ), - ModifierId @@ ("0000bfe96a7c0001e7a5ee00aafb80ff057fbe7f8c6680e33a3dc18001820100"), - 1.toShort, - 5 - ) + Right(ConstantPlaceholder(0, SSigmaProp)) + ), + Coll(), + Map( + ErgoBox.R4 -> ByteArrayConstant(Helpers.decodeBytes("34")), + ErgoBox.R5 -> TrueLeaf + ), + ModifierId @@ ("0000bfe96a7c0001e7a5ee00aafb80ff057fbe7f8c6680e33a3dc18001820100"), + 1.toShort, + 5 ) + ) - val tx = ErgoLikeTransaction( - IndexedSeq(), - IndexedSeq(input.wrappedValue) - ) + val tx = ErgoLikeTransaction( + IndexedSeq(), + IndexedSeq(input.wrappedValue) + ) - val tx2 = ErgoLikeTransaction( - IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(true)))))), - IndexedSeq(input.wrappedValue) - ) + val tx2 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(true)))))), + IndexedSeq(input.wrappedValue) + ) - val tx3 = ErgoLikeTransaction( - IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> IntConstant(0)))))), - IndexedSeq(input.wrappedValue) - ) + val tx3 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> IntConstant(0)))))), + IndexedSeq(input.wrappedValue) + ) - val tx4 = ErgoLikeTransaction( - IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(false)))))), - IndexedSeq(input.wrappedValue) - ) + val tx4 = ErgoLikeTransaction( + IndexedSeq(Input(input.ebox.id, ProverResult(Array.emptyByteArray, ContextExtension(Map(11.toByte -> BooleanConstant(false)))))), + IndexedSeq(input.wrappedValue) + ) - val ctx = CContext( - _dataInputs = Coll[Box](), - headers = Coll[Header](), - preHeader = CPreHeader( - 0.toByte, - Colls.fromArray(Array.fill(32)(0.toByte)), - -755484979487531112L, - 9223372036854775807L, - 11, - Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), - Helpers.decodeBytes("007f00") - ), - inputs = Coll[Box](input), - outputs = Coll[Box](), - height = 11, - selfBox = input.copy(), // in 3.x, 4.x implementation selfBox is never the same instance as input (see toSigmaContext) - selfIndex = 0, - lastBlockUtxoRootHash = CAvlTree( - AvlTreeData( - ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17").toColl, - AvlTreeFlags(true, true, true), - 1211925457, - None - ) - ), - _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), - vars = Colls - .replicate[AnyValue](10, null) // reserve 10 vars - .append(Coll[AnyValue]( - CAnyValue(Helpers.decodeBytes("00")), - CAnyValue(true))), - spendingTransaction = tx, - activatedScriptVersion = activatedVersionInTests, - currentErgoTreeVersion = ergoTreeVersionInTests - ) - val ctx2 = ctx.copy(spendingTransaction = tx2) - val ctx3 = ctx.copy(spendingTransaction = tx3, vars = ctx.vars.patch(11, Coll(CAnyValue(0)), 1)) - val ctx4 = ctx.copy(spendingTransaction = tx4, vars = ctx.vars.patch(11, Coll(CAnyValue(false)), 1)) + val ctx = CContext( + _dataInputs = Coll[Box](), + headers = Coll[Header](), + preHeader = CPreHeader( + 0.toByte, + Colls.fromArray(Array.fill(32)(0.toByte)), + -755484979487531112L, + 9223372036854775807L, + 11, + Helpers.decodeGroupElement("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + Helpers.decodeBytes("007f00") + ), + inputs = Coll[Box](input), + outputs = Coll[Box](), + height = 11, + selfBox = input.copy(), // in 3.x, 4.x implementation selfBox is never the same instance as input (see toSigmaContext) + selfIndex = 0, + lastBlockUtxoRootHash = CAvlTree( + AvlTreeData( + ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17").toColl, + AvlTreeFlags(true, true, true), + 1211925457, + None + ) + ), + _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), + vars = Colls + .replicate[AnyValue](10, null) // reserve 10 vars + .append(Coll[AnyValue]( + CAnyValue(Helpers.decodeBytes("00")), + CAnyValue(true))), + spendingTransaction = tx, + activatedScriptVersion = activatedVersionInTests, + currentErgoTreeVersion = ergoTreeVersionInTests + ) + val ctx2 = ctx.copy(spendingTransaction = tx2) + val ctx3 = ctx.copy(spendingTransaction = tx3, vars = ctx.vars.patch(11, Coll(CAnyValue(0)), 1)) + val ctx4 = ctx.copy(spendingTransaction = tx4, vars = ctx.vars.patch(11, Coll(CAnyValue(false)), 1)) + + (ctx, ctx2, ctx3, ctx4) + } - (ctx, ctx2, ctx3, ctx4) - } + property("getVarFromInput") { def getVarFromInput = { newFeature( - { (x: Context) => x.getVarFromInput[Boolean](0, 11)}, + { (x: Context) => x.getVarFromInput[Boolean](0, 11) }, "{ (x: Context) => x.getVarFromInput[Boolean](0, 11) }", FuncValue( Array((1, SContext)), @@ -1653,6 +1653,9 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ), getVarFromInput ) + } + + property("Context.getVar") { def getVar = { newFeature( @@ -1662,6 +1665,8 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } + val (_, ctx2, ctx3, ctx4) = contextData() + verifyCases( Seq( ctx2 -> new Expected(ExpectedResult(Success(Some(true)), None)), @@ -1672,8 +1677,6 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) } - - property("Option.getOrElse with lazy default") { val trace = TracedCost( From 1fbd4e598cd5331fe71ebe2492f5946c8c4bb095 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 24 Oct 2024 19:54:12 +0300 Subject: [PATCH 21/22] Update docs/LangSpec.md Co-authored-by: Alexander Slesarenko --- docs/LangSpec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/LangSpec.md b/docs/LangSpec.md index 7ec7dd7729..f1aace7de2 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -919,7 +919,7 @@ def longToByteArray(input: Long): Coll[Byte] def decodePoint(bytes: Coll[Byte]): GroupElement -/** Extracts Context variable from self input by id and type. +/** Extracts Context variable from SELF input by id and type. * ErgoScript is typed, so accessing a the variables is an operation which involves * some expected type given in brackets. Thus `getVar[Int](id)` expression should * evaluate to a valid value of the `Option[Int]` type. From 208a191534b54a197d40bd4fd6c338fa06a337f1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 24 Oct 2024 19:54:22 +0300 Subject: [PATCH 22/22] Update docs/LangSpec.md Co-authored-by: Alexander Slesarenko --- docs/LangSpec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/LangSpec.md b/docs/LangSpec.md index f1aace7de2..16defff5ff 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -976,7 +976,7 @@ def decodePoint(bytes: Coll[Byte]): GroupElement */ def getVar[T](tag: Int): Option[T] -/** Extracts Context variable from any input by input id, variable id and variable type. +/** Extracts Context variable from any input by input index, variable id and variable type. * Unlike getVar, it is not throwing exception when expected type does not match real type of the variable. * Thus it can be used to get context variable from self without exception, using selfBoxIndex, e.g. *