From b7e2a38132c43054fb73ada852ecba86e45cce32 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 19 Jun 2024 12:39:33 +0300 Subject: [PATCH 01/13] 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 02/13] 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 03/13] 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 04/13] 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 05/13] 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 12e888d1b776d276e4013ea21c4187f1f32f4417 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 12 Aug 2024 14:38:54 +0300 Subject: [PATCH 06/13] 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 07/13] 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 08/13] 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 09/13] 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 9036aec8769a1dd724231d258d11eadf005cb400 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 21 Oct 2024 23:04:16 +0300 Subject: [PATCH 10/13] 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 11/13] 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 12/13] 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 13/13] 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. *