Skip to content

I1039 Finishes executeFromSelfReg #1055

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b970b89
Implements executeFromSelfReg with tests and corner cases
novamon Mar 4, 2025
e0de4ad
Cleanup edge case in graphbuilding
novamon Mar 4, 2025
fc5e457
Reapply test fix
novamon Mar 4, 2025
7672ae6
Reapply test fix - shortened names because the name conflicted with t…
novamon Mar 4, 2025
3181763
Reapply cleanup
novamon Mar 4, 2025
d5fdea5
Reapply Coll[Byte] corner case test showing different behavior. i.e. …
novamon Mar 4, 2025
9a5f73e
Update IThrows test to pass correctly if not blocked by the exception…
novamon Mar 5, 2025
df49f2c
Update IReduct test to pass barring no exception thrown.
novamon Mar 5, 2025
48276d3
Update CCorner to again, match appropriate value barring thrown excep…
novamon Mar 5, 2025
cab77dd
remove unused script in tests
novamon Mar 5, 2025
f3cfdfe
Added appropriate check to DeserializeRegister, clean up tests
novamon Mar 9, 2025
35af418
get rid of warnings
novamon Mar 9, 2025
6b6f6c4
finish tests for proper default value forcing and invalid register
novamon Mar 9, 2025
9660bb7
Update LangSpec.md adding executeFromVar and executeFromSelfReg
novamon Mar 9, 2025
460f02e
correct LangSpec.md documentation
novamon Mar 9, 2025
08f3c03
Update github actions to v4
novamon Mar 11, 2025
92e7665
Update github actions release
novamon Mar 11, 2025
c7d8f46
Remove out of order code block
novamon Mar 13, 2025
d006353
Reapply edge case check, tests
novamon Mar 13, 2025
c7ae11a
Cleanup
novamon Mar 15, 2025
58b6c32
relax default to T instead of Option[T]. Add test from jozanek for ty…
novamon Mar 17, 2025
161e360
Update LandSpec.md to suit change in default value
novamon Mar 17, 2025
7cfdf17
remove unneeded imports.
novamon Mar 17, 2025
adf9521
Change executeFromSelfReg to executeFromSelfRegWithDefault, add execu…
novamon Mar 19, 2025
8c33977
Cleanup
novamon Mar 25, 2025
873f8ef
More cleanup
novamon Mar 25, 2025
869b079
Add AST Reflection to sigma.SigmaDataReflection.
novamon Mar 26, 2025
8eb10f3
Remove trailing ,
novamon Mar 26, 2025
762e2f2
Remove default empty value from bare version of executeFromSelfReg. U…
novamon Mar 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout current branch (full)
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0

Expand All @@ -36,7 +36,7 @@ jobs:
java-version: ${{ matrix.java }}

- name: Cache sbt
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: |
~/.sbt
Expand All @@ -45,7 +45,7 @@ jobs:
~/.cache/coursier/v1
~/AppData/Local/Coursier/Cache/v1
~/Library/Caches/Coursier/v1
key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}
key: ${{ runner.os }}-sbt-cache-v4-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}

- name: Runs tests
run: sbt -jvm-opts ci/ci.jvmopts ++${{ matrix.scala }} test
Expand All @@ -70,7 +70,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout current branch (full)
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0

Expand All @@ -91,7 +91,7 @@ jobs:
java-version: ${{ matrix.java }}

- name: Cache sbt
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: |
~/.sbt
Expand All @@ -100,7 +100,7 @@ jobs:
~/.cache/coursier/v1
~/AppData/Local/Coursier/Cache/v1
~/Library/Caches/Coursier/v1
key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}
key: ${{ runner.os }}-sbt-cache-v4-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}

- name: Runs tests and collect coverage
run: sbt -jvm-opts ci/ci.jvmopts ++${{ matrix.scala }} coreJS/test interpreterJS/test parsersJS/test sdkJS/test scJS/test
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ jobs:
name: Publish release to Sonatype
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Setup Java and Scala
uses: olafurpg/setup-scala@v10
with:
java-version: [email protected]

- name: Cache sbt
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: |
~/.sbt
Expand All @@ -29,7 +29,7 @@ jobs:
~/.cache/coursier/v1
~/AppData/Local/Coursier/Cache/v1
~/Library/Caches/Coursier/v1
key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}
key: ${{ runner.os }}-sbt-cache-v4-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}

- name: Setup NPM
uses: actions/setup-node@v3
Expand Down
12 changes: 12 additions & 0 deletions data/shared/src/main/scala/sigma/SigmaDataReflection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,18 @@ object SigmaDataReflection {
)
)

registerClassEntry(classOf[DeserializeRegister[_]],
constructors = Array(
mkConstructor(Array(classOf[RegisterId], classOf[SType], classOf[SOption[_]])) { args =>
new DeserializeRegister[SType](
args(0).asInstanceOf[RegisterId],
args(1).asInstanceOf[SType],
args(2).asInstanceOf[Option[Value[SType]]]
)
}
)
)

registerClassEntry(classOf[LongToByteArray],
constructors = Array(
mkConstructor(Array(classOf[Value[_]])) { args =>
Expand Down
2 changes: 1 addition & 1 deletion data/shared/src/main/scala/sigma/ast/Operations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ object Operations {
}

object DeserializeRegisterInfo extends InfoObject {
private val func = predefinedOps.funcs("executeFromSelfReg")
private val func = predefinedOps.funcs("executeFromSelfRegWithDefault")
val idArg: ArgInfo = func.argInfo("id")
val defaultArg: ArgInfo = func.argInfo("default")
val argInfos: Seq[ArgInfo] = Array(idArg, defaultArg)
Expand Down
53 changes: 49 additions & 4 deletions data/shared/src/main/scala/sigma/ast/SigmaPredef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ package sigma.ast

import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress}
import org.ergoplatform.ErgoBox.RegisterId
import scorex.util.encode.{Base16, Base58, Base64}
import sigma.data._
import sigma.{Colls}
import sigma.ast.SCollection.{SByteArray, SIntArray}
import sigma.ast.SOption.SIntOption
import sigma.ast.SigmaPropConstant
import sigma.ast.syntax._
import sigma.data.Nullable
import sigma.data.RType.asType
import sigma.Evaluation.stypeToRType
import scala.reflect.ClassTag
import sigma.exceptions.InvalidArguments
import sigma.serialization.CoreByteWriter.ArgInfo
import sigma.serialization.ValueSerializer
Expand Down Expand Up @@ -383,25 +389,63 @@ object SigmaPredef {
Seq(ArgInfo("id", "identifier of the context variable")))
)

val ExecuteFromSelfRegFunc = PredefinedFunc("executeFromSelfReg",
val ExecuteFromSelfRegWithDefaultFunc = PredefinedFunc("executeFromSelfRegWithDefault",
Lambda(
Seq(paramT),
Array("id" -> SByte, "default" -> SOption(tT)),
Array("id" -> SInt, "default" -> tT),
tT, None
),
PredefFuncInfo(undefined),
PredefFuncInfo(
{ case (Ident(_, SFunc(_, rtpe, _)), Seq(id: Constant[SNumericType]@unchecked, default)) =>
val idx: Int = SInt.downcast((id.value.asInstanceOf[AnyVal]))
if (idx < 0 || idx >= org.ergoplatform.ErgoBox.allRegisters.length) {
default.v
} else {
val r: RegisterId = org.ergoplatform.ErgoBox.registerByIndex(idx)
val d = Some(default.asValue[rtpe.type])
mkDeserializeRegister[rtpe.type](r, rtpe, d)
}
}),
OperationInfo(DeserializeRegister,
"""Extracts SELF register as \lst{Coll[Byte]}, deserializes it to script
| and then executes this script in the current context.
| The original \lst{Coll[Byte]} of the script is available as \lst{SELF.getReg[Coll[Byte]](id)}.
| Type parameter \lst{T} result type of the deserialized script.
| Throws an exception if the actual script type doesn't conform to \lst{T}.
| Returns a result of the script execution in the current context
| Returns a result of the script execution in the current context or the default value
| provided when the specified register is unavailable
""".stripMargin,
Seq(ArgInfo("id", "identifier of the register"),
ArgInfo("default", "optional default value, if register is not available")))
)

val ExecuteFromSelfRegFunc = PredefinedFunc("executeFromSelfReg",
Lambda(
Seq(paramT),
Array("id" -> SInt),
tT, None
),
PredefFuncInfo(
{ case (Ident(_, SFunc(_, rtpe, _)), Seq(id: Constant[SNumericType]@unchecked)) =>
val idx: Int = SInt.downcast((id.value.asInstanceOf[AnyVal]))
if (idx < 0 || idx >= org.ergoplatform.ErgoBox.allRegisters.length) {
throw new InvalidArguments(s"Invalid register specified $id")
}

val r: RegisterId = org.ergoplatform.ErgoBox.registerByIndex(idx)
mkDeserializeRegister[rtpe.type](r, rtpe, None)
}),
OperationInfo(DeserializeRegister,
"""Extracts SELF register as \lst{Coll[Byte]}, deserializes it to script
| and then executes this script in the current context.
| The original \lst{Coll[Byte]} of the script is available as \lst{SELF.getReg[Coll[Byte]](id)}.
| Type parameter \lst{T} result type of the deserialized script.
| Throws an exception if the actual script type doesn't conform to \lst{T}.
| Returns a result of the script execution in the current context
""".stripMargin,
Seq(ArgInfo("id", "identifier of the register")))
)

val globalFuncs: Map[String, PredefinedFunc] = Seq(
AllOfFunc,
AnyOfFunc,
Expand Down Expand Up @@ -429,6 +473,7 @@ object SigmaPredef {
AvlTreeFunc,
SubstConstantsFunc,
ExecuteFromVarFunc,
ExecuteFromSelfRegWithDefaultFunc,
ExecuteFromSelfRegFunc
).map(f => f.name -> f).toMap

Expand Down
2 changes: 1 addition & 1 deletion data/shared/src/main/scala/sigma/ast/transformers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ object DeserializeContext extends ValueCompanion {
}

/** Extract register of SELF box as Coll[Byte], deserialize it into Value and inline into executing script.
* NOTE: it only applicable to SELF box
* NOTE: it's only applicable to SELF box
*/
case class DeserializeRegister[V <: SType](reg: RegisterId, tpe: V, default: Option[Value[V]] = None) extends Deserialize[V] {
override def companion = DeserializeRegister
Expand Down
49 changes: 49 additions & 0 deletions docs/LangSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,55 @@ def PK(input: String): SigmaProp
*/
def deserialize[T](string: String): T

/**
*
* Extracts, deserializes and executes a script contained in the context variable specified
* by id. Returns the result of the script execution in the current context.
* Throws an exception if the result type of the execution doesn't conform to the return
* type specified.
*
* @param id context variable holding the serialized script to execute
* @tparam T expected type of the variable and return type.
* @return result of the executed script
* @throws InvalidType exception when the result type of the execution value is
* different from T.
*/
def executeFromVar[T](id: Byte): T

/**
*
* Extracts, deserializes and executes a script contained in the SELF register
* indicated by id. Returns the result of the script execution in the current context
* An exception is thrown if the result type of the execution doesn't conform to the
* return type specified and if an invalid register is specified.
*
* @param id register id holding the serialized script to execute
* @tparam T expected type of the register and return type.
* @return result of the executed script or default value
* @throws InterpreterException when a script reduces to false
* @throws InvalidType exception when the result type of the execution value is
* different from T.
*/
def executeFromSelfReg[T](id: Int): T

/**
*
* Extracts, deserializes and executes a script contained in the SELF register
* indicated by id. Returns the result of the script execution in the current context
* or default value, if the register is unavailable. An exception is thrown if the result
* type of the execution doesn't conform to the return type specified and if an invalid
* register is specified.
*
* @param id register id holding the serialized script to execute
* @param default value returned if the register is unavailable
* @tparam T expected type of the register and return type.
* @return result of the executed script or default value
* @throws InterpreterException when a script reduces to false
* @throws InvalidType exception when the result type of the execution value is
* different from T.
*/
def executeFromSelfRegWithDefault[T](id: Int, default: T): T

/**
* Transforms serialized bytes of ErgoTree with segregated constants by
* replacing constants at given positions with new values. This operation allow
Expand Down
9 changes: 8 additions & 1 deletion sc/shared/src/main/scala/sigma/compiler/ir/Base.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package sigma.compiler.ir

import debox.{cfor, Buffer => DBuffer}
import sigma.compiler.ir.core.MutableLazy
import sigma.ast.{DeserializeContext, SType}
import sigma.ast.{DeserializeContext, DeserializeRegister, SType}
import sigma.data.{AVHashMap, Nullable, RType}
import sigma.data.OverloadHack.Overloaded1
import sigma.reflection.RConstructor
Expand Down Expand Up @@ -211,6 +211,13 @@ abstract class Base { thisIR: IRContext =>
override def resultType: Elem[V#WrappedType] = e
}

/**
* Def done in order to carry on DeserializeRegister through stages of compilation intact
*/
case class DeserializeRegisterDef[V <: SType](d: DeserializeRegister[V], e: Elem[V#WrappedType]) extends Def[V#WrappedType] {
override def resultType: Elem[V#WrappedType] = e
}

/** Base class for virtualized instances of type companions.
* Each virtualized entity type (trait or class) may have virtualized companion class. */
abstract class CompanionDef[T] extends Def[T] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import sigma.{SigmaException, ast}
import sigmastate.interpreter.Interpreter.ScriptEnv

import scala.collection.mutable.ArrayBuffer
import scala.language.{existentials,implicitConversions}

/** Perform translation of typed expression given by [[Value]] to a graph in IRContext.
* Which be than be translated to [[ErgoTree]] by using [[TreeBuilding]].
Expand Down Expand Up @@ -553,6 +554,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
val e = stypeToElem(d.tpe)
DeserializeContextDef(d, e)

case d: DeserializeRegister[T] =>
val e = stypeToElem(d.tpe)
DeserializeRegisterDef[T](d, e)

case ValUse(valId, _) =>
env.getOrElse(valId, !!!(s"ValUse $valId not found in environment $env"))

Expand Down
3 changes: 3 additions & 0 deletions sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ trait TreeBuilding extends Base { IR: IRContext =>
case Def(DeserializeContextDef(d, _)) =>
d

case Def(DeserializeRegisterDef(d, _)) =>
d

case Def(IsContextProperty(v)) => v
case s if s == sigmaDslBuilder => Global

Expand Down
8 changes: 8 additions & 0 deletions sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,14 @@ class SigmaTyperTest extends AnyPropSpec
typecheck(env, "executeFromVar[Boolean](1)") shouldBe SBoolean
}

property("executeFromSelfRegWithDefault") {
typecheck(env, "executeFromSelfRegWithDefault[Boolean](4, getVar[Boolean](1).get)") shouldBe SBoolean

an[TyperException] should be thrownBy {
typecheck(env, "executeFromSelfRegWithDefault[Boolean](4, getVar[Int](1).get)")
}
}

property("LogicalNot") {
typecheck(env, "!true") shouldBe SBoolean
typefail(env, "!getVar[SigmaProp](1).get", 1, 2)
Expand Down
Loading