sigma.ast.values.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sigma-state_2.12 Show documentation
Show all versions of sigma-state_2.12 Show documentation
Interpreter of a Sigma-State language
The newest version!
package sigma.ast
import debox.cfor
import sigma.Extensions.ArrayOps
import sigma._
import sigma.ast.SCollection.SByteArray
import sigma.ast.TypeCodes.ConstantCode
import sigma.ast.syntax._
import sigma.crypto.{CryptoConstants, EcPointType}
import sigma.data.OverloadHack.Overloaded1
import sigma.data.{CSigmaDslBuilder, CSigmaProp, Nullable, RType, SigmaBoolean}
import sigma.eval.ErgoTreeEvaluator.DataEnv
import sigma.eval.{ErgoTreeEvaluator, SigmaDsl}
import sigma.exceptions.InterpreterException
import sigma.kiama.rewriting.Rewriter.count
import sigma.serialization.OpCodes._
import sigma.serialization.ValueCodes.OpCode
import sigma.serialization._
import sigma.util.CollectionUtil._
import sigma.util.Extensions._
import java.math.BigInteger
import java.util.{Arrays, Objects}
import scala.annotation.unused
import scala.collection.compat.immutable.ArraySeq
import scala.collection.mutable
import scala.language.implicitConversions
/** Base class for all ErgoTree expression nodes.
*
* @see [[ErgoTree]]
*/
abstract class Value[+S <: SType] extends SigmaNode {
/** The companion node descriptor with opCode, cost and other metadata. */
def companion: ValueCompanion
/** Unique id of the node class used in serialization of ErgoTree. */
def opCode: OpCode = companion.opCode
/** The type of the value represented by this node. If the value is an operation it is
* the type of operation result. */
def tpe: S
/** Every value represents an operation and that operation can be associated with a function type,
* describing functional meaning of the operation, kind of operation signature.
* Thus, we can obtain global operation identifiers by combining Value.opName with Value.opType,
* so that if (v1.opName == v2.opName) && (v1.opType == v2.opType) then v1 and v2 are functionally
* point-wise equivalent.
* This in particular means that if two _different_ ops have the same opType they _should_ have
* different opNames.
* Thus defined op ids are used in a v4.x Cost Model - a table of all existing primitives coupled with
* performance parameters.
* */
def opType: SFunc
/** Name of the operation. */
def opName: String = this.getClass.getSimpleName
/** Transforms this expression to SigmaProp expression or throws an exception. */
def toSigmaProp: SigmaPropValue = this match {
case b if b.tpe == SBoolean => BoolToSigmaProp(this.asBoolValue)
case p if p.tpe == SSigmaProp => p.asSigmaProp
case _ => sys.error(s"Expected SBoolean or SSigmaProp typed value, but was: $this")
}
/** Parser has some source information like line,column in the text. We need to keep it up until RuntimeCosting.
* The way to do this is to add Nullable property to every Value. Since Parser is always using SigmaBuilder
* to create nodes,
* Adding additional (implicit source: SourceContext) parameter to every builder method would pollute its API
* and also doesn't make sence during deserialization, where Builder is also used.
* We can assume some indirect mechanism to pass current source context into every mkXXX method of Builder.
* We can pass it using `scala.util.DynamicVariable` by wrapping each mkXXX call into `withValue { }` calls.
* The same will happen in Typer.
* We can take sourceContext from untyped nodes and use it while creating typed nodes.
* And we can make sourceContext of every Value writeOnce value, i.e. it will be Nullable.Null by default,
* but can be set afterwards, but only once.
* This property will not participate in equality and other operations, so will be invisible for existing code.
* But Builder can use it to set sourceContext if it is present.
*/
private[ast] var _sourceContext: Nullable[SourceContext] = Nullable.None
def sourceContext: Nullable[SourceContext] = _sourceContext
def sourceContext_=(srcCtx: Nullable[SourceContext]): Unit =
if (_sourceContext.isEmpty) {
_sourceContext = srcCtx
} else {
sys.error("_sourceContext can be set only once")
}
/** Defines an evaluation semantics of this tree node (aka Value or expression) in the given data environment.
* Should be implemented by all the ErgoTree nodes (aka operations).
* Thus, the ErgoTree interpreter implementation consists of combined implementations of this method.
* NOTE, this method shouldn't be called directly, instead use `evalTo` method.
*
* @param E Evaluator which defines evaluation context, cost accumulator, settings etc.
* @param env immutable map, which binds variables (given by ids) to the values
* @return the data value which is the result of evaluation
*/
protected def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any =
sys.error(s"Should be overriden in ${this.getClass}: $this")
/** Evaluates this node to the value of the given expected type.
* This method should called from all `eval` implementations.
*
* @tparam T expected type of the resulting value
* @param E Evaluator which defines evaluation context, cost accumulator, settings etc.
* @param env immutable map, which binds variables (given by ids) to the values
* @return the data value which is the result of evaluation
*/
@inline
final def evalTo[T](env: DataEnv)(implicit E: ErgoTreeEvaluator): T = {
if (E.settings.isMeasureOperationTime) E.profiler.onBeforeNode(this)
val v = eval(env)
if (E.settings.isMeasureOperationTime) E.profiler.onAfterNode(this)
v.asInstanceOf[T]
}
/** Add the cost given by the kind to the accumulator and associate it with this operation
* node.
*/
@inline
final def addCost(costKind: FixedCost)(implicit E: ErgoTreeEvaluator): Unit = {
E.addCost(costKind, this.companion.opDesc)
}
/** Add the cost given by the descriptor to the accumulator and associate it with this operation
* node.
*/
@inline
final def addCost[R](costKind: TypeBasedCost, tpe: SType)
(block: () => R)
(implicit E: ErgoTreeEvaluator): R = {
E.addTypeBasedCost(costKind, tpe, this.companion.opDesc)(block)
}
/** Add the cost of a repeated operation to the accumulator and associate it with this
* operation. The number of items (loop iterations) is known in advance (like in
* Coll.map operation)
*
* @param costKind cost descriptor of the operation
* @param nItems number of operations known in advance (before loop execution)
*/
@inline
final def addSeqCostNoOp(costKind: PerItemCost, nItems: Int)
(implicit E: ErgoTreeEvaluator): Unit = {
E.addSeqCostNoOp(costKind, nItems, this.companion.opDesc)
}
/** Add the cost of a repeated operation to the accumulator and associate it with this
* operation. The number of items (loop iterations) is known in advance (like in
* Coll.map operation)
*
* @param costKind cost descriptor of the operation
* @param nItems number of operations known in advance (before loop execution)
* @param block operation executed under the given cost
* @tparam R result type of the operation
*/
@inline
final def addSeqCost[R](costKind: PerItemCost, nItems: Int)
(block: () => R)(implicit E: ErgoTreeEvaluator): R = {
E.addSeqCost(costKind, nItems, this.companion.opDesc)(block)
}
}
object Value {
type PropositionCode = Byte
implicit def liftByte(n: Byte): Value[SByte.type] = ByteConstant(n)
implicit def liftShort(n: Short): Value[SShort.type] = ShortConstant(n)
implicit def liftInt(n: Int): Value[SInt.type] = IntConstant(n)
implicit def liftLong(n: Long): Value[SLong.type] = LongConstant(n)
implicit def liftByteArray(arr: Array[Byte]): Value[SByteArray] = ByteArrayConstant(arr)
implicit def liftBigInt(arr: BigInt): Value[SBigInt.type] = BigIntConstant(arr)
implicit def liftGroupElement(g: GroupElement): Value[SGroupElement.type] = GroupElementConstant(g)
implicit def liftECPoint(g: EcPointType): Value[SGroupElement.type] = GroupElementConstant(g)
implicit def liftSigmaProp(g: SigmaProp): Value[SSigmaProp.type] = SigmaPropConstant(g)
implicit def liftSigmaBoolean(sb: SigmaBoolean): Value[SSigmaProp.type] = SigmaPropConstant(SigmaDsl.SigmaProp(sb))
object Typed {
def unapply(v: SValue): Option[(SValue, SType)] = Some((v, v.tpe))
}
def notSupportedError(v: Any, opName: String) =
throw new IllegalArgumentException(s"Method $opName is not supported for node $v")
/** Immutable empty array of values. Can be used to avoid allocation. */
val EmptyArray = Array.empty[SValue]
/** Immutable empty Seq of values. Can be used to avoid allocation. */
val EmptySeq: IndexedSeq[SValue] = EmptyArray
/** Traverses the given expression tree and counts the number of deserialization
* operations. If it is non-zero, returns true. */
def hasDeserialize(exp: SValue): Boolean = {
val deserializeNode: PartialFunction[Any, Int] = {
case _: DeserializeContext[_] => 1
case _: DeserializeRegister[_] => 1
}
val c = count(deserializeNode)(exp)
c > 0
}
/** Traverses the given expression tree and counts the number of operations
* which read the blockchain context. If it is non-zero, returns true. */
def isUsingBlockchainContext(exp: SValue): Boolean = {
val blockchainContextNode: PartialFunction[Any, Int] = {
case Height => 1
case LastBlockUtxoRootHash => 1
case MinerPubkey => 1
case MethodCall(_, method, _, _) =>
method.objType match {
case SContextMethods =>
if (SContextMethods.BlockchainContextMethodNames.contains(method.name)) 1 else 0
case _ => 0
}
case _ => 0
}
val c = count(blockchainContextNode)(exp)
c > 0
}
def typeError(node: SValue, evalResult: Any) = {
val tpe = node.tpe
throw new InterpreterException(
s"""Invalid type returned by evaluator:
| expression: $node
| expected type: $tpe
| resulting value: $evalResult
""".stripMargin)
}
def typeError(tpe: SType, evalResult: Any) = {
throw new InterpreterException(
s"""Invalid type returned by evaluator:
| expected type: $tpe
| resulting value: $evalResult
""".stripMargin)
}
def checkType(node: SValue, evalResult: Any) = {
val tpe = node.tpe
if (!SType.isValueOfType(evalResult, tpe))
typeError(node, evalResult)
}
def checkType(tpe: SType, evalResult: Any) = {
if (!SType.isValueOfType(evalResult, tpe))
typeError(tpe, evalResult)
}
}
/** Base class for all companion objects which are used as operation descriptors. */
trait ValueCompanion extends SigmaNodeCompanion {
import ValueCompanion._
/** Unique id of the node class used in serialization of ErgoTree. */
def opCode: OpCode
/** Returns cost descriptor of this operation. */
def costKind: CostKind
override def toString: String = s"${this.getClass.getSimpleName}(${opCode.toUByte})"
def typeName: String = this.getClass.getSimpleName.replace("$", "")
def init() {
if (this.opCode != 0 && _allOperations.contains(this.opCode))
throw sys.error(s"Operation $this already defined")
_allOperations += (this.opCode -> this)
}
init()
val opDesc = CompanionDesc(this)
}
object ValueCompanion {
private val _allOperations: mutable.HashMap[Byte, ValueCompanion] = mutable.HashMap.empty
lazy val allOperations = _allOperations.toMap
}
/** Should be inherited by companion objects of operations with fixed cost kind. */
trait FixedCostValueCompanion extends ValueCompanion {
/** Returns cost descriptor of this operation. */
override def costKind: FixedCost
}
/** Should be inherited by companion objects of operations with per-item cost kind. */
trait PerItemCostValueCompanion extends ValueCompanion {
/** Returns cost descriptor of this operation. */
override def costKind: PerItemCost
}
/** Base class for ErgoTree nodes which represents a data value which has already been
* evaluated and no further evaluation (aka reduction) is necessary by the interpreter.
*
* @see Constant, ConcreteCollection, Tuple
*/
abstract class EvaluatedValue[+S <: SType] extends Value[S] {
/** The evaluated data value of the corresponding underlying data type. */
val value: S#WrappedType
override def opType: SFunc = {
val resType = tpe match {
case ct: SCollection[_] =>
SCollection(ct.typeParams.head.ident)
case ft @ SFunc(_, _, _) =>
ft.getGenericType
case _ => tpe
}
SFunc(ArraySeq.empty, resType)
}
}
/** Base class for all constant literals whose data value is already known and never
* changes.
*
* @see ConstantNode
*/
abstract class Constant[+S <: SType] extends EvaluatedValue[S]
/** ErgoTree node which represents data literals, i.e. data values embedded in an
* expression.
*
* @param value data value of the underlying Scala type
* @param tpe type descriptor of the data value and also the type of the value
* represented by this node.
* @see Constant
*/
case class ConstantNode[S <: SType](value: S#WrappedType, tpe: S) extends Constant[S] {
require(sigma.crypto.Platform.isCorrectType(value, tpe),
s"Invalid type of constant value $value, expected type $tpe")
override def companion: ValueCompanion = Constant
override def opCode: OpCode = companion.opCode
override def opName: String = s"Const"
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(Constant.costKind)
value
}
override def equals(obj: scala.Any): Boolean = (obj != null) && (this.eq(obj.asInstanceOf[AnyRef]) || (obj match {
case c: Constant[_] => tpe == c.tpe && Objects.deepEquals(value, c.value)
case _ => false
}))
override def hashCode(): Int = Arrays.deepHashCode(Array(value.asInstanceOf[AnyRef], tpe))
override def toString: String = tpe.asInstanceOf[SType] match {
case SGroupElement if value.isInstanceOf[GroupElement] =>
s"ConstantNode(${value.asInstanceOf[GroupElement].showToString},$tpe)"
case SGroupElement =>
sys.error(s"Invalid value in Constant($value, $tpe)")
case SInt => s"IntConstant($value)"
case SLong => s"LongConstant($value)"
case SBoolean if value == true => "TrueLeaf"
case SBoolean if value == false => "FalseLeaf"
case _ => s"ConstantNode($value,$tpe)"
}
}
object Constant extends FixedCostValueCompanion {
override def opCode: OpCode = OpCode @@ ConstantCode
/** Cost of: returning value from Constant node. */
override val costKind = FixedCost(JitCost(5))
/** Immutable empty array, can be used to save allocations in many places. */
val EmptyArray = Array.empty[Constant[SType]]
/** Immutable empty IndexedSeq, can be used to save allocations in many places. */
val EmptySeq: IndexedSeq[Constant[SType]] = Array.empty[Constant[SType]]
/** Helper factory method. */
def apply[S <: SType](
value: S#WrappedType,
tpe: S): Constant[S] = ConstantNode(value, tpe)
/** Recognizer of Constant tree nodes used in patterns. */
def unapply[S <: SType](v: EvaluatedValue[S]): Option[(S#WrappedType, S)] = v match {
case ConstantNode(value, tpe) => Some((value, tpe))
case _ => None
}
}
/** Placeholder for a constant in ErgoTree. Zero based index in ErgoTree.constants array. */
case class ConstantPlaceholder[S <: SType](
id: Int,
override val tpe: S) extends Value[S] {
def opType = SFunc(SInt, tpe)
override def companion: ValueCompanion = ConstantPlaceholder
override protected def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val c = E.constants(id)
addCost(ConstantPlaceholder.costKind)
val res = c.value
Value.checkType(c, res)
res
}
}
object ConstantPlaceholder extends ValueCompanion {
override def opCode: OpCode = ConstantPlaceholderCode
/** Cost of: accessing Constant in array by index. */
override val costKind = FixedCost(JitCost(1))
}
trait NotReadyValue[S <: SType] extends Value[S] {
}
// TODO v6.0: remove these TaggedVariable and TaggedVariableNode (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/584)
/** Reference a context variable by id. */
trait TaggedVariable[T <: SType] extends NotReadyValue[T] {
val varId: Byte
}
case class TaggedVariableNode[T <: SType](varId: Byte, override val tpe: T)
extends TaggedVariable[T] {
override def companion = TaggedVariable
def opType: SFunc = Value.notSupportedError(this, "opType")
}
object TaggedVariable extends ValueCompanion {
override def opCode: OpCode = TaggedVariableCode
override def costKind: CostKind = FixedCost(JitCost(1))
def apply[T <: SType](varId: Byte, tpe: T): TaggedVariable[T] =
TaggedVariableNode(varId, tpe)
}
/** High-level interface to internal representation of Unit constants in ErgoTree. */
object UnitConstant {
/** ErgoTree node that represent a literal of Unit type. It is global immutable value
* which should be reused wherever necessary to avoid allocations.
*/
val instance = apply()
/** Constucts a fresh new instance of Unit literal node. */
def apply() = Constant[SUnit.type]((), SUnit)
/** Recognizer to pattern match on Unit constant literal nodes (aka Unit constants). */
def unapply(node: SValue): Boolean = node match {
case ConstantNode(_, SUnit) => true
case _ => false
}
}
object ByteConstant {
def apply(value: Byte): Constant[SByte.type] = Constant[SByte.type](value, SByte)
}
object ShortConstant {
def apply(value: Short): Constant[SShort.type] = Constant[SShort.type](value, SShort)
}
object IntConstant {
def apply(value: Int): Constant[SInt.type] = Constant[SInt.type](value, SInt)
def unapply(v: SValue): Option[Int] = v match {
case Constant(value: Int, SInt) => Some(value)
case _ => None
}
def Zero = IntConstant(0)
}
object LongConstant {
def apply(value: Long): Constant[SLong.type] = Constant[SLong.type](value, SLong)
def unapply(v: SValue): Option[Long] = v match {
case Constant(value: Long, SLong) => Some(value)
case _ => None
}
}
object BigIntConstant {
def apply(value: BigInt): Constant[SBigInt.type] = Constant[SBigInt.type](value, SBigInt)
def apply(value: BigInteger): Constant[SBigInt.type] = Constant[SBigInt.type](SigmaDsl.BigInt(value), SBigInt)
def apply(value: Long): Constant[SBigInt.type] = Constant[SBigInt.type](SigmaDsl.BigInt(BigInteger.valueOf(value)), SBigInt)
}
object StringConstant {
def apply(value: String): Constant[SString.type] = Constant[SString.type](value, SString)
def unapply(v: SValue): Option[String] = v match {
case Constant(value: String, SString) => Some(value)
case _ => None
}
}
object BoxConstant {
def apply(value: Box): Constant[SBox.type] = Constant[SBox.type](value, SBox)
}
object GroupElementConstant {
def apply(value: EcPointType): Constant[SGroupElement.type] = apply(SigmaDsl.GroupElement(value))
def apply(value: GroupElement): Constant[SGroupElement.type] = Constant[SGroupElement.type](value, SGroupElement)
def unapply(v: SValue): Option[GroupElement] = v match {
case Constant(value: GroupElement, SGroupElement) => Some(value)
case _ => None
}
}
object SigmaPropConstant {
def apply(value: SigmaProp): Constant[SSigmaProp.type] = Constant[SSigmaProp.type](value, SSigmaProp)
def apply(value: SigmaBoolean): Constant[SSigmaProp.type] = Constant[SSigmaProp.type](CSigmaProp(value), SSigmaProp)
def unapply(v: SValue): Option[SigmaProp] = v match {
case Constant(value: SigmaProp, SSigmaProp) => Some(value)
case _ => None
}
}
object AvlTreeConstant {
def apply(value: AvlTree): Constant[SAvlTree.type] = Constant[SAvlTree.type](value, SAvlTree)
}
object PreHeaderConstant {
def apply(value: PreHeader): Constant[SPreHeader.type] = Constant[SPreHeader.type](value, SPreHeader)
def unapply(v: SValue): Option[PreHeader] = v match {
case Constant(value: PreHeader, SPreHeader) => Some(value)
case _ => None
}
}
object HeaderConstant {
def apply(value: Header): Constant[SHeader.type] = Constant[SHeader.type](value, SHeader)
def unapply(v: SValue): Option[Header] = v match {
case Constant(value: Header, SHeader) => Some(value)
case _ => None
}
}
trait NotReadyValueInt extends NotReadyValue[SInt.type] {
override def tpe = SInt
}
trait NotReadyValueLong extends NotReadyValue[SLong.type] {
override def tpe = SLong
}
trait NotReadyValueBigInt extends NotReadyValue[SBigInt.type] {
override def tpe = SBigInt
}
/** Base type for evaluated tree nodes of Coll type. */
trait EvaluatedCollection[T <: SType, C <: SCollection[T]] extends EvaluatedValue[C] {
/** Type descriptor of the collection elements. */
def elementType: T
}
object CollectionConstant {
def apply[T <: SType](
value: Coll[T#WrappedType],
elementType: T): Constant[SCollection[T]] =
Constant[SCollection[T]](value, SCollection(elementType))
def unapply[T <: SType](node: Value[SCollection[T]]): Option[(Coll[T#WrappedType], T)] = node match {
case c: Constant[SCollection[a]]@unchecked if c.tpe.isCollection =>
val v = c.value.asInstanceOf[Coll[T#WrappedType]]
val t = c.tpe.elemType
Some((v, t))
case _ => None
}
}
object ByteArrayConstant {
val ByteArrayTypeCode = (SCollectionType.CollectionTypeCode + SByte.typeCode).toByte
def apply(value: Coll[Byte]): CollectionConstant[SByte.type] = CollectionConstant[SByte.type](value, SByte)
def apply(value: Array[Byte]): CollectionConstant[SByte.type] = CollectionConstant[SByte.type](value.toColl, SByte)
def unapply(node: SValue): Option[Coll[Byte]] = node match {
case coll: CollectionConstant[SByte.type]@unchecked => coll match {
case CollectionConstant(arr, SByte) => Some(arr)
case _ => None
}
case _ => None
}
}
object ShortArrayConstant {
def apply(value: Coll[Short]): CollectionConstant[SShort.type] = CollectionConstant[SShort.type](value, SShort)
def apply(value: Array[Short]): CollectionConstant[SShort.type] = CollectionConstant[SShort.type](value.toColl, SShort)
def unapply(node: SValue): Option[Coll[Short]] = node match {
case coll: CollectionConstant[SShort.type]@unchecked => coll match {
case CollectionConstant(arr, SShort) => Some(arr)
case _ => None
}
case _ => None
}
}
object IntArrayConstant {
def apply(value: Coll[Int]): CollectionConstant[SInt.type] = CollectionConstant[SInt.type](value, SInt)
def apply(value: Array[Int]): CollectionConstant[SInt.type] = CollectionConstant[SInt.type](value.toColl, SInt)
def unapply(node: SValue): Option[Coll[Int]] = node match {
case coll: CollectionConstant[SInt.type]@unchecked => coll match {
case CollectionConstant(arr, SInt) => Some(arr)
case _ => None
}
case _ => None
}
}
object LongArrayConstant {
def apply(value: Coll[Long]): CollectionConstant[SLong.type] = CollectionConstant[SLong.type](value, SLong)
def apply(value: Array[Long]): CollectionConstant[SLong.type] = CollectionConstant[SLong.type](value.toColl, SLong)
def unapply(node: SValue): Option[Coll[Long]] = node match {
case coll: CollectionConstant[SLong.type]@unchecked => coll match {
case CollectionConstant(arr, SLong) => Some(arr)
case _ => None
}
case _ => None
}
}
object BigIntArrayConstant {
def apply(value: Coll[BigInt]): CollectionConstant[SBigInt.type] = CollectionConstant[SBigInt.type](value, SBigInt)
def apply(value: Array[BigInt]): CollectionConstant[SBigInt.type] = CollectionConstant[SBigInt.type](value.toColl, SBigInt)
def unapply(node: SValue): Option[Coll[BigInt]] = node match {
case coll: CollectionConstant[SBigInt.type]@unchecked => coll match {
case CollectionConstant(arr, SBigInt) => Some(arr)
case _ => None
}
case _ => None
}
}
object BoolArrayConstant {
val BoolArrayTypeCode = (SCollectionType.CollectionTypeCode + SBoolean.typeCode).toByte
def apply(value: Coll[Boolean]): CollectionConstant[SBoolean.type] = CollectionConstant[SBoolean.type](value, SBoolean)
def apply(value: Array[Boolean]): CollectionConstant[SBoolean.type] = apply(value.toColl)
def unapply(node: SValue): Option[Coll[Boolean]] = node match {
case coll: CollectionConstant[SBoolean.type]@unchecked => coll match {
case CollectionConstant(arr, SBoolean) => Some(arr)
case _ => None
}
case _ => None
}
}
trait NotReadyValueByteArray extends NotReadyValue[SByteArray] {
override def tpe = SByteArray
}
trait NotReadyValueAvlTree extends NotReadyValue[SAvlTree.type] {
override def tpe = SAvlTree
}
/** ErgoTree node that represents the operation of obtaining the generator of elliptic curve group.
* The generator g of the group is an element of the group such that, when written
* multiplicative form, every element of the group is a power of g.
*/
case object GroupGenerator extends EvaluatedValue[SGroupElement.type] with ValueCompanion {
override def opCode: OpCode = OpCodes.GroupGeneratorCode
override val costKind = FixedCost(JitCost(10))
override def tpe = SGroupElement
override val value = SigmaDsl.GroupElement(CryptoConstants.dlogGroup.generator)
override def companion = this
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(costKind)
SigmaDsl.groupGenerator
}
}
trait NotReadyValueGroupElement extends NotReadyValue[SGroupElement.type] {
override def tpe = SGroupElement
}
object BooleanConstant {
def fromBoolean(v: Boolean): BooleanConstant = if (v) TrueLeaf else FalseLeaf
def apply(value: Boolean): BooleanConstant = Constant[SBoolean.type](value, SBoolean)
def unapply(v: SValue): Option[Boolean] = v match {
case Constant(value: Boolean, SBoolean) => Some(value)
case _ => None
}
}
/** ErgoTree node which represents `true` literal. */
object TrueLeaf extends ConstantNode[SBoolean.type](true, SBoolean) with ValueCompanion {
override def companion = this
override def opCode: OpCode = TrueCode
override def costKind: FixedCost = Constant.costKind
override def toString: String = "TrueLeaf"
}
/** ErgoTree node which represents `false` literal. */
object FalseLeaf extends ConstantNode[SBoolean.type](false, SBoolean) with ValueCompanion {
override def companion = this
override def opCode: OpCode = FalseCode
override def costKind: FixedCost = Constant.costKind
override def toString: String = "FalseLeaf"
}
trait NotReadyValueBoolean extends NotReadyValue[SBoolean.type] {
override def tpe = SBoolean
}
trait NotReadyValueBox extends NotReadyValue[SBox.type] {
def tpe = SBox
}
/** ErgoTree node which converts a collection of expressions into a tuple of data values
* of different types. Each data value of the resulting collection is obtained by
* evaluating the corresponding expression in `items`. All items may have different
* types.
*
* @param items source collection of expressions
*/
case class Tuple(items: IndexedSeq[Value[SType]])
extends EvaluatedValue[STuple] // note, this superclass is required as Tuple can be in a register
with EvaluatedCollection[SAny.type, STuple] {
override def companion = Tuple
override lazy val tpe = STuple(items.map(_.tpe))
override def opType: SFunc = ???
override def elementType: SAny.type = SAny
override lazy val value = {
val xs = items.cast[EvaluatedValue[SAny.type]].map(_.value)
implicit val tAny: RType[Any] = sigma.AnyType
Colls.fromArray(xs.toArray)
}
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
// in v5.0 version we support only tuples of 2 elements to be equivalent with v4.x
if (items.length != 2)
syntax.error(s"Invalid tuple $this")
val item0 = items(0)
val x = item0.evalTo[Any](env)
Value.checkType(item0, x)
val item1 = items(1)
val y = item1.evalTo[Any](env)
Value.checkType(item1, y)
val res = (x, y) // special representation for pairs (to pass directly to Coll primitives)
addCost(Tuple.costKind)
res
}
}
object Tuple extends FixedCostValueCompanion {
override def opCode: OpCode = TupleCode
/** Cost of: 1) allocating a new tuple (of limited max size) */
override val costKind = FixedCost(JitCost(15))
def apply(items: Value[SType]*): Tuple = Tuple(items.toIndexedSeq)
}
/** ErgoTree node which converts a collection of expressions into a collection of data
* values. Each data value of the resulting collection is obtained by evaluating the
* corresponding expression in `items`. All items must have the same type.
*
* @param items source collection of expressions
* @param elementType type descriptor of elements in the resulting collection
*/
case class ConcreteCollection[V <: SType](items: Seq[Value[V]], elementType: V)
extends EvaluatedCollection[V, SCollection[V]] {
// TODO uncomment and make sure Ergo works with it, i.e. complex data types are never used for `items`.
// There is nothing wrong in using List, Vector and other fancy types as a concrete representation
// of `items`, but these types have sub-optimal performance (2-3x overhead comparing to WrappedArray)
// which is immediately visible in profile.
// NOTE, the assert below should be commented before production release.
// Is it there for debuging only, basically to catch call stacks where the fancy types may
// occasionally be used.
// assert(
// items.isInstanceOf[mutable.WrappedArray[_]] ||
// items.isInstanceOf[ArrayBuffer[_]] ||
// items.isInstanceOf[mutable.ArraySeq[_]],
// s"Invalid types of items ${items.getClass}")
private val isBooleanConstants = elementType == SBoolean && items.forall(_.isInstanceOf[Constant[_]])
override def companion =
if (isBooleanConstants) ConcreteCollectionBooleanConstant
else ConcreteCollection
val tpe = SCollection[V](elementType)
implicit lazy val tElement: RType[V#WrappedType] = Evaluation.stypeToRType(elementType)
// TODO refactor: this method is not used and can be removed
lazy val value = {
val xs = items.cast[EvaluatedValue[V]].map(_.value)
Colls.fromArray(xs.toArray(tElement.classTag))
}
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val len = items.length
addCost(ConcreteCollection.costKind)
val is = Array.ofDim[V#WrappedType](len)(tElement.classTag)
cfor(0)(_ < len, _ + 1) { i =>
val item = items(i)
val itemV = item.evalTo[V#WrappedType](env)
Value.checkType(item, itemV) // necessary because cast to V#WrappedType is erased
is(i) = itemV
}
Colls.fromArray(is)
}
}
object ConcreteCollection extends FixedCostValueCompanion {
override def opCode: OpCode = ConcreteCollectionCode
/** Cost of: allocating new collection
*
* @see ConcreteCollection_PerItem */
override val costKind = FixedCost(JitCost(20))
def fromSeq[V <: SType](items: Seq[Value[V]])(implicit tV: V): ConcreteCollection[V] =
ConcreteCollection(items, tV)
def fromItems[V <: SType](items: Value[V]*)(implicit tV: V): ConcreteCollection[V] =
ConcreteCollection(items, tV)
}
object ConcreteCollectionBooleanConstant extends ValueCompanion {
override def opCode: OpCode = ConcreteCollectionBooleanConstantCode
override def costKind = ConcreteCollection.costKind
}
trait LazyCollection[V <: SType] extends NotReadyValue[SCollection[V]]
sealed trait BlockItem extends NotReadyValue[SType] {
def id: Int
def rhs: SValue
def isValDef: Boolean
}
object BlockItem {
/** Immutable empty array, can be used to save allocations in many places. */
val EmptyArray = Array.empty[BlockItem]
/** Immutable empty IndexedSeq to save allocations in many places. */
val EmptySeq: IndexedSeq[BlockItem] = EmptyArray
}
/** IR node for let-bound expressions `let x = rhs` which is ValDef, or `let f[T] = rhs` which is FunDef.
* These nodes are used to represent ErgoTrees after common sub-expression elimination.
* This representation is more compact in serialized form.
*
* @param id unique identifier of the variable in the current scope. */
case class ValDef(
override val id: Int,
tpeArgs: Seq[STypeVar],
override val rhs: SValue) extends BlockItem {
require(id >= 0, "id must be >= 0")
override def companion = if (tpeArgs.isEmpty) ValDef else FunDef
override def tpe: SType = rhs.tpe
override def isValDef: Boolean = tpeArgs.isEmpty
/** This is not used as operation, but rather to form a program structure */
override def opType: SFunc = Value.notSupportedError(this, "opType")
}
object ValDef extends ValueCompanion {
override def opCode: OpCode = ValDefCode
override def costKind = Value.notSupportedError(this, "costKind")
def apply(id: Int, rhs: SValue): ValDef = ValDef(id, Nil, rhs)
}
object FunDef extends ValueCompanion {
override def opCode: OpCode = FunDefCode
override def costKind = Value.notSupportedError(this, "costKind")
def unapply(d: BlockItem): Option[(Int, Seq[STypeVar], SValue)] = d match {
case ValDef(id, targs, rhs) if !d.isValDef => Some((id, targs, rhs))
case _ => None
}
}
/** Special node which represents a reference to ValDef it was introduced as result of
* CSE. */
case class ValUse[T <: SType](valId: Int, tpe: T) extends NotReadyValue[T] {
override def companion = ValUse
/** This is not used as operation, but rather to form a program structure */
def opType: SFunc = Value.notSupportedError(this, "opType")
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(ValUse.costKind)
val res = env.getOrElse(valId, syntax.error(s"cannot resolve $this"))
Value.checkType(this, res)
res
}
}
object ValUse extends FixedCostValueCompanion {
override def opCode: OpCode = ValUseCode
/** Cost of: 1) Lookup in immutable HashMap by valId: Int 2) alloc of Some(v) */
override val costKind = FixedCost(JitCost(5))
}
/** The order of ValDefs in the block is used to assign ids to ValUse(id) nodes
* For all i: items(i).id == {number of ValDefs preceded in a graph} with respect to topological order.
* Specific topological order doesn't really matter, what is important is to preserve semantic linkage
* between ValUse(id) and ValDef with the corresponding id.
* This convention allow to valid serializing ids because we always serializing and deserializing
* in a fixed well defined order.
*/
case class BlockValue(
items: IndexedSeq[BlockItem],
result: SValue) extends NotReadyValue[SType] {
override def companion = BlockValue
def tpe: SType = result.tpe
/** This is not used as operation, but rather to form a program structure */
def opType: SFunc = Value.notSupportedError(this, "opType")
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
var curEnv = env
val len = items.length
addSeqCostNoOp(BlockValue.costKind, len)
cfor(0)(_ < len, _ + 1) { i =>
val vd = items(i).asInstanceOf[ValDef]
val v = vd.rhs.evalTo[Any](curEnv)
Value.checkType(vd, v)
E.addFixedCost(FuncValue.AddToEnvironmentDesc_CostKind,
FuncValue.AddToEnvironmentDesc) {
curEnv = curEnv + (vd.id -> v)
}
}
val res = result.evalTo[Any](curEnv)
Value.checkType(result, res)
res
}
}
object BlockValue extends ValueCompanion {
override def opCode: OpCode = BlockValueCode
override val costKind = PerItemCost(
baseCost = JitCost(1), perChunkCost = JitCost(1), chunkSize = 10)
}
/**
* @param args parameters list, where each parameter has an id and a type.
* @param body expression, which refers function parameters with ValUse.
*/
case class FuncValue(
args: IndexedSeq[(Int, SType)],
body: Value[SType]) extends NotReadyValue[SFunc] {
import FuncValue._
override def companion = FuncValue
lazy val tpe: SFunc = {
val nArgs = args.length
val argTypes = new Array[SType](nArgs)
cfor(0)(_ < nArgs, _ + 1) { i =>
argTypes(i) = args(i)._2
}
SFunc(argTypes, body.tpe)
}
/** This is not used as operation, but rather to form a program structure */
override def opType: SFunc = SFunc(ArraySeq.empty, tpe)
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(FuncValue.costKind)
if (args.length == 1) {
val arg0 = args(0)
(vArg: Any) => {
Value.checkType(arg0._2, vArg)
var env1: DataEnv = null
E.addFixedCost(AddToEnvironmentDesc_CostKind, AddToEnvironmentDesc) {
env1 = env + (arg0._1 -> vArg)
}
val res = body.evalTo[Any](env1)
Value.checkType(body, res)
res
}
} else {
syntax.error(s"Function must have 1 argument, but was: $this")
}
}
}
object FuncValue extends FixedCostValueCompanion {
val AddToEnvironmentDesc = NamedDesc("AddToEnvironment")
/** Cost of: adding value to evaluator environment */
val AddToEnvironmentDesc_CostKind = FixedCost(JitCost(5))
override def opCode: OpCode = FuncValueCode
/** Cost of: 1) switch on the number of args 2) allocating a new Scala closure
* Old cost: ("Lambda", "() => (D1) => R", lambdaCost), */
override val costKind = FixedCost(JitCost(5))
def apply(argId: Int, tArg: SType, body: SValue): FuncValue =
FuncValue(IndexedSeq((argId, tArg)), body)
}
/** Frontend representation of a block of Val definitions.
* { val x = ...; val y = ... }
* This node is not part of ErgoTree and hence have Undefined opCode. */
case class Block(bindings: Seq[Val], result: SValue) extends Value[SType] {
override def companion = Block
override def tpe: SType = result.tpe
/** This is not used as operation, but rather to form a program structure */
override def opType: SFunc = Value.notSupportedError(this, "opType")
}
object Block extends ValueCompanion {
override def opCode: OpCode = OpCodes.Undefined
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
/** Create a Block node with a single Val definition. */
def apply(let: Val, result: SValue)(implicit @unused o1: Overloaded1): Block =
Block(Seq(let), result)
}
/** IR node to represent explicit Zero Knowledge scope in ErgoTree.
* Compiler checks Zero Knowledge properties and issue error message is case of violations.
* ZK-scoping is optional, it can be used when the user want to ensure Zero Knowledge of
* specific set of operations.
* Usually it will require simple restructuring of the code to make the scope body explicit.
* Invariants checked by the compiler:
* - single ZKProof in ErgoTree in a root position
* - no boolean operations in the body, because otherwise the result may be disclosed
* - all the operations are over SigmaProp values
*
* For motivation and details see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/236
* */
case class ZKProofBlock(body: SigmaPropValue) extends BoolValue {
override def companion = ZKProofBlock
override def tpe = SBoolean
override def opType: SFunc = ZKProofBlock.OpType
}
object ZKProofBlock extends ValueCompanion {
override def opCode: OpCode = OpCodes.Undefined
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
val OpType = SFunc(SSigmaProp, SBoolean)
}
trait Val extends Value[SType] {
val name : String
val givenType: SType
val body : SValue
}
object Val {
def apply(name: String, body: SValue): Val = ValNode(name, NoType, body)
def apply(
name: String,
givenType: SType,
body: SValue): Val = ValNode(name, givenType, body)
def unapply(v: SValue): Option[(String, SType, SValue)] = v match {
case ValNode(name, givenType, body) => Some((name, givenType, body))
case _ => None
}
}
case class ValNode(
name: String,
givenType: SType,
body: SValue) extends Val {
override def companion = ValNode
override def tpe: SType = givenType ?: body.tpe
/** This is not used as operation, but rather to form a program structure */
override def opType: SFunc = Value.notSupportedError(this, "opType")
}
object ValNode extends ValueCompanion {
override def opCode: OpCode = OpCodes.Undefined
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
}
/** Frontend node to select a field from an object. Should be transformed to SelectField */
case class Select(
obj: Value[SType],
field: String,
resType: Option[SType] = None) extends Value[SType] {
override def companion = Select
override val tpe: SType = resType.getOrElse(obj.tpe match {
case p: SProduct =>
MethodsContainer.getMethod(p, field) match {
case Some(m) => m.stype
case None => NoType
}
case _ => NoType
})
override def opType: SFunc = SFunc(obj.tpe, tpe)
}
object Select extends ValueCompanion {
override def opCode: OpCode = OpCodes.Undefined
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
}
/** Frontend node to represent variable names parsed in a source code.
* Should be resolved during compilation to lambda argument, Val definition or
* compilation environment value. */
case class Ident(name: String, tpe: SType = NoType) extends Value[SType] {
override def companion = Ident
override def opType: SFunc = SFunc(ArraySeq.empty, tpe)
}
object Ident extends ValueCompanion {
override def opCode: OpCode = OpCodes.Undefined
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
def apply(name: String): Ident = Ident(name, NoType)
}
// TODO refactor: move to sigma.ast
/** ErgoTree node which represents application of function `func` to the given arguments.
*
* @param func expression which evaluates to a function
* @param args arguments of the function application
*/
case class Apply(
func: Value[SType],
args: IndexedSeq[Value[SType]]) extends Value[SType] {
override def companion = Apply
override lazy val tpe : SType = func.tpe match {
case SFunc(_, r, _) => r
case tColl: SCollectionType[_] => tColl.elemType
case _ => NoType
}
override lazy val opType: SFunc = {
val nArgs = args.length
val argTypes = new Array[SType](nArgs + 1)
argTypes(0) = func.tpe
cfor(0)(_ < nArgs, _ + 1) { i =>
argTypes(i + 1) = args(i).tpe
}
SFunc(argTypes, tpe)
}
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(Apply.costKind)
if (args.length == 1) {
val fV = func.evalTo[Any => Any](env)
val argV = args(0).evalTo[Any](env)
fV(argV)
} else {
// zero or more than 1 argument functions are not supported in v4.x, v5.0
// see `case Terms.Apply(f, Seq(x))` in RuntimeCosting which means other cases are not supported.
syntax.error(s"Function application must have 1 argument, but was: $this")
}
}
}
object Apply extends FixedCostValueCompanion {
override def opCode: OpCode = OpCodes.FuncApplyCode
/** Cost of: 1) switch on the number of args 2) Scala method call 3) add args to env
* Old cost: lambdaInvoke == 30 */
override val costKind = FixedCost(JitCost(30))
}
/** Apply types for type parameters of input value. */
case class ApplyTypes(
input: Value[SType],
tpeArgs: Seq[SType]) extends Value[SType] { node =>
override def companion = ApplyTypes
override lazy val tpe: SType = input.tpe match {
case funcType: SFunc =>
val subst = funcType.tpeParams.map(_.ident).zip(tpeArgs).toMap
applySubst(input.tpe, subst)
case _ => input.tpe
}
/** This is not used as operation, but rather to form a program structure */
override def opType: SFunc = Value.notSupportedError(this, "opType")
}
object ApplyTypes extends ValueCompanion {
override def opCode: OpCode = OpCodes.Undefined
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
}
/** Frontend node to represent potential method call in a source code.
* Should be resolved during compilation to MethodCall.
* Cannot be serialized to ErgoTree. */
case class MethodCallLike(
obj: Value[SType],
name: String,
args: IndexedSeq[Value[SType]],
tpe: SType = NoType) extends Value[SType] {
override def companion = MethodCallLike
override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe)
}
object MethodCallLike extends ValueCompanion {
override def opCode: OpCode = OpCodes.Undefined
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
}
/** Represents in ErgoTree an invocation of method of the object `obj` with arguments `args`.
* The SMethod instances in STypeCompanions may have type STypeIdent in methods types,
* but valid ErgoTree should have SMethod instances specialized for specific types of
* obj and args using `specializeFor`.
* This means, if we save typeId, methodId, and we save all the arguments,
* we can restore the specialized SMethod instance.
* This work by induction, if we assume all arguments are monomorphic,
* then we can make MethodCall monomorphic.
* Thus, all ErgoTree instances are monomorphic by construction.
*
* @param obj object on which method will be invoked
* @param method method to be invoked
* @param args arguments passed to the method on invocation
* @param typeSubst a map of concrete type for each generic type parameter
*/
case class MethodCall(
obj: Value[SType],
method: SMethod,
args: IndexedSeq[Value[SType]],
typeSubst: Map[STypeVar, SType]) extends Value[SType] {
override def companion = if (args.isEmpty) PropertyCall else MethodCall
override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe)
override val tpe: SType = method.stype match {
case f: SFunc => f.tRange.withSubstTypes(typeSubst)
case t => t.withSubstTypes(typeSubst)
}
/** @hotspot don't beautify this code */
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val objV = obj.evalTo[Any](env)
addCost(MethodCall.costKind) // MethodCall overhead
method.costKind match {
case fixed: FixedCost =>
val extra = method.extraDescriptors
val extraLen = extra.length
val len = args.length
val argsBuf = new Array[Any](len + extraLen)
cfor(0)(_ < len, _ + 1) { i =>
argsBuf(i) = args(i).evalTo[Any](env)
}
cfor(0)(_ < extraLen, _ + 1) { i =>
argsBuf(len + i) = extra(i)
}
var res: Any = null
E.addFixedCost(fixed, method.opDesc) {
res = method.invokeFixed(objV, argsBuf)
}
res
case _ =>
val len = args.length
val argsBuf = new Array[Any](len + 3)
argsBuf(0) = this
argsBuf(1) = objV
cfor(0)(_ < len, _ + 1) { i =>
argsBuf(i + 2) = args(i).evalTo[Any](env)
}
argsBuf(argsBuf.length - 1) = E
val evalMethod = method.genericMethod.evalMethod
evalMethod.invoke(method.objType, argsBuf.asInstanceOf[Array[AnyRef]]: _*)
}
}
}
object MethodCall extends FixedCostValueCompanion {
override def opCode: OpCode = OpCodes.MethodCallCode
/** Cost of: 1) packing args into Array 2) RMethod.invoke */
override val costKind = FixedCost(JitCost(4))
/** Helper constructor which allows to cast the resulting node to the specified
* [[sigma.ast.Value]] type `T`.
*
* @see [[sigmastate.lang.Terms.MethodCall]]
*/
def typed[T <: SValue](
obj: Value[SType],
method: SMethod,
args: IndexedSeq[Value[SType]],
typeSubst: Map[STypeVar, SType]): T = {
MethodCall(obj, method, args, typeSubst).asInstanceOf[T]
}
}
object PropertyCall extends FixedCostValueCompanion {
override def opCode: OpCode = OpCodes.PropertyCallCode
/** Cost of: 1) packing args into Array 2) RMethod.invoke */
override val costKind = FixedCost(JitCost(4))
}
/** Frontend implementation of lambdas. Should be transformed to FuncValue. */
case class Lambda(
tpeParams: Seq[STypeParam],
args: IndexedSeq[(String, SType)],
givenResType: SType,
body: Option[Value[SType]]) extends Value[SFunc] {
require(!(tpeParams.nonEmpty && body.nonEmpty), s"Generic function definitions are not supported, but found $this")
override def companion = Lambda
override lazy val tpe: SFunc = {
val sRange = givenResType ?: body.fold(NoType: SType)(_.tpe)
SFunc(args.map(_._2), sRange, tpeParams)
}
/** This is not used as operation, but rather to form a program structure */
override def opType: SFunc = SFunc(Vector(), tpe)
}
object Lambda extends ValueCompanion {
override def opCode: OpCode = OpCodes.Undefined
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
def apply(
args: IndexedSeq[(String, SType)],
resTpe: SType,
body: Value[SType]): Lambda =
Lambda(Nil, args, resTpe, Some(body))
def apply(
args: IndexedSeq[(String, SType)],
resTpe: SType,
body: Option[Value[SType]]): Lambda =
Lambda(Nil, args, resTpe, body)
def apply(
args: IndexedSeq[(String, SType)],
body: Value[SType]): Lambda = Lambda(Nil, args, NoType, Some(body))
}
/** When interpreted evaluates to a ByteArrayConstant built from Context.minerPubkey */
case object MinerPubkey extends NotReadyValueByteArray with ValueCompanion {
override def opCode: OpCode = OpCodes.MinerPubkeyCode
/** Cost of calling Context.minerPubkey Scala method. */
override val costKind = FixedCost(JitCost(20))
override val opType = SFunc(SContext, SCollection.SByteArray)
override def companion = this
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(this.costKind)
E.context.minerPubKey
}
}
/** When interpreted evaluates to a IntConstant built from Context.currentHeight */
case object Height extends NotReadyValueInt with FixedCostValueCompanion {
override def companion = this
override def opCode: OpCode = OpCodes.HeightCode
/** Cost of: 1) Calling Context.HEIGHT Scala method. */
override val costKind = FixedCost(JitCost(26))
override val opType = SFunc(SContext, SInt)
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(this.costKind)
E.context.HEIGHT
}
}
/** When interpreted evaluates to a collection of BoxConstant built from Context.boxesToSpend */
case object Inputs extends LazyCollection[SBox.type] with FixedCostValueCompanion {
override def companion = this
override def opCode: OpCode = OpCodes.InputsCode
/** Cost of: 1) Calling Context.INPUTS Scala method. */
override val costKind = FixedCost(JitCost(10))
override def tpe = SCollection.SBoxArray
override val opType = SFunc(SContext, tpe)
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(this.costKind)
E.context.INPUTS
}
}
/** When interpreted evaluates to a collection of BoxConstant built from Context.spendingTransaction.outputs */
case object Outputs extends LazyCollection[SBox.type] with FixedCostValueCompanion {
override def companion = this
override def opCode: OpCode = OpCodes.OutputsCode
/** Cost of: 1) Calling Context.OUTPUTS Scala method. */
override val costKind = FixedCost(JitCost(10))
override def tpe = SCollection.SBoxArray
override val opType = SFunc(SContext, tpe)
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(this.costKind)
E.context.OUTPUTS
}
}
/** When interpreted evaluates to a AvlTreeConstant built from Context.lastBlockUtxoRoot */
case object LastBlockUtxoRootHash extends NotReadyValueAvlTree with ValueCompanion {
override def companion = this
override def opCode: OpCode = OpCodes.LastBlockUtxoRootHashCode
/** Cost of: 1) Calling Context.LastBlockUtxoRootHash Scala method. */
override val costKind = FixedCost(JitCost(15))
override val opType = SFunc(SContext, tpe)
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(this.costKind)
E.context.LastBlockUtxoRootHash
}
}
/** When interpreted evaluates to a BoxConstant built from context.boxesToSpend(context.selfIndex) */
case object Self extends NotReadyValueBox with FixedCostValueCompanion {
override def companion = this
override def opCode: OpCode = OpCodes.SelfCode
/** Cost of: 1) Calling Context.SELF Scala method. */
override val costKind = FixedCost(JitCost(10))
override val opType = SFunc(SContext, SBox)
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(this.costKind)
E.context.SELF
}
}
/** When interpreted evaluates to the singleton instance of [[sigma.Context]].
* Corresponds to `CONTEXT` variable in ErgoScript which can be used like `CONTEXT.headers`.
*/
case object Context extends NotReadyValue[SContext.type] with ValueCompanion {
override def companion = this
override def opCode: OpCode = OpCodes.ContextCode
/** Cost of: 1) accessing global Context instance. */
override val costKind = FixedCost(JitCost(1))
override def tpe: SContext.type = SContext
override val opType: SFunc = SFunc(SUnit, SContext)
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(this.costKind)
E.context
}
}
/** When interpreted evaluates to the singleton instance of [[sigma.SigmaDslBuilder]].
* Corresponds to `Global` variable in ErgoScript which can be used like `Global.groupGenerator`.
*/
case object Global extends NotReadyValue[SGlobal.type] with FixedCostValueCompanion {
override def companion = this
override def opCode: OpCode = OpCodes.GlobalCode
/** Cost of: 1) accessing Global instance. */
override val costKind = FixedCost(JitCost(5))
override def tpe: SGlobal.type = SGlobal
override val opType: SFunc = SFunc(SUnit, SGlobal)
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
addCost(this.costKind)
CSigmaDslBuilder
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy