sigma.ast.trees.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, Map => DMap}
import scorex.crypto.hash.{Blake2b256, CryptographicHash32, Sha256}
import sigma.ast.ArithOp.OperationImpl
import sigma.ast.Operations._
import sigma.ast.SCollection.{SByteArray, SIntArray}
import sigma.ast.SOption.SIntOption
import sigma.ast.syntax._
import sigma.data.ExactIntegral._
import sigma.data.ExactOrdering._
import sigma.data.OverloadHack.Overloaded1
import sigma.data._
import sigma.serialization.CoreByteWriter.ArgInfo
import sigma.validation.SigmaValidationSettings
import sigma.{Coll, Colls, GroupElement, SigmaProp, VersionContext}
import NumericOps.{BigIntIsExactIntegral, BigIntIsExactOrdering}
import sigma.eval.ErgoTreeEvaluator.DataEnv
import sigma.eval.Extensions.EvalCollOps
import sigma.eval.{ErgoTreeEvaluator, SigmaDsl}
import sigma.serialization.OpCodes._
import sigma.serialization.ValueCodes.OpCode
import sigma.serialization._
import scala.collection.mutable
/** Embedding of Boolean values to SigmaProp values. As an example, this operation allows boolean experesions
* to be used as arguments of `atLeast(..., sigmaProp(boolExpr), ...)` operation.
* During execution results to either `TrueProp` or `FalseProp` values of SigmaProp type.
*/
case class BoolToSigmaProp(value: BoolValue) extends SigmaPropValue {
override def companion = BoolToSigmaProp
override def tpe = SSigmaProp
override def opType = BoolToSigmaProp.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val v = value.evalTo[Any](env)
addCost(BoolToSigmaProp.costKind)
if (VersionContext.current.isJitActivated) {
CSigmaProp(v.asInstanceOf[Boolean])
} else {
// before v5.0 is activated we follow the old v4.x semantics to handle cases
// when the value is not a boolean. There are historical transactions with such
// erroneous scripts. See property("BoolToSigmaProp with SigmaProp argument should be deserializable")
v match {
case sp: SigmaProp => sp
case _ =>
CSigmaProp(v.asInstanceOf[Boolean])
}
}
}
}
object BoolToSigmaProp extends FixedCostValueCompanion {
override def opCode: OpCode = OpCodes.BoolToSigmaPropCode
override val costKind = FixedCost(JitCost(15))
val OpType = SFunc(SBoolean, SSigmaProp)
}
/** ErgoTree operation to create a new SigmaProp value representing public key
* of discrete logarithm signature protocol. */
case class CreateProveDlog(value: Value[SGroupElement.type]) extends SigmaPropValue {
override def companion = CreateProveDlog
override def tpe = SSigmaProp
override def opType = CreateProveDlog.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val v = value.evalTo[GroupElement](env)
addCost(CreateProveDlog.costKind)
CSigmaProp.withProveDlog(v)
}
}
object CreateProveDlog extends FixedCostValueCompanion {
override def opCode: OpCode = OpCodes.ProveDlogCode
override val costKind = FixedCost(JitCost(10))
val OpType = SFunc(SGroupElement, SSigmaProp)
}
// TODO v6.0: implement `eval` method and add support in GraphBuilding (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/907)
/** Construct a new authenticated dictionary with given parameters and tree root digest.*/
case class CreateAvlTree(operationFlags: ByteValue,
digest: Value[SByteArray],
keyLength: IntValue,
valueLengthOpt: Value[SIntOption]) extends AvlTreeValue {
override def companion = CreateAvlTree
override def tpe = SAvlTree
override def opType = CreateAvlTree.OpType
}
object CreateAvlTree extends ValueCompanion {
override def opCode: OpCode = OpCodes.AvlTreeCode
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
val OpType = SFunc(Array(SByte, SByteArray, SInt, SIntOption), SAvlTree)
}
/** ErgoTree operation to create a new SigmaProp value representing public key
* of Diffie Hellman signature protocol.
* Common input: (g,h,u,v)*/
case class CreateProveDHTuple(gv: Value[SGroupElement.type],
hv: Value[SGroupElement.type],
uv: Value[SGroupElement.type],
vv: Value[SGroupElement.type]) extends SigmaPropValue {
override def companion = CreateProveDHTuple
override def tpe = SSigmaProp
override def opType = SFunc(IndexedSeq(SGroupElement, SGroupElement, SGroupElement, SGroupElement), SSigmaProp)
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val g = gv.evalTo[GroupElement](env)
val h = hv.evalTo[GroupElement](env)
val u = uv.evalTo[GroupElement](env)
val v = vv.evalTo[GroupElement](env)
addCost(CreateProveDHTuple.costKind)
CSigmaProp.withProveDHTuple(g, h, u, v)
}
}
object CreateProveDHTuple extends FixedCostValueCompanion {
override def opCode: OpCode = OpCodes.ProveDiffieHellmanTupleCode
override val costKind = FixedCost(JitCost(20))
}
trait SigmaTransformer[IV <: SigmaPropValue, OV <: SigmaPropValue] extends SigmaPropValue {
val items: Seq[IV]
}
trait SigmaTransformerCompanion extends ValueCompanion {
def argInfos: Seq[ArgInfo]
}
/**
* AND conjunction for sigma propositions
*/
case class SigmaAnd(items: Seq[SigmaPropValue]) extends SigmaTransformer[SigmaPropValue, SigmaPropValue] {
override def companion = SigmaAnd
override def tpe = SSigmaProp
override def opType = SigmaAnd.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val len = items.length
val is = new Array[SigmaProp](len)
cfor(0)(_ < len, _ + 1) { i =>
is(i) = items(i).evalTo[SigmaProp](env)
}
addSeqCost(SigmaAnd.costKind, len) { () =>
SigmaDsl.allZK(Colls.fromArray(is))
}
}
}
object SigmaAnd extends SigmaTransformerCompanion {
val OpType = SFunc(SCollection.SSigmaPropArray, SSigmaProp)
override def opCode: OpCode = OpCodes.SigmaAndCode
/** BaseCost:
* - constructing new CSigmaProp and allocation collection
* - one iteration over collection of items
*/
override val costKind = PerItemCost(
baseCost = JitCost(10), perChunkCost = JitCost(2), chunkSize = 1)
override def argInfos: Seq[ArgInfo] = SigmaAndInfo.argInfos
def apply(first: SigmaPropValue, second: SigmaPropValue, tail: SigmaPropValue*): SigmaAnd = SigmaAnd(Array(first, second) ++ tail)
}
/**
* OR disjunction for sigma propositions
*/
case class SigmaOr(items: Seq[SigmaPropValue]) extends SigmaTransformer[SigmaPropValue, SigmaPropValue] {
override def companion = SigmaOr
override def tpe = SSigmaProp
override def opType = SigmaOr.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val len = items.length
val is = new Array[SigmaProp](len)
cfor(0)(_ < len, _ + 1) { i =>
is(i) = items(i).evalTo[SigmaProp](env)
}
addSeqCost(SigmaOr.costKind, len) { () =>
SigmaDsl.anyZK(Colls.fromArray(is))
}
}
}
object SigmaOr extends SigmaTransformerCompanion {
val OpType = SFunc(SCollection.SSigmaPropArray, SSigmaProp)
override def opCode: OpCode = OpCodes.SigmaOrCode
/** BaseCost:
* - constructing new CSigmaProp and allocation collection
* - one iteration over collection of items */
override val costKind = PerItemCost(
baseCost = JitCost(10), perChunkCost = JitCost(2), chunkSize = 1)
override def argInfos: Seq[ArgInfo] = SigmaOrInfo.argInfos
def apply(head: SigmaPropValue, tail: SigmaPropValue*): SigmaOr = SigmaOr(head +: tail)
}
/** Base trait for companions of OR, AND and XorOf nodes. */
trait LogicalTransformerCompanion extends ValueCompanion {
def argInfos: Seq[ArgInfo]
val OpType: SFunc = SFunc(SCollection.SBooleanArray, SBoolean)
}
/**
* OR logical conjunction
*/
case class OR(input: Value[SCollection[SBoolean.type]])
extends Transformer[SCollection[SBoolean.type], SBoolean.type] with NotReadyValueBoolean {
override def companion = OR
override def opType = OR.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Coll[Boolean]](env)
var res = false
val len = inputV.length
var i = 0
E.addSeqCost(OR.costKind, this.companion.opDesc) { () =>
// this loop is bounded since ErgoTree is bounded by MaxBoxSize
while (i < len && !res) {
res ||= inputV(i)
i += 1
}
i // return actual number of processed items
}
res
}
}
object OR extends LogicalTransformerCompanion {
override def opCode: OpCode = OrCode
/** Base cost: operations factored out of reduction loop.
* Per-chunk cost: cost of scala `||` operations amortized over a chunk of boolean values.
* @see BinOr
* @see AND */
override val costKind = PerItemCost(
baseCost = JitCost(5), perChunkCost = JitCost(5), chunkSize = 64/*size of cache line in bytes*/)
override def argInfos: Seq[ArgInfo] = Operations.ORInfo.argInfos
def apply(children: Seq[Value[SBoolean.type]]): OR =
OR(ConcreteCollection.fromSeq(children))
def apply(items: Value[SBoolean.type]*)(implicit o: Overloaded1): OR = apply(items)
}
/** Similar to allOf, but performing logical XOR operation instead of `&&`
*/
case class XorOf(input: Value[SCollection[SBoolean.type]])
extends Transformer[SCollection[SBoolean.type], SBoolean.type] with NotReadyValueBoolean {
override def companion = XorOf
override def opType = XorOf.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Coll[Boolean]](env)
val len = inputV.length
addSeqCost(XorOf.costKind, len) { () =>
SigmaDsl.xorOf(inputV)
}
}
}
object XorOf extends LogicalTransformerCompanion {
override def opCode: OpCode = XorOfCode
/** Base cost: operations factored out of reduction loop.
* Per-chunk cost: cost of scala `||` operations amortized over a chunk of boolean values.
* @see BinOr
* @see AND */
override val costKind = PerItemCost(
baseCost = JitCost(20), perChunkCost = JitCost(5), chunkSize = 32)
override def argInfos: Seq[ArgInfo] = Operations.XorOfInfo.argInfos
def apply(children: Seq[Value[SBoolean.type]]): XorOf =
XorOf(ConcreteCollection.fromSeq(children))
}
/**
* AND logical conjunction
*/
case class AND(input: Value[SCollection[SBoolean.type]])
extends Transformer[SCollection[SBoolean.type], SBoolean.type]
with NotReadyValueBoolean {
override def companion = AND
override def opType = AND.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Coll[Boolean]](env)
var res = true
val len = inputV.length
var i = 0
E.addSeqCost(AND.costKind, this.companion.opDesc) { () =>
// this loop is bounded since ErgoTree is bounded by MaxBoxSize
while (i < len && res) {
res &&= inputV(i)
i += 1
}
i // return actual number of processed items
}
res
}
}
object AND extends LogicalTransformerCompanion {
override def opCode: OpCode = AndCode
/** Base cost: operations factored out of reduction loop.
* Per-chunk cost: cost of scala `&&` operations amortized over a chunk of boolean values.
* @see BinAnd
* @see OR */
override val costKind = PerItemCost(
baseCost = JitCost(10), perChunkCost = JitCost(5), chunkSize = 32/* half size of cache line in bytes */)
override def argInfos: Seq[ArgInfo] = Operations.ANDInfo.argInfos
def apply(children: Seq[Value[SBoolean.type]]): AND =
AND(ConcreteCollection.fromSeq(children))
// def apply(head: Value[SBoolean.type], tail: Value[SBoolean.type]*): AND = apply(head +: tail)
def apply(items: Value[SBoolean.type]*)(implicit o1: Overloaded1): AND = apply(items)
}
/**
* Logical threshold.
* AtLeast has two inputs: integer bound and children same as in AND/OR.
* The result is true if at least bound children are true.
*/
case class AtLeast(bound: Value[SInt.type], input: Value[SCollection[SSigmaProp.type]])
extends Transformer[SCollection[SSigmaProp.type], SSigmaProp.type]
with NotReadyValue[SSigmaProp.type] {
override def companion = AtLeast
override def tpe: SSigmaProp.type = SSigmaProp
override def opType: SFunc = AtLeast.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val b = bound.evalTo[Int](env)
val props = input.evalTo[Coll[SigmaProp]](env)
addSeqCost(AtLeast.costKind, props.length) { () =>
SigmaDsl.atLeast(b, props)
}
}
}
object AtLeast extends ValueCompanion {
override def opCode: OpCode = AtLeastCode
/** Base cost: constructing new CSigmaProp value
* Per chunk cost: obtaining SigmaBooleans for each chunk in AtLeast
*/
override val costKind = PerItemCost(
baseCost = JitCost(20), perChunkCost = JitCost(3), chunkSize = 5)
val OpType: SFunc = SFunc(Array(SInt, SCollection.SBooleanArray), SBoolean)
val MaxChildrenCount: Int = SigmaConstants.MaxChildrenCountForAtLeastOp.value
def apply(bound: Value[SInt.type], children: Seq[SigmaPropValue]): AtLeast =
AtLeast(bound, ConcreteCollection.fromSeq(children))
def apply(bound: Value[SInt.type], head: SigmaPropValue, tail: SigmaPropValue*): AtLeast =
apply(bound, head +: tail)
/** HOTSPOT: don't beautify this code */
def reduce(bound: Int, children: Seq[SigmaBoolean]): SigmaBoolean = {
import sigma.data.TrivialProp._
if (bound <= 0) return TrueProp
val nChildren = children.length
if (bound > nChildren) return FalseProp
var curBound = bound
var childrenLeft = nChildren
// invariant due to the two if statements above: 0=255) return FalseLeaf
var iChild = 0
while (iChild < nChildren) {
if (curBound == 1) {
sigmas ++= children.slice(iChild, nChildren)
return COR.normalized(sigmas.result())
}
// If at any point bound == number of children, convert to AND.
if (curBound == childrenLeft) {
sigmas ++= children.slice(iChild, nChildren)
return CAND.normalized(sigmas.result())
}
// at this point 1 // If child is true, remove child and reduce bound.
childrenLeft -= 1
curBound -= 1
case FalseProp => // If child is false, remove child, leave bound unchanged.
childrenLeft -= 1
case sigma => sigmas += sigma
}
// at this point 1<=curBound<=childrenLeft
iChild += 1
}
val ch = sigmas.result()
if (curBound == 1) return COR.normalized(ch)
if (curBound == childrenLeft) return CAND.normalized(ch)
CTHRESHOLD(curBound, ch)
}
}
/**
* Up cast for Numeric types
*/
case class Upcast[T <: SNumericType, R <: SNumericType](input: Value[T], tpe: R)
extends Transformer[T, R] {
require(input.tpe.isInstanceOf[SNumericType], s"Cannot create Upcast node for non-numeric type ${input.tpe}")
override def companion = Upcast
override def opType = Upcast.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[AnyVal](env)
addCost(Upcast.costKind, tpe) { () =>
tpe.upcast(inputV)
}
}
}
/** Base class for Upcast and Downcast companion objects. */
trait NumericCastCompanion extends ValueCompanion {
def argInfos: Seq[ArgInfo]
val OpType = SFunc(Array(SType.tT), SType.tR)
/** Returns cost descriptor of this operation. */
def costKind: TypeBasedCost = NumericCastCostKind
}
/** Cost of:
* 1) converting numeric value to the numeric value of the given type, i.e. Byte -> Int
* NOTE: the cost of BigInt casting is the same in JITC (comparing to AOTC) to simplify
* implementation.
*/
object NumericCastCostKind extends TypeBasedCost {
override def costFunc(targetTpe: SType): JitCost = targetTpe match {
case SBigInt => JitCost(30)
case _ => JitCost(10)
}
}
object Upcast extends NumericCastCompanion {
override def opCode: OpCode = OpCodes.UpcastCode
override def argInfos: Seq[ArgInfo] = UpcastInfo.argInfos
def tT = SType.tT
def tR = SType.tR
val BigIntOpType = SFunc(tT, SBigInt) // TODO check usage
}
/**
* Down cast for Numeric types
*/
case class Downcast[T <: SNumericType, R <: SNumericType](input: Value[T], tpe: R)
extends Transformer[T, R] {
require(input.tpe.isInstanceOf[SNumericType], s"Cannot create Downcast node for non-numeric type ${input.tpe}")
override def companion = Downcast
override def opType = Downcast.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[AnyVal](env)
addCost(Downcast.costKind, tpe) { () =>
tpe.downcast(inputV)
}
}
}
object Downcast extends NumericCastCompanion {
override def opCode: OpCode = OpCodes.DowncastCode
override def argInfos: Seq[ArgInfo] = DowncastInfo.argInfos
def tT = SType.tT
def tR = SType.tR
val BigIntOpType = SFunc(SBigInt, tR) // TODO check usage
}
/**
* Convert SLong to SByteArray
*/
case class LongToByteArray(input: Value[SLong.type])
extends Transformer[SLong.type, SByteArray] with NotReadyValueByteArray {
override def companion = LongToByteArray
override def opType = LongToByteArray.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Long](env)
addCost(LongToByteArray.costKind)
SigmaDsl.longToByteArray(inputV)
}
}
object LongToByteArray extends SimpleTransformerCompanion {
val OpType = SFunc(SLong, SByteArray)
override def opCode: OpCode = OpCodes.LongToByteArrayCode
override val costKind = FixedCost(JitCost(17))
override def argInfos: Seq[ArgInfo] = LongToByteArrayInfo.argInfos
}
/**
* Convert SByteArray to SLong
*/
case class ByteArrayToLong(input: Value[SByteArray])
extends Transformer[SByteArray, SLong.type] with NotReadyValueLong {
override def companion = ByteArrayToLong
override def opType = ByteArrayToLong.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Coll[Byte]](env)
addCost(ByteArrayToLong.costKind)
SigmaDsl.byteArrayToLong(inputV)
}
}
object ByteArrayToLong extends SimpleTransformerCompanion {
val OpType = SFunc(SByteArray, SLong)
override def opCode: OpCode = OpCodes.ByteArrayToLongCode
override val costKind = FixedCost(JitCost(16))
override def argInfos: Seq[ArgInfo] = ByteArrayToLongInfo.argInfos
}
/**
* Convert SByteArray to SBigInt
*/
case class ByteArrayToBigInt(input: Value[SByteArray])
extends Transformer[SByteArray, SBigInt.type] with NotReadyValueBigInt {
override def companion = ByteArrayToBigInt
override val opType = ByteArrayToBigInt.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Coll[Byte]](env)
addCost(ByteArrayToBigInt.costKind)
SigmaDsl.byteArrayToBigInt(inputV)
}
}
object ByteArrayToBigInt extends SimpleTransformerCompanion {
val OpType = SFunc(SByteArray, SBigInt)
override def opCode: OpCode = OpCodes.ByteArrayToBigIntCode
override val costKind = FixedCost(JitCost(30))
override def argInfos: Seq[ArgInfo] = ByteArrayToBigIntInfo.argInfos
}
/**
* Convert SByteArray to SGroupElement using CryptoConstants.dlogGroup.curve.decodePoint(bytes)
*/
case class DecodePoint(input: Value[SByteArray])
extends Transformer[SByteArray, SGroupElement.type] with NotReadyValueGroupElement {
override def companion = DecodePoint
override def opType = DecodePoint.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Coll[Byte]](env)
addCost(DecodePoint.costKind)
SigmaDsl.decodePoint(inputV)
}
}
object DecodePoint extends SimpleTransformerCompanion with FixedCostValueCompanion {
val OpType = SFunc(SByteArray, SGroupElement)
override def opCode: OpCode = OpCodes.DecodePointCode
/** Cost of:
* 1) create reader and read bytes in a new array
* 2) calling curve.decodePoint and obtain EcPoint
* 3) wrap EcPoint in GroupElement*/
override val costKind = FixedCost(JitCost(300))
override def argInfos: Seq[ArgInfo] = DecodePointInfo.argInfos
}
sealed abstract class CalcHash extends Transformer[SByteArray, SByteArray] with NotReadyValueByteArray {
def hashFn: CryptographicHash32
override def opType = CalcHash.OpType
}
object CalcHash {
val OpType = SFunc(SByteArray, SByteArray)
}
/**
* Calculate Blake2b hash from `input`
*/
case class CalcBlake2b256(override val input: Value[SByteArray]) extends CalcHash {
override def companion = CalcBlake2b256
override val hashFn: CryptographicHash32 = Blake2b256
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Coll[Byte]](env)
addSeqCost(CalcBlake2b256.costKind, inputV.length) { () =>
SigmaDsl.blake2b256(inputV)
}
}
}
object CalcBlake2b256 extends SimpleTransformerCompanion {
override def opCode: OpCode = OpCodes.CalcBlake2b256Code
/** Cost of: of hashing 1 block of data.
*
* This cost is used as a baseline to connect cost units with absolute time.
* The block validation have 1000000 of cost units budget, and we want this to
* correspond to 1 second. Thus we can assume 1 cost unit == 1 micro-second.
*
* It takes approximately 1 micro-seconds on average to compute hash of 128 bytes
* block on MacBook Pro (16-inch, 2019) 2.3 GHz 8-Core Intel Core i9.
*
* Thus per block cost of Blake2b256 hashing can be limited by 1 cost units.
* However, on a less powerful processor it may take much more time, so we add
* a factor of 3 for that. Additionally, the interpreter has overhead so that
* performing 1000 of hashes in a tight loop is 3-4 times faster than doing the same
* via ErgoTreeEvaluator. Thus we should add another factor of 2 and this takes
* place for all operations. So we will use a total factor of 10 to convert
* actual operation micro-seconds time (obtained via benchmarking) to cost unit
* estimation (used for cost prediction).
*
* Cost_in_units = time_in_micro-seconds * 7 = 7
*
* NOTE, 128 is the size of message chunk processed by Blake2b256 algorithm.
*
* @see [[sigmastate.interpreter.ErgoTreeEvaluator.DataBlockSize]]
*/
override val costKind = PerItemCost(
baseCost = JitCost(20), perChunkCost = JitCost(7), chunkSize = 128)
override def argInfos: Seq[ArgInfo] = CalcBlake2b256Info.argInfos
}
/**
* Calculate Sha256 hash from `input`
*/
case class CalcSha256(override val input: Value[SByteArray]) extends CalcHash {
override def companion = CalcSha256
override val hashFn: CryptographicHash32 = Sha256
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Coll[Byte]](env)
addSeqCost(CalcSha256.costKind, inputV.length) { () =>
SigmaDsl.sha256(inputV)
}
}
}
object CalcSha256 extends SimpleTransformerCompanion {
override def opCode: OpCode = OpCodes.CalcSha256Code
/** perChunkCost - cost of hashing 64 bytes of data (see also CalcBlake2b256). */
override val costKind = PerItemCost(
baseCost = JitCost(80), perChunkCost = JitCost(8), chunkSize = 64)
override def argInfos: Seq[ArgInfo] = CalcSha256Info.argInfos
}
/**
* Transforms serialized bytes of ErgoTree with segregated constants by replacing constants
* at given positions with new values. This operation allow to use serialized scripts as
* pre-defined templates.
* The typical usage is "check that output box have proposition equal to given script bytes,
* where minerPk (constants(0)) is replaced with currentMinerPk".
* Each constant in original scriptBytes have SType serialized before actual data (see ConstantSerializer).
* During substitution each value from newValues is checked to be an instance of the corresponding type.
* This means, the constants during substitution cannot change their types.
*
* @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1.
* @param positions zero based indexes in ErgoTree.constants array which should be replaced with new values
* @param newValues new values to be injected into the corresponding positions in ErgoTree.constants array
* @return original scriptBytes array where only specified constants are replaced and all other bytes remain exactly the same
*/
case class SubstConstants[T <: SType](scriptBytes: Value[SByteArray], positions: Value[SIntArray], newValues: Value[SCollection[T]])
extends NotReadyValueByteArray {
override def companion = SubstConstants
override val opType = SubstConstants.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val scriptBytesV = scriptBytes.evalTo[Coll[Byte]](env)
val positionsV = positions.evalTo[Coll[Int]](env)
val newValuesV = newValues.evalTo[Coll[T#WrappedType]](env)
var res: Coll[Byte] = null
E.addSeqCost(SubstConstants.costKind, SubstConstants.opDesc) { () =>
val typedNewVals: Array[Constant[SType]] =
try newValuesV.toArrayOfConstants
catch {
case e: Throwable =>
throw new RuntimeException(s"Cannot evaluate substConstants($scriptBytesV, $positionsV, $newValuesV)", e)
}
val (newBytes, nConstants) = SubstConstants.eval(
scriptBytes = scriptBytesV.toArray,
positions = positionsV.toArray,
newVals = typedNewVals)(SigmaDsl.validationSettings)
res = Colls.fromArray(newBytes)
nConstants
}
res
}
}
object SubstConstants extends ValueCompanion {
override def opCode: OpCode = OpCodes.SubstConstantsCode
override val costKind = PerItemCost(
baseCost = JitCost(100), perChunkCost = JitCost(100), chunkSize = 1)
val OpType = SFunc(Array(SByteArray, SIntArray, SCollection(SType.tT)), SByteArray)
/** Transforms serialized bytes of ErgoTree with segregated constants by
* replacing constants at given positions with new values. This operation
* allow to use serialized scripts as pre-defined templates.
* See [[SubstConstants]] for details.
*
* @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1.
* @param positions zero based indexes in ErgoTree.constants array which
* should be replaced with new values
* @param newVals new values to be injected into the corresponding
* positions in ErgoTree.constants array
* @return original scriptBytes array where only specified constants are
* replaced and all other bytes remain exactly the same
*/
def eval(scriptBytes: Array[Byte],
positions: Array[Int],
newVals: Array[Constant[SType]])(implicit vs: SigmaValidationSettings): (Array[Byte], Int) =
ErgoTreeSerializer.DefaultSerializer.substituteConstants(scriptBytes, positions, newVals)
}
/**
* A tree node with left and right descendants
*/
sealed trait Triple[LIV <: SType, RIV <: SType, OV <: SType] extends NotReadyValue[OV] {
val left: Value[LIV]
val right: Value[RIV]
}
sealed trait OneArgumentOperation[IV <: SType, OV <: SType] extends NotReadyValue[OV] {
val input: Value[IV]
override def opType = SFunc(input.tpe, tpe)
}
trait OneArgumentOperationCompanion extends ValueCompanion {
def argInfos: Seq[ArgInfo]
}
// TwoArgumentsOperation
sealed trait TwoArgumentsOperation[LIV <: SType, RIV <: SType, OV <: SType]
extends Triple[LIV, RIV, OV]
trait TwoArgumentOperationCompanion extends ValueCompanion {
def argInfos: Seq[ArgInfo]
}
/** Represents binary operation with the given opCode. */
case class ArithOp[T <: SType](left: Value[T], right: Value[T], override val opCode: OpCode)
extends TwoArgumentsOperation[T, T, T] with NotReadyValue[T] {
override def companion: ArithOpCompanion = ArithOp.operations(opCode)
override def tpe: T = left.tpe
override val opType = SFunc(Array[SType](left.tpe, right.tpe), tpe)
override def opName: String = ArithOp.opcodeToArithOpName(opCode)
// TODO refactor: avoid such enumaration, use ArithOp.operations map instead
override def toString: String = opCode match {
case OpCodes.PlusCode => s"Plus($left, $right)"
case OpCodes.MinusCode => s"Minus($left, $right)"
case OpCodes.MultiplyCode => s"Multiply($left, $right)"
case OpCodes.DivisionCode => s"Divide($left, $right)"
case OpCodes.ModuloCode => s"Modulo($left, $right)"
case OpCodes.MinCode => s"Min($left, $right)"
case OpCodes.MaxCode => s"Max($left, $right)"
}
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val x = left.evalTo[Any](env)
val y = right.evalTo[Any](env)
companion.eval(this, tpe.typeCode, x, y) // NOTE: cost is added as part of eval call
}
}
/** NOTE: by-name argument is required for correct initialization order. */
abstract class ArithOpCompanion(val opCode: OpCode, val name: String, _argInfos: => Seq[ArgInfo])
extends TwoArgumentOperationCompanion {
override def argInfos: Seq[ArgInfo] = _argInfos
override def costKind: TypeBasedCost
@inline final def eval(node: SValue, typeCode: SType.TypeCode, x: Any, y: Any)(implicit E: ErgoTreeEvaluator): Any = {
val impl = ArithOp.impls(typeCode)
node.addCost(costKind, impl.argTpe) { () =>
eval(impl, x, y)
}
}
def eval(impl: OperationImpl, x: Any, y: Any): Any
}
object ArithOp {
import OpCodes._
/** Addition operation `x + y`. */
object Plus extends ArithOpCompanion(PlusCode, "+", PlusInfo.argInfos) {
override def eval(impl: OperationImpl, x: Any, y: Any): Any = impl.i.plus(x, y)
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of Numeric
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(20)
case _ => JitCost(15)
}
}
}
/** Subtraction operation `x - y`. */
object Minus extends ArithOpCompanion(MinusCode, "-", MinusInfo.argInfos) {
override def eval(impl: OperationImpl, x: Any, y: Any): Any = impl.i.minus(x, y)
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of Numeric
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(20)
case _ => JitCost(15)
}
}
}
/** Multiplication operation `x * y`. */
object Multiply extends ArithOpCompanion(MultiplyCode, "*", MultiplyInfo.argInfos) {
override def eval(impl: OperationImpl, x: Any, y: Any): Any = impl.i.times(x, y)
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of Numeric
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(25)
case _ => JitCost(15)
}
}
}
/** Integer division operation `x / y`. */
object Division extends ArithOpCompanion(DivisionCode, "/", DivisionInfo.argInfos) {
override def eval(impl: OperationImpl, x: Any, y: Any): Any = impl.i.quot(x, y)
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of Integral
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(25)
case _ => JitCost(15)
}
}
}
/** Operation which returns remainder from dividing x by y.
* See ExactIntegral.divisionRemainder implementation for the concrete numeric type.
*/
object Modulo extends ArithOpCompanion(ModuloCode, "%", ModuloInfo.argInfos) {
override def eval(impl: OperationImpl, x: Any, y: Any): Any = impl.i.divisionRemainder(x, y)
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of Integral
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(25)
case _ => JitCost(15)
}
}
}
/** Return `x` if `x` <= `y`, otherwise `y`. */
object Min extends ArithOpCompanion(MinCode, "min", MinInfo.argInfos) {
override def eval(impl: OperationImpl, x: Any, y: Any): Any = impl.o.min(x, y)
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of ExactOrdering
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(10)
case _ => JitCost(5)
}
}
}
/** Return `x` if `x` >= `y`, otherwise `y`. */
object Max extends ArithOpCompanion(MaxCode, "max", MaxInfo.argInfos) {
override def eval(impl: OperationImpl, x: Any, y: Any): Any = impl.o.max(x, y)
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of ExactOrdering
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(10)
case _ => JitCost(5)
}
}
}
private[sigma] val operations: DMap[Byte, ArithOpCompanion] =
DMap.fromIterable(Seq(Plus, Minus, Multiply, Division, Modulo, Min, Max).map(o => (o.opCode, o)))
/** Represents implementation of numeric Arith operations for the given type argTpe. */
class OperationImpl(_i: ExactIntegral[_], _o: ExactOrdering[_], val argTpe: SType) {
val i = _i.asInstanceOf[ExactIntegral[Any]]
val o = _o.asInstanceOf[ExactOrdering[Any]]
}
private[sigma] val impls: DMap[SType.TypeCode, OperationImpl] =
DMap.fromIterable(Seq(
SByte -> new OperationImpl(ByteIsExactIntegral, ByteIsExactOrdering, SByte),
SShort -> new OperationImpl(ShortIsExactIntegral, ShortIsExactOrdering, SShort),
SInt -> new OperationImpl(IntIsExactIntegral, IntIsExactOrdering, SInt),
SLong -> new OperationImpl(LongIsExactIntegral, LongIsExactOrdering, SLong),
SBigInt -> new OperationImpl(BigIntIsExactIntegral, BigIntIsExactOrdering, SBigInt)
).map { case (t, n) => (t.typeCode, n) })
/** Returns operation name for the given opCode. */
def opcodeToArithOpName(opCode: Byte): String = operations.get(opCode) match {
case Some(c) => c.name
case _ => sys.error(s"Cannot find ArithOpName for opcode $opCode")
}
}
/** Negation operation on numeric type T.
* See ExactNumeric instance for the corresponding type T.
*/
case class Negation[T <: SType](input: Value[T]) extends OneArgumentOperation[T, T] {
require(input.tpe.isNumTypeOrNoType, s"invalid type ${input.tpe}")
override def companion = Negation
override def tpe: T = input.tpe
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[AnyVal](env)
val i = ArithOp.impls(input.tpe.typeCode).i
addCost(Negation.costKind)
i.negate(inputV)
}
}
object Negation extends OneArgumentOperationCompanion with FixedCostValueCompanion {
override def opCode: OpCode = OpCodes.NegationCode
override val costKind = FixedCost(JitCost(30))
override def argInfos: Seq[ArgInfo] = NegationInfo.argInfos
}
/** Not implemented in v4.x. */
case class BitInversion[T <: SType](input: Value[T]) extends OneArgumentOperation[T, T] {
require(input.tpe.isNumTypeOrNoType, s"invalid type ${input.tpe}")
override def companion = BitInversion
override def tpe: T = input.tpe
}
object BitInversion extends OneArgumentOperationCompanion {
override def opCode: OpCode = OpCodes.BitInversionCode
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
override def argInfos: Seq[ArgInfo] = BitInversionInfo.argInfos
}
/** ErgoTree node which represents a binary bit-wise operation with the given opCode. */
case class BitOp[T <: SType](left: Value[T], right: Value[T], override val opCode: OpCode)
extends TwoArgumentsOperation[T, T, T] with NotReadyValue[T] {
require(left.tpe.isNumTypeOrNoType && right.tpe.isNumTypeOrNoType, s"invalid types left:${left.tpe}, right:${right.tpe}")
override def companion = BitOp.operations(opCode)
override def tpe: T = left.tpe
override val opType = SFunc(Array[SType](left.tpe, right.tpe), tpe)
}
/** NOTE: by-name argument is required for correct initialization order. */
abstract class BitOpCompanion(val opCode: OpCode, val name: String, _argInfos: => Seq[ArgInfo]) extends TwoArgumentOperationCompanion {
override def argInfos: Seq[ArgInfo] = _argInfos
}
object BitOp {
import OpCodes._
object BitOr extends BitOpCompanion(BitOrCode, "|", BitOrInfo.argInfos) {
override val costKind = FixedCost(JitCost(1))
}
object BitAnd extends BitOpCompanion(BitAndCode, "&", BitAndInfo.argInfos) {
override val costKind = FixedCost(JitCost(1))
}
object BitXor extends BitOpCompanion(BitXorCode, "^", BitXorInfo.argInfos) {
override val costKind = FixedCost(JitCost(1))
}
object BitShiftRight extends BitOpCompanion(BitShiftRightCode, ">>", BitShiftRightInfo.argInfos) {
override val costKind = FixedCost(JitCost(1))
}
object BitShiftLeft extends BitOpCompanion(BitShiftLeftCode, "<<", BitShiftLeftInfo.argInfos) {
override val costKind = FixedCost(JitCost(1))
}
object BitShiftRightZeroed extends BitOpCompanion(BitShiftRightZeroedCode, ">>>", BitShiftRightZeroedInfo.argInfos) {
override val costKind = FixedCost(JitCost(1))
}
val operations: Map[Byte, BitOpCompanion] =
Seq(BitOr, BitAnd, BitXor, BitShiftRight, BitShiftLeft, BitShiftRightZeroed).map(o => (o.opCode, o)).toMap
def opcodeToName(opCode: Byte): String = operations.get(opCode) match {
case Some(c) => c.name
case _ => sys.error(s"Cannot find BitOpName for opcode $opCode")
}
}
// TODO v6.0: implement modular operations (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/327)
case class ModQ(input: Value[SBigInt.type])
extends NotReadyValue[SBigInt.type] {
override def companion = ModQ
override def tpe: SBigInt.type = SBigInt
override def opType: SFunc = SFunc(input.tpe, tpe)
}
object ModQ extends ValueCompanion {
override def opCode: OpCode = OpCodes.ModQCode
override val costKind: CostKind = FixedCost(JitCost(1))
}
case class ModQArithOp(left: Value[SBigInt.type], right: Value[SBigInt.type], override val opCode: OpCode)
extends NotReadyValue[SBigInt.type] {
override def companion = ModQArithOp.operations(opCode)
override def tpe: SBigInt.type = SBigInt
override def opType: SFunc = SFunc(Array(left.tpe, right.tpe), tpe)
}
abstract class ModQArithOpCompanion(val opCode: OpCode, val name: String) extends ValueCompanion {
def argInfos: Seq[ArgInfo]
override val costKind: CostKind = FixedCost(JitCost(1))
}
trait OpGroup[C <: ValueCompanion] {
def operations: Map[Byte, C]
}
object ModQArithOp extends OpGroup[ModQArithOpCompanion] {
import OpCodes._
object PlusModQ extends ModQArithOpCompanion(PlusModQCode, "PlusModQ") {
// override def argInfos: Seq[ArgInfo] = PlusModQInfo.argInfos
override def argInfos: Seq[ArgInfo] = Seq(ArgInfo("this", ""), ArgInfo("other", ""))
}
object MinusModQ extends ModQArithOpCompanion(MinusModQCode, "MinusModQ") {
// override def argInfos: Seq[ArgInfo] = MinusModQInfo.argInfos
override def argInfos: Seq[ArgInfo] = Seq(ArgInfo("this", ""), ArgInfo("other", ""))
}
val operations: Map[Byte, ModQArithOpCompanion] = Seq(PlusModQ, MinusModQ).map(o => (o.opCode, o)).toMap
def opcodeToName(opCode: Byte): String = operations.get(opCode) match {
case Some(c) => c.name
case _ => sys.error(s"Cannot find ModQArithOp operation name for opcode $opCode")
}
}
/**
* XOR for two SByteArray
*/
case class Xor(override val left: Value[SByteArray],
override val right: Value[SByteArray])
extends TwoArgumentsOperation[SByteArray, SByteArray, SByteArray]
with NotReadyValueByteArray {
override def companion = Xor
override def opType = Xor.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val lV = left.evalTo[Coll[Byte]](env)
val rV = right.evalTo[Coll[Byte]](env)
Xor.xorWithCosting(lV, rV)
}
}
object Xor extends TwoArgumentOperationCompanion {
val OpType = SFunc(Array(SByteArray, SByteArray), SByteArray)
override def opCode: OpCode = XorCode
override val costKind = PerItemCost(
baseCost = JitCost(10), perChunkCost = JitCost(2), chunkSize = 128)
override def argInfos: Seq[ArgInfo] = XorInfo.argInfos
/** Helper method which compute xor with correct costing accumulation */
def xorWithCosting(ls: Coll[Byte], rs: Coll[Byte])(implicit E: ErgoTreeEvaluator): Coll[Byte] = {
E.addSeqCost(Xor.costKind, math.min(ls.length, rs.length), Xor.opDesc) { () =>
Colls.xor(ls, rs)
}
}
}
case class Exponentiate(override val left: Value[SGroupElement.type],
override val right: Value[SBigInt.type])
extends TwoArgumentsOperation[SGroupElement.type, SBigInt.type, SGroupElement.type]
with NotReadyValueGroupElement {
override def companion = Exponentiate
override def opType = Exponentiate.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val leftV = left.evalTo[GroupElement](env)
val rightV = right.evalTo[sigma.BigInt](env)
addCost(Exponentiate.costKind)
leftV.exp(rightV)
}
}
object Exponentiate extends TwoArgumentOperationCompanion with FixedCostValueCompanion {
val OpType = SFunc(Array(SGroupElement, SBigInt), SGroupElement)
override def opCode: OpCode = ExponentiateCode
/** Cost of: 1) calling EcPoint.multiply 2) wrapping in GroupElement */
override val costKind = FixedCost(JitCost(900))
override def argInfos: Seq[ArgInfo] = ExponentiateInfo.argInfos
}
case class MultiplyGroup(override val left: Value[SGroupElement.type],
override val right: Value[SGroupElement.type])
extends TwoArgumentsOperation[SGroupElement.type, SGroupElement.type, SGroupElement.type]
with NotReadyValueGroupElement {
override def companion = MultiplyGroup
override def opType = MultiplyGroup.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val leftV = left.evalTo[GroupElement](env)
val rightV = right.evalTo[GroupElement](env)
addCost(MultiplyGroup.costKind)
leftV.multiply(rightV)
}
}
object MultiplyGroup extends TwoArgumentOperationCompanion with FixedCostValueCompanion {
val OpType = SFunc(Array(SGroupElement, SGroupElement), SGroupElement)
override def opCode: OpCode = MultiplyGroupCode
/** Cost of: 1) calling EcPoint.add 2) wrapping in GroupElement */
override val costKind = FixedCost(JitCost(40))
override def argInfos: Seq[ArgInfo] = MultiplyGroupInfo.argInfos
}
// Relation
sealed trait Relation[LIV <: SType, RIV <: SType] extends Triple[LIV, RIV, SBoolean.type]
with NotReadyValueBoolean
trait SimpleRelation[T <: SType] extends Relation[T, T] {
override def opType = SimpleRelation.GenericOpType
lazy val opImpl = ArithOp.impls(left.tpe.typeCode)
}
object SimpleRelation {
val GenericOpType = SFunc(SType.IndexedSeqOfT2, SBoolean)
}
trait RelationCompanion extends ValueCompanion {
def argInfos: Seq[ArgInfo]
}
/**
* Less operation for SInt
*/
case class LT[T <: SType](override val left: Value[T], override val right: Value[T]) extends SimpleRelation[T] {
override def companion = LT
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val lV = left.evalTo[Any](env)
val rV = right.evalTo[Any](env)
addCost(LT.costKind, left.tpe) { () =>
opImpl.o.lt(lV, rV)
}
}
}
object LT extends RelationCompanion {
override def opCode: OpCode = LtCode
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of Numeric
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(20)
case _ => JitCost(20)
}
}
override def argInfos: Seq[ArgInfo] = LTInfo.argInfos
}
/**
* Less or equals operation for SInt
*/
case class LE[T <: SType](override val left: Value[T], override val right: Value[T]) extends SimpleRelation[T] {
override def companion = LE
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val lV = left.evalTo[Any](env)
val rV = right.evalTo[Any](env)
addCost(LE.costKind, left.tpe) { () =>
opImpl.o.lteq(lV, rV)
}
}
}
object LE extends RelationCompanion {
override def opCode: OpCode = LeCode
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of Numeric
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(20) // cf. comparisonBigInt
case _ => JitCost(20) // cf. comparisonCost
}
}
override def argInfos: Seq[ArgInfo] = LEInfo.argInfos
}
/**
* Greater operation for [[SNumericType]] values
*/
case class GT[T <: SType](override val left: Value[T], override val right: Value[T]) extends SimpleRelation[T] {
override def companion = GT
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val lV = left.evalTo[Any](env)
val rV = right.evalTo[Any](env)
addCost(GT.costKind, left.tpe) { () =>
opImpl.o.gt(lV, rV)
}
}
}
object GT extends RelationCompanion {
override def opCode: OpCode = GtCode
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of Numeric
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(20) // cf. comparisonBigInt
case _ => JitCost(20) // cf. comparisonCost
}
}
override def argInfos: Seq[ArgInfo] = GTInfo.argInfos
}
/**
* Greater or equals operation for SInt
*/
case class GE[T <: SType](override val left: Value[T], override val right: Value[T]) extends SimpleRelation[T] {
override def companion = GE
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val lV = left.evalTo[Any](env)
val rV = right.evalTo[Any](env)
addCost(GE.costKind, left.tpe) { () =>
opImpl.o.gteq(lV, rV)
}
}
}
object GE extends RelationCompanion {
override def opCode: OpCode = GeCode
/** Cost of:
* 1) resolving ArithOpCompanion by typeCode
* 2) calling method of Numeric
*/
override val costKind = new TypeBasedCost {
override def costFunc(tpe: SType): JitCost = tpe match {
case SBigInt => JitCost(20)
case _ => JitCost(20)
}
}
override def argInfos: Seq[ArgInfo] = GEInfo.argInfos
}
/**
* Equals operation for SType
* todo: make EQ to really accept only values of the same type, now EQ(TrueLeaf, IntConstant(5)) is valid
*/
case class EQ[S <: SType](
override val left: Value[S],
override val right: Value[S]) extends SimpleRelation[S] {
override def companion = EQ
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val l = left.evalTo[S#WrappedType](env)
Value.checkType(left, l) // necessary because cast to S#WrappedType is erased
val r = right.evalTo[S#WrappedType](env)
Value.checkType(right, r) // necessary because cast to S#WrappedType is erased
DataValueComparer.equalDataValues(l, r)
}
}
object EQ extends RelationCompanion {
override def opCode: OpCode = EqCode
override def costKind = DynamicCost
override def argInfos: Seq[ArgInfo] = EQInfo.argInfos
}
/**
* Non-Equals operation for SType
*/
case class NEQ[S <: SType](override val left: Value[S], override val right: Value[S])
extends SimpleRelation[S] {
override def companion = NEQ
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val l = left.evalTo[S#WrappedType](env)
Value.checkType(left, l) // necessary because cast to S#WrappedType is erased
val r = right.evalTo[S#WrappedType](env)
Value.checkType(right, r) // necessary because cast to S#WrappedType is erased
!DataValueComparer.equalDataValues(l, r)
}
}
object NEQ extends RelationCompanion {
override def opCode: OpCode = NeqCode
override def costKind = DynamicCost
override def argInfos: Seq[ArgInfo] = NEQInfo.argInfos
}
/**
* Logical OR with lazy right argument which is evaluated only if left == false.
* If left argument is true, the right is guaranteed to NOT be evaluated
*/
case class BinOr(override val left: BoolValue, override val right: BoolValue)
extends Relation[SBoolean.type, SBoolean.type] {
override def companion = BinOr
override def opType = BinOr.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val l = left.evalTo[Boolean](env)
addCost(BinOr.costKind)
l || right.evalTo[Boolean](env) // rely on short-cutting semantics of Scala's ||
}
}
object BinOr extends RelationCompanion with FixedCostValueCompanion {
val OpType = SFunc(Array(SBoolean, SBoolean), SBoolean)
override def opCode: OpCode = BinOrCode
/** Cost of: scala `||` operation
* Old cost: ("BinOr", "(Boolean, Boolean) => Boolean", logicCost) */
override val costKind = FixedCost(JitCost(20))
override def argInfos: Seq[ArgInfo] = BinOrInfo.argInfos
}
/**
* Logical AND with lazy right argument which is evaluated only if left == true.
* If left argument is false, the right is guaranteed to NOT be evaluated
*/
case class BinAnd(override val left: BoolValue, override val right: BoolValue)
extends Relation[SBoolean.type, SBoolean.type] {
override def companion = BinAnd
override def opType = BinAnd.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val l = left.evalTo[Boolean](env)
addCost(BinAnd.costKind)
l && right.evalTo[Boolean](env) // rely on short-cutting semantics of Scala's &&
}
}
object BinAnd extends RelationCompanion with FixedCostValueCompanion {
val OpType = SFunc(Array(SBoolean, SBoolean), SBoolean)
override def opCode: OpCode = BinAndCode
/** Cost of: scala `&&` operation
* Old cost: ("BinAnd", "(Boolean, Boolean) => Boolean", logicCost) */
override val costKind = FixedCost(JitCost(20))
override def argInfos: Seq[ArgInfo] = BinAndInfo.argInfos
}
case class BinXor(override val left: BoolValue, override val right: BoolValue)
extends Relation[SBoolean.type, SBoolean.type] {
override def companion = BinXor
override def opType = BinXor.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val leftV = left.evalTo[Boolean](env)
val rightV = right.evalTo[Boolean](env)
addCost(BinXor.costKind)
leftV ^ rightV
}
}
object BinXor extends RelationCompanion with FixedCostValueCompanion {
val OpType = SFunc(Array(SBoolean, SBoolean), SBoolean)
override def opCode: OpCode = BinXorCode
/** Cost of: scala `^` operation
* Old cost: ("BinXor", "(Boolean, Boolean) => Boolean", logicCost) */
override val costKind = FixedCost(JitCost(20))
override def argInfos: Seq[ArgInfo] = BinXorInfo.argInfos
}
/**
* A tree node with three descendants
*/
sealed trait Quadruple[IV1 <: SType, IV2 <: SType, IV3 <: SType, OV <: SType] extends NotReadyValue[OV] {
val first: Value[IV1]
val second: Value[IV2]
val third: Value[IV3]
val opType = SFunc(Array(first.tpe, second.tpe, third.tpe), tpe)
}
/**
* Perform a lookup of key `key` in a tree with root `tree` using proof `proof`.
* Throws exception if proof is incorrect
* Return Some(bytes) of leaf with key `key` if it exists
* Return None if leaf with provided key does not exist.
*/
case class TreeLookup(tree: Value[SAvlTree.type],
key: Value[SByteArray],
proof: Value[SByteArray]) extends Quadruple[SAvlTree.type, SByteArray, SByteArray, SOption[SByteArray]] {
override def companion = TreeLookup
override def tpe = SOption[SByteArray](SByteArray)
override lazy val first = tree
override lazy val second = key
override lazy val third = proof
}
trait QuadrupleCompanion extends ValueCompanion {
def argInfos: Seq[ArgInfo]
}
object TreeLookup extends QuadrupleCompanion {
override def opCode: OpCode = OpCodes.AvlTreeGetCode
override def costKind: CostKind = Value.notSupportedError(this, "costKind")
override def argInfos: Seq[ArgInfo] = TreeLookupInfo.argInfos
}
/**
* If conditional function.
* Non-lazy - evaluate both branches.
*
* @param condition - condition to check
* @param trueBranch - branch that will be used if condition is true
* @param falseBranch - branch that will be used if condition is false
*/
case class If[T <: SType](condition: Value[SBoolean.type], trueBranch: Value[T], falseBranch: Value[T])
extends Quadruple[SBoolean.type, T, T, T] {
override def companion = If
override def tpe = trueBranch.tpe
override lazy val first = condition
override lazy val second = trueBranch
override lazy val third = falseBranch
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val c = condition.evalTo[Boolean](env)
addCost(If.costKind)
if (c) {
val res = trueBranch.evalTo[T#WrappedType](env)
Value.checkType(trueBranch, res) // necessary because cast to T#WrappedType is erased
res
} else {
val res = falseBranch.evalTo[T#WrappedType](env)
Value.checkType(falseBranch, res) // necessary because cast to T#WrappedType is erased
res
}
}
}
object If extends QuadrupleCompanion with FixedCostValueCompanion {
override def opCode: OpCode = OpCodes.IfCode
/** Cost of: conditional switching to the right branch (excluding the cost both
* condition itself and the branches) */
override val costKind = FixedCost(JitCost(10))
override def argInfos: Seq[ArgInfo] = IfInfo.argInfos
val GenericOpType = SFunc(Array(SBoolean, SType.tT, SType.tT), SType.tT)
}
case class LogicalNot(input: Value[SBoolean.type]) extends NotReadyValueBoolean {
override def companion = LogicalNot
override def opType = LogicalNot.OpType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Boolean](env)
addCost(LogicalNot.costKind)
!inputV
}
}
object LogicalNot extends FixedCostValueCompanion {
val OpType = SFunc(Array(SBoolean), SBoolean)
override def opCode: OpCode = OpCodes.LogicalNotCode
/** Cost of: scala `!` operation */
override val costKind = FixedCost(JitCost(15))
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy