Skip to content

Commit

Permalink
merging w. 6.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
kushti committed Oct 24, 2024
2 parents 03bfd6a + b074b28 commit 9d7962a
Show file tree
Hide file tree
Showing 23 changed files with 558 additions and 130 deletions.
11 changes: 11 additions & 0 deletions core/shared/src/main/scala/sigma/SigmaDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,17 @@ trait Context {
*/
def getVar[T](id: Byte)(implicit cT: RType[T]): Option[T]

/**
* A variant of `getVar` to extract a context variable by id and type from any input
*
* @param inputIndex - input index
* @param id - context variable id
* @tparam T - expected type of the variable
* @return Some(value) if the variable is defined in the context AND has the given type.
* None otherwise
*/
def getVarFromInput[T](inputIndex: Short, id: Byte)(implicit cT: RType[T]): Option[T]

def vars: Coll[AnyValue]

/** Maximum version of ErgoTree currently activated on the network.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ object ReflectionData {
mkMethod(clazz, "getVar", Array[Class[_]](classOf[Byte], classOf[RType[_]])) { (obj, args) =>
obj.asInstanceOf[Context].getVar(args(0).asInstanceOf[Byte])(args(1).asInstanceOf[RType[_]])
},
mkMethod(clazz, "getVarFromInput", Array[Class[_]](classOf[Short], classOf[Byte], classOf[RType[_]])) { (obj, args) =>
obj.asInstanceOf[Context].getVarFromInput(args(0).asInstanceOf[Short], args(1).asInstanceOf[Byte])(args(2).asInstanceOf[RType[_]])
},
mkMethod(clazz, "headers", Array[Class[_]]()) { (obj, _) =>
obj.asInstanceOf[Context].headers
}
Expand Down
12 changes: 12 additions & 0 deletions data/shared/src/main/scala/sigma/ast/SMethod.scala
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,18 @@ object SMethod {
(implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2]): RMethod =
RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass)

/** Return [[Method]] descriptor for the given `methodName` on the given `cT` type.
* @param methodName the name of the method to lookup
* @param cT the class where to search the methodName
* @param cA1 the class of the method's first argument
* @param cA2 the class of the method's second argument
* @param cA3 the class of the method's third argument
*/
def javaMethodOf[T, A1, A2, A3]
(methodName: String)
(implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2], cA3: ClassTag[A3]): RMethod =
RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass, cA3.runtimeClass)

/** Default fallback method call recognizer which builds MethodCall ErgoTree nodes. */
val MethodCallIrBuilder: PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue] = {
case (builder, obj, method, args, tparamSubst) =>
Expand Down
13 changes: 13 additions & 0 deletions data/shared/src/main/scala/sigma/ast/SigmaPredef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -468,6 +480,7 @@ object SigmaPredef {
ExecuteFromVarFunc,
ExecuteFromSelfRegFunc,
SerializeFunc,
GetVarFromInputFunc,
FromBigEndianBytesFunc
).map(f => f.name -> f).toMap

Expand Down
42 changes: 39 additions & 3 deletions data/shared/src/main/scala/sigma/ast/methods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1677,16 +1677,52 @@ case object SContextMethods extends MonoTypeMethods {
lazy val selfBoxIndexMethod = propertyCall("selfBoxIndex", SInt, 8, FixedCost(JitCost(20)))
lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash)
lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey)
lazy val getVarMethod = SMethod(

lazy val getVarV5Method = SMethod(
this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind)
.withInfo(GetVar, "Get context variable with given \\lst{varId} and type.",
ArgInfo("varId", "\\lst{Byte} identifier of context variable"))

protected override def getMethods() = super.getMethods() ++ Seq(
lazy val getVarV6Method = SMethod(
this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind, Seq(tT))
.withIRInfo(
MethodCallIrBuilder,
javaMethodOf[Context, Byte, RType[_]]("getVar"),
{ mtype => Array(mtype.tRange.asOption[SType].elemType) })
.withInfo(MethodCall, "Get context variable with given \\lst{varId} and type.")

lazy val getVarFromInputMethod = SMethod(
this, "getVarFromInput", SFunc(Array(SContext, SShort, SByte), SOption(tT), Array(paramT)), 12, GetVar.costKind, Seq(tT))
.withIRInfo(
MethodCallIrBuilder,
javaMethodOf[Context, Short, Byte, RType[_]]("getVarFromInput"),
{ mtype => Array(mtype.tRange.asOption[SType].elemType) })
.withInfo(MethodCall, "Get context variable with given \\lst{varId} and type.",
ArgInfo("inputIdx", "Index of input to read variable from."),
ArgInfo("varId", "Index of variable.")
)

private lazy val commonMethods = super.getMethods() ++ Array(
dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod,
selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod
selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod
)

private lazy val v5Methods = commonMethods ++ Seq(
getVarV5Method
)

private lazy val v6Methods = commonMethods ++ Seq(
getVarV6Method, getVarFromInputMethod
)

protected override def getMethods(): Seq[SMethod] = {
if (VersionContext.current.isV6SoftForkActivated) {
v6Methods
} else {
v5Methods
}
}

/** Names of methods which provide blockchain context.
* This value can be reused where necessary to avoid allocations. */
val BlockchainContextMethodNames: IndexedSeq[String] = Array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer}
* @param values internal container of the key-value pairs
*/
case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ <: SType]]) {
def add(bindings: VarBinding*): ContextExtension =
def add(bindings: VarBinding*): ContextExtension = {
ContextExtension(values ++ bindings)
}

/**
* @param varId - index of context variable
* @return context variable with provided index or None if it is not there
*/
def get(varId: Byte): Option[EvaluatedValue[_ <: SType]] = values.get(varId)
}

object ContextExtension {
Expand Down
14 changes: 13 additions & 1 deletion docs/LangSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 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.
* <pre class="stHighlight">
* {
* val idx = CONTEXT.selfBoxIndex
* sigmaProp(CONTEXT.getVarFromInput[Int](idx.toShort, 1.toByte).get == 5)
* }
* </pre>
*/
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}


Expand Down
12 changes: 12 additions & 0 deletions interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package sigmastate.eval

import debox.cfor
import org.ergoplatform.{ErgoLikeTransactionTemplate, UnsignedInput}
import sigma.Evaluation.stypeToRType
import sigma.Extensions.ArrayOps
import sigma._
import sigma.ast.SType
import sigma.data._
import sigma.exceptions.InvalidType

Expand All @@ -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 {
Expand Down Expand Up @@ -69,6 +73,14 @@ case class CContext(
} else None
}

override def getVarFromInput[T](inputIndex: Short, id: Byte)(implicit tT: RType[T]): Option[T] = {
spendingTransaction.inputs.lift(inputIndex).flatMap(_.extension.get(id)) match {
case Some(v) if stypeToRType[SType](v.tpe) == tT => Some(v.value.asInstanceOf[T])
case _ =>
None
}
}

/** Return a new context instance with variables collection updated.
* @param bindings a new binding of the context variables with new values.
* @return a new instance (if `bindings` non-empty) with the specified bindings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 10 additions & 0 deletions sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sigma.compiler.ir

import org.ergoplatform._
import sigma.ast.SType.tT
import sigma.Evaluation.stypeToRType
import sigma.ast.SType.tT
import sigma.ast.TypeCodes.LastConstantCode
Expand Down Expand Up @@ -1059,6 +1060,15 @@ 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))
val c3 = stypeToElem(typeSubst.apply(tT))
ctx.getVarFromInput(c1, c2)(c3)
case _ => throwError()
}
case (tree: Ref[AvlTree]@unchecked, SAvlTreeMethods) => method.name match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sigma.compiler.ir

import org.ergoplatform._
import sigma.VersionContext
import sigma.Evaluation.{rtypeToSType, stypeToRType}
import sigma.ast.SType.tT
import sigma.ast._
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,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];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1626,10 +1626,19 @@ object Context extends EntityObject("Context") {
}

override def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = {
val st = Evaluation.rtypeToSType(cT.sourceType)
asRep[WOption[T]](mkMethodCall(self,
ContextClass.getMethod("getVar", classOf[Sym], classOf[Elem[_]]),
Array[AnyRef](id, cT),
true, false, element[WOption[T]]))
true, false, element[WOption[T]], Map(tT -> st)))
}

override def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = {
val st = Evaluation.rtypeToSType(cT.sourceType)
asRep[WOption[T]](mkMethodCall(self,
ContextClass.getMethod("getVarFromInput", classOf[Sym], classOf[Sym], classOf[Elem[_]]),
Array[AnyRef](inputId, varId, cT),
true, false, element[WOption[T]], Map(tT -> st)))
}

}
Expand Down Expand Up @@ -1723,10 +1732,19 @@ object Context extends EntityObject("Context") {
}

def getVar[T](id: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = {
val st = Evaluation.rtypeToSType(cT.sourceType)
asRep[WOption[T]](mkMethodCall(source,
ContextClass.getMethod("getVar", classOf[Sym], classOf[Elem[_]]),
Array[AnyRef](id, cT),
true, true, element[WOption[T]]))
true, true, element[WOption[T]], Map(tT -> st)))
}

def getVarFromInput[T](inputId: Ref[Short], varId: Ref[Byte])(implicit cT: Elem[T]): Ref[WOption[T]] = {
val st = Evaluation.rtypeToSType(cT.sourceType)
asRep[WOption[T]](mkMethodCall(source,
ContextClass.getMethod("getVarFromInput", classOf[Sym], classOf[Sym], classOf[Elem[_]]),
Array[AnyRef](inputId, varId, cT),
true, true, element[WOption[T]], Map(tT -> st)))
}
}

Expand All @@ -1745,7 +1763,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"
))
}
}
Expand Down
25 changes: 18 additions & 7 deletions sc/shared/src/main/scala/sigma/compiler/phases/SigmaTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,19 @@ class SigmaTyper(val builder: SigmaBuilder,
res

case Apply(ApplyTypes(sel @ Select(obj, n, _), Seq(rangeTpe)), args) =>
// downcast getVarFromInput arguments to short and byte
val nArgs = if (n == SContextMethods.getVarFromInputMethod.name &&
args.length == 2 &&
args(0).isInstanceOf[Constant[_]] &&
args(1).isInstanceOf[Constant[_]] &&
args(0).tpe.isNumType &&
args(1).tpe.isNumType) {
IndexedSeq(ShortConstant(SShort.downcast(args(0).asInstanceOf[Constant[SNumericType]].value.asInstanceOf[AnyVal])).withSrcCtx(args(0).sourceContext),
ByteConstant(SByte.downcast(args(1).asInstanceOf[Constant[SNumericType]].value.asInstanceOf[AnyVal])).withSrcCtx(args(1).sourceContext))
} else args

val newObj = assignType(env, obj)
val newArgs = args.map(assignType(env, _))
val newArgs = nArgs.map(assignType(env, _))
newObj.tpe match {
case p: SProduct =>
MethodsContainer.getMethod(p, n) match {
Expand All @@ -155,7 +166,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)
Expand Down Expand Up @@ -221,6 +232,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)
Expand Down Expand Up @@ -409,11 +425,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4533,6 +4533,7 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite =>
.append(Coll[AnyValue](
CAnyValue(Helpers.decodeBytes("00")),
CAnyValue(true))),
spendingTransaction = null,
activatedScriptVersion = activatedVersionInTests,
currentErgoTreeVersion = ergoTreeVersionInTests
)
Expand Down
Loading

0 comments on commit 9d7962a

Please sign in to comment.