Skip to content

Commit

Permalink
predefined fn, LangSpec notes, polishing PR
Browse files Browse the repository at this point in the history
  • Loading branch information
kushti committed Sep 30, 2024
1 parent a3f25ef commit f24e9ad
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 10 deletions.
7 changes: 7 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,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 =
Expand Down
15 changes: 14 additions & 1 deletion 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 @@ -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]) = {
Expand Down
8 changes: 5 additions & 3 deletions data/shared/src/main/scala/sigma/ast/methods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

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 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.
* <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 @@ -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[_]] &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
Expand All @@ -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()
}
Expand Down Expand Up @@ -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()
}
Expand Down

0 comments on commit f24e9ad

Please sign in to comment.